Measure libcosmic again: 0 of 164 files to 8, and the blockers changed kind
Re-ran the libcosmic measurement now that generics, associated types, impl Trait in both positions, iterators, futures, closures, trait impls, macro expansion and the concurrency primitives have landed. Files that translate went from 0 of 164 to 8, with another 34 blocked only by a `mod` declaration, which is an artifact of testing files one at a time. The kind of blocker matters more than the count. Generics (66 first time), trait objects (20) and async no longer appear at all. What replaced them is the iced API: new (11), unique, spacing, Radians, .cosmic, .activate, .scroller_width. That is the dependency closure rather than a feature list. Two general things fell out. #[cfg(target_env)] joins the host predicates, read from the triple this binary was built for. And `--cfg <name>` now declares a custom flag as rustc's does: libcosmic's build script sets wayland_platform and xdg_portal, and since we do not run build scripts the caller declares them rather than us guessing. An undeclared flag is still refused, which tests/multifile.rs pins in both directions. This does not mean libcosmic is close, and DESIGN.md says so. Even if every file lowered the result would not run, because libcosmic's behaviour is iced's and iced is not transpiled -- at which point "files produce a translation" measures nothing. The narrower honest reading is that the language is no longer what stops it. 52 differential cases and 6 integration tests, all green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ba4c4ab parent: 1b93db2 modified
DESIGN.md +29 -0 | @@ -388,6 +388,35 @@ with 66 generic-parameter blockers, 20 trait objects, 51 `async` uses, 235 | ||
| 388 | 388 | not features the crate happens to use; they are its architecture. `libcosmic` |
| 389 | 389 | is a north star, not a next step. |
| 390 | 390 | |
| 391 | +### Measured again, after the language features | |
| 392 | + | |
| 393 | +Re-run once generics, associated types, `impl Trait` in both positions, | |
| 394 | +iterators, futures, closures, trait impls, macro expansion and the | |
| 395 | +concurrency primitives had landed: | |
| 396 | + | |
| 397 | +| | first attempt | now | | |
| 398 | +|---|---|---| | |
| 399 | +| files that translate | **0 of 164** | **8 of 164** | | |
| 400 | +| blocked only by `mod` (an artifact of testing files singly) | — | 34 | | |
| 401 | +| top blocker | generics (66) | calls into dependencies (36) | | |
| 402 | + | |
| 403 | +**The language blockers are essentially gone.** Generics, trait objects and | |
| 404 | +`async` no longer appear at all. What replaced them is the `iced` API: | |
| 405 | +`new` (11), `unique`, `spacing`, `Radians`, `.cosmic`, `.activate`, | |
| 406 | +`.scroller_width`. That is the dependency closure, not a feature list. | |
| 407 | + | |
| 408 | +Two smaller things fell out and are general: `#[cfg(target_env)]` joins the | |
| 409 | +host predicates, and `--cfg <name>` now declares a custom flag the way | |
| 410 | +rustc's does. `libcosmic`'s build script sets `wayland_platform` and | |
| 411 | +`xdg_portal`; we do not run build scripts, so the caller declares them rather | |
| 412 | +than us guessing. An undeclared flag is still refused. | |
| 413 | + | |
| 414 | +**This does not mean libcosmic is close.** Even if every file lowered, the | |
| 415 | +result would not run: libcosmic's behaviour *is* `iced`'s, and `iced` is not | |
| 416 | +transpiled. At that point "files produce a translation" would measure nothing | |
| 417 | +at all. The honest reading is narrower and still worth something — the | |
| 418 | +*language* is no longer what stops it. | |
| 419 | + | |
| 391 | 420 | ### The blocker survey, and what it says about roadmaps |
| 392 | 421 | |
| 393 | 422 | 400 crates from the local registry (under 4,000 lines each), all their module |
| @@ -388,6 +388,35 @@ with 66 generic-parameter blockers, 20 trait objects, 51 `async` uses, 235 | |||
| 388 | not features the crate happens to use; they are its architecture. `libcosmic` | 388 | not features the crate happens to use; they are its architecture. `libcosmic` |
| 389 | is a north star, not a next step. | 389 | is a north star, not a next step. |
| 390 | 390 | ||
| 391 | +### Measured again, after the language features | ||
| 392 | + | ||
| 393 | +Re-run once generics, associated types, `impl Trait` in both positions, | ||
| 394 | +iterators, futures, closures, trait impls, macro expansion and the | ||
| 395 | +concurrency primitives had landed: | ||
| 396 | + | ||
| 397 | +| | first attempt | now | | ||
| 398 | +|---|---|---| | ||
| 399 | +| files that translate | **0 of 164** | **8 of 164** | | ||
| 400 | +| blocked only by `mod` (an artifact of testing files singly) | — | 34 | | ||
| 401 | +| top blocker | generics (66) | calls into dependencies (36) | | ||
| 402 | + | ||
| 403 | +**The language blockers are essentially gone.** Generics, trait objects and | ||
| 404 | +`async` no longer appear at all. What replaced them is the `iced` API: | ||
| 405 | +`new` (11), `unique`, `spacing`, `Radians`, `.cosmic`, `.activate`, | ||
| 406 | +`.scroller_width`. That is the dependency closure, not a feature list. | ||
| 407 | + | ||
| 408 | +Two smaller things fell out and are general: `#[cfg(target_env)]` joins the | ||
| 409 | +host predicates, and `--cfg <name>` now declares a custom flag the way | ||
| 410 | +rustc's does. `libcosmic`'s build script sets `wayland_platform` and | ||
| 411 | +`xdg_portal`; we do not run build scripts, so the caller declares them rather | ||
| 412 | +than us guessing. An undeclared flag is still refused. | ||
| 413 | + | ||
| 414 | +**This does not mean libcosmic is close.** Even if every file lowered, the | ||
| 415 | +result would not run: libcosmic's behaviour *is* `iced`'s, and `iced` is not | ||
| 416 | +transpiled. At that point "files produce a translation" would measure nothing | ||
| 417 | +at all. The honest reading is narrower and still worth something — the | ||
| 418 | +*language* is no longer what stops it. | ||
| 419 | + | ||
| 391 | ### The blocker survey, and what it says about roadmaps | 420 | ### The blocker survey, and what it says about roadmaps |
| 392 | 421 | ||
| 393 | 400 crates from the local registry (under 4,000 lines each), all their module | 422 | 400 crates from the local registry (under 4,000 lines each), all their module |
modified
src/lower.rs +28 -1 | @@ -309,6 +309,10 @@ pub struct Lowerer { | ||
| 309 | 309 | /// evaluated against these exactly as rustc would, so an item that is |
| 310 | 310 | /// dropped here is genuinely not part of the program being compiled. |
| 311 | 311 | pub features: Vec<String>, |
| 312 | + /// Custom `cfg` flags the caller declared with `--cfg <name>`. A build | |
| 313 | + /// script would normally set these; we do not run build scripts, so they | |
| 314 | + /// are declared rather than guessed. | |
| 315 | + pub cfgs: Vec<String>, | |
| 312 | 316 | dropped_by_cfg: usize, |
| 313 | 317 | /// Return type of the proc being lowered, so `return e` and a trailing |
| 314 | 318 | /// expression can type their literals the way Rust's inference would. |
| @@ -366,6 +370,7 @@ impl Lowerer { | ||
| 366 | 370 | modules: Vec::new(), |
| 367 | 371 | emitted: 0, |
| 368 | 372 | features: Vec::new(), |
| 373 | + cfgs: Vec::new(), | |
| 369 | 374 | dropped_by_cfg: 0, |
| 370 | 375 | ret: None, |
| 371 | 376 | target: None, |
| @@ -889,6 +894,11 @@ impl Lowerer { | ||
| 889 | 894 | // models: a normal (non-`--test`) debug build, not a docs build. |
| 890 | 895 | // Anything platform-specific stays rejected, since we would be |
| 891 | 896 | // picking a target on the user's behalf. |
| 897 | + syn::Meta::Path(p) | |
| 898 | + if self.cfgs.iter().any(|c| p.is_ident(c.as_str())) => | |
| 899 | + { | |
| 900 | + Ok(true) | |
| 901 | + } | |
| 892 | 902 | syn::Meta::Path(p) if p.is_ident("test") => Ok(false), |
| 893 | 903 | syn::Meta::Path(p) if p.is_ident("debug_assertions") => Ok(true), |
| 894 | 904 | syn::Meta::Path(p) if p.is_ident("docsrs") || p.is_ident("doc") => Ok(false), |
| @@ -902,7 +912,8 @@ impl Lowerer { | ||
| 902 | 912 | if nv.path.is_ident("target_os") |
| 903 | 913 | || nv.path.is_ident("target_arch") |
| 904 | 914 | || nv.path.is_ident("target_family") |
| 905 | - || nv.path.is_ident("target_vendor") => | |
| 915 | + || nv.path.is_ident("target_vendor") | |
| 916 | + || nv.path.is_ident("target_env") => | |
| 906 | 917 | { |
| 907 | 918 | let syn::Expr::Lit(syn::ExprLit { lit: Lit::Str(s), .. }) = &nv.value else { |
| 908 | 919 | return Err("this `cfg` key expects a string".into()); |
| @@ -913,6 +924,11 @@ impl Lowerer { | ||
| 913 | 924 | "target_os" => std::env::consts::OS, |
| 914 | 925 | "target_arch" => std::env::consts::ARCH, |
| 915 | 926 | "target_family" => std::env::consts::FAMILY, |
| 927 | + // `target_env` is the ABI flavour: "gnu", "musl", | |
| 928 | + // "msvc" or empty. Rust exposes no constant for it, so | |
| 929 | + // it is read from the target triple this binary was | |
| 930 | + // built for. | |
| 931 | + "target_env" => TARGET_ENV, | |
| 916 | 932 | _ => "unknown", |
| 917 | 933 | }) |
| 918 | 934 | } |
| @@ -6561,6 +6577,17 @@ fn unsigned_peer(t: &Nim) -> Result<&'static str, String> { | ||
| 6561 | 6577 | }) |
| 6562 | 6578 | } |
| 6563 | 6579 | |
| 6580 | +/// The ABI flavour of the host, as `#[cfg(target_env = ..)]` spells it. | |
| 6581 | +const TARGET_ENV: &str = if cfg!(target_env = "gnu") { | |
| 6582 | + "gnu" | |
| 6583 | +} else if cfg!(target_env = "musl") { | |
| 6584 | + "musl" | |
| 6585 | +} else if cfg!(target_env = "msvc") { | |
| 6586 | + "msvc" | |
| 6587 | +} else { | |
| 6588 | + "" | |
| 6589 | +}; | |
| 6590 | + | |
| 6564 | 6591 | fn quote_meta(m: &syn::Meta) -> String { |
| 6565 | 6592 | match m { |
| 6566 | 6593 | syn::Meta::Path(p) => path_name(p), |
| @@ -309,6 +309,10 @@ pub struct Lowerer { | |||
| 309 | /// evaluated against these exactly as rustc would, so an item that is | 309 | /// evaluated against these exactly as rustc would, so an item that is |
| 310 | /// dropped here is genuinely not part of the program being compiled. | 310 | /// dropped here is genuinely not part of the program being compiled. |
| 311 | pub features: Vec<String>, | 311 | pub features: Vec<String>, |
| 312 | + /// Custom `cfg` flags the caller declared with `--cfg <name>`. A build | ||
| 313 | + /// script would normally set these; we do not run build scripts, so they | ||
| 314 | + /// are declared rather than guessed. | ||
| 315 | + pub cfgs: Vec<String>, | ||
| 312 | dropped_by_cfg: usize, | 316 | dropped_by_cfg: usize, |
| 313 | /// Return type of the proc being lowered, so `return e` and a trailing | 317 | /// Return type of the proc being lowered, so `return e` and a trailing |
| 314 | /// expression can type their literals the way Rust's inference would. | 318 | /// expression can type their literals the way Rust's inference would. |
| @@ -366,6 +370,7 @@ impl Lowerer { | |||
| 366 | modules: Vec::new(), | 370 | modules: Vec::new(), |
| 367 | emitted: 0, | 371 | emitted: 0, |
| 368 | features: Vec::new(), | 372 | features: Vec::new(), |
| 373 | + cfgs: Vec::new(), | ||
| 369 | dropped_by_cfg: 0, | 374 | dropped_by_cfg: 0, |
| 370 | ret: None, | 375 | ret: None, |
| 371 | target: None, | 376 | target: None, |
| @@ -889,6 +894,11 @@ impl Lowerer { | |||
| 889 | // models: a normal (non-`--test`) debug build, not a docs build. | 894 | // models: a normal (non-`--test`) debug build, not a docs build. |
| 890 | // Anything platform-specific stays rejected, since we would be | 895 | // Anything platform-specific stays rejected, since we would be |
| 891 | // picking a target on the user's behalf. | 896 | // picking a target on the user's behalf. |
| 897 | + syn::Meta::Path(p) | ||
| 898 | + if self.cfgs.iter().any(|c| p.is_ident(c.as_str())) => | ||
| 899 | + { | ||
| 900 | + Ok(true) | ||
| 901 | + } | ||
| 892 | syn::Meta::Path(p) if p.is_ident("test") => Ok(false), | 902 | syn::Meta::Path(p) if p.is_ident("test") => Ok(false), |
| 893 | syn::Meta::Path(p) if p.is_ident("debug_assertions") => Ok(true), | 903 | syn::Meta::Path(p) if p.is_ident("debug_assertions") => Ok(true), |
| 894 | syn::Meta::Path(p) if p.is_ident("docsrs") || p.is_ident("doc") => Ok(false), | 904 | syn::Meta::Path(p) if p.is_ident("docsrs") || p.is_ident("doc") => Ok(false), |
| @@ -902,7 +912,8 @@ impl Lowerer { | |||
| 902 | if nv.path.is_ident("target_os") | 912 | if nv.path.is_ident("target_os") |
| 903 | || nv.path.is_ident("target_arch") | 913 | || nv.path.is_ident("target_arch") |
| 904 | || nv.path.is_ident("target_family") | 914 | || nv.path.is_ident("target_family") |
| 905 | - || nv.path.is_ident("target_vendor") => | 915 | + || nv.path.is_ident("target_vendor") |
| 916 | + || nv.path.is_ident("target_env") => | ||
| 906 | { | 917 | { |
| 907 | let syn::Expr::Lit(syn::ExprLit { lit: Lit::Str(s), .. }) = &nv.value else { | 918 | let syn::Expr::Lit(syn::ExprLit { lit: Lit::Str(s), .. }) = &nv.value else { |
| 908 | return Err("this `cfg` key expects a string".into()); | 919 | return Err("this `cfg` key expects a string".into()); |
| @@ -913,6 +924,11 @@ impl Lowerer { | |||
| 913 | "target_os" => std::env::consts::OS, | 924 | "target_os" => std::env::consts::OS, |
| 914 | "target_arch" => std::env::consts::ARCH, | 925 | "target_arch" => std::env::consts::ARCH, |
| 915 | "target_family" => std::env::consts::FAMILY, | 926 | "target_family" => std::env::consts::FAMILY, |
| 927 | + // `target_env` is the ABI flavour: "gnu", "musl", | ||
| 928 | + // "msvc" or empty. Rust exposes no constant for it, so | ||
| 929 | + // it is read from the target triple this binary was | ||
| 930 | + // built for. | ||
| 931 | + "target_env" => TARGET_ENV, | ||
| 916 | _ => "unknown", | 932 | _ => "unknown", |
| 917 | }) | 933 | }) |
| 918 | } | 934 | } |
| @@ -6561,6 +6577,17 @@ fn unsigned_peer(t: &Nim) -> Result<&'static str, String> { | |||
| 6561 | }) | 6577 | }) |
| 6562 | } | 6578 | } |
| 6563 | 6579 | ||
| 6580 | +/// The ABI flavour of the host, as `#[cfg(target_env = ..)]` spells it. | ||
| 6581 | +const TARGET_ENV: &str = if cfg!(target_env = "gnu") { | ||
| 6582 | + "gnu" | ||
| 6583 | +} else if cfg!(target_env = "musl") { | ||
| 6584 | + "musl" | ||
| 6585 | +} else if cfg!(target_env = "msvc") { | ||
| 6586 | + "msvc" | ||
| 6587 | +} else { | ||
| 6588 | + "" | ||
| 6589 | +}; | ||
| 6590 | + | ||
| 6564 | fn quote_meta(m: &syn::Meta) -> String { | 6591 | fn quote_meta(m: &syn::Meta) -> String { |
| 6565 | match m { | 6592 | match m { |
| 6566 | syn::Meta::Path(p) => path_name(p), | 6593 | syn::Meta::Path(p) => path_name(p), |
modified
src/main.rs +11 -5 | @@ -39,6 +39,7 @@ fn run() -> Result<(), String> { | ||
| 39 | 39 | let mut inputs: Vec<PathBuf> = Vec::new(); |
| 40 | 40 | let mut output: Option<PathBuf> = None; |
| 41 | 41 | let mut features: Vec<String> = Vec::new(); |
| 42 | + let mut cfgs: Vec<String> = Vec::new(); | |
| 42 | 43 | |
| 43 | 44 | while let Some(a) = args.next() { |
| 44 | 45 | match a.to_string_lossy().as_ref() { |
| @@ -52,13 +53,17 @@ fn run() -> Result<(), String> { | ||
| 52 | 53 | "--cfg" => { |
| 53 | 54 | let v = args.next().ok_or("`--cfg` needs an argument")?; |
| 54 | 55 | let v = v.to_string_lossy().into_owned(); |
| 55 | - let f = v | |
| 56 | - .strip_prefix("feature=") | |
| 57 | - .ok_or("only `--cfg feature=<name>` is supported")?; | |
| 58 | - features.push(f.trim_matches('"').to_string()); | |
| 56 | + match v.strip_prefix("feature=") { | |
| 57 | + Some(f) => features.push(f.trim_matches('"').to_string()), | |
| 58 | + // A bare `--cfg name` declares a custom flag, as rustc's | |
| 59 | + // does. A build script would normally set these, and we do | |
| 60 | + // not run build scripts -- so the caller says which are on | |
| 61 | + // rather than us guessing. | |
| 62 | + None => cfgs.push(v), | |
| 63 | + } | |
| 59 | 64 | } |
| 60 | 65 | "-h" | "--help" => { |
| 61 | - println!("usage: rustnim <input.rs>... [--cfg feature=<name>]... [-o <output.nim>]"); | |
| 66 | + println!("usage: rustnim <input.rs>... [--cfg <name>|feature=<name>]... [-o <output.nim>]"); | |
| 62 | 67 | println!(); |
| 63 | 68 | println!("Several inputs are concatenated into one Nim module, in the"); |
| 64 | 69 | println!("order given. That is how a multi-file crate is handled: Nim"); |
| @@ -101,6 +106,7 @@ fn run() -> Result<(), String> { | ||
| 101 | 106 | |
| 102 | 107 | let mut lowerer = lower::Lowerer::new(); |
| 103 | 108 | lowerer.features = features; |
| 109 | + lowerer.cfgs = cfgs; | |
| 104 | 110 | // A `mod x;` is satisfied when x.rs is one of the inputs. |
| 105 | 111 | lowerer.modules = inputs |
| 106 | 112 | .iter() |
| @@ -39,6 +39,7 @@ fn run() -> Result<(), String> { | |||
| 39 | let mut inputs: Vec<PathBuf> = Vec::new(); | 39 | let mut inputs: Vec<PathBuf> = Vec::new(); |
| 40 | let mut output: Option<PathBuf> = None; | 40 | let mut output: Option<PathBuf> = None; |
| 41 | let mut features: Vec<String> = Vec::new(); | 41 | let mut features: Vec<String> = Vec::new(); |
| 42 | + let mut cfgs: Vec<String> = Vec::new(); | ||
| 42 | 43 | ||
| 43 | while let Some(a) = args.next() { | 44 | while let Some(a) = args.next() { |
| 44 | match a.to_string_lossy().as_ref() { | 45 | match a.to_string_lossy().as_ref() { |
| @@ -52,13 +53,17 @@ fn run() -> Result<(), String> { | |||
| 52 | "--cfg" => { | 53 | "--cfg" => { |
| 53 | let v = args.next().ok_or("`--cfg` needs an argument")?; | 54 | let v = args.next().ok_or("`--cfg` needs an argument")?; |
| 54 | let v = v.to_string_lossy().into_owned(); | 55 | let v = v.to_string_lossy().into_owned(); |
| 55 | - let f = v | 56 | + match v.strip_prefix("feature=") { |
| 56 | - .strip_prefix("feature=") | 57 | + Some(f) => features.push(f.trim_matches('"').to_string()), |
| 57 | - .ok_or("only `--cfg feature=<name>` is supported")?; | 58 | + // A bare `--cfg name` declares a custom flag, as rustc's |
| 58 | - features.push(f.trim_matches('"').to_string()); | 59 | + // does. A build script would normally set these, and we do |
| 60 | + // not run build scripts -- so the caller says which are on | ||
| 61 | + // rather than us guessing. | ||
| 62 | + None => cfgs.push(v), | ||
| 63 | + } | ||
| 59 | } | 64 | } |
| 60 | "-h" | "--help" => { | 65 | "-h" | "--help" => { |
| 61 | - println!("usage: rustnim <input.rs>... [--cfg feature=<name>]... [-o <output.nim>]"); | 66 | + println!("usage: rustnim <input.rs>... [--cfg <name>|feature=<name>]... [-o <output.nim>]"); |
| 62 | println!(); | 67 | println!(); |
| 63 | println!("Several inputs are concatenated into one Nim module, in the"); | 68 | println!("Several inputs are concatenated into one Nim module, in the"); |
| 64 | println!("order given. That is how a multi-file crate is handled: Nim"); | 69 | println!("order given. That is how a multi-file crate is handled: Nim"); |
| @@ -101,6 +106,7 @@ fn run() -> Result<(), String> { | |||
| 101 | 106 | ||
| 102 | let mut lowerer = lower::Lowerer::new(); | 107 | let mut lowerer = lower::Lowerer::new(); |
| 103 | lowerer.features = features; | 108 | lowerer.features = features; |
| 109 | + lowerer.cfgs = cfgs; | ||
| 104 | // A `mod x;` is satisfied when x.rs is one of the inputs. | 110 | // A `mod x;` is satisfied when x.rs is one of the inputs. |
| 105 | lowerer.modules = inputs | 111 | lowerer.modules = inputs |
| 106 | .iter() | 112 | .iter() |
modified
tests/multifile.rs +29 -0 | @@ -122,6 +122,35 @@ fn an_unevaluable_cfg_predicate_is_reported_not_assumed() { | ||
| 122 | 122 | assert!(err.contains("not a predicate rustnim can evaluate"), "unexpected: {err}"); |
| 123 | 123 | } |
| 124 | 124 | |
| 125 | +#[test] | |
| 126 | +fn a_custom_cfg_is_declared_not_guessed() { | |
| 127 | + // A build script would normally set these. We do not run build scripts, | |
| 128 | + // so the caller declares which are on -- as rustc's own `--cfg` does. | |
| 129 | + let d = work("cfg-custom"); | |
| 130 | + let f = d.join("c.rs"); | |
| 131 | + fs::write( | |
| 132 | + &f, | |
| 133 | + "#[cfg(wayland_platform)]\nfn on_wayland() -> i32 { 1 }\n\ | |
| 134 | + #[cfg(some_other_flag)]\nfn not_declared() -> i32 { 2 }\n\ | |
| 135 | + fn main() {}\n", | |
| 136 | + ) | |
| 137 | + .unwrap(); | |
| 138 | + | |
| 139 | + // Undeclared, so it cannot be evaluated and is reported. | |
| 140 | + let (ok, _, err) = run(&d, &[f.to_str().unwrap()]); | |
| 141 | + assert!(!ok, "an undeclared cfg must not be assumed"); | |
| 142 | + assert!(err.contains("not a predicate rustnim can evaluate"), "{err}"); | |
| 143 | + | |
| 144 | + // Declared: the gated item is kept, and the one still undeclared is gone. | |
| 145 | + let (ok, out, err) = run( | |
| 146 | + &d, | |
| 147 | + &[f.to_str().unwrap(), "--cfg", "wayland_platform", "--cfg", "some_other_flag"], | |
| 148 | + ); | |
| 149 | + assert!(ok, "{err}"); | |
| 150 | + assert!(out.contains("proc on_wayland"), "{out}"); | |
| 151 | + assert!(out.contains("proc not_declared"), "{out}"); | |
| 152 | +} | |
| 153 | + | |
| 125 | 154 | #[test] |
| 126 | 155 | fn host_facts_are_evaluated() { |
| 127 | 156 | // These are determined by the machine the generated Nim is compiled for, |
| @@ -122,6 +122,35 @@ fn an_unevaluable_cfg_predicate_is_reported_not_assumed() { | |||
| 122 | assert!(err.contains("not a predicate rustnim can evaluate"), "unexpected: {err}"); | 122 | assert!(err.contains("not a predicate rustnim can evaluate"), "unexpected: {err}"); |
| 123 | } | 123 | } |
| 124 | 124 | ||
| 125 | +#[test] | ||
| 126 | +fn a_custom_cfg_is_declared_not_guessed() { | ||
| 127 | + // A build script would normally set these. We do not run build scripts, | ||
| 128 | + // so the caller declares which are on -- as rustc's own `--cfg` does. | ||
| 129 | + let d = work("cfg-custom"); | ||
| 130 | + let f = d.join("c.rs"); | ||
| 131 | + fs::write( | ||
| 132 | + &f, | ||
| 133 | + "#[cfg(wayland_platform)]\nfn on_wayland() -> i32 { 1 }\n\ | ||
| 134 | + #[cfg(some_other_flag)]\nfn not_declared() -> i32 { 2 }\n\ | ||
| 135 | + fn main() {}\n", | ||
| 136 | + ) | ||
| 137 | + .unwrap(); | ||
| 138 | + | ||
| 139 | + // Undeclared, so it cannot be evaluated and is reported. | ||
| 140 | + let (ok, _, err) = run(&d, &[f.to_str().unwrap()]); | ||
| 141 | + assert!(!ok, "an undeclared cfg must not be assumed"); | ||
| 142 | + assert!(err.contains("not a predicate rustnim can evaluate"), "{err}"); | ||
| 143 | + | ||
| 144 | + // Declared: the gated item is kept, and the one still undeclared is gone. | ||
| 145 | + let (ok, out, err) = run( | ||
| 146 | + &d, | ||
| 147 | + &[f.to_str().unwrap(), "--cfg", "wayland_platform", "--cfg", "some_other_flag"], | ||
| 148 | + ); | ||
| 149 | + assert!(ok, "{err}"); | ||
| 150 | + assert!(out.contains("proc on_wayland"), "{out}"); | ||
| 151 | + assert!(out.contains("proc not_declared"), "{out}"); | ||
| 152 | +} | ||
| 153 | + | ||
| 125 | #[test] | 154 | #[test] |
| 126 | fn host_facts_are_evaluated() { | 155 | fn host_facts_are_evaluated() { |
| 127 | // These are determined by the machine the generated Nim is compiled for, | 156 | // These are determined by the machine the generated Nim is compiled for, |