mirror of
https://github.com/openai/codex.git
synced 2026-09-06 15:29:32 +00:00
Add explicit toolchain inputs for native voice builds (#43109)
## Why Native voice builds ignore ambient compiler flags, but need a way to accept declared compiler, linker, and archive-tool inputs. ## What changed - Add repeated `--c-flag`, `--cxx-flag`, and `--link-flag` options plus optional `--ar` and `--ranlib` paths for Unix builds. Record flags and pass them through CMake, Meson, and libffi, including Objective-C flags on macOS. - Preserve literal libffi compiler arguments with response files. Reject flags containing whitespace, libffi paths requiring shell quoting, and Unix toolchain overrides on Windows. - Add a pinned Bazel `pkg-config` tool built with LLVM archive tools and native macOS configure checks. Register the pinned Make toolchain and patch its bootstrap to pass preprocessor flags and enable cross-compilation configuration. ## Testing Extend native-build tests to verify compiler and linker flags, explicit archive tools, Meson and Objective-C inputs, literal definitions through shell and recursive Make expansion, and rejection of unsupported overrides. GitOrigin-RevId: e5ea541ec76abfa170f6b0411b0f14cd698c07e7
This commit is contained in:
committed by
copyberry
parent
e4ce83419b
commit
e67a8ae6c9
2
.bazelrc
2
.bazelrc
@@ -27,6 +27,8 @@ common --incompatible_strict_action_env
|
||||
# Not ideal, but We need to allow dotslash to be found
|
||||
common:linux --test_env=PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
|
||||
common:macos --test_env=PATH=/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
|
||||
# Native pkg-config configure checks execute target programs.
|
||||
common:macos --strategy=CcConfigureMakeRule=remote,sandboxed,local
|
||||
|
||||
# Pass through some env vars Windows needs to use powershell?
|
||||
common:windows --test_env=SYSTEMROOT
|
||||
|
||||
16
MODULE.bazel
16
MODULE.bazel
@@ -147,6 +147,22 @@ bazel_dep(name = "rules_platform", version = "0.1.0")
|
||||
bazel_dep(name = "aws-lc", version = "5.1.0.bcr.1")
|
||||
bazel_dep(name = "rules_rs", version = "0.0.96")
|
||||
bazel_dep(name = "rules_foreign_cc", version = "0.15.1")
|
||||
single_version_override(
|
||||
module_name = "rules_foreign_cc",
|
||||
patch_strip = 1,
|
||||
patches = [
|
||||
"//patches:rules_foreign_cc_make_cppflags.patch",
|
||||
"//patches:rules_foreign_cc_make_xcompile.patch",
|
||||
],
|
||||
version = "0.15.1",
|
||||
)
|
||||
|
||||
# Use the existing pinned Make build for foreign_cc consumers, including the
|
||||
# native voice pkg-config bootstrap, instead of a PATH-provided Make.
|
||||
register_toolchains("@rules_foreign_cc//toolchains:built_make_toolchain")
|
||||
|
||||
foreign_cc_tools = use_extension("@rules_foreign_cc//foreign_cc:extensions.bzl", "tools")
|
||||
use_repo(foreign_cc_tools, "pkgconfig_src")
|
||||
|
||||
single_version_override(
|
||||
module_name = "rules_rs",
|
||||
|
||||
2
MODULE.bazel.lock
generated
2
MODULE.bazel.lock
generated
@@ -60,7 +60,6 @@
|
||||
"https://bcr.bazel.build/modules/bazel_lib/3.2.2/MODULE.bazel": "e2c890c8a515d6bca9c66d47718aa9e44b458fde64ec7204b8030bf2d349058c",
|
||||
"https://bcr.bazel.build/modules/bazel_lib/3.2.2/source.json": "9e84e115c20e14652c5c21401ae85ff4daa8702e265b5c0b3bf89353f17aa212",
|
||||
"https://bcr.bazel.build/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8",
|
||||
"https://bcr.bazel.build/modules/bazel_skylib/1.1.1/MODULE.bazel": "1add3e7d93ff2e6998f9e118022c84d163917d912f5afafb3058e3d2f1545b5e",
|
||||
"https://bcr.bazel.build/modules/bazel_skylib/1.2.0/MODULE.bazel": "44fe84260e454ed94ad326352a698422dbe372b21a1ac9f3eab76eb531223686",
|
||||
"https://bcr.bazel.build/modules/bazel_skylib/1.2.1/MODULE.bazel": "f35baf9da0efe45fa3da1696ae906eea3d615ad41e2e3def4aeb4e8bc0ef9a7a",
|
||||
"https://bcr.bazel.build/modules/bazel_skylib/1.3.0/MODULE.bazel": "20228b92868bf5cfc41bda7afc8a8ba2a543201851de39d990ec957b513579c5",
|
||||
@@ -171,7 +170,6 @@
|
||||
"https://bcr.bazel.build/modules/rules_cc/0.2.8/MODULE.bazel": "f1df20f0bf22c28192a794f29b501ee2018fa37a3862a1a2132ae2940a23a642",
|
||||
"https://bcr.bazel.build/modules/rules_foreign_cc/0.15.1/MODULE.bazel": "c2c60d26c79fda484acb95cdbec46e89d6b28b4845cb277160ce1e0c8622bb88",
|
||||
"https://bcr.bazel.build/modules/rules_foreign_cc/0.15.1/source.json": "a161811a63ba8a859086da3b7ff3ad04f2e9c255d7727b41087103fc0eb22f55",
|
||||
"https://bcr.bazel.build/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "c9e8c682bf75b0e7c704166d79b599f93b72cfca5ad7477df596947891feeef6",
|
||||
"https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/MODULE.bazel": "40c97d1144356f52905566c55811f13b299453a14ac7769dfba2ac38192337a8",
|
||||
"https://bcr.bazel.build/modules/rules_java/4.0.0/MODULE.bazel": "5a78a7ae82cd1a33cef56dc578c7d2a46ed0dca12643ee45edbb8417899e6f74",
|
||||
"https://bcr.bazel.build/modules/rules_java/5.3.5/MODULE.bazel": "a4ec4f2db570171e3e5eb753276ee4b389bae16b96207e9d3230895c99644b86",
|
||||
|
||||
@@ -8,6 +8,8 @@ exports_files([
|
||||
"rules_rust_windows_msvc_direct_link_args.patch",
|
||||
"rules_rust_windows_process_wrapper_skip_temp_outputs.patch",
|
||||
"rules_cc_rusty_v8_custom_libcxx.patch",
|
||||
"rules_foreign_cc_make_cppflags.patch",
|
||||
"rules_foreign_cc_make_xcompile.patch",
|
||||
"rules_rs_build_script_deps_annotation.patch",
|
||||
"rules_rs_windows_msvc_linker.patch",
|
||||
"rules_rs_zlib_snapshot_urls.patch",
|
||||
|
||||
15
patches/rules_foreign_cc_make_cppflags.patch
Normal file
15
patches/rules_foreign_cc_make_cppflags.patch
Normal file
@@ -0,0 +1,15 @@
|
||||
diff --git a/foreign_cc/built_tools/make_build.bzl b/foreign_cc/built_tools/make_build.bzl
|
||||
--- a/foreign_cc/built_tools/make_build.bzl
|
||||
+++ b/foreign_cc/built_tools/make_build.bzl
|
||||
@@ -114,6 +114,11 @@
|
||||
"ARFLAGS": _join_flags_list(ctx.workspace_name, arflags),
|
||||
"CC": absolute_cc,
|
||||
"CFLAGS": _join_flags_list(ctx.workspace_name, non_sysroot_cflags),
|
||||
+ # Configure's preprocessor checks need the declared header paths too.
|
||||
+ "CPPFLAGS": " ".join([
|
||||
+ "$${CPPFLAGS:-}$$",
|
||||
+ _join_flags_list(ctx.workspace_name, non_sysroot_cflags),
|
||||
+ ]).strip(),
|
||||
"LD": absolute_ld,
|
||||
"LDFLAGS": _join_flags_list(ctx.workspace_name, non_sysroot_ldflags),
|
||||
})
|
||||
10
patches/rules_foreign_cc_make_xcompile.patch
Normal file
10
patches/rules_foreign_cc_make_xcompile.patch
Normal file
@@ -0,0 +1,10 @@
|
||||
diff --git a/toolchains/private/BUILD.bazel b/toolchains/private/BUILD.bazel
|
||||
--- a/toolchains/private/BUILD.bazel
|
||||
+++ b/toolchains/private/BUILD.bazel
|
||||
@@ -23,5 +23,6 @@
|
||||
make_tool(
|
||||
name = "make_tool",
|
||||
srcs = "@gnumake_src//:all_srcs",
|
||||
+ configure_xcompile = True,
|
||||
tags = ["manual"],
|
||||
)
|
||||
58
third_party/voice/BUILD.bazel
vendored
58
third_party/voice/BUILD.bazel
vendored
@@ -1,3 +1,6 @@
|
||||
load("@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl", "native_tool_toolchain")
|
||||
load(":pkg_config.bzl", "pkg_config")
|
||||
|
||||
# All native build consumers must receive the same pinned source manifest.
|
||||
exports_files(["opus-toolchain.cmake"])
|
||||
|
||||
@@ -45,3 +48,58 @@ filegroup(
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
alias(
|
||||
name = "pkg_config_linker",
|
||||
actual = select({
|
||||
"@platforms//os:macos": "@llvm//tools:ld64.lld",
|
||||
"//conditions:default": "@llvm//tools:ld.lld",
|
||||
}),
|
||||
)
|
||||
|
||||
# Keep the pinned pkg-config sources, but explicitly use LLVM archive tools.
|
||||
# The upstream bootstrap otherwise selects host ar/ranlib on macOS.
|
||||
pkg_config(
|
||||
name = "pkg_config",
|
||||
build_data = [
|
||||
":pkg_config_linker",
|
||||
"@llvm//tools:llvm-ar",
|
||||
"@llvm//tools:llvm-ranlib",
|
||||
],
|
||||
# foreign_cc normalizes copied source timestamps, preserving the release
|
||||
# configure files when remote execution materializes inputs in a new order.
|
||||
configure_in_place = True,
|
||||
configure_options = [
|
||||
"--with-internal-glib",
|
||||
"--disable-shared",
|
||||
],
|
||||
copts = ["-Wno-int-conversion"],
|
||||
env = {
|
||||
"AR": "$(execpath @llvm//tools:llvm-ar)",
|
||||
"LD": "$(execpath :pkg_config_linker)",
|
||||
"PKG_CONFIG": "/bin/false",
|
||||
"RANLIB": "$(execpath @llvm//tools:llvm-ranlib)",
|
||||
},
|
||||
lib_source = "@pkgconfig_src//:all_srcs",
|
||||
# foreign_cc rewrites *-config files as text. Keep the executable out of
|
||||
# that transformation, which otherwise corrupts its native code signature.
|
||||
out_binaries = ["pkg-config-tool"],
|
||||
out_include_dir = "",
|
||||
out_static_libs = [],
|
||||
postfix_script = 'mv "$$INSTALLDIR/bin/pkg-config" "$$INSTALLDIR/bin/pkg-config-tool"',
|
||||
tags = ["manual"],
|
||||
target_compatible_with = select({
|
||||
"@platforms//os:windows": ["@platforms//:incompatible"],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
# Internal GLib needs no pkg-config. Avoid bootstrapping another copy first;
|
||||
# the explicit failing command above forbids falling back to a host copy.
|
||||
toolchain = "@rules_foreign_cc//toolchains:preinstalled_pkgconfig_toolchain",
|
||||
)
|
||||
|
||||
native_tool_toolchain(
|
||||
name = "pkg_config_tool",
|
||||
path = "$(execpath :pkg_config)",
|
||||
tags = ["manual"],
|
||||
target = ":pkg_config",
|
||||
)
|
||||
|
||||
10
third_party/voice/README.md
vendored
10
third_party/voice/README.md
vendored
@@ -77,7 +77,15 @@ transitive imports must satisfy the same private-library and system-import check
|
||||
## Native build recipe
|
||||
|
||||
`build_native.py` runs the unmodified upstream build systems in a new output
|
||||
directory, using the same archives. Specify the target and existing compiler,
|
||||
directory, using the same archives. Unix builds accept repeated `--c-flag=...`,
|
||||
`--cxx-flag=...`, `--link-flag=...` and optional `--ar`/`--ranlib` inputs.
|
||||
These overrides are rejected on Windows; ambient flags remain ignored.
|
||||
Libffi uses compiler response files to preserve literal definitions through
|
||||
configure, recursive Make and libtool. Response-file paths must not require shell
|
||||
quoting. Its
|
||||
Autoconf recipe cannot preserve flags containing whitespace; those are rejected.
|
||||
Declared archiver and ranlib paths must not require shell quoting for libffi.
|
||||
Specify the target and existing compiler,
|
||||
CMake, make, pkg-config and shell paths explicitly. It requires a matching
|
||||
native host: GNU Linux, macOS, or Windows MSVC, on x64 or ARM64.
|
||||
|
||||
|
||||
78
third_party/voice/build_native.py
vendored
78
third_party/voice/build_native.py
vendored
@@ -66,6 +66,25 @@ class NativeBuild:
|
||||
name: getattr(args, name).absolute()
|
||||
for name in ("cc", "cxx", "cmake", "make", "pkg_config", "shell")
|
||||
}
|
||||
self.toolchain.update(
|
||||
(name, path.absolute())
|
||||
for name in ("ar", "ranlib")
|
||||
if (path := getattr(args, name, None)) is not None
|
||||
)
|
||||
self.quote = subprocess.list2cmdline if self.windows else shlex.join
|
||||
self.flags = {
|
||||
variable: list(getattr(args, argument, []))
|
||||
for variable, argument in (
|
||||
("CFLAGS", "c_flag"),
|
||||
("CXXFLAGS", "cxx_flag"),
|
||||
("LDFLAGS", "link_flag"),
|
||||
)
|
||||
}
|
||||
if self.windows and (
|
||||
any(self.flags.values())
|
||||
or any(name in self.toolchain for name in ("ar", "ranlib"))
|
||||
):
|
||||
raise ValueError("Explicit toolchain overrides require a Unix build host")
|
||||
self.bootstrap_make = (args.bootstrap_make or args.make).absolute()
|
||||
for tool in (*self.toolchain.values(), self.bootstrap_make):
|
||||
if not tool.is_file():
|
||||
@@ -127,6 +146,14 @@ class NativeBuild:
|
||||
}
|
||||
)
|
||||
self.cmake_platform = []
|
||||
self.environment.update(
|
||||
(name, self.quote(flags)) for name, flags in self.flags.items() if flags
|
||||
)
|
||||
self.environment.update(
|
||||
(name.upper(), self.quote([str(self.toolchain[name])]))
|
||||
for name in ("ar", "ranlib")
|
||||
if name in self.toolchain
|
||||
)
|
||||
if args.target.endswith("apple-darwin"):
|
||||
self.environment["MACOSX_DEPLOYMENT_TARGET"] = args.deployment_target
|
||||
self.cmake_platform = [
|
||||
@@ -142,6 +169,7 @@ class NativeBuild:
|
||||
self.record = {
|
||||
"target": args.target,
|
||||
"deployment_target": args.deployment_target,
|
||||
"flags": self.flags,
|
||||
"steps": [],
|
||||
}
|
||||
|
||||
@@ -199,6 +227,11 @@ class NativeBuild:
|
||||
"-DCMAKE_INSTALL_LIBDIR=lib",
|
||||
f"-DCMAKE_C_COMPILER={self.toolchain['cc']}",
|
||||
f"-DCMAKE_CXX_COMPILER={self.toolchain['cxx']}",
|
||||
*(
|
||||
f"-DCMAKE_{name.upper()}={self.toolchain[name]}"
|
||||
for name in ("ar", "ranlib")
|
||||
if name in self.toolchain
|
||||
),
|
||||
f"-DCMAKE_PREFIX_PATH={self.prefix}",
|
||||
"-DCMAKE_FIND_USE_PACKAGE_REGISTRY=OFF",
|
||||
"-DCMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY=OFF",
|
||||
@@ -223,8 +256,7 @@ class NativeBuild:
|
||||
def meson(self, name, options):
|
||||
directory = self.output / "build" / name
|
||||
meson = [sys.executable, self.sources["meson"] / "meson.py"]
|
||||
quote = subprocess.list2cmdline if self.windows else shlex.join
|
||||
include = quote([f"{'/I' if self.windows else '-I'}{self.prefix / 'include'}"])
|
||||
include = f"{'/I' if self.windows else '-I'}{self.prefix / 'include'}"
|
||||
link = (
|
||||
[f"/LIBPATH:{self.prefix / 'lib'}"]
|
||||
if self.windows
|
||||
@@ -233,8 +265,14 @@ class NativeBuild:
|
||||
if self.args.target.endswith("unknown-linux-gnu"):
|
||||
link[-1] = "-Wl,-rpath,$ORIGIN:$ORIGIN/.."
|
||||
self.environment.update(
|
||||
{"CFLAGS": include, "CXXFLAGS": include, "LDFLAGS": quote(link)}
|
||||
{
|
||||
name: self.quote([*flags, *(link if name == "LDFLAGS" else [include])])
|
||||
for name, flags in self.flags.items()
|
||||
}
|
||||
)
|
||||
if self.args.target.endswith("apple-darwin"):
|
||||
self.environment["OBJC"] = str(self.toolchain["cc"])
|
||||
self.environment["OBJCFLAGS"] = self.environment["CFLAGS"]
|
||||
self.run(
|
||||
name + "-configure",
|
||||
[
|
||||
@@ -340,6 +378,36 @@ class NativeBuild:
|
||||
"CONFIG_SHELL": self.posix_path(self.toolchain["shell"]),
|
||||
}
|
||||
)
|
||||
else:
|
||||
for name in ("ar", "ranlib"):
|
||||
if name in self.toolchain:
|
||||
path = str(self.toolchain[name])
|
||||
if shlex.quote(path) != path:
|
||||
raise ValueError(
|
||||
f"libffi {name} path cannot require shell quoting"
|
||||
)
|
||||
# Response files preserve compiler arguments through Autoconf's word
|
||||
# splitting and Make/libtool's shell expansion without losing flags
|
||||
# added by configure (such as -fexceptions).
|
||||
for name, flags in self.flags.items():
|
||||
if any(
|
||||
any(character.isspace() for character in flag) for flag in flags
|
||||
):
|
||||
raise ValueError("libffi flags cannot contain whitespace")
|
||||
if flags:
|
||||
response = ffi_build / f"{name.lower()}.rsp"
|
||||
if shlex.quote(str(response)) != str(response):
|
||||
raise ValueError(
|
||||
"libffi response-file path cannot require shell quoting"
|
||||
)
|
||||
response.write_text(
|
||||
"\n".join(
|
||||
'"' + flag.replace("\\", "\\\\").replace('"', '\\"') + '"'
|
||||
for flag in flags
|
||||
)
|
||||
+ "\n"
|
||||
)
|
||||
environment[name] = f"@{response}"
|
||||
self.run(
|
||||
"libffi-configure",
|
||||
[
|
||||
@@ -446,6 +514,10 @@ def main():
|
||||
type=Path,
|
||||
help="NMake on Windows; defaults to --make elsewhere",
|
||||
)
|
||||
for name in ("ar", "ranlib"):
|
||||
parser.add_argument(f"--{name}", type=Path)
|
||||
for name in ("c-flag", "cxx-flag", "link-flag"):
|
||||
parser.add_argument(f"--{name}", action="append", default=[])
|
||||
parser.add_argument("--target", required=True)
|
||||
parser.add_argument(
|
||||
"--deployment-target",
|
||||
|
||||
33
third_party/voice/pkg_config.bzl
vendored
Normal file
33
third_party/voice/pkg_config.bzl
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
"""Build the pinned pkg-config with native Mac configure checks."""
|
||||
|
||||
load("@rules_foreign_cc//foreign_cc:defs.bzl", "configure_make_variant")
|
||||
|
||||
def pkg_config(name, **kwargs):
|
||||
"""Keep Linux remote builds and select native Mac producers by OS and CPU."""
|
||||
tags = kwargs.pop("tags", [])
|
||||
configure_make_variant(
|
||||
name = name + "_default",
|
||||
tags = tags,
|
||||
**kwargs
|
||||
)
|
||||
for arch in ["aarch64", "x86_64"]:
|
||||
native.config_setting(
|
||||
name = name + "_macos_" + arch,
|
||||
constraint_values = ["@platforms//os:macos", "@platforms//cpu:" + arch],
|
||||
)
|
||||
configure_make_variant(
|
||||
name = name + "_native_macos_" + arch,
|
||||
exec_compatible_with = ["@platforms//os:macos", "@platforms//cpu:" + arch],
|
||||
tags = tags + ["no-remote-exec"],
|
||||
**kwargs
|
||||
)
|
||||
native.alias(
|
||||
name = name,
|
||||
tags = tags,
|
||||
actual = select({
|
||||
":" + name + "_macos_aarch64": ":" + name + "_native_macos_aarch64",
|
||||
":" + name + "_macos_x86_64": ":" + name + "_native_macos_x86_64",
|
||||
"//conditions:default": ":" + name + "_default",
|
||||
}),
|
||||
visibility = kwargs.get("visibility"),
|
||||
)
|
||||
191
third_party/voice/test_build_native.py
vendored
191
third_party/voice/test_build_native.py
vendored
@@ -96,16 +96,39 @@ class NativeBuildTests(unittest.TestCase):
|
||||
self.args.cxx = compiler
|
||||
self.args.cmake = Path(tools["cmake"])
|
||||
self.args.make = Path(tools["make"])
|
||||
headers = self.root / "declared headers"
|
||||
headers.mkdir()
|
||||
(headers / "build_flag.h").write_text(
|
||||
'#define BUILD_FLAG "linked with flags"\n'
|
||||
)
|
||||
self.args.cxx_flag = [f"-I{headers}"]
|
||||
for name in ("ar", "ranlib"):
|
||||
tool = shutil.which(name)
|
||||
if tool is None:
|
||||
self.skipTest(f"Requires {name}")
|
||||
wrapper = self.root / f"declared {name}"
|
||||
wrapper.write_text(
|
||||
"#!/bin/sh\n"
|
||||
f"touch {shlex.quote(str(self.root / (name + '.used')))}\n"
|
||||
f'exec {shlex.quote(tool)} "$@"\n'
|
||||
)
|
||||
wrapper.chmod(0o755)
|
||||
setattr(self.args, name, wrapper)
|
||||
source = self.root / "cpp-source"
|
||||
source.mkdir()
|
||||
(source / "CMakeLists.txt").write_text(
|
||||
"cmake_minimum_required(VERSION 3.15)\n"
|
||||
"project(cpp_driver LANGUAGES CXX)\n"
|
||||
"add_library(message STATIC message.cpp)\n"
|
||||
"add_executable(cpp_driver main.cpp)\n"
|
||||
"target_link_libraries(cpp_driver PRIVATE message)\n"
|
||||
"install(TARGETS cpp_driver DESTINATION bin)\n"
|
||||
)
|
||||
(source / "main.cpp").write_text(
|
||||
'#include <iostream>\nint main() { std::cout << "linked"; }\n'
|
||||
"#include <iostream>\nconst char* message();\nint main() { std::cout << message(); }\n"
|
||||
)
|
||||
(source / "message.cpp").write_text(
|
||||
'#include "build_flag.h"\nconst char* message() { return BUILD_FLAG; }\n'
|
||||
)
|
||||
build = NativeBuild(self.args, self.environment)
|
||||
build.output.mkdir()
|
||||
@@ -117,7 +140,98 @@ class NativeBuildTests(unittest.TestCase):
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
self.assertEqual(result.stdout, "linked")
|
||||
self.assertEqual(result.stdout, "linked with flags")
|
||||
self.assertTrue(
|
||||
all((self.root / (name + ".used")).is_file() for name in ("ar", "ranlib"))
|
||||
)
|
||||
|
||||
@unittest.skipIf(os.name == "nt", "Exercises Autoconf's Unix tool expansion")
|
||||
def test_libffi_rejects_quoted_archive_tool_paths(self):
|
||||
for name in ("ar", "ranlib"):
|
||||
for index, path in enumerate(("declared tool", "declared'tool")):
|
||||
with self.subTest(name=name, path=path):
|
||||
(self.root / path).touch()
|
||||
setattr(self.args, name, self.root / path)
|
||||
self.args.output = self.root / f"{name}-{index}"
|
||||
build = NativeBuild(self.args, self.environment)
|
||||
with (
|
||||
patch(
|
||||
"build_native.prepare_sources",
|
||||
side_effect=lambda *args: (build.output / "build").mkdir(),
|
||||
),
|
||||
patch.object(build, "cmake"),
|
||||
patch.object(build, "meson"),
|
||||
patch.object(build, "run"),
|
||||
self.assertRaisesRegex(ValueError, "libffi .* path"),
|
||||
):
|
||||
build.build()
|
||||
setattr(self.args, name, None)
|
||||
|
||||
@unittest.skipIf(os.name == "nt", "Exercises Autoconf's Unix flag expansion")
|
||||
def test_libffi_compiler_receives_literal_defines_without_shell_quotes(self):
|
||||
compiler = shutil.which("cc")
|
||||
if compiler is None:
|
||||
self.skipTest("Requires a C compiler")
|
||||
self.args.cc = Path(compiler)
|
||||
make = shutil.which("make")
|
||||
if make is None:
|
||||
self.skipTest("Requires Make")
|
||||
for index, (flag, expected) in enumerate(
|
||||
[
|
||||
('-DBUILD_FLAG="redacted"', "redacted\n"),
|
||||
(r'-DBUILD_FLAG="C:\\voice"', "C:\\voice\n"),
|
||||
('-DBUILD_FLAG="two words"', None),
|
||||
]
|
||||
):
|
||||
with self.subTest(flag=flag):
|
||||
self.args.c_flag = [flag]
|
||||
self.args.output = self.root / str(index)
|
||||
build = NativeBuild(self.args, self.environment)
|
||||
with (
|
||||
patch(
|
||||
"build_native.prepare_sources",
|
||||
side_effect=lambda *args: (build.output / "build").mkdir(),
|
||||
),
|
||||
patch.object(build, "cmake"),
|
||||
patch.object(build, "meson"),
|
||||
patch.object(build, "run") as run,
|
||||
):
|
||||
if " " in flag:
|
||||
with self.assertRaisesRegex(ValueError, "libffi flags"):
|
||||
build.build()
|
||||
continue
|
||||
build.build()
|
||||
environment = next(
|
||||
call.kwargs["environment"]
|
||||
for call in run.call_args_list
|
||||
if call.args[0] == "libffi-configure"
|
||||
)
|
||||
(build.output / "probe.c").write_text(
|
||||
"#include <stdio.h>\nint main(void) { puts(BUILD_FLAG); }\n"
|
||||
)
|
||||
subprocess.run(
|
||||
["/bin/sh", "-c", "$CC $CFLAGS probe.c -o probe"],
|
||||
cwd=build.output,
|
||||
env=environment,
|
||||
check=True,
|
||||
)
|
||||
result = subprocess.run(
|
||||
[build.output / "probe"], check=True, capture_output=True, text=True
|
||||
)
|
||||
self.assertEqual(result.stdout, expected)
|
||||
(build.output / "Makefile").write_text(
|
||||
f"CC={compiler}\nCFLAGS={environment['CFLAGS']} -fexceptions\n"
|
||||
"MAKEOVERRIDES=\nall:\n\t$(MAKE) nested\n"
|
||||
"nested:\n\t$(CC) $(CFLAGS) probe.c -o probe\n\t./probe\n"
|
||||
)
|
||||
result = subprocess.run(
|
||||
[make, "--silent"],
|
||||
cwd=build.output,
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
self.assertEqual(result.stdout, expected)
|
||||
|
||||
@unittest.skipIf(os.name == "nt", "Unix install paths; Windows layout is unchanged")
|
||||
def test_cmake_installs_relative_library_paths(self):
|
||||
@@ -130,11 +244,13 @@ class NativeBuildTests(unittest.TestCase):
|
||||
setattr(self.args, argument, Path(shutil.which(tool)))
|
||||
source = self.root / "library-source"
|
||||
source.mkdir()
|
||||
self.args.c_flag = ["-DBUILD_FLAG=42"]
|
||||
self.args.link_flag = ["-Wl,-rpath,declared-relative-path"]
|
||||
(source / "CMakeLists.txt").write_text(
|
||||
"cmake_minimum_required(VERSION 3.15)\nproject(relative_paths LANGUAGES C)\n"
|
||||
"add_library(fixture SHARED fixture.c)\ninstall(TARGETS fixture DESTINATION lib)\n"
|
||||
)
|
||||
(source / "fixture.c").write_text("int fixture(void) { return 42; }\n")
|
||||
(source / "fixture.c").write_text("int fixture(void) { return BUILD_FLAG; }\n")
|
||||
build = NativeBuild(self.args, self.environment)
|
||||
build.output.mkdir()
|
||||
build.sources = {"fixture": source}
|
||||
@@ -145,14 +261,15 @@ class NativeBuildTests(unittest.TestCase):
|
||||
metadata = inspect(build.tools / "lib/libfixture.dylib", self.args.target)
|
||||
self.assertEqual(
|
||||
(metadata.identity, metadata.rpaths),
|
||||
("@rpath/libfixture.dylib", ("@loader_path",)),
|
||||
("@rpath/libfixture.dylib", ("declared-relative-path", "@loader_path")),
|
||||
)
|
||||
else:
|
||||
from linux_runtime import inspect
|
||||
|
||||
metadata = inspect(build.tools / "lib/libfixture.so", self.args.target)
|
||||
self.assertEqual(
|
||||
(metadata.identity, metadata.rpaths), ("libfixture.so", ("29=$ORIGIN",))
|
||||
(metadata.identity, metadata.rpaths),
|
||||
("libfixture.so", ("29=declared-relative-path:$ORIGIN",)),
|
||||
)
|
||||
|
||||
def test_build_refuses_existing_output_before_reading_sources(self):
|
||||
@@ -184,6 +301,7 @@ class NativeBuildTests(unittest.TestCase):
|
||||
{
|
||||
"target": self.args.target,
|
||||
"deployment_target": "11.0",
|
||||
"flags": {"CFLAGS": [], "CXXFLAGS": [], "LDFLAGS": []},
|
||||
"steps": [{"name": "failure", "command": command, "exit_code": 23}],
|
||||
},
|
||||
)
|
||||
@@ -203,22 +321,64 @@ class NativeBuildTests(unittest.TestCase):
|
||||
self.assertEqual(environment["PKG_CONFIG_PATH"], "")
|
||||
|
||||
def test_meson_receives_private_link_inputs_with_spaces(self):
|
||||
if platform.system() == "Darwin":
|
||||
self.args.cc = Path(shutil.which("cc"))
|
||||
self.args.c_flag = [] if os.name == "nt" else ["-DDECLARED_C=1"]
|
||||
self.args.cxx_flag = [] if os.name == "nt" else ["-DDECLARED_CXX=1"]
|
||||
self.args.link_flag = (
|
||||
[] if os.name == "nt" else ["-Ldeclared input with spaces"]
|
||||
)
|
||||
build = NativeBuild(self.args, self.environment)
|
||||
self.assertEqual(
|
||||
build.record["flags"],
|
||||
{
|
||||
"CFLAGS": self.args.c_flag,
|
||||
"CXXFLAGS": self.args.cxx_flag,
|
||||
"LDFLAGS": self.args.link_flag,
|
||||
},
|
||||
)
|
||||
build.sources = {"meson": self.root / "meson", "glib": self.root / "glib"}
|
||||
with patch.object(build, "run"):
|
||||
build.meson("glib", [])
|
||||
if platform.system() == "Darwin":
|
||||
subprocess.run(
|
||||
[
|
||||
build.environment["OBJC"],
|
||||
*shlex.split(build.environment["OBJCFLAGS"]),
|
||||
"-x",
|
||||
"objective-c",
|
||||
"-fsyntax-only",
|
||||
"-",
|
||||
],
|
||||
input="#if DECLARED_C != 1\n#error missing declared flags\n#endif\n",
|
||||
text=True,
|
||||
check=True,
|
||||
)
|
||||
quote = subprocess.list2cmdline if build.windows else shlex.join
|
||||
include = f"{'/I' if build.windows else '-I'}{build.prefix / 'include'}"
|
||||
self.assertEqual(
|
||||
{name: build.environment[name] for name in ("CFLAGS", "CXXFLAGS")},
|
||||
{
|
||||
"CFLAGS": quote([*self.args.c_flag, include]),
|
||||
"CXXFLAGS": quote([*self.args.cxx_flag, include]),
|
||||
},
|
||||
)
|
||||
if build.windows:
|
||||
self.assertEqual(
|
||||
build.environment["LDFLAGS"], f'"/LIBPATH:{build.prefix / "lib"}"'
|
||||
build.environment["LDFLAGS"],
|
||||
quote([*self.args.link_flag, f"/LIBPATH:{build.prefix / 'lib'}"]),
|
||||
)
|
||||
else:
|
||||
self.assertEqual(
|
||||
shlex.split(build.environment["LDFLAGS"]),
|
||||
[
|
||||
*self.args.link_flag,
|
||||
f"-L{build.prefix / 'lib'}",
|
||||
"-Wl,-rpath,$ORIGIN:$ORIGIN/.."
|
||||
if build.args.target.endswith("unknown-linux-gnu")
|
||||
else f"-Wl,-rpath,{build.prefix / 'lib'}",
|
||||
(
|
||||
"-Wl,-rpath,$ORIGIN:$ORIGIN/.."
|
||||
if build.args.target.endswith("unknown-linux-gnu")
|
||||
else f"-Wl,-rpath,{build.prefix / 'lib'}"
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -242,6 +402,19 @@ class NativeBuildTests(unittest.TestCase):
|
||||
with self.assertRaises(subprocess.CalledProcessError):
|
||||
build.posix_path(path)
|
||||
|
||||
def test_windows_rejects_unix_overrides_before_creating_output(self):
|
||||
self.args.target = "x86_64-pc-windows-msvc"
|
||||
for name, value in (
|
||||
("c_flag", [r"/IC:\SDK\include"]),
|
||||
("ranlib", Path(sys.executable)),
|
||||
):
|
||||
with patch("build_native.validate_target"):
|
||||
setattr(self.args, name, value)
|
||||
with self.assertRaisesRegex(ValueError, "Unix build host"):
|
||||
NativeBuild(self.args, self.environment)
|
||||
delattr(self.args, name)
|
||||
self.assertFalse(self.args.output.exists())
|
||||
|
||||
def test_windows_recipes_use_explicit_targets_and_posix_paths(self):
|
||||
self.environment["USERPROFILE"] = str(self.root / "user profile")
|
||||
for architecture, flag in (("x86_64", "-m64"), ("aarch64", "-marm64")):
|
||||
|
||||
Reference in New Issue
Block a user