Skip to main content

rustc_session/config/
native_libs.rs

1//! Parser for the `-l` command-line option, which links the generated crate to
2//! a native library.
3//!
4//! (There is also a similar but separate syntax for `#[link]` attributes,
5//! which have their own parser in `rustc_metadata`.)
6
7use 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
17/// Parses all `-l` options.
18pub(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    /// If unstable values are not permitted, exits with a fatal error made by
40    /// combining the given strings.
41    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
51/// Parses the value of a single `-l` option.
52fn 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" => {
57            NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None }
58        }
59        "dylib" => NativeLibKind::Dylib { as_needed: None },
60        "framework" => NativeLibKind::Framework { as_needed: None },
61        "link-arg" => {
62            cx.on_unstable_value(
63                "library kind `link-arg` is unstable",
64                ", the `-Z unstable-options` flag must also be passed to use it",
65                " and only accepted on the nightly compiler",
66            );
67            NativeLibKind::LinkArg
68        }
69        _ => 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!(
70            "unknown library kind `{kind}`, expected one of: static, dylib, framework, link-arg"
71        )),
72    });
73
74    // Provisionally create the result, so that modifiers can modify it.
75    let mut native_lib = NativeLib {
76        name: name.to_owned(),
77        new_name: new_name.map(str::to_owned),
78        kind,
79        verbatim: None,
80    };
81
82    if let Some(modifiers) = modifiers {
83        // If multiple modifiers are present, they are separated by commas.
84        for modifier in modifiers.split(',') {
85            parse_and_apply_modifier(cx, modifier, &mut native_lib);
86        }
87    }
88
89    if native_lib.name.is_empty() {
90        cx.early_dcx.early_fatal("library name must not be empty");
91    }
92
93    native_lib
94}
95
96/// Parses one of the comma-separated modifiers (prefixed by `+` or `-`), and
97/// modifies `native_lib` appropriately.
98///
99/// Exits with a fatal error if a malformed/unknown/inappropriate modifier is
100/// found.
101fn parse_and_apply_modifier(cx: &ParseNativeLibCx<'_>, modifier: &str, native_lib: &mut NativeLib) {
102    let early_dcx = cx.early_dcx;
103
104    // Split off the leading `+` or `-` into a boolean value.
105    let (modifier, value) = match modifier.split_at_checked(1) {
106        Some(("+", m)) => (m, true),
107        Some(("-", m)) => (m, false),
108        _ => cx.early_dcx.early_fatal(
109            "invalid linking modifier syntax, expected '+' or '-' prefix \
110             before one of: bundle, verbatim, whole-archive, as-needed, export-symbols",
111        ),
112    };
113
114    // Assigns the value (from `+` or `-`) to an empty `Option<bool>`, or emits
115    // a fatal error if the option has already been set.
116    let assign_modifier = |opt_bool: &mut Option<bool>| {
117        if opt_bool.is_some() {
118            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");
119            early_dcx.early_fatal(msg)
120        }
121        *opt_bool = Some(value);
122    };
123
124    // Check that the modifier is applicable to the native lib kind, and apply it.
125    match (modifier, &mut native_lib.kind) {
126        ("bundle", NativeLibKind::Static { bundle, .. }) => assign_modifier(bundle),
127        ("bundle", _) => early_dcx
128            .early_fatal("linking modifier `bundle` is only compatible with `static` linking kind"),
129
130        ("export-symbols", NativeLibKind::Static { export_symbols, .. }) => {
131            assign_modifier(export_symbols)
132        }
133        ("export-symbols", _) => early_dcx.early_fatal(
134            "linking modifier `export-symbols` is only compatible with `static` linking kind",
135        ),
136
137        ("verbatim", _) => assign_modifier(&mut native_lib.verbatim),
138
139        ("whole-archive", NativeLibKind::Static { whole_archive, .. }) => {
140            assign_modifier(whole_archive)
141        }
142        ("whole-archive", _) => early_dcx.early_fatal(
143            "linking modifier `whole-archive` is only compatible with `static` linking kind",
144        ),
145
146        ("as-needed", NativeLibKind::Dylib { as_needed })
147        | ("as-needed", NativeLibKind::Framework { as_needed })
148        | ("as-needed", NativeLibKind::RawDylib { as_needed }) => {
149            cx.on_unstable_value(
150                "linking modifier `as-needed` is unstable",
151                ", the `-Z unstable-options` flag must also be passed to use it",
152                " and only accepted on the nightly compiler",
153            );
154            assign_modifier(as_needed)
155        }
156        ("as-needed", _) => early_dcx.early_fatal(
157            "linking modifier `as-needed` is only compatible with \
158             `dylib` and `framework` linking kinds",
159        ),
160
161        _ => 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, export-symbols",
                modifier))
    })format!(
162            "unknown linking modifier `{modifier}`, expected one \
163             of: bundle, verbatim, whole-archive, as-needed, export-symbols"
164        )),
165    }
166}
167
168#[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)]
169struct NativeLibParts<'a> {
170    kind: Option<&'a str>,
171    modifiers: Option<&'a str>,
172    name: &'a str,
173    new_name: Option<&'a str>,
174}
175
176/// Splits a string of the form `[KIND[:MODIFIERS]=]NAME[:NEW_NAME]` into those
177/// individual parts. This cannot fail, but the resulting strings require
178/// further validation.
179fn split_native_lib_value(value: &str) -> NativeLibParts<'_> {
180    // Split the initial value into `[KIND=]NAME`.
181    let name = value;
182    let (kind, name) = match name.split_once('=') {
183        Some((prefix, name)) => (Some(prefix), name),
184        None => (None, name),
185    };
186
187    // Split the kind part, if present, into `KIND[:MODIFIERS]`.
188    let (kind, modifiers) = match kind {
189        Some(kind) => match kind.split_once(':') {
190            Some((kind, modifiers)) => (Some(kind), Some(modifiers)),
191            None => (Some(kind), None),
192        },
193        None => (None, None),
194    };
195
196    // Split the name part into `NAME[:NEW_NAME]`.
197    let (name, new_name) = match name.split_once(':') {
198        Some((name, new_name)) => (name, Some(new_name)),
199        None => (name, None),
200    };
201
202    NativeLibParts { kind, modifiers, name, new_name }
203}