Repository navigation
Unquoted [target.<triple>] config is ignored for triples that contain a dot #17529
Description
Problem
Minimized Claude report:
Since nightly-2026-09-24, Cargo ignores [target.<triple>] config when the triple contains a dot and the key is written without quotes. The environment variable form is also ignored. Examples of such triples are thumbv8m.main-none-eabihf and thumbv8m.main-none-eabi.
All keys in the table are affected. I confirmed rustflags and runner. Cargo builds without the flags and runs the binary directly instead of through the runner.
The quoted key [target."thumbv8m.main-none-eabihf"] now works. It did not work before, see #15901. Triples without a dot, such as thumbv7em-none-eabihf, are not affected.
Unquoted headers for these triples are common in embedded projects. Before this change they were the only form that worked.
Version
- Last good: cargo 1.100.0-nightly (495c385d0 2026-09-16), rustc 6bb1652a0 (nightly-2026-09-23).
- First bad: cargo 1.100.0-nightly (98a09e7e7 2026-09-21), rustc 6eeff9a52 (nightly-2026-09-24).
Steps
rustup toolchain install nightly-2026-09-24 --profile minimal -t thumbv8m.main-none-eabihf
cargo new --lib repro && cd repro
echo '#![no_std]' > src/lib.rs
mkdir .cargo
cat > .cargo/config.toml <<'EOF'
[target.thumbv8m.main-none-eabihf]
rustflags = ["-Cbogus"]
EOF
cargo +nightly-2026-09-24 check --target thumbv8m.main-none-eabihfExpected: error: unknown codegen option: `bogus` .
Actual: the build succeeds. The flag is not passed.
Results for -Cbogus in each form:
| Config form | nightly-2026-09-23 | nightly-2026-09-24 |
|---|---|---|
[target.thumbv8m.main-none-eabihf] |
applied | ignored |
[target."thumbv8m.main-none-eabihf"] |
ignored | applied |
CARGO_TARGET_THUMBV8M_MAIN_NONE_EABIHF_RUSTFLAGS |
applied | ignored |
Possible cause
#17490 "refactor(context): Split config keys in code, not at runtime" is in the range. It changes the lookup from format!("target.{}.{}", target, key), which was split on dots at runtime, to ["target", target, key]. See the rustflags lookup and the target table lookup. TOML parses the unquoted header [target.thumbv8m.main-none-eabihf] as the nested tables target.thumbv8m."main-none-eabihf". The old runtime split matched that nesting. The new lookup does not. The environment variable form breaks in the same lookup, because the env name is now built from the single part thumbv8m.main-none-eabihf and keeps the dot.
Possible Solution(s)
Use the quoted key when it is set in config. Otherwise use the nested form, split on dots. rustflags_from_target reads the flags from the same TargetConfig, so both lookups agree. ConfigKey::push maps . to _ in the env name, so the environment variable works with either form.
--- a/src/compiler/build_context/target_info.rs
+++ b/src/compiler/build_context/target_info.rs
@@ -14,3 +14,3 @@ use crate::compiler::CrateType;
use crate::compiler::apply_env_config;
-use crate::context::{GlobalContext, StringList, TargetConfig};
+use crate::context::{GlobalContext, TargetConfig};
use crate::util::interning::InternedString;
@@ -763,9 +763,2 @@ enum Flags {
impl Flags {
- fn as_key(self) -> &'static str {
- match self {
- Flags::Rust => "rustflags",
- Flags::Rustdoc => "rustdocflags",
- }
- }
-
fn as_env(self) -> &'static str {
@@ -874,4 +867,9 @@ fn rustflags_from_target(
};
- if let Some(args) = gctx.get::<Option<StringList>>(["target", target, flag.as_key()])? {
- rustflags.extend(args.as_slice().iter().cloned());
+ let target_config = gctx.target_cfg_triple(target)?;
+ let args = match flag {
+ Flags::Rust => target_config.rustflags,
+ Flags::Rustdoc => target_config.rustdocflags,
+ };
+ if let Some(args) = args {
+ rustflags.extend(args.val.as_slice().iter().cloned());
}
--- a/src/context/key.rs
+++ b/src/context/key.rs
@@ -67,3 +67,3 @@ impl ConfigKey {
pub fn push(&mut self, name: &str) {
- let env = name.replace("-", "_").to_uppercase();
+ let env = name.replace(['-', '.'], "_").to_uppercase();
self._push(&env, name);
--- a/src/context/target.rs
+++ b/src/context/target.rs
@@ -115,3 +115,10 @@ pub(super) fn load_host_triple(gctx: &GlobalContext, triple: &str) -> CargoResul
pub(super) fn load_target_triple(gctx: &GlobalContext, triple: &str) -> CargoResult<TargetConfig> {
- load_config_table(gctx, &["target", triple])
+ // TOML parses the unquoted `[target.thumbv8m.main-none-eabihf]` as nested
+ // tables. Use the quoted key when it is set, else the nested form.
+ let quoted = ["target", triple];
+ if gctx.get_cv(&ConfigKey::from_parts(quoted))?.is_some() {
+ return load_config_table(gctx, "ed);
+ }
+ let nested: Vec<&str> = std::iter::once("target").chain(triple.split('.')).collect();
+ load_config_table(gctx, &nested)
}I tested this on top of 98a09e7e7. All three forms in the table above apply -Cbogus. The environment variable also applies when a quoted table for the triple exists. An unquoted runner is used again. cargo test --test testsuite -- config:: rustflags:: passes. A regression test for the unquoted header and the environment variable is not included.
Notes
No response
Version
Activity
weihanglo commentedon Sep 28, 2026
I am surprised that
[target.thumbv8m.main-none-eabihf]worked, and it was actually mentioned as a workaround in #15901 (comment).epage commentedon Sep 29, 2026
It appears that the bug that caused
target."thumbv8m.main-none-eabihf"to not work is the bug that causedtarget.thumbv8m.main-none-eabihfto work: we looked up the the target by dotted key and weren't properly escaping the., so we looked an extra level deeper inside the configcargo/src/context/target.rs
Lines 114 to 117 in 523e530
At #15901 (comment), I commented
#15901 (comment) noted that these keys/targets shouldn't exist and that #17490 is incomplete
epage commentedon Sep 29, 2026
I'm assuming this could be worked around by specifying both keys
weihanglo commentedon Sep 29, 2026
To me, I probably care less about a unintended unquoted TOML but more the env case. The latter should either continue working, or we provide some alternative (-Zadvanced-env?). However, I wonder if we can give any future-incompat warning for the unquoted TOML case at least
epage commentedon Sep 29, 2026
For env, I wonder if we could just add another
replaceincargo/src/context/key.rs
Line 68 in 523e530
.shouldn't be in existing keys, so it shouldn't cause any issues to replace it with_.fix(config): Allow dotted tuple workaround to work for now
fix(config): Proper dotted tuple support with legacy fallback (rust-l…
fix(config): Allow dotted tuple workaround to work for now
epage commentedon Sep 29, 2026
#17537 backports to Rust 1.100.0 (beta)
Add workaround for rust-lang/cargo#17529
Fix workaround for rust-lang/cargo#17529