Repository: rust-lang/cargo (rust)

Issue:
cargo rustc `--crate-type=lib,cdylib` only uplifts the first artifact kind
### Problem

When manually specifying the crate-type on the command line via `--crate-type=type1,type2` , the artifacts are correctly built (visible in the `deps` directory with a hash), but only the first `crate-type` artifact is uplifted to `target/$profile`.

### Steps

Create a library with 
```toml
[lib]
crate-type = [ "lib", "cdylib" ]
```

When building with `cargo build` or `cargo rustc` `target/$profile` contains both the rlib and the cdylib artifacts.

When using cargo rustc `--crate-type=cdylib,lib` instead, only the **first** artifact kind will be uplifted from the deps directory. (When reproducing, remember to run cargo clean after each step to delete the previous artifacts)

```shell
ll target/debug/deps/
total 20728
-rw-r--r--. 1 jschwender jschwender 17622720 Sep  5 11:10 librust_lib_requiring_envvar-80fb9f595b956614.a
-rwxr-xr-x. 2 jschwender jschwender  3594440 Sep  5 11:10 librust_lib_requiring_envvar-80fb9f595b956614.so
-rw-r--r--. 1 jschwender jschwender      397 Sep  5 11:10 rust_lib_requiring_envvar-80fb9f595b956614.d
```

```shell
ll target/debug/
total 3516
drwxr-xr-x. 1 jschwender jschwender     168 Sep  5 11:10 build
drwxr-xr-x. 1 jschwender jschwender     278 Sep  5 11:10 deps
drwxr-xr-x. 1 jschwender jschwender       0 Sep  5 11:10 examples
drwxr-xr-x. 1 jschwender jschwender     142 Sep  5 11:10 incremental
-rw-r--r--. 1 jschwender jschwender     216 Sep  5 11:10 librust_lib_requiring_envvar.d
-rwxr-xr-x. 2 jschwender jschwender 3594440 Sep  5 11:10 librust_lib_requiring_envvar.so
````

The order of the 


### Possible Solution(s)

_No response_

### Notes

_No response_

### Version

Tested on stable:

```text
cargo 1.80.0 (376290515 2024-07-16)
release: 1.80.0
commit-hash: 37629051518c3df9ac2c1744589362a02ecafa99
commit-date: 2024-07-16
host: x86_64-unknown-linux-gnu
libgit2: 1.7.2 (sys:0.18.3 vendored)
libcurl: 8.6.0-DEV (sys:0.4.72+curl-8.6.0 vendored ssl:OpenSSL/1.1.1w)
ssl: OpenSSL 1.1.1w  11 Sep 2023
os: Fedora 40.0.0 [64-bit]
```

and nightly: 

```
cargo 1.83.0-nightly (c1fa840a8 2024-08-29)
release: 1.83.0-nightly
commit-hash: c1fa840a85eca53818895901a53fae34247448b2
commit-date: 2024-08-29
host: x86_64-unknown-linux-gnu
libgit2: 1.8.1 (sys:0.19.0 vendored)
libcurl: 8.9.0-DEV (sys:0.4.74+curl-8.9.0 vendored ssl:OpenSSL/1.1.1w)
ssl: OpenSSL 1.1.1w  11 Sep 2023
os: Fedora 40.0.0 [64-bit]
```
```


Pull request description:
Fix parsing of comma separated values in --crate-type flag

### What does this PR try to resolve?

According to the documentation the `--crate-type` flag accepts a comma separated list of crate types. However, these are never actually split into individual items and passed verbatim to rustc.

Since cargo fails to associate cases such as 'staticlib,cdylib' to a specific crate type internally, it has to invoke rustc to determine the output file types for this unknown crate type, which returns only the first file type of the first crate type in the list. Consequently cargo will be looking only for a single '.a' artifact on Linux to be copied to the target directory.

Fix this by splitting the list of provided crate types into individual items before further processing them.

Fixes #14498

### How should we test and review this PR?

Updated corresponding test cases

### Additional information


Patch:
diff --git a/src/bin/cargo/commands/rustc.rs b/src/bin/cargo/commands/rustc.rs
index 999f8d64c..454282f0e 100644
--- a/src/bin/cargo/commands/rustc.rs
+++ b/src/bin/cargo/commands/rustc.rs
@@ -91,7 +91,16 @@ pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult {
         ops::print(&ws, &compile_opts, opt_value)?;
         return Ok(());
     }
-    let crate_types = values(args, CRATE_TYPE_ARG_NAME);
+
+    let crate_types = args
+        .get_many::<String>(CRATE_TYPE_ARG_NAME)
+        .into_iter()
+        .flatten()
+        .flat_map(|s| s.split(','))
+        .filter(|s| !s.is_empty())
+        .map(String::from)
+        .collect::<Vec<String>>();
+
     compile_opts.target_rustc_crate_types = if crate_types.is_empty() {
         None
     } else {


Test patch:
diff --git a/tests/testsuite/rustc.rs b/tests/testsuite/rustc.rs
index c3afb6a01..9222255aa 100644
--- a/tests/testsuite/rustc.rs
+++ b/tests/testsuite/rustc.rs
@@ -238,7 +238,7 @@ fn build_with_crate_types_for_foo() {
     p.cargo("rustc -v --crate-type lib,cdylib")
         .with_stderr_data(str![[r#"
 [COMPILING] foo v0.0.1 ([ROOT]/foo)
-[RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib,cdylib [..]`
+[RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib --crate-type cdylib [..]`
 [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
 
 "#]])
@@ -302,7 +302,7 @@ fn build_with_crate_types_to_example() {
         .with_stderr_data(str![[r#"
 [COMPILING] foo v0.0.1 ([ROOT]/foo)
 [RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib [..]`
-[RUNNING] `rustc --crate-name ex --edition=2015 examples/ex.rs [..]--crate-type lib,cdylib [..]`
+[RUNNING] `rustc --crate-name ex --edition=2015 examples/ex.rs [..]--crate-type lib --crate-type cdylib [..]`
 [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
 
 "#]])
@@ -338,7 +338,7 @@ fn build_with_crate_types_to_one_of_multi_examples() {
         .with_stderr_data(str![[r#"
 [COMPILING] foo v0.0.1 ([ROOT]/foo)
 [RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib [..]`
-[RUNNING] `rustc --crate-name ex1 --edition=2015 examples/ex1.rs [..]--crate-type lib,cdylib [..]`
+[RUNNING] `rustc --crate-name ex1 --edition=2015 examples/ex1.rs [..]--crate-type lib --crate-type cdylib [..]`
 [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
 
 "#]])