Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make sure rerun/rerun_py/re_viewer build info is updated on each build #2087

Merged
merged 8 commits into from
May 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/re_analytics/build.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
fn main() {
re_build_build_info::rebuild_if_crate_changed("re_analytics");
re_build_build_info::export_env_vars();
}
2 changes: 2 additions & 0 deletions crates/re_build_build_info/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,6 @@ all-features = true

[dependencies]
anyhow.workspace = true
cargo_metadata = "0.15"
glob = "0.3"
time = { workspace = true, features = ["formatting"] }
14 changes: 12 additions & 2 deletions crates/re_build_build_info/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ use anyhow::Context as _;

use std::process::Command;

mod rebuild_detector;

pub use rebuild_detector::rebuild_if_crate_changed;

// Situations to consider
// ----------------------
//
Expand Down Expand Up @@ -66,17 +70,23 @@ pub fn export_env_vars() {

// Make sure we re-run the build script if the branch or commit changes:
if let Ok(head_path) = git_path("HEAD") {
eprintln!("cargo:rerun-if-changed={head_path}"); // Track changes to branch
rerun_if_changed(&head_path); // Track changes to branch
if let Ok(head) = std::fs::read_to_string(&head_path) {
if let Some(git_file) = head.strip_prefix("ref: ") {
if let Ok(path) = git_path(git_file) {
eprintln!("cargo:rerun-if-changed={path}"); // Track changes to commit hash
rerun_if_changed(&path); // Track changes to commit hash
}
}
}
}
}

fn rerun_if_changed(path: &str) {
// Make sure the file exists, otherwise we'll be rebuilding all the time.
assert!(std::path::Path::new(path).exists(), "Failed to find {path}");
println!("cargo:rerun-if-changed={path}");
}

fn set_env(name: &str, value: &str) {
println!("cargo:rustc-env={name}={value}");
}
Expand Down
140 changes: 140 additions & 0 deletions crates/re_build_build_info/src/rebuild_detector.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
#![allow(clippy::unwrap_used)]

use std::{
collections::{HashMap, HashSet},
path::PathBuf,
};

use cargo_metadata::{CargoOpt, Metadata, MetadataCommand, Package, PackageId};

/// Call from `build.rs` to trigger a rebuild whenever any source file of the given package
/// _or any of its dependencies_ changes, recursively.
///
/// This will work even if the package depends on crates that are outside of the workspace,
/// included with `path = …`
pub fn rebuild_if_crate_changed(pkg_name: &str) {
if !is_tracked_env_var_set("IS_IN_RERUN_WORKSPACE") {
// Only run if we are in the rerun workspace, not on users machines.
return;
}

let metadata = MetadataCommand::new()
.features(CargoOpt::AllFeatures)
.exec()
.unwrap();

let mut files_to_watch = Default::default();

let pkgs = Packages::from_metadata(&metadata);
pkgs.track_implicit_dep(pkg_name, &mut files_to_watch);

for path in &files_to_watch {
rerun_if_changed(path);
}
}

fn get_and_track_env_var(env_var_name: &str) -> Result<String, std::env::VarError> {
println!("cargo:rerun-if-env-changed={env_var_name}");
std::env::var(env_var_name)
}

fn is_tracked_env_var_set(env_var_name: &str) -> bool {
let var = get_and_track_env_var(env_var_name).map(|v| v.to_lowercase());
var == Ok("1".to_owned()) || var == Ok("yes".to_owned()) || var == Ok("true".to_owned())
}

fn rerun_if_changed(path: &std::path::Path) {
// Make sure the file exists, otherwise we'll be rebuilding all the time.
assert!(path.exists(), "Failed to find {path:?}");
println!("cargo:rerun-if-changed={}", path.to_str().unwrap());
}

fn rerun_if_changed_glob(path: &str, files_to_watch: &mut HashSet<PathBuf>) {
// Workaround for windows verbatim paths not working with glob.
// Issue: https://github.com/rust-lang/glob/issues/111
// Fix: https://github.com/rust-lang/glob/pull/112
// Fixed on upstream, but no release containing the fix as of writing.
let path = path.trim_start_matches(r"\\?\");

for path in glob::glob(path).unwrap() {
files_to_watch.insert(path.unwrap());
}
}

// ---

struct Packages<'a> {
pkgs: HashMap<&'a str, &'a Package>,
}

impl<'a> Packages<'a> {
pub fn from_metadata(metadata: &'a Metadata) -> Self {
let pkgs = metadata
.packages
.iter()
.map(|pkg| (pkg.name.as_str(), pkg))
.collect::<HashMap<_, _>>();

Self { pkgs }
}

/// Tracks an implicit dependency of the given name.
///
/// This will generate all the appropriate `cargo:rerun-if-changed` clauses
/// so that package `pkg_name` as well as all of it direct and indirect
/// dependencies are properly tracked whether they are remote, in-workspace,
/// or locally patched.
pub fn track_implicit_dep(&self, pkg_name: &str, files_to_watch: &mut HashSet<PathBuf>) {
let pkg = self.pkgs.get(pkg_name).unwrap_or_else(|| {
let found_names: Vec<&str> = self.pkgs.values().map(|pkg| pkg.name.as_str()).collect();
panic!("Failed to find package {pkg_name:?} among {found_names:?}")
});

// Track the root package itself
{
let mut path = pkg.manifest_path.clone();
path.pop();

// NOTE: Since we track the cargo manifest, past this point we only need to
// account for locally patched dependencies.
rerun_if_changed_glob(path.join("Cargo.toml").as_ref(), files_to_watch);
rerun_if_changed_glob(path.join("**/*.rs").as_ref(), files_to_watch);
rerun_if_changed_glob(path.join("**/*.wgsl").as_ref(), files_to_watch);
}

// Track all direct and indirect dependencies of that root package
let mut tracked = HashSet::new();
self.track_patched_deps(&mut tracked, pkg, files_to_watch);
}

/// Recursively walk the tree of dependencies of the given `root` package, making sure
/// to track all potentially modified, locally patched dependencies.
fn track_patched_deps(
&self,
tracked: &mut HashSet<PackageId>,
root: &Package,
files_to_watch: &mut HashSet<PathBuf>,
) {
for dep_pkg in root
.dependencies
.iter()
// NOTE: We'd like to just use `dep.source`/`dep.path`, unfortunately they do not
// account for crate patches at this level, so we build our own little index
// and use that instead.
.filter_map(|dep| self.pkgs.get(dep.name.as_str()))
{
let exists_on_local_disk = dep_pkg.source.is_none();
if exists_on_local_disk {
let mut dep_path = dep_pkg.manifest_path.clone();
dep_path.pop();

rerun_if_changed_glob(dep_path.join("Cargo.toml").as_ref(), files_to_watch); // manifest too!
rerun_if_changed_glob(dep_path.join("**/*.rs").as_ref(), files_to_watch);
}

if tracked.insert(dep_pkg.id.clone()) {
self.track_patched_deps(tracked, dep_pkg, files_to_watch);
}
}
}
}
1 change: 0 additions & 1 deletion crates/re_renderer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,5 @@ web-sys = { version = "0.3.61", features = [
[build-dependencies]
anyhow.workspace = true
clean-path = "0.2"
glob = "0.3"
pathdiff = "0.2"
walkdir = "2.0"
17 changes: 5 additions & 12 deletions crates/re_renderer/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,10 @@ use walkdir::{DirEntry, WalkDir};

// ---

// Mapping to cargo:rerun-if-changed with glob support
fn rerun_if_changed(path: &str) {
// Workaround for windows verbatim paths not working with glob.
// Issue: https://github.com/rust-lang/glob/issues/111
// Fix: https://github.com/rust-lang/glob/pull/112
// Fixed on upstream, but no release containing the fix as of writing.
let path = path.trim_start_matches(r"\\?\");

for path in glob::glob(path).unwrap() {
println!("cargo:rerun-if-changed={}", path.unwrap().to_string_lossy());
}
fn rerun_if_changed(path: &std::path::Path) {
// Make sure the file exists, otherwise we'll be rebuilding all the time.
assert!(path.exists(), "Failed to find {path:?}");
println!("cargo:rerun-if-changed={}", path.to_str().unwrap());
}

// ---
Expand Down Expand Up @@ -200,7 +193,7 @@ pub fn init() {
);

for entry in entries {
rerun_if_changed(entry.path().to_string_lossy().as_ref());
rerun_if_changed(entry.path());

// The relative path to get from the current shader file to `workspace_shaders.rs`.
// We must make sure to pass relative paths to `include_str`!
Expand Down
1 change: 1 addition & 0 deletions crates/re_sdk/build.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
fn main() {
re_build_build_info::rebuild_if_crate_changed("re_sdk");
re_build_build_info::export_env_vars();
}
1 change: 1 addition & 0 deletions crates/re_viewer/build.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
fn main() {
re_build_build_info::rebuild_if_crate_changed("re_viewer");
re_build_build_info::export_env_vars();
}
3 changes: 1 addition & 2 deletions crates/re_web_viewer_server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,5 @@ re_analytics = { workspace = true, optional = true }


[build-dependencies]
glob = "0.3.0"
cargo_metadata = "0.15"
re_build_build_info.workspace = true
re_build_web_viewer.workspace = true
102 changes: 5 additions & 97 deletions crates/re_web_viewer_server/build.rs
Original file line number Diff line number Diff line change
@@ -1,95 +1,9 @@
#![allow(clippy::unwrap_used)]

use std::collections::{HashMap, HashSet};

use cargo_metadata::{CargoOpt, Metadata, MetadataCommand, Package, PackageId};

// ---

// Mapping to cargo:rerun-if-changed with glob support
fn rerun_if_changed(path: &str) {
// Workaround for windows verbatim paths not working with glob.
// Issue: https://github.com/rust-lang/glob/issues/111
// Fix: https://github.com/rust-lang/glob/pull/112
// Fixed on upstream, but no release containing the fix as of writing.
let path = path.trim_start_matches(r"\\?\");

for path in glob::glob(path).unwrap() {
println!("cargo:rerun-if-changed={}", path.unwrap().to_string_lossy());
}
}

// ---

struct Packages<'a> {
pkgs: HashMap<&'a str, &'a Package>,
}

impl<'a> Packages<'a> {
pub fn from_metadata(metadata: &'a Metadata) -> Self {
let pkgs = metadata
.packages
.iter()
.map(|pkg| (pkg.name.as_str(), pkg))
.collect::<HashMap<_, _>>();

Self { pkgs }
}

/// Tracks an implicit dependency of the given name.
///
/// This will generate all the appropriate `cargo:rerun-if-changed` clauses
/// so that package `pkg_name` as well as all of it direct and indirect
/// dependencies are properly tracked whether they are remote, in-workspace,
/// or locally patched.
pub fn track_implicit_dep(&self, pkg_name: &str) {
let pkg = self.pkgs.get(pkg_name).unwrap_or_else(|| {
let found_names: Vec<&str> = self.pkgs.values().map(|pkg| pkg.name.as_str()).collect();
panic!("Failed to find package {pkg_name:?} among {found_names:?}")
});

// Track the root package itself
{
let mut path = pkg.manifest_path.clone();
path.pop();

// NOTE: Since we track the cargo manifest, past this point we only need to
// account for locally patched dependencies.
rerun_if_changed(path.join("Cargo.toml").as_ref());
rerun_if_changed(path.join("**/*.rs").as_ref());
rerun_if_changed(path.join("**/*.wgsl").as_ref());
}

// Track all direct and indirect dependencies of that root package
let mut tracked = HashSet::new();
self.track_patched_deps(&mut tracked, pkg);
}

/// Recursively walk the tree of dependencies of the given `root` package, making sure
/// to track all potentially modified, locally patched dependencies.
fn track_patched_deps(&self, tracked: &mut HashSet<PackageId>, root: &Package) {
for dep_pkg in root
.dependencies
.iter()
// NOTE: We'd like to just use `dep.source`/`dep.path`, unfortunately they do not
// account for crate patches at this level, so we build our own little index
// and use that instead.
.filter_map(|dep| self.pkgs.get(dep.name.as_str()))
{
let exists_on_local_disk = dep_pkg.source.is_none();
if exists_on_local_disk {
let mut dep_path = dep_pkg.manifest_path.clone();
dep_path.pop();

rerun_if_changed(dep_path.join("Cargo.toml").as_ref()); // manifest too!
rerun_if_changed(dep_path.join("**/*.rs").as_ref());
}

if tracked.insert(dep_pkg.id.clone()) {
self.track_patched_deps(tracked, dep_pkg);
}
}
}
// Make sure the file exists, otherwise we'll be rebuilding all the time.
assert!(std::path::Path::new(path).exists(), "Failed to find {path}");
println!("cargo:rerun-if-changed={path}");
}

fn get_and_track_env_var(env_var_name: &str) -> Result<String, std::env::VarError> {
Expand All @@ -116,20 +30,14 @@ fn main() {
// Rebuild the web-viewer Wasm,
// because the web_server library bundles it with `include_bytes!`.

let metadata = MetadataCommand::new()
.features(CargoOpt::AllFeatures)
.exec()
.unwrap();

rerun_if_changed("../../web_viewer/favicon.ico");
rerun_if_changed("../../web_viewer/favicon.svg");
rerun_if_changed("../../web_viewer/index.html");
rerun_if_changed("../../web_viewer/sw.js");

let pkgs = Packages::from_metadata(&metadata);
// We implicitly depend on re_viewer, which means we also implicitly depend on
// all of its direct and indirect dependencies (which are potentially in-workspace
// or patched!).
pkgs.track_implicit_dep("re_viewer");
re_build_build_info::rebuild_if_crate_changed("re_viewer");

if get_and_track_env_var("CARGO_FEATURE___CI").is_ok() {
// If the `__ci` feature is set we skip building the web viewer wasm, saving a lot of time.
Expand Down
Loading