rustc_session/
output.rs

1//! Related to out filenames of compilation (e.g. binaries).
2
3use std::path::Path;
4
5use rustc_ast as ast;
6use rustc_span::{Span, Symbol, sym};
7
8use crate::Session;
9use crate::config::{self, CrateType, OutFileName, OutputFilenames, OutputType};
10use crate::errors::{self, CrateNameEmpty, FileIsNotWriteable, InvalidCharacterInCrateName};
11
12pub fn out_filename(
13    sess: &Session,
14    crate_type: CrateType,
15    outputs: &OutputFilenames,
16    crate_name: Symbol,
17) -> OutFileName {
18    let default_filename = filename_for_input(sess, crate_type, crate_name, outputs);
19    let out_filename = outputs
20        .outputs
21        .get(&OutputType::Exe)
22        .and_then(|s| s.to_owned())
23        .or_else(|| outputs.single_output_file.clone())
24        .unwrap_or(default_filename);
25
26    if let OutFileName::Real(ref path) = out_filename {
27        check_file_is_writeable(path, sess);
28    }
29
30    out_filename
31}
32
33/// Make sure files are writeable. Mac, FreeBSD, and Windows system linkers
34/// check this already -- however, the Linux linker will happily overwrite a
35/// read-only file. We should be consistent.
36pub fn check_file_is_writeable(file: &Path, sess: &Session) {
37    if !is_writeable(file) {
38        sess.dcx().emit_fatal(FileIsNotWriteable { file });
39    }
40}
41
42fn is_writeable(p: &Path) -> bool {
43    match p.metadata() {
44        Err(..) => true,
45        Ok(m) => !m.permissions().readonly(),
46    }
47}
48
49/// Validate the given crate name.
50///
51/// Note that this validation is more permissive than identifier parsing. It considers
52/// non-empty sequences of alphanumeric and underscore characters to be valid crate names.
53/// Most notably, it accepts names starting with a numeric character like `0`!
54///
55/// Furthermore, this shouldn't be taken as the canonical crate name validator.
56/// Other places may use a more restrictive grammar (e.g., identifier or ASCII identifier).
57pub fn validate_crate_name(sess: &Session, crate_name: Symbol, span: Option<Span>) {
58    let mut guar = None;
59
60    if crate_name.is_empty() {
61        guar = Some(sess.dcx().emit_err(CrateNameEmpty { span }));
62    }
63
64    for c in crate_name.as_str().chars() {
65        if c.is_alphanumeric() || c == '_' {
66            continue;
67        }
68        guar = Some(sess.dcx().emit_err(InvalidCharacterInCrateName {
69            span,
70            character: c,
71            crate_name,
72        }));
73    }
74
75    if let Some(guar) = guar {
76        guar.raise_fatal();
77    }
78}
79
80pub fn filename_for_metadata(sess: &Session, outputs: &OutputFilenames) -> OutFileName {
81    let out_filename = outputs.path(OutputType::Metadata);
82    if let OutFileName::Real(ref path) = out_filename {
83        check_file_is_writeable(path, sess);
84    }
85    out_filename
86}
87
88pub fn filename_for_input(
89    sess: &Session,
90    crate_type: CrateType,
91    crate_name: Symbol,
92    outputs: &OutputFilenames,
93) -> OutFileName {
94    let libname = format!("{}{}", crate_name, sess.opts.cg.extra_filename);
95
96    match crate_type {
97        CrateType::Rlib => {
98            OutFileName::Real(outputs.out_directory.join(&format!("lib{libname}.rlib")))
99        }
100        CrateType::Cdylib | CrateType::ProcMacro | CrateType::Dylib | CrateType::Sdylib => {
101            let (prefix, suffix) = (&sess.target.dll_prefix, &sess.target.dll_suffix);
102            OutFileName::Real(outputs.out_directory.join(&format!("{prefix}{libname}{suffix}")))
103        }
104        CrateType::Staticlib => {
105            let (prefix, suffix) = sess.staticlib_components(false);
106            OutFileName::Real(outputs.out_directory.join(&format!("{prefix}{libname}{suffix}")))
107        }
108        CrateType::Executable => {
109            let suffix = &sess.target.exe_suffix;
110            let out_filename = outputs.path(OutputType::Exe);
111            if let OutFileName::Real(ref path) = out_filename {
112                if suffix.is_empty() {
113                    out_filename
114                } else {
115                    OutFileName::Real(path.with_extension(&suffix[1..]))
116                }
117            } else {
118                out_filename
119            }
120        }
121    }
122}
123
124/// Returns default crate type for target
125///
126/// Default crate type is used when crate type isn't provided neither
127/// through cmd line arguments nor through crate attributes
128///
129/// It is CrateType::Executable for all platforms but iOS as there is no
130/// way to run iOS binaries anyway without jailbreaking and
131/// interaction with Rust code through static library is the only
132/// option for now
133pub fn default_output_for_target(sess: &Session) -> CrateType {
134    if !sess.target.executables { CrateType::Staticlib } else { CrateType::Executable }
135}
136
137/// Checks if target supports crate_type as output
138pub fn invalid_output_for_target(sess: &Session, crate_type: CrateType) -> bool {
139    if let CrateType::Cdylib | CrateType::Dylib | CrateType::ProcMacro = crate_type {
140        if !sess.target.dynamic_linking {
141            return true;
142        }
143        if sess.crt_static(Some(crate_type)) && !sess.target.crt_static_allows_dylibs {
144            return true;
145        }
146    }
147    if let CrateType::ProcMacro | CrateType::Dylib = crate_type
148        && sess.target.only_cdylib
149    {
150        return true;
151    }
152    if let CrateType::Executable = crate_type
153        && !sess.target.executables
154    {
155        return true;
156    }
157
158    false
159}
160
161pub const CRATE_TYPES: &[(Symbol, CrateType)] = &[
162    (sym::rlib, CrateType::Rlib),
163    (sym::dylib, CrateType::Dylib),
164    (sym::cdylib, CrateType::Cdylib),
165    (sym::lib, config::default_lib_output()),
166    (sym::staticlib, CrateType::Staticlib),
167    (sym::proc_dash_macro, CrateType::ProcMacro),
168    (sym::bin, CrateType::Executable),
169    (sym::sdylib, CrateType::Sdylib),
170];
171
172pub fn categorize_crate_type(s: Symbol) -> Option<CrateType> {
173    Some(CRATE_TYPES.iter().find(|(key, _)| *key == s)?.1)
174}
175
176pub fn collect_crate_types(
177    session: &Session,
178    backend_crate_types: &[CrateType],
179    codegen_backend_name: &'static str,
180    attrs: &[ast::Attribute],
181) -> Vec<CrateType> {
182    // If we're generating a test executable, then ignore all other output
183    // styles at all other locations
184    if session.opts.test {
185        if !session.target.executables {
186            session.dcx().emit_warn(errors::UnsupportedCrateTypeForTarget {
187                crate_type: CrateType::Executable,
188                target_triple: &session.opts.target_triple,
189            });
190            return Vec::new();
191        }
192        return vec![CrateType::Executable];
193    }
194
195    // Shadow `sdylib` crate type in interface build.
196    if session.opts.unstable_opts.build_sdylib_interface {
197        return vec![CrateType::Rlib];
198    }
199
200    // Only check command line flags if present. If no types are specified by
201    // command line, then reuse the empty `base` Vec to hold the types that
202    // will be found in crate attributes.
203    // JUSTIFICATION: before wrapper fn is available
204    #[allow(rustc::bad_opt_access)]
205    let mut base = session.opts.crate_types.clone();
206    if base.is_empty() {
207        let attr_types = attrs.iter().filter_map(|a| {
208            if a.has_name(sym::crate_type)
209                && let Some(s) = a.value_str()
210            {
211                categorize_crate_type(s)
212            } else {
213                None
214            }
215        });
216        base.extend(attr_types);
217        if base.is_empty() {
218            base.push(default_output_for_target(session));
219        } else {
220            base.sort();
221            base.dedup();
222        }
223    }
224
225    base.retain(|crate_type| {
226        if invalid_output_for_target(session, *crate_type) {
227            session.dcx().emit_warn(errors::UnsupportedCrateTypeForTarget {
228                crate_type: *crate_type,
229                target_triple: &session.opts.target_triple,
230            });
231            false
232        } else if !backend_crate_types.contains(crate_type) {
233            session.dcx().emit_warn(errors::UnsupportedCrateTypeForCodegenBackend {
234                crate_type: *crate_type,
235                codegen_backend: codegen_backend_name,
236            });
237            false
238        } else {
239            true
240        }
241    });
242
243    base
244}