Compare commits

...

12 Commits

Author SHA1 Message Date
heisen 3030826d89
Merge 000267ddfc into cf7b3c4cf3 2024-05-03 03:23:23 +02:00
bors cf7b3c4cf3 Auto merge of #13852 - Muscraft:revert-seperating-lints, r=epage
fix(lints): Prevent inheritance from bring exposed for published packages

#13843 demonstrated a regression caused by #13801, where we started to keep `[lints]` and `[workspace.lints]` separate, and not truly resolve `[lints]`. This was a nice thing to have and made it easier to tell when a lint came from a workspace. The downside of doing so is the lints table would not get resolved when vendoring or publishing.

To fix this issue, I reverted the change for keeping `[lints]` and `[workspace.lints]` separate and modified how cargo's linting system figures out where a lint is coming from. Due to this change, we no longer specify that a lint was set by `[workspace.lints]`, only `[lints]`. It is true that a lint level is set by `[lints]` always, as it would've had to specify the lint outright or specify that it was inheriting it, seeing that, I do not think this is a regression in diagnostic quality. I still manage to keep the ability to render a lint's location in the workspace's manifest when running ` analyze_cargo_lints_table`, which I am pleased about.
2024-05-03 00:49:36 +00:00
Scott Schafer 6c2334613c
fix(lints): Prevent inheritance from bring exposed for published packages 2024-05-02 15:37:53 -06:00
bors 97181c67e1 Auto merge of #13851 - weihanglo:macos, r=epage
refactor: remove unnecessary branch for link binary on macOS
2024-05-02 20:11:50 +00:00
Weihang Lo f8aead9338
refactor: remove unnecessary branch for link binary on macOS
The other workaround branch should have covered that.
2024-05-02 16:04:26 -04:00
heisen-li 000267ddfc Adjust chapter content to be guidance rather than practice. 2024-04-19 17:24:58 +08:00
heisen-li 55a381892d Merge branch 'workspace_example' of https://github.com/heisen-li/cargo into workspace_example
* 'workspace_example' of https://github.com/heisen-li/cargo:
  Use specific technical terms.
2024-04-19 12:44:44 +08:00
heisen-li 6128f9cbfc Merge branch 'master' of https://github.com/rust-lang/cargo into workspace_example
* 'master' of https://github.com/rust-lang/cargo: (28 commits)
  fix(toml)!: Disallow source-less dependencies
  fix(credential): trim newlines in token from stdin for credential providers
  fix(schemas): Allow parsing pre-release with X
  test(schemas): Add PartialVersion unit tests
  test(msrv): Migrate most parse tests to unit tests
  fix(msrv): Error, rather than panic, on rust-version 'x'
  test(msrv): Show current parse behavior with X
  show buggy behavior of not trimming newlines in new credential process test
  fix(msrv): Put MSRV-aware resolver behind a config
  test(msrv): Show config on stable
  test(msrv): Prep for config to be added
  test(resolver): Verify some more msrv cases
  test(msrv): Reorganize MSRV tests
  test(msrv): Show regular MSRV resolve case
  test(msrv): Group bad rust-version tests
  feat(install): Including Locking message
  refactor(resolve): Make it easier to customize around the resolve call
  docs: Clarify why we aren't printing Locking in some cases
  feat(fix): Migrate from project to package on Edition 2024
  feat(fix): Report manifest migrations
  ...
2024-04-19 12:44:00 +08:00
heisen-li 2ff01c6aab Modify error description and link 2024-04-19 12:42:32 +08:00
heisen 060f29f33c
Use specific technical terms.
Co-authored-by: Weihang Lo <weihanglo@users.noreply.github.com>
2024-04-19 11:31:56 +08:00
heisen-li 0f806ab0fe Adjust the article location to below the test directory. 2024-04-18 20:32:08 +08:00
heisen-li dd7f6d8b9b Add Create workspace doc 2024-04-17 17:14:10 +08:00
9 changed files with 275 additions and 235 deletions

View File

@ -1286,10 +1286,6 @@ pub trait TestEnv: Sized {
.env_remove("USER") // not set on some rust-lang docker images
.env_remove("XDG_CONFIG_HOME") // see #2345
.env_remove("OUT_DIR"); // see #13204
if cfg!(target_os = "macos") {
// Work-around a bug in macOS 10.15, see `link_or_copy` for details.
self = self.env("__CARGO_COPY_DONT_LINK_DO_NOT_USE_THIS", "1");
}
if cfg!(windows) {
self = self.env("USERPROFILE", paths::home());
}

View File

@ -565,26 +565,18 @@ fn _link_or_copy(src: &Path, dst: &Path) -> Result<()> {
src
};
symlink(src, dst)
} else if env::var_os("__CARGO_COPY_DONT_LINK_DO_NOT_USE_THIS").is_some() {
// This is a work-around for a bug in macOS 10.15. When running on
// APFS, there seems to be a strange race condition with
// Gatekeeper where it will forcefully kill a process launched via
// `cargo run` with SIGKILL. Copying seems to avoid the problem.
// This shouldn't affect anyone except Cargo's test suite because
// it is very rare, and only seems to happen under heavy load and
// rapidly creating lots of executables and running them.
// See https://github.com/rust-lang/cargo/issues/7821 for the
// gory details.
fs::copy(src, dst).map(|_| ())
} else {
if cfg!(target_os = "macos") {
// This is a work-around for a bug on macos. There seems to be a race condition
// with APFS when hard-linking binaries. Gatekeeper does not have signing or
// hash information stored in kernel when running the process. Therefore killing it.
// This problem does not appear when copying files as kernel has time to process it.
// Note that: fs::copy on macos is using CopyOnWrite (syscall fclonefileat) which should be
// as fast as hardlinking.
// See https://github.com/rust-lang/cargo/issues/10060 for the details
// There seems to be a race condition with APFS when hard-linking
// binaries. Gatekeeper does not have signing or hash information
// stored in kernel when running the process. Therefore killing it.
// This problem does not appear when copying files as kernel has
// time to process it. Note that: fs::copy on macos is using
// CopyOnWrite (syscall fclonefileat) which should be as fast as
// hardlinking. See these issues for the details:
//
// * https://github.com/rust-lang/cargo/issues/7821
// * https://github.com/rust-lang/cargo/issues/10060
fs::copy(src, dst).map_or_else(
|e| {
if e.raw_os_error()

View File

@ -1149,25 +1149,11 @@ impl<'gctx> Workspace<'gctx> {
}
pub fn emit_warnings(&self) -> CargoResult<()> {
let ws_lints = self
.root_maybe()
.workspace_config()
.inheritable()
.and_then(|i| i.lints().ok())
.unwrap_or_default();
let ws_cargo_lints = ws_lints
.get("cargo")
.cloned()
.unwrap_or_default()
.into_iter()
.collect();
for (path, maybe_pkg) in &self.packages.packages {
let path = path.join("Cargo.toml");
if let MaybePackage::Package(pkg) = maybe_pkg {
if self.gctx.cli_unstable().cargo_lints {
self.emit_lints(pkg, &path, &ws_cargo_lints)?
self.emit_lints(pkg, &path)?
}
}
let warnings = match maybe_pkg {
@ -1195,12 +1181,7 @@ impl<'gctx> Workspace<'gctx> {
Ok(())
}
pub fn emit_lints(
&self,
pkg: &Package,
path: &Path,
ws_cargo_lints: &manifest::TomlToolLints,
) -> CargoResult<()> {
pub fn emit_lints(&self, pkg: &Package, path: &Path) -> CargoResult<()> {
let mut error_count = 0;
let toml_lints = pkg
.manifest()
@ -1214,16 +1195,6 @@ impl<'gctx> Workspace<'gctx> {
.cloned()
.unwrap_or(manifest::TomlToolLints::default());
// We should only be using workspace lints if the `[lints]` table is
// present in the manifest, and `workspace` is set to `true`
let ws_cargo_lints = pkg
.manifest()
.resolved_toml()
.lints
.as_ref()
.is_some_and(|l| l.workspace)
.then(|| ws_cargo_lints);
let ws_contents = match self.root_maybe() {
MaybePackage::Package(pkg) => pkg.manifest().contents(),
MaybePackage::Virtual(v) => v.contents(),
@ -1238,36 +1209,14 @@ impl<'gctx> Workspace<'gctx> {
pkg,
&path,
&cargo_lints,
ws_cargo_lints,
ws_contents,
ws_document,
self.root_manifest(),
self.gctx,
)?;
check_im_a_teapot(
pkg,
&path,
&cargo_lints,
ws_cargo_lints,
&mut error_count,
self.gctx,
)?;
check_implicit_features(
pkg,
&path,
&cargo_lints,
ws_cargo_lints,
&mut error_count,
self.gctx,
)?;
unused_dependencies(
pkg,
&path,
&cargo_lints,
ws_cargo_lints,
&mut error_count,
self.gctx,
)?;
check_im_a_teapot(pkg, &path, &cargo_lints, &mut error_count, self.gctx)?;
check_implicit_features(pkg, &path, &cargo_lints, &mut error_count, self.gctx)?;
unused_dependencies(pkg, &path, &cargo_lints, &mut error_count, self.gctx)?;
if error_count > 0 {
Err(crate::util::errors::AlreadyPrintedError::new(anyhow!(
"encountered {error_count} errors(s) while running lints"

View File

@ -19,7 +19,6 @@ pub fn analyze_cargo_lints_table(
pkg: &Package,
path: &Path,
pkg_lints: &TomlToolLints,
ws_lints: Option<&TomlToolLints>,
ws_contents: &str,
ws_document: &ImDocument<String>,
ws_path: &Path,
@ -30,20 +29,11 @@ pub fn analyze_cargo_lints_table(
let manifest_path = rel_cwd_manifest_path(path, gctx);
let ws_path = rel_cwd_manifest_path(ws_path, gctx);
let mut unknown_lints = Vec::new();
for (lint_name, specified_in) in pkg_lints
.keys()
.map(|name| (name, SpecifiedIn::Package))
.chain(
ws_lints
.map(|l| l.keys())
.unwrap_or_default()
.map(|name| (name, SpecifiedIn::Workspace)),
)
{
for lint_name in pkg_lints.keys().map(|name| name) {
let Some((name, default_level, edition_lint_opts, feature_gate)) =
find_lint_or_group(lint_name)
else {
unknown_lints.push((lint_name, specified_in));
unknown_lints.push(lint_name);
continue;
};
@ -52,7 +42,6 @@ pub fn analyze_cargo_lints_table(
*default_level,
*edition_lint_opts,
pkg_lints,
ws_lints,
manifest.edition(),
);
@ -66,7 +55,6 @@ pub fn analyze_cargo_lints_table(
verify_feature_enabled(
name,
feature_gate,
reason,
manifest,
&manifest_path,
ws_contents,
@ -83,7 +71,6 @@ pub fn analyze_cargo_lints_table(
manifest,
&manifest_path,
pkg_lints,
ws_lints,
ws_contents,
ws_document,
&ws_path,
@ -130,7 +117,6 @@ fn find_lint_or_group<'a>(
fn verify_feature_enabled(
lint_name: &str,
feature_gate: &Feature,
reason: LintLevelReason,
manifest: &Manifest,
manifest_path: &str,
ws_contents: &str,
@ -151,55 +137,55 @@ fn verify_feature_enabled(
"consider adding `cargo-features = [\"{}\"]` to the top of the manifest",
dash_feature_name
);
let message = match reason {
LintLevelReason::Package => {
let span =
get_span(manifest.document(), &["lints", "cargo", lint_name], false).unwrap();
Level::Error
.title(&title)
.snippet(
Snippet::source(manifest.contents())
.origin(&manifest_path)
.annotation(Level::Error.span(span).label(&label))
.fold(true),
)
.footer(Level::Help.title(&help))
}
LintLevelReason::Workspace => {
let lint_span = get_span(
ws_document,
&["workspace", "lints", "cargo", lint_name],
false,
let message = if let Some(span) =
get_span(manifest.document(), &["lints", "cargo", lint_name], false)
{
Level::Error
.title(&title)
.snippet(
Snippet::source(manifest.contents())
.origin(&manifest_path)
.annotation(Level::Error.span(span).label(&label))
.fold(true),
)
.unwrap();
let inherit_span_key =
get_span(manifest.document(), &["lints", "workspace"], false).unwrap();
let inherit_span_value =
get_span(manifest.document(), &["lints", "workspace"], true).unwrap();
.footer(Level::Help.title(&help))
} else {
let lint_span = get_span(
ws_document,
&["workspace", "lints", "cargo", lint_name],
false,
)
.expect(&format!(
"could not find `cargo::{lint_name}` in `[lints]`, or `[workspace.lints]` "
));
Level::Error
.title(&title)
.snippet(
Snippet::source(ws_contents)
.origin(&ws_path)
.annotation(Level::Error.span(lint_span).label(&label))
.fold(true),
)
.footer(
Level::Note.title(&second_title).snippet(
Snippet::source(manifest.contents())
.origin(&manifest_path)
.annotation(
Level::Note
.span(inherit_span_key.start..inherit_span_value.end),
)
.fold(true),
),
)
.footer(Level::Help.title(&help))
}
_ => unreachable!("LintLevelReason should be one that is user specified"),
let inherited_note = if let (Some(inherit_span_key), Some(inherit_span_value)) = (
get_span(manifest.document(), &["lints", "workspace"], false),
get_span(manifest.document(), &["lints", "workspace"], true),
) {
Level::Note.title(&second_title).snippet(
Snippet::source(manifest.contents())
.origin(&manifest_path)
.annotation(
Level::Note.span(inherit_span_key.start..inherit_span_value.end),
)
.fold(true),
)
} else {
Level::Note.title(&second_title)
};
Level::Error
.title(&title)
.snippet(
Snippet::source(ws_contents)
.origin(&ws_path)
.annotation(Level::Error.span(lint_span).label(&label))
.fold(true),
)
.footer(inherited_note)
.footer(Level::Help.title(&help))
};
*error_count += 1;
@ -287,7 +273,6 @@ impl Lint {
pub fn level(
&self,
pkg_lints: &TomlToolLints,
ws_lints: Option<&TomlToolLints>,
edition: Edition,
unstable_features: &Features,
) -> (LintLevel, LintLevelReason) {
@ -310,7 +295,6 @@ impl Lint {
g.default_level,
g.edition_lint_opts,
pkg_lints,
ws_lints,
edition,
),
)
@ -322,7 +306,6 @@ impl Lint {
self.default_level,
self.edition_lint_opts,
pkg_lints,
ws_lints,
edition,
),
)))
@ -378,7 +361,6 @@ pub enum LintLevelReason {
Default,
Edition(Edition),
Package,
Workspace,
}
impl Display for LintLevelReason {
@ -387,7 +369,6 @@ impl Display for LintLevelReason {
LintLevelReason::Default => write!(f, "by default"),
LintLevelReason::Edition(edition) => write!(f, "in edition {}", edition),
LintLevelReason::Package => write!(f, "in `[lints]`"),
LintLevelReason::Workspace => write!(f, "in `[workspace.lints]`"),
}
}
}
@ -398,22 +379,15 @@ impl LintLevelReason {
LintLevelReason::Default => false,
LintLevelReason::Edition(_) => false,
LintLevelReason::Package => true,
LintLevelReason::Workspace => true,
}
}
}
enum SpecifiedIn {
Package,
Workspace,
}
fn level_priority(
name: &str,
default_level: LintLevel,
edition_lint_opts: Option<(Edition, LintLevel)>,
pkg_lints: &TomlToolLints,
ws_lints: Option<&TomlToolLints>,
edition: Edition,
) -> (LintLevel, LintLevelReason, i8) {
let (unspecified_level, reason) = if let Some(level) = edition_lint_opts
@ -436,12 +410,6 @@ fn level_priority(
LintLevelReason::Package,
defined_level.priority(),
)
} else if let Some(defined_level) = ws_lints.and_then(|l| l.get(name)) {
(
defined_level.level().into(),
LintLevelReason::Workspace,
defined_level.priority(),
)
} else {
(unspecified_level, reason, 0)
}
@ -460,17 +428,12 @@ pub fn check_im_a_teapot(
pkg: &Package,
path: &Path,
pkg_lints: &TomlToolLints,
ws_lints: Option<&TomlToolLints>,
error_count: &mut usize,
gctx: &GlobalContext,
) -> CargoResult<()> {
let manifest = pkg.manifest();
let (lint_level, reason) = IM_A_TEAPOT.level(
pkg_lints,
ws_lints,
manifest.edition(),
manifest.unstable_features(),
);
let (lint_level, reason) =
IM_A_TEAPOT.level(pkg_lints, manifest.edition(), manifest.unstable_features());
if lint_level == LintLevel::Allow {
return Ok(());
@ -534,7 +497,6 @@ pub fn check_implicit_features(
pkg: &Package,
path: &Path,
pkg_lints: &TomlToolLints,
ws_lints: Option<&TomlToolLints>,
error_count: &mut usize,
gctx: &GlobalContext,
) -> CargoResult<()> {
@ -547,7 +509,7 @@ pub fn check_implicit_features(
}
let (lint_level, reason) =
IMPLICIT_FEATURES.level(pkg_lints, ws_lints, edition, manifest.unstable_features());
IMPLICIT_FEATURES.level(pkg_lints, edition, manifest.unstable_features());
if lint_level == LintLevel::Allow {
return Ok(());
}
@ -611,30 +573,25 @@ const UNKNOWN_LINTS: Lint = Lint {
};
fn output_unknown_lints(
unknown_lints: Vec<(&String, SpecifiedIn)>,
unknown_lints: Vec<&String>,
manifest: &Manifest,
manifest_path: &str,
pkg_lints: &TomlToolLints,
ws_lints: Option<&TomlToolLints>,
ws_contents: &str,
ws_document: &ImDocument<String>,
ws_path: &str,
error_count: &mut usize,
gctx: &GlobalContext,
) -> CargoResult<()> {
let (lint_level, reason) = UNKNOWN_LINTS.level(
pkg_lints,
ws_lints,
manifest.edition(),
manifest.unstable_features(),
);
let (lint_level, reason) =
UNKNOWN_LINTS.level(pkg_lints, manifest.edition(), manifest.unstable_features());
if lint_level == LintLevel::Allow {
return Ok(());
}
let level = lint_level.to_diagnostic_level();
let mut emitted_source = None;
for (lint_name, specified_in) in unknown_lints {
for lint_name in unknown_lints {
if lint_level == LintLevel::Forbid || lint_level == LintLevel::Deny {
*error_count += 1;
}
@ -651,50 +608,50 @@ fn output_unknown_lints(
let help =
matching.map(|(name, kind)| format!("there is a {kind} with a similar name: `{name}`"));
let mut message = match specified_in {
SpecifiedIn::Package => {
let span =
get_span(manifest.document(), &["lints", "cargo", lint_name], false).unwrap();
let mut message = if let Some(span) =
get_span(manifest.document(), &["lints", "cargo", lint_name], false)
{
level.title(&title).snippet(
Snippet::source(manifest.contents())
.origin(&manifest_path)
.annotation(Level::Error.span(span))
.fold(true),
)
} else {
let lint_span = get_span(
ws_document,
&["workspace", "lints", "cargo", lint_name],
false,
)
.expect(&format!(
"could not find `cargo::{lint_name}` in `[lints]`, or `[workspace.lints]` "
));
level.title(&title).snippet(
let inherited_note = if let (Some(inherit_span_key), Some(inherit_span_value)) = (
get_span(manifest.document(), &["lints", "workspace"], false),
get_span(manifest.document(), &["lints", "workspace"], true),
) {
Level::Note.title(&second_title).snippet(
Snippet::source(manifest.contents())
.origin(&manifest_path)
.annotation(Level::Error.span(span))
.annotation(
Level::Note.span(inherit_span_key.start..inherit_span_value.end),
)
.fold(true),
)
}
SpecifiedIn::Workspace => {
let lint_span = get_span(
ws_document,
&["workspace", "lints", "cargo", lint_name],
false,
)
.unwrap();
let inherit_span_key =
get_span(manifest.document(), &["lints", "workspace"], false).unwrap();
let inherit_span_value =
get_span(manifest.document(), &["lints", "workspace"], true).unwrap();
} else {
Level::Note.title(&second_title)
};
level
.title(&title)
.snippet(
Snippet::source(ws_contents)
.origin(&ws_path)
.annotation(Level::Error.span(lint_span))
.fold(true),
)
.footer(
Level::Note.title(&second_title).snippet(
Snippet::source(manifest.contents())
.origin(&manifest_path)
.annotation(
Level::Note
.span(inherit_span_key.start..inherit_span_value.end),
)
.fold(true),
),
)
}
level
.title(&title)
.snippet(
Snippet::source(ws_contents)
.origin(&ws_path)
.annotation(Level::Error.span(lint_span))
.fold(true),
)
.footer(inherited_note)
};
if emitted_source.is_none() {
@ -728,7 +685,6 @@ pub fn unused_dependencies(
pkg: &Package,
path: &Path,
pkg_lints: &TomlToolLints,
ws_lints: Option<&TomlToolLints>,
error_count: &mut usize,
gctx: &GlobalContext,
) -> CargoResult<()> {
@ -739,12 +695,8 @@ pub fn unused_dependencies(
return Ok(());
}
let (lint_level, reason) = UNUSED_OPTIONAL_DEPENDENCY.level(
pkg_lints,
ws_lints,
edition,
manifest.unstable_features(),
);
let (lint_level, reason) =
UNUSED_OPTIONAL_DEPENDENCY.level(pkg_lints, edition, manifest.unstable_features());
if lint_level == LintLevel::Allow {
return Ok(());
}

View File

@ -492,7 +492,15 @@ fn resolve_toml(
}
resolved_toml.target = (!resolved_target.is_empty()).then_some(resolved_target);
resolved_toml.lints = original_toml.lints.clone();
let resolved_lints = original_toml
.lints
.clone()
.map(|value| lints_inherit_with(value, || inherit()?.lints()))
.transpose()?;
resolved_toml.lints = resolved_lints.map(|lints| manifest::InheritableLints {
workspace: false,
lints,
});
resolved_toml.badges = original_toml.badges.clone();
} else {
@ -1336,18 +1344,18 @@ fn to_real_manifest(
}
}
let resolved_lints = resolved_toml
.lints
.clone()
.map(|value| {
lints_inherit_with(value, || {
load_inheritable_fields(gctx, manifest_file, &workspace_config)?.lints()
})
})
.transpose()?;
verify_lints(resolved_lints.as_ref(), gctx, warnings)?;
let rustflags = lints_to_rustflags(&resolved_lints.unwrap_or_default());
verify_lints(
resolved_toml.resolved_lints().expect("previously resolved"),
gctx,
warnings,
)?;
let default = manifest::TomlLints::default();
let rustflags = lints_to_rustflags(
resolved_toml
.resolved_lints()
.expect("previously resolved")
.unwrap_or(&default),
);
let metadata = ManifestMetadata {
description: resolved_package

View File

@ -14,6 +14,7 @@
* [Package Layout](guide/project-layout.md)
* [Cargo.toml vs Cargo.lock](guide/cargo-toml-vs-cargo-lock.md)
* [Tests](guide/tests.md)
* [Creating a Workspace](guide/creating-a-new-workspace.md)
* [Continuous Integration](guide/continuous-integration.md)
* [Cargo Home](guide/cargo-home.md)
* [Build Cache](guide/build-cache.md)

View File

@ -0,0 +1,141 @@
# Creating a New Workspace
A [workspace][def-workspace] is a collection of one or more packages,
called workspace members, that are managed together.
In this chapter, we will create a workspace `new_workspace` containing
binary member `foo` and library member `bar`.
As mentioned in [`[workspace]` section][workspace-section], the workspace must
have at least one member, either the [root package] or a [virtual manifest].
Next we create a workspace containing [root package].
For convenience, you can first create a package using the command `cargo new new_workspace`.
Then add the `[workspace]` section to the `Cargo.toml` file in the root directory
to make it a manifest of the workspace:
```toml
# [new_workspace]/Cargo.toml
[workspace]
[package]
name = "new_workspace"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
```
Then, continue adding members `foo` and `bar` to the workspace:
```console
$ cd new_workspace
$ cargo new foo
$ cargo new bar --lib
```
Cargo will automatically add members to `Cargo.toml`
At this point, the workspace will contain three members: `foo` and `bar` and
the default member `new_workspace`.
```toml
# [new_workspace]/Cargo.toml
[workspace]
members = [ "bar", "foo" ]
[package]
name = "new_workspace"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
```
The package at this point contains the following files:
```console
$ cd new_workspace
$ tree .
.
├── bar
│ ├── Cargo.toml
│ └── src
│ └── lib.rs
├── Cargo.toml
├── foo
│ ├── Cargo.toml
│ └── src
│ └── main.rs
└── src
└── main.rs
5 directories, 6 files
```
Let's move on and create a virtual workspace.
In the another `new_workspace` empty directory, create a new `Cargo.toml` file and
add the `[workspace]` section:
```toml
# [new_workspace]/Cargo.toml
[workspace]
```
If using a virtual workspace, then the version of [resolver] needs to be specified
in the table (if not, the default version of resolver for a workspace is `1`,
even if the default resolver version for workspace members is `2`), for example:
```toml
# [new_workspace]/Cargo.toml
[workspace]
resolver = "2"
```
Likewise, you can then use the `cargo new <package>` command to create
binary member `foo` and library member `bar`.
```toml
# [new_workspace]/Cargo.toml
[workspace]
resolver = "2"
members = [ "bar","foo"]
```
The package at this point contains the following files:
```console
$ cd new_workspace
$ tree .
.
├── bar
│ ├── Cargo.toml
│ └── src
│ └── lib.rs
├── Cargo.toml
└── foo
├── Cargo.toml
└── src
└── main.rs
4 directories, 5 files
```
Up to this point, we have a workspace with two members.
Whenever you run `cargo build` under the workspace root directory, Cargo builds
all member at once.
Instead of building the entire workspace, you could use the `--package`/`-p` flag
to select certain packages.
For example, `cargo build -p foo` will build only `foo` package.
[workspace-section]: ../reference/workspaces.md#the-workspace-section
[root package]: ../reference/workspaces.md#root-package
[virtual manifest]: ../reference/workspaces.md#virtual-workspace
[def-workspace]: ../appendix/glossary.md#workspace '"workspace" (glossary entry)'
[resolver]: ../reference/resolver.md

View File

@ -5,6 +5,7 @@ develop Rust packages.
* [Why Cargo Exists](why-cargo-exists.md)
* [Creating a New Package](creating-a-new-project.md)
* [Creating a Workspace](creating-a-new-workspace.md)
* [Working on an Existing Cargo Package](working-on-an-existing-project.md)
* [Dependencies](dependencies.md)
* [Package Layout](project-layout.md)

View File

@ -975,7 +975,7 @@ error: `im_a_teapot` is specified
13 | im-a-teapot = true
| ^^^^^^^^^^^^^^^^^^
|
= note: `cargo::im_a_teapot` is set to `forbid` in `[workspace.lints]`
= note: `cargo::im_a_teapot` is set to `forbid` in `[lints]`
",
)
.run();