Skip to content

fix(toml): Remove workaround for rustc frontmatter support #15570

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

Merged
merged 1 commit into from
May 22, 2025
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
18 changes: 18 additions & 0 deletions src/cargo/core/compiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,15 @@ fn prepare_rustc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult
.compilation
.rustc_process(unit, is_primary, is_workspace)?;
build_base_args(build_runner, &mut base, unit)?;
if unit.pkg.manifest().is_embedded() {
if !gctx.cli_unstable().script {
anyhow::bail!(
"parsing `{}` requires `-Zscript`",
unit.pkg.manifest_path().display()
);
}
base.arg("-Z").arg("crate-attr=feature(frontmatter)");
}

base.inherit_jobserver(&build_runner.jobserver);
build_deps_args(&mut base, build_runner, unit)?;
Expand Down Expand Up @@ -774,6 +783,15 @@ fn prepare_rustdoc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResu
let bcx = build_runner.bcx;
// script_metadata is not needed here, it is only for tests.
let mut rustdoc = build_runner.compilation.rustdoc_process(unit, None)?;
if unit.pkg.manifest().is_embedded() {
if !bcx.gctx.cli_unstable().script {
anyhow::bail!(
"parsing `{}` requires `-Zscript`",
unit.pkg.manifest_path().display()
);
}
rustdoc.arg("-Z").arg("crate-attr=feature(frontmatter)");
}
rustdoc.inherit_jobserver(&build_runner.jobserver);
let crate_name = unit.target.crate_name();
rustdoc.arg("--crate-name").arg(&crate_name);
Expand Down
97 changes: 6 additions & 91 deletions src/cargo/util/toml/embedded.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
use anyhow::Context as _;

use cargo_util_schemas::manifest::PackageName;

use crate::util::restricted_names;
use crate::CargoResult;
use crate::GlobalContext;

pub(super) fn expand_manifest(
content: &str,
path: &std::path::Path,
gctx: &GlobalContext,
) -> CargoResult<String> {
pub(super) fn expand_manifest(content: &str) -> CargoResult<String> {
let source = ScriptSource::parse(content)?;
if let Some(frontmatter) = source.frontmatter() {
match source.info() {
Expand All @@ -24,74 +17,13 @@ pub(super) fn expand_manifest(
}
}

// HACK: until rustc has native support for this syntax, we have to remove it from the
// source file
use std::fmt::Write as _;
let hash = crate::util::hex::short_hash(&path.to_string_lossy());
let mut rel_path = std::path::PathBuf::new();
rel_path.push("target");
rel_path.push(&hash[0..2]);
rel_path.push(&hash[2..]);
let target_dir = gctx.home().join(rel_path);
let hacked_path = target_dir
.join(
path.file_name()
.expect("always a name for embedded manifests"),
)
.into_path_unlocked();
let mut hacked_source = String::new();
if let Some(shebang) = source.shebang() {
writeln!(hacked_source, "{shebang}")?;
}
writeln!(hacked_source)?; // open
for _ in 0..frontmatter.lines().count() {
writeln!(hacked_source)?;
}
writeln!(hacked_source)?; // close
writeln!(hacked_source, "{}", source.content())?;
if let Some(parent) = hacked_path.parent() {
cargo_util::paths::create_dir_all(parent)?;
}
cargo_util::paths::write_if_changed(&hacked_path, hacked_source)?;

let manifest = inject_bin_path(&frontmatter, &hacked_path)
.with_context(|| format!("failed to parse manifest at `{}`", path.display()))?;
let manifest = toml::to_string_pretty(&manifest)?;
Ok(manifest)
Ok(frontmatter.to_owned())
} else {
let frontmatter = "";
let manifest = inject_bin_path(frontmatter, path)
.with_context(|| format!("failed to parse manifest at `{}`", path.display()))?;
let manifest = toml::to_string_pretty(&manifest)?;
Ok(manifest)
Ok(frontmatter.to_owned())
}
}

/// HACK: Add a `[[bin]]` table to the `original_toml`
fn inject_bin_path(manifest: &str, path: &std::path::Path) -> CargoResult<toml::Table> {
let mut manifest: toml::Table = toml::from_str(&manifest)?;

let bin_path = path.to_string_lossy().into_owned();
let file_stem = path
.file_stem()
.ok_or_else(|| anyhow::format_err!("no file name"))?
.to_string_lossy();
let name = sanitize_name(file_stem.as_ref());
let bin_name = name.clone();

let mut bin = toml::Table::new();
bin.insert("name".to_owned(), toml::Value::String(bin_name));
bin.insert("path".to_owned(), toml::Value::String(bin_path));
manifest
.entry("bin")
.or_insert_with(|| Vec::<toml::Value>::new().into())
.as_array_mut()
.ok_or_else(|| anyhow::format_err!("`bin` must be an array"))?
.push(toml::Value::Table(bin));

Ok(manifest)
}

/// Ensure the package name matches the validation from `ops::cargo_new::check_name`
pub fn sanitize_name(name: &str) -> String {
let placeholder = if name.contains('_') {
Expand Down Expand Up @@ -584,25 +516,12 @@ fn main() {}

#[track_caller]
fn expand(source: &str) -> String {
let shell = crate::Shell::from_write(Box::new(Vec::new()));
let cwd = std::env::current_dir().unwrap();
let home = home::cargo_home_with_cwd(&cwd).unwrap();
let gctx = GlobalContext::new(shell, cwd, home);
expand_manifest(source, std::path::Path::new("/home/me/test.rs"), &gctx)
.unwrap_or_else(|err| panic!("{}", err))
expand_manifest(source).unwrap_or_else(|err| panic!("{}", err))
}

#[test]
fn expand_default() {
assert_data_eq!(
expand(r#"fn main() {}"#),
str![[r#"
[[bin]]
name = "test-"
path = "/home/me/test.rs"

"#]]
);
assert_data_eq!(expand(r#"fn main() {}"#), str![""]);
}

#[test]
Expand All @@ -617,12 +536,8 @@ fn main() {}
"#
),
str![[r#"
[[bin]]
name = "test-"
path = [..]

[dependencies]
time = "0.1.25"
time="0.1.25"

"#]]
);
Expand Down
38 changes: 32 additions & 6 deletions src/cargo/util/toml/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ fn read_toml_string(path: &Path, is_embedded: bool, gctx: &GlobalContext) -> Car
if !gctx.cli_unstable().script {
anyhow::bail!("parsing `{}` requires `-Zscript`", path.display());
}
contents = embedded::expand_manifest(&contents, path, gctx)?;
contents = embedded::expand_manifest(&contents)?;
}
Ok(contents)
}
Expand Down Expand Up @@ -368,8 +368,37 @@ fn normalize_toml(
original_package.autolib.or(auto_embedded),
warnings,
)?;
let original_toml_bin = if is_embedded {
let manifest_file_stem = manifest_file
.file_stem()
.expect("file name enforced previously");
let name = embedded::sanitize_name(manifest_file_stem.to_string_lossy().as_ref());
let manifest_file_name = manifest_file
.file_name()
.expect("file name enforced previously");
let path = PathBuf::from(manifest_file_name);
Cow::Owned(Some(vec![manifest::TomlBinTarget {
name: Some(name),
crate_type: None,
crate_type2: None,
path: Some(manifest::PathValue(path)),
filename: None,
test: None,
doctest: None,
bench: None,
doc: None,
doc_scrape_examples: None,
proc_macro: None,
proc_macro2: None,
harness: None,
required_features: None,
edition: None,
}]))
} else {
Cow::Borrowed(&original_toml.bin)
};
normalized_toml.bin = Some(targets::normalize_bins(
original_toml.bin.as_ref(),
original_toml_bin.as_ref().as_ref(),
package_root,
package_name,
edition,
Expand Down Expand Up @@ -1345,10 +1374,7 @@ pub fn to_real_manifest(
let invalid_fields = [
("`workspace`", original_toml.workspace.is_some()),
("`lib`", original_toml.lib.is_some()),
(
"`bin`",
original_toml.bin.as_ref().map(|b| b.len()).unwrap_or(0) != 1,
),
("`bin`", original_toml.bin.is_some()),
("`example`", original_toml.example.is_some()),
("`test`", original_toml.test.is_some()),
("`bench`", original_toml.bench.is_some()),
Expand Down
4 changes: 2 additions & 2 deletions tests/testsuite/fix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2542,7 +2542,7 @@ edition = "2021"
);
}

#[cargo_test]
#[cargo_test(nightly, reason = "-Zscript is unstable")]
fn migrate_removes_project_for_script() {
let p = project()
.file(
Expand Down Expand Up @@ -2576,7 +2576,7 @@ fn main() {
[MIGRATING] foo.rs from 2021 edition to 2024
[FIXED] foo.rs (1 fix)
[CHECKING] foo v0.0.0 ([ROOT]/foo/foo.rs)
[MIGRATING] [ROOT]/home/.cargo/target/[HASH]/foo.rs from 2021 edition to 2024
[MIGRATING] foo.rs from 2021 edition to 2024
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s

"#]])
Expand Down
2 changes: 1 addition & 1 deletion tests/testsuite/lockfile_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,7 @@ fn install_lock_file_path_must_present() {
.run();
}

#[cargo_test]
#[cargo_test(nightly, reason = "-Zscript is unstable")]
fn run_embed() {
let lockfile_path = "mylockfile/Cargo.lock";
let invalid_lockfile = "Cargo.lock";
Expand Down
4 changes: 2 additions & 2 deletions tests/testsuite/open_namespaces.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ fn explicit_bin_within_namespace() {
.run();
}

#[cargo_test]
#[cargo_test(nightly, reason = "-Zscript is unstable")]
#[cfg(unix)]
fn namespaced_script_name() {
let p = cargo_test_support::project()
Expand Down Expand Up @@ -314,7 +314,7 @@ fn main() {}
"bin"
],
"name": "foo::bar",
"src_path": "[ROOT]/home/.cargo/target/[HASH]/foo::bar.rs",
"src_path": "[ROOT]/foo/foo::bar.rs",
"test": true
}
],
Expand Down
Loading