]> git.proxmox.com Git - cargo.git/commitdiff
Discover crate type information late if necessary.
authorMark Simulacrum <mark.simulacrum@gmail.com>
Mon, 21 Aug 2017 11:46:31 +0000 (05:46 -0600)
committerMark Simulacrum <mark.simulacrum@gmail.com>
Sat, 26 Aug 2017 14:52:20 +0000 (08:52 -0600)
Some crates aren't found during eager crate-type searching due to being
behind `cfg(...)` flags. We still want to be able to handle these,
though, so when necessary we now call rustc again to get crate-type
information for these cfg-ed crates.

src/cargo/ops/cargo_rustc/context.rs
tests/proc-macro.rs

index 5b67830e7073761543270f2c199ccaaddaac4323..865f8b42a50d64400fc0dbb65603891db050d238 100755 (executable)
@@ -1,19 +1,21 @@
 #![allow(deprecated)]
 
 use std::collections::{HashSet, HashMap, BTreeSet};
+use std::collections::hash_map::Entry;
 use std::env;
 use std::fmt;
 use std::hash::{Hasher, Hash, SipHasher};
 use std::path::{Path, PathBuf};
 use std::str::{self, FromStr};
 use std::sync::Arc;
+use std::cell::RefCell;
 
 use jobserver::Client;
 
 use core::{Package, PackageId, PackageSet, Resolve, Target, Profile};
 use core::{TargetKind, Profiles, Dependency, Workspace};
 use core::dependency::Kind as DepKind;
-use util::{self, internal, Config, profile, Cfg, CfgExpr};
+use util::{self, ProcessBuilder, internal, Config, profile, Cfg, CfgExpr};
 use util::errors::{CargoResult, CargoResultExt};
 
 use super::TargetConfig;
@@ -58,10 +60,28 @@ pub struct Context<'a, 'cfg: 'a> {
 
 #[derive(Clone, Default)]
 struct TargetInfo {
-    crate_types: HashMap<String, Option<(String, String)>>,
+    crate_type_process: Option<ProcessBuilder>,
+    crate_types: RefCell<HashMap<String, Option<(String, String)>>>,
     cfg: Option<Vec<Cfg>>,
 }
 
+impl TargetInfo {
+    fn discover_crate_type(&self, crate_type: &str) -> CargoResult<Option<(String, String)>> {
+        let mut process = self.crate_type_process.clone().unwrap();
+
+        process.arg("--crate-type").arg(crate_type);
+
+        let output = process.exec_with_output().chain_err(|| {
+            format!("failed to run `rustc` to learn about \
+                     crate-type {} information", crate_type)
+        })?;
+
+        let error = str::from_utf8(&output.stderr).unwrap();
+        let output = str::from_utf8(&output.stdout).unwrap();
+        Ok(parse_crate_type(crate_type, error, &mut output.lines())?)
+    }
+}
+
 #[derive(Clone)]
 pub struct Metadata(u64);
 
@@ -220,13 +240,16 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
                .args(&rustflags)
                .env_remove("RUST_LOG");
 
-        for crate_type in crate_types {
-            process.arg("--crate-type").arg(crate_type);
-        }
         if kind == Kind::Target {
             process.arg("--target").arg(&self.target_triple());
         }
 
+        let crate_type_process = process.clone();
+
+        for crate_type in crate_types {
+            process.arg("--crate-type").arg(crate_type);
+        }
+
         let mut with_cfg = process.clone();
         with_cfg.arg("--print=sysroot");
         with_cfg.arg("--print=cfg");
@@ -245,29 +268,8 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
         let mut lines = output.lines();
         let mut map = HashMap::new();
         for crate_type in crate_types {
-            let not_supported = error.lines().any(|line| {
-                (line.contains("unsupported crate type") ||
-                 line.contains("unknown crate type")) &&
-                line.contains(crate_type)
-            });
-            if not_supported {
-                map.insert(crate_type.to_string(), None);
-                continue;
-            }
-            let line = match lines.next() {
-                Some(line) => line,
-                None => bail!("malformed output when learning about \
-                               target-specific information from rustc"),
-            };
-            let mut parts = line.trim().split("___");
-            let prefix = parts.next().unwrap();
-            let suffix = match parts.next() {
-                Some(part) => part,
-                None => bail!("output of --print=file-names has changed in \
-                               the compiler, cannot parse"),
-            };
-
-            map.insert(crate_type.to_string(), Some((prefix.to_string(), suffix.to_string())));
+            let out = parse_crate_type(crate_type, error, &mut lines)?;
+            map.insert(crate_type.to_string(), out);
         }
 
         if has_cfg_and_sysroot {
@@ -303,7 +305,8 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
             Kind::Target => &mut self.target_info,
             Kind::Host => &mut self.host_info,
         };
-        info.crate_types = map;
+        info.crate_type_process = Some(crate_type_process);
+        info.crate_types = RefCell::new(map);
         info.cfg = cfg;
         Ok(())
     }
@@ -596,8 +599,17 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
             } else {
                 let mut add = |crate_type: &str, linkable: bool| -> CargoResult<()> {
                     let crate_type = if crate_type == "lib" {"rlib"} else {crate_type};
-                    match info.crate_types.get(crate_type) {
-                        Some(&Some((ref prefix, ref suffix))) => {
+                    let mut crate_types = info.crate_types.borrow_mut();
+                    let entry = crate_types.entry(crate_type.to_string());
+                    let crate_type_info = match entry {
+                        Entry::Occupied(o) => &*o.into_mut(),
+                        Entry::Vacant(v) => {
+                            let value = info.discover_crate_type(&v.key())?;
+                            &*v.insert(value)
+                        }
+                    };
+                    match *crate_type_info {
+                        Some((ref prefix, ref suffix)) => {
                             let filename = out_dir.join(format!("{}{}{}", prefix, stem, suffix));
                             let link_dst = link_stem.clone().map(|(ld, ls)| {
                                 ld.join(format!("{}{}{}", prefix, ls, suffix))
@@ -606,14 +618,10 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
                             Ok(())
                         }
                         // not supported, don't worry about it
-                        Some(&None) => {
+                        None => {
                             unsupported.push(crate_type.to_string());
                             Ok(())
                         }
-                        None => {
-                            bail!("failed to learn about crate-type `{}` early on",
-                                  crate_type)
-                        }
                     }
                 };
 
@@ -1095,3 +1103,32 @@ impl fmt::Display for Metadata {
         write!(f, "{:016x}", self.0)
     }
 }
+
+fn parse_crate_type(
+    crate_type: &str,
+    error: &str,
+    lines: &mut str::Lines,
+) -> CargoResult<Option<(String, String)>> {
+    let not_supported = error.lines().any(|line| {
+        (line.contains("unsupported crate type") ||
+         line.contains("unknown crate type")) &&
+        line.contains(crate_type)
+    });
+    if not_supported {
+        return Ok(None);
+    }
+    let line = match lines.next() {
+        Some(line) => line,
+        None => bail!("malformed output when learning about \
+            crate-type {} information", crate_type),
+    };
+    let mut parts = line.trim().split("___");
+    let prefix = parts.next().unwrap();
+    let suffix = match parts.next() {
+        Some(part) => part,
+        None => bail!("output of --print=file-names has changed in \
+            the compiler, cannot parse"),
+    };
+
+    Ok(Some((prefix.to_string(), suffix.to_string())))
+}
index ee04a74e1dcd6cd9bf509211655240e28daf9e98..3e168c49b55f3db52db7b08326ec2e4726c4bd82 100644 (file)
@@ -5,6 +5,60 @@ use cargotest::is_nightly;
 use cargotest::support::{project, execs};
 use hamcrest::assert_that;
 
+#[test]
+fn probe_cfg_before_crate_type_discovery() {
+    if !is_nightly() {
+        return;
+    }
+
+    let client = project("client")
+        .file("Cargo.toml", r#"
+            [package]
+            name = "client"
+            version = "0.0.1"
+            authors = []
+
+            [target.'cfg(not(stage300))'.dependencies.noop]
+            path = "../noop"
+        "#)
+        .file("src/main.rs", r#"
+            #![feature(proc_macro)]
+
+            #[macro_use]
+            extern crate noop;
+
+            #[derive(Noop)]
+            struct X;
+
+            fn main() {}
+        "#);
+    let noop = project("noop")
+        .file("Cargo.toml", r#"
+            [package]
+            name = "noop"
+            version = "0.0.1"
+            authors = []
+
+            [lib]
+            proc-macro = true
+        "#)
+        .file("src/lib.rs", r#"
+            #![feature(proc_macro, proc_macro_lib)]
+
+            extern crate proc_macro;
+            use proc_macro::TokenStream;
+
+            #[proc_macro_derive(Noop)]
+            pub fn noop(_input: TokenStream) -> TokenStream {
+                "".parse().unwrap()
+            }
+        "#);
+    noop.build();
+
+    assert_that(client.cargo_process("build"),
+                execs().with_status(0));
+}
+
 #[test]
 fn noop() {
     if !is_nightly() {