1 use crate::core
::Target
;
2 use crate::util
::errors
::CargoResult
;
3 use crate::util
::interning
::InternedString
;
4 use crate::util
::{Config, StableHasher}
;
5 use anyhow
::Context
as _
;
7 use std
::collections
::BTreeSet
;
9 use std
::hash
::{Hash, Hasher}
;
12 /// Indicator for how a unit is being compiled.
14 /// This is used primarily for organizing cross compilations vs host
15 /// compilations, where cross compilations happen at the request of `--target`
16 /// and host compilations happen for things like build scripts and procedural
18 #[derive(PartialEq, Eq, Hash, Debug, Clone, Copy, PartialOrd, Ord)]
19 pub enum CompileKind
{
20 /// Attached to a unit that is compiled for the "host" system or otherwise
21 /// is compiled without a `--target` flag. This is used for procedural
22 /// macros and build scripts, or if the `--target` flag isn't passed.
25 /// Attached to a unit to be compiled for a particular target. This is used
26 /// for units when the `--target` flag is passed.
27 Target(CompileTarget
),
31 pub fn is_host(&self) -> bool
{
32 matches
!(self, CompileKind
::Host
)
35 pub fn for_target(self, target
: &Target
) -> CompileKind
{
36 // Once we start compiling for the `Host` kind we continue doing so, but
37 // if we are a `Target` kind and then we start compiling for a target
38 // that needs to be on the host we lift ourselves up to `Host`.
40 CompileKind
::Host
=> CompileKind
::Host
,
41 CompileKind
::Target(_
) if target
.for_host() => CompileKind
::Host
,
42 CompileKind
::Target(n
) => CompileKind
::Target(n
),
46 /// Creates a new list of `CompileKind` based on the requested list of
49 /// If no targets are given then this returns a single-element vector with
50 /// `CompileKind::Host`.
51 pub fn from_requested_targets(
54 ) -> CargoResult
<Vec
<CompileKind
>> {
55 let dedup
= |targets
: &[String
]| {
58 .map(|value
| Ok(CompileKind
::Target(CompileTarget
::new(value
)?
)))
59 // First collect into a set to deduplicate any `--target` passed
61 .collect
::<CargoResult
<BTreeSet
<_
>>>()?
62 // ... then generate a flat list for everything else to use.
67 if !targets
.is_empty() {
68 return dedup(targets
);
71 let kinds
= match &config
.build_config()?
.target
{
72 None
=> Ok(vec
![CompileKind
::Host
]),
73 Some(build_target_config
) => dedup(&build_target_config
.values(config
)?
),
79 /// Hash used for fingerprinting.
81 /// Metadata hashing uses the normal Hash trait, which does not
82 /// differentiate on `.json` file contents. The fingerprint hash does
83 /// check the contents.
84 pub fn fingerprint_hash(&self) -> u64 {
86 CompileKind
::Host
=> 0,
87 CompileKind
::Target(target
) => target
.fingerprint_hash(),
92 impl serde
::ser
::Serialize
for CompileKind
{
93 fn serialize
<S
>(&self, s
: S
) -> Result
<S
::Ok
, S
::Error
>
95 S
: serde
::ser
::Serializer
,
98 CompileKind
::Host
=> None
::<&str>.serialize(s
),
99 CompileKind
::Target(t
) => Some(t
.name
).serialize(s
),
104 /// Abstraction for the representation of a compilation target that Cargo has.
106 /// Compilation targets are one of two things right now:
108 /// 1. A raw target string, like `x86_64-unknown-linux-gnu`.
109 /// 2. The path to a JSON file, such as `/path/to/my-target.json`.
111 /// Raw target strings are typically dictated by `rustc` itself and represent
112 /// built-in targets. Custom JSON files are somewhat unstable, but supported
113 /// here in Cargo. Note that for JSON target files this `CompileTarget` stores a
114 /// full canonicalized path to the target.
116 /// The main reason for this existence is to handle JSON target files where when
117 /// we call rustc we pass full paths but when we use it for Cargo's purposes
118 /// like naming directories or looking up configuration keys we only check the
119 /// file stem of JSON target files. For built-in rustc targets this is just an
120 /// uninterpreted string basically.
121 #[derive(PartialEq, Eq, Hash, Debug, Clone, Copy, PartialOrd, Ord, Serialize)]
122 pub struct CompileTarget
{
123 name
: InternedString
,
127 pub fn new(name
: &str) -> CargoResult
<CompileTarget
> {
128 let name
= name
.trim();
130 anyhow
::bail
!("target was empty");
132 if !name
.ends_with(".json") {
133 return Ok(CompileTarget { name: name.into() }
);
136 // If `name` ends in `.json` then it's likely a custom target
137 // specification. Canonicalize the path to ensure that different builds
138 // with different paths always produce the same result.
139 let path
= Path
::new(name
)
141 .with_context(|| format
!("target path {:?} is not a valid file", name
))?
;
146 .map_err(|_
| anyhow
::format_err
!("target path is not valid unicode"))?
;
147 Ok(CompileTarget { name: name.into() }
)
150 /// Returns the full unqualified name of this target, suitable for passing
151 /// to `rustc` directly.
153 /// Typically this is pretty much the same as `short_name`, but for the case
154 /// of JSON target files this will be a full canonicalized path name for the
155 /// current filesystem.
156 pub fn rustc_target(&self) -> InternedString
{
160 /// Returns a "short" version of the target name suitable for usage within
161 /// Cargo for configuration and such.
163 /// This is typically the same as `rustc_target`, or the full name, but for
164 /// JSON target files this returns just the file stem (e.g. `foo` out of
165 /// `foo.json`) instead of the full path.
166 pub fn short_name(&self) -> &str {
167 // Flexible target specifications often point at json files, so if it
168 // looks like we've got one of those just use the file stem (the file
169 // name without ".json") as a short name for this target. Note that the
170 // `unwrap()` here should never trigger since we have a nonempty name
171 // and it starts as utf-8 so it's always utf-8
172 if self.name
.ends_with(".json") {
173 Path
::new(&self.name
).file_stem().unwrap().to_str().unwrap()
179 /// See [`CompileKind::fingerprint_hash`].
180 pub fn fingerprint_hash(&self) -> u64 {
181 let mut hasher
= StableHasher
::new();
185 .then(|| fs
::read_to_string(self.name
))
187 Some(Ok(contents
)) => {
188 // This may have some performance concerns, since it is called
189 // fairly often. If that ever seems worth fixing, consider
190 // embedding this in `CompileTarget`.
191 contents
.hash(&mut hasher
);
194 self.name
.hash(&mut hasher
);