1 use cargo
::sources
::CRATES_IO_REGISTRY
;
2 use cargo
::util
::print_available_packages
;
3 use indexmap
::IndexMap
;
4 use indexmap
::IndexSet
;
6 use cargo
::core
::dependency
::DepKind
;
7 use cargo
::core
::FeatureValue
;
8 use cargo
::ops
::cargo_add
::add
;
9 use cargo
::ops
::cargo_add
::AddOptions
;
10 use cargo
::ops
::cargo_add
::DepOp
;
11 use cargo
::ops
::resolve_ws
;
12 use cargo
::util
::command_prelude
::*;
13 use cargo
::util
::interning
::InternedString
;
14 use cargo
::util
::toml_mut
::manifest
::DepTable
;
15 use cargo
::CargoResult
;
17 pub fn cli() -> Command
{
18 clap
::Command
::new("add")
19 .about("Add dependencies to a Cargo.toml manifest file")
22 cargo add [OPTIONS] <DEP>[@<VERSION>] ...
23 cargo add [OPTIONS] --path <PATH> ...
24 cargo add [OPTIONS] --git <URL> ..."
26 .after_help("Run `cargo help add` for more detailed information.\n")
27 .group(clap
::ArgGroup
::new("selected").multiple(true).required(true))
29 clap
::Arg
::new("crates")
32 .help("Reference to a package to add as a dependency")
34 "Reference to a package to add as a dependency
36 You can reference a package by:
37 - `<name>`, like `cargo add serde` (latest version will be used)
38 - `<name>@<version-req>`, like `cargo add serde@1` or `cargo add serde@=1.0.38`"
41 flag("no-default-features",
42 "Disable the default features"),
43 flag("default-features",
44 "Re-enable the default features")
45 .overrides_with("no-default-features"),
46 clap
::Arg
::new("features")
49 .value_name("FEATURES")
50 .action(ArgAction
::Append
)
51 .help("Space or comma separated list of features to activate"),
53 "Mark the dependency as optional")
54 .long_help("Mark the dependency as optional
56 The package name will be exposed as feature of your crate.")
57 .conflicts_with("dev"),
59 "Mark the dependency as required")
60 .long_help("Mark the dependency as required
62 The package will be removed from your features.")
63 .conflicts_with("dev")
64 .overrides_with("optional"),
65 clap
::Arg
::new("rename")
67 .action(ArgAction
::Set
)
69 .help("Rename the dependency")
70 .long_help("Rename the dependency
73 - Depending on multiple versions of a crate
74 - Depend on crates with the same name from different registries"),
77 .arg_package("Package to modify")
79 .arg_dry_run("Don't actually write the manifest")
80 .next_help_heading("Source")
82 clap
::Arg
::new("path")
84 .action(ArgAction
::Set
)
86 .help("Filesystem path to local crate to add")
88 .conflicts_with("git"),
91 .action(ArgAction
::Set
)
93 .help("Git repository location")
94 .long_help("Git repository location
96 Without any other information, cargo will use latest commit on the main branch.")
98 clap
::Arg
::new("branch")
100 .action(ArgAction
::Set
)
101 .value_name("BRANCH")
102 .help("Git branch to download the crate from")
105 clap
::Arg
::new("tag")
107 .action(ArgAction
::Set
)
109 .help("Git tag to download the crate from")
112 clap
::Arg
::new("rev")
114 .action(ArgAction
::Set
)
116 .help("Git reference to download the crate from")
117 .long_help("Git reference to download the crate from
119 This is the catch all, handling hashes to named references in remote repositories.")
122 clap
::Arg
::new("registry")
124 .action(ArgAction
::Set
)
126 .help("Package registry for this dependency"),
128 .next_help_heading("Section")
131 "Add as development dependency")
132 .long_help("Add as development dependency
134 Dev-dependencies are not used when compiling a package for building, but are used for compiling tests, examples, and benchmarks.
136 These dependencies are not propagated to other packages which depend on this package.")
139 "Add as build dependency")
140 .long_help("Add as build dependency
142 Build-dependencies are the only dependencies available for use by build scripts (`build.rs` files).")
144 clap
::Arg
::new("target")
146 .action(ArgAction
::Set
)
147 .value_name("TARGET")
148 .value_parser(clap
::builder
::NonEmptyStringValueParser
::new())
149 .help("Add as dependency to the given target platform")
153 pub fn exec(config
: &mut Config
, args
: &ArgMatches
) -> CliResult
{
154 let dry_run
= args
.dry_run();
155 let section
= parse_section(args
);
157 let ws
= args
.workspace(config
)?
;
159 if args
.is_present_with_zero_values("package") {
160 print_available_packages(&ws
)?
;
163 let packages
= args
.packages_from_flags()?
;
164 let packages
= packages
.get_packages(&ws
)?
;
165 let spec
= match packages
.len() {
167 return Err(CliError
::new(
169 "no packages selected to modify. Please specify one with `-p <PKGID>`"
176 let names
= packages
.iter().map(|p
| p
.name()).collect
::<Vec
<_
>>();
177 return Err(CliError
::new(
179 "`cargo add` could not determine which package to modify. \
180 Use the `--package` option to specify a package. \n\
181 available packages: {}",
189 let dependencies
= parse_dependencies(config
, args
)?
;
191 let options
= AddOptions
{
201 // Reload the workspace since we've changed dependencies
202 let ws
= args
.workspace(config
)?
;
209 fn parse_dependencies(config
: &Config
, matches
: &ArgMatches
) -> CargoResult
<Vec
<DepOp
>> {
210 let path
= matches
.get_one
::<String
>("path");
211 let git
= matches
.get_one
::<String
>("git");
212 let branch
= matches
.get_one
::<String
>("branch");
213 let rev
= matches
.get_one
::<String
>("rev");
214 let tag
= matches
.get_one
::<String
>("tag");
215 let rename
= matches
.get_one
::<String
>("rename");
216 let registry
= match matches
.registry(config
)?
{
217 Some(reg
) if reg
== CRATES_IO_REGISTRY
=> None
,
220 let default_features
= default_features(matches
);
221 let optional
= optional(matches
);
223 let mut crates
= matches
224 .get_many
::<String
>("crates")
227 .map(|c
| (Some(c
.clone()), None
))
228 .collect
::<IndexMap
<_
, _
>>();
229 let mut infer_crate_name
= false;
230 if crates
.is_empty() {
231 if path
.is_some() || git
.is_some() {
232 crates
.insert(None
, None
);
233 infer_crate_name
= true;
235 unreachable
!("clap should ensure we have some source selected");
238 for feature
in matches
239 .get_many
::<String
>("features")
243 .flat_map(parse_feature
)
245 let parsed_value
= FeatureValue
::new(InternedString
::new(feature
));
247 FeatureValue
::Feature(_
) => {
248 if 1 < crates
.len() {
249 let candidates
= crates
254 c
.as_deref().expect("only none when there is 1"),
258 .collect
::<Vec
<_
>>();
259 anyhow
::bail
!("feature `{feature}` must be qualified by the dependency it's being activated for, like {}", candidates
.join(", "));
263 .expect("always at least one crate")
265 .get_or_insert_with(IndexSet
::new
)
266 .insert(feature
.to_owned());
268 FeatureValue
::Dep { .. }
=> {
269 anyhow
::bail
!("feature `{feature}` is not allowed to use explicit `dep:` syntax",)
271 FeatureValue
::DepFeature
{
276 if infer_crate_name
{
277 anyhow
::bail
!("`{feature}` is unsupported when inferring the crate name, use `{dep_feature}`");
279 if dep_feature
.contains('
/'
) {
280 anyhow
::bail
!("multiple slashes in feature `{feature}` is not allowed");
282 crates
.get_mut(&Some(dep_name
.as_str().to_owned())).ok_or_else(|| {
283 anyhow
::format_err
!("feature `{dep_feature}` activated for crate `{dep_name}` but the crate wasn't specified")
285 .get_or_insert_with(IndexSet
::new
)
286 .insert(dep_feature
.as_str().to_owned());
291 let mut deps
: Vec
<DepOp
> = Vec
::new();
292 for (crate_spec
, features
) in crates
{
295 rename
: rename
.map(String
::from
),
299 registry
: registry
.clone(),
300 path
: path
.map(String
::from
),
301 git
: git
.map(String
::from
),
302 branch
: branch
.map(String
::from
),
303 rev
: rev
.map(String
::from
),
304 tag
: tag
.map(String
::from
),
309 if deps
.len() > 1 && rename
.is_some() {
310 anyhow
::bail
!("cannot specify multiple crates with `--rename`");
316 fn default_features(matches
: &ArgMatches
) -> Option
<bool
> {
318 matches
.flag("default-features"),
319 matches
.flag("no-default-features"),
323 fn optional(matches
: &ArgMatches
) -> Option
<bool
> {
324 resolve_bool_arg(matches
.flag("optional"), matches
.flag("no-optional"))
327 fn resolve_bool_arg(yes
: bool
, no
: bool
) -> Option
<bool
> {
329 (true, false) => Some(true),
330 (false, true) => Some(false),
331 (false, false) => None
,
332 (_
, _
) => unreachable
!("clap should make this impossible"),
336 fn parse_section(matches
: &ArgMatches
) -> DepTable
{
337 let kind
= if matches
.flag("dev") {
339 } else if matches
.flag("build") {
345 let mut table
= DepTable
::new().set_kind(kind
);
347 if let Some(target
) = matches
.get_one
::<String
>("target") {
348 assert
!(!target
.is_empty(), "Target specification may not be empty");
349 table
= table
.set_target(target
);
355 /// Split feature flag list
356 fn parse_feature(feature
: &str) -> impl Iterator
<Item
= &str> {
357 // Not re-using `CliFeatures` because it uses a BTreeSet and loses user's ordering
360 .flat_map(|s
| s
.split('
,'
))
361 .filter(|s
| !s
.is_empty())