Skip to content

Commit caad908

Browse files
authored
Give useful error on missing workspace manifest (#1475)
Previously if the workspace root manifest was missing, a panic would be triggered due to unwrapping a `None` option. Now, we suggest the missing manifest if possible, or direct you to work it out yourself if we can't.
1 parent 0e86b9d commit caad908

File tree

3 files changed

+99
-19
lines changed

3 files changed

+99
-19
lines changed

crate_universe/src/cli/splice.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ pub fn splice(opt: SpliceOptions) -> Result<()> {
7070
let splicer = Splicer::new(splicing_dir, splicing_manifest)?;
7171

7272
// Splice together the manifest
73-
let manifest_path = splicer.splice_workspace()?;
73+
let manifest_path = splicer.splice_workspace(&opt.cargo)?;
7474

7575
// Generate a lockfile
7676
let cargo_lockfile = generate_lockfile(

crate_universe/src/cli/vendor.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ pub fn vendor(opt: VendorOptions) -> Result<()> {
127127

128128
// Splice together the manifest
129129
let manifest_path = splicer
130-
.splice_workspace()
130+
.splice_workspace(&opt.cargo)
131131
.context("Failed to splice workspace")?;
132132

133133
// Gather a cargo lockfile

crate_universe/src/splicing/splicer.rs

Lines changed: 97 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::fs;
55
use std::path::{Path, PathBuf};
66

77
use anyhow::{bail, Context, Result};
8+
use cargo_metadata::MetadataCommand;
89
use cargo_toml::{Dependency, Manifest};
910
use normpath::PathExt;
1011

@@ -44,6 +45,7 @@ impl<'a> SplicerKind<'a> {
4445
pub fn new(
4546
manifests: &'a HashMap<PathBuf, Manifest>,
4647
splicing_manifest: &'a SplicingManifest,
48+
cargo: &Path,
4749
) -> Result<Self> {
4850
// First check for any workspaces in the provided manifests
4951
let workspace_owned: HashMap<&PathBuf, &Manifest> = manifests
@@ -67,7 +69,33 @@ impl<'a> SplicerKind<'a> {
6769
bail!("When splicing manifests, there can only be 1 root workspace manifest");
6870
}
6971

72+
// This is an error case - we've detected some manifests are in a workspace, but can't
73+
// find it.
74+
// This block is just for trying to give as useful an error message as possible in this
75+
// case.
76+
if workspace_roots.is_empty() {
77+
let sorted_manifests: BTreeSet<_> = manifests.keys().collect();
78+
for manifest_path in sorted_manifests {
79+
let metadata_result = MetadataCommand::new()
80+
.cargo_path(cargo)
81+
.current_dir(manifest_path.parent().unwrap())
82+
.manifest_path(manifest_path)
83+
.no_deps()
84+
.exec();
85+
if let Ok(metadata) = metadata_result {
86+
let label = Label::from_absolute_path(
87+
metadata.workspace_root.join("Cargo.toml").as_std_path(),
88+
);
89+
if let Ok(label) = label {
90+
bail!("Missing root workspace manifest. Please add the following label to the `manifests` key: \"{}\"", label);
91+
}
92+
}
93+
}
94+
bail!("Missing root workspace manifest. Please add the label of the workspace root to the `manifests` key");
95+
}
96+
7097
// Ensure all workspace owned manifests are members of the one workspace root
98+
// UNWRAP: Safe because we've checked workspace_roots isn't empty.
7199
let (root_manifest_path, root_manifest) = workspace_roots.drain().last().unwrap();
72100
let external_workspace_members: BTreeSet<String> = workspace_packages
73101
.into_iter()
@@ -470,8 +498,9 @@ impl Splicer {
470498
}
471499

472500
/// Build a new workspace root
473-
pub fn splice_workspace(&self) -> Result<SplicedManifest> {
474-
SplicerKind::new(&self.manifests, &self.splicing_manifest)?.splice(&self.workspace_dir)
501+
pub fn splice_workspace(&self, cargo: &Path) -> Result<SplicedManifest> {
502+
SplicerKind::new(&self.manifests, &self.splicing_manifest, cargo)?
503+
.splice(&self.workspace_dir)
475504
}
476505
}
477506

@@ -706,6 +735,10 @@ mod test {
706735
(PathBuf::from("cargo"), PathBuf::from("rustc"))
707736
}
708737

738+
fn cargo() -> PathBuf {
739+
get_cargo_and_rustc_paths().0
740+
}
741+
709742
fn generate_metadata(manifest_path: &Path) -> cargo_metadata::Metadata {
710743
let manifest_dir = manifest_path.parent().unwrap_or_else(|| {
711744
panic!(
@@ -746,16 +779,28 @@ mod test {
746779
}
747780

748781
fn mock_cargo_toml(path: &Path, name: &str) -> cargo_toml::Manifest {
782+
mock_cargo_toml_with_dependencies(path, name, &[])
783+
}
784+
785+
fn mock_cargo_toml_with_dependencies(
786+
path: &Path,
787+
name: &str,
788+
deps: &[&str],
789+
) -> cargo_toml::Manifest {
749790
let manifest = cargo_toml::Manifest::from_str(&textwrap::dedent(&format!(
750791
r#"
751792
[package]
752-
name = "{}"
793+
name = "{name}"
753794
version = "0.0.1"
754795
755796
[lib]
756797
path = "lib.rs"
798+
799+
[dependencies]
800+
{dependencies}
757801
"#,
758-
name
802+
name = name,
803+
dependencies = deps.join("\n")
759804
)))
760805
.unwrap();
761806

@@ -809,7 +854,12 @@ mod test {
809854
.join("root_pkg")
810855
.join(pkg)
811856
.join("Cargo.toml");
812-
mock_cargo_toml(&manifest_path, pkg);
857+
let deps = if pkg == &"sub_pkg_b" {
858+
vec![r#"sub_pkg_a = { path = "../sub_pkg_a" }"#]
859+
} else {
860+
vec![]
861+
};
862+
mock_cargo_toml_with_dependencies(&manifest_path, pkg, &deps);
813863

814864
splicing_manifest.manifests.insert(
815865
manifest_path,
@@ -842,6 +892,9 @@ mod test {
842892
let manifest_path = root_pkg.join("Cargo.toml");
843893
fs::create_dir_all(&manifest_path.parent().unwrap()).unwrap();
844894
fs::write(&manifest_path, toml::to_string(&manifest).unwrap()).unwrap();
895+
{
896+
File::create(root_pkg.join("BUILD.bazel")).unwrap();
897+
}
845898

846899
let sub_pkg_a = root_pkg.join("sub_pkg_a");
847900
let sub_pkg_b = root_pkg.join("sub_pkg_b");
@@ -855,7 +908,7 @@ mod test {
855908

856909
splicing_manifest.manifests.insert(
857910
manifest_path,
858-
Label::from_str("//pkg_root:Cargo.toml").unwrap(),
911+
Label::from_str("//root_pkg:Cargo.toml").unwrap(),
859912
);
860913

861914
(splicing_manifest, cache_dir)
@@ -977,7 +1030,7 @@ mod test {
9771030
let workspace_manifest =
9781031
Splicer::new(workspace_root.as_ref().to_path_buf(), splicing_manifest)
9791032
.unwrap()
980-
.splice_workspace()
1033+
.splice_workspace(&cargo())
9811034
.unwrap();
9821035

9831036
// Ensure metadata is valid
@@ -1010,7 +1063,7 @@ mod test {
10101063
let workspace_manifest =
10111064
Splicer::new(workspace_root.as_ref().to_path_buf(), splicing_manifest)
10121065
.unwrap()
1013-
.splice_workspace()
1066+
.splice_workspace(&cargo())
10141067
.unwrap();
10151068

10161069
// Ensure metadata is valid
@@ -1041,15 +1094,15 @@ mod test {
10411094
// Remove everything but the root manifest
10421095
splicing_manifest
10431096
.manifests
1044-
.retain(|_, label| *label == Label::from_str("//pkg_root:Cargo.toml").unwrap());
1097+
.retain(|_, label| *label == Label::from_str("//root_pkg:Cargo.toml").unwrap());
10451098
assert_eq!(splicing_manifest.manifests.len(), 1);
10461099

10471100
// Splice the workspace
10481101
let workspace_root = tempfile::tempdir().unwrap();
10491102
let workspace_manifest =
10501103
Splicer::new(workspace_root.as_ref().to_path_buf(), splicing_manifest)
10511104
.unwrap()
1052-
.splice_workspace();
1105+
.splice_workspace(&cargo());
10531106

10541107
assert!(workspace_manifest.is_err());
10551108

@@ -1062,6 +1115,33 @@ mod test {
10621115
);
10631116
}
10641117

1118+
#[test]
1119+
fn splice_workspace_report_missing_root() {
1120+
let (mut splicing_manifest, _cache_dir) = mock_splicing_manifest_with_workspace();
1121+
1122+
// Remove everything but the root manifest
1123+
splicing_manifest
1124+
.manifests
1125+
.retain(|_, label| *label != Label::from_str("//root_pkg:Cargo.toml").unwrap());
1126+
assert_eq!(splicing_manifest.manifests.len(), 2);
1127+
1128+
// Splice the workspace
1129+
let workspace_root = tempfile::tempdir().unwrap();
1130+
let workspace_manifest =
1131+
Splicer::new(workspace_root.as_ref().to_path_buf(), splicing_manifest)
1132+
.unwrap()
1133+
.splice_workspace(&cargo());
1134+
1135+
assert!(workspace_manifest.is_err());
1136+
1137+
// Ensure both the missing manifests are mentioned in the error string
1138+
let err_str = format!("{:?}", &workspace_manifest);
1139+
assert!(
1140+
err_str.contains("Missing root workspace manifest")
1141+
&& err_str.contains("//root_pkg:Cargo.toml")
1142+
);
1143+
}
1144+
10651145
#[test]
10661146
fn splice_workspace_report_external_workspace_members() {
10671147
let (mut splicing_manifest, _cache_dir) = mock_splicing_manifest_with_workspace();
@@ -1101,7 +1181,7 @@ mod test {
11011181
let workspace_manifest =
11021182
Splicer::new(workspace_root.as_ref().to_path_buf(), splicing_manifest)
11031183
.unwrap()
1104-
.splice_workspace();
1184+
.splice_workspace(&cargo());
11051185

11061186
assert!(workspace_manifest.is_err());
11071187

@@ -1124,7 +1204,7 @@ mod test {
11241204
let workspace_manifest =
11251205
Splicer::new(workspace_root.as_ref().to_path_buf(), splicing_manifest)
11261206
.unwrap()
1127-
.splice_workspace()
1207+
.splice_workspace(&cargo())
11281208
.unwrap();
11291209

11301210
// Ensure metadata is valid
@@ -1153,7 +1233,7 @@ mod test {
11531233
let workspace_manifest =
11541234
Splicer::new(workspace_root.as_ref().to_path_buf(), splicing_manifest)
11551235
.unwrap()
1156-
.splice_workspace()
1236+
.splice_workspace(&cargo())
11571237
.unwrap();
11581238

11591239
// Check the default resolver version
@@ -1202,7 +1282,7 @@ mod test {
12021282
let workspace_manifest =
12031283
Splicer::new(workspace_root.as_ref().to_path_buf(), splicing_manifest)
12041284
.unwrap()
1205-
.splice_workspace()
1285+
.splice_workspace(&cargo())
12061286
.unwrap();
12071287

12081288
// Check the specified resolver version
@@ -1253,7 +1333,7 @@ mod test {
12531333
let workspace_root = tempfile::tempdir().unwrap();
12541334
Splicer::new(workspace_root.as_ref().to_path_buf(), splicing_manifest)
12551335
.unwrap()
1256-
.splice_workspace()
1336+
.splice_workspace(&cargo())
12571337
.unwrap();
12581338

12591339
let cargo_config = workspace_root.as_ref().join(".cargo").join("config.toml");
@@ -1286,7 +1366,7 @@ mod test {
12861366
let workspace_root = tempfile::tempdir().unwrap();
12871367
Splicer::new(workspace_root.as_ref().to_path_buf(), splicing_manifest)
12881368
.unwrap()
1289-
.splice_workspace()
1369+
.splice_workspace(&cargo())
12901370
.unwrap();
12911371

12921372
let cargo_config = workspace_root.as_ref().join(".cargo").join("config.toml");
@@ -1313,7 +1393,7 @@ mod test {
13131393
let workspace_root = temp_dir.as_ref().join("workspace_root");
13141394
let splicing_result = Splicer::new(workspace_root.clone(), splicing_manifest)
13151395
.unwrap()
1316-
.splice_workspace();
1396+
.splice_workspace(&cargo());
13171397

13181398
// Ensure cargo config files in parent directories lead to errors
13191399
assert!(splicing_result.is_err());

0 commit comments

Comments
 (0)