Files
hakurei/internal/rosa/llvm.go
Ophestra ff98c9ded9
All checks were successful
Test / Create distribution (push) Successful in 50s
Test / Sandbox (push) Successful in 3m2s
Test / ShareFS (push) Successful in 5m10s
Test / Sandbox (race detector) (push) Successful in 5m37s
Test / Hpkg (push) Successful in 5m40s
Test / Hakurei (push) Successful in 6m9s
Test / Hakurei (race detector) (push) Successful in 8m16s
Test / Flake checks (push) Successful in 2m0s
internal/rosa: llvm bootstrap artifacts
This bootstraps the LLVM toolchain across multiple artifacts.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-01-19 02:48:44 +09:00

305 lines
7.2 KiB
Go

package rosa
import (
"runtime"
"slices"
"strconv"
"strings"
"hakurei.app/container/check"
"hakurei.app/internal/pkg"
)
// llvmAttr holds the attributes that will be applied to a new [pkg.Artifact]
// containing a LLVM variant.
type llvmAttr struct {
flags int
// Concatenated with default environment for CMakeAttr.Env.
env []string
// Concatenated with generated entries for CMakeAttr.Cache.
cmake [][2]string
// Override CMakeAttr.Append.
append []string
// Concatenated with default dependencies for CMakeAttr.Extra.
extra []pkg.Artifact
// Concatenated with default fixup for CMakeAttr.Script.
script string
// Passed through to CMakeAttr.Prefix.
prefix *check.Absolute
}
const (
llvmProjectClang = 1 << iota
llvmProjectLld
llvmProjectAll = 1<<iota - 1
llvmRuntimeCompilerRT = 1 << iota
llvmRuntimeLibunwind
llvmRuntimeLibc
llvmRuntimeLibcxx
llvmRuntimeLibcxxABI
llvmAll = 1<<iota - 1
llvmRuntimeAll = llvmAll - (2 * llvmProjectAll) - 1
)
// llvmFlagName resolves a llvmAttr.flags project or runtime flag to its name.
func llvmFlagName(flag int) string {
switch flag {
case llvmProjectClang:
return "clang"
case llvmProjectLld:
return "lld"
case llvmRuntimeCompilerRT:
return "compiler-rt"
case llvmRuntimeLibunwind:
return "libunwind"
case llvmRuntimeLibc:
return "libc"
case llvmRuntimeLibcxx:
return "libcxx"
case llvmRuntimeLibcxxABI:
return "libcxxabi"
default:
panic("invalid flag " + strconv.Itoa(flag))
}
}
// newLLVM returns a [pkg.Artifact] containing a LLVM variant.
func (t Toolchain) newLLVM(variant string, attr *llvmAttr) pkg.Artifact {
const (
version = "21.1.8"
checksum = "8SUpqDkcgwOPsqHVtmf9kXfFeVmjVxl4LMn-qSE1AI_Xoeju-9HaoPNGtidyxyka"
)
if attr == nil {
panic("LLVM attr must be non-nil")
}
var projects, runtimes []string
for i := 1; i < llvmProjectAll; i <<= 1 {
if attr.flags&i != 0 {
projects = append(projects, llvmFlagName(i))
}
}
for i := (llvmProjectAll + 1) << 1; i < llvmRuntimeAll; i <<= 1 {
if attr.flags&i != 0 {
runtimes = append(runtimes, llvmFlagName(i))
}
}
var script, scriptEarly string
cache := [][2]string{
{"CMAKE_BUILD_TYPE", "Release"},
{"LLVM_HOST_TRIPLE", `"${ROSA_TRIPLE}"`},
{"LLVM_DEFAULT_TARGET_TRIPLE", `"${ROSA_TRIPLE}"`},
}
if len(projects) > 0 {
cache = append(cache,
[2]string{"LLVM_ENABLE_PROJECTS", `"${ROSA_LLVM_PROJECTS}"`})
}
if len(runtimes) > 0 {
cache = append(cache,
[2]string{"LLVM_ENABLE_RUNTIMES", `"${ROSA_LLVM_RUNTIMES}"`})
}
cmakeAppend := []string{"llvm"}
if attr.append != nil {
cmakeAppend = attr.append
} else {
cache = append(cache,
[2]string{"LLVM_ENABLE_LIBCXX", "ON"},
[2]string{"LLVM_USE_LINKER", "lld"},
[2]string{"LLVM_INSTALL_BINUTILS_SYMLINKS", "ON"},
[2]string{"LLVM_INSTALL_CCTOOLS_SYMLINKS", "ON"},
)
}
extra := []pkg.Artifact{
t.NewPython(),
t.NewKernelHeaders(),
}
if t == toolchainStage3 {
extra = nil
}
if attr.flags&llvmProjectClang != 0 {
cache = append(cache,
[2]string{"CLANG_DEFAULT_CXX_STDLIB", "libc++"},
[2]string{"CLANG_DEFAULT_RTLIB", "compiler-rt"},
)
}
if attr.flags&llvmProjectLld != 0 {
script += `
ln -s ld.lld /work/system/bin/ld
`
}
if attr.flags&llvmRuntimeCompilerRT != 0 {
if attr.append == nil {
cache = append(cache,
[2]string{"COMPILER_RT_USE_LLVM_UNWINDER", "ON"})
}
}
if attr.flags&llvmRuntimeLibunwind != 0 {
cache = append(cache,
[2]string{"LIBUNWIND_USE_COMPILER_RT", "ON"})
}
if attr.flags&llvmRuntimeLibcxx != 0 {
cache = append(cache,
[2]string{"LIBCXX_HAS_MUSL_LIBC", "ON"},
[2]string{"LIBCXX_USE_COMPILER_RT", "ON"},
)
if t > toolchainStage3 {
// libcxxabi fails to compile if c++ headers not prefixed in /usr
// is found by the compiler, and doing this is easier than
// overriding CXXFLAGS; not using mv here to avoid chown failures
scriptEarly += `
cp -r /system/include /usr/include && rm -rf /system/include
`
}
}
if attr.flags&llvmRuntimeLibcxxABI != 0 {
cache = append(cache,
[2]string{"LIBCXXABI_USE_COMPILER_RT", "ON"},
[2]string{"LIBCXXABI_USE_LLVM_UNWINDER", "ON"},
)
}
return t.NewViaCMake("llvm", version, variant, pkg.NewHTTPGetTar(
nil, "https://github.com/llvm/llvm-project/archive/refs/tags/"+
"llvmorg-"+version+".tar.gz",
mustDecode(checksum),
pkg.TarGzip,
), &CMakeAttr{
Cache: slices.Concat(cache, attr.cmake),
Append: cmakeAppend,
Extra: slices.Concat(attr.extra, extra),
Prefix: attr.prefix,
Env: slices.Concat([]string{
"ROSA_LLVM_PROJECTS=" + strings.Join(projects, ";"),
"ROSA_LLVM_RUNTIMES=" + strings.Join(runtimes, ";"),
}, attr.env),
ScriptEarly: scriptEarly, Script: script + attr.script,
})
}
// NewLLVM returns LLVM toolchain across multiple [pkg.Artifact].
func (t Toolchain) NewLLVM() (musl, compilerRT, runtimes, clang pkg.Artifact) {
var target string
switch runtime.GOARCH {
case "386", "amd64":
target = "X86"
default:
panic("unsupported target " + runtime.GOARCH)
}
minimalDeps := [][2]string{
{"LLVM_ENABLE_ZLIB", "OFF"},
{"LLVM_ENABLE_ZSTD", "OFF"},
{"LLVM_ENABLE_LIBXML2", "OFF"},
}
compilerRT = t.newLLVM("compiler-rt", &llvmAttr{
env: []string{
ldflags(false),
},
cmake: [][2]string{
// libc++ not yet available
{"CMAKE_CXX_COMPILER_TARGET", ""},
{"COMPILER_RT_BUILD_BUILTINS", "ON"},
{"COMPILER_RT_DEFAULT_TARGET_ONLY", "ON"},
{"LLVM_ENABLE_PER_TARGET_RUNTIME_DIR", "ON"},
// does not work without libunwind
{"COMPILER_RT_BUILD_CTX_PROFILE", "OFF"},
{"COMPILER_RT_BUILD_LIBFUZZER", "OFF"},
{"COMPILER_RT_BUILD_MEMPROF", "OFF"},
{"COMPILER_RT_BUILD_PROFILE", "OFF"},
{"COMPILER_RT_BUILD_SANITIZERS", "OFF"},
{"COMPILER_RT_BUILD_XRAY", "OFF"},
},
append: []string{"compiler-rt"},
extra: []pkg.Artifact{t.NewMusl(&MuslAttr{
Headers: true,
Env: []string{
"CC=clang",
},
})},
script: `
mkdir -p "${ROSA_INSTALL_PREFIX}/lib/clang/21/lib/"
ln -s \
"../../../${ROSA_TRIPLE}" \
"${ROSA_INSTALL_PREFIX}/lib/clang/21/lib/"
ln -s \
"clang_rt.crtbegin-$(uname -m).o" \
"${ROSA_INSTALL_PREFIX}/lib/${ROSA_TRIPLE}/crtbeginS.o"
ln -s \
"clang_rt.crtend-$(uname -m).o" \
"${ROSA_INSTALL_PREFIX}/lib/${ROSA_TRIPLE}/crtendS.o"
`,
})
musl = t.NewMusl(&MuslAttr{
Extra: []pkg.Artifact{compilerRT},
Env: []string{
ldflags(false),
"CC=clang",
"LIBCC=/system/lib/clang/21/lib/" +
triplet() + "/libclang_rt.builtins.a",
"AR=ar",
"RANLIB=ranlib",
},
})
runtimes = t.newLLVM("runtimes", &llvmAttr{
env: []string{
ldflags(false),
},
flags: llvmRuntimeLibunwind | llvmRuntimeLibcxx | llvmRuntimeLibcxxABI,
cmake: slices.Concat([][2]string{
// libc++ not yet available
{"CMAKE_CXX_COMPILER_WORKS", "ON"},
{"LIBCXX_HAS_ATOMIC_LIB", "OFF"},
{"LIBCXXABI_HAS_CXA_THREAD_ATEXIT_IMPL", "OFF"},
}, minimalDeps),
append: []string{"runtimes"},
extra: []pkg.Artifact{
compilerRT,
musl,
},
})
clang = t.newLLVM("clang", &llvmAttr{
flags: llvmProjectClang | llvmProjectLld,
env: []string{
"CFLAGS=" + cflags,
"CXXFLAGS=" + cxxflags(),
ldflags(false),
},
cmake: slices.Concat([][2]string{
{"LLVM_TARGETS_TO_BUILD", target},
{"CMAKE_CROSSCOMPILING", "OFF"},
{"CXX_SUPPORTS_CUSTOM_LINKER", "ON"},
}, minimalDeps),
extra: []pkg.Artifact{
musl,
compilerRT,
runtimes,
},
})
return
}