1use 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
33pub 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
49pub 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 help: span.is_none().then_some(()),
73 }));
74 }
75
76 if let Some(guar) = guar {
77 guar.raise_fatal();
78 }
79}
80
81pub fn filename_for_metadata(sess: &Session, outputs: &OutputFilenames) -> OutFileName {
82 let out_filename = outputs.path(OutputType::Metadata);
83 if let OutFileName::Real(ref path) = out_filename {
84 check_file_is_writeable(path, sess);
85 }
86 out_filename
87}
88
89pub fn filename_for_input(
90 sess: &Session,
91 crate_type: CrateType,
92 crate_name: Symbol,
93 outputs: &OutputFilenames,
94) -> OutFileName {
95 let libname = format!("{}{}", crate_name, sess.opts.cg.extra_filename);
96
97 match crate_type {
98 CrateType::Rlib => {
99 OutFileName::Real(outputs.out_directory.join(&format!("lib{libname}.rlib")))
100 }
101 CrateType::Cdylib | CrateType::ProcMacro | CrateType::Dylib | CrateType::Sdylib => {
102 let (prefix, suffix) = (&sess.target.dll_prefix, &sess.target.dll_suffix);
103 OutFileName::Real(outputs.out_directory.join(&format!("{prefix}{libname}{suffix}")))
104 }
105 CrateType::Staticlib => {
106 let (prefix, suffix) = sess.staticlib_components(false);
107 OutFileName::Real(outputs.out_directory.join(&format!("{prefix}{libname}{suffix}")))
108 }
109 CrateType::Executable => {
110 let suffix = &sess.target.exe_suffix;
111 let out_filename = outputs.path(OutputType::Exe);
112 if let OutFileName::Real(ref path) = out_filename {
113 if suffix.is_empty() {
114 out_filename
115 } else {
116 OutFileName::Real(path.with_extension(&suffix[1..]))
117 }
118 } else {
119 out_filename
120 }
121 }
122 }
123}
124
125pub fn default_output_for_target(sess: &Session) -> CrateType {
135 if !sess.target.executables { CrateType::Staticlib } else { CrateType::Executable }
136}
137
138pub fn invalid_output_for_target(sess: &Session, crate_type: CrateType) -> bool {
140 if let CrateType::Cdylib | CrateType::Dylib | CrateType::ProcMacro = crate_type {
141 if !sess.target.dynamic_linking {
142 return true;
143 }
144 if sess.crt_static(Some(crate_type)) && !sess.target.crt_static_allows_dylibs {
145 return true;
146 }
147 }
148 if let CrateType::ProcMacro | CrateType::Dylib = crate_type
149 && sess.target.only_cdylib
150 {
151 return true;
152 }
153 if let CrateType::Executable = crate_type
154 && !sess.target.executables
155 {
156 return true;
157 }
158
159 false
160}
161
162pub const CRATE_TYPES: &[(Symbol, CrateType)] = &[
163 (sym::rlib, CrateType::Rlib),
164 (sym::dylib, CrateType::Dylib),
165 (sym::cdylib, CrateType::Cdylib),
166 (sym::lib, config::default_lib_output()),
167 (sym::staticlib, CrateType::Staticlib),
168 (sym::proc_dash_macro, CrateType::ProcMacro),
169 (sym::bin, CrateType::Executable),
170 (sym::sdylib, CrateType::Sdylib),
171];
172
173pub fn categorize_crate_type(s: Symbol) -> Option<CrateType> {
174 Some(CRATE_TYPES.iter().find(|(key, _)| *key == s)?.1)
175}
176
177pub fn collect_crate_types(
178 session: &Session,
179 backend_crate_types: &[CrateType],
180 codegen_backend_name: &'static str,
181 attrs: &[ast::Attribute],
182) -> Vec<CrateType> {
183 if session.opts.test {
186 if !session.target.executables {
187 session.dcx().emit_warn(errors::UnsupportedCrateTypeForTarget {
188 crate_type: CrateType::Executable,
189 target_triple: &session.opts.target_triple,
190 });
191 return Vec::new();
192 }
193 return vec![CrateType::Executable];
194 }
195
196 if session.opts.unstable_opts.build_sdylib_interface {
198 return vec![CrateType::Rlib];
199 }
200
201 #[allow(rustc::bad_opt_access)]
206 let mut base = session.opts.crate_types.clone();
207 if base.is_empty() {
208 let attr_types = attrs.iter().filter_map(|a| {
209 if a.has_name(sym::crate_type)
210 && let Some(s) = a.value_str()
211 {
212 categorize_crate_type(s)
213 } else {
214 None
215 }
216 });
217 base.extend(attr_types);
218 if base.is_empty() {
219 base.push(default_output_for_target(session));
220 } else {
221 base.sort();
222 base.dedup();
223 }
224 }
225
226 base.retain(|crate_type| {
227 if invalid_output_for_target(session, *crate_type) {
228 session.dcx().emit_warn(errors::UnsupportedCrateTypeForTarget {
229 crate_type: *crate_type,
230 target_triple: &session.opts.target_triple,
231 });
232 false
233 } else if !backend_crate_types.contains(crate_type) {
234 session.dcx().emit_warn(errors::UnsupportedCrateTypeForCodegenBackend {
235 crate_type: *crate_type,
236 codegen_backend: codegen_backend_name,
237 });
238 false
239 } else {
240 true
241 }
242 });
243
244 base
245}