diff --git a/scripts/target_determinator.py b/scripts/target_determinator.py index 2e04cc5cea60..3a6f0fb45ab7 100755 --- a/scripts/target_determinator.py +++ b/scripts/target_determinator.py @@ -34,9 +34,12 @@ def log(s: str) -> None: def filter_targets(bazel: Path, targets: str) -> str: + # Need to quote targets for inclusion in another query. + quoted_targets = "\n".join([f'"{t}"' for t in targets.splitlines()]) + with tempfile.NamedTemporaryFile(mode="w+") as tmp: query = ( - f"let t = set({targets}) in " + f"let t = set({quoted_targets}) in " "kind(rule, $t) except attr(tags, manual, $t)\n" ) query_lines = query.splitlines() diff --git a/toolchain/install/BUILD b/toolchain/install/BUILD index eadbaa94fb61..94b200226525 100644 --- a/toolchain/install/BUILD +++ b/toolchain/install/BUILD @@ -7,6 +7,7 @@ load( "LLVM_VERSION_MAJOR", ) load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test") +load("@rules_python//python:defs.bzl", "py_test") load("//bazel/cc_toolchains:defs.bzl", "cc_env") load("//bazel/manifest:defs.bzl", "manifest") load("//toolchain/base:llvm_tools.bzl", "LLVM_MAIN_TOOLS", "LLVM_TOOL_ALIASES") @@ -119,6 +120,13 @@ cc_binary( ], ) +clang_aliases = [ + "clang", + "clang++", + "clang-cl", + "clang-cpp", +] + # TODO: Add remaining aliases of LLD for Windows and WASM when we have support # for them wired up through the busybox. lld_aliases = [ @@ -126,7 +134,7 @@ lld_aliases = [ "ld64.lld", ] -llvm_binaries = lld_aliases + [ +llvm_binaries = clang_aliases + lld_aliases + [ tool.bin_name for tool in LLVM_MAIN_TOOLS.values() ] + [ @@ -167,13 +175,7 @@ install_dirs = { ), install_filegroup("core", "//core:prelude"), ], - "lib/carbon/llvm/bin": [ - install_symlink( - "clang", - "../../carbon-busybox", - is_driver = True, - ), - ] + [install_symlink( + "lib/carbon/llvm/bin": [install_symlink( name, "../../carbon-busybox", is_driver = True, @@ -191,6 +193,13 @@ make_install_filegroups( prefix = "prefix_root", ) +py_test( + name = "llvm_symlinks_test", + size = "small", + srcs = ["llvm_symlinks_test.py"], + data = [":install_data"], +) + manifest( name = "install_data_manifest.txt", srcs = [":install_data"], diff --git a/toolchain/install/busybox_main.cpp b/toolchain/install/busybox_main.cpp index 5c723471b5b9..3b9a3ed66dee 100644 --- a/toolchain/install/busybox_main.cpp +++ b/toolchain/install/busybox_main.cpp @@ -52,6 +52,16 @@ static auto Main(int argc, char** argv) -> ErrorOr { auto subcommand_args = llvm::StringSwitch>( *busybox_info.mode) + // The `clang` program name used configures the default for its + // `--driver-mode` flag. The first of these is redundant with the + // default, but we group it here for clarity. + .Case("clang", {"clang", "--"}) + .Case("clang++", {"clang", "--", "--driver-mode=g++"}) + .Case("clang-cl", {"clang", "--", "--driver-mode=cl"}) + .Case("clang-cpp", {"clang", "--", "--driver-mode=cpp"}) + + // LLD has platform-specific program names that we translate into + // platform flags. .Case("ld.lld", {"lld", "--platform=gnu", "--"}) .Case("ld64.lld", {"lld", "--platform=darwin", "--"}) diff --git a/toolchain/install/llvm_symlinks_test.py b/toolchain/install/llvm_symlinks_test.py new file mode 100644 index 000000000000..4621da6339aa --- /dev/null +++ b/toolchain/install/llvm_symlinks_test.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 + +"""Checks various LLVM tool symlinks behave as expected.""" + +__copyright__ = """ +Part of the Carbon Language project, under the Apache License v2.0 with LLVM +Exceptions. See /LICENSE for license information. +SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +""" + +from pathlib import Path +import subprocess +import os +import sys +import unittest + + +class LLVMSymlinksTest(unittest.TestCase): + def setUp(self) -> None: + # The install root is adjacent to the test script + self.install_root = Path(sys.argv[0]).parent / "prefix_root" + self.tmpdir = Path(os.environ["TEST_TMPDIR"]) + self.test_o_file = self.tmpdir / "test.o" + self.test_o_file.touch() + + def get_link_cmd(self, clang: Path) -> list[str | Path]: + return [ + clang, + # We pick an arbitrary linux target to get stable results. + "--target=aarch64-unknown-linux-gnu", + # Verbose printing to help with debugging. + "-v", + # Print out the link command rather than running it. + "-###", + # Give the link command an output. + "-o", + self.tmpdir / "test", + # A test input file. This won't be read though. + self.test_o_file, + ] + + def test_clang(self) -> None: + bin = self.install_root / "lib/carbon/llvm/bin/clang" + run = subprocess.run( + self.get_link_cmd(bin), check=True, capture_output=True, text=True + ) + # Check that we do have a plausible link command. + self.assertRegex(run.stderr, r'"-m" "aarch64linux"') + + # Ensure it doesn't contain the C++ standard library. + self.assertNotRegex(run.stderr, r'"-lstdc++"') + + def test_clangplusplus(self) -> None: + bin = self.install_root / "lib/carbon/llvm/bin/clang++" + run = subprocess.run( + self.get_link_cmd(bin), check=True, capture_output=True, text=True + ) + # Check that we do have a plausible link command. + self.assertRegex(run.stderr, r'"-m" "aarch64linux"') + + # Ensure it doesn't contain the C++ standard library. + self.assertNotRegex(run.stderr, r'"-lstdc++"') + + def test_clang_cl(self) -> None: + bin = self.install_root / "lib/carbon/llvm/bin/clang-cl" + run = subprocess.run( + # Use the `cl.exe`-specific help flag to test the mode. + [bin, "/?"], + check=True, + capture_output=True, + text=True, + ) + + # This should print the help string, including `cl.exe` specifics. + self.assertRegex(run.stdout, r"CL.EXE COMPATIBILITY OPTIONS:") + + def test_clang_cpp(self) -> None: + # Note that this is a test of the C-preprocessor mode, not C++ mode. + + # Create a test file that we'll preprocess. + text_file = self.tmpdir / "test.txt" + with open(text_file, "w") as f: + f.write("TEST\n") + + # Run the preprocessor using a CPP-specific command line reading from + # the test file and writing to stdout. We define a macro that we'll + # check is expanded. + bin = self.install_root / "lib/carbon/llvm/bin/clang-cpp" + run = subprocess.run( + [bin, "-D", "TEST=SUCCESS", text_file, "-"], + check=True, + capture_output=True, + text=True, + ) + + self.assertEqual(run.stderr, "") + self.assertRegex(run.stdout, r"(^|\n)SUCCESS\n") + + +if __name__ == "__main__": + unittest.main()