Skip to content

Commit 591993f

Browse files
committed
Allow unpublishable crates to be packaged
1 parent 51e049f commit 591993f

File tree

3 files changed

+122
-74
lines changed

3 files changed

+122
-74
lines changed

src/cargo/ops/cargo_package.rs

Lines changed: 46 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::core::resolver::CliFeatures;
1313
use crate::core::resolver::HasDevUnits;
1414
use crate::core::{Feature, PackageIdSpecQuery, Shell, Verbosity, Workspace};
1515
use crate::core::{Package, PackageId, PackageSet, Resolve, SourceId};
16-
use crate::ops::registry::{infer_registry, RegistryOrIndex};
16+
use crate::ops::registry::infer_registry;
1717
use crate::sources::registry::index::{IndexPackage, RegistryDependency};
1818
use crate::sources::{PathSource, CRATES_IO_REGISTRY};
1919
use crate::util::cache_lock::CacheLockMode;
@@ -33,6 +33,8 @@ use tar::{Archive, Builder, EntryType, Header, HeaderMode};
3333
use tracing::debug;
3434
use unicase::Ascii as UncasedAscii;
3535

36+
use super::RegistryOrIndex;
37+
3638
#[derive(Clone)]
3739
pub struct PackageOpts<'gctx> {
3840
pub gctx: &'gctx GlobalContext,
@@ -173,6 +175,46 @@ fn create_package(
173175
return Ok(dst);
174176
}
175177

178+
/// Determine which registry the packages are for.
179+
///
180+
/// The registry only affects the built packages if there are dependencies within the
181+
/// packages that we're packaging: if we're packaging foo-bin and foo-lib, and foo-bin
182+
/// depends on foo-lib, then the foo-lib entry in foo-bin's lockfile will depend on the
183+
/// registry that we're building packages for.
184+
fn get_registry(
185+
gctx: &GlobalContext,
186+
pkgs: &[&Package],
187+
reg_or_index: Option<RegistryOrIndex>,
188+
) -> CargoResult<SourceId> {
189+
let reg_or_index = match reg_or_index.clone() {
190+
Some(r) => Some(r),
191+
None => infer_registry(pkgs)?,
192+
};
193+
194+
// Validate the registry against the packages' allow-lists.
195+
let reg = reg_or_index
196+
.clone()
197+
.unwrap_or_else(|| RegistryOrIndex::Registry(CRATES_IO_REGISTRY.to_owned()));
198+
if let RegistryOrIndex::Registry(reg_name) = reg {
199+
for pkg in pkgs {
200+
if let Some(allowed) = pkg.publish().as_ref() {
201+
// If allowed is empty (i.e. package.publish is false), we let it slide.
202+
// This allows packaging unpublishable packages (although packaging might
203+
// fail later if the unpublishable package is a dependency of something else).
204+
if !allowed.is_empty() && !allowed.iter().any(|a| a == &reg_name) {
205+
bail!(
206+
"`{}` cannot be packaged.\n\
207+
The registry `{}` is not listed in the `package.publish` value in Cargo.toml.",
208+
pkg.name(),
209+
reg_name
210+
);
211+
}
212+
}
213+
}
214+
}
215+
Ok(ops::registry::get_source_id(gctx, reg_or_index.as_ref())?.replacement)
216+
}
217+
176218
/// Packages an entire workspace.
177219
///
178220
/// Returns the generated package files. If `opts.list` is true, skips
@@ -243,7 +285,9 @@ pub fn package(ws: &Workspace<'_>, opts: &PackageOpts<'_>) -> CargoResult<Vec<Fi
243285
} else {
244286
let tarball = create_package(ws, &pkg, ar_files, local_reg.as_ref())?;
245287
if let Some(local_reg) = local_reg.as_mut() {
246-
local_reg.add_package(ws, &pkg, &tarball)?;
288+
if pkg.publish() != &Some(Vec::new()) {
289+
local_reg.add_package(ws, &pkg, &tarball)?;
290+
}
247291
}
248292
outputs.push((pkg, opts, tarball));
249293
}
@@ -261,43 +305,6 @@ pub fn package(ws: &Workspace<'_>, opts: &PackageOpts<'_>) -> CargoResult<Vec<Fi
261305
Ok(outputs.into_iter().map(|x| x.2).collect())
262306
}
263307

264-
/// Determine which registry the packages are for.
265-
///
266-
/// The registry only affects the built packages if there are dependencies within the
267-
/// packages that we're packaging: if we're packaging foo-bin and foo-lib, and foo-bin
268-
/// depends on foo-lib, then the foo-lib entry in foo-bin's lockfile will depend on the
269-
/// registry that we're building packages for.
270-
fn get_registry(
271-
gctx: &GlobalContext,
272-
pkgs: &[&Package],
273-
reg_or_index: Option<RegistryOrIndex>,
274-
) -> CargoResult<SourceId> {
275-
let reg_or_index = match reg_or_index.clone() {
276-
Some(r) => Some(r),
277-
None => infer_registry(pkgs)?,
278-
};
279-
280-
// Validate the registry against the packages' allow-lists.
281-
let reg = reg_or_index
282-
.clone()
283-
.unwrap_or_else(|| RegistryOrIndex::Registry(CRATES_IO_REGISTRY.to_owned()));
284-
if let RegistryOrIndex::Registry(reg_name) = reg {
285-
for pkg in pkgs {
286-
if let Some(allowed) = pkg.publish().as_ref() {
287-
if !allowed.iter().any(|a| a == &reg_name) {
288-
bail!(
289-
"`{}` cannot be packaged.\n\
290-
The registry `{}` is not listed in the `package.publish` value in Cargo.toml.",
291-
pkg.name(),
292-
reg_name
293-
);
294-
}
295-
}
296-
}
297-
}
298-
Ok(ops::registry::get_source_id(gctx, reg_or_index.as_ref())?.replacement)
299-
}
300-
301308
/// Just the part of the dependency graph that's between the packages we're packaging.
302309
/// (Is the package name a good key? Does it uniquely identify packages?)
303310
#[derive(Clone, Debug, Default)]

src/cargo/ops/registry/mod.rs

Lines changed: 39 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -325,39 +325,49 @@ pub(crate) struct RegistrySourceIds {
325325

326326
/// If this set of packages has an unambiguous publish registry, find it.
327327
pub(crate) fn infer_registry(pkgs: &[&Package]) -> CargoResult<Option<RegistryOrIndex>> {
328-
if pkgs[1..].iter().all(|p| p.publish() == pkgs[0].publish()) {
328+
// Ignore "publish = false" packages while inferring the registry.
329+
let publishable_pkgs: Vec<_> = pkgs
330+
.iter()
331+
.filter(|p| p.publish() != &Some(Vec::new()))
332+
.collect();
333+
334+
if let Some((first, rest)) = publishable_pkgs.split_first() {
329335
// If all packages have the same publish settings, we take that as the default.
330-
match pkgs[0].publish().as_deref() {
331-
Some([unique_pkg_reg]) => {
332-
Ok(Some(RegistryOrIndex::Registry(unique_pkg_reg.to_owned())))
336+
if rest.iter().all(|p| p.publish() == first.publish()) {
337+
match publishable_pkgs[0].publish().as_deref() {
338+
Some([unique_pkg_reg]) => {
339+
Ok(Some(RegistryOrIndex::Registry(unique_pkg_reg.to_owned())))
340+
}
341+
None | Some([]) => Ok(None),
342+
Some(regs) => {
343+
let mut regs: Vec<_> = regs.iter().map(|s| format!("\"{}\"", s)).collect();
344+
regs.sort();
345+
regs.dedup();
346+
// unwrap: the match block ensures that there's more than one reg.
347+
let (last_reg, regs) = regs.split_last().unwrap();
348+
bail!(
349+
"--registry is required to disambiguate between {} or {} registries",
350+
regs.join(", "),
351+
last_reg
352+
)
353+
}
333354
}
334-
None | Some([]) => Ok(None),
335-
Some(regs) => {
336-
let mut regs: Vec<_> = regs.iter().map(|s| format!("\"{}\"", s)).collect();
337-
regs.sort();
338-
regs.dedup();
339-
// unwrap: the match block ensures that there's more than one reg.
340-
let (last_reg, regs) = regs.split_last().unwrap();
341-
bail!(
342-
"--registry is required to disambiguate between {} or {} registries",
343-
regs.join(", "),
344-
last_reg
345-
)
355+
} else {
356+
let common_regs = publishable_pkgs
357+
.iter()
358+
// `None` means "all registries", so drop them instead of including them
359+
// in the intersection.
360+
.filter_map(|p| p.publish().as_deref())
361+
.map(|p| p.iter().collect::<HashSet<_>>())
362+
.reduce(|xs, ys| xs.intersection(&ys).cloned().collect())
363+
.unwrap_or_default();
364+
if common_regs.is_empty() {
365+
bail!("conflicts between `package.publish` fields in the selected packages");
366+
} else {
367+
bail!("--registry is required because not all `package.publish` settings agree",);
346368
}
347369
}
348370
} else {
349-
let common_regs = pkgs
350-
.iter()
351-
// `None` means "all registries", so drop them instead of including them
352-
// in the intersection.
353-
.filter_map(|p| p.publish().as_deref())
354-
.map(|p| p.iter().collect::<HashSet<_>>())
355-
.reduce(|xs, ys| xs.intersection(&ys).cloned().collect())
356-
.unwrap_or_default();
357-
if common_regs.is_empty() {
358-
bail!("conflicts between `package.publish` fields in the selected packages");
359-
} else {
360-
bail!("--registry is required because not all `package.publish` settings agree",);
361-
}
371+
Ok(None)
362372
}
363373
}

tests/testsuite/package.rs

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6227,19 +6227,41 @@ fn registry_inference_ignores_unpublishable() {
62276227

62286228
p.cargo("package -Zpackage-workspace")
62296229
.masquerade_as_nightly_cargo(&["package-workspace"])
6230-
.with_status(101)
62316230
.with_stderr_data(str![[r#"
6232-
[ERROR] conflicts between `package.publish` fields in the selected packages
6231+
[PACKAGING] dep v0.1.0 ([ROOT]/foo/dep)
6232+
[PACKAGED] 3 files, [FILE_SIZE]B ([FILE_SIZE]B compressed)
6233+
[PACKAGING] main v0.0.1 ([ROOT]/foo/main)
6234+
[UPDATING] `alternative` index
6235+
[PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed)
6236+
[VERIFYING] dep v0.1.0 ([ROOT]/foo/dep)
6237+
[COMPILING] dep v0.1.0 ([ROOT]/foo/target/package/dep-0.1.0)
6238+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
6239+
[VERIFYING] main v0.0.1 ([ROOT]/foo/main)
6240+
[UPDATING] `alternative` index
6241+
[UNPACKING] dep v0.1.0 (registry `[ROOT]/foo/target/package/tmp-registry`)
6242+
[COMPILING] dep v0.1.0 (registry `alternative`)
6243+
[COMPILING] main v0.0.1 ([ROOT]/foo/target/package/main-0.0.1)
6244+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
62336245
62346246
"#]])
62356247
.run();
62366248

62376249
p.cargo("package -Zpackage-workspace --registry=alternative")
62386250
.masquerade_as_nightly_cargo(&["package-workspace"])
6239-
.with_status(101)
62406251
.with_stderr_data(str![[r#"
6241-
[ERROR] `main` cannot be packaged.
6242-
The registry `alternative` is not listed in the `package.publish` value in Cargo.toml.
6252+
[PACKAGING] dep v0.1.0 ([ROOT]/foo/dep)
6253+
[PACKAGED] 3 files, [FILE_SIZE]B ([FILE_SIZE]B compressed)
6254+
[PACKAGING] main v0.0.1 ([ROOT]/foo/main)
6255+
[UPDATING] `alternative` index
6256+
[PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed)
6257+
[VERIFYING] dep v0.1.0 ([ROOT]/foo/dep)
6258+
[COMPILING] dep v0.1.0 ([ROOT]/foo/target/package/dep-0.1.0)
6259+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
6260+
[VERIFYING] main v0.0.1 ([ROOT]/foo/main)
6261+
[UPDATING] `alternative` index
6262+
[COMPILING] dep v0.1.0 (registry `alternative`)
6263+
[COMPILING] main v0.0.1 ([ROOT]/foo/target/package/main-0.0.1)
6264+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
62436265
62446266
"#]])
62456267
.run();
@@ -6464,7 +6486,16 @@ fn unpublishable_dependency() {
64646486
.masquerade_as_nightly_cargo(&["package-workspace"])
64656487
.with_status(101)
64666488
.with_stderr_data(str![[r#"
6467-
[ERROR] conflicts between `package.publish` fields in the selected packages
6489+
[PACKAGING] dep v0.1.0 ([ROOT]/foo/dep)
6490+
[PACKAGED] 3 files, [FILE_SIZE]B ([FILE_SIZE]B compressed)
6491+
[PACKAGING] main v0.0.1 ([ROOT]/foo/main)
6492+
[UPDATING] `alternative` index
6493+
[ERROR] failed to prepare local package for uploading
6494+
6495+
Caused by:
6496+
no matching package named `dep` found
6497+
location searched: registry `alternative`
6498+
required by package `main v0.0.1 ([ROOT]/foo/main)`
64686499
64696500
"#]])
64706501
.run();

0 commit comments

Comments
 (0)