1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
use crate::core::compiler::BuildContext; use crate::core::{InternedString, Target}; use crate::util::errors::{CargoResult, CargoResultExt}; use serde::Serialize; use std::path::Path; /// Indicator for how a unit is being compiled. /// /// This is used primarily for organizing cross compilations vs host /// compilations, where cross compilations happen at the request of `--target` /// and host compilations happen for things like build scripts and procedural /// macros. #[derive(PartialEq, Eq, Hash, Debug, Clone, Copy, PartialOrd, Ord, Serialize)] pub enum CompileKind { /// Attached to a unit that is compiled for the "host" system or otherwise /// is compiled without a `--target` flag. This is used for procedural /// macros and build scripts, or if the `--target` flag isn't passed. Host, /// Attached to a unit to be compiled for a particular target. This is used /// for units when the `--target` flag is passed. Target(CompileTarget), } impl CompileKind { pub fn is_host(&self) -> bool { match self { CompileKind::Host => true, _ => false, } } pub fn for_target(self, target: &Target) -> CompileKind { // Once we start compiling for the `Host` kind we continue doing so, but // if we are a `Target` kind and then we start compiling for a target // that needs to be on the host we lift ourselves up to `Host`. match self { CompileKind::Host => CompileKind::Host, CompileKind::Target(_) if target.for_host() => CompileKind::Host, CompileKind::Target(n) => CompileKind::Target(n), } } /// Returns a "short" name for this kind, suitable for keying off /// configuration in Cargo or presenting to users. pub fn short_name(&self, bcx: &BuildContext<'_, '_>) -> &str { match self { CompileKind::Host => bcx.host_triple().as_str(), CompileKind::Target(target) => target.short_name(), } } } /// Abstraction for the representation of a compilation target that Cargo has. /// /// Compilation targets are one of two things right now: /// /// 1. A raw target string, like `x86_64-unknown-linux-gnu`. /// 2. The path to a JSON file, such as `/path/to/my-target.json`. /// /// Raw target strings are typically dictated by `rustc` itself and represent /// built-in targets. Custom JSON files are somewhat unstable, but supported /// here in Cargo. Note that for JSON target files this `CompileTarget` stores a /// full canonicalized path to the target. /// /// The main reason for this existence is to handle JSON target files where when /// we call rustc we pass full paths but when we use it for Cargo's purposes /// like naming directories or looking up configuration keys we only check the /// file stem of JSON target files. For built-in rustc targets this is just an /// uninterpreted string basically. #[derive(PartialEq, Eq, Hash, Debug, Clone, Copy, PartialOrd, Ord, Serialize)] pub struct CompileTarget { name: InternedString, } impl CompileTarget { pub fn new(name: &str) -> CargoResult<CompileTarget> { let name = name.trim(); if name.is_empty() { failure::bail!("target was empty"); } if !name.ends_with(".json") { return Ok(CompileTarget { name: name.into() }); } // If `name` ends in `.json` then it's likely a custom target // specification. Canonicalize the path to ensure that different builds // with different paths always produce the same result. let path = Path::new(name) .canonicalize() .chain_err(|| failure::format_err!("target path {:?} is not a valid file", name))?; let name = path .into_os_string() .into_string() .map_err(|_| failure::format_err!("target path is not valid unicode"))?; Ok(CompileTarget { name: name.into() }) } /// Returns the full unqualified name of this target, suitable for passing /// to `rustc` directly. /// /// Typically this is pretty much the same as `short_name`, but for the case /// of JSON target files this will be a full canonicalized path name for the /// current filesystem. pub fn rustc_target(&self) -> &str { &self.name } /// Returns a "short" version of the target name suitable for usage within /// Cargo for configuration and such. /// /// This is typically the same as `rustc_target`, or the full name, but for /// JSON target files this returns just the file stem (e.g. `foo` out of /// `foo.json`) instead of the full path. pub fn short_name(&self) -> &str { // Flexible target specifications often point at json files, so if it // looks like we've got one of those just use the file stem (the file // name without ".json") as a short name for this target. Note that the // `unwrap()` here should never trigger since we have a nonempty name // and it starts as utf-8 so it's always utf-8 if self.name.ends_with(".json") { Path::new(&self.name).file_stem().unwrap().to_str().unwrap() } else { &self.name } } }