From 699b709b3d1dc892228c90c8debca80f385d4126 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jiri=20Dan=C4=9Bk?= Date: Tue, 23 Sep 2025 20:22:07 +0200 Subject: [PATCH 01/22] see if I can mount /mnt on github actions too # Conflicts: # runtimes/minimal/ubi9-python-3.12/Dockerfile.cpu --- runtimes/minimal/ubi9-python-3.12/Dockerfile.cpu | 2 ++ scripts/sandbox.py | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/runtimes/minimal/ubi9-python-3.12/Dockerfile.cpu b/runtimes/minimal/ubi9-python-3.12/Dockerfile.cpu index 4a16d6f7b9..961a33dc45 100644 --- a/runtimes/minimal/ubi9-python-3.12/Dockerfile.cpu +++ b/runtimes/minimal/ubi9-python-3.12/Dockerfile.cpu @@ -13,6 +13,8 @@ WORKDIR /opt/app-root/bin # OS Packages needs to be installed as root USER 0 +RUN ls /mnt && false + ### BEGIN upgrade first to avoid fixable vulnerabilities # If we have a Red Hat subscription prepared, refresh it RUN /bin/bash <<'EOF' diff --git a/scripts/sandbox.py b/scripts/sandbox.py index a8592b09d9..b42d14aea4 100755 --- a/scripts/sandbox.py +++ b/scripts/sandbox.py @@ -47,7 +47,13 @@ def main() -> int: with tempfile.TemporaryDirectory(delete=True) as tmpdir: setup_sandbox(prereqs, pathlib.Path(tmpdir)) - command = [arg if arg != "{};" else tmpdir for arg in args.remaining[1:]] + additional_arguments = ["--volume=/usr:/mnt:ro", tmpdir] + command = [] + for arg in args.remaining[1:]: + if arg == "{};": + command.extend(additional_arguments) + else: + command.append(arg) print(f"running {command=}") try: subprocess.check_call(command) From a7546136ebe9a23f50a171b3394b2d4645cb4618 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jiri=20Dan=C4=9Bk?= Date: Tue, 23 Sep 2025 21:47:14 +0200 Subject: [PATCH 02/22] install zig --- Makefile | 29 +++++++++++++++++++++++++++++ scripts/sandbox.py | 4 +++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c835c34127..b76ea02829 100644 --- a/Makefile +++ b/Makefile @@ -429,6 +429,35 @@ refresh-lock-files: scan-image-vulnerabilities: python ci/security-scan/quay_security_analysis.py +ARCH := $(shell uname -m) +ifeq ($(ARCH),amd64) + ARCH := x86_64 +else ifeq ($(ARCH),arm64) + ARCH := aarch64 +endif + +ZIG_VERSION := 0.15.1 +ZIG_BINARY := zig-$(ZIG_VERSION) + +bin/zig-$(ZIG_VERSION): + @echo "Installing Zig $(ZIG_VERSION)..." + TMPDIR=$(shell mktemp -d) + wget https://ziglang.org/download/$(ZIG_VERSION)/zig-$(ARCH)-linux-$(ZIG_VERSION).tar.xz + tar -xJf zig-$(ARCH)-linux-$(ZIG_VERSION).tar.xz -C $$TMPDIR --strip-components=1 + mv $$TMPDIR bin/zig-$(ZIG_VERSION) + rm -rf zig-$(ARCH)-linux-$(ZIG_VERSION).tar.xz + @echo "Zig installed as bin/zig-$(ZIG_VERSION)" + +# This should be .PHONY because it's an alias/action +.PHONY: install-zig +install-zig: bin/zig-$(ZIG_VERSION) + @echo "Zig is ready to use!" + +# Another .PHONY target for cleanup +.PHONY: clean-zig +clean-zig: + rm -rf bin/zig-$(ZIG_VERSION) + # This is used primarily for gen_gha_matrix_jobs.py to we know the set of all possible images we may want to build .PHONY: all-images ifeq ($(RELEASE_PYTHON_VERSION), 3.12) diff --git a/scripts/sandbox.py b/scripts/sandbox.py index b42d14aea4..cd1367fa0a 100755 --- a/scripts/sandbox.py +++ b/scripts/sandbox.py @@ -47,7 +47,7 @@ def main() -> int: with tempfile.TemporaryDirectory(delete=True) as tmpdir: setup_sandbox(prereqs, pathlib.Path(tmpdir)) - additional_arguments = ["--volume=/usr:/mnt:ro", tmpdir] + additional_arguments = [f"--volume={os.getcwd()}/bin/zig-0.15.1:/mnt", tmpdir] command = [] for arg in args.remaining[1:]: if arg == "{};": @@ -69,6 +69,8 @@ def buildinputs( ) -> list[pathlib.Path]: if not (ROOT_DIR / "bin/buildinputs").exists(): subprocess.check_call([MAKE, "bin/buildinputs"], cwd=ROOT_DIR) + if not (ROOT_DIR / "bin/zig-0.15.1").exists(): + subprocess.check_call([MAKE, "bin/zig-0.15.1"], cwd=ROOT_DIR) stdout = subprocess.check_output([ROOT_DIR / "bin/buildinputs", str(dockerfile)], text=True, cwd=ROOT_DIR, env={"TARGETPLATFORM": platform, **os.environ}) From 02e48bb7fce7902db8866658fda519242a7b7355 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jiri=20Dan=C4=9Bk?= Date: Tue, 23 Sep 2025 21:54:44 +0200 Subject: [PATCH 03/22] Add progress display for wget in Zig installation --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index b76ea02829..8ec4c4543a 100644 --- a/Makefile +++ b/Makefile @@ -442,7 +442,7 @@ ZIG_BINARY := zig-$(ZIG_VERSION) bin/zig-$(ZIG_VERSION): @echo "Installing Zig $(ZIG_VERSION)..." TMPDIR=$(shell mktemp -d) - wget https://ziglang.org/download/$(ZIG_VERSION)/zig-$(ARCH)-linux-$(ZIG_VERSION).tar.xz + wget --progress=dot:giga https://ziglang.org/download/$(ZIG_VERSION)/zig-$(ARCH)-linux-$(ZIG_VERSION).tar.xz tar -xJf zig-$(ARCH)-linux-$(ZIG_VERSION).tar.xz -C $$TMPDIR --strip-components=1 mv $$TMPDIR bin/zig-$(ZIG_VERSION) rm -rf zig-$(ARCH)-linux-$(ZIG_VERSION).tar.xz From 8f9962880e035bc749b1900c1ae64f34f057af1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jiri=20Dan=C4=9Bk?= Date: Tue, 23 Sep 2025 21:58:13 +0200 Subject: [PATCH 04/22] Configure sandbox to support cross-compilation with Zig for s390x-linux-gnu target. --- scripts/sandbox.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/sandbox.py b/scripts/sandbox.py index cd1367fa0a..d8ef8c7342 100755 --- a/scripts/sandbox.py +++ b/scripts/sandbox.py @@ -47,7 +47,13 @@ def main() -> int: with tempfile.TemporaryDirectory(delete=True) as tmpdir: setup_sandbox(prereqs, pathlib.Path(tmpdir)) - additional_arguments = [f"--volume={os.getcwd()}/bin/zig-0.15.1:/mnt", tmpdir] + target = "s390x-linux-gnu" + additional_arguments = [ + f"--volume={os.getcwd()}/bin/zig-0.15.1:/mnt", + f"--build-arg=CC=/mnt/zig-0.15.1/zig cc -target {target}", + f"--build-arg=CXX=/mnt/zig-0.15.1/zig c++ -target {target}", + tmpdir, + ] command = [] for arg in args.remaining[1:]: if arg == "{};": From 454049f60734eba91dcddada28d6cb8fc7cd68b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jiri=20Dan=C4=9Bk?= Date: Tue, 23 Sep 2025 21:58:32 +0200 Subject: [PATCH 05/22] Fix Dockerfile.cpu mount check logic in `ubi9-python-3.12` runtime --- runtimes/minimal/ubi9-python-3.12/Dockerfile.cpu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtimes/minimal/ubi9-python-3.12/Dockerfile.cpu b/runtimes/minimal/ubi9-python-3.12/Dockerfile.cpu index 961a33dc45..6235661067 100644 --- a/runtimes/minimal/ubi9-python-3.12/Dockerfile.cpu +++ b/runtimes/minimal/ubi9-python-3.12/Dockerfile.cpu @@ -13,7 +13,7 @@ WORKDIR /opt/app-root/bin # OS Packages needs to be installed as root USER 0 -RUN ls /mnt && false +RUN ls /mnt && true ### BEGIN upgrade first to avoid fixable vulnerabilities # If we have a Red Hat subscription prepared, refresh it From e5d25381183e7dad0d8af820e6dfd584bd6bc956 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jiri=20Dan=C4=9Bk?= Date: Tue, 23 Sep 2025 22:19:09 +0200 Subject: [PATCH 06/22] Modify sandbox to set `CC` and `CXX` as environment variables for Zig cross-compilation and unset them afterward. --- scripts/sandbox.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/sandbox.py b/scripts/sandbox.py index d8ef8c7342..a60e957bc1 100755 --- a/scripts/sandbox.py +++ b/scripts/sandbox.py @@ -50,8 +50,10 @@ def main() -> int: target = "s390x-linux-gnu" additional_arguments = [ f"--volume={os.getcwd()}/bin/zig-0.15.1:/mnt", - f"--build-arg=CC=/mnt/zig-0.15.1/zig cc -target {target}", - f"--build-arg=CXX=/mnt/zig-0.15.1/zig c++ -target {target}", + f"--env=CC=/mnt/zig-0.15.1/zig cc -target {target}", + f"--env=CXX=/mnt/zig-0.15.1/zig c++ -target {target}", + f"--unsetenv=CC", + f"--unsetenv=CXX", tmpdir, ] command = [] From d077f190a569d2b32f03d467b1f5b78564faa3f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jiri=20Dan=C4=9Bk?= Date: Tue, 23 Sep 2025 22:27:49 +0200 Subject: [PATCH 07/22] Simplify Zig paths in sandbox for cross-compilation environment variables `CC` and `CXX`. --- scripts/sandbox.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/sandbox.py b/scripts/sandbox.py index a60e957bc1..c08a1a889a 100755 --- a/scripts/sandbox.py +++ b/scripts/sandbox.py @@ -50,8 +50,8 @@ def main() -> int: target = "s390x-linux-gnu" additional_arguments = [ f"--volume={os.getcwd()}/bin/zig-0.15.1:/mnt", - f"--env=CC=/mnt/zig-0.15.1/zig cc -target {target}", - f"--env=CXX=/mnt/zig-0.15.1/zig c++ -target {target}", + f"--env=CC=/mnt/zig cc -target {target}", + f"--env=CXX=/mnt/zig c++ -target {target}", f"--unsetenv=CC", f"--unsetenv=CXX", tmpdir, From 48a4a521b990a15612e8f2d54521bf53862332ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jiri=20Dan=C4=9Bk?= Date: Tue, 23 Sep 2025 22:45:55 +0200 Subject: [PATCH 08/22] Update target in sandbox for s390x to specify glibc version (2.34). --- scripts/sandbox.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/scripts/sandbox.py b/scripts/sandbox.py index c08a1a889a..db0198bcf4 100755 --- a/scripts/sandbox.py +++ b/scripts/sandbox.py @@ -47,7 +47,8 @@ def main() -> int: with tempfile.TemporaryDirectory(delete=True) as tmpdir: setup_sandbox(prereqs, pathlib.Path(tmpdir)) - target = "s390x-linux-gnu" + # target glibc 2.28 or newer (supports FORTIFY_SOURCE) + target = "s390x-linux-gnu.2.34" additional_arguments = [ f"--volume={os.getcwd()}/bin/zig-0.15.1:/mnt", f"--env=CC=/mnt/zig cc -target {target}", @@ -70,6 +71,24 @@ def main() -> int: return err.returncode return 0 +""" +Downloading jedi + × Failed to build `pyzmq==27.1.0` + ├─▶ The build backend returned an error + ╰─▶ Call to `scikit_build_core.build.build_wheel` failed (exit status: 1) + [stdout] + *** scikit-build-core 0.11.6 using CMake 3.26.5 (wheel) + *** Configuring CMake... + loading initial cache file /tmp/tmpf9bnfh5o/build/CMakeInit.txt + -- Configuring incomplete, errors occurred! + [stderr] + CMake Error at /usr/share/cmake/Modules/CMakeDetermineCCompiler.cmake:49 + (message): + Could not find compiler set in environment variable CC: + /mnt/zig-0.15.1/zig cc -target s390x-linux-gnu. + Call Stack (most recent call first): + CMakeLists.txt:2 (project) +""" def buildinputs( dockerfile: pathlib.Path | str, From 0c41a8ae9c5061d680218862d4ddf2673ac7454a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jiri=20Dan=C4=9Bk?= Date: Tue, 23 Sep 2025 22:46:22 +0200 Subject: [PATCH 09/22] Address deprecation warning and unsupported arg in s390x build process --- scripts/sandbox.py | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/scripts/sandbox.py b/scripts/sandbox.py index db0198bcf4..923e98e7ac 100755 --- a/scripts/sandbox.py +++ b/scripts/sandbox.py @@ -90,6 +90,46 @@ def main() -> int: CMakeLists.txt:2 (project) """ +""" +creating build/temp.linux-s390x-cpython-312/psutil/arch/linux + /mnt/zig cc -target s390x-linux-gnu -fno-strict-overflow + -Wsign-compare -DDYNAMIC_ANNOTATIONS_ENABLED=1 -DNDEBUG + -O2 -fexceptions -g -grecord-gcc-switches -pipe + -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 + -Wp,-D_GLIBCXX_ASSERTIONS -fstack-protector-strong + -m64 -march=z14 -mtune=z15 -fasynchronous-unwind-tables + -fstack-clash-protection -O2 -fexceptions -g -grecord-gcc-switches + -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 + -Wp,-D_GLIBCXX_ASSERTIONS -fstack-protector-strong + -m64 -march=z14 -mtune=z15 -fasynchronous-unwind-tables + -fstack-clash-protection -O2 -fexceptions -g -grecord-gcc-switches + -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 + -Wp,-D_GLIBCXX_ASSERTIONS -fstack-protector-strong + -m64 -march=z14 -mtune=z15 -fasynchronous-unwind-tables + -fstack-clash-protection -fPIC -DPSUTIL_POSIX=1 -DPSUTIL_SIZEOF_PID_T=4 + -DPSUTIL_VERSION=700 -DPy_LIMITED_API=0x03060000 + -DPSUTIL_LINUX=1 -I/tmp/.tmpWlL4ZP/builds-v0/.tmpOwAhw2/include + -I/usr/include/python3.12 -c psutil/_psutil_common.c -o + build/temp.linux-s390x-cpython-312/psutil/_psutil_common.o + [stderr] + /tmp/.tmpWlL4ZP/builds-v0/.tmpOwAhw2/lib64/python3.12/site-packages/setuptools/dist.py:759: + SetuptoolsDeprecationWarning: License classifiers are deprecated. + !! + + ******************************************************************************** + Please consider removing the following classifiers in favor of a + SPDX license expression: + License :: OSI Approved :: BSD License + See + https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#license + for details. + + ******************************************************************************** + !! + self._finalize_license_expression() + error: unsupported preprocessor arg: -D_FORTIFY_SOURCE +""" + def buildinputs( dockerfile: pathlib.Path | str, platform: Literal["linux/amd64", "linux/arm64", "linux/s390x", "linux/ppc64le"] = "linux/amd64" From 2bf813d9af8d22a6b18e4f9cb3c29a3f57508bfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jiri=20Dan=C4=9Bk?= Date: Wed, 24 Sep 2025 00:35:36 +0200 Subject: [PATCH 10/22] Add wrapper scripts for Zig cross-compilation and adjust s390x sandbox configuration --- Makefile | 8 +++++++- scripts/sandbox.py | 11 +++++++++-- wrapper.py | 25 +++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 wrapper.py diff --git a/Makefile b/Makefile index 8ec4c4543a..2fde2c83c6 100644 --- a/Makefile +++ b/Makefile @@ -444,8 +444,14 @@ bin/zig-$(ZIG_VERSION): TMPDIR=$(shell mktemp -d) wget --progress=dot:giga https://ziglang.org/download/$(ZIG_VERSION)/zig-$(ARCH)-linux-$(ZIG_VERSION).tar.xz tar -xJf zig-$(ARCH)-linux-$(ZIG_VERSION).tar.xz -C $$TMPDIR --strip-components=1 - mv $$TMPDIR bin/zig-$(ZIG_VERSION) rm -rf zig-$(ARCH)-linux-$(ZIG_VERSION).tar.xz + + printf '%s\n' '#!/bin/sh' 'exec /mnt/zig cc -target s390x-linux-gnu.2.34 "$@"' > $$TMPDIR/zig-cc + printf '%s\n' '#!/bin/sh' 'exec /mnt/zig c++ -target s390x-linux-gnu.2.34 "$@"' > $$TMPDIR/zig-c++ + chmod +x $$TMPDIR/zig-cc + chmod +x $$TMPDIR/zig-c++ + + mv $$TMPDIR bin/zig-$(ZIG_VERSION) @echo "Zig installed as bin/zig-$(ZIG_VERSION)" # This should be .PHONY because it's an alias/action diff --git a/scripts/sandbox.py b/scripts/sandbox.py index 923e98e7ac..0dc640699b 100755 --- a/scripts/sandbox.py +++ b/scripts/sandbox.py @@ -51,8 +51,15 @@ def main() -> int: target = "s390x-linux-gnu.2.34" additional_arguments = [ f"--volume={os.getcwd()}/bin/zig-0.15.1:/mnt", - f"--env=CC=/mnt/zig cc -target {target}", - f"--env=CXX=/mnt/zig c++ -target {target}", + # f"--env=CC=/mnt/zig cc -target {target}", + # f"--env=CXX=/mnt/zig c++ -target {target}", + # f"--env=CC=/mnt/zig-cc", + # f"--env=CXX=/mnt/zig-c++", + # -Wp,-D_FORTIFY_SOURCE=2 + # https://github.com/giampaolo/psutil/blob/master/setup.py#L254 + # defaults to using python's flags + # f"--env=CFLAGS=", + "--env=CXXFLAGS=-Dundefined=64", f"--unsetenv=CC", f"--unsetenv=CXX", tmpdir, diff --git a/wrapper.py b/wrapper.py new file mode 100644 index 0000000000..69d0201a28 --- /dev/null +++ b/wrapper.py @@ -0,0 +1,25 @@ +#! /usr/bin/env python3 +import os +import pathlib +import sys + +def main(): + arg0 = pathlib.Path(sys.argv[0]).name + args = [] + for arg in sys.argv[1:]: + if arg.startswith("-Wp,-D"): + args.append(arg.replace("-Wp,-D", "-D", 1)) + else: + args.append(arg) + + if arg0 == "zig-cc": + args = ["/mnt/zig", "cc", "-target", "s390x-linux-gnu.2.34"] + args + elif arg0 == "zig-c++": + args = ["/mnt/zig", "c++", "-target", "s390x-linux-gnu.2.34"] + args + else: + raise ValueError(f"Unknown argument {arg0}") + + os.execve(args[0], args, os.environ) + +if __name__ == "__main__": + sys.exit(main()) From 728baa9369f45fa3608aa214a38a8d4f7f1a3792 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jiri=20Dan=C4=9Bk?= Date: Wed, 24 Sep 2025 10:46:25 +0200 Subject: [PATCH 11/22] Add `zigcc` wrapper for s390x cross-compilation, including `Makefile`, tests, and Go module setup. --- scripts/zigcc/Makefile | 21 ++++++++++++++ scripts/zigcc/go.mod | 3 ++ scripts/zigcc/zigcc.go | 56 +++++++++++++++++++++++++++++++++++++ scripts/zigcc/zigcc_test.go | 35 +++++++++++++++++++++++ 4 files changed, 115 insertions(+) create mode 100644 scripts/zigcc/Makefile create mode 100644 scripts/zigcc/go.mod create mode 100644 scripts/zigcc/zigcc.go create mode 100644 scripts/zigcc/zigcc_test.go diff --git a/scripts/zigcc/Makefile b/scripts/zigcc/Makefile new file mode 100644 index 0000000000..23ff791e8a --- /dev/null +++ b/scripts/zigcc/Makefile @@ -0,0 +1,21 @@ +.PHONY: build test clean + +build: bin/zigcc + +# always build for linux and the machine's native architecture +bin/zigcc: *.go go.mod + GOOS=linux go build -o $@ -ldflags="-s -w" -v ./... + +test: + go test -v ./... + +fmt: + go fmt ./... + +vet: + go vet ./... + +clean: + go clean + rm -f bin/* + rmdir bin diff --git a/scripts/zigcc/go.mod b/scripts/zigcc/go.mod new file mode 100644 index 0000000000..e2b750b7bb --- /dev/null +++ b/scripts/zigcc/go.mod @@ -0,0 +1,3 @@ +module zigcc + +go 1.24 diff --git a/scripts/zigcc/zigcc.go b/scripts/zigcc/zigcc.go new file mode 100644 index 0000000000..cb7657570e --- /dev/null +++ b/scripts/zigcc/zigcc.go @@ -0,0 +1,56 @@ +package main + +import ( + "fmt" + "os" + "path/filepath" + "strings" + "syscall" +) + +func processArg0(arg0 string) (string, error) { + switch arg0 { + case "zig-cc": + return "cc", nil + case "zig-c++": + return "c++", nil + default: + return "", fmt.Errorf("unknown wrapper name: %s", arg0) + } +} + +func processArgs(args []string) []string { + newArgs := make([]string, 0, len(args)) + for _, arg := range args { + if strings.HasPrefix(arg, "-Wp,") { + newArgs = append(newArgs, strings.Split(arg, ",")[1:]...) + } else { + newArgs = append(newArgs, arg) + } + } + return newArgs +} + +func main() { + arg0 := filepath.Base(os.Args[0]) + subcommand, err := processArg0(arg0) + if err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) + } + + newArgs := make([]string, 0, len(os.Args)+4) + newArgs = append(newArgs, + "/mnt/zig", // Path to the real Zig executable. + subcommand, + "-target", + "s390x-linux-gnu.2.34", + ) + newArgs = append(newArgs, processArgs(os.Args[1:])...) + + env := os.Environ() + if err := syscall.Exec(newArgs[0], newArgs, env); err != nil { + fmt.Fprintf(os.Stderr, "Error executing zig: %v\n", err) + os.Exit(1) + } +} diff --git a/scripts/zigcc/zigcc_test.go b/scripts/zigcc/zigcc_test.go new file mode 100644 index 0000000000..1bea00d6e0 --- /dev/null +++ b/scripts/zigcc/zigcc_test.go @@ -0,0 +1,35 @@ +package main + +import ( + "fmt" + "reflect" + "testing" +) + +func TestProcessWp(t *testing.T) { + args := []string{"-Wp,-D_FORTIFY_SOURCE=2"} + newArgs := processArgs(args) + if !reflect.DeepEqual(newArgs, []string{"-D_FORTIFY_SOURCE=2"}) { + t.Fatalf("expected -DFOO=bar, got %v", newArgs) + } + for _, tc := range []struct { + args []string + expected []string + }{ + { + args: []string{"-Wp,-D_FORTIFY_SOURCE=2"}, + expected: []string{"-D_FORTIFY_SOURCE=2"}, + }, + { + args: []string{"-Wp,-DNDEBUG,-D_FORTIFY_SOURCE=2"}, + expected: []string{"-DNDEBUG", "-D_FORTIFY_SOURCE=2"}, + }, + } { + t.Run(fmt.Sprint(tc.args), func(t *testing.T) { + newArgs := processArgs(tc.args) + if !reflect.DeepEqual(newArgs, tc.expected) { + t.Fatalf("expected %#v, got %#v", tc.expected, newArgs) + } + }) + } +} From 40b2c23e80db034a9bbb494ad219cd29a5c0cd4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jiri=20Dan=C4=9Bk?= Date: Wed, 24 Sep 2025 10:46:47 +0200 Subject: [PATCH 12/22] Update sandbox to correctly set `CC` and `CXX` for Zig, add cache busting, and ensure `zigcc` wrappers are built --- scripts/sandbox.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/scripts/sandbox.py b/scripts/sandbox.py index 0dc640699b..217ba4ad22 100755 --- a/scripts/sandbox.py +++ b/scripts/sandbox.py @@ -53,12 +53,13 @@ def main() -> int: f"--volume={os.getcwd()}/bin/zig-0.15.1:/mnt", # f"--env=CC=/mnt/zig cc -target {target}", # f"--env=CXX=/mnt/zig c++ -target {target}", - # f"--env=CC=/mnt/zig-cc", - # f"--env=CXX=/mnt/zig-c++", + f"--env=CC=/mnt/zig-cc", + f"--env=CXX=/mnt/zig-c++", # -Wp,-D_FORTIFY_SOURCE=2 # https://github.com/giampaolo/psutil/blob/master/setup.py#L254 # defaults to using python's flags # f"--env=CFLAGS=", + f"--env=bustcachez=", "--env=CXXFLAGS=-Dundefined=64", f"--unsetenv=CC", f"--unsetenv=CXX", @@ -145,6 +146,10 @@ def buildinputs( subprocess.check_call([MAKE, "bin/buildinputs"], cwd=ROOT_DIR) if not (ROOT_DIR / "bin/zig-0.15.1").exists(): subprocess.check_call([MAKE, "bin/zig-0.15.1"], cwd=ROOT_DIR) + if not (ROOT_DIR / "bin/zig-0.15.1/zigcc").exists(): + subprocess.check_call([MAKE, "build"], cwd=ROOT_DIR / "scripts/zigcc") + shutil.copy(ROOT_DIR / "scripts/zigcc/bin/zigcc", ROOT_DIR / "bin/zig-0.15.1/zig-cc") + shutil.copy(ROOT_DIR / "scripts/zigcc/bin/zigcc", ROOT_DIR / "bin/zig-0.15.1/zig-c++") stdout = subprocess.check_output([ROOT_DIR / "bin/buildinputs", str(dockerfile)], text=True, cwd=ROOT_DIR, env={"TARGETPLATFORM": platform, **os.environ}) From eea6333618beabb2f7542375617dc207bb7b968a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jiri=20Dan=C4=9Bk?= Date: Wed, 24 Sep 2025 10:47:14 +0200 Subject: [PATCH 13/22] Add `ninja-build` dependency for s390x and ppc64le in `ubi9-python-3.12` Dockerfile --- runtimes/minimal/ubi9-python-3.12/Dockerfile.cpu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtimes/minimal/ubi9-python-3.12/Dockerfile.cpu b/runtimes/minimal/ubi9-python-3.12/Dockerfile.cpu index 6235661067..460d3c21ef 100644 --- a/runtimes/minimal/ubi9-python-3.12/Dockerfile.cpu +++ b/runtimes/minimal/ubi9-python-3.12/Dockerfile.cpu @@ -41,7 +41,7 @@ ARCH=$(uname -m) echo "Detected architecture: $ARCH" PACKAGES="perl mesa-libGL skopeo" if [ "$ARCH" = "s390x" ] || [ "$ARCH" = "ppc64le" ]; then - PACKAGES="$PACKAGES gcc g++ make openssl-devel autoconf automake libtool cmake" + PACKAGES="$PACKAGES gcc g++ ninja-build openssl-devel autoconf automake libtool cmake" fi dnf install -y --setopt=keepcache=1 $PACKAGES EOF From 4a4c7526a99513523802d196fe35fd6a481c9906 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jiri=20Dan=C4=9Bk?= Date: Wed, 24 Sep 2025 19:31:59 +0200 Subject: [PATCH 14/22] without zig, before 10 min https://github.com/opendatahub-io/notebooks/actions/runs/17971438786/job/51114822510?pr=2535 --- scripts/sandbox.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/sandbox.py b/scripts/sandbox.py index 217ba4ad22..6df75c426a 100755 --- a/scripts/sandbox.py +++ b/scripts/sandbox.py @@ -53,8 +53,8 @@ def main() -> int: f"--volume={os.getcwd()}/bin/zig-0.15.1:/mnt", # f"--env=CC=/mnt/zig cc -target {target}", # f"--env=CXX=/mnt/zig c++ -target {target}", - f"--env=CC=/mnt/zig-cc", - f"--env=CXX=/mnt/zig-c++", + # f"--env=CC=/mnt/zig-cc", + # f"--env=CXX=/mnt/zig-c++", # -Wp,-D_FORTIFY_SOURCE=2 # https://github.com/giampaolo/psutil/blob/master/setup.py#L254 # defaults to using python's flags From 7ce40ed64f2dd5560a8fd35324ad5b70bedb8d54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jiri=20Dan=C4=9Bk?= Date: Thu, 20 Nov 2025 18:22:16 +0100 Subject: [PATCH 15/22] Bump Zig to version 0.15.2 in Makefile --- Makefile | 2 +- scripts/sandbox.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 2fde2c83c6..d8f9c855a2 100644 --- a/Makefile +++ b/Makefile @@ -436,7 +436,7 @@ else ifeq ($(ARCH),arm64) ARCH := aarch64 endif -ZIG_VERSION := 0.15.1 +ZIG_VERSION := 0.15.2 ZIG_BINARY := zig-$(ZIG_VERSION) bin/zig-$(ZIG_VERSION): diff --git a/scripts/sandbox.py b/scripts/sandbox.py index 6df75c426a..2194d3dd9f 100755 --- a/scripts/sandbox.py +++ b/scripts/sandbox.py @@ -50,7 +50,7 @@ def main() -> int: # target glibc 2.28 or newer (supports FORTIFY_SOURCE) target = "s390x-linux-gnu.2.34" additional_arguments = [ - f"--volume={os.getcwd()}/bin/zig-0.15.1:/mnt", + f"--volume={os.getcwd()}/bin/zig-0.15.2:/mnt", # f"--env=CC=/mnt/zig cc -target {target}", # f"--env=CXX=/mnt/zig c++ -target {target}", # f"--env=CC=/mnt/zig-cc", @@ -144,12 +144,12 @@ def buildinputs( ) -> list[pathlib.Path]: if not (ROOT_DIR / "bin/buildinputs").exists(): subprocess.check_call([MAKE, "bin/buildinputs"], cwd=ROOT_DIR) - if not (ROOT_DIR / "bin/zig-0.15.1").exists(): - subprocess.check_call([MAKE, "bin/zig-0.15.1"], cwd=ROOT_DIR) - if not (ROOT_DIR / "bin/zig-0.15.1/zigcc").exists(): + if not (ROOT_DIR / "bin/zig-0.15.2").exists(): + subprocess.check_call([MAKE, "bin/zig-0.15.2"], cwd=ROOT_DIR) + if not (ROOT_DIR / "bin/zig-0.15.2/zigcc").exists(): subprocess.check_call([MAKE, "build"], cwd=ROOT_DIR / "scripts/zigcc") - shutil.copy(ROOT_DIR / "scripts/zigcc/bin/zigcc", ROOT_DIR / "bin/zig-0.15.1/zig-cc") - shutil.copy(ROOT_DIR / "scripts/zigcc/bin/zigcc", ROOT_DIR / "bin/zig-0.15.1/zig-c++") + shutil.copy(ROOT_DIR / "scripts/zigcc/bin/zigcc", ROOT_DIR / "bin/zig-0.15.2/zig-cc") + shutil.copy(ROOT_DIR / "scripts/zigcc/bin/zigcc", ROOT_DIR / "bin/zig-0.15.2/zig-c++") stdout = subprocess.check_output([ROOT_DIR / "bin/buildinputs", str(dockerfile)], text=True, cwd=ROOT_DIR, env={"TARGETPLATFORM": platform, **os.environ}) From 5cba801cf6098272c615f75be18d2ff9da9ce181 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jiri=20Dan=C4=9Bk?= Date: Thu, 20 Nov 2025 18:33:21 +0100 Subject: [PATCH 16/22] with zig again --- scripts/sandbox.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/sandbox.py b/scripts/sandbox.py index 2194d3dd9f..f40b29edb2 100755 --- a/scripts/sandbox.py +++ b/scripts/sandbox.py @@ -53,8 +53,8 @@ def main() -> int: f"--volume={os.getcwd()}/bin/zig-0.15.2:/mnt", # f"--env=CC=/mnt/zig cc -target {target}", # f"--env=CXX=/mnt/zig c++ -target {target}", - # f"--env=CC=/mnt/zig-cc", - # f"--env=CXX=/mnt/zig-c++", + f"--env=CC=/mnt/zig-cc", + f"--env=CXX=/mnt/zig-c++", # -Wp,-D_FORTIFY_SOURCE=2 # https://github.com/giampaolo/psutil/blob/master/setup.py#L254 # defaults to using python's flags From ed370888fe6aed4a07c7d3be31778190b3e4f91c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jiri=20Dan=C4=9Bk?= Date: Thu, 20 Nov 2025 19:32:57 +0100 Subject: [PATCH 17/22] Remove `zigcc` wrapper script for s390x cross-compilation --- wrapper.py | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100644 wrapper.py diff --git a/wrapper.py b/wrapper.py deleted file mode 100644 index 69d0201a28..0000000000 --- a/wrapper.py +++ /dev/null @@ -1,25 +0,0 @@ -#! /usr/bin/env python3 -import os -import pathlib -import sys - -def main(): - arg0 = pathlib.Path(sys.argv[0]).name - args = [] - for arg in sys.argv[1:]: - if arg.startswith("-Wp,-D"): - args.append(arg.replace("-Wp,-D", "-D", 1)) - else: - args.append(arg) - - if arg0 == "zig-cc": - args = ["/mnt/zig", "cc", "-target", "s390x-linux-gnu.2.34"] + args - elif arg0 == "zig-c++": - args = ["/mnt/zig", "c++", "-target", "s390x-linux-gnu.2.34"] + args - else: - raise ValueError(f"Unknown argument {arg0}") - - os.execve(args[0], args, os.environ) - -if __name__ == "__main__": - sys.exit(main()) From 9d4971e376d2eb60356607afd6bc8cd7753c7a57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jiri=20Dan=C4=9Bk?= Date: Thu, 20 Nov 2025 19:33:08 +0100 Subject: [PATCH 18/22] Add dynamic target resolution for `zigcc` based on architecture --- scripts/zigcc/zigcc.go | 48 +++++++++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/scripts/zigcc/zigcc.go b/scripts/zigcc/zigcc.go index cb7657570e..8ab5c88b2a 100644 --- a/scripts/zigcc/zigcc.go +++ b/scripts/zigcc/zigcc.go @@ -4,16 +4,46 @@ import ( "fmt" "os" "path/filepath" + "runtime" "strings" "syscall" ) +const ( + zig = "/mnt/zig" +) + +func getTarget() string { + var arch string + switch runtime.GOARCH { + case "amd64": + arch = "x86_64" + case "arm64": + arch = "aarch64" + case "ppc64le": + arch = "ppc64le" + case "s390x": + arch = "s390x" + default: + fmt.Fprintf(os.Stderr, "Error: unknown architecture: %s\n", runtime.GOARCH) + os.Exit(1) + } + return arch + "-linux-gnu.2.34" +} + func processArg0(arg0 string) (string, error) { switch arg0 { - case "zig-cc": + case "cc": return "cc", nil - case "zig-c++": + case "c++": return "c++", nil + + // `llvm-` prefix so that CMake finds it + case "llvm-ar": + return "ar", nil + case "llvm-ranlib": + return "ranlib", nil + default: return "", fmt.Errorf("unknown wrapper name: %s", arg0) } @@ -39,13 +69,15 @@ func main() { os.Exit(1) } - newArgs := make([]string, 0, len(os.Args)+4) - newArgs = append(newArgs, - "/mnt/zig", // Path to the real Zig executable. + target := getTarget() + + newArgs := []string{ + zig, subcommand, - "-target", - "s390x-linux-gnu.2.34", - ) + } + if subcommand == "cc" || subcommand == "c++" { + newArgs = append(newArgs, "-target", target) + } newArgs = append(newArgs, processArgs(os.Args[1:])...) env := os.Environ() From b0cd2502a35ed17d3789c35edf209ba78607e6e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jiri=20Dan=C4=9Bk?= Date: Thu, 20 Nov 2025 22:38:14 +0100 Subject: [PATCH 19/22] Refactor `zigcc` to use environment-based architecture detection, enhance cross-compilation tool wrapping, and document usage in README. --- Makefile | 5 ----- scripts/sandbox.py | 43 +++++++++++++++++++++++++++-------------- scripts/zigcc/README.md | 27 ++++++++++++++++++++++++++ scripts/zigcc/zigcc.go | 19 +++++++++++++----- 4 files changed, 69 insertions(+), 25 deletions(-) create mode 100644 scripts/zigcc/README.md diff --git a/Makefile b/Makefile index d8f9c855a2..0c9a69fc3b 100644 --- a/Makefile +++ b/Makefile @@ -446,11 +446,6 @@ bin/zig-$(ZIG_VERSION): tar -xJf zig-$(ARCH)-linux-$(ZIG_VERSION).tar.xz -C $$TMPDIR --strip-components=1 rm -rf zig-$(ARCH)-linux-$(ZIG_VERSION).tar.xz - printf '%s\n' '#!/bin/sh' 'exec /mnt/zig cc -target s390x-linux-gnu.2.34 "$@"' > $$TMPDIR/zig-cc - printf '%s\n' '#!/bin/sh' 'exec /mnt/zig c++ -target s390x-linux-gnu.2.34 "$@"' > $$TMPDIR/zig-c++ - chmod +x $$TMPDIR/zig-cc - chmod +x $$TMPDIR/zig-c++ - mv $$TMPDIR bin/zig-$(ZIG_VERSION) @echo "Zig installed as bin/zig-$(ZIG_VERSION)" diff --git a/scripts/sandbox.py b/scripts/sandbox.py index f40b29edb2..3aa8fcc774 100755 --- a/scripts/sandbox.py +++ b/scripts/sandbox.py @@ -47,22 +47,35 @@ def main() -> int: with tempfile.TemporaryDirectory(delete=True) as tmpdir: setup_sandbox(prereqs, pathlib.Path(tmpdir)) - # target glibc 2.28 or newer (supports FORTIFY_SOURCE) - target = "s390x-linux-gnu.2.34" additional_arguments = [ + # Mount the zigcc utility f"--volume={os.getcwd()}/bin/zig-0.15.2:/mnt", - # f"--env=CC=/mnt/zig cc -target {target}", - # f"--env=CXX=/mnt/zig c++ -target {target}", - f"--env=CC=/mnt/zig-cc", - f"--env=CXX=/mnt/zig-c++", - # -Wp,-D_FORTIFY_SOURCE=2 - # https://github.com/giampaolo/psutil/blob/master/setup.py#L254 - # defaults to using python's flags - # f"--env=CFLAGS=", - f"--env=bustcachez=", + f"--env=ZIGCC_ARCH={args.platform.split('/')[1]}", + "--unsetenv=ZIGCC_ARCH", + # CMake heeds these + "--env=CC=/mnt/cc", + "--env=CXX=/mnt/c++", + "--unsetenv=CC", + "--unsetenv=CXX", + # CMake ignores these + "--env=AR=/mnt/ar", + "--env=RANLIB=/mnt/ranlib", + "--env=STRIP=/mnt/strip", + "--unsetenv=AR", + "--unsetenv=RANLIB", + "--unsetenv=STRIP", + # Parallelism + "--env=UV_CONCURRENT_BUILDS=8", + "--unsetenv=UV_CONCURRENT_BUILDS", + "--env=UV_CONCURRENT_DOWNLOADS=10", + "--unsetenv=UV_CONCURRENT_DOWNLOADS", + # Workaround for a s390x compilation issue "--env=CXXFLAGS=-Dundefined=64", - f"--unsetenv=CC", - f"--unsetenv=CXX", + "--unsetenv=CXXFLAGS", + + "--env=bustcachez=xx", + "--unsetenv=bustcachez", + tmpdir, ] command = [] @@ -148,8 +161,8 @@ def buildinputs( subprocess.check_call([MAKE, "bin/zig-0.15.2"], cwd=ROOT_DIR) if not (ROOT_DIR / "bin/zig-0.15.2/zigcc").exists(): subprocess.check_call([MAKE, "build"], cwd=ROOT_DIR / "scripts/zigcc") - shutil.copy(ROOT_DIR / "scripts/zigcc/bin/zigcc", ROOT_DIR / "bin/zig-0.15.2/zig-cc") - shutil.copy(ROOT_DIR / "scripts/zigcc/bin/zigcc", ROOT_DIR / "bin/zig-0.15.2/zig-c++") + for alias in ["cc", "c++", "ar", "llvm-ar", "ranlib", "llvm-ranlib", "strip", "llvm-strip"]: + shutil.copy(ROOT_DIR / "scripts/zigcc/bin/zigcc", ROOT_DIR / "bin/zig-0.15.2" / alias) stdout = subprocess.check_output([ROOT_DIR / "bin/buildinputs", str(dockerfile)], text=True, cwd=ROOT_DIR, env={"TARGETPLATFORM": platform, **os.environ}) diff --git a/scripts/zigcc/README.md b/scripts/zigcc/README.md new file mode 100644 index 0000000000..40fc703547 --- /dev/null +++ b/scripts/zigcc/README.md @@ -0,0 +1,27 @@ +# zigcc + +_Launcher for `zig cc`, `zig c++`, and related subcommands for more efficient cross-compilation_ + +## Cross-compilation overview + +### Qemu-user-static + +Docker/Podman can perform cross-compilation using `qemu-user-static`. +The idea is to install the various `qemu-user` binaries as interpreters for foreign architecture binaries. +Launching such binary will then automatically run it under qemu interpreter. + +Docker is uniquely suitable to run binaries like this, because container images bring all dependencies with them. + +### Traditional cross-compilation + +For CMake, I can imagine an approach which involves installing a cross compiler and mounting arm64 docker image to provide arm64 environment with libraries. + + +### Zig + +The `zig cc` command bundles clang in a way that simplifies its usage for cross compilation, + + +## Credits + +This is inspired by diff --git a/scripts/zigcc/zigcc.go b/scripts/zigcc/zigcc.go index 8ab5c88b2a..eee9963819 100644 --- a/scripts/zigcc/zigcc.go +++ b/scripts/zigcc/zigcc.go @@ -4,7 +4,6 @@ import ( "fmt" "os" "path/filepath" - "runtime" "strings" "syscall" ) @@ -15,7 +14,7 @@ const ( func getTarget() string { var arch string - switch runtime.GOARCH { + switch os.Getenv("ZIGCC_ARCH") { case "amd64": arch = "x86_64" case "arm64": @@ -25,9 +24,11 @@ func getTarget() string { case "s390x": arch = "s390x" default: - fmt.Fprintf(os.Stderr, "Error: unknown architecture: %s\n", runtime.GOARCH) + fmt.Fprintf(os.Stderr, "Error: unknown architecture: %s\n", os.Getenv("ZIGCC_ARCH")) os.Exit(1) } + + // target glibc 2.28 or newer (supports FORTIFY_SOURCE) return arch + "-linux-gnu.2.34" } @@ -39,10 +40,15 @@ func processArg0(arg0 string) (string, error) { return "c++", nil // `llvm-` prefix so that CMake finds it - case "llvm-ar": + // https://gitlab.kitware.com/cmake/cmake/-/issues/23554 + // https://gitlab.kitware.com/cmake/cmake/-/issues/18712#note_1006035 + // ../../libtool: line 1887: /mnt/ar: No such file or directory + case "ar", "llvm-ar": return "ar", nil - case "llvm-ranlib": + case "ranlib", "llvm-ranlib": return "ranlib", nil + case "strip", "llvm-strip": + return "strip", nil default: return "", fmt.Errorf("unknown wrapper name: %s", arg0) @@ -52,6 +58,9 @@ func processArg0(arg0 string) (string, error) { func processArgs(args []string) []string { newArgs := make([]string, 0, len(args)) for _, arg := range args { + // deal with -Wp,-D_FORTIFY_SOURCE=2: + // this comes in https://github.com/giampaolo/psutil/blob/master/setup.py#L254 + // build defaults to using python's flags and they are the RHEL fortified ones if strings.HasPrefix(arg, "-Wp,") { newArgs = append(newArgs, strings.Split(arg, ",")[1:]...) } else { From c441a6f450bf599e710514cf437615334f4f8cb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jiri=20Dan=C4=9Bk?= Date: Thu, 20 Nov 2025 22:39:47 +0100 Subject: [PATCH 20/22] Clean up unused environment variables in `sandbox.py`. --- scripts/sandbox.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/scripts/sandbox.py b/scripts/sandbox.py index 3aa8fcc774..6717e1eb23 100755 --- a/scripts/sandbox.py +++ b/scripts/sandbox.py @@ -64,18 +64,10 @@ def main() -> int: "--unsetenv=AR", "--unsetenv=RANLIB", "--unsetenv=STRIP", - # Parallelism - "--env=UV_CONCURRENT_BUILDS=8", - "--unsetenv=UV_CONCURRENT_BUILDS", - "--env=UV_CONCURRENT_DOWNLOADS=10", - "--unsetenv=UV_CONCURRENT_DOWNLOADS", # Workaround for a s390x compilation issue "--env=CXXFLAGS=-Dundefined=64", "--unsetenv=CXXFLAGS", - "--env=bustcachez=xx", - "--unsetenv=bustcachez", - tmpdir, ] command = [] From e51c1a0d26f962c382679741fac2ea5351b80386 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jiri=20Dan=C4=9Bk?= Date: Fri, 21 Nov 2025 09:22:47 +0100 Subject: [PATCH 21/22] Improve error messages in `zigcc.go` for clarity and update `ppc64le` architecture string to `powerpc64le`. --- scripts/zigcc/zigcc.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/zigcc/zigcc.go b/scripts/zigcc/zigcc.go index eee9963819..6af58157ea 100644 --- a/scripts/zigcc/zigcc.go +++ b/scripts/zigcc/zigcc.go @@ -20,11 +20,11 @@ func getTarget() string { case "arm64": arch = "aarch64" case "ppc64le": - arch = "ppc64le" + arch = "powerpc64le" case "s390x": arch = "s390x" default: - fmt.Fprintf(os.Stderr, "Error: unknown architecture: %s\n", os.Getenv("ZIGCC_ARCH")) + fmt.Fprintf(os.Stderr, "zigcc.go: Error: unknown architecture: %s\n", os.Getenv("ZIGCC_ARCH")) os.Exit(1) } @@ -51,7 +51,7 @@ func processArg0(arg0 string) (string, error) { return "strip", nil default: - return "", fmt.Errorf("unknown wrapper name: %s", arg0) + return "", fmt.Errorf("zigcc.go: Error: unknown wrapper name: %s", arg0) } } @@ -74,7 +74,7 @@ func main() { arg0 := filepath.Base(os.Args[0]) subcommand, err := processArg0(arg0) if err != nil { - fmt.Fprintf(os.Stderr, "Error: %v\n", err) + fmt.Fprintf(os.Stderr, "zigcc.go: Error: %v\n", err) os.Exit(1) } @@ -91,7 +91,7 @@ func main() { env := os.Environ() if err := syscall.Exec(newArgs[0], newArgs, env); err != nil { - fmt.Fprintf(os.Stderr, "Error executing zig: %v\n", err) + fmt.Fprintf(os.Stderr, "zigcc.go: Error executing zig: %v\n", err) os.Exit(1) } } From 1f0cac9213b3dc8754355ed58b30763b2d142e25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jiri=20Dan=C4=9Bk?= Date: Fri, 21 Nov 2025 14:43:46 +0100 Subject: [PATCH 22/22] Enhance `zigcc` wrapper for improved cross-compilation: update `getTarget` logic, add glibc version handling, and integrate debugging tools. Document usage and background in README. Add tests and Dockerfile for validation. --- scripts/sandbox.py | 6 ++++- scripts/zigcc/README.md | 41 +++++++++++++++++++++++++++++++ scripts/zigcc/test/Dockerfile | 35 ++++++++++++++++++++++++++ scripts/zigcc/test/run.py | 46 +++++++++++++++++++++++++++++++++++ scripts/zigcc/zigcc.go | 38 +++++++++++++++++++++++++---- 5 files changed, 160 insertions(+), 6 deletions(-) create mode 100644 scripts/zigcc/test/Dockerfile create mode 100644 scripts/zigcc/test/run.py diff --git a/scripts/sandbox.py b/scripts/sandbox.py index 6717e1eb23..6574b74f7e 100755 --- a/scripts/sandbox.py +++ b/scripts/sandbox.py @@ -65,7 +65,11 @@ def main() -> int: "--unsetenv=RANLIB", "--unsetenv=STRIP", # Workaround for a s390x compilation issue - "--env=CXXFLAGS=-Dundefined=64", + # Codeserver: SPDLOG_CONSTEXPR_FUNC is to work around a consteval issue with zig c++ + # ../deps/spdlog/include/spdlog/details/fmt_helper.h:105:54: error: call to consteval function 'fmt::basic_format_string<...>' is not a constant expression + # Clang (via Zig) is stricter about consteval requirements than GCC + # The format string "{:02}" cannot be evaluated as a constant expression in this context + "--env=CXXFLAGS=-Dundefined=64 -DFMT_CONSTEVAL= -DSPDLOG_CONSTEXPR_FUNC=", "--unsetenv=CXXFLAGS", tmpdir, diff --git a/scripts/zigcc/README.md b/scripts/zigcc/README.md index 40fc703547..b51b279e36 100644 --- a/scripts/zigcc/README.md +++ b/scripts/zigcc/README.md @@ -2,6 +2,25 @@ _Launcher for `zig cc`, `zig c++`, and related subcommands for more efficient cross-compilation_ +This script helps by using a native compiler binary to compile for the target architecture. +This means that the compiler then is running at the native speed. + +## Background + +Cross-compilation means compiling for a different architecture than the one on which the compiler is running. +Specifically, it is when you're creating a workbench container image for say x86_64 on an arm64 machine such as an M-series MacBook. +Everything that's running in the container will run slower, because it has to run under qemu. + +This slowdown is especially noticeable when compiling C/C++ code for IBM Power and Z, such as Python extension modules that don't have precompiled binaries for these architectures on PyPI. + +## Usage + +```commandline +gmake codeserver-ubi9-python-3.12 BUILD_ARCH=linux/s390x CONTAINER_BUILD_CACHE_ARGS= +``` + +This is about 50% faster than cross-compiling through `qemu-s390x-static` or `qemu-ppc64le-static`. + ## Cross-compilation overview ### Qemu-user-static @@ -19,9 +38,31 @@ For CMake, I can imagine an approach which involves installing a cross compiler ### Zig +https://zig.guide/working-with-c/zig-cc/ + The `zig cc` command bundles clang in a way that simplifies its usage for cross compilation, +#### Wrapper (zigcc.go) + +We need a wrapper so that we can transform CLI arguments to work with `zig cc`. + +The main problem is the `-Wl,D_FORTIFY_SOURCE=2` flag, because zig has limited handling for -Wl, and does not do -Wl,D correctly. + +The wrapper should be written in a low-overhead language, like Go, or possibly Bash, certainly not Python. +The lower the overhead of the wrapper, the better, since the compiler is invoked many times during a typical build. + +### Debugging + +To observe the effect of the wrapper, we can use `execsnoop` from `bcc-tools` to monitor the compiler invocations during a container build. + +```commandline +$ podman machine ssh +# bootc usr-overlay +# dnf install bcc-tools +# /usr/share/bcc/tools/execsnoop +``` + ## Credits This is inspired by diff --git a/scripts/zigcc/test/Dockerfile b/scripts/zigcc/test/Dockerfile new file mode 100644 index 0000000000..ed8b52f261 --- /dev/null +++ b/scripts/zigcc/test/Dockerfile @@ -0,0 +1,35 @@ +FROM quay.io/centos/centos:stream9 + +# Don't install the GCC compiler, we will inject our own CC +#RUN dnf install -y gcc + +# Check CC is in fact injected +RUN /bin/bash <<'EOF' +set -Eeuxo pipefail +ls ${CC} +${CC} --version +EOF + +#RUN /mnt/zig clang -x c /dev/null --no-default-config -target s390x-unknown-linux5.10.0-gnu2.34.0 -mhard-float -fno-PIE -fPIC -gdwarf-4 -gdwarf32 -fno-lto -MD -MV -MF /root/.cache/zig/tmp/4ee4407ea328f5c8-null.o.d -fhosted -fno-omit-frame-pointer +#RUN /mnt/zig cc -target s390x-linux-gnu.2.34 --sysroot / -isystem /usr/include -L/usr/lib64 -isystem /usr/local/include -L/usr/local/lib64 -dM -E -x c /dev/null +RUN /mnt/zig -cc1 -triple x86_64-unknown-linux5.10.0-gnu2.34.0 -E -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name abi-note.S -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -fmath-errno -ffp-contract=on -fno-rounding-math + +# Create a directory for the program +RUN mkdir /app + +# Copy the C source code into the container +RUN sh -c 'cat > /app/hello.c' < +int main() { + printf("Hello, World!\\n"); + return 0; +} +EOF + +# Compile the C program +RUN ${CC} /app/hello.c -o /app/hello +# Check it works +RUN /app/hello + +# Set the entry point to run the compiled program +CMD ["/app/hello"] diff --git a/scripts/zigcc/test/run.py b/scripts/zigcc/test/run.py new file mode 100644 index 0000000000..a850d3e68e --- /dev/null +++ b/scripts/zigcc/test/run.py @@ -0,0 +1,46 @@ +#! /usr/bin/env python3 + +import argparse +import logging +import pathlib +import subprocess +import sys +from typing import cast + +ROOT_DIR = pathlib.Path(__file__).parent.parent.parent.parent + +logging.basicConfig(level=logging.INFO) +logging.root.name = pathlib.Path(__file__).name + + +class Args(argparse.Namespace): + platform: str + + +def main() -> int: + p = argparse.ArgumentParser() + p.add_argument("--platform", default="linux/amd64", help="Target platform for the build") + args = cast(Args, p.parse_args()) + + dockerfile_path = ROOT_DIR / "scripts" / "zigcc" / "test" / "Dockerfile" + sandbox_script_path = ROOT_DIR / "scripts" / "sandbox.py" + + return subprocess.call( + [sys.executable, str(sandbox_script_path), + "--dockerfile", str(dockerfile_path), + "--platform", args.platform, + "--", + "podman", "build", + "--no-cache", + "--platform", args.platform, + "-t", "hello-world-test", + # dockerfile path in podman command is required, Dockerfile is not copied to sandbox + "-f", str(dockerfile_path), + "{};"], + # sandbox.py assumes running from repo root + cwd=ROOT_DIR + ) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/zigcc/zigcc.go b/scripts/zigcc/zigcc.go index 6af58157ea..4e76ebbe81 100644 --- a/scripts/zigcc/zigcc.go +++ b/scripts/zigcc/zigcc.go @@ -4,15 +4,17 @@ import ( "fmt" "os" "path/filepath" + "slices" "strings" "syscall" ) const ( - zig = "/mnt/zig" + zig = "/mnt/zig" + glibcVersion = "2.34" ) -func getTarget() string { +func getTarget(subcommand string, args []string) string { var arch string switch os.Getenv("ZIGCC_ARCH") { case "amd64": @@ -29,7 +31,24 @@ func getTarget() string { } // target glibc 2.28 or newer (supports FORTIFY_SOURCE) - return arch + "-linux-gnu.2.34" + //return arch + "-linux-gnu" + // specify glibc version with . separator between gnu and the version, otherwise + // error: unable to parse target query 'x86_64-linux-gnu2.34': UnknownApplicationBinaryInterface + // error: unable to parse target query 'x86_64-linux-gnu+2.34': UnknownApplicationBinaryInterface + // error: unable to parse target query 'x86_64-unknown-linux-gnu.2.34': UnknownOperatingSystem + // error: unable to parse target query 'x86_64-linux-gnuabi2.34': UnknownApplicationBinaryInterface + // but for some reason, I'm still having problems with npm + // zig c++ -target s390x-linux-gnu.2.34 -o Release/obj.target/windows.node -shared -pthread -rdynamic -m64 -march=z196 -Wl,-soname=windows.node -Wl,--start-group -Wl,--end-group + // npm error zig: error: version '.2.34' in target triple 's390x-unknown-linux-gnu.2.34' is invalid + if subcommand == "c++" { + // https://github.com/ziglang/zig/issues/25994#issuecomment-3562961055 + return arch + "-linux-gnu" + } + if slices.Contains(args, "--version") || slices.Contains(args, "-v") { + // https://github.com/ziglang/zig/issues/22269 + return arch + "-linux-gnu" + } + return arch + "-linux-gnu" + "." + glibcVersion } func processArg0(arg0 string) (string, error) { @@ -78,7 +97,8 @@ func main() { os.Exit(1) } - target := getTarget() + argv := os.Args[1:] + target := getTarget(subcommand, argv) newArgs := []string{ zig, @@ -86,8 +106,16 @@ func main() { } if subcommand == "cc" || subcommand == "c++" { newArgs = append(newArgs, "-target", target) + // codeserver: :33:10: fatal error: 'X11/Xlib.h' file not found + // -isystem=... does not work, requires passing as two separate args + newArgs = append(newArgs, + "--sysroot", "/", + "-isystem", "/usr/include", + "-L/usr/lib64", + "-isystem", "/usr/local/include", + "-L/usr/local/lib64") } - newArgs = append(newArgs, processArgs(os.Args[1:])...) + newArgs = append(newArgs, processArgs(argv)...) env := os.Environ() if err := syscall.Exec(newArgs[0], newArgs, env); err != nil {