Skip to content

Commit 45106d7

Browse files
Merge pull request #5 from ClangBuiltLinux/llvm-bootstrap
Initial statically linked clang image
2 parents 5dce240 + 47afa6e commit 45106d7

File tree

12 files changed

+414
-0
lines changed

12 files changed

+414
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
*.swp
2+
3+
llvm-project/llvm-project-*
4+
llvm-project/clang

linux/Dockerfile

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
FROM alpine:edge
2+
3+
# TODO: change to container registry
4+
COPY --from=clangbuiltlinux/llvm-project:latest llvm-project/llvm/build/bin /usr/local/bin
5+
COPY --from=clangbuiltlinux/llvm-project:latest /usr/local/lib /usr/local/lib
6+
COPY --from=clangbuiltlinux/llvm-project:latest /usr/local/include /usr/local/include
7+
RUN cd /usr/lib/ && \
8+
for library in libc++abi.so.1 libc++.a libc++abi.a libc++.so.1 libunwind.so.1 libunwind.a; \
9+
do ln -s "/usr/local/lib/x86_64-alpine-linux-musl/${library}" . ; \
10+
done
11+
12+
RUN apk add wget
13+
14+
RUN wget --no-clobber --no-verbose \
15+
https://git.kernel.org/torvalds/t/linux-5.18-rc6.tar.gz && \
16+
tar xvf linux-5.18-rc6.tar.gz
17+
18+
RUN apk add make musl-dev rsync
19+
20+
RUN make -C linux-5.18-rc6 INSTALL_HDR_PATH=/sysroot/usr LLVM=1 -j$(nproc) headers_install
21+
RUN apk del rsync musl-dev make wget

linux/build.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#!/usr/bin/env sh
2+
docker build --tag clangbuiltlinux/linux:latest .

llvm-project/Dockerfile

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
FROM alpine:edge AS source
2+
RUN wget --no-clobber \
3+
https://github.com/llvm/llvm-project/releases/download/llvmorg-14.0.1/llvm-project-14.0.1.src.tar.xz
4+
5+
# Stage one
6+
FROM alpine:edge AS stage_one
7+
8+
COPY --from=source llvm-project-14.0.1.src.tar.xz /
9+
RUN tar xf llvm-project-14.0.1.src.tar.xz && \
10+
mv llvm-project-14.0.1.src llvm-project
11+
12+
# Install the stage zero compiler; the prebuilt clang from alpine.
13+
# _Unwind_Resume issues with llvm-libunwind? Cmake can't find it either?
14+
#RUN apk add --no-cache clang lld compiler-rt musl-dev
15+
RUN apk add --no-cache clang lld llvm13 musl-dev
16+
17+
# libcxxabi depends on zlib-dev!
18+
# compiler-rt depends on libexecinfo-dev! (unless gwp-asan is disabled)
19+
# TODO: libxml2?
20+
# llvm cmake throws lots of warnings without git
21+
RUN apk add --no-cache cmake ninja python3 zlib-dev git
22+
23+
# This seems like a mistake that clang++ in alpine cannot compile C++ hello
24+
# world; the libstdc++ headers are distributed with g++ in Alpine rather than
25+
# the libstdc++ package. So we need to install g++ on Alpine to get the
26+
# libstdc++ headers, but this will also pull in many dependencies like gcc and
27+
# GNU binutils. Let's remove the binaries installed by g++, gcc, and binutils
28+
# while keeping the headers to ensure an additional level of hermiticity.
29+
RUN apk add --no-cache g++ && \
30+
apk info -L g++ gcc | grep "^usr/bin" | xargs rm && \
31+
ln -sf /usr/bin/clang /usr/bin/cc && \
32+
apk info -L binutils | \
33+
grep -e "^usr/bin" -e "^usr/$(clang -print-target-triple)" | xargs rm && \
34+
ln -sf /usr/bin/ld.lld /usr/bin/ld
35+
36+
# Test stage 0 clang and clang++.
37+
COPY hello.c hello.cpp /
38+
RUN clang hello.c && ./a.out && \
39+
clang++ hello.cpp && ./a.out
40+
41+
COPY stage1.cmake llvm-project/.
42+
ARG LLVM_BUILD_DIR=llvm-project/llvm/build
43+
RUN cmake \
44+
-B ${LLVM_BUILD_DIR} \
45+
-C llvm-project/stage1.cmake \
46+
-S llvm-project/llvm \
47+
-G Ninja
48+
49+
# While these could be one single command, generally for development it's very
50+
# painful when one of many specified targets fail as then nothing gets cached
51+
# by docker. Break these into distinct commands to optimize for development
52+
# iteration speed.
53+
RUN ninja -C ${LLVM_BUILD_DIR} install-builtins
54+
RUN ninja -C ${LLVM_BUILD_DIR} install-compiler-rt
55+
RUN ninja -C ${LLVM_BUILD_DIR} install-unwind
56+
RUN ninja -C ${LLVM_BUILD_DIR} install-cxxabi
57+
# Necessary for libcxx
58+
RUN apk add --no-cache linux-headers
59+
RUN ninja -C ${LLVM_BUILD_DIR} install-cxx
60+
RUN ninja -C ${LLVM_BUILD_DIR} install-clang install-lld
61+
RUN ninja -C ${LLVM_BUILD_DIR} install-clang-resource-headers
62+
RUN ninja -C ${LLVM_BUILD_DIR} install-llvm-ar
63+
64+
### END STAGE1
65+
### START STAGE2
66+
67+
# STAGE2 goal, build WITHOUT apk add
68+
FROM alpine:edge as stage_two
69+
COPY --from=stage_one /usr/local/bin /usr/local/bin
70+
COPY --from=stage_one /usr/local/lib /usr/local/lib
71+
COPY --from=stage_one /usr/local/include /usr/local/include
72+
COPY hello.c hello.cpp /
73+
74+
# TODO: I think we want to use CMAKE_INSTALL_LIBDIR, CMAKE_INSTALL_PREFIX, and
75+
# DESTDIR to start building up the sysroot...then we could avoid this
76+
# symlinking nonsense at the start of other builds.
77+
RUN cd /usr/lib/ && \
78+
for library in libc++abi.so.1 libc++.a libc++abi.a libc++.so.1 libunwind.so.1 libunwind.a; \
79+
do ln -s "/usr/local/lib/x86_64-alpine-linux-musl/${library}" . ; \
80+
done
81+
82+
# The stage 1 build of clang/lld/other binaries still depends on libstdc++.
83+
RUN apk add --no-cache libstdc++ musl-dev
84+
85+
# Pause for a quick sanity check
86+
# Stage 1 clang only defaulted-on compiler-rt and lld
87+
RUN clang++ hello.cpp \
88+
--stdlib=libc++ \
89+
--rtlib=compiler-rt \
90+
--unwindlib=libunwind \
91+
-fuse-ld=lld && \
92+
./a.out
93+
94+
RUN clang++ hello.cpp \
95+
--stdlib=libc++ \
96+
--unwindlib=libunwind && \
97+
./a.out
98+
99+
RUN clang++ hello.cpp \
100+
--stdlib=libc++ \
101+
--unwindlib=libunwind \
102+
-static \
103+
-lc++abi && \
104+
./a.out
105+
106+
COPY --from=source llvm-project-14.0.1.src.tar.xz /
107+
RUN tar xf llvm-project-14.0.1.src.tar.xz && \
108+
mv llvm-project-14.0.1.src llvm-project && \
109+
mkdir llvm-project/llvm/build
110+
111+
RUN apk add --no-cache cmake ninja python3 zlib-dev git zlib-static
112+
113+
# Cmake will prioritize .so over .a. :(
114+
RUN rm /lib/libz.so
115+
116+
COPY stage2.cmake llvm-project/.
117+
ARG LLVM_BUILD_DIR=llvm-project/llvm/build
118+
RUN cmake \
119+
-B ${LLVM_BUILD_DIR} \
120+
-C llvm-project/stage2.cmake \
121+
-S llvm-project/llvm \
122+
-G Ninja
123+
124+
# TODO: build the rest of llvm binutils.
125+
RUN ninja -C ${LLVM_BUILD_DIR} install-clang install-lld
126+
RUN ninja -C ${LLVM_BUILD_DIR} install-clang-resource-headers
127+
128+
# Pause for a quick sanity check
129+
# python3 and cmake both depend on libstdc++
130+
RUN apk del libstdc++ cmake ninja python3 zlib-dev git zlib-static
131+
#RUN apk del libatomic
132+
# Stage 2 clang should have intended defaults
133+
RUN clang hello.c && ./a.out && \
134+
clang hello.c -static && ./a.out && \
135+
clang++ hello.cpp && ./a.out && \
136+
clang++ hello.cpp -static -lc++abi && ./a.out

llvm-project/build.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/usr/bin/env sh
2+
docker build --tag clangbuiltlinux/llvm-project:latest .
3+
docker cp llvm-project:/usr/local/bin/clang-14 clang

llvm-project/hello.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#include <stdio.h>
2+
int main() { puts("hello world"); }

llvm-project/hello.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#include <iostream>
2+
int main() { std::cout << "Hello world" << std::endl; }

llvm-project/stage1.cmake

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# Please try to keep these cmake variables alphabetically sorted.
2+
3+
# Enable optimizations, as opposed to a debug build.
4+
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "")
5+
6+
# Use Alpine's clang and clang++ from their clang package as the stage0
7+
# compilers.
8+
set(CMAKE_CXX_COMPILER "/usr/bin/clang++" CACHE FILEPATH "")
9+
set(CMAKE_C_COMPILER "/usr/bin/clang" CACHE FILEPATH "")
10+
11+
# Set the default target triple to match the host.
12+
# TODO: passing in the value of $(clang -print-multiarch) causes failures.
13+
# It seems that alpine clang's default target triple is x86_64-linux-gnu.
14+
# Perhaps missing alpine in the triple causes some incompatibility?
15+
set(LLVM_DEFAULT_TARGET_TRIPLE "x86_64-alpine-linux-musl" CACHE STRING "")
16+
17+
# Use Alpine's lld as the stage0 linker to link everything.
18+
set(LLVM_ENABLE_LLD ON CACHE BOOL "")
19+
20+
# For stage 1, just build clang and LLD, which are used by the "runtimes build"
21+
# to build the runtime libraries of LLVM.
22+
set(LLVM_ENABLE_PROJECTS "clang;lld;" CACHE STRING "")
23+
24+
# LLVM "runtimes" build.
25+
set(LLVM_ENABLE_RUNTIMES "compiler-rt;libcxx;libcxxabi;libunwind;" CACHE STRING "")
26+
27+
# FORCE_ON causes the build to fail if zlib is not found in the environment
28+
# during configuration, rather than much later during link.
29+
set(LLVM_ENABLE_ZLIB "FORCE_ON" CACHE STRING "")
30+
31+
# Just build stage1 to target the host. It's not the end product, so it won't
32+
# be able to target all of the kernel targets we can build.
33+
set(LLVM_TARGETS_TO_BUILD "host;" CACHE STRING "")
34+
35+
# Set clang's default -fuse-ld= to lld.
36+
set(CLANG_DEFAULT_LINKER "lld" CACHE STRING "")
37+
38+
# Set clang's default --rtlib= to compiler-rt.
39+
set(CLANG_DEFAULT_RTLIB "compiler-rt" CACHE STRING "")
40+
41+
# Disable arc migrate. We don't use that, ever.
42+
set(CLANG_ENABLE_ARCMT OFF CACHE BOOL "")
43+
44+
# Disable static analyzer. Don't need it for stage1.
45+
set(CLANG_ENABLE_STATIC_ANALYZER OFF CACHE BOOL "")
46+
47+
# Disable plugin support. Don't need it, ever.
48+
set(CLANG_PLUGIN_SUPPORT OFF CACHE BOOL "")
49+
50+
# The compiler builtins are necessary.
51+
set(COMPILER_RT_BUILD_BUILTINS ON CACHE BOOL "")
52+
53+
# GWP ASAN fails to build without libexecinfo-dev. Don't need it for stage1.
54+
set(COMPILER_RT_BUILD_GWP_ASAN OFF CACHE BOOL "")
55+
56+
# Don't need libfuzzer, ever.
57+
set(COMPILER_RT_BUILD_LIBFUZZER OFF CACHE BOOL "")
58+
59+
# Don't need memprof, ever.
60+
set(COMPILER_RT_BUILD_MEMPROF OFF CACHE BOOL "")
61+
62+
# Don't need ORC, ever.
63+
set(COMPILER_RT_BUILD_ORC OFF CACHE BOOL "")
64+
65+
# Disable profiling support. Not necessary for stage1.
66+
set(COMPILER_RT_BUILD_PROFILE OFF CACHE BOOL "")
67+
68+
# Disable sanitizer support. Not necessary for stage1.
69+
set(COMPILER_RT_BUILD_SANITIZERS OFF CACHE BOOL "")
70+
71+
# Don't need xray.
72+
set(COMPILER_RT_BUILD_XRAY OFF CACHE BOOL "")
73+
74+
# Compiler-rt is meant as a replacement to libgcc.
75+
set(COMPILER_RT_HAS_GCC_S_LIB OFF CACHE BOOL "")
76+
77+
# Who needs docs anyways? Can't RTFM if there is no FM.
78+
set(LIBUNWIND_INCLUDE_DOCS OFF CACHE BOOL "")
79+
80+
# We don't plan to run the tests, disable them.
81+
set(LIBUNWIND_INCLUDE_TESTS OFF CACHE BOOL "")
82+
83+
# We need the headers available when we install-libunwind.
84+
set(LIBUNWIND_INSTALL_HEADERS ON CACHE BOOL "")
85+
86+
# Libunwind should use compiler-rt rather than libgcc.
87+
set(LIBUNWIND_USE_COMPILER_RT ON CACHE BOOL "")
88+
89+
# This is needed to break the cycle between libc, libc++, and libunwind.
90+
set(LIBCXXABI_ENABLE_STATIC_UNWINDER ON CACHE BOOL "")
91+
92+
# We don't plan to run the tests; don't build them.
93+
set(LIBCXXABI_INCLUDE_TESTS OFF CACHE BOOL "")
94+
95+
# We want libc++abi to use compiler-rt.
96+
set(LIBCXXABI_USE_COMPILER_RT ON CACHE BOOL "")
97+
98+
# We want libc++abi to use LLVM's libunwind.
99+
set(LIBCXXABI_USE_LLVM_UNWINDER ON CACHE BOOL "")
100+
101+
# We want libc++ to use libc++abi.
102+
set(LIBCXX_CXX_ABI libcxxabi CACHE BOOL "")
103+
104+
# We don't need C++ experimental APIs.
105+
set(LIBCXX_ENABLE_EXPERIMENTAL_LIBRARY OFF CACHE BOOL "")
106+
107+
# We want libc++ to use compiler-rt. If we don't disable libatomic explicitly,
108+
# libc++ will try to link against it.
109+
set(LIBCXX_HAS_ATOMIC_LIB OFF CACHE BOOL "")
110+
set(LIBCXX_HAS_GCC_LIB OFF CACHE BOOL "")
111+
set(LIBCXX_HAS_GCC_S_LIB OFF CACHE BOOL "")
112+
113+
# The C++ standard library requires the C library. libc++ assumes that the
114+
# default C library is glibc due to the prevalence, however, as we are using
115+
# musl, we need to indicate that we are using musl to prevent failures due to
116+
# incorrect assumptions, particularly about locales.
117+
set(LIBCXX_HAS_MUSL_LIBC ON CACHE BOOL "")
118+
119+
# We don't plan to run the benchmarks, so don't build them.
120+
set(LIBCXX_INCLUDE_BENCHMARKS OFF CACHE BOOL "")
121+
122+
# Don't build docs.
123+
set(LIBCXX_INCLUDE_DOCS OFF CACHE BOOL "")
124+
125+
# Don't build tests.
126+
set(LIBCXX_INCLUDE_TESTS OFF CACHE BOOL "")
127+
128+
# We want libc++ to use compiler-rt.
129+
set(LIBCXX_USE_COMPILER_RT ON CACHE BOOL "")

llvm-project/stage2.cmake

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Please try to keep these cmake variables alphabetically sorted.
2+
3+
# Enable optimizations, as opposed to a debug build.
4+
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "")
5+
6+
# Use stage1's clang to bootstrap stage2, as opposed to Alpine's clang (which
7+
# shouldn't be installed on the system at this point, but would be installed to
8+
# /usr/bin/clang).
9+
set(CMAKE_CXX_COMPILER "/usr/local/bin/clang++" CACHE FILEPATH "")
10+
11+
# Use libc++ from stage1.
12+
# We could not set CLANG_DEFAULT_CXX_STDLIB in stage1 because Alpine doesn't
13+
# distribute libc++ ATM.
14+
# Even though we later will set LLVM_ENABLE_LIBCXX, cmake will fail thinking
15+
# that "The C++ compiler is not able to compile a simple test program." As with
16+
# stage1, this is an unfortunate effect of Alpine packaging the stdlibc++
17+
# headers in the g++ package, rather than the libstdc++ package. In stage1, we
18+
# used those headers; in stage2 we do not.
19+
set(CMAKE_CXX_FLAGS "--stdlib=libc++" CACHE STRING "")
20+
21+
# See above comment for CMAKE_CXX_COMPILER.
22+
set(CMAKE_C_COMPILER "/usr/local/bin/clang" CACHE FILEPATH "")
23+
24+
# Use libunwind from stage1.
25+
# Statically link resulting executable.
26+
set(CMAKE_EXE_LINKER_FLAGS "--unwindlib=libunwind -static -lc++abi" CACHE STRING "")
27+
set(CMAKE_SHARED_LINKER_FLAGS "--unwindlib=libunwind" CACHE STRING "")
28+
29+
# Set the default target triple to match the host.
30+
# TODO: passing in the value of $(clang -print-multiarch) causes failures.
31+
# It seems that alpine clang's default target triple is x86_64-linux-gnu.
32+
# Perhaps missing alpine in the triple causes some incompatibility?
33+
set(LLVM_DEFAULT_TARGET_TRIPLE x86_64-alpine-linux-musl CACHE STRING "")
34+
35+
# Use libc++ from stage1.
36+
# TODO: is CMAKE_CXX_FLAGS still necessary if this is set?
37+
set(LLVM_ENABLE_LIBCXX ON CACHE BOOL "")
38+
39+
# Use lld from stage1.
40+
set(LLVM_ENABLE_LLD ON CACHE BOOL "")
41+
42+
# Just build clang and lld for now.
43+
set(LLVM_ENABLE_PROJECTS "clang;lld" CACHE STRING "")
44+
45+
# FORCE_ON causes the build to fail if zlib is not found in the environment
46+
# during configuration, rather than much later during link.
47+
set(LLVM_ENABLE_ZLIB "FORCE_ON" CACHE STRING "")
48+
49+
# This is necessary to statically link libc++ into clang.
50+
set(LLVM_STATIC_LINK_CXX_STDLIB "1" CACHE STRING "")
51+
52+
# Just build support for x86 for now.
53+
# TODO: change this to host when adding more stages.
54+
set(LLVM_TARGETS_TO_BUILD "X86;" CACHE STRING "")
55+
56+
# Set clang's default --stdlib= to libc++.
57+
set(CLANG_DEFAULT_CXX_STDLIB "libc++" CACHE STRING "")
58+
59+
# Set clang's default -fuse-ld= to lld.
60+
set(CLANG_DEFAULT_LINKER "lld" CACHE STRING "")
61+
62+
# Have clang default to llvm-objcopy.
63+
set(CLANG_DEFAULT_OBJCOPY "llvm-objcopy" CACHE STRING "")
64+
65+
# Set clang's default --rtlib= to compiler-rt.
66+
set(CLANG_DEFAULT_RTLIB "compiler-rt" CACHE STRING "")
67+
68+
# Set clang's default --unwindlib= to libunwind.
69+
set(CLANG_DEFAULT_UNWINDLIB "libunwind" CACHE STRING "")
70+
71+
# Disable arc migrate. We don't use that, ever.
72+
set(CLANG_ENABLE_ARCMT OFF CACHE BOOL "")
73+
74+
# Disable static analyzer. Don't need it for stage2.
75+
set(CLANG_ENABLE_STATIC_ANALYZER OFF CACHE BOOL "")
76+
77+
# Disable plugin support. Don't need it, ever.
78+
set(CLANG_PLUGIN_SUPPORT OFF CACHE BOOL "")

0 commit comments

Comments
 (0)