diff options
Diffstat (limited to 'python')
-rw-r--r-- | python/servo/build_commands.py | 58 | ||||
-rw-r--r-- | python/servo/try_parser.py | 33 |
2 files changed, 76 insertions, 15 deletions
diff --git a/python/servo/build_commands.py b/python/servo/build_commands.py index b21f89980c0..42988debf35 100644 --- a/python/servo/build_commands.py +++ b/python/servo/build_commands.py @@ -13,10 +13,11 @@ import os.path as path import pathlib import shutil import stat +import subprocess import sys from time import time -from typing import Optional +from typing import Optional, List import notifypy @@ -40,6 +41,32 @@ SUPPORTED_ASAN_TARGETS = ["aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu"] +def get_rustc_llvm_version() -> Optional[List[int]]: + """Determine the LLVM version of `rustc` and return it as a List[major, minor, patch, ...] + + In some cases we want to ensure that the LLVM version of rustc and clang match, e.g. + when using ASAN for both C/C++ and Rust code, we want to use the same ASAN implementation. + This function assumes that rustc points to the rust compiler we are interested in, which should + be valid in both rustup managed environment and on nix. + """ + try: + result = subprocess.run(['rustc', '--version', '--verbose'], encoding='utf-8', capture_output=True) + result.check_returncode() + for line in result.stdout.splitlines(): + line_lowercase = line.lower() + if line_lowercase.startswith("llvm version:"): + llvm_version = line_lowercase.strip("llvm version:") + llvm_version = llvm_version.strip() + version = llvm_version.split('.') + print(f"Info: rustc is using LLVM version {'.'.join(version)}") + return version + else: + print(f"Error: Couldn't find LLVM version in output of `rustc --version --verbose`: `{result.stdout}`") + except Exception as e: + print(f"Error: Failed to determine rustc version: {e}") + return None + + @CommandProvider class MachCommands(CommandBase): @Command('build', description='Build Servo', category='build') @@ -99,11 +126,30 @@ class MachCommands(CommandBase): env["RUSTFLAGS"] = env.get("RUSTFLAGS", "") + " -Zsanitizer=address" opts += ["-Zbuild-std"] kwargs["target_override"] = target_triple - # TODO: Investigate sanitizers in C/C++ code: - # env.setdefault("CFLAGS", "") - # env.setdefault("CXXFLAGS", "") - # env["CFLAGS"] += " -fsanitize=address" - # env["CXXFLAGS"] += " -fsanitize=address" + + # Note: We want to use the same clang/LLVM version as rustc. + rustc_llvm_version = get_rustc_llvm_version() + if rustc_llvm_version is None: + raise RuntimeError("Unable to determine necessary clang version for ASAN support") + llvm_major: int = rustc_llvm_version[0] + target_clang = f"clang-{llvm_major}" + target_cxx = f"clang++-{llvm_major}" + if shutil.which(target_clang) is None or shutil.which(target_cxx) is None: + raise RuntimeError(f"--with-asan requires `{target_clang}` and `{target_cxx}` to be in PATH") + env.setdefault("TARGET_CC", target_clang) + env.setdefault("TARGET_CXX", target_cxx) + # TODO: We should also parse the LLVM version from the clang compiler we chose. + # It's unclear if the major version being the same is sufficient. + + # We need to use `TARGET_CFLAGS`, since we don't want to compile host dependencies with ASAN, + # since that causes issues when building build-scripts / proc macros. + env.setdefault("TARGET_CFLAGS", "") + env.setdefault("TARGET_CXXFLAGS", "") + env["TARGET_CFLAGS"] += " -fsanitize=address" + env["TARGET_CXXFLAGS"] += " -fsanitize=address" + env["TARGET_LDFLAGS"] = "-static-libasan" + # By default build mozjs from source to enable ASAN with mozjs. + env.setdefault("MOZJS_FROM_SOURCE", "1") # asan replaces system allocator with asan allocator # we need to make sure that we do not replace it with jemalloc diff --git a/python/servo/try_parser.py b/python/servo/try_parser.py index 9a6b31693b0..0d78599c06f 100644 --- a/python/servo/try_parser.py +++ b/python/servo/try_parser.py @@ -39,11 +39,12 @@ class JobConfig(object): unit_tests: bool = False build_libservo: bool = False bencher: bool = False + build_args: str = "" wpt_args: str = "" number_of_wpt_chunks: int = 20 # These are the fields that must match in between two JobConfigs for them to be able to be # merged. If you modify any of the fields above, make sure to update this line as well. - merge_compatibility_fields: ClassVar[List[str]] = ['workflow', 'profile', 'wpt_args'] + merge_compatibility_fields: ClassVar[List[str]] = ['workflow', 'profile', 'wpt_args', 'build_args'] def merge(self, other: JobConfig) -> bool: """Try to merge another job with this job. Returns True if merging is successful @@ -209,7 +210,8 @@ class TestParser(unittest.TestCase): 'build_libservo': False, 'workflow': 'linux', 'wpt': False, - 'wpt_args': '' + 'wpt_args': '', + 'build_args': '' }] }) @@ -225,7 +227,8 @@ class TestParser(unittest.TestCase): "unit_tests": True, 'build_libservo': False, 'bencher': True, - "wpt_args": "" + "wpt_args": "", + 'build_args': '' }, { "name": "MacOS (Unit Tests)", @@ -236,7 +239,8 @@ class TestParser(unittest.TestCase): "unit_tests": True, 'build_libservo': False, 'bencher': False, - "wpt_args": "" + "wpt_args": "", + 'build_args': '' }, { "name": "Windows (Unit Tests)", @@ -247,7 +251,8 @@ class TestParser(unittest.TestCase): "unit_tests": True, 'build_libservo': False, 'bencher': False, - "wpt_args": "" + "wpt_args": "", + 'build_args': '' }, { "name": "Android", @@ -258,7 +263,8 @@ class TestParser(unittest.TestCase): "unit_tests": False, 'build_libservo': False, 'bencher': False, - "wpt_args": "" + "wpt_args": "", + 'build_args': '' }, { "name": "OpenHarmony", @@ -269,7 +275,8 @@ class TestParser(unittest.TestCase): "unit_tests": False, 'build_libservo': False, 'bencher': False, - "wpt_args": "" + "wpt_args": "", + 'build_args': '' }, { "name": "Lint", @@ -280,7 +287,9 @@ class TestParser(unittest.TestCase): "unit_tests": False, 'build_libservo': False, 'bencher': False, - "wpt_args": ""} + "wpt_args": "", + 'build_args': '' + } ]}) def test_job_merging(self): @@ -295,7 +304,8 @@ class TestParser(unittest.TestCase): 'build_libservo': False, 'workflow': 'linux', 'wpt': True, - 'wpt_args': '' + 'wpt_args': '', + 'build_args': '' }] }) @@ -327,6 +337,11 @@ class TestParser(unittest.TestCase): self.assertFalse(a.merge(b), "Should not merge jobs that run different WPT tests.") self.assertEqual(a, JobConfig("Linux (Unit Tests)", Workflow.LINUX, unit_tests=True)) + a = JobConfig("Linux (Unit Tests)", Workflow.LINUX, unit_tests=True) + b = JobConfig("Linux (Unit Tests)", Workflow.LINUX, unit_tests=True, build_args="--help") + self.assertFalse(a.merge(b), "Should not merge jobs with different build arguments.") + self.assertEqual(a, JobConfig("Linux (Unit Tests)", Workflow.LINUX, unit_tests=True)) + def test_full(self): self.assertDictEqual(json.loads(Config("full").to_json()), json.loads(Config("").to_json())) |