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