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

model: set mtime of links to epoch to prevent spurious rebuilds #1808

Merged
merged 1 commit into from
Nov 10, 2021
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
1 change: 1 addition & 0 deletions sources/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 sources/models/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ url = "2.1"

[build-dependencies]
cargo-readme = "3.1"
filetime = "0.2"
rand = "0.8"

[lib]
Expand Down
43 changes: 39 additions & 4 deletions sources/models/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
//
// See README.md to understand the symlink setup.

use filetime::{set_symlink_file_times, FileTime};
use rand::{distributions::Alphanumeric, thread_rng, Rng};
use std::env;
use std::fs::{self, File};
Expand All @@ -12,9 +13,21 @@ use std::os::unix::fs::symlink;
use std::path::{Path, PathBuf};
use std::process;

/// The name of the environment variable that tells us the current variant; we need to rebuild if
/// this changes.
const VARIANT_ENV: &str = "VARIANT";

/// We create a link from 'current' to the variant selected by the environment variable above.
const VARIANT_LINK: &str = "src/variant/current";

/// We create a link for the 'variant' module's mod.rs; this can't be checked into the repo because
/// the `src/variant` directory is a cache mount created by Docker before building a package.
/// This isn't variant-specific, so we can have a fixed link target. The file has top-level
/// definitions that apply to all models, and defines a 'current' submodule (that Rust will be able
/// to find through the 'current' link mentioned above) and re-exports everything in 'current' so
/// that consumers of the model don't have to care what the current variant is.
const MOD_LINK: &str = "src/variant/mod.rs";
const VARIANT_ENV: &str = "VARIANT";
const MOD_LINK_TARGET: &str = "../variant_mod.rs";

fn main() {
// Tell cargo when we have to rerun; we always want variant links to be correct, especially
Expand Down Expand Up @@ -54,11 +67,33 @@ fn link_current_variant() {

// Also create the link for mod.rs so Rust can import source from the "current" link
// created above.
let mod_target = "../variant_mod.rs";
symlink_safe(&mod_target, MOD_LINK).unwrap_or_else(|e| {
eprintln!("Failed to create symlink at '{}' pointing to '{}' - we need this to build a Rust module structure through the `current` link. Error: {}", MOD_LINK, mod_target, e);
symlink_safe(MOD_LINK_TARGET, MOD_LINK).unwrap_or_else(|e| {
eprintln!("Failed to create symlink at '{}' pointing to '{}' - we need this to build a Rust module structure through the `current` link. Error: {}", MOD_LINK, MOD_LINK_TARGET, e);
process::exit(1);
});

// Set the mtime of the links to a fixed time, the epoch. This is because cargo decides
// whether to rerun build.rs based on the "rerun-if-changed" statements printed above and the
// mtime of the files they reference. If the mtime of the file doesn't match the mtime of the
// "output" file in the build directory (which contains the output of the rerun-if prints) then
// it rebuilds. Those times won't match because we don't control when they happen, meaning
// we'd rebuild every time. Setting to a consistent time means we only rebuild when the other
// rerun-if statements apply, the important one being the variant changing.
//
// Note that we still use rerun-if-changed for these links in case someone changes them outside
// of this build.rs. If they really want to get around our system, they'd also need to set the
// mtime to epoch, and then hopefully they know what they're doing.
for link in &[VARIANT_LINK, MOD_LINK] {
// Do our best, but if we fail, rebuilding isn't the end of the world.
// Note: set_symlink_file_times is the only method that operates on the symlink rather than
// its target, and it also updates atime, which we don't care about but isn't harmful.
if let Err(e) = set_symlink_file_times(link, FileTime::zero(), FileTime::zero()) {
eprintln!(
"Warning: unable to set mtime on {}; crate may rebuild unnecessarily: {}",
link, e
);
}
}
}

fn generate_readme() {
Expand Down