Skip to content

Commit 7b13469

Browse files
committed
Auto merge of #6772 - Aaron1011:feature/final-pub-priv, r=ehuss
Implement the 'frontend' of public-private dependencies This is part of rust-lang/rust#44663 This implements the 'frontend' portion of [RFC 1977](https://github.com/rust-lang/rfcs/blob/master/text/1977-public-private-dependencies.md). Once PRs rust-lang/rust#59335 and rust-lang/crates.io#1685 are merged, it will be possible to test the full public-private dependency feature: marking a dependency a public, seeing exported_private_dependencies warnings from rustc, and seeing pub-dep-reachability errors from Cargo. Everything in this commit should be fully backwards-compatible - users who don't enable the 'public-dependency' cargo feature won't notice any changes. Note that this commit does *not* implement the remaining two features of the RFC: * Choosing smallest versions when 'cargo publish' is run * Turning exported_private_dependencies warnings into hard errors when 'cargo publish' is run The former is a major change to Cargo's behavior, and should be done in a separate PR with some kind of rollout plan. The latter is described by the RFC as being enabled at 'some point in the future'. This can be done via a follow-up PR.
2 parents 0c891a0 + f4aac94 commit 7b13469

File tree

15 files changed

+335
-15
lines changed

15 files changed

+335
-15
lines changed

src/cargo/core/compiler/build_context/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,11 @@ impl<'a, 'cfg> BuildContext<'a, 'cfg> {
9393
.extern_crate_name(unit.pkg.package_id(), dep.pkg.package_id(), dep.target)
9494
}
9595

96+
pub fn is_public_dependency(&self, unit: &Unit<'a>, dep: &Unit<'a>) -> bool {
97+
self.resolve
98+
.is_public_dep(unit.pkg.package_id(), dep.pkg.package_id())
99+
}
100+
96101
/// Whether a dependency should be compiled for the host or target platform,
97102
/// specified by `Kind`.
98103
pub fn dep_platform_activated(&self, dep: &Dependency, kind: Kind) -> bool {

src/cargo/core/compiler/fingerprint.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -315,11 +315,13 @@ pub fn prepare_target<'a, 'cfg>(
315315
/// A compilation unit dependency has a fingerprint that is comprised of:
316316
/// * its package ID
317317
/// * its extern crate name
318+
/// * its public/private status
318319
/// * its calculated fingerprint for the dependency
319320
#[derive(Clone)]
320321
struct DepFingerprint {
321322
pkg_id: u64,
322323
name: String,
324+
public: bool,
323325
fingerprint: Arc<Fingerprint>,
324326
}
325327

@@ -420,7 +422,7 @@ impl Serialize for DepFingerprint {
420422
where
421423
S: ser::Serializer,
422424
{
423-
(&self.pkg_id, &self.name, &self.fingerprint.hash()).serialize(ser)
425+
(&self.pkg_id, &self.name, &self.public, &self.fingerprint.hash()).serialize(ser)
424426
}
425427
}
426428

@@ -429,10 +431,11 @@ impl<'de> Deserialize<'de> for DepFingerprint {
429431
where
430432
D: de::Deserializer<'de>,
431433
{
432-
let (pkg_id, name, hash) = <(u64, String, u64)>::deserialize(d)?;
434+
let (pkg_id, name, public, hash) = <(u64, String, bool, u64)>::deserialize(d)?;
433435
Ok(DepFingerprint {
434436
pkg_id,
435437
name,
438+
public,
436439
fingerprint: Arc::new(Fingerprint {
437440
memoized_hash: Mutex::new(Some(hash)),
438441
..Fingerprint::new()
@@ -845,11 +848,13 @@ impl hash::Hash for Fingerprint {
845848
for DepFingerprint {
846849
pkg_id,
847850
name,
851+
public,
848852
fingerprint,
849853
} in deps
850854
{
851855
pkg_id.hash(h);
852856
name.hash(h);
857+
public.hash(h);
853858
// use memoized dep hashes to avoid exponential blowup
854859
h.write_u64(Fingerprint::hash(fingerprint));
855860
}
@@ -895,6 +900,7 @@ impl DepFingerprint {
895900
) -> CargoResult<DepFingerprint> {
896901
let fingerprint = calculate(cx, dep)?;
897902
let name = cx.bcx.extern_crate_name(parent, dep)?;
903+
let public = cx.bcx.is_public_dependency(parent, dep);
898904

899905
// We need to be careful about what we hash here. We have a goal of
900906
// supporting renaming a project directory and not rebuilding
@@ -915,6 +921,7 @@ impl DepFingerprint {
915921
Ok(DepFingerprint {
916922
pkg_id,
917923
name,
924+
public,
918925
fingerprint,
919926
})
920927
}

src/cargo/core/compiler/mod.rs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use log::debug;
2323
use same_file::is_same_file;
2424
use serde::Serialize;
2525

26+
use crate::core::Feature;
2627
pub use self::build_config::{BuildConfig, CompileMode, MessageFormat};
2728
pub use self::build_context::{BuildContext, FileFlavor, TargetConfig, TargetInfo};
2829
use self::build_plan::BuildPlan;
@@ -966,22 +967,32 @@ fn build_deps_args<'a, 'cfg>(
966967
}
967968
}
968969

970+
let mut unstable_opts = false;
971+
969972
for dep in dep_targets {
970973
if dep.mode.is_run_custom_build() {
971974
cmd.env("OUT_DIR", &cx.files().build_script_out_dir(&dep));
972975
}
973976
if dep.target.linkable() && !dep.mode.is_doc() {
974-
link_to(cmd, cx, unit, &dep)?;
977+
link_to(cmd, cx, unit, &dep, &mut unstable_opts)?;
975978
}
976979
}
977980

981+
// This will only be set if we're already usign a feature
982+
// requiring nightly rust
983+
if unstable_opts {
984+
cmd.arg("-Z").arg("unstable-options");
985+
}
986+
987+
978988
return Ok(());
979989

980990
fn link_to<'a, 'cfg>(
981991
cmd: &mut ProcessBuilder,
982992
cx: &mut Context<'a, 'cfg>,
983993
current: &Unit<'a>,
984994
dep: &Unit<'a>,
995+
need_unstable_opts: &mut bool
985996
) -> CargoResult<()> {
986997
let bcx = cx.bcx;
987998
for output in cx.outputs(dep)?.iter() {
@@ -995,7 +1006,17 @@ fn build_deps_args<'a, 'cfg>(
9951006
v.push(cx.files().out_dir(dep));
9961007
v.push(&path::MAIN_SEPARATOR.to_string());
9971008
v.push(&output.path.file_name().unwrap());
998-
cmd.arg("--extern").arg(&v);
1009+
1010+
if current.pkg.manifest().features().require(Feature::public_dependency()).is_ok() &&
1011+
!bcx.is_public_dependency(current, dep) {
1012+
1013+
cmd.arg("--extern-private");
1014+
*need_unstable_opts = true;
1015+
} else {
1016+
cmd.arg("--extern");
1017+
}
1018+
1019+
cmd.arg(&v);
9991020
}
10001021
Ok(())
10011022
}

src/cargo/core/dependency.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,10 @@ impl Dependency {
301301

302302
/// Sets whether the dependency is public.
303303
pub fn set_public(&mut self, public: bool) -> &mut Dependency {
304+
if public {
305+
// Setting 'public' only makes sense for normal dependencies
306+
assert_eq!(self.kind(), Kind::Normal);
307+
}
304308
Rc::make_mut(&mut self.inner).public = public;
305309
self
306310
}

src/cargo/core/features.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,9 @@ features! {
199199

200200
// Declarative build scripts.
201201
[unstable] metabuild: bool,
202+
203+
// Specifying the 'public' attribute on dependencies
204+
[unstable] public_dependency: bool,
202205
}
203206
}
204207

src/cargo/core/resolver/resolve.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::iter::FromIterator;
66
use url::Url;
77

88
use crate::core::{Dependency, PackageId, PackageIdSpec, Summary, Target};
9+
use crate::core::dependency::Kind;
910
use crate::util::errors::CargoResult;
1011
use crate::util::Graph;
1112

@@ -29,6 +30,8 @@ pub struct Resolve {
2930
checksums: HashMap<PackageId, Option<String>>,
3031
metadata: Metadata,
3132
unused_patches: Vec<PackageId>,
33+
// A map from packages to a set of their public dependencies
34+
public_dependencies: HashMap<PackageId, HashSet<PackageId>>,
3235
}
3336

3437
impl Resolve {
@@ -41,6 +44,21 @@ impl Resolve {
4144
unused_patches: Vec<PackageId>,
4245
) -> Resolve {
4346
let reverse_replacements = replacements.iter().map(|(&p, &r)| (r, p)).collect();
47+
let public_dependencies = graph.iter().map(|p| {
48+
let public_deps = graph.edges(p).flat_map(|(dep_package, deps)| {
49+
let id_opt: Option<PackageId> = deps.iter().find(|d| d.kind() == Kind::Normal).and_then(|d| {
50+
if d.is_public() {
51+
Some(dep_package.clone())
52+
} else {
53+
None
54+
}
55+
});
56+
id_opt
57+
}).collect::<HashSet<PackageId>>();
58+
59+
(p.clone(), public_deps)
60+
}).collect();
61+
4462
Resolve {
4563
graph,
4664
replacements,
@@ -50,6 +68,7 @@ impl Resolve {
5068
unused_patches,
5169
empty_features: HashSet::new(),
5270
reverse_replacements,
71+
public_dependencies
5372
}
5473
}
5574

@@ -197,6 +216,12 @@ unable to verify that `{0}` is the same as when the lockfile was generated
197216
self.features.get(&pkg).unwrap_or(&self.empty_features)
198217
}
199218

219+
pub fn is_public_dep(&self, pkg: PackageId, dep: PackageId) -> bool {
220+
self.public_dependencies.get(&pkg)
221+
.map(|public_deps| public_deps.contains(&dep))
222+
.unwrap_or_else(|| panic!("Unknown dependency {:?} for package {:?}", dep, pkg))
223+
}
224+
200225
pub fn features_sorted(&self, pkg: PackageId) -> Vec<&str> {
201226
let mut v = Vec::from_iter(self.features(pkg).iter().map(|s| s.as_ref()));
202227
v.sort_unstable();

src/cargo/core/workspace.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use glob::glob;
88
use log::debug;
99
use url::Url;
1010

11+
use crate::core::features::Features;
1112
use crate::core::profiles::Profiles;
1213
use crate::core::registry::PackageRegistry;
1314
use crate::core::{Dependency, PackageIdSpec, PackageId};
@@ -540,17 +541,21 @@ impl<'cfg> Workspace<'cfg> {
540541
Ok(())
541542
}
542543

544+
pub fn features(&self) -> &Features {
545+
match self.root_maybe() {
546+
MaybePackage::Package(p) => p.manifest().features(),
547+
MaybePackage::Virtual(vm) => vm.features(),
548+
}
549+
}
550+
543551
/// Validates a workspace, ensuring that a number of invariants are upheld:
544552
///
545553
/// 1. A workspace only has one root.
546554
/// 2. All workspace members agree on this one root as the root.
547555
/// 3. The current crate is a member of this workspace.
548556
fn validate(&mut self) -> CargoResult<()> {
549557
// Validate config profiles only once per workspace.
550-
let features = match self.root_maybe() {
551-
MaybePackage::Package(p) => p.manifest().features(),
552-
MaybePackage::Virtual(vm) => vm.features(),
553-
};
558+
let features = self.features();
554559
let mut warnings = Vec::new();
555560
self.config.profiles()?.validate(features, &mut warnings)?;
556561
for warning in warnings {

src/cargo/ops/cargo_package.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use crate::core::resolver::Method;
1818
use crate::core::{
1919
Package, PackageId, PackageIdSpec, PackageSet, Resolve, Source, SourceId, Verbosity, Workspace,
2020
};
21+
use crate::core::Feature;
2122
use crate::ops;
2223
use crate::sources::PathSource;
2324
use crate::util::errors::{CargoResult, CargoResultExt};
@@ -590,6 +591,14 @@ fn run_verify(ws: &Workspace<'_>, tar: &FileLock, opts: &PackageOpts<'_>) -> Car
590591
let pkg_fingerprint = hash_all(&dst)?;
591592
let ws = Workspace::ephemeral(new_pkg, config, None, true)?;
592593

594+
let rustc_args = if pkg.manifest().features().require(Feature::public_dependency()).is_ok() {
595+
// FIXME: Turn this on at some point in the future
596+
//Some(vec!["-D exported_private_dependencies".to_string()])
597+
None
598+
} else {
599+
None
600+
};
601+
593602
let exec: Arc<dyn Executor> = Arc::new(DefaultExecutor);
594603
ops::compile_ws(
595604
&ws,
@@ -604,7 +613,7 @@ fn run_verify(ws: &Workspace<'_>, tar: &FileLock, opts: &PackageOpts<'_>) -> Car
604613
required_features_filterable: true,
605614
},
606615
target_rustdoc_args: None,
607-
target_rustc_args: None,
616+
target_rustc_args: rustc_args,
608617
local_rustdoc_args: None,
609618
export_dir: None,
610619
},

src/cargo/ops/resolve.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::rc::Rc;
33

44
use log::{debug, trace};
55

6+
use crate::core::Feature;
67
use crate::core::registry::PackageRegistry;
78
use crate::core::resolver::{self, Method, Resolve};
89
use crate::core::{PackageId, PackageIdSpec, PackageSet, Source, SourceId, Workspace};
@@ -331,7 +332,7 @@ pub fn resolve_with_previous<'cfg>(
331332
registry,
332333
&try_to_use,
333334
Some(ws.config()),
334-
false, // TODO: use "public and private dependencies" feature flag
335+
ws.features().require(Feature::public_dependency()).is_ok(),
335336
)?;
336337
resolved.register_used_patches(registry.patches());
337338
if register_patches {

src/cargo/sources/registry/mod.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ struct RegistryDependency<'a> {
285285
kind: Option<Cow<'a, str>>,
286286
registry: Option<Cow<'a, str>>,
287287
package: Option<Cow<'a, str>>,
288+
public: Option<bool>
288289
}
289290

290291
impl<'a> RegistryDependency<'a> {
@@ -300,6 +301,7 @@ impl<'a> RegistryDependency<'a> {
300301
kind,
301302
registry,
302303
package,
304+
public
303305
} = self;
304306

305307
let id = if let Some(registry) = &registry {
@@ -324,6 +326,9 @@ impl<'a> RegistryDependency<'a> {
324326
None => None,
325327
};
326328

329+
// All dependencies are private by default
330+
let public = public.unwrap_or(false);
331+
327332
// Unfortunately older versions of cargo and/or the registry ended up
328333
// publishing lots of entries where the features array contained the
329334
// empty feature, "", inside. This confuses the resolution process much
@@ -341,7 +346,8 @@ impl<'a> RegistryDependency<'a> {
341346
.set_default_features(default_features)
342347
.set_features(features)
343348
.set_platform(platform)
344-
.set_kind(kind);
349+
.set_kind(kind)
350+
.set_public(public);
345351

346352
Ok(dep)
347353
}

src/cargo/util/toml/mod.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ pub struct DetailedTomlDependency {
239239
#[serde(rename = "default_features")]
240240
default_features2: Option<bool>,
241241
package: Option<String>,
242+
public: Option<bool>
242243
}
243244

244245
#[derive(Debug, Deserialize, Serialize)]
@@ -1461,6 +1462,16 @@ impl DetailedTomlDependency {
14611462
cx.features.require(Feature::rename_dependency())?;
14621463
dep.set_explicit_name_in_toml(name_in_toml);
14631464
}
1465+
1466+
if let Some(p) = self.public {
1467+
cx.features.require(Feature::public_dependency())?;
1468+
1469+
if dep.kind() != Kind::Normal {
1470+
bail!("'public' specifier can only be used on regular dependencies, not {:?} dependencies", dep.kind());
1471+
}
1472+
1473+
dep.set_public(p);
1474+
}
14641475
Ok(dep)
14651476
}
14661477
}

src/doc/src/reference/unstable.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,3 +264,20 @@ conflicting binaries from another package.
264264
Additionally, a new flag `--no-track` is available to prevent `cargo install`
265265
from writing tracking information in `$CARGO_HOME` about which packages are
266266
installed.
267+
268+
### public-dependency
269+
* Tracking Issue: [#44663](https://github.com/rust-lang/rust/issues/44663)
270+
271+
The 'public-dependency' features allows marking dependencies as 'public'
272+
or 'private'. When this feature is enabled, additional information is passed to rustc to allow
273+
the 'exported_private_dependencies' lint to function properly.
274+
275+
This requires the appropriate key to be set in `cargo-features`:
276+
277+
```toml
278+
cargo-features = ["public-dependency"]
279+
280+
[dependencies]
281+
my_dep = { version = "1.2.3", public = true }
282+
private_dep = "2.0.0" # Will be 'private' by default
283+
```

tests/testsuite/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ mod profile_config;
6767
mod profile_overrides;
6868
mod profile_targets;
6969
mod profiles;
70+
mod pub_priv;
7071
mod publish;
7172
mod publish_lockfile;
7273
mod read_manifest;

0 commit comments

Comments
 (0)