Skip to content

Commit e2e2ddd

Browse files
committed
Auto merge of #10517 - Muscraft:rfc2906-part2, r=epage
Part 2 of RFC2906 -- allow inheriting from a different `Cargo.toml` Tracking issue: #8415 RFC: rust-lang/rfcs#2906 [Part 1](#10497) This PR focuses on inheriting from a root workspace: - Allow inheriting from a different `Cargo.toml` - Add in searching for a workspace root in `to_real_manifest` as needed - Fixed problem where a package would try to pull a dependency from a workspace and specify `{ workspace = true, optional = true }` and it would not respect the `optional` - Added tests to verify everything is in working order Remaining implementation work for the RFC - Correctly inherit fields that are relative paths - Including adding support for inheriting `license_file`, `readme`, and path-dependencies - Path dependencies infer version directive - Lock workspace dependencies and warn when unused - Optimizations, as needed - Evaluate any new fields for being inheritable (e.g. `rust-version`) Problems: - There is duplication of code that can't be removed without significant refactoring - Potential to parse the same manifest many times when searching for a root - This should not happen when a `[package]` specifies its workspace - This should only happen if the workspace root is greater than one folder above
2 parents 1a052ae + 8b3ce98 commit e2e2ddd

File tree

5 files changed

+760
-106
lines changed

5 files changed

+760
-106
lines changed

src/cargo/core/manifest.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,15 @@ pub enum EitherManifest {
2626
Virtual(VirtualManifest),
2727
}
2828

29+
impl EitherManifest {
30+
pub(crate) fn workspace_config(&self) -> &WorkspaceConfig {
31+
match *self {
32+
EitherManifest::Real(ref r) => r.workspace_config(),
33+
EitherManifest::Virtual(ref v) => v.workspace_config(),
34+
}
35+
}
36+
}
37+
2938
/// Contains all the information about a package, as loaded from a `Cargo.toml`.
3039
///
3140
/// This is deserialized using the [`TomlManifest`] type.

src/cargo/core/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ pub use self::shell::{Shell, Verbosity};
1111
pub use self::source::{GitReference, Source, SourceId, SourceMap};
1212
pub use self::summary::{FeatureMap, FeatureValue, Summary};
1313
pub use self::workspace::{
14-
InheritableFields, MaybePackage, Workspace, WorkspaceConfig, WorkspaceRootConfig,
14+
find_workspace_root, InheritableFields, MaybePackage, Workspace, WorkspaceConfig,
15+
WorkspaceRootConfig,
1516
};
1617

1718
pub mod compiler;

src/cargo/core/workspace.rs

Lines changed: 114 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -591,16 +591,6 @@ impl<'cfg> Workspace<'cfg> {
591591
/// Returns an error if `manifest_path` isn't actually a valid manifest or
592592
/// if some other transient error happens.
593593
fn find_root(&mut self, manifest_path: &Path) -> CargoResult<Option<PathBuf>> {
594-
fn read_root_pointer(member_manifest: &Path, root_link: &str) -> PathBuf {
595-
let path = member_manifest
596-
.parent()
597-
.unwrap()
598-
.join(root_link)
599-
.join("Cargo.toml");
600-
debug!("find_root - pointer {}", path.display());
601-
paths::normalize_path(&path)
602-
}
603-
604594
{
605595
let current = self.packages.load(manifest_path)?;
606596
match *current.workspace_config() {
@@ -615,42 +605,25 @@ impl<'cfg> Workspace<'cfg> {
615605
}
616606
}
617607

618-
for path in paths::ancestors(manifest_path, None).skip(2) {
619-
if path.ends_with("target/package") {
620-
break;
621-
}
622-
623-
let ances_manifest_path = path.join("Cargo.toml");
608+
for ances_manifest_path in find_root_iter(manifest_path, self.config) {
624609
debug!("find_root - trying {}", ances_manifest_path.display());
625-
if ances_manifest_path.exists() {
626-
match *self.packages.load(&ances_manifest_path)?.workspace_config() {
627-
WorkspaceConfig::Root(ref ances_root_config) => {
628-
debug!("find_root - found a root checking exclusion");
629-
if !ances_root_config.is_excluded(manifest_path) {
630-
debug!("find_root - found!");
631-
return Ok(Some(ances_manifest_path));
632-
}
633-
}
634-
WorkspaceConfig::Member {
635-
root: Some(ref path_to_root),
636-
} => {
637-
debug!("find_root - found pointer");
638-
return Ok(Some(read_root_pointer(&ances_manifest_path, path_to_root)));
610+
match *self.packages.load(&ances_manifest_path)?.workspace_config() {
611+
WorkspaceConfig::Root(ref ances_root_config) => {
612+
debug!("find_root - found a root checking exclusion");
613+
if !ances_root_config.is_excluded(manifest_path) {
614+
debug!("find_root - found!");
615+
return Ok(Some(ances_manifest_path));
639616
}
640-
WorkspaceConfig::Member { .. } => {}
641617
}
642-
}
643-
644-
// Don't walk across `CARGO_HOME` when we're looking for the
645-
// workspace root. Sometimes a package will be organized with
646-
// `CARGO_HOME` pointing inside of the workspace root or in the
647-
// current package, but we don't want to mistakenly try to put
648-
// crates.io crates into the workspace by accident.
649-
if self.config.home() == path {
650-
break;
618+
WorkspaceConfig::Member {
619+
root: Some(ref path_to_root),
620+
} => {
621+
debug!("find_root - found pointer");
622+
return Ok(Some(read_root_pointer(&ances_manifest_path, path_to_root)));
623+
}
624+
WorkspaceConfig::Member { .. } => {}
651625
}
652626
}
653-
654627
Ok(None)
655628
}
656629

@@ -1653,6 +1626,10 @@ impl WorkspaceRootConfig {
16531626
.collect::<Result<Vec<_>, _>>()?;
16541627
Ok(res)
16551628
}
1629+
1630+
pub fn inheritable(&self) -> &InheritableFields {
1631+
&self.inheritable_fields
1632+
}
16561633
}
16571634

16581635
/// A group of fields that are inheritable by members of the workspace
@@ -1841,3 +1818,99 @@ impl InheritableFields {
18411818
})
18421819
}
18431820
}
1821+
1822+
fn parse_manifest(manifest_path: &Path, config: &Config) -> CargoResult<EitherManifest> {
1823+
let key = manifest_path.parent().unwrap();
1824+
let source_id = SourceId::for_path(key)?;
1825+
let (manifest, _nested_paths) = read_manifest(manifest_path, source_id, config)?;
1826+
Ok(manifest)
1827+
}
1828+
1829+
pub fn find_workspace_root(manifest_path: &Path, config: &Config) -> CargoResult<Option<PathBuf>> {
1830+
for ances_manifest_path in find_root_iter(manifest_path, config) {
1831+
debug!("find_root - trying {}", ances_manifest_path.display());
1832+
match *parse_manifest(&ances_manifest_path, config)?.workspace_config() {
1833+
WorkspaceConfig::Root(ref ances_root_config) => {
1834+
debug!("find_root - found a root checking exclusion");
1835+
if !ances_root_config.is_excluded(manifest_path) {
1836+
debug!("find_root - found!");
1837+
return Ok(Some(ances_manifest_path));
1838+
}
1839+
}
1840+
WorkspaceConfig::Member {
1841+
root: Some(ref path_to_root),
1842+
} => {
1843+
debug!("find_root - found pointer");
1844+
return Ok(Some(read_root_pointer(&ances_manifest_path, path_to_root)));
1845+
}
1846+
WorkspaceConfig::Member { .. } => {}
1847+
}
1848+
}
1849+
Ok(None)
1850+
}
1851+
1852+
fn read_root_pointer(member_manifest: &Path, root_link: &str) -> PathBuf {
1853+
let path = member_manifest
1854+
.parent()
1855+
.unwrap()
1856+
.join(root_link)
1857+
.join("Cargo.toml");
1858+
debug!("find_root - pointer {}", path.display());
1859+
paths::normalize_path(&path)
1860+
}
1861+
1862+
fn find_root_iter<'a>(
1863+
manifest_path: &'a Path,
1864+
config: &'a Config,
1865+
) -> impl Iterator<Item = PathBuf> + 'a {
1866+
LookBehind::new(paths::ancestors(manifest_path, None).skip(2))
1867+
.take_while(|path| !path.curr.ends_with("target/package"))
1868+
// Don't walk across `CARGO_HOME` when we're looking for the
1869+
// workspace root. Sometimes a package will be organized with
1870+
// `CARGO_HOME` pointing inside of the workspace root or in the
1871+
// current package, but we don't want to mistakenly try to put
1872+
// crates.io crates into the workspace by accident.
1873+
.take_while(|path| {
1874+
if let Some(last) = path.last {
1875+
config.home() != last
1876+
} else {
1877+
true
1878+
}
1879+
})
1880+
.map(|path| path.curr.join("Cargo.toml"))
1881+
.filter(|ances_manifest_path| ances_manifest_path.exists())
1882+
}
1883+
1884+
struct LookBehindWindow<'a, T: ?Sized> {
1885+
curr: &'a T,
1886+
last: Option<&'a T>,
1887+
}
1888+
1889+
struct LookBehind<'a, T: ?Sized, K: Iterator<Item = &'a T>> {
1890+
iter: K,
1891+
last: Option<&'a T>,
1892+
}
1893+
1894+
impl<'a, T: ?Sized, K: Iterator<Item = &'a T>> LookBehind<'a, T, K> {
1895+
fn new(items: K) -> Self {
1896+
Self {
1897+
iter: items,
1898+
last: None,
1899+
}
1900+
}
1901+
}
1902+
1903+
impl<'a, T: ?Sized, K: Iterator<Item = &'a T>> Iterator for LookBehind<'a, T, K> {
1904+
type Item = LookBehindWindow<'a, T>;
1905+
1906+
fn next(&mut self) -> Option<Self::Item> {
1907+
match self.iter.next() {
1908+
None => None,
1909+
Some(next) => {
1910+
let last = self.last;
1911+
self.last = Some(next);
1912+
Some(LookBehindWindow { curr: next, last })
1913+
}
1914+
}
1915+
}
1916+
}

0 commit comments

Comments
 (0)