Flow: Installing Flow on a system without glibc

Created on 31 Mar 2016  路  13Comments  路  Source: facebook/flow

I am trying to install flow on an alpine linux docker image. I am able to install node without issue, but can't get flow to compile. In looking through the make code it should work on systems without glibc (alpine is based on musl), but I am unable to get make to build flow. I have worked through the various steps and it seems that the only part of the make that fails is the installation of inotify. Has anyone been able to successfully build flow on Alpine or other non-glibc system?

Most helpful comment

I've created Alpine package for Flow. It's available for x86_64 and aarch64 in the Alpine's testing repository for now. Just install it using apk, e.g.:

apk add --repository https://nl.alpinelinux.org/alpine/edge/testing flow

All 13 comments

I'm also trying to do the same, and have also tried building it from source, and I get stuck on the _inotify_ problem.

Here's the Dockfile I'm using to build the binary:

FROM alpine:edge

RUN echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories
RUN apk --no-cache add alpine-sdk ocaml libelf ncurses inotify-tools git

WORKDIR /tmp
RUN git clone --branch v0.22.1 https://github.com/facebook/flow.git --depth 1
WORKDIR /tmp/flow
RUN make

And here's the output of _make_:

mkdir -p bin
tar czf bin/flowlib.tar.gz -C lib .
echo "const char* const BuildInfo_kRevision = \"$(git rev-parse HEAD 2>/dev/null || hg identify -i)\";" > hack/utils/get_build_id.gen.c
ocamlbuild -ocamlc "ocamlopt   -ccopt -DNO_LZ4"\
    hack/third-party/inotify/inotify_stubs.o hack/heap/hh_shared.o hack/hhi/hhi_elf.o hack/utils/files.o hack/utils/get_build_id.gen.o hack/utils/get_build_id.o hack/utils/handle_stubs.o hack/utils/nproc.o hack/utils/realpath.o hack/utils/sysinfo.o hack/utils/priorities.o hack/utils/win32_support.o hack/hhi/hhi_win32res_stubs.o src/embedded/flowlib_elf.o
+ ocamlopt   -ccopt -DNO_LZ4 -c hack/third-party/inotify/inotify_stubs.c
hack/third-party/inotify/inotify_stubs.c:41:28: fatal error: inotify_compat.h: No such file or directory
compilation terminated.
Command exited with code 2.
Compilation unsuccessful after building 1 target (0 cached) in 00:00:00.
Makefile:132: recipe for target 'build-flow-native-deps' failed
make: *** [build-flow-native-deps] Error 10

This error hack/third-party/inotify/inotify_stubs.c:41:28: fatal error: inotify_compat.h: No such file or directory is interesting, because the inotify_compat.h file exits in the same directory as inotify_stubs.c, as it can be verified here https://github.com/facebook/flow/tree/v0.22.1/hack/third-party/inotify.

In order to get a bit further in the testing, I decided to just run the inotify portion of make. I ran this command:
ocamlbuild -ocamlc "ocamlopt -ccopt -DNO_LZ4" hack/third-party/inotify/inotify_stubs.o

This gave me the following error:
hack/third-party/inotify/inotify_stubs.c:41:28: fatal error: inotify_compat.h: No such file or directory

I edited line 41 of inotify_stubs.c from:
#include "inotify_compat.h"
to:
#include /tmp/flow/hack/third-party/inotify/inotify_compat.h

Then I ran make again and got this error:

In file included from hack/third-party/inotify/inotify_stubs.c:41:0:
/tmp/flow/hack/third-party/inotify/inotify_compat.h:53:17: error: unknown type name 'inotify_init'
 _syscall0(long, inotify_init);
                 ^
/tmp/flow/hack/third-party/inotify/inotify_compat.h:54:16: error: unknown type name 'inotify_add_watch'
 _syscall3(int, inotify_add_watch, int, fd, char *, name, int, mask);
                ^
/tmp/flow/hack/third-party/inotify/inotify_compat.h:54:40: error: unknown type name 'fd'
 _syscall3(int, inotify_add_watch, int, fd, char *, name, int, mask);
                                        ^
/tmp/flow/hack/third-party/inotify/inotify_compat.h:54:52: error: unknown type name 'name'
 _syscall3(int, inotify_add_watch, int, fd, char *, name, int, mask);
                                                    ^
/tmp/flow/hack/third-party/inotify/inotify_compat.h:54:63: error: unknown type name 'mask'
 _syscall3(int, inotify_add_watch, int, fd, char *, name, int, mask);
                                                               ^
/tmp/flow/hack/third-party/inotify/inotify_compat.h:55:16: error: unknown type name 'inotify_rm_watch'
 _syscall2(int, inotify_rm_watch, int, fd, int, wd);
                ^
/tmp/flow/hack/third-party/inotify/inotify_compat.h:55:39: error: unknown type name 'fd'
 _syscall2(int, inotify_rm_watch, int, fd, int, wd);
                                       ^
/tmp/flow/hack/third-party/inotify/inotify_compat.h:55:48: error: unknown type name 'wd'
 _syscall2(int, inotify_rm_watch, int, fd, int, wd);
                                                ^
hack/third-party/inotify/inotify_stubs.c: In function 'stub_inotify_init':
hack/third-party/inotify/inotify_stubs.c:77:14: warning: implicit declaration of function 'inotify_init' [-Wimplicit-function-declaration]
         fd = inotify_init();
              ^
hack/third-party/inotify/inotify_stubs.c: In function 'stub_inotify_add_watch':
hack/third-party/inotify/inotify_stubs.c:102:14: warning: implicit declaration of function 'inotify_add_watch' [-Wimplicit-function-declaration]
         wd = inotify_add_watch(Int_val(fd), String_val(path), cv_mask);
              ^
hack/third-party/inotify/inotify_stubs.c: In function 'stub_inotify_rm_watch':
hack/third-party/inotify/inotify_stubs.c:113:15: warning: implicit declaration of function 'inotify_rm_watch' [-Wimplicit-function-declaration]
         ret = inotify_rm_watch(Int_val(fd), Int_val(wd));
               ^
Command exited with code 2.
Compilation unsuccessful after building 1 target (0 cached) in 00:00:00.

If I remove the inotify portion of the ocamlbuild command, everything else builds. This is the only part of the build that is failing. Any ideas?

I was able to go around these errors by installing the libelf-dev and linux-headers packages, and modifying one of the source files.

The error: unknown type name <nome_func_name> happens because of lines 32 to 36 on inotify_stubs.c:

#if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 4
#define GLIBC_SUPPORT_INOTIFY 1
#else
#define GLIBC_SUPPORT_INOTIFY 0
#endif

I think musl doesn't provide these values, so the compiler can't find them and disables GLIBC_SUPPORT_INOTIFY.

After hardcoding #define GLIBC_SUPPORT_INOTIFY 1 on inotify_stubs.c, the project compiles with some warnings, here's the output of ./flow version:

/tmp/flow/bin # ./flow version
Flow, a static type checker for JavaScript, version 0.22.1

But something goes wrong when running the binary normally, it enters a loop trying to start the flow server:

/tmp/flow/bin # ./flow
Launching Flow server for /tmp/flow
Spawned flow server (child pid=41)
Logs will go to /tmp/flow/zStmpzSflow.log
Launching Flow server for /tmp/flow
Spawned flow server (child pid=63)
Logs will go to /tmp/flow/zStmpzSflow.log
Launching Flow server for /tmp/flow
Spawned flow server (child pid=85)
Logs will go to /tmp/flow/zStmpzSflow.log
Launching Flow server for /tmp/flow
Spawned flow server (child pid=107)
Logs will go to /tmp/flow/zStmpzSflow.log
Launching Flow server for /tmp/flow
Spawned flow server (child pid=129)
Logs will go to /tmp/flow/zStmpzSflow.log
^C

Here's the content of zStmpzSflow.log, which by the way, is the output of running ./flow server:

2016-04-03 20:09:36] Initializing Server (This might take some time)
[2016-04-03 20:09:36] executable=/tmp/flow/bin/flow
[2016-04-03 20:09:36] version=0.22.1
[2016-04-03 20:09:36] Parsing
[2016-04-03 20:09:36] Building package heap
[2016-04-03 20:09:36] Running local inference
[2016-04-03 20:09:37] Re-resolving directly dependent files
[2016-04-03 20:09:37] Calculating dependencies
[2016-04-03 20:09:37] Merging
[2016-04-03 20:09:40] Done
[2016-04-03 20:09:40] Server is READY
[2016-04-03 20:09:40] Took 4.309712 seconds to initialize.
[2016-04-03 20:09:40] Status: Error
[2016-04-03 20:09:40] Modified lib file: /tmp/flow/flowlib_1177d80d/bom.js
[2016-04-03 20:09:40] Modified lib file: /tmp/flow/flowlib_1177d80d/core.js
[2016-04-03 20:09:40] Modified lib file: /tmp/flow/flowlib_1177d80d/cssom.js
[2016-04-03 20:09:40] Modified lib file: /tmp/flow/flowlib_1177d80d/dom.js
[2016-04-03 20:09:40] Modified lib file: /tmp/flow/flowlib_1177d80d/node.js
[2016-04-03 20:09:40] Modified lib file: /tmp/flow/flowlib_1177d80d/react.js
[2016-04-03 20:09:40] flow server is out of date. Exiting.

I found two related issues #826 and #1380, but wasn't able to make any further progress. Some changes are also needed to make runtests.sh run under sh, or install bash, but I haven't gotten around to that yet.

Here's the new Dockerfile:

FROM alpine:edge

WORKDIR /tmp/flow/
COPY glibc_support_inotify.patch /tmp/
RUN echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories \
 && apk --no-cache add \
    alpine-sdk \
    ocaml \
    libelf \
    libelf-dev \
    ncurses \
    inotify-tools \
    linux-headers \
    git \
 && git clone --depth 1 --branch v0.22.1 https://github.com/facebook/flow.git /tmp/flow \
 && mv /tmp/glibc_support_inotify.patch /tmp/flow/ \
 && git apply glibc_support_inotify.patch
 && make

And the glibc_support_inotify.patch:

diff --git a/hack/third-party/inotify/inotify_stubs.c b/hack/third-party/inotify/inotify_stubs.c
index 08fd301..ecfe222 100644
--- a/hack/third-party/inotify/inotify_stubs.c
+++ b/hack/third-party/inotify/inotify_stubs.c
@@ -29,11 +29,7 @@

 #include <features.h>

-#if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 4
 #define GLIBC_SUPPORT_INOTIFY 1
-#else
-#define GLIBC_SUPPORT_INOTIFY 0
-#endif

 #if GLIBC_SUPPORT_INOTIFY
 #include <sys/inotify.h>

To run the test suite, you need to have the bash and diffutils packages installed.

2 tests are skipped because there are no .exp in their directories.

suggest

5 tests fail:

incremental_basic
incremental_cycle
incremental_delete
incremental_path

temp_dir fails because Busybox's version of mktemp requires a least 6 X chars in its template file/directory argument, while GNU's requires at least 3, and Darwin's has no minimum requirement.

The other 4 tests fail because they expect _flow_ to find errors in the input files, but it doesn't find any. Here are the logs (the test assertions are done by diffing the actual output from .out files and the expected output from .exp files in each directory):

[鉁梋 FAIL: incremental_basic
--- incremental_basic.exp   2016-04-05 21:15:35.000000000 +0000
+++ incremental_basic.out   2016-04-05 21:27:58.950993391 +0000
@@ -1,32 +1,4 @@
-
-a.js:2:17,17: number
-This type is incompatible with
-a.js:2:8,13: string
-
-b.js:3:17,17: string
-This type is incompatible with
-b.js:3:8,13: number
-
-c.js:3:17,17: number
-This type is incompatible with
-c.js:3:8,13: string
-
-Found 3 errors
-
-a.js:2:17,17: number
-This type is incompatible with
-a.js:2:8,13: string
-
-Found 1 error
-
-c.js:3:17,17: number
-This type is incompatible with
-c.js:3:8,13: string
-
-Found 1 error
-
-c.js:3:17,17: number
-This type is incompatible with
-c.js:3:8,13: string
-
-Found 1 error
+No errors!
+No errors!
+No errors!
+No errors!
[鉁梋 FAIL: incremental_cycle
--- incremental_cycle.exp   2016-04-05 21:15:35.000000000 +0000
+++ incremental_cycle.out   2016-04-05 21:27:58.680981758 +0000
@@ -1,48 +1,4 @@
-
-A.js:4:6,11: number
-This type is incompatible with
-C.js:6:6,6: B
-
-A.js:5:6,11: string
-This type is incompatible with
-B.js:6:6,6: C
-
-B.js:6:6,6: C
-This type is incompatible with
-A.js:5:6,11: string
-
-C.js:6:6,6: B
-This type is incompatible with
-A.js:4:6,11: number
-
-Found 4 errors
-
-A.js:4:6,11: number
-This type is incompatible with
-C.js:6:6,6: string
-
-C.js:6:6,6: string
-This type is incompatible with
-A.js:4:6,11: number
-
-Found 2 errors
-
-A.js:4:6,11: number
-This type is incompatible with
-C.js:6:6,6: string
-
-C.js:6:6,6: string
-This type is incompatible with
-A.js:4:6,11: number
-
-Found 2 errors
-
-A.js:4:6,11: number
-This type is incompatible with
-C.js:6:6,6: string
-
-C.js:6:6,6: string
-This type is incompatible with
-A.js:4:6,11: number
-
-Found 2 errors
+No errors!
+No errors!
+No errors!
+No errors!
[鉁梋 FAIL: incremental_delete
--- incremental_delete.exp  2016-04-05 21:15:35.000000000 +0000
+++ incremental_delete.out  2016-04-05 21:27:58.690982188 +0000
@@ -1,54 +1,5 @@
-
-a.js:2:17,17: number
-This type is incompatible with
-a.js:2:8,13: string
-
-b.js:3:17,17: string
-This type is incompatible with
-b.js:3:8,13: number
-
-c.js:3:17,17: number
-This type is incompatible with
-c.js:3:8,13: string
-
-Found 3 errors
-
-a.js:2:17,17: number
-This type is incompatible with
-a.js:2:8,13: string
-
-b.js:3:17,17: string
-This type is incompatible with
-b.js:3:8,13: number
-
-Found 2 errors
-
-a.js:2:17,17: number
-This type is incompatible with
-a.js:2:8,13: string
-
-Found 1 error
-
-a.js:2:17,17: number
-This type is incompatible with
-a.js:2:8,13: string
-
-b.js:3:17,17: string
-This type is incompatible with
-b.js:3:8,13: number
-
-Found 2 errors
-
-a.js:2:17,17: number
-This type is incompatible with
-a.js:2:8,13: string
-
-b.js:3:17,17: string
-This type is incompatible with
-b.js:3:8,13: number
-
-c.js:3:17,17: number
-This type is incompatible with
-c.js:3:8,13: string
-
-Found 3 errors
+No errors!
+No errors!
+No errors!
+No errors!
+No errors!
[鉁梋 FAIL: incremental_path
--- incremental_path.exp    2016-04-05 21:15:35.000000000 +0000
+++ incremental_path.out    2016-04-05 21:27:58.030953752 +0000
@@ -1,18 +1,3 @@
-
-dir/a.js:2:2,13: number
-This type is incompatible with
-dir/a.js:2:16,22: boolean
-
-Found 1 error
-
-dir/a.js:2:2,13: string
-This type is incompatible with
-dir/a.js:2:16,22: boolean
-
-Found 1 error
-
-dir/a.js:2:2,13: number
-This type is incompatible with
-dir/a.js:2:16,22: boolean
-
-Found 1 error
+No errors!
+No errors!
+No errors!

Here's the updated Dockerfile:

FROM alpine:edge

WORKDIR /tmp/flow/
COPY flow.patch /tmp/
RUN echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories \
 && apk --no-cache add \
    alpine-sdk \
    ocaml \
    libelf \
    libelf-dev \
    ncurses \
    inotify-tools \
    linux-headers \
    git \
    bash \
    diffutils \
 && git clone --depth 1 --branch v0.22.1 https://github.com/facebook/flow.git /tmp/flow \
 && mv /tmp/flow.patch /tmp/flow/ \
 && git apply flow.patch \
 && make

And the flow.patch file:

diff --git a/hack/third-party/inotify/inotify_stubs.c b/hack/third-party/inotify/inotify_stubs.c
index 08fd301..ecfe222 100644
--- a/hack/third-party/inotify/inotify_stubs.c
+++ b/hack/third-party/inotify/inotify_stubs.c
@@ -29,11 +29,7 @@

 #include <features.h>

-#if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 4
 #define GLIBC_SUPPORT_INOTIFY 1
-#else
-#define GLIBC_SUPPORT_INOTIFY 0
-#endif

 #if GLIBC_SUPPORT_INOTIFY
 #include <sys/inotify.h>
diff --git a/tests/temp_dir/test.sh b/tests/temp_dir/test.sh
index d3b0d6f..33ba688 100755
--- a/tests/temp_dir/test.sh
+++ b/tests/temp_dir/test.sh
@@ -1,7 +1,7 @@
 #!/bin/sh
 FLOW=$1
 $FLOW stop 2> /dev/null > /dev/null
-DIR=$(mktemp -d /tmp/flow.XXXXX)
+DIR=$(mktemp -d /tmp/flow.XXXXXX)
 $FLOW start --wait --temp-dir "$DIR"
 if [[ "$OSTYPE" == "msys"* ]]; then
     [ -f "$DIR"/*.sock ]    && echo "sock file exists"

I tried building _v0.22.1_ using debian:jessie as a base image, and stumbled upon the same errors.

Looking at the build history on Travis, it seems there was a bug in release v0.22.1, which was later fixed, but the pull request doesn't seem to have been merged into the branch.

A new build, for branch v0.22.2, seems to have the fix, but it hasn't been released yet.

You can build it from _HEAD_, with all tests passing, by fixing the GLIBC_SUPPORT_INOTIFY and mktemp problems.

Dockerfile:

FROM alpine:edge
LABEL build=flow

WORKDIR /tmp/flow/
COPY flow.patch /tmp/
RUN echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories \
 && apk --no-cache add \
    alpine-sdk \
    ocaml \
    libelf \
    libelf-dev \
    ncurses \
    inotify-tools \
    linux-headers \
    git \
    bash \
    diffutils \
 && git clone --depth 1 https://github.com/facebook/flow.git /tmp/flow \
 && mv /tmp/flow.patch /tmp/flow/ \
 && git apply flow.patch \
 && make

flow.patch:

diff --git a/hack/third-party/inotify/inotify_stubs.c b/hack/third-party/inotify/inotify_stubs.c
index 08fd301..ecfe222 100644
--- a/hack/third-party/inotify/inotify_stubs.c
+++ b/hack/third-party/inotify/inotify_stubs.c
@@ -29,11 +29,7 @@

 #include <features.h>

-#if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 4
 #define GLIBC_SUPPORT_INOTIFY 1
-#else
-#define GLIBC_SUPPORT_INOTIFY 0
-#endif

 #if GLIBC_SUPPORT_INOTIFY
 #include <sys/inotify.h>
diff --git a/runtests.sh b/runtests.sh
index fcc2eef..8c2d46f 100755
--- a/runtests.sh
+++ b/runtests.sh
@@ -155,7 +155,7 @@ runtest() {
         # parallel runs of the same test don't stomp on each other (Facebook
         # internally runs a stress test to look for flaky tests). If a test
         # fails, we'll then copy the files back to the source directory.
-        OUT_DIR=$(mktemp -d /tmp/flow_test.XXXXX)
+        OUT_DIR=$(mktemp -d /tmp/flow_test.XXXXXX)

         # deletes the temp directory
         function cleanup {
diff --git a/tests/temp_dir/test.sh b/tests/temp_dir/test.sh
index 1efcc24..e0906b0 100755
--- a/tests/temp_dir/test.sh
+++ b/tests/temp_dir/test.sh
@@ -1,7 +1,7 @@
 #!/bin/bash
 FLOW=$1
 $FLOW stop 2> /dev/null > /dev/null
-DIR=$(mktemp -d /tmp/flow.XXXXX)
+DIR=$(mktemp -d /tmp/flow.XXXXXX)
 $FLOW start --wait --temp-dir "$DIR" 2> /dev/null > /dev/null
 if [[ "$OSTYPE" == "msys"* ]]; then
     [ -f "$DIR"/*.sock ]    && echo "sock file exists"
@@ -17,8 +17,8 @@ $FLOW stop --temp-dir "$DIR" 2> /dev/null > /dev/null
 rm -rf "$DIR"

 # Test a .flowconfig with temp_dir
-DIR=$(mktemp -d /tmp/flow.XXXXX)
-TEST_DIR=$(mktemp -d /tmp/flow.XXXXX)
+DIR=$(mktemp -d /tmp/flow.XXXXXX)
+TEST_DIR=$(mktemp -d /tmp/flow.XXXXXX)
 printf "[options]\ntemp_dir=%s" "$DIR" > "$TEST_DIR/.flowconfig"
 $FLOW status "$TEST_DIR" 2> /dev/null > /dev/null
 if [[ "$OSTYPE" == "msys"* ]]; then

@jbrockett I've done some unofficial docker images of flow based on Alpine Linux. It works pretty well. Would be cool if the flow team would release official Alpine based docker containers.

Thanks @gsmoraes and @shouze for the fixes. I can now build flow on alpine. Hopefully the mktemp fix will get merged soon.

Hello guys any progress here?

If you are already building inside docker, it will not be possible to use @shouze's images. I just extract the binary and install libelf like this:

RUN npm install flow-bin

# glibc needed for rkt
RUN apk --no-cache add ca-certificates openssl && \
    wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://raw.githubusercontent.com/sgerrand/alpine-pkg-glibc/master/sgerrand.rsa.pub && \
    apk --no-cache -X http://apkproxy.heroku.com/sgerrand/alpine-pkg-glibc add glibc glibc-bin

# install rkt, docker doesn't seem to work on alpine
WORKDIR /tmp
RUN wget https://github.com/coreos/rkt/releases/download/v1.24.0/rkt-v1.24.0.tar.gz
RUN tar xf rkt-v1.24.0.tar.gz
WORKDIR /tmp/rkt-v1.24.0

# extract flow binary from image and use it instead of the one flow-bin ships
# flow-bin's binary needs libelf1 which is not available on alpine
RUN ./rkt run docker://rezzza/docker-flow --insecure-options=image || true
RUN ./rkt image export registry-1.docker.io/rezzza/docker-flow:latest ../docker-flow.tar
WORKDIR /tmp
RUN tar xf docker-flow.tar
RUN ls node_modules/flow-bin/flow-linux64-*/flow
# here we link into node_modules installed in the first line
RUN ln -sf $PWD/rootfs/usr/local/bin/flow node_modules/flow-bin/flow-linux64-*/flow
RUN apk update && apk add libelf
RUN ls /usr/lib/libelf.so.0

I think this should be fixed in master by updating the inotify code. If this doesn't actually work, please reopen the task!

I've created Alpine package for Flow. It's available for x86_64 and aarch64 in the Alpine's testing repository for now. Just install it using apk, e.g.:

apk add --repository https://nl.alpinelinux.org/alpine/edge/testing flow

ERROR: unsatisfiable constraints:
flow (missing):
required by: world[flow]

it seems on 31st May 2020, the package has been labeled "unmaintained" and no longer works in Alpine Linux builds on Testing/edge channel.

Has this been intentional ? Is there any solution in the works ?
All of our builds are failing and the package is deeply integrated in our application. Can someone provide a solution please ?

If you need help yet, follow these instructions!

First Add 3 Alpine edge repositories, probably you don't have the last Alpine Version.

sed -i '$a http://nl.alpinelinux.org/alpine/edge/main\nhttp://nl.alpinelinux.org/alpine/edge/community\nhttp://nl.alpinelinux.org/alpine/edge/testing' /etc/apk/repositories

Update your alpine packages

apk update

Be Happy!

apk add s3fs-fuse

Was this page helpful?
0 / 5 - 0 ratings