1 //! A Cargo build script binary used in unit tests for the Bazel `cargo_build_script` rule
2 
3 use std::collections::HashSet;
4 use std::path::PathBuf;
5 
main()6 fn main() {
7     // The cargo_build_script macro appends an underscore to the given name.
8     //
9     // This file would be the only expected source file within the CARGO_MANIFEST_DIR without
10     // any exec root symlink functionality.
11     let build_script = PathBuf::from(
12         std::env::args()
13             .next()
14             .expect("Unable to get the build script executable"),
15     );
16 
17     let build_script_name = build_script
18         .file_name()
19         .expect("Unable to get the build script name")
20         .to_str()
21         .expect("Unable to convert the build script name to a string");
22 
23     let mut root_files = std::fs::read_dir(".")
24         .expect("Unable to read the current directory")
25         .map(|entry| {
26             entry
27                 .expect("Failed to get entry")
28                 .file_name()
29                 .into_string()
30                 .expect("Failed to convert file name to string")
31         })
32         .collect::<HashSet<_>>();
33 
34     assert!(
35         root_files.take(build_script_name).is_some(),
36         "Build script must be in the current directory"
37     );
38 
39     let cargo_manifest_dir_file = root_files.take("cargo_manifest_dir_file.txt");
40     assert!(
41         cargo_manifest_dir_file.is_some(),
42         "'cargo_manifest_dir_file.txt' must be in the current directory"
43     );
44     assert_eq!(
45         std::fs::read_to_string(cargo_manifest_dir_file.unwrap()).unwrap(),
46         "This is a file to be found alongside the build script."
47     );
48 
49     if symlink_feature_enabled() {
50         assert!(
51             root_files.take("bazel-out").is_some(),
52             "'bazel-out' must be in the current directory when the symlink feature is enabled"
53         );
54         assert!(
55             root_files.take("external").is_some(),
56             "'external' must be in the current directory when the symlink feature is enabled"
57         );
58     }
59 
60     let remaining_files = root_files
61         .iter()
62         // An __action_home_<hash> directory is created in some remote execution builds.
63         .filter(|file| !file.starts_with("__action_home"))
64         .collect::<HashSet<_>>();
65 
66     // If we're in a sandbox then there should be no other files in the current directory.
67     let is_in_sandbox = is_in_sandbox(&root_files);
68     assert_eq!(
69         remaining_files.is_empty(),
70         is_in_sandbox,
71         "There should not be any other files in the current directory, found {:?}",
72         root_files
73     );
74 }
75 
76 /// Check if the symlink feature is enabled.
symlink_feature_enabled() -> bool77 fn symlink_feature_enabled() -> bool {
78     std::env::var("RULES_RUST_SYMLINK_EXEC_ROOT")
79         .map(|v| v == "1")
80         .unwrap_or(false)
81 }
82 
83 /// Check if the current directory is in a sandbox.
84 ///
85 /// This is done by checking if the current directory contains a directory prefixed with
86 /// `local-spawn-runner`. If it does, then it is assumed to not be in a sandbox.
87 ///
88 /// Non-sandboxed builds contain one or more directories in the exec root with the following
89 /// structure:
90 ///   local-spawn-runner.6722268259075335658/
91 ///   `-- work/
92 ///   local-spawn-runner.3585764808440126801/
93 ///   `-- work/
is_in_sandbox(cwd_files: &HashSet<String>) -> bool94 fn is_in_sandbox(cwd_files: &HashSet<String>) -> bool {
95     for file in cwd_files {
96         if file.starts_with("local-spawn-runner.") {
97             return false;
98         }
99     }
100 
101     true
102 }
103