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