Skip to content

Instantly share code, notes, and snippets.

@tylerchr
Created July 21, 2018 01:37
Show Gist options
  • Save tylerchr/15a74b05944cfb90729db6a51265b6c9 to your computer and use it in GitHub Desktop.
Save tylerchr/15a74b05944cfb90729db6a51265b6c9 to your computer and use it in GitHub Desktop.
Compiling V8 for alpine (link against musl)

It turns out that compiling V8 for use in alpine (i.e., linked with musl) is a real pain.

After reading what must have been the entire contents of the Internet on the subject (see list below) I came up with the following process that seems to do the trick pretty cleanly.

At the heart of this strategy are two tricks:

  1. It turns out you actually can compile GN in alpine, but only if you build it from the newly-split gn.googlesource.com repository rather than the Chromium repo.

  2. The depot_tools build helpers don't seem to work in alpine, but no matter! This runs them in a debian:9 container to fetch all the code, and then copies the results to an alpine container for compilation.

After these breakthroughs it's a relatively straightforward procedure. Included in this gist is a multi-stage Dockerfile that shows a pretty easy way to build a musl-linked copy of V8.

Prior Work

#
# Building V8 for alpine is a real pain. We have to compile from source, because it has to be
# linked against musl, and we also have to recompile some of the build tools as the official
# build workflow tends to assume glibc by including vendored tools that link against it.
#
# The general strategy is this:
#
# 1. Build GN for alpine (this is a build dependency)
# 2. Use depot_tools to fetch the V8 source and dependencies (needs glibc)
# 3. Build V8 for alpine
# 4. Make warez
#
#
# STEP 1
# Build GN for alpine
#
FROM alpine:latest as gn-builder
# This is the GN commit that we want to build. Most commits will probably build just fine but
# this happened to be the latest commit when I did this.
ARG GN_COMMIT=d7111cb6877187d1f378bd231e14ffdd5fdd87ae
RUN \
apk add --update --virtual .gn-build-dependencies \
alpine-sdk \
binutils-gold \
clang \
curl \
git \
llvm4 \
ninja \
python \
tar \
xz \
# Two quick fixes: we need the LLVM tooling in $PATH, and we
# also have to use gold instead of ld.
&& PATH=$PATH:/usr/lib/llvm4/bin \
&& cp -f /usr/bin/ld.gold /usr/bin/ld \
# Clone and build gn
&& git clone https://gn.googlesource.com/gn /tmp/gn \
&& git -C /tmp/gn checkout ${GN_COMMIT} \
&& cd /tmp/gn \
&& python build/gen.py --no-sysroot \
&& ninja -C out \
&& cp -f /tmp/gn/out/gn /usr/local/bin/gn \
# Remove build dependencies and temporary files
&& apk del .gn-build-dependencies \
&& rm -rf /tmp/* /var/tmp/* /var/cache/apk/*
#
# STEP 2
# Use depot_tools to fetch the V8 source and dependencies
#
# The depot_tools scripts have a hard dependency on glibc (or at least a soft one that I didn't
# bother figuring out). Fortunately we only need it to actually download the source and its dependencies
# so we can do this in a place with glibc, and then pass the results on to an alpine builder.
#
FROM debian:9 as source
# The V8 version we want to use. It's assumed that this will be a version tag, but it's just
# used as "git commit $V8_VERSION" so anything that git can resolve will work.
ARG V8_VERSION=6.7.77
RUN \
set -x && \
apt-get update && \
apt-get install -y \
git \
curl \
python && \
# Clone depot_tools
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git /tmp/depot_tools && \
PATH=$PATH:/tmp/depot_tools && \
# fetch V8
cd /tmp && \
fetch v8 && \
cd /tmp/v8 && \
git checkout ${V8_VERSION} && \
gclient sync && \
# cleanup
apt-get remove --purge -y \
git \
curl \
python && \
apt-get autoremove -y && \
rm -rf /var/lib/apt/lists/*
#
# STEP 3
# Build V8 for alpine
#
FROM alpine:latest as v8
COPY --from=source /tmp/v8 /tmp/v8
COPY --from=gn-builder /usr/local/bin/gn /tmp/v8/buildtools/linux64/gn
RUN \
apk add --update --virtual .v8-build-dependencies \
curl \
g++ \
gcc \
glib-dev \
icu-dev \
libstdc++ \
linux-headers \
make \
ninja \
python \
tar \
xz \
# Configure our V8 build
&& cd /tmp/v8 && \
./tools/dev/v8gen.py x64.release -- \
binutils_path=\"/usr/bin\" \
target_os=\"linux\" \
target_cpu=\"x64\" \
v8_target_cpu=\"x64\" \
v8_enable_future=true \
is_official_build=true \
is_component_build=false \
is_cfi=false \
is_clang=false \
use_custom_libcxx=false \
use_sysroot=false \
use_gold=false \
use_allocator_shim=false \
treat_warnings_as_errors=false \
symbol_level=0 \
strip_debug_info=true \
v8_use_external_startup_data=false \
v8_enable_i18n_support=false \
v8_enable_gdbjit=false \
v8_static_library=true \
v8_experimental_extra_library_files=[] \
v8_extra_library_files=[] \
# Build V8
&& ninja -C out.gn/x64.release -j $(getconf _NPROCESSORS_ONLN) \
# Brag
&& find /tmp/v8/out.gn/x64.release -name '*.a' \
# clean up
&& apk del .v8-build-dependencies
#
# STEP 4
# Build something that links against V8.
#
# This isn't really a step, but it uses the built V8 in a Go program that links against it to show
# that it worked.
#
FROM golang:1.10-alpine
COPY --from=v8 /tmp/v8/include /tmp/v8/include
COPY --from=v8 /tmp/v8/out.gn/x64.release/obj /tmp/v8/lib
RUN \
apk add --update git alpine-sdk && \
CGO_CXXFLAGS="-I/tmp/v8/include" CGO_LDFLAGS="-L/tmp/v8/lib" go get github.com/augustoroman/v8/...
CMD ["/go/bin/v8-runjs"]
@rqelibari
Copy link

rqelibari commented Dec 30, 2018

@tylerchr: Thank you very much for sharing the result of your efforts!
This is exactly what I was looking for in order to build rq on Alpine linux.

Docker Hub version

As this script is rather long, I think a pre-build image on Docker Hub would be much more favorable.
Like that people can reference the pre-build image directly in their Dockerfile instead of copy pasting this script into theirs.

I order to not bother you too much I did this over here. Hope you like it :-)

EDIT: There is already a similar script made by alexmasterov (alexmasterov/aline-v8).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment