From 56f145c8371e69bc960ab503c30f5f01c7a0e599 Mon Sep 17 00:00:00 2001 From: Colin Finck Date: Mon, 7 Oct 2024 13:04:10 +0200 Subject: [PATCH 1/2] Add support for caching target json specs for Rust compilation. Fixes #2174 --- docs/Rust.md | 1 - src/compiler/rust.rs | 47 ++++++++++++++++++++++++++++++++++++++------ 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/docs/Rust.md b/docs/Rust.md index 9b805d7b13..ef266e7265 100644 --- a/docs/Rust.md +++ b/docs/Rust.md @@ -7,6 +7,5 @@ sccache includes support for caching Rust compilation. This includes many caveat * Compilation from stdin is not supported, a source file must be provided. * Values from `env!` require Rust >= 1.46 to be tracked in caching. * Procedural macros that read files from the filesystem may not be cached properly -* Target specs aren't hashed (e.g. custom target specs) If you are using Rust 1.18 or later, you can ask cargo to wrap all compilation with sccache by setting `RUSTC_WRAPPER=sccache` in your build environment. diff --git a/src/compiler/rust.rs b/src/compiler/rust.rs index 3e9e930bc1..881bfd170f 100644 --- a/src/compiler/rust.rs +++ b/src/compiler/rust.rs @@ -170,6 +170,8 @@ pub struct ParsedArguments { color_mode: ColorMode, /// Whether `--json` was passed to this invocation. has_json: bool, + /// A `--target` parameter that specifies a path to a JSON file. + target_json: Option, } /// A struct on which to hang a `Compilation` impl. @@ -1060,6 +1062,7 @@ fn parse_arguments(arguments: &[OsString], cwd: &Path) -> CompilerArguments CompilerArguments (), Some(Target(target)) => match target { - ArgTarget::Path(_) | ArgTarget::Unsure(_) => cannot_cache!("target"), + ArgTarget::Path(json_path) => target_json = Some(json_path.to_owned()), + ArgTarget::Unsure(_) => cannot_cache!("target unsure"), ArgTarget::Name(_) => (), }, None => { @@ -1267,6 +1271,7 @@ fn parse_arguments(arguments: &[OsString], cwd: &Path) -> CompilerArguments>(); let staticlib_hashes = hash_all_archives(&abs_staticlibs, pool); - let ((source_files, source_hashes, mut env_deps), extern_hashes, staticlib_hashes) = futures::try_join!( + // Hash the content of the specified target json file, if any. + let mut target_json_files = Vec::new(); + if let Some(path) = &target_json { + trace!( + "[{}]: hashing target json file {}", + crate_name, + path.display() + ); + let abs_target_json = cwd.join(path); + target_json_files.push(abs_target_json); + } + + let target_json_hash = hash_all(&target_json_files, pool); + + // Perform all hashing operations on the files. + let ( + (source_files, source_hashes, mut env_deps), + extern_hashes, + staticlib_hashes, + target_json_hash, + ) = futures::try_join!( source_files_and_hashes_and_env_deps, extern_hashes, - staticlib_hashes + staticlib_hashes, + target_json_hash )?; + // If you change any of the inputs to the hash, you should change `CACHE_VERSION`. let mut m = Digest::new(); // Hash inputs: @@ -1393,6 +1421,10 @@ where // in those paths (rlibs and static libs used in the compilation) are used as hash // inputs below. .filter(|&(arg, _)| !(arg == "--extern" || arg == "-L" || arg == "--out-dir")) + // We also exclude `--target` if it specifies a path to a .json file. The file content + // is used as hash input below. + // If `--target` specifies a string, it continues to be hashed as part of the arguments. + .filter(|&(arg, _)| target_json.is_none() || arg != "--target") // A few argument types were not passed in a deterministic order // by older versions of cargo: --extern, -L, --cfg. We'll filter the rest of those // out, sort them, and append them to the rest of the arguments. @@ -1410,14 +1442,16 @@ where // 4. The digest of all source files (this includes src file from cmdline). // 5. The digest of all files listed on the commandline (self.externs). // 6. The digest of all static libraries listed on the commandline (self.staticlibs). + // 7. The digest of the content of the target json file specified via `--target` (if any). for h in source_hashes .into_iter() .chain(extern_hashes) .chain(staticlib_hashes) + .chain(target_json_hash) { m.update(h.as_bytes()); } - // 7. Environment variables: Hash all environment variables listed in the rustc dep-info + // 8. Environment variables: Hash all environment variables listed in the rustc dep-info // output. Additionally also has all environment variables starting with `CARGO_`, // since those are not listed in dep-info but affect cacheability. env_deps.sort(); @@ -1442,9 +1476,9 @@ where val.hash(&mut HashToDigest { digest: &mut m }); } } - // 8. The cwd of the compile. This will wind up in the rlib. + // 9. The cwd of the compile. This will wind up in the rlib. cwd.hash(&mut HashToDigest { digest: &mut m }); - // 9. The version of the compiler. + // 10. The version of the compiler. version.hash(&mut HashToDigest { digest: &mut m }); // Turn arguments into a simple Vec to calculate outputs. @@ -3331,6 +3365,7 @@ proc_macro false color_mode: ColorMode::Auto, has_json: false, profile: None, + target_json: None, }, }); let creator = new_creator(); From 7e96849f8a088612595b677fc42a9598974bbfed Mon Sep 17 00:00:00 2001 From: Colin Finck Date: Mon, 18 Nov 2024 12:46:31 +0100 Subject: [PATCH 2/2] Add a test for parsing the `--target` argument. --- src/compiler/rust.rs | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/compiler/rust.rs b/src/compiler/rust.rs index 881bfd170f..ba29704bfa 100644 --- a/src/compiler/rust.rs +++ b/src/compiler/rust.rs @@ -3736,4 +3736,47 @@ proc_macro false assert_eq!(h.profile, Some("foo-a1b6419f8321841f.profraw".into())); } + + #[test] + fn test_parse_target() { + // Parse a --target argument that is a string (not a path to a .json file). + let h = parses!( + "--crate-name", + "foo", + "--crate-type", + "lib", + "./src/lib.rs", + "--emit=dep-info,link", + "--out-dir", + "/out", + "--target", + "string" + ); + assert!(h.arguments.contains(&Argument::WithValue( + "--target", + ArgData::Target(ArgTarget::Name("string".to_owned())), + ArgDisposition::Separated + ))); + assert!(h.target_json.is_none()); + + // Parse a --target argument that is a path. + let h = parses!( + "--crate-name", + "foo", + "--crate-type", + "lib", + "./src/lib.rs", + "--emit=dep-info,link", + "--out-dir", + "/out", + "--target", + "/path/to/target.json" + ); + assert!(h.arguments.contains(&Argument::WithValue( + "--target", + ArgData::Target(ArgTarget::Path(PathBuf::from("/path/to/target.json"))), + ArgDisposition::Separated + ))); + assert_eq!(h.target_json, Some(PathBuf::from("/path/to/target.json"))); + } }