You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
rustcall-extract manifest|wrap --mode cratewithout--crate-root treats every FILE argument as its own module root. Nothing in RustCall.jl reaches it: scan_crate (src/crate_bindings.jl) and both wrap_crate call sites (src/pyo3.jl) pass crate_root = lib_root. It is a CLI affordance for callers that enumerate .rs files themselves.
#343 gave that mode something it did not have before: files reached implicitly, through an include! and through the mod declarations inside a fragment. A listed file's own mod is still the caller's to list — without a root there is no tree to place it in — but a fragment is not something a caller can name, so it and what it declares are followed. That is the right behaviour, and it opens a class of questions the mode has never had to answer.
The class
Which scan wins when a file is both listed and reachable implicitly.Follow out-of-line mod declarations inside include!d files (#343) #352 makes the caller's list win, which fixes the duplicate-symbol failure it was written for (rustcall_deep claimed twice) but keeps the unpositioned scan. For PyO3 the position is the answer: with pub mod api { include!("frag.rs"); } and a #[pyfunction] in frag.rs, the listed-root scan records an empty module_path, so function_wrapper generates crate_name::f instead of crate_name::api::f and the wrapper does not compile. The positioned scan is the one to keep, and the explicit root scan the one to suppress.
What identifies a visited file. The key is (canonical path, module_path). FilePosition now also carries reachable, enclosing_cfg, symbol_path and marked. Under a lenient scan, #[cfg(feature = "x")] pub mod api { include!("frag.rs"); } beside #[cfg(not(feature = "x"))] mod api { include!("frag.rs"); } queues one fragment at one module path with two different positions; only the LIFO winner is scanned, so a #[pyfunction] can inherit the private branch's skip reason and crate_needs_pyo3_wrapper then declines to generate a wrapper for a build that does enable x.
Both are visible failures — a wrapper that does not compile, or one that is not generated — not silently wrong bindings, and neither is reachable from Julia.
Acceptance
a file that is both listed and reachable through an include! is scanned once, at the position the include! gives it, and the manifest records that module_path;
two cfg-exclusive modules including one fragment yield both positions in a lenient scan;
Split out of the review of PR #352 (#343).
The mode
rustcall-extract manifest|wrap --mode cratewithout--crate-roottreats every FILE argument as its own module root. Nothing in RustCall.jl reaches it:scan_crate(src/crate_bindings.jl) and bothwrap_cratecall sites (src/pyo3.jl) passcrate_root = lib_root. It is a CLI affordance for callers that enumerate.rsfiles themselves.#343 gave that mode something it did not have before: files reached implicitly, through an
include!and through themoddeclarations inside a fragment. A listed file's ownmodis still the caller's to list — without a root there is no tree to place it in — but a fragment is not something a caller can name, so it and what it declares are followed. That is the right behaviour, and it opens a class of questions the mode has never had to answer.The class
Which scan wins when a file is both listed and reachable implicitly. Follow out-of-line
moddeclarations inside include!d files (#343) #352 makes the caller's list win, which fixes the duplicate-symbol failure it was written for (rustcall_deepclaimed twice) but keeps the unpositioned scan. For PyO3 the position is the answer: withpub mod api { include!("frag.rs"); }and a#[pyfunction]infrag.rs, the listed-root scan records an emptymodule_path, sofunction_wrappergeneratescrate_name::finstead ofcrate_name::api::fand the wrapper does not compile. The positioned scan is the one to keep, and the explicit root scan the one to suppress.What identifies a visited file. The key is
(canonical path, module_path).FilePositionnow also carriesreachable,enclosing_cfg,symbol_pathandmarked. Under a lenient scan,#[cfg(feature = "x")] pub mod api { include!("frag.rs"); }beside#[cfg(not(feature = "x"))] mod api { include!("frag.rs"); }queues one fragment at one module path with two different positions; only the LIFO winner is scanned, so a#[pyfunction]can inherit the private branch's skip reason andcrate_needs_pyo3_wrapperthen declines to generate a wrapper for a build that does enablex.Both are visible failures — a wrapper that does not compile, or one that is not generated — not silently wrong bindings, and neither is reachable from Julia.
Acceptance
include!is scanned once, at the position theinclude!gives it, and the manifest records thatmodule_path;moddeclarations inside include!d files (#343) #352 fixed stays fixed (extract_manifest([lib, frag]; mode = "crate")yields each item once);pulled_instays the one step both walks share.