From 967cc116ec1ef561ca7cbe53ef4e0d84c546a0fd Mon Sep 17 00:00:00 2001 From: Peter Ebden Date: Sun, 16 Aug 2026 10:15:28 +0100 Subject: [PATCH 1/2] Convert cross-compile tests to Go --- test.sh | 6 ----- test/cross_compile/BUILD | 23 +++++++++++-------- test/cross_compile/README.md | 8 ++++--- test/cross_compile/bin_test.sh | 8 +++---- test/cross_compile/cross_compiled_bin_test.sh | 9 ++++++++ test/cross_compile/lib/BUILD | 7 ++---- test/cross_compile/lib/lib.c | 5 ---- test/cross_compile/lib/lib.go | 8 +++++++ test/cross_compile/lib/lib.h | 2 -- test/cross_compile/lib/main.go | 7 ------ test/cross_compile/location_test.sh | 4 +++- test/cross_compile/main.c | 8 ------- test/cross_compile/main.go | 11 +++++++++ 13 files changed, 55 insertions(+), 51 deletions(-) create mode 100755 test/cross_compile/cross_compiled_bin_test.sh delete mode 100644 test/cross_compile/lib/lib.c create mode 100644 test/cross_compile/lib/lib.go delete mode 100644 test/cross_compile/lib/lib.h delete mode 100644 test/cross_compile/lib/main.go delete mode 100644 test/cross_compile/main.c create mode 100644 test/cross_compile/main.go diff --git a/test.sh b/test.sh index dac5196fb8..e2b6de1457 100755 --- a/test.sh +++ b/test.sh @@ -22,12 +22,6 @@ check_path_for_excludes() { warn "xz not found, excluding update tests" EXCLUDES="${EXCLUDES} --exclude=xz" fi - GCCVER="`cc -dumpversion`" - if [ ! -d "/usr/lib/gcc/x86_64-linux-gnu/${GCCVER%.*.*}/32" ] && [ ! -d "/usr/lib/gcc/x86_64-pc-linux-gnu/$GCCVER/32" ]; then - warn "32-bit gcc libraries not found, excluding cross-compile tests" - EXCLUDES="${EXCLUDES} --exclude=x86" - fi - echo $EXCLUDES } diff --git a/test/cross_compile/BUILD b/test/cross_compile/BUILD index 8fe949e944..1362142624 100644 --- a/test/cross_compile/BUILD +++ b/test/cross_compile/BUILD @@ -1,4 +1,4 @@ -subinclude("//test/build_defs", "///cc//build_defs:c") +subinclude("//test/build_defs") genrule( name = "record_arch", @@ -24,9 +24,9 @@ plz_e2e_test( cmd = "plz test -a test_x86 //test/cross_compile:test_arch", ) -c_binary( +go_binary( name = "bin", - srcs = ["main.c"], + srcs = ["main.go"], deps = ["//test/cross_compile/lib"], ) @@ -34,13 +34,20 @@ sh_test( name = "bin_test", src = "bin_test.sh", data = [":bin"], +) + +sh_test( + name = "cross_compiled_bin_test", + src = "cross_compiled_bin_test.sh", + data = [":bin"], labels = ["manual"], ) +# N.B. Go can cross-compile to any platform it supports without needing a toolchain installed, +# so unlike the C rules this used to use, this needs nothing set up to run. plz_e2e_test( - name = "x86_test", - cmd = "plz test -a linux_x86 //test/cross_compile:bin_test", - labels = ["x86"], + name = "cross_compile_binary_test", + cmd = "plz test -a linux_arm64 //test/cross_compile:cross_compiled_bin_test", ) # Test for #582 @@ -60,8 +67,7 @@ sh_test( plz_e2e_test( name = "location_cross_compile_test", - cmd = "plz test -a linux_x86 //test/cross_compile:location_test", - labels = ["x86"], + cmd = "plz test -a linux_arm64 //test/cross_compile:location_test", ) config_setting( @@ -104,5 +110,4 @@ sh_test( plz_e2e_test( name = "select_cross_compile_test", cmd = "plz test -a linux_x86 //test/cross_compile:select_test", - labels = ["x86"], ) diff --git a/test/cross_compile/README.md b/test/cross_compile/README.md index e2bbdbb805..bc7d106eed 100644 --- a/test/cross_compile/README.md +++ b/test/cross_compile/README.md @@ -1,5 +1,7 @@ Example rules for testing cross-compiling. -One can test a simple binary with `plz build -a linux_x86 //test/cross_compile:bin`. -The configuration for that is stored in `.plzconfig_linux_x86` at the repo root. -This typically requires some additional packages to be installed; e.g. `gcc-multilib` or similar. +One can test a simple binary with `plz build -a linux_arm64 //test/cross_compile:bin`. + +These use Go rather than C deliberately: the Go toolchain can produce binaries for any platform +it supports without anything extra being installed, whereas the C version of this needed a +cross-compiling gcc (e.g. `gcc-multilib`) which is often not present. diff --git a/test/cross_compile/bin_test.sh b/test/cross_compile/bin_test.sh index 21a26cf1d2..4afc86cc0e 100755 --- a/test/cross_compile/bin_test.sh +++ b/test/cross_compile/bin_test.sh @@ -1,11 +1,9 @@ #!/bin/bash +# Checks that the binary built for the host architecture actually works, so that the +# cross-compiled test below is checking the architecture of something that isn't broken. set -eu if [ "`test/cross_compile/bin`" != "42" ]; then - echo "unexpected output" - exit 1 -fi -if [ ! `file test/cross_compile/bin | grep 32-bit` ]; then - echo "unexpected architecture of binary" + echo "unexpected output: `test/cross_compile/bin`" exit 1 fi diff --git a/test/cross_compile/cross_compiled_bin_test.sh b/test/cross_compile/cross_compiled_bin_test.sh new file mode 100755 index 0000000000..03bac0478d --- /dev/null +++ b/test/cross_compile/cross_compiled_bin_test.sh @@ -0,0 +1,9 @@ +#!/bin/bash +# Checks that a binary cross-compiled for another architecture really is built for it. +# We can't run it here, of course; that it runs at all is covered by bin_test on the host arch. +set -eu + +if ! file test/cross_compile/bin | grep -q 'ARM aarch64'; then + echo "unexpected architecture of binary: `file test/cross_compile/bin`" + exit 1 +fi diff --git a/test/cross_compile/lib/BUILD b/test/cross_compile/lib/BUILD index fcd68f223f..ea25f8c97c 100644 --- a/test/cross_compile/lib/BUILD +++ b/test/cross_compile/lib/BUILD @@ -1,11 +1,8 @@ # This contains an arbitrary set of rules intended to exercise # aspects of the cross-compilation code. -subinclude("///cc//build_defs:c") - -c_library( +go_library( name = "lib", - srcs = ["lib.c"], - hdrs = ["lib.h"], + srcs = ["lib.go"], visibility = ["//test/cross_compile:all"], ) diff --git a/test/cross_compile/lib/lib.c b/test/cross_compile/lib/lib.c deleted file mode 100644 index 668a5b1544..0000000000 --- a/test/cross_compile/lib/lib.c +++ /dev/null @@ -1,5 +0,0 @@ -#include - -int GetAnswer() { - return 42; -} diff --git a/test/cross_compile/lib/lib.go b/test/cross_compile/lib/lib.go new file mode 100644 index 0000000000..f60660ccd2 --- /dev/null +++ b/test/cross_compile/lib/lib.go @@ -0,0 +1,8 @@ +// Package lib exists to give the cross-compilation test something to build alongside the binary, +// so that we exercise cross-compiling a dependency and not just a single target. +package lib + +// GetAnswer returns, as usual, the answer to Life, the Universe and Everything. +func GetAnswer() int { + return 42 +} diff --git a/test/cross_compile/lib/lib.h b/test/cross_compile/lib/lib.h deleted file mode 100644 index 0569fb7d21..0000000000 --- a/test/cross_compile/lib/lib.h +++ /dev/null @@ -1,2 +0,0 @@ -// Returns, as usual, the answer to Life, the Universe and Everything. -int GetAnswer(); diff --git a/test/cross_compile/lib/main.go b/test/cross_compile/lib/main.go deleted file mode 100644 index 3bc4c28377..0000000000 --- a/test/cross_compile/lib/main.go +++ /dev/null @@ -1,7 +0,0 @@ -package main - -import "fmt" - -func main() { - fmt.Println("42") -} diff --git a/test/cross_compile/location_test.sh b/test/cross_compile/location_test.sh index 46a49cabf6..25b47e0df5 100755 --- a/test/cross_compile/location_test.sh +++ b/test/cross_compile/location_test.sh @@ -1,6 +1,8 @@ #!/bin/bash +# $DATA is arch_location.txt, whose contents are the $(location) of :record_arch. That must come +# out the same whether or not we're cross-compiling, which is what #582 was about. -if [ "`cat $DATA`" != "test/cross_compile/arch_location.txt" ]; then +if [ "`cat $DATA`" != "test/cross_compile/arch.txt" ]; then echo "Unexpected contents of file: `cat $DATA`" exit 1 fi diff --git a/test/cross_compile/main.c b/test/cross_compile/main.c deleted file mode 100644 index 31744d23f9..0000000000 --- a/test/cross_compile/main.c +++ /dev/null @@ -1,8 +0,0 @@ -#include - -#include "test/cross_compile/lib/lib.h" - - -int main(int argc, const char* argv[]) { - printf("%d\n", GetAnswer()); -} diff --git a/test/cross_compile/main.go b/test/cross_compile/main.go new file mode 100644 index 0000000000..22b4f3faec --- /dev/null +++ b/test/cross_compile/main.go @@ -0,0 +1,11 @@ +package main + +import ( + "fmt" + + "github.com/thought-machine/please/test/cross_compile/lib" +) + +func main() { + fmt.Println(lib.GetAnswer()) +} From 4070509e74d9921b8bb2be514846b3678a778e0a Mon Sep 17 00:00:00 2001 From: Peter Ebden Date: Sun, 16 Aug 2026 10:43:53 +0100 Subject: [PATCH 2/2] Fix CI failures (hopefully) --- test/cross_compile/BUILD | 18 ++++++++++-- test/cross_compile/README.md | 3 ++ test/cross_compile/arch_checker/BUILD | 5 ++++ test/cross_compile/arch_checker/main.go | 28 +++++++++++++++++++ test/cross_compile/cross_compiled_bin_test.sh | 9 ------ 5 files changed, 52 insertions(+), 11 deletions(-) create mode 100644 test/cross_compile/arch_checker/BUILD create mode 100644 test/cross_compile/arch_checker/main.go delete mode 100755 test/cross_compile/cross_compiled_bin_test.sh diff --git a/test/cross_compile/BUILD b/test/cross_compile/BUILD index 1362142624..7828b4f498 100644 --- a/test/cross_compile/BUILD +++ b/test/cross_compile/BUILD @@ -1,5 +1,15 @@ subinclude("//test/build_defs") +# A static link goes via the host C compiler, which can't handle objects for the architecture +# we're cross-compiling to. Go turns cgo off by itself when cross-compiling, so a dynamic link +# here is a pure-Go one and needs no C toolchain at all. +# N.B. Scoped to this package, so it can't affect the real cross-compiled builds of Please. +package( + go = { + "default_static": False, + }, +) + genrule( name = "record_arch", outs = ["arch.txt"], @@ -36,11 +46,15 @@ sh_test( data = [":bin"], ) -sh_test( +# N.B. arch_checker is a test tool, not data, so that it's built for the host and can actually +# run here while :bin is built for the architecture we're testing. +gentest( name = "cross_compiled_bin_test", - src = "cross_compiled_bin_test.sh", data = [":bin"], labels = ["manual"], + no_test_output = True, + test_cmd = "$TOOL $(location :bin) EM_AARCH64", + test_tools = ["//test/cross_compile/arch_checker"], ) # N.B. Go can cross-compile to any platform it supports without needing a toolchain installed, diff --git a/test/cross_compile/README.md b/test/cross_compile/README.md index bc7d106eed..2c03c889f7 100644 --- a/test/cross_compile/README.md +++ b/test/cross_compile/README.md @@ -5,3 +5,6 @@ One can test a simple binary with `plz build -a linux_arm64 //test/cross_compile These use Go rather than C deliberately: the Go toolchain can produce binaries for any platform it supports without anything extra being installed, whereas the C version of this needed a cross-compiling gcc (e.g. `gcc-multilib`) which is often not present. + +The `package()` call in the BUILD file turns off static linking for these rules; a static link +runs the host C compiler, which can't handle objects for the architecture we're targeting. diff --git a/test/cross_compile/arch_checker/BUILD b/test/cross_compile/arch_checker/BUILD new file mode 100644 index 0000000000..0abcaefcb3 --- /dev/null +++ b/test/cross_compile/arch_checker/BUILD @@ -0,0 +1,5 @@ +go_binary( + name = "arch_checker", + srcs = ["main.go"], + visibility = ["//test/cross_compile:all"], +) diff --git a/test/cross_compile/arch_checker/main.go b/test/cross_compile/arch_checker/main.go new file mode 100644 index 0000000000..e695328c4c --- /dev/null +++ b/test/cross_compile/arch_checker/main.go @@ -0,0 +1,28 @@ +// Binary arch_checker asserts that a binary was built for a particular machine architecture. +// It exists so the cross-compilation test doesn't have to shell out to `file`, which isn't +// present on all the images we build on. +package main + +import ( + "debug/elf" + "fmt" + "os" +) + +func main() { + if len(os.Args) != 3 { + fmt.Fprintf(os.Stderr, "usage: %s \n", os.Args[0]) + os.Exit(2) + } + path, expected := os.Args[1], os.Args[2] + f, err := elf.Open(path) + if err != nil { + fmt.Fprintf(os.Stderr, "couldn't read %s as an ELF binary: %s\n", path, err) + os.Exit(1) + } + defer f.Close() + if f.Machine.String() != expected { + fmt.Fprintf(os.Stderr, "%s was built for %s, expected %s\n", path, f.Machine, expected) + os.Exit(1) + } +} diff --git a/test/cross_compile/cross_compiled_bin_test.sh b/test/cross_compile/cross_compiled_bin_test.sh deleted file mode 100755 index 03bac0478d..0000000000 --- a/test/cross_compile/cross_compiled_bin_test.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -# Checks that a binary cross-compiled for another architecture really is built for it. -# We can't run it here, of course; that it runs at all is covered by bin_test on the host arch. -set -eu - -if ! file test/cross_compile/bin | grep -q 'ARM aarch64'; then - echo "unexpected architecture of binary: `file test/cross_compile/bin`" - exit 1 -fi