diff --git a/build_extra/rust/dummy.rs b/build_extra/rust/empty_crate.rs similarity index 100% rename from build_extra/rust/dummy.rs rename to build_extra/rust/empty_crate.rs diff --git a/build_extra/rust/get_rust_ldflags.cmd b/build_extra/rust/get_rustc_info.cmd similarity index 100% rename from build_extra/rust/get_rust_ldflags.cmd rename to build_extra/rust/get_rustc_info.cmd diff --git a/build_extra/rust/get_rust_ldflags.py b/build_extra/rust/get_rustc_info.py similarity index 88% rename from build_extra/rust/get_rust_ldflags.py rename to build_extra/rust/get_rustc_info.py index 79c7bc3fb6..1b61bdbc04 100755 --- a/build_extra/rust/get_rust_ldflags.py +++ b/build_extra/rust/get_rustc_info.py @@ -25,28 +25,21 @@ # this script then reads the linker arguments from that temporary file, and # then filters it to remove flags that are irrelevant or undesirable. +import json +import re import sys import os from os import path -import re import subprocess import tempfile -def capture_args(argsfile_path): +def capture_linker_args(argsfile_path): with open(argsfile_path, "wb") as argsfile: argsfile.write("\n".join(sys.argv[1:])) -def main(): - # If ARGSFILE_PATH is set this script is being invoked by rustc, which - # thinks we are a linker. All we do now is write our argv to the specified - # file and exit. Further processing is done by our grandparent process, - # also this script but invoked by gn. - argsfile_path = os.getenv("ARGSFILE_PATH") - if argsfile_path is not None: - return capture_args(argsfile_path) - +def get_ldflags(rustc_args): # Prepare the environment for rustc. rustc_env = os.environ.copy() @@ -80,12 +73,13 @@ def main(): # Build the rustc command line. # * `-Clinker=` tells rustc to use our fake linker. # * `-Csave-temps` prevents rustc from deleting object files after - # linking. We need to preserve the file `xx.crate.allocator.rcgu.o`. + # linking. We need to preserve the extra object file with allocator + # symbols (`_rust_alloc` etc.) in it that rustc produces. rustc_cmd = [ "rustc", "-Clinker=" + rustc_linker, "-Csave-temps", - ] + sys.argv[1:] + ] + rustc_args # Spawn the rust compiler. rustc_proc = subprocess.Popen( @@ -143,13 +137,16 @@ def main(): elif arg.endswith(".rlib"): # Built-in Rust library, e.g. `libstd-8524caae8408aac2.rlib`. pass - elif arg.endswith(".crate.allocator.rcgu.o"): + elif re.match(r"^empty_crate\.[a-z0-9]+\.rcgu.o$", arg): # This file is needed because it contains certain allocator # related symbols (e.g. `__rust_alloc`, `__rust_oom`). # The Rust compiler normally generates this file just before # linking an executable. We pass `-Csave-temps` to rustc so it # doesn't delete the file when it's done linking. pass + elif arg.endswith(".crate.allocator.rcgu.o"): + # Same as above, but for rustc version 1.29.0 and older. + pass elif arg.endswith(".lib") and not arg.startswith("msvcrt"): # Include most Windows static/import libraries (e.g. `ws2_32.lib`). # However we ignore Rusts choice of C runtime (`mvcrt*.lib`). @@ -172,8 +169,34 @@ def main(): ldflags += [arg] - # Write the filtered ldflags to stdout, separated by newline characters. - sys.stdout.write("\n".join(ldflags)) + return ldflags + + +def get_version(): + version = subprocess.check_output(["rustc", "--version"]) + version = version.strip() # Remove trailing newline. + return version + + +def main(): + # If ARGSFILE_PATH is set this script is being invoked by rustc, which + # thinks we are a linker. All we do now is write our argv to the specified + # file and exit. Further processing is done by our grandparent process, + # also this script but invoked by gn. + argsfile_path = os.getenv("ARGSFILE_PATH") + if argsfile_path is not None: + return capture_linker_args(argsfile_path) + + empty_crate_source = path.join(path.dirname(__file__), "empty_crate.rs") + + info = { + "version": get_version(), + "ldflags_bin": get_ldflags([empty_crate_source]), + "ldflags_test": get_ldflags([empty_crate_source, "--test"]) + } + + # Write the information dict as a json object. + json.dump(info, sys.stdout) if __name__ == '__main__': diff --git a/build_extra/rust/rust.gni b/build_extra/rust/rust.gni index 9cb5729daa..889e254675 100644 --- a/build_extra/rust/rust.gni +++ b/build_extra/rust/rust.gni @@ -29,23 +29,15 @@ out_dir = "$root_out_dir/rust_crates" # * To sidestep rustc weirdness (e.g. on Windows, it always links with the # release C runtime library, even for debug builds). # -# The `get_rust_ldflags` tool outputs the linker flags that are needed to +# The `get_rustc_info` tool outputs the linker flags that are needed to # successfully link rustc object code into an executable. # We generate two sets of ldflags: -# `rust_bin_ldflags`: Used for rust_executable targets. -# `rust_test_ldflags`: Used for rust_test targets; includes the test harness. +# `ldflags_bin` : Used for rust_executable targets. +# `ldflags_test`: Used for rust_test targets; includes the test harness. # # The tool works by compiling and linking something with rustc, and analyzing # the arguments it passes to the system linker. That's what dummy.rs is for. -dummy_rs_path = rebase_path("dummy.rs", root_build_dir) -rust_bin_ldflags = - exec_script("get_rust_ldflags.py", [ dummy_rs_path ], "list lines") -rust_test_ldflags = exec_script("get_rust_ldflags.py", - [ - dummy_rs_path, - "--test", - ], - "list lines") +_rustc_info = exec_script("get_rustc_info.py", [], "json") template("rust_crate") { config_name = "${target_name}_config" @@ -184,7 +176,10 @@ template("rust_crate") { # This is to disambiguate multiple versions of the same crate. "-Cextra-filename=$crate_suffix", - "-Cmetadata=$crate_suffix", + + # Appending the rustc version to the crate metadata ensures that they are + # rebuilt when rustc is upgraded, by changing the command line. + "-Cmetadata=\"${crate_suffix}_${_rustc_info.version}\"", # This is needed for transitive dependencies. "-L", @@ -244,9 +239,9 @@ template("rust_executable") { forward_variables_from(invoker, "*") if (defined(is_test) && is_test) { - ldflags = rust_test_ldflags + ldflags = _rustc_info.ldflags_test } else { - ldflags = rust_bin_ldflags + ldflags = _rustc_info.ldflags_bin } if (!defined(deps)) {