1use rustc_feature::UnstableFeatures;
8use rustc_hir::attrs::NativeLibKind;
9
10use crate::EarlyDiagCtxt;
11use crate::config::UnstableOptions;
12use crate::utils::NativeLib;
13
14#[cfg(test)]
15mod tests;
16
17pub(crate) fn parse_native_libs(
19 early_dcx: &EarlyDiagCtxt,
20 unstable_opts: &UnstableOptions,
21 unstable_features: UnstableFeatures,
22 matches: &getopts::Matches,
23) -> Vec<NativeLib> {
24 let cx = ParseNativeLibCx {
25 early_dcx,
26 unstable_options_enabled: unstable_opts.unstable_options,
27 is_nightly: unstable_features.is_nightly_build(),
28 };
29 matches.opt_strs("l").into_iter().map(|value| parse_native_lib(&cx, &value)).collect()
30}
31
32struct ParseNativeLibCx<'a> {
33 early_dcx: &'a EarlyDiagCtxt,
34 unstable_options_enabled: bool,
35 is_nightly: bool,
36}
37
38impl ParseNativeLibCx<'_> {
39 fn on_unstable_value(&self, message: &str, if_nightly: &str, if_stable: &str) {
42 if self.unstable_options_enabled {
43 return;
44 }
45
46 let suffix = if self.is_nightly { if_nightly } else { if_stable };
47 self.early_dcx.early_fatal(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}{1}", message, suffix))
})format!("{message}{suffix}"));
48 }
49}
50
51fn parse_native_lib(cx: &ParseNativeLibCx<'_>, value: &str) -> NativeLib {
53 let NativeLibParts { kind, modifiers, name, new_name } = split_native_lib_value(value);
54
55 let kind = kind.map_or(NativeLibKind::Unspecified, |kind| match kind {
56 "static" => NativeLibKind::Static { bundle: None, whole_archive: None },
57 "dylib" => NativeLibKind::Dylib { as_needed: None },
58 "framework" => NativeLibKind::Framework { as_needed: None },
59 "link-arg" => {
60 cx.on_unstable_value(
61 "library kind `link-arg` is unstable",
62 ", the `-Z unstable-options` flag must also be passed to use it",
63 " and only accepted on the nightly compiler",
64 );
65 NativeLibKind::LinkArg
66 }
67 _ => cx.early_dcx.early_fatal(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("unknown library kind `{0}`, expected one of: static, dylib, framework, link-arg",
kind))
})format!(
68 "unknown library kind `{kind}`, expected one of: static, dylib, framework, link-arg"
69 )),
70 });
71
72 let mut native_lib = NativeLib {
74 name: name.to_owned(),
75 new_name: new_name.map(str::to_owned),
76 kind,
77 verbatim: None,
78 };
79
80 if let Some(modifiers) = modifiers {
81 for modifier in modifiers.split(',') {
83 parse_and_apply_modifier(cx, modifier, &mut native_lib);
84 }
85 }
86
87 if native_lib.name.is_empty() {
88 cx.early_dcx.early_fatal("library name must not be empty");
89 }
90
91 native_lib
92}
93
94fn parse_and_apply_modifier(cx: &ParseNativeLibCx<'_>, modifier: &str, native_lib: &mut NativeLib) {
100 let early_dcx = cx.early_dcx;
101
102 let (modifier, value) = match modifier.split_at_checked(1) {
104 Some(("+", m)) => (m, true),
105 Some(("-", m)) => (m, false),
106 _ => cx.early_dcx.early_fatal(
107 "invalid linking modifier syntax, expected '+' or '-' prefix \
108 before one of: bundle, verbatim, whole-archive, as-needed",
109 ),
110 };
111
112 let assign_modifier = |opt_bool: &mut Option<bool>| {
115 if opt_bool.is_some() {
116 let msg = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("multiple `{0}` modifiers in a single `-l` option",
modifier))
})format!("multiple `{modifier}` modifiers in a single `-l` option");
117 early_dcx.early_fatal(msg)
118 }
119 *opt_bool = Some(value);
120 };
121
122 match (modifier, &mut native_lib.kind) {
124 ("bundle", NativeLibKind::Static { bundle, .. }) => assign_modifier(bundle),
125 ("bundle", _) => early_dcx
126 .early_fatal("linking modifier `bundle` is only compatible with `static` linking kind"),
127
128 ("verbatim", _) => assign_modifier(&mut native_lib.verbatim),
129
130 ("whole-archive", NativeLibKind::Static { whole_archive, .. }) => {
131 assign_modifier(whole_archive)
132 }
133 ("whole-archive", _) => early_dcx.early_fatal(
134 "linking modifier `whole-archive` is only compatible with `static` linking kind",
135 ),
136
137 ("as-needed", NativeLibKind::Dylib { as_needed })
138 | ("as-needed", NativeLibKind::Framework { as_needed })
139 | ("as-needed", NativeLibKind::RawDylib { as_needed }) => {
140 cx.on_unstable_value(
141 "linking modifier `as-needed` is unstable",
142 ", the `-Z unstable-options` flag must also be passed to use it",
143 " and only accepted on the nightly compiler",
144 );
145 assign_modifier(as_needed)
146 }
147 ("as-needed", _) => early_dcx.early_fatal(
148 "linking modifier `as-needed` is only compatible with \
149 `dylib` and `framework` linking kinds",
150 ),
151
152 _ => early_dcx.early_fatal(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("unknown linking modifier `{0}`, expected one of: bundle, verbatim, whole-archive, as-needed",
modifier))
})format!(
153 "unknown linking modifier `{modifier}`, expected one \
154 of: bundle, verbatim, whole-archive, as-needed"
155 )),
156 }
157}
158
159#[derive(#[automatically_derived]
impl<'a> ::core::fmt::Debug for NativeLibParts<'a> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field4_finish(f,
"NativeLibParts", "kind", &self.kind, "modifiers",
&self.modifiers, "name", &self.name, "new_name", &&self.new_name)
}
}Debug, #[automatically_derived]
impl<'a> ::core::cmp::PartialEq for NativeLibParts<'a> {
#[inline]
fn eq(&self, other: &NativeLibParts<'a>) -> bool {
self.kind == other.kind && self.modifiers == other.modifiers &&
self.name == other.name && self.new_name == other.new_name
}
}PartialEq, #[automatically_derived]
impl<'a> ::core::cmp::Eq for NativeLibParts<'a> {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) -> () {
let _: ::core::cmp::AssertParamIsEq<Option<&'a str>>;
let _: ::core::cmp::AssertParamIsEq<Option<&'a str>>;
let _: ::core::cmp::AssertParamIsEq<&'a str>;
let _: ::core::cmp::AssertParamIsEq<Option<&'a str>>;
}
}Eq)]
160struct NativeLibParts<'a> {
161 kind: Option<&'a str>,
162 modifiers: Option<&'a str>,
163 name: &'a str,
164 new_name: Option<&'a str>,
165}
166
167fn split_native_lib_value(value: &str) -> NativeLibParts<'_> {
171 let name = value;
173 let (kind, name) = match name.split_once('=') {
174 Some((prefix, name)) => (Some(prefix), name),
175 None => (None, name),
176 };
177
178 let (kind, modifiers) = match kind {
180 Some(kind) => match kind.split_once(':') {
181 Some((kind, modifiers)) => (Some(kind), Some(modifiers)),
182 None => (Some(kind), None),
183 },
184 None => (None, None),
185 };
186
187 let (name, new_name) = match name.split_once(':') {
189 Some((name, new_name)) => (name, Some(new_name)),
190 None => (name, None),
191 };
192
193 NativeLibParts { kind, modifiers, name, new_name }
194}