use super::super::super::*;
const SYSTEM_INCLUDE_DISCOVERY_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(30);
pub(super) struct SystemIncludesOutcome {
pub(super) includes: Vec<NormalizedPath>,
pub(super) system_includes_ns: u64,
pub(super) system_watch_ns: u64,
}
pub(super) async fn discover_system_includes(
state: &SharedState,
compiler: &NormalizedPath,
lineage: &crate::daemon::lineage::Lineage,
compiler_priority: CompilePriority,
want_rust_miss_profile: bool,
) -> SystemIncludesOutcome {
let t_system_includes = want_rust_miss_profile.then(std::time::Instant::now);
let compiler_family = crate::compiler::detect_family(&compiler.to_string_lossy());
let needs_discovery = compiler_family.needs_system_include_discovery();
let system_includes = if !needs_discovery {
Vec::new()
} else {
let use_fast = matches!(compiler_family, crate::compiler::CompilerFamily::Clang);
let cached = {
let cache = state.system_includes.lock().await;
cache.get(compiler).map(|paths| paths.to_vec())
};
if let Some(paths) = cached {
paths
} else {
let discovered =
discover_system_include_paths(compiler, lineage, compiler_priority, use_fast).await;
let (resolved, inserted_snapshot) = {
let mut cache = state.system_includes.lock().await;
if let Some(paths) = cache.get(compiler) {
(paths.to_vec(), None)
} else if let Some(discovered) = discovered {
cache.insert(compiler.clone(), discovered);
let paths = cache
.get(compiler)
.map(|paths| paths.to_vec())
.unwrap_or_default();
let snapshot = cache.clone();
(paths, Some(snapshot))
} else {
(Vec::new(), None)
}
};
if let Some(snapshot) = inserted_snapshot {
if state.system_includes_loaded.load(Ordering::Acquire) {
let path = state.system_includes_cache_path.clone();
tokio::task::spawn_blocking(move || {
if let Err(e) = snapshot.save_to_disk(path.as_path()) {
tracing::warn!(
path = %path.display(),
"system include cache write-through failed: {e}"
);
}
});
}
}
resolved
}
};
let system_includes_ns = t_system_includes
.map(|t| t.elapsed().as_nanos() as u64)
.unwrap_or(0);
let t_system_watch = want_rust_miss_profile.then(std::time::Instant::now);
watch_directories(state, &system_includes).await;
let system_watch_ns = t_system_watch
.map(|t| t.elapsed().as_nanos() as u64)
.unwrap_or(0);
SystemIncludesOutcome {
includes: system_includes,
system_includes_ns,
system_watch_ns,
}
}
async fn discover_system_include_paths(
compiler: &NormalizedPath,
lineage: &crate::daemon::lineage::Lineage,
compiler_priority: CompilePriority,
use_fast: bool,
) -> Option<Vec<NormalizedPath>> {
let disc_args = if use_fast {
crate::depgraph::discovery_args_fast()
} else {
crate::depgraph::discovery_args()
};
let output = run_discovery_command(compiler, &disc_args, lineage, compiler_priority).await;
match output {
Ok(out) => {
let stderr = String::from_utf8_lossy(&out.stderr);
let mut paths = if use_fast {
crate::depgraph::parse_cc1_system_include_output(&stderr)
} else {
crate::depgraph::parse_system_include_output(&stderr)
};
if use_fast && paths.is_empty() {
let slow_args = crate::depgraph::discovery_args();
match run_discovery_command(compiler, &slow_args, lineage, compiler_priority).await
{
Ok(out) => {
let stderr = String::from_utf8_lossy(&out.stderr);
paths = crate::depgraph::parse_system_include_output(&stderr);
}
Err(e) => {
tracing::warn!(
"failed to run fallback compiler for include discovery: {e}"
);
}
}
}
Some(paths)
}
Err(e) => {
tracing::warn!("failed to run compiler for include discovery: {e}");
None
}
}
}
async fn run_discovery_command(
compiler: &NormalizedPath,
args: &[&str],
lineage: &crate::daemon::lineage::Lineage,
compiler_priority: CompilePriority,
) -> std::io::Result<std::process::Output> {
let mut cmd = tokio::process::Command::new(compiler);
cmd.args(args);
lineage.apply_to_tokio(&mut cmd, None);
crate::daemon::process::tokio_command_output_with_priority_timeout(
&mut cmd,
compiler_priority,
SYSTEM_INCLUDE_DISCOVERY_TIMEOUT,
)
.await
}