diff options
| author | Ophestra <cat@gensokyo.uk> | 2026-10-22 01:56:23 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2026-10-22 11:10:01 +0900 |
| commit | 9dfbf95db02054204defbe422099c02b790a19fc (patch) | |
| tree | f8c318ebfc5c824b2eb5e7a8aac3b835ee05a4ed | |
| parent | 29f86b3446c9fe35d2a359df45471abdaa35cea4 (diff) | |
internal/rosa: initial cross-compilation support
This successfully cross-compiles the toolchain. More work is needed to
support cross-compilation on all native helpers and existing packages.
Signed-off-by: Ophestra <cat@gensokyo.uk>
| -rw-r--r-- | cmd/mbf/main.go | 85 | ||||
| -rw-r--r-- | internal/rosa/azalea/evaluate.go | 3 | ||||
| -rw-r--r-- | internal/rosa/cmake.go | 15 | ||||
| -rw-r--r-- | internal/rosa/llvm.go | 129 | ||||
| -rw-r--r-- | internal/rosa/llvm_test.go | 2 | ||||
| -rw-r--r-- | internal/rosa/make.go | 16 | ||||
| -rw-r--r-- | internal/rosa/package/kernel/package.az | 8 | ||||
| -rw-r--r-- | internal/rosa/package/llvm/package.az | 52 | ||||
| -rw-r--r-- | internal/rosa/package/musl/package.az | 15 | ||||
| -rw-r--r-- | internal/rosa/rosa.go | 100 | ||||
| -rw-r--r-- | internal/rosa/state.go | 50 |
11 files changed, 371 insertions, 104 deletions
diff --git a/cmd/mbf/main.go b/cmd/mbf/main.go index dd5ee624..9c41e3ff 100644 --- a/cmd/mbf/main.go +++ b/cmd/mbf/main.go @@ -742,24 +742,39 @@ func main() { ) } - c.NewCommand( - "all", - command.UsageInternal, - func([]string) error { - all := rosa.CollectAll() - a := make(pkg.Collect, len(all)) - for i, h := range all { - _, a[i] = rosa.Load(h) - } - return cm.Do(func(cache *pkg.Cache) (err error) { - _, _, err = cache.Cure(&a) - if pkg.IsCollected(err) { - err = nil + { + var flagCross string + c.NewCommand( + "all", + command.UsageInternal, + func([]string) error { + s := &builtin + if flagCross != "" { + s = s.NewCross(flagCross) + if !s.HasStageEarly() { + return pkg.UnsupportedArchError(flagCross) + } } - return - }) - }, - ) + + all := s.CollectAll() + a := make(pkg.Collect, len(all)) + for i, h := range all { + _, a[i] = rosa.Load(h) + } + return cm.Do(func(cache *pkg.Cache) (err error) { + _, _, err = cache.Cure(&a) + if pkg.IsCollected(err) { + err = nil + } + return + }) + }, + ).Flag( + &flagCross, + "cross", command.StringFlag(""), + "Target architecture (cross-compilation)", + ) + } { var ( @@ -768,6 +783,7 @@ func main() { flagExport string flagRemote bool flagNoReply bool + flagCross string flagFaults bool flagPop bool flagRebuild bool @@ -793,6 +809,14 @@ func main() { t -= 1 } + s := &builtin + if flagCross != "" { + s = s.NewCross(flagCross) + if !s.HasStageEarly() { + return pkg.UnsupportedArchError(flagCross) + } + } + if flagGentoo != "" { var checksum pkg.Checksum if len(flagChecksum) != 0 { @@ -800,12 +824,12 @@ func main() { return err } } - builtin.SetGentooStage3(flagGentoo, checksum) + s.SetGentooStage3(flagGentoo, checksum) t -= 3 // magic number to discourage misuse } - _, a := rosa.LoadAt(t, rosa.ArtifactH(unique.Make(args[0]))) + _, a := s.New(t).Load(rosa.ArtifactH(unique.Make(args[0]))) if a == nil { return fmt.Errorf("unknown artifact %q", args[0]) } @@ -964,6 +988,10 @@ func main() { "no-reply", command.BoolFlag(false), "Do not receive a reply from the daemon", ).Flag( + &flagCross, + "cross", command.StringFlag(""), + "Target architecture (cross-compilation)", + ).Flag( &flagBoot, "boot", command.BoolFlag(false), "Build on the stage0 toolchain", @@ -1111,20 +1139,23 @@ func main() { "Remove identifiers not reachable by loaded packages", func([]string) error { return cm.Do(func(cache *pkg.Cache) error { - t := rosa.New().Std() - handles := t.CollectAll() + s := rosa.New() + s.DropCaches(runtime.GOARCH, s.Flags()) + handles := s.Std().CollectAll() - flags := t.Flags() - a := t.Append(nil, handles...) + flags := s.Flags() + a := s.Std().Append(nil, handles...) for arch := range rosa.Arches(nil) { if arch == runtime.GOARCH { continue } - t.DropCaches(arch, rosa.OptSkipCheck) - a = t.Append(a, handles...) - t.DropCaches(arch, flags) - a = t.Append(a, handles...) + s.DropCaches(arch, rosa.OptSkipCheck) + a = s.Std().Append(a, handles...) + s.DropCaches(arch, flags) + a = s.Std().Append(a, handles...) + s.DropCaches(runtime.GOARCH, flags) + a = s.NewCross(arch).Std().Append(a, handles...) } ids, checksums, err := cache.Clean( diff --git a/internal/rosa/azalea/evaluate.go b/internal/rosa/azalea/evaluate.go index cb5e8d12..59702062 100644 --- a/internal/rosa/azalea/evaluate.go +++ b/internal/rosa/azalea/evaluate.go @@ -206,6 +206,8 @@ var ( IdentTools = unique.Make(Ident("tools")) // IdentInputs has the same semantics as [IdentTools]. IdentInputs = unique.Make(Ident("inputs")) + // IdentCross has the same semantics as [IdentTools]. + IdentCross = unique.Make(Ident("cross")) // IdentRuntime has the same semantics as [IdentTools]. IdentRuntime = unique.Make(Ident("runtime")) // IdentExtra has the same semantics as [IdentTools]. @@ -361,6 +363,7 @@ func evaluateAny(d PF, s []Frame, expr, rp any) bool { for _, special := range [...]unique.Handle[Ident]{ IdentTools, IdentInputs, + IdentCross, IdentRuntime, IdentExtra, } { diff --git a/internal/rosa/cmake.go b/internal/rosa/cmake.go index 2a262e6e..c037a239 100644 --- a/internal/rosa/cmake.go +++ b/internal/rosa/cmake.go @@ -86,12 +86,25 @@ func (attr *CMakeHelper) script(t Toolchain, name string) string { } cache = append(cache, attr.Cache...) + var cross string + if t.cross() { + cross = ` + -DCMAKE_SYSTEM_NAME=Linux \ + -DCMAKE_SYSROOT=/system/` + t.arch + ` \ + -DCMAKE_C_COMPILER=/system/bin/clang \ + -DCMAKE_CXX_COMPILER=/system/bin/clang++ \ + -DCMAKE_ASM_COMPILER=/system/bin/clang \` + } + return ` cmake -G ` + generate + ` \ - -Wno-author -Wno-unused-cli \ + -Wno-author -Wno-unused-cli \` + cross + ` -DCMAKE_C_COMPILER_TARGET='` + target + `' \ + -DCMAKE_C_FLAGS='--target=` + target + `' \ -DCMAKE_CXX_COMPILER_TARGET='` + target + `' \ + -DCMAKE_CXX_FLAGS='--target=` + target + `' \ -DCMAKE_ASM_COMPILER_TARGET='` + target + `' \ + -DCMAKE_ASM_FLAGS='--target=` + target + `' \ -DCMAKE_INSTALL_LIBDIR=lib \ ` + strings.Join(slices.Collect(func(yield func(string) bool) { for _, v := range cache { diff --git a/internal/rosa/llvm.go b/internal/rosa/llvm.go index d9ca83e1..4d3e4b21 100644 --- a/internal/rosa/llvm.go +++ b/internal/rosa/llvm.go @@ -75,53 +75,112 @@ func init() { target := t.triple() cache := []KV{ - {"ENABLE_LINKER_BUILD_ID", "ON"}, - {"COMPILER_RT_USE_BUILTINS_LIBRARY", "ON"}, - {"COMPILER_RT_DEFAULT_TARGET_ONLY", "ON"}, - {"COMPILER_RT_BUILD_GWP_ASAN", "OFF"}, - {"LIBCXX_CXX_ABI", "libcxxabi"}, - {"LIBCXX_USE_COMPILER_RT", "ON"}, - {"LIBCXX_ENABLE_STATIC_ABI_LIBRARY", "OFF"}, - {"LIBCXX_HAS_MUSL_LIBC", "ON"}, - {"LIBCXX_HARDENING_MODE", "fast"}, - {"LIBCXX_STATICALLY_LINK_ABI_IN_STATIC_LIBRARY", "ON"}, - {"LIBCXXABI_USE_LLVM_UNWINDER", "ON"}, - {"LIBCXXABI_ENABLE_STATIC_UNWINDER", "ON"}, - {"LIBCXXABI_USE_COMPILER_RT", "ON"}, - {"LIBCXXABI_STATICALLY_LINK_UNWINDER_IN_STATIC_LIBRARY", "ON"}, + {"LLVM_HOST_TRIPLE", "'" + target + "'"}, + {"LLVM_ENABLE_PROJECTS", "'" + strings.Join([]string{ + "clang", + "lld", + }, ";") + "'"}, + {"LLVM_ENABLE_RUNTIMES", "'" + strings.Join([]string{ + "compiler-rt", + "libcxx", + "libcxxabi", + "libunwind", + }, ";") + "'"}, + + {"LLVM_ENABLE_LLD", "ON"}, {"LLVM_INSTALL_BINUTILS_SYMLINKS", "ON"}, {"LLVM_INSTALL_UTILS", "ON"}, + + {"CLANG_DEFAULT_RTLIB", "compiler-rt"}, + {"CLANG_DEFAULT_UNWINDLIB", "libunwind"}, + {"CLANG_DEFAULT_CXX_STDLIB", "libc++"}, + {"CLANG_CONFIG_FILE_SYSTEM_DIR", "/system/etc/clang"}, + } + + common := []KV{ + {"CMAKE_POSITION_INDEPENDENT_CODE", "ON"}, + {"LLVM_BUILD_LLVM_DYLIB", "ON"}, {"LLVM_LINK_LLVM_DYLIB", "ON"}, {"LLVM_APPEND_VC_REV", "OFF"}, + {"LLVM_ENABLE_PER_TARGET_RUNTIME_DIR", "ON"}, + {"LLVM_INCLUDE_BENCHMARKS", "OFF"}, {"LLVM_ENABLE_RTTI", "ON"}, {"LLVM_ENABLE_ZLIB", "FORCE_ON"}, {"LLVM_ENABLE_ZSTD", "FORCE_ON"}, - {"LLVM_ENABLE_PER_TARGET_RUNTIME_DIR", "ON"}, - {"LLVM_INCLUDE_BENCHMARKS", "OFF"}, - {"CLANG_DEFAULT_RTLIB", "compiler-rt"}, - {"CLANG_DEFAULT_UNWINDLIB", "libunwind"}, - {"CLANG_DEFAULT_CXX_STDLIB", "libc++"}, - {"CLANG_CONFIG_FILE_SYSTEM_DIR", "/system/etc/clang"}, {"LLVM_ENABLE_FFI", "OFF"}, {"LLVM_ENABLE_LIBXML2", "OFF"}, {"LLVM_ENABLE_LIBCXX", "ON"}, - {"LLVM_ENABLE_LLD", "ON"}, + + {"COMPILER_RT_USE_BUILTINS_LIBRARY", "ON"}, + {"COMPILER_RT_DEFAULT_TARGET_ONLY", "ON"}, + {"COMPILER_RT_BUILD_GWP_ASAN", "OFF"}, + // symbols: clock_gettime, mallopt + {"COMPILER_RT_INCLUDE_TESTS", "OFF"}, + {"LIBUNWIND_ENABLE_ASSERTIONS", "OFF"}, {"LIBUNWIND_USE_COMPILER_RT", "ON"}, - {"LLVM_HOST_TRIPLE", "'" + target + "'"}, - {"LLVM_ENABLE_PROJECTS", "'" + strings.Join([]string{ - "clang", - "lld", - }, ";") + "'"}, - {"LLVM_ENABLE_RUNTIMES", "'" + strings.Join([]string{ - "compiler-rt", - "libcxx", - "libcxxabi", - "libunwind", - }, ";") + "'"}, + {"LIBCXX_CXX_ABI", "libcxxabi"}, + {"LIBCXX_USE_COMPILER_RT", "ON"}, + {"LIBCXX_ENABLE_STATIC_ABI_LIBRARY", "OFF"}, + {"LIBCXX_HAS_MUSL_LIBC", "ON"}, + {"LIBCXX_HARDENING_MODE", "fast"}, + {"LIBCXX_STATICALLY_LINK_ABI_IN_STATIC_LIBRARY", "ON"}, + + {"LIBCXXABI_USE_LLVM_UNWINDER", "ON"}, + {"LIBCXXABI_ENABLE_STATIC_UNWINDER", "ON"}, + {"LIBCXXABI_USE_COMPILER_RT", "ON"}, + {"LIBCXXABI_STATICALLY_LINK_UNWINDER_IN_STATIC_LIBRARY", "ON"}, + } + cache = append(cache, common...) + + var ( + triples []string + paths []pkg.ExecPath + ) + for _, arch := range []string{ + "amd64", + "arm64", + "riscv64", + } { + _triple := triple(arch) + triples = append(triples, _triple) + if t.stage.isStage0() { + continue + } + + if arch != t.Arch() { + sysroot := AbsSystem.Append(arch) + ct := t.native() + if ct.Arch() != arch { + ct = t.NewCross(arch).New(t.stage) + } + paths = append(paths, pkg.Path( + sysroot, true, ct.Append(nil, + _musl, + _kernelHeaders, + )..., + )) + cache = append(cache, []KV{ + {"BUILTINS_" + _triple + "_CMAKE_SYSROOT", sysroot.String()}, + {"RUNTIMES_" + _triple + "_CMAKE_SYSROOT", sysroot.String()}, + {"RUNTIMES_" + _triple + "_LLVM_INCLUDE_TESTS", "OFF"}, + }...) + } + + cache = append(cache, []KV{ + {"BUILTINS_" + _triple + "_CMAKE_BUILD_TYPE", "Release"}, + {"RUNTIMES_" + _triple + "_CMAKE_BUILD_TYPE", "Release"}, + {"RUNTIMES_" + _triple + "_CXX_SUPPORTS_FNO_EXCEPTIONS_FLAG", "ON"}, + {"RUNTIMES_" + _triple + "_CXX_SUPPORTS_FUNWIND_TABLES_FLAG", "ON"}, + }...) + for _, p := range common { + p[0] = "RUNTIMES_" + _triple + "_" + p[0] + cache = append(cache, p) + } } + targets := strings.Join(triples, ";") if !t.stage.isStage0() { skipChecks := []string{ @@ -181,9 +240,10 @@ func init() { } cache = append(cache, []KV{ - // symbols: clock_gettime, mallopt - {"COMPILER_RT_INCLUDE_TESTS", "OFF"}, + {"CLANG_VENDOR", "'Rosa OS'"}, + {"LLVM_BUILTIN_TARGETS", "'" + targets + "'"}, + {"LLVM_RUNTIME_TARGETS", "'" + targets + "'"}, {"LLVM_BUILD_TESTS", "ON"}, {"LLVM_LIT_ARGS", litArgs(true, skipChecks...)}, }...) @@ -198,6 +258,7 @@ func init() { "tr", "awk", }, + Paths: paths, ScriptEarly: ` rm -f /system/bin/clang++.cfg diff --git a/internal/rosa/llvm_test.go b/internal/rosa/llvm_test.go index 3fa8fc9d..29f2611c 100644 --- a/internal/rosa/llvm_test.go +++ b/internal/rosa/llvm_test.go @@ -8,7 +8,7 @@ import ( ) func TestLLVMInputs(t *testing.T) { - const wantInputCount = 472 + const wantInputCount = 600 _, llvm := rosa.MustLoad(rosa.H("llvm")) var n int diff --git a/internal/rosa/make.go b/internal/rosa/make.go index a41db646..f46780f5 100644 --- a/internal/rosa/make.go +++ b/internal/rosa/make.go @@ -101,19 +101,31 @@ func (attr *MakeHelper) script(t Toolchain, name string) string { if attr == nil { attr = new(MakeHelper) } + cross := t.cross() var configure string target := t.triple() + if cross { + configure += ` +export CC='clang --target=` + target + ` --sysroot=/system/` + t.arch + `' +export CXX='clang++ --target=` + target + ` --sysroot=/system/` + t.arch + `' +` + } + if !attr.SkipConfigure { - configure += attr.ConfigureName - if configure == "" { + if attr.ConfigureName == "" { configure += `/usr/src/` + name + `/configure` + } else { + configure += attr.ConfigureName } if attr.ForceAutoconf || attr.ConfigureName == "" { configure += ` \ --prefix=/system` host := target build := host + if cross { + build = triple(t.native().Arch()) + } if attr.Host != "" { host = attr.Host diff --git a/internal/rosa/package/kernel/package.az b/internal/rosa/package/kernel/package.az index 47bc4323..8e2dcfe5 100644 --- a/internal/rosa/package/kernel/package.az +++ b/internal/rosa/package/kernel/package.az @@ -29,6 +29,12 @@ package kernel-headers { make = [ "-f /usr/src/kernel-headers/Makefile", "O=/tmp/kbuild", + "ARCH='"+arch { + default = arch; + + amd64 = "x86"; + riscv64 = "riscv"; + }+"'", "LLVM=1", `HOSTLDFLAGS="${LDFLAGS:-''}"`, "INSTALL_HDR_PATH=/work/system", @@ -47,7 +53,7 @@ cat \ amd64 = "8BdskuTo4WxcJDFbdHakHmvkeIASmOkDd0LanTjaXwkmoMa5PeMf0gqee0FqYRFd"; arm64 = "RQ27XMgxEj5M6cagVHljCE1n-9q_gFayOaakWr0PoAq5yllfLHEz0KQx1fXLwCzp"; - riscv64 = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + riscv64 = "N4YuFlWVfdgnmoJMFMrTL3vhnzh-yUpxGmFaHqSuNIOmgBn2rI_SWcodONGcfOhw"; }; tools = [ rsync ]; diff --git a/internal/rosa/package/llvm/package.az b/internal/rosa/package/llvm/package.az index 631228be..02cea79a 100644 --- a/internal/rosa/package/llvm/package.az +++ b/internal/rosa/package/llvm/package.az @@ -17,13 +17,41 @@ package llvm-project { ]; } +package early-builtins { + description = "early LLVM runtime: compiler-rt (builtins)"; + source = llvm-project; + + exclusive = true; + + exec = cmake { + append = [ "compiler-rt/lib/builtins" ]; + cache = { + "LLVM_HOST_TRIPLE": triple; + "LLVM_ENABLE_RTTI": "ON"; + "LLVM_BUILD_LLVM_DYLIB": "ON"; + "LLVM_LINK_LLVM_DYLIB": "ON"; + "LLVM_ENABLE_PER_TARGET_RUNTIME_DIR": "ON"; + + "COMPILER_RT_DEFAULT_TARGET_ONLY": "ON"; + }; + test = nil; + }; + + tools = [ python-early ]; + + inputs = [ + musl-headers, + kernel-headers, + ]; +} + package early-compiler-rt { description = "early LLVM runtime: compiler-rt"; source = llvm-project; exclusive = true; early = ` -LLVM_VERSION_MAJOR="$(cmake -P /dev/stdin <<< ' +LLVM_VERSION_MAJOR="$(CLICOLOR_FORCE= cmake -P /dev/stdin <<< ' include(cmake/Modules/LLVMVersion.cmake) message(NOTICE "${LLVM_VERSION_MAJOR}") ' 2>&1)" @@ -36,6 +64,9 @@ message(NOTICE "${LLVM_VERSION_MAJOR}") // libc++ not yet available "CMAKE_CXX_COMPILER_TARGET": ""; + // musl not yet available + "CMAKE_TRY_COMPILE_TARGET_TYPE": "STATIC_LIBRARY"; + "LLVM_HOST_TRIPLE": triple; "LLVM_ENABLE_RTTI": "ON"; "LLVM_BUILD_LLVM_DYLIB": "ON"; @@ -45,15 +76,15 @@ message(NOTICE "${LLVM_VERSION_MAJOR}") "COMPILER_RT_BUILD_BUILTINS": "ON"; "COMPILER_RT_DEFAULT_TARGET_ONLY": "ON"; "COMPILER_RT_USE_BUILTINS_LIBRARY": "ON"; - "COMPILER_RT_SANITIZERS_TO_BUILD": "asan"; - "COMPILER_RT_BUILD_GWP_ASAN": "OFF"; // does not work without libunwind "COMPILER_RT_BUILD_CTX_PROFILE": "OFF"; + "COMPILER_RT_BUILD_SANITIZERS": "OFF"; "COMPILER_RT_BUILD_LIBFUZZER": "OFF"; "COMPILER_RT_BUILD_MEMPROF": "OFF"; "COMPILER_RT_BUILD_PROFILE": "OFF"; "COMPILER_RT_BUILD_XRAY": "OFF"; + "COMPILER_RT_BUILD_ORC": "OFF"; }; test = nil; postInstall = ` @@ -71,14 +102,14 @@ ln -s \ `; }; - inputs = [ - python-early, + tools = [ python-early ]; + inputs = [ musl-headers, kernel-headers, ]; - runtime = [ musl ]; + cross = [ early-builtins ]; } package early-runtimes { @@ -120,14 +151,17 @@ package early-runtimes { test = nil; }; - inputs = [ - python-early, + tools = [ python-early ]; + inputs = [ zlib, zstd, early-compiler-rt, kernel-headers, ]; - runtime = [ early-compiler-rt ]; + runtime = [ + musl, + early-compiler-rt, + ]; } diff --git a/internal/rosa/package/musl/package.az b/internal/rosa/package/musl/package.az index c264c782..7afa7e2f 100644 --- a/internal/rosa/package/musl/package.az +++ b/internal/rosa/package/musl/package.az @@ -24,6 +24,9 @@ package musl-headers { exec = make { defaults = false; + configure = { + "target": triple; + }; make = [ "DESTDIR=/work", "install-headers" ]; check = nil; install = "# headers installed during make"; @@ -46,9 +49,20 @@ package musl { env = [ "LDFLAGS=" + earlyLDFLAGS, ]; + early = ` +[ -e '/system/`+arch+`' ] && \ + ln -s ../../../../`+arch+`/system/lib/`+triple+` /system/lib/clang/*/lib || true +`; exec = make { defaults = false; + configure = { + "target": triple; + + "AR": "ar"; + "RANLIB": "ranlib"; + "CC": "'clang --target="+triple+" -rtlib=compiler-rt'"; + }; check = nil; postInstall = ` mkdir -p /work/system/bin @@ -61,5 +75,6 @@ rmdir -v /work/lib `; }; + cross = [ early-compiler-rt ]; tools = [ coreutils ]; } diff --git a/internal/rosa/rosa.go b/internal/rosa/rosa.go index c1f71cfa..e83d7097 100644 --- a/internal/rosa/rosa.go +++ b/internal/rosa/rosa.go @@ -268,11 +268,10 @@ function apply { return t.New(name+"-src", Unversioned, nil, &PackageAttr{ Paths: paths, + Tools: P{_patch}, }, &GenericHelper{ Build: buf.String(), - }, - _patch, - ) + }) } // helperInPlace is a special directory value for omitting the cd statement. @@ -338,6 +337,8 @@ type PackageAttr struct { // Whether to create [pkg.KindExecNet] instead of [pkg.KindExec]. HostNet bool + // Inputs added only during cross-compilation. + Cross P // Native utilities used by the build process. Tools P // Unregistered extras. @@ -419,9 +420,16 @@ func (t Toolchain) New( root := make([]pkg.Artifact, 0, 1<<3+ len(attr.Extra)+ len(attr.Tools)+ + len(attr.Cross)+ len(inputs)) root = append(root, attr.Extra...) + cross := t.cross() + var rootCross []pkg.Artifact + if cross { + rootCross = make([]pkg.Artifact, 0, len(inputs)+len(attr.Cross)) + } + const lcMessages = "LC_MESSAGES=C.UTF-8" srn := rn env := slices.Clone(attr.Env) @@ -490,24 +498,51 @@ func (t Toolchain) New( wantsChmod, wantsWrite := helper.wantsChmod(), helper.wantsWrite() { + native := t.native() pv := paGet() + spv := pv + if cross { + spv = paGet() + } for _, p := range helper.tools() { - root = t.appendHandle(root, pv, p) + root = native.appendHandle(root, pv, p) } - for _, p := range helper.inputs() { - root = t.appendHandle(root, pv, p) + + if !cross { + for _, p := range helper.inputs() { + root = t.appendHandle(root, spv, p) + } + } else { + for _, p := range helper.inputs() { + rootCross = t.appendHandle(rootCross, spv, p) + } } + for _, p := range attr.Tools { - root = t.appendHandle(root, pv, p) + root = native.appendHandle(root, pv, p) } - for _, p := range inputs { - root = t.appendHandle(root, pv, p) + + if !cross { + for _, p := range inputs { + root = t.appendHandle(root, spv, p) + } + } else { + for _, p := range inputs { + rootCross = t.appendHandle(rootCross, spv, p) + } + for _, p := range attr.Cross { + rootCross = t.appendHandle(rootCross, spv, p) + } } - boot := t.S.New(t.stage - 1) + + boot := native.S.New(t.stage - 1) for _, p := range extraBoot { root = boot.appendHandle(root, pv, p) } paPut(pv) + if cross { + paPut(spv) + } } if t.stage == stageGentoo { @@ -569,37 +604,51 @@ ln -s ../bin /usr/` scriptEarly += " /bin/)\n" } + arch := t.Arch() + sys := []pkg.ExecPath{pkg.Path( + fhs.AbsRoot, true, + root..., + ), pkg.Path( + absCureScript, false, + pkg.NewFile(scriptName, []byte( + "#!/system/bin/sh\n"+ + "set -eu -o pipefail\n"+ + scriptEarly+helper.script(t, name), + )), + )} + if cross { + var _arch string + _arch, arch = arch, t.S.native.Arch() + if len(rootCross) > 0 { + sys = append(sys, pkg.Path( + AbsSystem.Append(_arch), true, + rootCross..., + )) + } + srn += "-cross-" + _arch + } + return pkg.NewExec( - srn, t.arch, attr.KnownChecksum, pkg.ExecTimeoutMax, + srn, arch, attr.KnownChecksum, pkg.ExecTimeoutMax, attr.HostNet, attr.Exclusive, fhs.AbsRoot, env, absCureScript, nil, - slices.Concat([]pkg.ExecPath{pkg.Path( - fhs.AbsRoot, true, - root..., - ), pkg.Path( - absCureScript, false, - pkg.NewFile(scriptName, []byte( - "#!/system/bin/sh\n"+ - "set -eu -o pipefail\n"+ - scriptEarly+helper.script(t, name), - )), - )}, paths)..., + slices.Concat(sys, paths)..., ) } // GenericHelper directly passes build script segments for invocations of // one-off build scripts too specific to fit into any other [Helper] -// implementation, but can still benefit from [Toolchain.NewPackage]. +// implementation, but can still benefit from [Toolchain.New]. type GenericHelper struct { // Remain in current directory. InPlace bool // Concatenated for [Toolchain]. - Build, Check, Install string + Cross, Build, Check, Install string } var _ Helper = new(GenericHelper) @@ -634,6 +683,9 @@ func (attr *GenericHelper) script(t Toolchain, _ string) string { } script := attr.Build + if t.cross() { + script = attr.Cross + script + } if t.opts&OptSkipCheck == 0 { script += attr.Check } diff --git a/internal/rosa/state.go b/internal/rosa/state.go index 6d198d6b..18f9a6f3 100644 --- a/internal/rosa/state.go +++ b/internal/rosa/state.go @@ -202,6 +202,9 @@ type S struct { // For initialising arch. archOnce sync.Once + // Native state for tool acquisition. + native *S + // Built-in functions. frame azalea.Frame // For initialising frame. @@ -219,9 +222,37 @@ type S struct { gentooStage3 pkg.Artifact } +// ErrInvalidCross is panicked by [S.NewCross] attempting to derive a +// crosscompiling state for the native architecture. +var ErrInvalidCross = errors.New("invalid cross-compilation state") + +// NewCross returns a copy of s for cross-compilation. Testing is disabled in +// the copy. +func (s *S) NewCross(arch string) *S { + if s.native != nil && arch == s.native.Arch() { + panic(ErrInvalidCross) + } + + p := s.Clone() + p.opts |= OptSkipCheck + p.native, p.arch = s, arch + return p +} + +// cross returns whether s is a cross-compilation state. +func (s *S) cross() bool { return s.native != nil } + +// native returns the equivalent toolchain for compilation host. +func (t Toolchain) native() Toolchain { + if t.S.native != nil { + t.S = t.S.native + } + return t +} + // Clone returns a copy of s. func (s *S) Clone() *S { - v := S{arch: s.arch, opts: s.opts} + v := S{arch: s.arch, opts: s.opts, native: s.native} s.p.Range(func(key, value any) bool { v.p.Store(key, value) v.len.Add(1) @@ -479,6 +510,11 @@ func (s *S) getFrame() azalea.Frame { identDefault := k("default") identArch := unique.Make(azalea.Ident(s.arch)) + native := runtime.GOARCH + if s.cross() { + native = s.native.arch + } + const compressOffset = 1 << 8 enumCompress := map[unique.Handle[azalea.Ident]]any{ k("uncompressed"): 0, @@ -491,9 +527,9 @@ func (s *S) getFrame() azalea.Frame { s.frame.Val = map[unique.Handle[azalea.Ident]]any{ k("unversioned"): Unversioned, - k("hostTriple"): triple(runtime.GOARCH), - k("hostArch"): runtime.GOARCH, - k("hostLinuxArch"): linuxArch(runtime.GOARCH), + k("hostTriple"): triple(native), + k("hostArch"): native, + k("hostLinuxArch"): linuxArch(native), k("triple"): s.triple(), k("arch"): s.arch, @@ -820,6 +856,7 @@ func (s *S) getFrame() azalea.Frame { chdir := true if err = args.Apply(map[unique.Handle[azalea.Ident]]any{ k("chdir"): &chdir, + k("cross"): &attr.Cross, k("build"): &attr.Build, k("check"): &attr.Check, k("install"): &attr.Install, @@ -1063,7 +1100,7 @@ func (ctx *evalContext) pf( overlay string join bool - tools, inputs, extra azalea.Array + tools, inputs, cross, extra azalea.Array runtimes = azalea.Array{} bootstrap = true @@ -1096,6 +1133,7 @@ func (ctx *evalContext) pf( k("source"): &source, k("exec"): &helper, k("inputs"): &inputs, + k("cross"): &cross, k("tools"): &tools, k("runtime"): &runtimes, k("join"): &join, @@ -1109,6 +1147,8 @@ func (ctx *evalContext) pf( var inputsH, extraH P if inputsH, err = toHandles(inputs); err != nil { return + } else if attr.Cross, err = toHandles(cross); err != nil { + return } else if attr.Tools, err = toHandles(tools); err != nil { return } else if extraH, err = toHandles(extra); err != nil { |
