Auto merge of #71858 - petrochenkov:env, r=Mark-Simulacrum
Print environment variables accessed by rustc as special comments into depinfo files So cargo (and perhaps others tools) can use them for linting (at least) or for actually rebuilding crates on env var changes. --- I've recently observed one more forgotten environment variable in a build script https://github.com/rust-lang/rust/pull/71314/commits/8a77d1ca3fc2df789157f7986ddbaf2a377ff0fe and thought it would be nice to provide the list of accessed variables to cargo automatically as a part of depinfo. Unsurprisingly, I wasn't the first who had this idea - cc https://github.com/rust-lang/rust/issues/70517 https://github.com/rust-lang/rust/issues/40364 https://github.com/rust-lang/rust/issues/44074. Also, there are dozens of uses of `(option_)env!` in rustc repo and, like, half of them are not registered in build scripts. --- Description: - depinfo files are extended with special comments containing info about environment variables accessed during compilation. - Comment format for environment variables with successfully retrieved value: `# env-dep:KEY=VALUE`. - Comment format for environment variables without successfully retrieved value: `# env-dep:KEY` (can happen with `option_env!`). - `KEY` and `VALUE` are minimally escaped (`\n`, `\r`, `\\`) so they don't break makefile comments and can be unescaped by anything that can unescape standard `escape_default` and friends. FCP report: https://github.com/rust-lang/rust/pull/71858#issuecomment-633071488 Closes https://github.com/rust-lang/rust/issues/70517 Closes https://github.com/rust-lang/rust/issues/40364 Closes https://github.com/rust-lang/rust/issues/44074 A new issue in the cargo repo will be needed to track the cargo side of this feature. r? @ehuss
This commit is contained in:
commit
1033351a51
6 changed files with 64 additions and 7 deletions
|
@ -22,8 +22,10 @@ pub fn expand_option_env<'cx>(
|
|||
};
|
||||
|
||||
let sp = cx.with_def_site_ctxt(sp);
|
||||
let e = match env::var(&var.as_str()) {
|
||||
Err(..) => {
|
||||
let value = env::var(&var.as_str()).ok().as_deref().map(Symbol::intern);
|
||||
cx.parse_sess.env_depinfo.borrow_mut().insert((Symbol::intern(&var), value));
|
||||
let e = match value {
|
||||
None => {
|
||||
let lt = cx.lifetime(sp, Ident::new(kw::StaticLifetime, sp));
|
||||
cx.expr_path(cx.path_all(
|
||||
sp,
|
||||
|
@ -37,10 +39,10 @@ pub fn expand_option_env<'cx>(
|
|||
))],
|
||||
))
|
||||
}
|
||||
Ok(s) => cx.expr_call_global(
|
||||
Some(value) => cx.expr_call_global(
|
||||
sp,
|
||||
cx.std_path(&[sym::option, sym::Option, sym::Some]),
|
||||
vec![cx.expr_str(sp, Symbol::intern(&s))],
|
||||
vec![cx.expr_str(sp, value)],
|
||||
),
|
||||
};
|
||||
MacEager::expr(e)
|
||||
|
@ -78,12 +80,14 @@ pub fn expand_env<'cx>(
|
|||
}
|
||||
|
||||
let sp = cx.with_def_site_ctxt(sp);
|
||||
let e = match env::var(&*var.as_str()) {
|
||||
Err(_) => {
|
||||
let value = env::var(&*var.as_str()).ok().as_deref().map(Symbol::intern);
|
||||
cx.parse_sess.env_depinfo.borrow_mut().insert((var, value));
|
||||
let e = match value {
|
||||
None => {
|
||||
cx.span_err(sp, &msg.as_str());
|
||||
return DummyResult::any(sp);
|
||||
}
|
||||
Ok(s) => cx.expr_str(sp, Symbol::intern(&s)),
|
||||
Some(value) => cx.expr_str(sp, value),
|
||||
};
|
||||
MacEager::expr(e)
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#![feature(bool_to_option)]
|
||||
#![feature(crate_visibility_modifier)]
|
||||
#![feature(decl_macro)]
|
||||
#![feature(inner_deref)]
|
||||
#![feature(nll)]
|
||||
#![feature(or_patterns)]
|
||||
#![feature(proc_macro_internals)]
|
||||
|
|
|
@ -549,6 +549,22 @@ fn escape_dep_filename(filename: &FileName) -> String {
|
|||
filename.to_string().replace(" ", "\\ ")
|
||||
}
|
||||
|
||||
// Makefile comments only need escaping newlines and `\`.
|
||||
// The result can be unescaped by anything that can unescape `escape_default` and friends.
|
||||
fn escape_dep_env(symbol: Symbol) -> String {
|
||||
let s = symbol.as_str();
|
||||
let mut escaped = String::with_capacity(s.len());
|
||||
for c in s.chars() {
|
||||
match c {
|
||||
'\n' => escaped.push_str(r"\n"),
|
||||
'\r' => escaped.push_str(r"\r"),
|
||||
'\\' => escaped.push_str(r"\\"),
|
||||
_ => escaped.push(c),
|
||||
}
|
||||
}
|
||||
escaped
|
||||
}
|
||||
|
||||
fn write_out_deps(
|
||||
sess: &Session,
|
||||
boxed_resolver: &Steal<Rc<RefCell<BoxedResolver>>>,
|
||||
|
@ -604,6 +620,25 @@ fn write_out_deps(
|
|||
for path in files {
|
||||
writeln!(file, "{}:", path)?;
|
||||
}
|
||||
|
||||
// Emit special comments with information about accessed environment variables.
|
||||
let env_depinfo = sess.parse_sess.env_depinfo.borrow();
|
||||
if !env_depinfo.is_empty() {
|
||||
let mut envs: Vec<_> = env_depinfo
|
||||
.iter()
|
||||
.map(|(k, v)| (escape_dep_env(*k), v.map(escape_dep_env)))
|
||||
.collect();
|
||||
envs.sort_unstable();
|
||||
writeln!(file)?;
|
||||
for (k, v) in envs {
|
||||
write!(file, "# env-dep:{}", k)?;
|
||||
if let Some(v) = v {
|
||||
write!(file, "={}", v)?;
|
||||
}
|
||||
writeln!(file)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})();
|
||||
|
||||
|
|
|
@ -135,6 +135,8 @@ pub struct ParseSess {
|
|||
pub symbol_gallery: SymbolGallery,
|
||||
/// The parser has reached `Eof` due to an unclosed brace. Used to silence unnecessary errors.
|
||||
pub reached_eof: Lock<bool>,
|
||||
/// Environment variables accessed during the build and their values when they exist.
|
||||
pub env_depinfo: Lock<FxHashSet<(Symbol, Option<Symbol>)>>,
|
||||
}
|
||||
|
||||
impl ParseSess {
|
||||
|
@ -160,6 +162,7 @@ impl ParseSess {
|
|||
gated_spans: GatedSpans::default(),
|
||||
symbol_gallery: SymbolGallery::default(),
|
||||
reached_eof: Lock::new(false),
|
||||
env_depinfo: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
8
src/test/run-make/env-dep-info/Makefile
Normal file
8
src/test/run-make/env-dep-info/Makefile
Normal file
|
@ -0,0 +1,8 @@
|
|||
-include ../../run-make-fulldeps/tools.mk
|
||||
|
||||
all:
|
||||
EXISTING_ENV=1 EXISTING_OPT_ENV=1 $(RUSTC) --emit dep-info main.rs
|
||||
$(CGREP) "# env-dep:EXISTING_ENV=1" < $(TMPDIR)/main.d
|
||||
$(CGREP) "# env-dep:EXISTING_OPT_ENV=1" < $(TMPDIR)/main.d
|
||||
$(CGREP) "# env-dep:NONEXISTENT_OPT_ENV" < $(TMPDIR)/main.d
|
||||
$(CGREP) "# env-dep:ESCAPE\nESCAPE\\" < $(TMPDIR)/main.d
|
6
src/test/run-make/env-dep-info/main.rs
Normal file
6
src/test/run-make/env-dep-info/main.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
fn main() {
|
||||
env!("EXISTING_ENV");
|
||||
option_env!("EXISTING_OPT_ENV");
|
||||
option_env!("NONEXISTENT_OPT_ENV");
|
||||
option_env!("ESCAPE\nESCAPE\\");
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue