Skip to content

Commit f5667f4

Browse files
authored
Only copy to sysroot libraries from latest compilation (rust-lang#1840)
As part of cargo build-dev, we recreate the folder target/kani which will include libraries used by the compiler. We used to blindly copy all files inside the build folder, but this may contain older versions of certain libraries triggering issues while resolving dependencies. Now, we only copy artifacts from the current build.
1 parent 6adb351 commit f5667f4

File tree

1 file changed

+98
-66
lines changed

1 file changed

+98
-66
lines changed

tools/build-kani/src/sysroot.rs

Lines changed: 98 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
//!
1313
//! Note: We don't cross-compile. Target is the same as the host.
1414
15-
use crate::{cp, cp_files, AutoRun};
16-
use cargo_metadata::Message;
15+
use crate::{cp, AutoRun};
16+
use cargo_metadata::{Artifact, Message};
1717
use std::ffi::OsStr;
1818
use std::fs;
1919
use std::io::BufReader;
@@ -31,12 +31,12 @@ macro_rules! path_buf {
3131

3232
#[cfg(target_os = "linux")]
3333
fn lib_extension() -> &'static str {
34-
".so"
34+
"so"
3535
}
3636

3737
#[cfg(target_os = "macos")]
3838
fn lib_extension() -> &'static str {
39-
".dylib"
39+
"dylib"
4040
}
4141

4242
/// Returns the path to Kani sysroot. I.e.: folder where we store pre-compiled binaries and
@@ -111,96 +111,128 @@ pub fn build_lib() {
111111
.spawn()
112112
.expect("Failed to run `cargo build`.");
113113

114-
// Remove kani "std" library leftover.
115-
filter_kani_std(&mut cmd);
114+
// Collect the build artifacts.
115+
let artifacts = build_artifacts(&mut cmd);
116116
let _ = cmd.wait().expect("Couldn't get cargo's exit status");
117117

118-
// Create sysroot folder.
118+
// Create sysroot folder hierarchy.
119119
let sysroot_lib = kani_sysroot_lib();
120120
sysroot_lib.exists().then(|| fs::remove_dir_all(&sysroot_lib));
121-
fs::create_dir_all(&sysroot_lib).expect(&format!("Failed to create {sysroot_lib:?}"));
121+
let std_path = path_buf!(&sysroot_lib, "rustlib", target, "lib");
122+
fs::create_dir_all(&std_path).expect(&format!("Failed to create {std_path:?}"));
122123

123-
// Copy Kani libraries to inside sysroot folder.
124-
let target_folder = Path::new(target_dir);
125-
let macro_lib = format!("libkani_macros{}", lib_extension());
126-
let kani_macros = path_buf!(target_folder, "debug", macro_lib);
127-
cp(&kani_macros, &sysroot_lib).unwrap();
124+
// Copy Kani libraries into sysroot top folder.
125+
copy_libs(&artifacts, &sysroot_lib, &is_kani_lib);
126+
// Copy standard libraries into rustlib/<target>/lib/ folder.
127+
copy_libs(&artifacts, &std_path, &is_std_lib);
128+
}
128129

129-
let kani_rlib_folder = path_buf!(target_folder, target, "debug");
130-
cp_files(&kani_rlib_folder, &sysroot_lib, &is_rlib).unwrap();
130+
/// Check if an artifact is a rust library that can be used by rustc on further crates compilations.
131+
/// This inspects the kind of targets that this artifact originates from.
132+
fn is_rust_lib(artifact: &Artifact) -> bool {
133+
artifact.target.kind.iter().any(|kind| match kind.as_str() {
134+
"lib" | "rlib" | "proc-macro" => true,
135+
"bin" | "dylib" | "cdylib" | "staticlib" | "custom-build" => false,
136+
_ => unreachable!("Unknown crate type {kind}"),
137+
})
138+
}
131139

132-
// Copy `std` libraries and dependencies to sysroot folder following expected path format.
133-
let src_path = path_buf!(target_folder, target, "debug", "deps");
140+
/// Return whether this a kani library.
141+
/// For a given artifact, check if this is a library or proc_macro, and whether this is a local
142+
/// crate, i.e., that it is part of the Kani repository.
143+
fn is_kani_lib(artifact: &Artifact) -> bool {
144+
is_rust_lib(artifact) && artifact.target.src_path.starts_with(env!("KANI_REPO_ROOT"))
145+
}
134146

135-
let dst_path = path_buf!(sysroot_lib, "rustlib", target, "lib");
136-
fs::create_dir_all(&dst_path).unwrap();
137-
cp_files(&src_path, &dst_path, &is_rlib).unwrap();
147+
/// Is this a std library or one of its dependencies.
148+
/// For a given artifact, check if this is a library or proc_macro, and whether its source does
149+
/// not belong to a Kani library.
150+
fn is_std_lib(artifact: &Artifact) -> bool {
151+
is_rust_lib(artifact) && !is_kani_lib(artifact)
138152
}
139153

140-
/// Kani's "std" library may cause a name conflict with the rust standard library. We remove it
141-
/// from the `deps/` folder, since we already store it outside of the `deps/` folder.
142-
/// For that, we retrieve its location from `cargo build` output.
143-
fn filter_kani_std(cargo_cmd: &mut Child) {
154+
/// Copy the library files from the artifacts that match the given `predicate`.
155+
/// This function will iterate over the list of artifacts generated by the compiler, it will
156+
/// filter the artifacts according to the given predicate. For the artifacts that satisfy the
157+
/// predicate, it will copy the following files to the `target` folder.
158+
/// - `rlib`: Store metadata for future codegen and executable code for concrete executions.
159+
/// - shared library which are used for proc_macros.
160+
fn copy_libs<P>(artifacts: &[Artifact], target: &Path, predicate: P)
161+
where
162+
P: FnMut(&Artifact) -> bool,
163+
{
164+
assert!(target.is_dir(), "Expected a folder, but found {}", target.display());
165+
for artifact in artifacts.iter().cloned().filter(predicate) {
166+
artifact
167+
.filenames
168+
.iter()
169+
.filter(|path| {
170+
path.extension() == Some("rlib") || path.extension() == Some(lib_extension())
171+
})
172+
.for_each(|lib| cp(lib.clone().as_std_path(), target).unwrap());
173+
}
174+
}
175+
176+
/// Collect all the artifacts generated by Cargo build.
177+
/// This will also include libraries that didn't need to be rebuild.
178+
fn build_artifacts(cargo_cmd: &mut Child) -> Vec<Artifact> {
144179
let reader = BufReader::new(cargo_cmd.stdout.take().unwrap());
145-
for message in Message::parse_stream(reader) {
146-
match message.unwrap() {
147-
Message::CompilerMessage(msg) => {
148-
// Print message as cargo would.
149-
println!("{msg:?}")
150-
}
151-
Message::CompilerArtifact(artifact) => {
152-
// Remove the `rlib` and `rmeta` files for our `std` library from the deps folder.
153-
if artifact.target.name == "std"
154-
&& artifact.target.src_path.starts_with(env!("KANI_REPO_ROOT"))
155-
{
156-
let rmeta = artifact.filenames.iter().find(|p| p.extension() == Some("rmeta"));
157-
let mut glob = PathBuf::from(rmeta.unwrap());
158-
glob.set_extension("*");
159-
Command::new("rm").arg("-f").arg(glob.as_os_str()).run().unwrap();
180+
Message::parse_stream(reader)
181+
.filter_map(|message| {
182+
match message.unwrap() {
183+
Message::CompilerMessage(msg) => {
184+
// Print message as cargo would.
185+
println!("{msg:?}");
186+
None
160187
}
188+
Message::CompilerArtifact(artifact) => Some(artifact),
189+
Message::BuildScriptExecuted(_) | Message::BuildFinished(_) => {
190+
// do nothing
191+
None
192+
}
193+
// Non-exhaustive enum.
194+
_ => None,
161195
}
162-
Message::BuildScriptExecuted(_script) => {
163-
// do nothing
164-
}
165-
Message::BuildFinished(_finished) => {
166-
// do nothing
167-
}
168-
// Non-exhaustive enum.
169-
_ => (),
170-
}
171-
}
196+
})
197+
.collect()
172198
}
173199

174200
/// Build Kani libraries using the regular rust toolchain standard libraries.
175201
/// We should be able to remove this once the MIR linker is stable.
176202
pub fn build_lib_legacy() {
177203
// Run cargo build with -Z build-std
178204
let target_dir = env!("KANI_LEGACY_LIBS");
179-
let args =
180-
["build", "-p", "std", "-p", "kani", "-p", "kani_macros", "--target-dir", target_dir];
181-
Command::new("cargo")
205+
let args = [
206+
"build",
207+
"-p",
208+
"std",
209+
"-p",
210+
"kani",
211+
"-p",
212+
"kani_macros",
213+
"--target-dir",
214+
target_dir,
215+
"--message-format",
216+
"json-diagnostic-rendered-ansi",
217+
];
218+
let mut child = Command::new("cargo")
182219
.env("CARGO_ENCODED_RUSTFLAGS", ["--cfg=kani"].join("\x1f"))
183220
.args(args)
184-
.run()
221+
.stdout(Stdio::piped())
222+
.spawn()
185223
.expect("Failed to build Kani libraries.");
186224

225+
// Collect the build artifacts.
226+
let artifacts = build_artifacts(&mut child);
227+
let _ = child.wait().expect("Couldn't get cargo's exit status");
228+
187229
// Create sysroot folder.
188230
let legacy_lib = kani_sysroot_legacy_lib();
189231
legacy_lib.exists().then(|| fs::remove_dir_all(&legacy_lib));
190-
fs::create_dir_all(&legacy_lib).expect(&format!("Failed to create {legacy_lib:?}"));
191-
192-
// Copy Kani libraries to inside the lib folder.
193-
let target_folder = Path::new(target_dir);
194-
let macro_lib = format!("libkani_macros{}", lib_extension());
195-
let kani_macros = path_buf!(target_folder, "debug", macro_lib);
196-
cp(&kani_macros, &legacy_lib).unwrap();
197-
198-
let kani_rlib_folder = path_buf!(target_folder, "debug");
199-
cp_files(&kani_rlib_folder, &legacy_lib, &is_rlib).unwrap();
200-
}
232+
fs::create_dir_all(&legacy_lib).expect(&format!("Failed to create {:?}", legacy_lib));
201233

202-
fn is_rlib(path: &Path) -> bool {
203-
path.is_file() && String::from(path.file_name().unwrap().to_string_lossy()).ends_with(".rlib")
234+
// Copy Kani libraries to inside the legacy-lib folder.
235+
copy_libs(&artifacts, &legacy_lib, &is_kani_lib);
204236
}
205237

206238
/// Extra arguments to be given to `cargo build` while building Kani's binaries.

0 commit comments

Comments
 (0)