|
34 | 34 | //! the target's settings, though `target-feature` and `link-args` will *add*
|
35 | 35 | //! to the list specified by the target, rather than replace.
|
36 | 36 |
|
| 37 | +// ignore-tidy-filelength |
| 38 | + |
| 39 | +use std::assert_matches::assert_matches; |
37 | 40 | use std::borrow::Cow;
|
38 | 41 | use std::collections::BTreeMap;
|
39 | 42 | use std::hash::{Hash, Hasher};
|
@@ -1605,13 +1608,11 @@ macro_rules! supported_targets {
|
1605 | 1608 |
|
1606 | 1609 | #[cfg(test)]
|
1607 | 1610 | mod tests {
|
1608 |
| - mod tests_impl; |
1609 |
| - |
1610 | 1611 | // Cannot put this into a separate file without duplication, make an exception.
|
1611 | 1612 | $(
|
1612 | 1613 | #[test] // `#[test]`
|
1613 | 1614 | fn $module() {
|
1614 |
| - tests_impl::test_target(crate::spec::targets::$module::target()); |
| 1615 | + crate::spec::targets::$module::target().test_target() |
1615 | 1616 | }
|
1616 | 1617 | )+
|
1617 | 1618 | }
|
@@ -1998,6 +1999,14 @@ impl TargetWarnings {
|
1998 | 1999 | }
|
1999 | 2000 | }
|
2000 | 2001 |
|
| 2002 | +/// For the [`Target::check_consistency`] function, determines whether the given target is a builtin or a JSON |
| 2003 | +/// target. |
| 2004 | +#[derive(Copy, Clone, Debug, PartialEq)] |
| 2005 | +enum TargetKind { |
| 2006 | + Json, |
| 2007 | + Builtin, |
| 2008 | +} |
| 2009 | + |
2001 | 2010 | /// Everything `rustc` knows about how to compile for a specific target.
|
2002 | 2011 | ///
|
2003 | 2012 | /// Every field here must be specified, and has no default value.
|
@@ -2849,6 +2858,306 @@ impl Target {
|
2849 | 2858 | self.max_atomic_width.unwrap_or_else(|| self.pointer_width.into())
|
2850 | 2859 | }
|
2851 | 2860 |
|
| 2861 | + fn check_consistency(&self, kind: TargetKind) -> Result<(), String> { |
| 2862 | + macro_rules! check { |
| 2863 | + ($b:expr, $($msg:tt)*) => { |
| 2864 | + if !$b { |
| 2865 | + return Err(format!($($msg)*)); |
| 2866 | + } |
| 2867 | + } |
| 2868 | + } |
| 2869 | + macro_rules! check_eq { |
| 2870 | + ($left:expr, $right:expr, $($msg:tt)*) => { |
| 2871 | + if ($left) != ($right) { |
| 2872 | + return Err(format!($($msg)*)); |
| 2873 | + } |
| 2874 | + } |
| 2875 | + } |
| 2876 | + macro_rules! check_ne { |
| 2877 | + ($left:expr, $right:expr, $($msg:tt)*) => { |
| 2878 | + if ($left) == ($right) { |
| 2879 | + return Err(format!($($msg)*)); |
| 2880 | + } |
| 2881 | + } |
| 2882 | + } |
| 2883 | + macro_rules! check_matches { |
| 2884 | + ($left:expr, $right:pat, $($msg:tt)*) => { |
| 2885 | + if !matches!($left, $right) { |
| 2886 | + return Err(format!($($msg)*)); |
| 2887 | + } |
| 2888 | + } |
| 2889 | + } |
| 2890 | + |
| 2891 | + check_eq!( |
| 2892 | + self.is_like_osx, |
| 2893 | + self.vendor == "apple", |
| 2894 | + "`is_like_osx` must be set if and only if `vendor` is `apple`" |
| 2895 | + ); |
| 2896 | + check_eq!( |
| 2897 | + self.is_like_solaris, |
| 2898 | + self.os == "solaris" || self.os == "illumos", |
| 2899 | + "`is_like_solaris` must be set if and only if `os` is `solaris` or `illumos`" |
| 2900 | + ); |
| 2901 | + check_eq!( |
| 2902 | + self.is_like_windows, |
| 2903 | + self.os == "windows" || self.os == "uefi", |
| 2904 | + "`is_like_windows` must be set if and only if `os` is `windows` or `uefi`" |
| 2905 | + ); |
| 2906 | + check_eq!( |
| 2907 | + self.is_like_wasm, |
| 2908 | + self.arch == "wasm32" || self.arch == "wasm64", |
| 2909 | + "`is_like_wasm` must be set if and only if `arch` is `wasm32` or `wasm64`" |
| 2910 | + ); |
| 2911 | + if self.is_like_msvc { |
| 2912 | + check!(self.is_like_windows, "if `is_like_msvc` is set, `is_like_windows` must be set"); |
| 2913 | + } |
| 2914 | + |
| 2915 | + // Check that default linker flavor is compatible with some other key properties. |
| 2916 | + check_eq!( |
| 2917 | + self.is_like_osx, |
| 2918 | + matches!(self.linker_flavor, LinkerFlavor::Darwin(..)), |
| 2919 | + "`linker_flavor` must be `darwin` if and only if `is_like_osx` is set" |
| 2920 | + ); |
| 2921 | + check_eq!( |
| 2922 | + self.is_like_msvc, |
| 2923 | + matches!(self.linker_flavor, LinkerFlavor::Msvc(..)), |
| 2924 | + "`linker_flavor` must be `mscv` if and only if `is_like_msvc` is set" |
| 2925 | + ); |
| 2926 | + check_eq!( |
| 2927 | + self.is_like_wasm && self.os != "emscripten", |
| 2928 | + matches!(self.linker_flavor, LinkerFlavor::WasmLld(..)), |
| 2929 | + "`linker_flavor` must be `wasm-lld` if and only if `is_like_wasm` is set and the `os` is not `emscripten`", |
| 2930 | + ); |
| 2931 | + check_eq!( |
| 2932 | + self.os == "emscripten", |
| 2933 | + matches!(self.linker_flavor, LinkerFlavor::EmCc), |
| 2934 | + "`linker_flavor` must be `em-cc` if and only if `os` is `emscripten`" |
| 2935 | + ); |
| 2936 | + check_eq!( |
| 2937 | + self.arch == "bpf", |
| 2938 | + matches!(self.linker_flavor, LinkerFlavor::Bpf), |
| 2939 | + "`linker_flavor` must be `bpf` if and only if `arch` is `bpf`" |
| 2940 | + ); |
| 2941 | + check_eq!( |
| 2942 | + self.arch == "nvptx64", |
| 2943 | + matches!(self.linker_flavor, LinkerFlavor::Ptx), |
| 2944 | + "`linker_flavor` must be `ptc` if and only if `arch` is `nvptx64`" |
| 2945 | + ); |
| 2946 | + |
| 2947 | + for args in [ |
| 2948 | + &self.pre_link_args, |
| 2949 | + &self.late_link_args, |
| 2950 | + &self.late_link_args_dynamic, |
| 2951 | + &self.late_link_args_static, |
| 2952 | + &self.post_link_args, |
| 2953 | + ] { |
| 2954 | + for (&flavor, flavor_args) in args { |
| 2955 | + check!(!flavor_args.is_empty(), "linker flavor args must not be empty"); |
| 2956 | + // Check that flavors mentioned in link args are compatible with the default flavor. |
| 2957 | + match self.linker_flavor { |
| 2958 | + LinkerFlavor::Gnu(..) => { |
| 2959 | + check_matches!( |
| 2960 | + flavor, |
| 2961 | + LinkerFlavor::Gnu(..), |
| 2962 | + "mixing GNU and non-GNU linker flavors" |
| 2963 | + ); |
| 2964 | + } |
| 2965 | + LinkerFlavor::Darwin(..) => { |
| 2966 | + check_matches!( |
| 2967 | + flavor, |
| 2968 | + LinkerFlavor::Darwin(..), |
| 2969 | + "mixing Darwin and non-Darwin linker flavors" |
| 2970 | + ) |
| 2971 | + } |
| 2972 | + LinkerFlavor::WasmLld(..) => { |
| 2973 | + check_matches!( |
| 2974 | + flavor, |
| 2975 | + LinkerFlavor::WasmLld(..), |
| 2976 | + "mixing wasm and non-wasm linker flavors" |
| 2977 | + ) |
| 2978 | + } |
| 2979 | + LinkerFlavor::Unix(..) => { |
| 2980 | + check_matches!( |
| 2981 | + flavor, |
| 2982 | + LinkerFlavor::Unix(..), |
| 2983 | + "mixing unix and non-unix linker flavors" |
| 2984 | + ); |
| 2985 | + } |
| 2986 | + LinkerFlavor::Msvc(..) => { |
| 2987 | + check_matches!( |
| 2988 | + flavor, |
| 2989 | + LinkerFlavor::Msvc(..), |
| 2990 | + "mixing MSVC and non-MSVC linker flavors" |
| 2991 | + ); |
| 2992 | + } |
| 2993 | + LinkerFlavor::EmCc |
| 2994 | + | LinkerFlavor::Bpf |
| 2995 | + | LinkerFlavor::Ptx |
| 2996 | + | LinkerFlavor::Llbc => { |
| 2997 | + check_eq!(flavor, self.linker_flavor, "mixing different linker flavors") |
| 2998 | + } |
| 2999 | + } |
| 3000 | + |
| 3001 | + // Check that link args for cc and non-cc versions of flavors are consistent. |
| 3002 | + let check_noncc = |noncc_flavor| -> Result<(), String> { |
| 3003 | + if let Some(noncc_args) = args.get(&noncc_flavor) { |
| 3004 | + for arg in flavor_args { |
| 3005 | + if let Some(suffix) = arg.strip_prefix("-Wl,") { |
| 3006 | + check!( |
| 3007 | + noncc_args.iter().any(|a| a == suffix), |
| 3008 | + " link args for cc and non-cc versions of flavors are not consistent" |
| 3009 | + ); |
| 3010 | + } |
| 3011 | + } |
| 3012 | + } |
| 3013 | + Ok(()) |
| 3014 | + }; |
| 3015 | + |
| 3016 | + match self.linker_flavor { |
| 3017 | + LinkerFlavor::Gnu(Cc::Yes, lld) => check_noncc(LinkerFlavor::Gnu(Cc::No, lld))?, |
| 3018 | + LinkerFlavor::WasmLld(Cc::Yes) => check_noncc(LinkerFlavor::WasmLld(Cc::No))?, |
| 3019 | + LinkerFlavor::Unix(Cc::Yes) => check_noncc(LinkerFlavor::Unix(Cc::No))?, |
| 3020 | + _ => {} |
| 3021 | + } |
| 3022 | + } |
| 3023 | + |
| 3024 | + // Check that link args for lld and non-lld versions of flavors are consistent. |
| 3025 | + for cc in [Cc::No, Cc::Yes] { |
| 3026 | + check_eq!( |
| 3027 | + args.get(&LinkerFlavor::Gnu(cc, Lld::No)), |
| 3028 | + args.get(&LinkerFlavor::Gnu(cc, Lld::Yes)), |
| 3029 | + "link args for lld and non-lld versions of flavors are not consistent", |
| 3030 | + ); |
| 3031 | + check_eq!( |
| 3032 | + args.get(&LinkerFlavor::Darwin(cc, Lld::No)), |
| 3033 | + args.get(&LinkerFlavor::Darwin(cc, Lld::Yes)), |
| 3034 | + "link args for lld and non-lld versions of flavors are not consistent", |
| 3035 | + ); |
| 3036 | + } |
| 3037 | + check_eq!( |
| 3038 | + args.get(&LinkerFlavor::Msvc(Lld::No)), |
| 3039 | + args.get(&LinkerFlavor::Msvc(Lld::Yes)), |
| 3040 | + "link args for lld and non-lld versions of flavors are not consistent", |
| 3041 | + ); |
| 3042 | + } |
| 3043 | + |
| 3044 | + if self.link_self_contained.is_disabled() { |
| 3045 | + check!( |
| 3046 | + self.pre_link_objects_self_contained.is_empty() |
| 3047 | + && self.post_link_objects_self_contained.is_empty(), |
| 3048 | + "if `link_self_contained` is disabled, then `pre_link_objects_self_contained` and `post_link_objects_self_contained` must be empty", |
| 3049 | + ); |
| 3050 | + } |
| 3051 | + |
| 3052 | + // If your target really needs to deviate from the rules below, |
| 3053 | + // except it and document the reasons. |
| 3054 | + // Keep the default "unknown" vendor instead. |
| 3055 | + check_ne!(self.vendor, "", "`vendor` cannot be empty"); |
| 3056 | + check_ne!(self.os, "", "`os` cannot be empty"); |
| 3057 | + if !self.can_use_os_unknown() { |
| 3058 | + // Keep the default "none" for bare metal targets instead. |
| 3059 | + check_ne!( |
| 3060 | + self.os, |
| 3061 | + "unknown", |
| 3062 | + "`unknown` os can only be used on particular targets; use `none` for bare-metal targets" |
| 3063 | + ); |
| 3064 | + } |
| 3065 | + |
| 3066 | + // Check dynamic linking stuff |
| 3067 | + // BPF: when targeting user space vms (like rbpf), those can load dynamic libraries. |
| 3068 | + // hexagon: when targeting QuRT, that OS can load dynamic libraries. |
| 3069 | + // wasm{32,64}: dynamic linking is inherent in the definition of the VM. |
| 3070 | + if self.os == "none" |
| 3071 | + && (self.arch != "bpf" |
| 3072 | + && self.arch != "hexagon" |
| 3073 | + && self.arch != "wasm32" |
| 3074 | + && self.arch != "wasm64") |
| 3075 | + { |
| 3076 | + check!( |
| 3077 | + !self.dynamic_linking, |
| 3078 | + "dynamic linking is not supported on this OS/architecture" |
| 3079 | + ); |
| 3080 | + } |
| 3081 | + if self.only_cdylib |
| 3082 | + || self.crt_static_allows_dylibs |
| 3083 | + || !self.late_link_args_dynamic.is_empty() |
| 3084 | + { |
| 3085 | + check!( |
| 3086 | + self.dynamic_linking, |
| 3087 | + "dynamic linking must be allowed when `only_cdylib` or `crt_static_allows_dylibs` or `late_link_args_dynamic` are set" |
| 3088 | + ); |
| 3089 | + } |
| 3090 | + // Apparently PIC was slow on wasm at some point, see comments in wasm_base.rs |
| 3091 | + if self.dynamic_linking && !(self.is_like_wasm && self.os != "emscripten") { |
| 3092 | + check_eq!( |
| 3093 | + self.relocation_model, |
| 3094 | + RelocModel::Pic, |
| 3095 | + "targets that support dynamic linking must use the `pic` relocation model" |
| 3096 | + ); |
| 3097 | + } |
| 3098 | + if self.position_independent_executables { |
| 3099 | + check_eq!( |
| 3100 | + self.relocation_model, |
| 3101 | + RelocModel::Pic, |
| 3102 | + "targets that support position-independent executables must use the `pic` relocation model" |
| 3103 | + ); |
| 3104 | + } |
| 3105 | + // The UEFI targets do not support dynamic linking but still require PIC (#101377). |
| 3106 | + // We also allow this for JSON targets since otherwise, our default values would fail this test. |
| 3107 | + // FIXME (https://github.com/rust-lang/rust/issues/133459): can we fix the defaults? |
| 3108 | + if self.relocation_model == RelocModel::Pic |
| 3109 | + && (self.os != "uefi" && kind == TargetKind::Builtin) |
| 3110 | + { |
| 3111 | + check!( |
| 3112 | + self.dynamic_linking || self.position_independent_executables, |
| 3113 | + "when the relocation model is `pic`, the target must support dynamic linking or use position-independent executables. \ |
| 3114 | + Set the relocation model to `static` to avoid this requirement" |
| 3115 | + ); |
| 3116 | + } |
| 3117 | + if self.static_position_independent_executables { |
| 3118 | + assert!(self.position_independent_executables); |
| 3119 | + } |
| 3120 | + if self.position_independent_executables { |
| 3121 | + assert!(self.executables); |
| 3122 | + } |
| 3123 | + |
| 3124 | + // Check crt static stuff |
| 3125 | + if self.crt_static_default || self.crt_static_allows_dylibs { |
| 3126 | + assert!(self.crt_static_respected); |
| 3127 | + } |
| 3128 | + |
| 3129 | + // Check that RISC-V targets always specify which ABI they use. |
| 3130 | + match &*self.arch { |
| 3131 | + "riscv32" => { |
| 3132 | + assert_matches!(&*self.llvm_abiname, "ilp32" | "ilp32f" | "ilp32d" | "ilp32e") |
| 3133 | + } |
| 3134 | + "riscv64" => { |
| 3135 | + // Note that the `lp64e` is still unstable as it's not (yet) part of the ELF psABI. |
| 3136 | + assert_matches!(&*self.llvm_abiname, "lp64" | "lp64f" | "lp64d" | "lp64q" | "lp64e") |
| 3137 | + } |
| 3138 | + _ => {} |
| 3139 | + } |
| 3140 | + |
| 3141 | + Ok(()) |
| 3142 | + } |
| 3143 | + |
| 3144 | + /// Test target self-consistency and JSON encoding/decoding roundtrip. |
| 3145 | + #[cfg(test)] |
| 3146 | + fn test_target(mut self) { |
| 3147 | + let recycled_target = Target::from_json(self.to_json()).map(|(j, _)| j); |
| 3148 | + self.update_to_cli(); |
| 3149 | + self.check_consistency(TargetKind::Builtin).unwrap(); |
| 3150 | + assert_eq!(recycled_target, Ok(self)); |
| 3151 | + } |
| 3152 | + |
| 3153 | + // Add your target to the whitelist if it has `std` library |
| 3154 | + // and you certainly want "unknown" for the OS name. |
| 3155 | + fn can_use_os_unknown(&self) -> bool { |
| 3156 | + self.llvm_target == "wasm32-unknown-unknown" |
| 3157 | + || self.llvm_target == "wasm64-unknown-unknown" |
| 3158 | + || (self.env == "sgx" && self.vendor == "fortanix") |
| 3159 | + } |
| 3160 | + |
2852 | 3161 | /// Loads a target descriptor from a JSON object.
|
2853 | 3162 | pub fn from_json(obj: Json) -> Result<(Target, TargetWarnings), String> {
|
2854 | 3163 | // While ugly, this code must remain this way to retain
|
@@ -3456,6 +3765,7 @@ impl Target {
|
3456 | 3765 | key!(supports_xray, bool);
|
3457 | 3766 |
|
3458 | 3767 | base.update_from_cli();
|
| 3768 | + base.check_consistency(TargetKind::Json)?; |
3459 | 3769 |
|
3460 | 3770 | // Each field should have been read using `Json::remove` so any keys remaining are unused.
|
3461 | 3771 | let remaining_keys = obj.keys();
|
|
0 commit comments