Skip to content

Commit 4af7b6c

Browse files
Shourya742ZhongyaoChen
authored andcommitted
add parse_download_ci_llvm function and invoke from parse_inner
1 parent fcfa878 commit 4af7b6c

File tree

2 files changed

+248
-7
lines changed

2 files changed

+248
-7
lines changed

src/bootstrap/src/core/build_steps/llvm.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,10 @@ pub(crate) fn detect_llvm_freshness(config: &Config, is_git: bool) -> PathFreshn
196196
///
197197
/// This checks the build triple platform to confirm we're usable at all, and if LLVM
198198
/// with/without assertions is available.
199-
pub(crate) fn is_ci_llvm_available_for_target(config: &Config, asserts: bool) -> bool {
199+
pub(crate) fn is_ci_llvm_available_for_target(
200+
host_target: &TargetSelection,
201+
asserts: bool,
202+
) -> bool {
200203
// This is currently all tier 1 targets and tier 2 targets with host tools
201204
// (since others may not have CI artifacts)
202205
// https://doc.rust-lang.org/rustc/platform-support.html#tier-1
@@ -235,8 +238,8 @@ pub(crate) fn is_ci_llvm_available_for_target(config: &Config, asserts: bool) ->
235238
("x86_64-unknown-netbsd", false),
236239
];
237240

238-
if !supported_platforms.contains(&(&*config.host_target.triple, asserts))
239-
&& (asserts || !supported_platforms.contains(&(&*config.host_target.triple, true)))
241+
if !supported_platforms.contains(&(&*host_target.triple, asserts))
242+
&& (asserts || !supported_platforms.contains(&(&*host_target.triple, true)))
240243
{
241244
return false;
242245
}

src/bootstrap/src/core/config/config.rs

Lines changed: 242 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1200,8 +1200,19 @@ impl Config {
12001200
config.llvm_enable_warnings = llvm_enable_warnings.unwrap_or(false);
12011201
config.llvm_build_config = llvm_build_config.clone().unwrap_or(Default::default());
12021202

1203-
config.llvm_from_ci =
1204-
config.parse_download_ci_llvm(llvm_download_ci_llvm, config.llvm_assertions);
1203+
config.llvm_from_ci = parse_download_ci_llvm(
1204+
&config.exec_ctx,
1205+
&config.submodules,
1206+
&config.stage0_metadata,
1207+
&config.src,
1208+
config.path_modification_cache.clone(),
1209+
&config.host_target,
1210+
&config.download_rustc_commit,
1211+
&config.rust_info,
1212+
config.is_running_on_ci,
1213+
llvm_download_ci_llvm,
1214+
config.llvm_assertions,
1215+
);
12051216

12061217
if config.llvm_from_ci {
12071218
let warn = |option: &str| {
@@ -1902,7 +1913,11 @@ impl Config {
19021913
let has_changes = self.has_changes_from_upstream(LLVM_INVALIDATION_PATHS);
19031914

19041915
// Return false if there are untracked changes, otherwise check if CI LLVM is available.
1905-
if has_changes { false } else { llvm::is_ci_llvm_available_for_target(self, asserts) }
1916+
if has_changes {
1917+
false
1918+
} else {
1919+
llvm::is_ci_llvm_available_for_target(&self.host_target, asserts)
1920+
}
19061921
};
19071922

19081923
match download_ci_llvm {
@@ -1921,7 +1936,7 @@ impl Config {
19211936
}
19221937

19231938
// If download-ci-llvm=true we also want to check that CI llvm is available
1924-
b && llvm::is_ci_llvm_available_for_target(self, asserts)
1939+
b && llvm::is_ci_llvm_available_for_target(&self.host_target, asserts)
19251940
}
19261941
StringOrBool::String(s) if s == "if-unchanged" => if_unchanged(),
19271942
StringOrBool::String(other) => {
@@ -2467,3 +2482,226 @@ pub fn git_config(stage0_metadata: &build_helper::stage0_parser::Stage0) -> GitC
24672482
git_merge_commit_email: &stage0_metadata.config.git_merge_commit_email,
24682483
}
24692484
}
2485+
2486+
pub fn parse_download_ci_llvm(
2487+
exec_ctx: &ExecutionContext,
2488+
submodules: &Option<bool>,
2489+
stage0_metadata: &build_helper::stage0_parser::Stage0,
2490+
src: &Path,
2491+
path_modification_cache: Arc<Mutex<HashMap<Vec<&'static str>, PathFreshness>>>,
2492+
host_target: &TargetSelection,
2493+
download_rustc_commit: &Option<String>,
2494+
rust_info: &channel::GitInfo,
2495+
is_running_on_ci: bool,
2496+
download_ci_llvm: Option<StringOrBool>,
2497+
asserts: bool,
2498+
) -> bool {
2499+
// We don't ever want to use `true` on CI, as we should not
2500+
// download upstream artifacts if there are any local modifications.
2501+
let default = if is_running_on_ci {
2502+
StringOrBool::String("if-unchanged".to_string())
2503+
} else {
2504+
StringOrBool::Bool(true)
2505+
};
2506+
let download_ci_llvm = download_ci_llvm.unwrap_or(default);
2507+
2508+
let if_unchanged = || {
2509+
if rust_info.is_from_tarball() {
2510+
// Git is needed for running "if-unchanged" logic.
2511+
println!("ERROR: 'if-unchanged' is only compatible with Git managed sources.");
2512+
crate::exit!(1);
2513+
}
2514+
2515+
// Fetching the LLVM submodule is unnecessary for self-tests.
2516+
#[cfg(not(test))]
2517+
update_submodule(submodules, exec_ctx, src, rust_info, "src/llvm-project");
2518+
2519+
// Check for untracked changes in `src/llvm-project` and other important places.
2520+
let has_changes = has_changes_from_upstream(
2521+
stage0_metadata,
2522+
src,
2523+
path_modification_cache,
2524+
LLVM_INVALIDATION_PATHS,
2525+
);
2526+
2527+
// Return false if there are untracked changes, otherwise check if CI LLVM is available.
2528+
if has_changes {
2529+
false
2530+
} else {
2531+
llvm::is_ci_llvm_available_for_target(host_target, asserts)
2532+
}
2533+
};
2534+
2535+
match download_ci_llvm {
2536+
StringOrBool::Bool(b) => {
2537+
if !b && download_rustc_commit.is_some() {
2538+
panic!(
2539+
"`llvm.download-ci-llvm` cannot be set to `false` if `rust.download-rustc` is set to `true` or `if-unchanged`."
2540+
);
2541+
}
2542+
2543+
if b && is_running_on_ci {
2544+
// On CI, we must always rebuild LLVM if there were any modifications to it
2545+
panic!(
2546+
"`llvm.download-ci-llvm` cannot be set to `true` on CI. Use `if-unchanged` instead."
2547+
);
2548+
}
2549+
2550+
// If download-ci-llvm=true we also want to check that CI llvm is available
2551+
b && llvm::is_ci_llvm_available_for_target(host_target, asserts)
2552+
}
2553+
StringOrBool::String(s) if s == "if-unchanged" => if_unchanged(),
2554+
StringOrBool::String(other) => {
2555+
panic!("unrecognized option for download-ci-llvm: {other:?}")
2556+
}
2557+
}
2558+
}
2559+
2560+
pub fn has_changes_from_upstream(
2561+
stage0_metadata: &build_helper::stage0_parser::Stage0,
2562+
src: &Path,
2563+
path_modification_cache: Arc<Mutex<HashMap<Vec<&'static str>, PathFreshness>>>,
2564+
paths: &[&'static str],
2565+
) -> bool {
2566+
match check_path_modifications_(stage0_metadata, src, path_modification_cache, paths) {
2567+
PathFreshness::LastModifiedUpstream { .. } => false,
2568+
PathFreshness::HasLocalModifications { .. } | PathFreshness::MissingUpstream => true,
2569+
}
2570+
}
2571+
2572+
#[cfg_attr(
2573+
feature = "tracing",
2574+
instrument(
2575+
level = "trace",
2576+
name = "Config::update_submodule",
2577+
skip_all,
2578+
fields(relative_path = ?relative_path),
2579+
),
2580+
)]
2581+
pub(crate) fn update_submodule(
2582+
submodules: &Option<bool>,
2583+
exec_ctx: &ExecutionContext,
2584+
src: &Path,
2585+
rust_info: &channel::GitInfo,
2586+
relative_path: &str,
2587+
) {
2588+
if rust_info.is_from_tarball() || !submodules_(submodules, rust_info) {
2589+
return;
2590+
}
2591+
2592+
let absolute_path = src.join(relative_path);
2593+
2594+
// NOTE: This check is required because `jj git clone` doesn't create directories for
2595+
// submodules, they are completely ignored. The code below assumes this directory exists,
2596+
// so create it here.
2597+
if !absolute_path.exists() {
2598+
t!(fs::create_dir_all(&absolute_path));
2599+
}
2600+
2601+
// NOTE: The check for the empty directory is here because when running x.py the first time,
2602+
// the submodule won't be checked out. Check it out now so we can build it.
2603+
if !git_info(exec_ctx, false, &absolute_path).is_managed_git_subrepository()
2604+
&& !helpers::dir_is_empty(&absolute_path)
2605+
{
2606+
return;
2607+
}
2608+
2609+
// Submodule updating actually happens during in the dry run mode. We need to make sure that
2610+
// all the git commands below are actually executed, because some follow-up code
2611+
// in bootstrap might depend on the submodules being checked out. Furthermore, not all
2612+
// the command executions below work with an empty output (produced during dry run).
2613+
// Therefore, all commands below are marked with `run_in_dry_run()`, so that they also run in
2614+
// dry run mode.
2615+
let submodule_git = || {
2616+
let mut cmd = helpers::git(Some(&absolute_path));
2617+
cmd.run_in_dry_run();
2618+
cmd
2619+
};
2620+
2621+
// Determine commit checked out in submodule.
2622+
let checked_out_hash =
2623+
submodule_git().args(["rev-parse", "HEAD"]).run_capture_stdout(exec_ctx).stdout();
2624+
let checked_out_hash = checked_out_hash.trim_end();
2625+
// Determine commit that the submodule *should* have.
2626+
let recorded = helpers::git(Some(src))
2627+
.run_in_dry_run()
2628+
.args(["ls-tree", "HEAD"])
2629+
.arg(relative_path)
2630+
.run_capture_stdout(exec_ctx)
2631+
.stdout();
2632+
2633+
let actual_hash = recorded
2634+
.split_whitespace()
2635+
.nth(2)
2636+
.unwrap_or_else(|| panic!("unexpected output `{recorded}`"));
2637+
2638+
if actual_hash == checked_out_hash {
2639+
// already checked out
2640+
return;
2641+
}
2642+
2643+
println!("Updating submodule {relative_path}");
2644+
2645+
helpers::git(Some(src))
2646+
.allow_failure()
2647+
.run_in_dry_run()
2648+
.args(["submodule", "-q", "sync"])
2649+
.arg(relative_path)
2650+
.run(exec_ctx);
2651+
2652+
// Try passing `--progress` to start, then run git again without if that fails.
2653+
let update = |progress: bool| {
2654+
// Git is buggy and will try to fetch submodules from the tracking branch for *this* repository,
2655+
// even though that has no relation to the upstream for the submodule.
2656+
let current_branch = helpers::git(Some(src))
2657+
.allow_failure()
2658+
.run_in_dry_run()
2659+
.args(["symbolic-ref", "--short", "HEAD"])
2660+
.run_capture(exec_ctx);
2661+
2662+
let mut git = helpers::git(Some(&src)).allow_failure();
2663+
git.run_in_dry_run();
2664+
if current_branch.is_success() {
2665+
// If there is a tag named after the current branch, git will try to disambiguate by prepending `heads/` to the branch name.
2666+
// This syntax isn't accepted by `branch.{branch}`. Strip it.
2667+
let branch = current_branch.stdout();
2668+
let branch = branch.trim();
2669+
let branch = branch.strip_prefix("heads/").unwrap_or(branch);
2670+
git.arg("-c").arg(format!("branch.{branch}.remote=origin"));
2671+
}
2672+
git.args(["submodule", "update", "--init", "--recursive", "--depth=1"]);
2673+
if progress {
2674+
git.arg("--progress");
2675+
}
2676+
git.arg(relative_path);
2677+
git
2678+
};
2679+
if !update(true).allow_failure().run(exec_ctx) {
2680+
update(false).allow_failure().run(exec_ctx);
2681+
}
2682+
2683+
// Save any local changes, but avoid running `git stash pop` if there are none (since it will exit with an error).
2684+
// diff-index reports the modifications through the exit status
2685+
let has_local_modifications =
2686+
!submodule_git().allow_failure().args(["diff-index", "--quiet", "HEAD"]).run(exec_ctx);
2687+
if has_local_modifications {
2688+
submodule_git().allow_failure().args(["stash", "push"]).run(exec_ctx);
2689+
}
2690+
2691+
submodule_git().allow_failure().args(["reset", "-q", "--hard"]).run(exec_ctx);
2692+
submodule_git().allow_failure().args(["clean", "-qdfx"]).run(exec_ctx);
2693+
2694+
if has_local_modifications {
2695+
submodule_git().allow_failure().args(["stash", "pop"]).run(exec_ctx);
2696+
}
2697+
}
2698+
2699+
pub fn git_info(exec_ctx: &ExecutionContext, omit_git_hash: bool, dir: &Path) -> GitInfo {
2700+
GitInfo::new(omit_git_hash, dir, exec_ctx)
2701+
}
2702+
2703+
pub fn submodules_(submodules: &Option<bool>, rust_info: &channel::GitInfo) -> bool {
2704+
// If not specified in config, the default is to only manage
2705+
// submodules if we're currently inside a git repository.
2706+
submodules.unwrap_or(rust_info.is_managed_git_subrepository())
2707+
}

0 commit comments

Comments
 (0)