aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-10-22 01:56:23 +0900
committerOphestra <cat@gensokyo.uk>2026-10-22 11:10:01 +0900
commit9dfbf95db02054204defbe422099c02b790a19fc (patch)
treef8c318ebfc5c824b2eb5e7a8aac3b835ee05a4ed
parent29f86b3446c9fe35d2a359df45471abdaa35cea4 (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.go85
-rw-r--r--internal/rosa/azalea/evaluate.go3
-rw-r--r--internal/rosa/cmake.go15
-rw-r--r--internal/rosa/llvm.go129
-rw-r--r--internal/rosa/llvm_test.go2
-rw-r--r--internal/rosa/make.go16
-rw-r--r--internal/rosa/package/kernel/package.az8
-rw-r--r--internal/rosa/package/llvm/package.az52
-rw-r--r--internal/rosa/package/musl/package.az15
-rw-r--r--internal/rosa/rosa.go100
-rw-r--r--internal/rosa/state.go50
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 {