Fix masquerade_as_nightly_cargo in work threads

Previously, since `ENABLE_NIGHTLY_FEATURES` and
`NIGHTLY_FEATURES_ENABLED` were thread locals, reading them in any other
thread would always say nightly features were disabled. Now, they are
tied to the `Context` itself, so it is both more clear how the variables
are being set and fixes the behavior within work threads.

Note that `Context` is not thread-safe, so this passes a boolean through
to `BuildOutput::parse`.
This commit is contained in:
Joshua Nelson 2021-02-18 13:46:31 -05:00
parent e56417c8c8
commit 4b096beaed
14 changed files with 101 additions and 58 deletions

View file

@ -55,9 +55,8 @@ proptest! {
fn prop_minimum_version_errors_the_same(
PrettyPrintRegistry(input) in registry_strategy(50, 20, 60)
) {
enable_nightly_features();
let mut config = Config::default().unwrap();
enable_nightly_features(&mut config);
config
.configure(
1,
@ -553,11 +552,6 @@ fn test_resolving_maximum_version_with_transitive_deps() {
#[test]
fn test_resolving_minimum_version_with_transitive_deps() {
enable_nightly_features(); // -Z minimal-versions
// When the minimal-versions config option is specified then the lowest
// possible version of a package should be selected. "util 1.0.0" can't be
// selected because of the requirements of "bar", so the minimum version
// must be 1.1.1.
let reg = registry(vec![
pkg!(("util", "1.2.2")),
pkg!(("util", "1.0.0")),
@ -567,6 +561,12 @@ fn test_resolving_minimum_version_with_transitive_deps() {
]);
let mut config = Config::default().unwrap();
// -Z minimal-versions
// When the minimal-versions config option is specified then the lowest
// possible version of a package should be selected. "util 1.0.0" can't be
// selected because of the requirements of "bar", so the minimum version
// must be 1.1.1.
enable_nightly_features(&mut config);
config
.configure(
1,

View file

@ -48,7 +48,7 @@ Available unstable (nightly-only) flags:
Run with 'cargo -Z [FLAG] [SUBCOMMAND]'"
);
if !features::nightly_features_allowed() {
if !features::nightly_features_allowed(config) {
drop_println!(
config,
"\nUnstable flags are only available on the nightly channel \

View file

@ -16,7 +16,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
if !(unstable.credential_process || unstable.unstable_options) {
const SEE: &str = "See https://github.com/rust-lang/cargo/issues/8933 for more \
information about the `cargo logout` command.";
if features::nightly_features_allowed() {
if features::nightly_features_allowed(config) {
return Err(format_err!(
"the `cargo logout` command is unstable, pass `-Z unstable-options` to enable it\n\
{}",

View file

@ -22,7 +22,6 @@ fn main() {
pretty_env_logger::init_custom_env("CARGO_LOG");
#[cfg(not(feature = "pretty-env-logger"))]
env_logger::init_from_env("CARGO_LOG");
cargo::core::maybe_allow_nightly_features();
let mut config = match Config::default() {
Ok(cfg) => cfg,
@ -31,8 +30,9 @@ fn main() {
cargo::exit_with_error(e.into(), &mut shell)
}
};
cargo::core::maybe_allow_nightly_features(&mut config);
let result = match cargo::ops::fix_maybe_exec_rustc() {
let result = match cargo::ops::fix_maybe_exec_rustc(&config) {
Ok(true) => Ok(()),
Ok(false) => {
let _token = cargo::util::job::setup();

View file

@ -295,6 +295,7 @@ fn build_work(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Job> {
paths::create_dir_all(&script_out_dir)?;
let extra_link_arg = cx.bcx.config.cli_unstable().extra_link_arg;
let nightly_features_allowed = nightly_features_allowed(cx.bcx.config);
// Prepare the unit of "dirty work" which will actually run the custom build
// command.
@ -369,7 +370,7 @@ fn build_work(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Job> {
},
true,
)
.chain_err(|| format!("failed to run custom build command for `{}`", pkg_name));
.chain_err(|| format!("failed to run custom build command for `{}`", pkg_descr));
if let Err(error) = output {
insert_warnings_in_build_outputs(
@ -403,6 +404,7 @@ fn build_work(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Job> {
&script_out_dir,
&script_out_dir,
extra_link_arg,
nightly_features_allowed,
)?;
if json_messages {
@ -429,6 +431,7 @@ fn build_work(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Job> {
&prev_script_out_dir,
&script_out_dir,
extra_link_arg,
nightly_features_allowed,
)?,
};
@ -480,6 +483,7 @@ impl BuildOutput {
script_out_dir_when_generated: &Path,
script_out_dir: &Path,
extra_link_arg: bool,
nightly_features_allowed: bool,
) -> CargoResult<BuildOutput> {
let contents = paths::read_bytes(path)?;
BuildOutput::parse(
@ -489,6 +493,7 @@ impl BuildOutput {
script_out_dir_when_generated,
script_out_dir,
extra_link_arg,
nightly_features_allowed,
)
}
@ -501,6 +506,7 @@ impl BuildOutput {
script_out_dir_when_generated: &Path,
script_out_dir: &Path,
extra_link_arg: bool,
nightly_features_allowed: bool,
) -> CargoResult<BuildOutput> {
let mut library_paths = Vec::new();
let mut library_links = Vec::new();
@ -582,7 +588,7 @@ impl BuildOutput {
// to set RUSTC_BOOTSTRAP.
// If this is a nightly build, setting RUSTC_BOOTSTRAP wouldn't affect the
// behavior, so still only give a warning.
if nightly_features_allowed() {
if nightly_features_allowed {
warnings.push(format!("Cannot set `RUSTC_BOOTSTRAP={}` from {}.\n\
note: Crates cannot set `RUSTC_BOOTSTRAP` themselves, as doing so would subvert the stability guarantees of Rust for your project.\n\
help: See https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-env for details.",
@ -850,6 +856,7 @@ fn prev_build_output(cx: &mut Context<'_, '_>, unit: &Unit) -> (Option<BuildOutp
.unwrap_or_else(|_| script_out_dir.clone());
let extra_link_arg = cx.bcx.config.cli_unstable().extra_link_arg;
let nightly_features_allowed = nightly_features_allowed(cx.bcx.config);
(
BuildOutput::parse_file(
@ -859,6 +866,7 @@ fn prev_build_output(cx: &mut Context<'_, '_>, unit: &Unit) -> (Option<BuildOutp
&prev_script_out_dir,
&script_out_dir,
extra_link_arg,
nightly_features_allowed,
)
.ok(),
prev_script_out_dir,

View file

@ -700,8 +700,8 @@ fn add_error_format_and_color(cx: &Context<'_, '_>, cmd: &mut ProcessBuilder, pi
}
cmd.arg(json);
if nightly_features_allowed() {
let config = cx.bcx.config;
let config = cx.bcx.config;
if nightly_features_allowed(config) {
match (
config.cli_unstable().terminal_width,
config.shell().err_width().diagnostic_terminal_width(),

View file

@ -4,6 +4,7 @@ use crate::core::profiles::{Profile, UnitFor};
use crate::core::{nightly_features_allowed, PackageId, Target};
use crate::util::interning::InternedString;
use crate::util::CargoResult;
use crate::Config;
use std::collections::HashMap;
use std::io::Write;
@ -62,8 +63,12 @@ struct SerializedUnitDep {
// internal detail that is mostly used for building the graph.
}
pub fn emit_serialized_unit_graph(root_units: &[Unit], unit_graph: &UnitGraph) -> CargoResult<()> {
let is_nightly = nightly_features_allowed();
pub fn emit_serialized_unit_graph(
root_units: &[Unit],
unit_graph: &UnitGraph,
config: &Config,
) -> CargoResult<()> {
let is_nightly = nightly_features_allowed(config);
let mut units: Vec<(&Unit, &Vec<UnitDep>)> = unit_graph.iter().collect();
units.sort_unstable();
// Create a map for quick lookup for dependencies.

View file

@ -102,6 +102,7 @@ use serde::{Deserialize, Serialize};
use crate::util::errors::CargoResult;
use crate::util::{indented_lines, ProcessBuilder};
use crate::Config;
pub const SEE_CHANNELS: &str =
"See https://doc.rust-lang.org/book/appendix-07-nightly-rust.html for more information \
@ -406,16 +407,25 @@ pub struct Feature {
}
impl Features {
pub fn new(features: &[String], warnings: &mut Vec<String>) -> CargoResult<Features> {
pub fn new(
features: &[String],
config: &Config,
warnings: &mut Vec<String>,
) -> CargoResult<Features> {
let mut ret = Features::default();
for feature in features {
ret.add(feature, warnings)?;
ret.add(feature, config, warnings)?;
ret.activated.push(feature.to_string());
}
Ok(ret)
}
fn add(&mut self, feature_name: &str, warnings: &mut Vec<String>) -> CargoResult<()> {
fn add(
&mut self,
feature_name: &str,
config: &Config,
warnings: &mut Vec<String>,
) -> CargoResult<()> {
let (slot, feature) = match self.status(feature_name) {
Some(p) => p,
None => bail!("unknown cargo feature `{}`", feature_name),
@ -453,7 +463,7 @@ impl Features {
);
warnings.push(warning);
}
Status::Unstable if !nightly_features_allowed() => bail!(
Status::Unstable if !nightly_features_allowed(config) => bail!(
"the cargo feature `{}` requires a nightly version of \
Cargo, but this is the `{}` channel\n\
{}\n{}",
@ -488,7 +498,9 @@ impl Features {
let feature = feature.name.replace("_", "-");
let mut msg = format!("feature `{}` is required", feature);
if nightly_features_allowed() {
// TODO
let channel = channel();
if channel == "nightly" || channel == "dev" {
let s = format!(
"\n\nconsider adding `cargo-features = [\"{0}\"]` \
to the manifest",
@ -602,8 +614,12 @@ where
}
impl CliUnstable {
pub fn parse(&mut self, flags: &[String]) -> CargoResult<Vec<String>> {
if !flags.is_empty() && !nightly_features_allowed() {
pub fn parse(
&mut self,
flags: &[String],
nightly_features_allowed: bool,
) -> CargoResult<Vec<String>> {
if !flags.is_empty() && !nightly_features_allowed {
bail!(
"the `-Z` flag is only accepted on the nightly channel of Cargo, \
but this is the `{}` channel\n\
@ -759,7 +775,9 @@ impl CliUnstable {
information about the `{}` flag.",
issue, flag
);
if nightly_features_allowed() {
// NOTE: a `config` isn't available here, check the channel directly
let channel = channel();
if channel == "nightly" || channel == "dev" {
bail!(
"the `{}` flag is unstable, pass `-Z unstable-options` to enable it\n\
{}",
@ -773,7 +791,7 @@ impl CliUnstable {
{}\n\
{}",
flag,
channel(),
channel,
SEE_CHANNELS,
see
);
@ -817,12 +835,12 @@ thread_local!(
/// - this is an `#[test]` that called `enable_nightly_features`
/// - this is a integration test that uses `ProcessBuilder`
/// that called `masquerade_as_nightly_cargo`
pub fn nightly_features_allowed() -> bool {
if ENABLE_NIGHTLY_FEATURES.with(|c| c.get()) {
pub fn nightly_features_allowed(config: &Config) -> bool {
if config.enable_nightly_features {
return true;
}
match &channel()[..] {
"nightly" | "dev" => true, //NIGHTLY_FEATURES_ALLOWED.with(|c| c.get()),
"nightly" | "dev" => config.maybe_allow_nightly_features,
_ => false,
}
}
@ -831,13 +849,14 @@ pub fn nightly_features_allowed() -> bool {
/// development channel is nightly or dev.
///
/// Used by cargo main to ensure that a cargo build from source has nightly features
pub fn maybe_allow_nightly_features() {
NIGHTLY_FEATURES_ALLOWED.with(|c| c.set(true));
pub fn maybe_allow_nightly_features(config: &mut Config) {
config.maybe_allow_nightly_features = true;
}
/// Forcibly enables nightly features for this thread.
///
/// Used by tests to allow the use of nightly features.
pub fn enable_nightly_features() {
ENABLE_NIGHTLY_FEATURES.with(|c| c.set(true));
/// NOTE: this should be called before `configure()`. Consider using `ConfigBuilder::enable_nightly_features` instead.
pub fn enable_nightly_features(config: &mut Config) {
config.enable_nightly_features = true;
}

View file

@ -287,7 +287,7 @@ pub fn compile_ws<'a>(
let interner = UnitInterner::new();
let bcx = create_bcx(ws, options, &interner)?;
if options.build_config.unit_graph {
unit_graph::emit_serialized_unit_graph(&bcx.roots, &bcx.unit_graph)?;
unit_graph::emit_serialized_unit_graph(&bcx.roots, &bcx.unit_graph, ws.config())?;
return Compilation::new(&bcx);
}
let _p = profile::start("compiling");

View file

@ -201,7 +201,7 @@ fn check_version_control(config: &Config, opts: &FixOptions) -> CargoResult<()>
/// `true` if in `fix` proxy mode, and the fix was complete without any
/// warnings or errors. If there are warnings or errors, this does not return,
/// and the process exits with the corresponding `rustc` exit code.
pub fn fix_maybe_exec_rustc() -> CargoResult<bool> {
pub fn fix_maybe_exec_rustc(config: &Config) -> CargoResult<bool> {
let lock_addr = match env::var(FIX_ENV) {
Ok(s) => s,
Err(_) => return Ok(false),
@ -216,7 +216,7 @@ pub fn fix_maybe_exec_rustc() -> CargoResult<bool> {
let rustc = util::process(&args.rustc).wrapped(workspace_rustc.as_ref());
trace!("start rustfixing {:?}", args.file);
let fixes = rustfix_crate(&lock_addr, &rustc, &args.file, &args)?;
let fixes = rustfix_crate(&lock_addr, &rustc, &args.file, &args, config)?;
// Ok now we have our final goal of testing out the changes that we applied.
// If these changes went awry and actually started to cause the crate to
@ -296,8 +296,9 @@ fn rustfix_crate(
rustc: &ProcessBuilder,
filename: &Path,
args: &FixArgs,
config: &Config,
) -> Result<FixedCrate, Error> {
args.check_edition_and_send_status()?;
args.check_edition_and_send_status(config)?;
// First up, we want to make sure that each crate is only checked by one
// process at a time. If two invocations concurrently check a crate then
@ -679,7 +680,7 @@ impl FixArgs {
/// Validates the edition, and sends a message indicating what is being
/// done.
fn check_edition_and_send_status(&self) -> CargoResult<()> {
fn check_edition_and_send_status(&self, config: &Config) -> CargoResult<()> {
let to_edition = match self.prepare_for_edition {
Some(s) => s,
None => {
@ -699,7 +700,7 @@ impl FixArgs {
// Unfortunately this results in a pretty poor error message when
// multiple jobs run in parallel (the error appears multiple
// times). Hopefully this doesn't happen often in practice.
if !to_edition.is_stable() && !nightly_features_allowed() {
if !to_edition.is_stable() && !nightly_features_allowed(config) {
bail!(
"cannot migrate {} to edition {to_edition}\n\
Edition {to_edition} is unstable and not allowed in this release, \

View file

@ -184,6 +184,8 @@ pub struct Config {
doc_extern_map: LazyCell<RustdocExternMap>,
progress_config: ProgressConfig,
env_config: LazyCell<EnvConfig>,
pub(crate) enable_nightly_features: bool,
pub(crate) maybe_allow_nightly_features: bool,
}
impl Config {
@ -268,6 +270,8 @@ impl Config {
doc_extern_map: LazyCell::new(),
progress_config: ProgressConfig::default(),
env_config: LazyCell::new(),
enable_nightly_features: false,
maybe_allow_nightly_features: false,
}
}
@ -755,7 +759,11 @@ impl Config {
unstable_flags: &[String],
cli_config: &[String],
) -> CargoResult<()> {
for warning in self.unstable_flags.parse(unstable_flags)? {
let nightly_features_allowed = nightly_features_allowed(&self);
for warning in self
.unstable_flags
.parse(unstable_flags, nightly_features_allowed)?
{
self.shell().warn(warning)?;
}
if !unstable_flags.is_empty() {
@ -821,7 +829,7 @@ impl Config {
fn load_unstable_flags_from_config(&mut self) -> CargoResult<()> {
// If nightly features are enabled, allow setting Z-flags from config
// using the `unstable` table. Ignore that block otherwise.
if nightly_features_allowed() {
if nightly_features_allowed(self) {
self.unstable_flags = self
.get::<Option<CliUnstable>>("unstable")?
.unwrap_or_default();
@ -830,7 +838,7 @@ impl Config {
// allows the CLI to override config files for both enabling
// and disabling, and doing it up top allows CLI Zflags to
// control config parsing behavior.
self.unstable_flags.parse(unstable_flags_cli)?;
self.unstable_flags.parse(unstable_flags_cli, true)?;
}
}

View file

@ -1037,7 +1037,7 @@ impl TomlManifest {
// Parse features first so they will be available when parsing other parts of the TOML.
let empty = Vec::new();
let cargo_features = me.cargo_features.as_ref().unwrap_or(&empty);
let features = Features::new(cargo_features, &mut warnings)?;
let features = Features::new(cargo_features, config, &mut warnings)?;
let project = me.project.as_ref().or_else(|| me.package.as_ref());
let project = project.ok_or_else(|| anyhow!("no `package` section found"))?;
@ -1076,7 +1076,7 @@ impl TomlManifest {
let mut msg =
"`rust-version` is not supported on this version of Cargo and will be ignored"
.to_string();
if nightly_features_allowed() {
if nightly_features_allowed(config) {
msg.push_str(
"\n\n\
consider adding `cargo-features = [\"rust-version\"]` to the manifest",
@ -1432,7 +1432,7 @@ impl TomlManifest {
let mut deps = Vec::new();
let empty = Vec::new();
let cargo_features = me.cargo_features.as_ref().unwrap_or(&empty);
let features = Features::new(cargo_features, &mut warnings)?;
let features = Features::new(cargo_features, config, &mut warnings)?;
let (replace, patch) = {
let mut cx = Context {

View file

@ -20,6 +20,7 @@ pub struct ConfigBuilder {
unstable: Vec<String>,
config_args: Vec<String>,
cwd: Option<PathBuf>,
enable_nightly_features: bool,
}
impl ConfigBuilder {
@ -29,6 +30,7 @@ impl ConfigBuilder {
unstable: Vec::new(),
config_args: Vec::new(),
cwd: None,
enable_nightly_features: false,
}
}
@ -44,6 +46,12 @@ impl ConfigBuilder {
self
}
/// Unconditionaly enable nightly features, even on stable channels.
pub fn enable_nightly_features(&mut self) -> &mut Self {
self.enable_nightly_features = true;
self
}
/// Passes a `--config` flag.
pub fn config_arg(&mut self, arg: impl Into<String>) -> &mut Self {
if !self.unstable.iter().any(|s| s == "unstable-options") {
@ -67,15 +75,14 @@ impl ConfigBuilder {
/// Creates the `Config`, returning a Result.
pub fn build_err(&self) -> CargoResult<Config> {
if !self.unstable.is_empty() {
// This is unfortunately global. Some day that should be fixed.
enable_nightly_features();
}
let output = Box::new(fs::File::create(paths::root().join("shell.out")).unwrap());
let shell = Shell::from_write(output);
let cwd = self.cwd.clone().unwrap_or_else(|| paths::root());
let homedir = paths::home();
let mut config = Config::new(shell, cwd, homedir);
if self.enable_nightly_features || !self.unstable.is_empty() {
enable_nightly_features(&mut config);
}
config.set_env(self.env.clone());
config.set_search_stop_path(paths::root());
config.configure(
@ -1095,40 +1102,37 @@ Caused by:
/// Assert that unstable options can be configured with the `unstable` table in
/// cargo config files
fn unstable_table_notation() {
cargo::core::enable_nightly_features();
write_config(
"\
[unstable]
print-im-a-teapot = true
",
);
let config = ConfigBuilder::new().build();
let config = ConfigBuilder::new().enable_nightly_features().build();
assert_eq!(config.cli_unstable().print_im_a_teapot, true);
}
#[cargo_test]
/// Assert that dotted notation works for configuring unstable options
fn unstable_dotted_notation() {
cargo::core::enable_nightly_features();
write_config(
"\
unstable.print-im-a-teapot = true
",
);
let config = ConfigBuilder::new().build();
let config = ConfigBuilder::new().enable_nightly_features().build();
assert_eq!(config.cli_unstable().print_im_a_teapot, true);
}
#[cargo_test]
/// Assert that Zflags on the CLI take precedence over those from config
fn unstable_cli_precedence() {
cargo::core::enable_nightly_features();
write_config(
"\
unstable.print-im-a-teapot = true
",
);
let config = ConfigBuilder::new().build();
let config = ConfigBuilder::new().enable_nightly_features().build();
assert_eq!(config.cli_unstable().print_im_a_teapot, true);
let config = ConfigBuilder::new()

View file

@ -343,12 +343,10 @@ fn named_config_profile() {
// middle exists in Cargo.toml, the others in .cargo/config
use super::config::ConfigBuilder;
use cargo::core::compiler::CompileMode;
use cargo::core::enable_nightly_features;
use cargo::core::profiles::{Profiles, UnitFor};
use cargo::core::{PackageId, Workspace};
use cargo::util::interning::InternedString;
use std::fs;
enable_nightly_features();
paths::root().join(".cargo").mkdir_p();
fs::write(
paths::root().join(".cargo/config"),
@ -394,7 +392,7 @@ fn named_config_profile() {
"#,
)
.unwrap();
let config = ConfigBuilder::new().build();
let config = ConfigBuilder::new().enable_nightly_features().build();
let profile_name = InternedString::new("foo");
let ws = Workspace::new(&paths::root().join("Cargo.toml"), &config).unwrap();
let profiles = Profiles::new(&ws, profile_name).unwrap();