//!
//! # Defining a new target
//!
-//! Targets are defined using [JSON](http://json.org/). The `Target` struct in
+//! Targets are defined using [JSON](https://json.org/). The `Target` struct in
//! this module defines the format the JSON file should take, though each
//! underscore in the field names should be replaced with a hyphen (`-`) in the
//! JSON file. Some fields are required in every target specification, such as
mod android_base;
mod apple_base;
mod apple_sdk_base;
-mod arm_base;
mod avr_gnu_base;
mod bpf_base;
mod dragonfly_base;
mod netbsd_base;
mod openbsd_base;
mod redox_base;
-mod riscv_base;
mod solaris_base;
mod thumb_base;
mod uefi_msvc_base;
}
}
+#[derive(Clone, Copy, PartialEq, Hash, Debug)]
+pub enum FramePointer {
+ /// Forces the machine code generator to always preserve the frame pointers.
+ Always,
+ /// Forces the machine code generator to preserve the frame pointers except for the leaf
+ /// functions (i.e. those that don't call other functions).
+ NonLeaf,
+ /// Allows the machine code generator to omit the frame pointers.
+ ///
+ /// This option does not guarantee that the frame pointers will be omitted.
+ MayOmit,
+}
+
+impl FromStr for FramePointer {
+ type Err = ();
+ fn from_str(s: &str) -> Result<Self, ()> {
+ Ok(match s {
+ "always" => Self::Always,
+ "non-leaf" => Self::NonLeaf,
+ "may-omit" => Self::MayOmit,
+ _ => return Err(()),
+ })
+ }
+}
+
+impl ToJson for FramePointer {
+ fn to_json(&self) -> Json {
+ match *self {
+ Self::Always => "always",
+ Self::NonLeaf => "non-leaf",
+ Self::MayOmit => "may-omit",
+ }
+ .to_json()
+ }
+}
+
macro_rules! supported_targets {
( $(($( $triple:literal, )+ $module:ident ),)+ ) => {
$(mod $module;)+
("armv7-unknown-freebsd", armv7_unknown_freebsd),
("i686-unknown-freebsd", i686_unknown_freebsd),
("powerpc64-unknown-freebsd", powerpc64_unknown_freebsd),
+ ("powerpc64le-unknown-freebsd", powerpc64le_unknown_freebsd),
("x86_64-unknown-freebsd", x86_64_unknown_freebsd),
("x86_64-unknown-dragonfly", x86_64_unknown_dragonfly),
("bpfel-unknown-none", bpfel_unknown_none),
}
+/// Warnings encountered when parsing the target `json`.
+///
+/// Includes fields that weren't recognized and fields that don't have the expected type.
+#[derive(Debug, PartialEq)]
+pub struct TargetWarnings {
+ unused_fields: Vec<String>,
+ incorrect_type: Vec<String>,
+}
+
+impl TargetWarnings {
+ pub fn empty() -> Self {
+ Self { unused_fields: Vec::new(), incorrect_type: Vec::new() }
+ }
+
+ pub fn warning_messages(&self) -> Vec<String> {
+ let mut warnings = vec![];
+ if !self.unused_fields.is_empty() {
+ warnings.push(format!(
+ "target json file contains unused fields: {}",
+ self.unused_fields.join(", ")
+ ));
+ }
+ if !self.incorrect_type.is_empty() {
+ warnings.push(format!(
+ "target json file contains fields whose value doesn't have the correct json type: {}",
+ self.incorrect_type.join(", ")
+ ));
+ }
+ warnings
+ }
+}
+
/// Everything `rustc` knows about how to compile for a specific target.
///
/// Every field here must be specified, and has no default value.
/// Architecture to use for ABI considerations. Valid options include: "x86",
/// "x86_64", "arm", "aarch64", "mips", "powerpc", "powerpc64", and others.
pub arch: String,
- /// [Data layout](http://llvm.org/docs/LangRef.html#data-layout) to pass to LLVM.
+ /// [Data layout](https://llvm.org/docs/LangRef.html#data-layout) to pass to LLVM.
pub data_layout: String,
/// Optional settings with defaults.
pub options: TargetOptions,
pub os: String,
/// Environment name to use for conditional compilation (`target_env`). Defaults to "".
pub env: String,
+ /// ABI name to distinguish multiple ABIs on the same OS and architecture. For instance, `"eabi"`
+ /// or `"eabihf"`. Defaults to "".
+ pub abi: String,
/// Vendor name to use for conditional compilation (`target_vendor`). Defaults to "unknown".
pub vendor: String,
/// Default linker flavor used if `-C linker-flavor` or `-C linker` are not passed
pub tls_model: TlsModel,
/// Do not emit code that uses the "red zone", if the ABI has one. Defaults to false.
pub disable_redzone: bool,
- /// Eliminate frame pointers from stack frames if possible. Defaults to true.
- pub eliminate_frame_pointer: bool,
+ /// Frame pointer mode for this target. Defaults to `MayOmit`.
+ pub frame_pointer: FramePointer,
/// Emit each function in its own section. Defaults to true.
pub function_sections: bool,
/// String to prepend to the name of every dynamic library. Defaults to "lib".
/// Panic strategy: "unwind" or "abort"
pub panic_strategy: PanicStrategy,
- /// A list of ABIs unsupported by the current target. Note that generic ABIs
- /// are considered to be supported on all platforms and cannot be marked
- /// unsupported.
- pub unsupported_abis: Vec<Abi>,
-
/// Whether or not linking dylibs to a static CRT is allowed.
pub crt_static_allows_dylibs: bool,
/// Whether or not the CRT is statically linked by default.
c_int_width: "32".to_string(),
os: "none".to_string(),
env: String::new(),
+ abi: String::new(),
vendor: "unknown".to_string(),
linker_flavor: LinkerFlavor::Gcc,
linker: option_env!("CFG_DEFAULT_LINKER").map(|s| s.to_string()),
code_model: None,
tls_model: TlsModel::GeneralDynamic,
disable_redzone: false,
- eliminate_frame_pointer: true,
+ frame_pointer: FramePointer::MayOmit,
function_sections: true,
dll_prefix: "lib".to_string(),
dll_suffix: ".so".to_string(),
max_atomic_width: None,
atomic_cas: true,
panic_strategy: PanicStrategy::Unwind,
- unsupported_abis: vec![],
crt_static_allows_dylibs: false,
crt_static_default: false,
crt_static_respected: false,
/// Given a function ABI, turn it into the correct ABI for this target.
pub fn adjust_abi(&self, abi: Abi) -> Abi {
match abi {
- Abi::System { unwind } => {
- if self.is_like_windows && self.arch == "x86" {
- Abi::Stdcall { unwind }
- } else {
- Abi::C { unwind }
- }
- }
- // These ABI kinds are ignored on non-x86 Windows targets.
- // See https://docs.microsoft.com/en-us/cpp/cpp/argument-passing-and-naming-conventions
- // and the individual pages for __stdcall et al.
- Abi::Stdcall { unwind } | Abi::Thiscall { unwind } => {
- if self.is_like_windows && self.arch != "x86" { Abi::C { unwind } } else { abi }
- }
- Abi::Fastcall | Abi::Vectorcall => {
- if self.is_like_windows && self.arch != "x86" {
- Abi::C { unwind: false }
- } else {
- abi
- }
- }
- Abi::EfiApi => {
- if self.arch == "x86_64" {
- Abi::Win64
- } else {
- Abi::C { unwind: false }
- }
+ Abi::C { .. } => self.default_adjusted_cabi.unwrap_or(abi),
+ Abi::System { unwind } if self.is_like_windows && self.arch == "x86" => {
+ Abi::Stdcall { unwind }
}
+ Abi::System { unwind } => Abi::C { unwind },
+ Abi::EfiApi if self.arch == "x86_64" => Abi::Win64,
+ Abi::EfiApi => Abi::C { unwind: false },
- Abi::C { unwind } => self.default_adjusted_cabi.unwrap_or(Abi::C { unwind }),
+ // See commentary in `is_abi_supported`.
+ Abi::Stdcall { .. } | Abi::Thiscall { .. } if self.arch == "x86" => abi,
+ Abi::Stdcall { unwind } | Abi::Thiscall { unwind } => Abi::C { unwind },
+ Abi::Fastcall if self.arch == "x86" => abi,
+ Abi::Vectorcall if ["x86", "x86_64"].contains(&&self.arch[..]) => abi,
+ Abi::Fastcall | Abi::Vectorcall => Abi::C { unwind: false },
abi => abi,
}
}
+ /// Returns a None if the UNSUPPORTED_CALLING_CONVENTIONS lint should be emitted
+ pub fn is_abi_supported(&self, abi: Abi) -> Option<bool> {
+ use Abi::*;
+ Some(match abi {
+ Rust
+ | C { .. }
+ | System { .. }
+ | RustIntrinsic
+ | RustCall
+ | PlatformIntrinsic
+ | Unadjusted
+ | Cdecl
+ | EfiApi => true,
+ X86Interrupt => ["x86", "x86_64"].contains(&&self.arch[..]),
+ Aapcs | CCmseNonSecureCall => ["arm", "aarch64"].contains(&&self.arch[..]),
+ Win64 | SysV64 => self.arch == "x86_64",
+ PtxKernel => self.arch == "nvptx64",
+ Msp430Interrupt => self.arch == "msp430",
+ AmdGpuKernel => self.arch == "amdgcn",
+ AvrInterrupt | AvrNonBlockingInterrupt => self.arch == "avr",
+ Wasm => ["wasm32", "wasm64"].contains(&&self.arch[..]),
+ // On windows these fall-back to platform native calling convention (C) when the
+ // architecture is not supported.
+ //
+ // This is I believe a historical accident that has occurred as part of Microsoft
+ // striving to allow most of the code to "just" compile when support for 64-bit x86
+ // was added and then later again, when support for ARM architectures was added.
+ //
+ // This is well documented across MSDN. Support for this in Rust has been added in
+ // #54576. This makes much more sense in context of Microsoft's C++ than it does in
+ // Rust, but there isn't much leeway remaining here to change it back at the time this
+ // comment has been written.
+ //
+ // Following are the relevant excerpts from the MSDN documentation.
+ //
+ // > The __vectorcall calling convention is only supported in native code on x86 and
+ // x64 processors that include Streaming SIMD Extensions 2 (SSE2) and above.
+ // > ...
+ // > On ARM machines, __vectorcall is accepted and ignored by the compiler.
+ //
+ // -- https://docs.microsoft.com/en-us/cpp/cpp/vectorcall?view=msvc-160
+ //
+ // > On ARM and x64 processors, __stdcall is accepted and ignored by the compiler;
+ //
+ // -- https://docs.microsoft.com/en-us/cpp/cpp/stdcall?view=msvc-160
+ //
+ // > In most cases, keywords or compiler switches that specify an unsupported
+ // > convention on a particular platform are ignored, and the platform default
+ // > convention is used.
+ //
+ // -- https://docs.microsoft.com/en-us/cpp/cpp/argument-passing-and-naming-conventions
+ Stdcall { .. } | Fastcall | Thiscall { .. } | Vectorcall if self.is_like_windows => {
+ true
+ }
+ // Outside of Windows we want to only support these calling conventions for the
+ // architectures for which these calling conventions are actually well defined.
+ Stdcall { .. } | Fastcall | Thiscall { .. } if self.arch == "x86" => true,
+ Vectorcall if ["x86", "x86_64"].contains(&&self.arch[..]) => true,
+ // Return a `None` for other cases so that we know to emit a future compat lint.
+ Stdcall { .. } | Fastcall | Thiscall { .. } | Vectorcall => return None,
+ })
+ }
+
/// Minimum integer size in bits that this target can perform atomic
/// operations on.
pub fn min_atomic_width(&self) -> u64 {
self.max_atomic_width.unwrap_or_else(|| self.pointer_width.into())
}
- pub fn is_abi_supported(&self, abi: Abi) -> bool {
- abi.generic() || !self.unsupported_abis.contains(&abi)
- }
-
/// Loads a target descriptor from a JSON object.
- pub fn from_json(obj: Json) -> Result<Target, String> {
+ pub fn from_json(mut obj: Json) -> Result<(Target, TargetWarnings), String> {
// While ugly, this code must remain this way to retain
// compatibility with existing JSON fields and the internal
// expected naming of the Target and TargetOptions structs.
// are round-tripped through this code to catch cases where
// the JSON parser is not updated to match the structs.
- let get_req_field = |name: &str| {
- obj.find(name)
- .and_then(Json::as_string)
- .map(str::to_string)
+ let mut get_req_field = |name: &str| {
+ obj.remove_key(name)
+ .and_then(|j| Json::as_string(&j).map(str::to_string))
.ok_or_else(|| format!("Field {} in target specification is required", name))
};
options: Default::default(),
};
+ let mut incorrect_type = vec![];
+
macro_rules! key {
($key_name:ident) => ( {
let name = (stringify!($key_name)).replace("_", "-");
- if let Some(s) = obj.find(&name).and_then(Json::as_string) {
- base.$key_name = s.to_string();
+ if let Some(s) = obj.remove_key(&name).and_then(|j| Json::as_string(&j).map(str::to_string)) {
+ base.$key_name = s;
}
} );
($key_name:ident = $json_name:expr) => ( {
let name = $json_name;
- if let Some(s) = obj.find(&name).and_then(Json::as_string) {
- base.$key_name = s.to_string();
+ if let Some(s) = obj.remove_key(&name).and_then(|j| Json::as_string(&j).map(str::to_string)) {
+ base.$key_name = s;
}
} );
($key_name:ident, bool) => ( {
let name = (stringify!($key_name)).replace("_", "-");
- if let Some(s) = obj.find(&name).and_then(Json::as_boolean) {
+ if let Some(s) = obj.remove_key(&name).and_then(|j| Json::as_boolean(&j)) {
base.$key_name = s;
}
} );
($key_name:ident, Option<u32>) => ( {
let name = (stringify!($key_name)).replace("_", "-");
- if let Some(s) = obj.find(&name).and_then(Json::as_u64) {
+ if let Some(s) = obj.remove_key(&name).and_then(|j| Json::as_u64(&j)) {
if s < 1 || s > 5 {
return Err("Not a valid DWARF version number".to_string());
}
} );
($key_name:ident, Option<u64>) => ( {
let name = (stringify!($key_name)).replace("_", "-");
- if let Some(s) = obj.find(&name).and_then(Json::as_u64) {
+ if let Some(s) = obj.remove_key(&name).and_then(|j| Json::as_u64(&j)) {
base.$key_name = Some(s);
}
} );
($key_name:ident, MergeFunctions) => ( {
let name = (stringify!($key_name)).replace("_", "-");
- obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| {
+ obj.remove_key(&name[..]).and_then(|o| o.as_string().and_then(|s| {
match s.parse::<MergeFunctions>() {
Ok(mergefunc) => base.$key_name = mergefunc,
_ => return Some(Err(format!("'{}' is not a valid value for \
} );
($key_name:ident, RelocModel) => ( {
let name = (stringify!($key_name)).replace("_", "-");
- obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| {
+ obj.remove_key(&name[..]).and_then(|o| o.as_string().and_then(|s| {
match s.parse::<RelocModel>() {
Ok(relocation_model) => base.$key_name = relocation_model,
_ => return Some(Err(format!("'{}' is not a valid relocation model. \
} );
($key_name:ident, CodeModel) => ( {
let name = (stringify!($key_name)).replace("_", "-");
- obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| {
+ obj.remove_key(&name[..]).and_then(|o| o.as_string().and_then(|s| {
match s.parse::<CodeModel>() {
Ok(code_model) => base.$key_name = Some(code_model),
_ => return Some(Err(format!("'{}' is not a valid code model. \
} );
($key_name:ident, TlsModel) => ( {
let name = (stringify!($key_name)).replace("_", "-");
- obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| {
+ obj.remove_key(&name[..]).and_then(|o| o.as_string().and_then(|s| {
match s.parse::<TlsModel>() {
Ok(tls_model) => base.$key_name = tls_model,
_ => return Some(Err(format!("'{}' is not a valid TLS model. \
} );
($key_name:ident, PanicStrategy) => ( {
let name = (stringify!($key_name)).replace("_", "-");
- obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| {
+ obj.remove_key(&name[..]).and_then(|o| o.as_string().and_then(|s| {
match s {
"unwind" => base.$key_name = PanicStrategy::Unwind,
"abort" => base.$key_name = PanicStrategy::Abort,
} );
($key_name:ident, RelroLevel) => ( {
let name = (stringify!($key_name)).replace("_", "-");
- obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| {
+ obj.remove_key(&name[..]).and_then(|o| o.as_string().and_then(|s| {
match s.parse::<RelroLevel>() {
Ok(level) => base.$key_name = level,
_ => return Some(Err(format!("'{}' is not a valid value for \
} );
($key_name:ident, SplitDebuginfo) => ( {
let name = (stringify!($key_name)).replace("_", "-");
- obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| {
+ obj.remove_key(&name[..]).and_then(|o| o.as_string().and_then(|s| {
match s.parse::<SplitDebuginfo>() {
Ok(level) => base.$key_name = level,
_ => return Some(Err(format!("'{}' is not a valid value for \
} );
($key_name:ident, list) => ( {
let name = (stringify!($key_name)).replace("_", "-");
- if let Some(v) = obj.find(&name).and_then(Json::as_array) {
- base.$key_name = v.iter()
- .map(|a| a.as_string().unwrap().to_string())
- .collect();
+ if let Some(j) = obj.remove_key(&name){
+ if let Some(v) = Json::as_array(&j) {
+ base.$key_name = v.iter()
+ .map(|a| a.as_string().unwrap().to_string())
+ .collect();
+ } else {
+ incorrect_type.push(name)
+ }
}
} );
($key_name:ident, opt_list) => ( {
let name = (stringify!($key_name)).replace("_", "-");
- if let Some(v) = obj.find(&name).and_then(Json::as_array) {
- base.$key_name = Some(v.iter()
- .map(|a| a.as_string().unwrap().to_string())
- .collect());
+ if let Some(j) = obj.remove_key(&name) {
+ if let Some(v) = Json::as_array(&j) {
+ base.$key_name = Some(v.iter()
+ .map(|a| a.as_string().unwrap().to_string())
+ .collect());
+ } else {
+ incorrect_type.push(name)
+ }
}
} );
($key_name:ident, optional) => ( {
let name = (stringify!($key_name)).replace("_", "-");
- if let Some(o) = obj.find(&name[..]) {
+ if let Some(o) = obj.remove_key(&name[..]) {
base.$key_name = o
.as_string()
.map(|s| s.to_string() );
} );
($key_name:ident, LldFlavor) => ( {
let name = (stringify!($key_name)).replace("_", "-");
- obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| {
+ obj.remove_key(&name[..]).and_then(|o| o.as_string().and_then(|s| {
if let Some(flavor) = LldFlavor::from_str(&s) {
base.$key_name = flavor;
} else {
} );
($key_name:ident, LinkerFlavor) => ( {
let name = (stringify!($key_name)).replace("_", "-");
- obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| {
+ obj.remove_key(&name[..]).and_then(|o| o.as_string().and_then(|s| {
match LinkerFlavor::from_str(s) {
Some(linker_flavor) => base.$key_name = linker_flavor,
_ => return Some(Err(format!("'{}' is not a valid value for linker-flavor. \
} );
($key_name:ident, StackProbeType) => ( {
let name = (stringify!($key_name)).replace("_", "-");
- obj.find(&name[..]).and_then(|o| match StackProbeType::from_json(o) {
+ obj.remove_key(&name[..]).and_then(|o| match StackProbeType::from_json(&o) {
Ok(v) => {
base.$key_name = v;
Some(Ok(()))
} );
($key_name:ident, SanitizerSet) => ( {
let name = (stringify!($key_name)).replace("_", "-");
- obj.find(&name[..]).and_then(|o| o.as_array()).and_then(|a| {
- for s in a {
- base.$key_name |= match s.as_string() {
- Some("address") => SanitizerSet::ADDRESS,
- Some("leak") => SanitizerSet::LEAK,
- Some("memory") => SanitizerSet::MEMORY,
- Some("thread") => SanitizerSet::THREAD,
- Some("hwaddress") => SanitizerSet::HWADDRESS,
- Some(s) => return Some(Err(format!("unknown sanitizer {}", s))),
- _ => return Some(Err(format!("not a string: {:?}", s))),
- };
+ if let Some(o) = obj.remove_key(&name[..]) {
+ if let Some(a) = o.as_array() {
+ for s in a {
+ base.$key_name |= match s.as_string() {
+ Some("address") => SanitizerSet::ADDRESS,
+ Some("leak") => SanitizerSet::LEAK,
+ Some("memory") => SanitizerSet::MEMORY,
+ Some("thread") => SanitizerSet::THREAD,
+ Some("hwaddress") => SanitizerSet::HWADDRESS,
+ Some(s) => return Err(format!("unknown sanitizer {}", s)),
+ _ => return Err(format!("not a string: {:?}", s)),
+ };
+ }
+ } else {
+ incorrect_type.push(name)
}
- Some(Ok(()))
- }).unwrap_or(Ok(()))
+ }
+ Ok::<(), String>(())
} );
($key_name:ident, crt_objects_fallback) => ( {
let name = (stringify!($key_name)).replace("_", "-");
- obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| {
+ obj.remove_key(&name[..]).and_then(|o| o.as_string().and_then(|s| {
match s.parse::<CrtObjectsFallback>() {
Ok(fallback) => base.$key_name = Some(fallback),
_ => return Some(Err(format!("'{}' is not a valid CRT objects fallback. \
} );
($key_name:ident, link_objects) => ( {
let name = (stringify!($key_name)).replace("_", "-");
- if let Some(val) = obj.find(&name[..]) {
+ if let Some(val) = obj.remove_key(&name[..]) {
let obj = val.as_object().ok_or_else(|| format!("{}: expected a \
JSON object with fields per CRT object kind.", name))?;
let mut args = CrtObjects::new();
} );
($key_name:ident, link_args) => ( {
let name = (stringify!($key_name)).replace("_", "-");
- if let Some(val) = obj.find(&name[..]) {
+ if let Some(val) = obj.remove_key(&name[..]) {
let obj = val.as_object().ok_or_else(|| format!("{}: expected a \
JSON object with fields per linker-flavor.", name))?;
let mut args = LinkArgs::new();
} );
($key_name:ident, env) => ( {
let name = (stringify!($key_name)).replace("_", "-");
- if let Some(a) = obj.find(&name[..]).and_then(|o| o.as_array()) {
- for o in a {
- if let Some(s) = o.as_string() {
- let p = s.split('=').collect::<Vec<_>>();
- if p.len() == 2 {
- let k = p[0].to_string();
- let v = p[1].to_string();
- base.$key_name.push((k, v));
+ if let Some(o) = obj.remove_key(&name[..]) {
+ if let Some(a) = o.as_array() {
+ for o in a {
+ if let Some(s) = o.as_string() {
+ let p = s.split('=').collect::<Vec<_>>();
+ if p.len() == 2 {
+ let k = p[0].to_string();
+ let v = p[1].to_string();
+ base.$key_name.push((k, v));
+ }
}
}
+ } else {
+ incorrect_type.push(name)
}
}
} );
($key_name:ident, Option<Abi>) => ( {
let name = (stringify!($key_name)).replace("_", "-");
- obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| {
+ obj.remove_key(&name[..]).and_then(|o| o.as_string().and_then(|s| {
match lookup_abi(s) {
Some(abi) => base.$key_name = Some(abi),
_ => return Some(Err(format!("'{}' is not a valid value for abi", s))),
})).unwrap_or(Ok(()))
} );
($key_name:ident, TargetFamilies) => ( {
- let value = obj.find("target-family");
- if let Some(v) = value.and_then(Json::as_array) {
- base.$key_name = v.iter()
- .map(|a| a.as_string().unwrap().to_string())
- .collect();
- } else if let Some(v) = value.and_then(Json::as_string) {
- base.$key_name = vec![v.to_string()];
+ if let Some(value) = obj.remove_key("target-family") {
+ if let Some(v) = Json::as_array(&value) {
+ base.$key_name = v.iter()
+ .map(|a| a.as_string().unwrap().to_string())
+ .collect();
+ } else if let Some(v) = Json::as_string(&value) {
+ base.$key_name = vec![v.to_string()];
+ }
}
} );
}
- if let Some(s) = obj.find("target-endian").and_then(Json::as_string) {
- base.endian = s.parse()?;
+ if let Some(j) = obj.remove_key("target-endian") {
+ if let Some(s) = Json::as_string(&j) {
+ base.endian = s.parse()?;
+ } else {
+ incorrect_type.push("target-endian".to_string())
+ }
}
+
+ if let Some(fp) = obj.remove_key("frame-pointer") {
+ if let Some(s) = Json::as_string(&fp) {
+ base.frame_pointer = s
+ .parse()
+ .map_err(|()| format!("'{}' is not a valid value for frame-pointer", s))?;
+ } else {
+ incorrect_type.push("frame-pointer".to_string())
+ }
+ }
+
key!(is_builtin, bool);
key!(c_int_width = "target-c-int-width");
key!(os);
key!(env);
+ key!(abi);
key!(vendor);
key!(linker_flavor, LinkerFlavor)?;
key!(linker, optional);
key!(code_model, CodeModel)?;
key!(tls_model, TlsModel)?;
key!(disable_redzone, bool);
- key!(eliminate_frame_pointer, bool);
key!(function_sections, bool);
key!(dll_prefix);
key!(dll_suffix);
key!(supported_sanitizers, SanitizerSet)?;
key!(default_adjusted_cabi, Option<Abi>)?;
- // NB: The old name is deprecated, but support for it is retained for
- // compatibility.
- for name in ["abi-blacklist", "unsupported-abis"].iter() {
- if let Some(array) = obj.find(name).and_then(Json::as_array) {
- for name in array.iter().filter_map(|abi| abi.as_string()) {
- match lookup_abi(name) {
- Some(abi) => {
- if abi.generic() {
- return Err(format!(
- "The ABI \"{}\" is considered to be supported on all \
- targets and cannot be marked unsupported",
- abi
- ));
- }
-
- base.unsupported_abis.push(abi)
- }
- None => {
- return Err(format!(
- "Unknown ABI \"{}\" in target specification",
- name
- ));
- }
- }
- }
- }
+ if base.is_builtin {
+ // This can cause unfortunate ICEs later down the line.
+ return Err(format!("may not set is_builtin for targets not built-in"));
}
-
- Ok(base)
+ // Each field should have been read using `Json::remove_key` so any keys remaining are unused.
+ let remaining_keys = obj.as_object().ok_or("Expected JSON object for target")?.keys();
+ Ok((
+ base,
+ TargetWarnings { unused_fields: remaining_keys.cloned().collect(), incorrect_type },
+ ))
}
/// Search for a JSON file specifying the given target triple.
///
/// The error string could come from any of the APIs called, including filesystem access and
/// JSON decoding.
- pub fn search(target_triple: &TargetTriple, sysroot: &PathBuf) -> Result<Target, String> {
+ pub fn search(
+ target_triple: &TargetTriple,
+ sysroot: &PathBuf,
+ ) -> Result<(Target, TargetWarnings), String> {
use rustc_serialize::json;
use std::env;
use std::fs;
- fn load_file(path: &Path) -> Result<Target, String> {
+ fn load_file(path: &Path) -> Result<(Target, TargetWarnings), String> {
let contents = fs::read(path).map_err(|e| e.to_string())?;
let obj = json::from_reader(&mut &contents[..]).map_err(|e| e.to_string())?;
Target::from_json(obj)
TargetTriple::TargetTriple(ref target_triple) => {
// check if triple is in list of built-in targets
if let Some(t) = load_builtin(target_triple) {
- return Ok(t);
+ return Ok((t, TargetWarnings::empty()));
}
// search for a file named `target_triple`.json in RUST_TARGET_PATH
target_option_val!(c_int_width, "target-c-int-width");
target_option_val!(os);
target_option_val!(env);
+ target_option_val!(abi);
target_option_val!(vendor);
target_option_val!(linker_flavor);
target_option_val!(linker);
target_option_val!(code_model);
target_option_val!(tls_model);
target_option_val!(disable_redzone);
- target_option_val!(eliminate_frame_pointer);
+ target_option_val!(frame_pointer);
target_option_val!(function_sections);
target_option_val!(dll_prefix);
target_option_val!(dll_suffix);
d.insert("default-adjusted-cabi".to_string(), Abi::name(abi).to_json());
}
- if default.unsupported_abis != self.unsupported_abis {
- d.insert(
- "unsupported-abis".to_string(),
- self.unsupported_abis
- .iter()
- .map(|&name| Abi::name(name).to_json())
- .collect::<Vec<_>>()
- .to_json(),
- );
- }
-
Json::Object(d)
}
}