Skip to content

Commit f656c4d

Browse files
Add support for caching target json specs for Rust compilation. (#2269)
* Add support for caching target json specs for Rust compilation. Fixes #2174 * Add a test for parsing the `--target` argument. --------- Co-authored-by: Sylvestre Ledru <[email protected]>
1 parent 84f3737 commit f656c4d

File tree

2 files changed

+84
-7
lines changed

2 files changed

+84
-7
lines changed

docs/Rust.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,5 @@ sccache includes support for caching Rust compilation. This includes many caveat
77
* Compilation from stdin is not supported, a source file must be provided.
88
* Values from `env!` require Rust >= 1.46 to be tracked in caching.
99
* Procedural macros that read files from the filesystem may not be cached properly
10-
* Target specs aren't hashed (e.g. custom target specs)
1110

1211
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.

src/compiler/rust.rs

Lines changed: 84 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,8 @@ pub struct ParsedArguments {
188188
color_mode: ColorMode,
189189
/// Whether `--json` was passed to this invocation.
190190
has_json: bool,
191+
/// A `--target` parameter that specifies a path to a JSON file.
192+
target_json: Option<PathBuf>,
191193
}
192194

193195
/// A struct on which to hang a `Compilation` impl.
@@ -1081,6 +1083,7 @@ fn parse_arguments(arguments: &[OsString], cwd: &Path) -> CompilerArguments<Pars
10811083
let mut has_json = false;
10821084
let mut profile = None;
10831085
let mut gcno = false;
1086+
let mut target_json = None;
10841087

10851088
for arg in ArgsIter::new(arguments.iter().cloned(), &ARGS[..]) {
10861089
let arg = try_or_cannot_cache!(arg, "argument parse");
@@ -1167,7 +1170,8 @@ fn parse_arguments(arguments: &[OsString], cwd: &Path) -> CompilerArguments<Pars
11671170
}
11681171
Some(PassThrough(_)) => (),
11691172
Some(Target(target)) => match target {
1170-
ArgTarget::Path(_) | ArgTarget::Unsure(_) => cannot_cache!("target"),
1173+
ArgTarget::Path(json_path) => target_json = Some(json_path.to_owned()),
1174+
ArgTarget::Unsure(_) => cannot_cache!("target unsure"),
11711175
ArgTarget::Name(_) => (),
11721176
},
11731177
None => {
@@ -1297,6 +1301,7 @@ fn parse_arguments(arguments: &[OsString], cwd: &Path) -> CompilerArguments<Pars
12971301
emit,
12981302
color_mode,
12991303
has_json,
1304+
target_json,
13001305
})
13011306
}
13021307

@@ -1339,6 +1344,7 @@ where
13391344
has_json,
13401345
profile,
13411346
gcno,
1347+
target_json,
13421348
..
13431349
},
13441350
} = *self;
@@ -1394,11 +1400,33 @@ where
13941400
let abs_staticlibs = staticlibs.iter().map(|s| cwd.join(s)).collect::<Vec<_>>();
13951401
let staticlib_hashes = hash_all_archives(&abs_staticlibs, pool);
13961402

1397-
let ((source_files, source_hashes, mut env_deps), extern_hashes, staticlib_hashes) = futures::try_join!(
1403+
// Hash the content of the specified target json file, if any.
1404+
let mut target_json_files = Vec::new();
1405+
if let Some(path) = &target_json {
1406+
trace!(
1407+
"[{}]: hashing target json file {}",
1408+
crate_name,
1409+
path.display()
1410+
);
1411+
let abs_target_json = cwd.join(path);
1412+
target_json_files.push(abs_target_json);
1413+
}
1414+
1415+
let target_json_hash = hash_all(&target_json_files, pool);
1416+
1417+
// Perform all hashing operations on the files.
1418+
let (
1419+
(source_files, source_hashes, mut env_deps),
1420+
extern_hashes,
1421+
staticlib_hashes,
1422+
target_json_hash,
1423+
) = futures::try_join!(
13981424
source_files_and_hashes_and_env_deps,
13991425
extern_hashes,
1400-
staticlib_hashes
1426+
staticlib_hashes,
1427+
target_json_hash
14011428
)?;
1429+
14021430
// If you change any of the inputs to the hash, you should change `CACHE_VERSION`.
14031431
let mut m = Digest::new();
14041432
// Hash inputs:
@@ -1424,6 +1452,10 @@ where
14241452
// in those paths (rlibs and static libs used in the compilation) are used as hash
14251453
// inputs below.
14261454
.filter(|&(arg, _)| !(arg == "--extern" || arg == "-L" || arg == "--out-dir"))
1455+
// We also exclude `--target` if it specifies a path to a .json file. The file content
1456+
// is used as hash input below.
1457+
// If `--target` specifies a string, it continues to be hashed as part of the arguments.
1458+
.filter(|&(arg, _)| target_json.is_none() || arg != "--target")
14271459
// A few argument types were not passed in a deterministic order
14281460
// by older versions of cargo: --extern, -L, --cfg. We'll filter the rest of those
14291461
// out, sort them, and append them to the rest of the arguments.
@@ -1441,14 +1473,16 @@ where
14411473
// 4. The digest of all source files (this includes src file from cmdline).
14421474
// 5. The digest of all files listed on the commandline (self.externs).
14431475
// 6. The digest of all static libraries listed on the commandline (self.staticlibs).
1476+
// 7. The digest of the content of the target json file specified via `--target` (if any).
14441477
for h in source_hashes
14451478
.into_iter()
14461479
.chain(extern_hashes)
14471480
.chain(staticlib_hashes)
1481+
.chain(target_json_hash)
14481482
{
14491483
m.update(h.as_bytes());
14501484
}
1451-
// 7. Environment variables: Hash all environment variables listed in the rustc dep-info
1485+
// 8. Environment variables: Hash all environment variables listed in the rustc dep-info
14521486
// output. Additionally also has all environment variables starting with `CARGO_`,
14531487
// since those are not listed in dep-info but affect cacheability.
14541488
env_deps.sort();
@@ -1482,9 +1516,9 @@ where
14821516
m.update(b"=");
14831517
val.hash(&mut HashToDigest { digest: &mut m });
14841518
}
1485-
// 8. The cwd of the compile. This will wind up in the rlib.
1519+
// 9. The cwd of the compile. This will wind up in the rlib.
14861520
cwd.hash(&mut HashToDigest { digest: &mut m });
1487-
// 9. The version of the compiler.
1521+
// 10. The version of the compiler.
14881522
version.hash(&mut HashToDigest { digest: &mut m });
14891523

14901524
// Turn arguments into a simple Vec<OsString> to calculate outputs.
@@ -3391,6 +3425,7 @@ proc_macro false
33913425
has_json: false,
33923426
profile: None,
33933427
gcno: None,
3428+
target_json: None,
33943429
},
33953430
});
33963431
let creator = new_creator();
@@ -3763,4 +3798,47 @@ proc_macro false
37633798

37643799
assert_eq!(h.gcno, Some("foo-a1b6419f8321841f.gcno".into()));
37653800
}
3801+
3802+
#[test]
3803+
fn test_parse_target() {
3804+
// Parse a --target argument that is a string (not a path to a .json file).
3805+
let h = parses!(
3806+
"--crate-name",
3807+
"foo",
3808+
"--crate-type",
3809+
"lib",
3810+
"./src/lib.rs",
3811+
"--emit=dep-info,link",
3812+
"--out-dir",
3813+
"/out",
3814+
"--target",
3815+
"string"
3816+
);
3817+
assert!(h.arguments.contains(&Argument::WithValue(
3818+
"--target",
3819+
ArgData::Target(ArgTarget::Name("string".to_owned())),
3820+
ArgDisposition::Separated
3821+
)));
3822+
assert!(h.target_json.is_none());
3823+
3824+
// Parse a --target argument that is a path.
3825+
let h = parses!(
3826+
"--crate-name",
3827+
"foo",
3828+
"--crate-type",
3829+
"lib",
3830+
"./src/lib.rs",
3831+
"--emit=dep-info,link",
3832+
"--out-dir",
3833+
"/out",
3834+
"--target",
3835+
"/path/to/target.json"
3836+
);
3837+
assert!(h.arguments.contains(&Argument::WithValue(
3838+
"--target",
3839+
ArgData::Target(ArgTarget::Path(PathBuf::from("/path/to/target.json"))),
3840+
ArgDisposition::Separated
3841+
)));
3842+
assert_eq!(h.target_json, Some(PathBuf::from("/path/to/target.json")));
3843+
}
37663844
}

0 commit comments

Comments
 (0)