rustc_session/
output.rs
1use std::path::Path;
4
5use rustc_ast::{self as ast, attr};
6use rustc_span::{Span, Symbol, sym};
7
8use crate::Session;
9use crate::config::{self, CrateType, Input, OutFileName, OutputFilenames, OutputType};
10use crate::errors::{
11 self, CrateNameDoesNotMatch, CrateNameEmpty, CrateNameInvalid, FileIsNotWriteable,
12 InvalidCharacterInCrateName, InvalidCrateNameHelp,
13};
14
15pub fn out_filename(
16 sess: &Session,
17 crate_type: CrateType,
18 outputs: &OutputFilenames,
19 crate_name: Symbol,
20) -> OutFileName {
21 let default_filename = filename_for_input(sess, crate_type, crate_name, outputs);
22 let out_filename = outputs
23 .outputs
24 .get(&OutputType::Exe)
25 .and_then(|s| s.to_owned())
26 .or_else(|| outputs.single_output_file.clone())
27 .unwrap_or(default_filename);
28
29 if let OutFileName::Real(ref path) = out_filename {
30 check_file_is_writeable(path, sess);
31 }
32
33 out_filename
34}
35
36pub fn check_file_is_writeable(file: &Path, sess: &Session) {
40 if !is_writeable(file) {
41 sess.dcx().emit_fatal(FileIsNotWriteable { file });
42 }
43}
44
45fn is_writeable(p: &Path) -> bool {
46 match p.metadata() {
47 Err(..) => true,
48 Ok(m) => !m.permissions().readonly(),
49 }
50}
51
52pub fn find_crate_name(sess: &Session, attrs: &[ast::Attribute]) -> Symbol {
53 let validate = |s: Symbol, span: Option<Span>| {
54 validate_crate_name(sess, s, span);
55 s
56 };
57
58 let attr_crate_name =
63 attr::find_by_name(attrs, sym::crate_name).and_then(|at| at.value_str().map(|s| (at, s)));
64
65 if let Some(ref s) = sess.opts.crate_name {
66 let s = Symbol::intern(s);
67 if let Some((attr, name)) = attr_crate_name {
68 if name != s {
69 sess.dcx().emit_err(CrateNameDoesNotMatch { span: attr.span, s, name });
70 }
71 }
72 return validate(s, None);
73 }
74
75 if let Some((attr, s)) = attr_crate_name {
76 return validate(s, Some(attr.span));
77 }
78 if let Input::File(ref path) = sess.io.input {
79 if let Some(s) = path.file_stem().and_then(|s| s.to_str()) {
80 if s.starts_with('-') {
81 sess.dcx().emit_err(CrateNameInvalid { s });
82 } else {
83 return validate(Symbol::intern(&s.replace('-', "_")), None);
84 }
85 }
86 }
87
88 sym::rust_out
89}
90
91pub fn validate_crate_name(sess: &Session, s: Symbol, sp: Option<Span>) {
92 let mut guar = None;
93 {
94 if s.is_empty() {
95 guar = Some(sess.dcx().emit_err(CrateNameEmpty { span: sp }));
96 }
97 for c in s.as_str().chars() {
98 if c.is_alphanumeric() {
99 continue;
100 }
101 if c == '_' {
102 continue;
103 }
104 guar = Some(sess.dcx().emit_err(InvalidCharacterInCrateName {
105 span: sp,
106 character: c,
107 crate_name: s,
108 crate_name_help: if sp.is_none() {
109 Some(InvalidCrateNameHelp::AddCrateName)
110 } else {
111 None
112 },
113 }));
114 }
115 }
116
117 if let Some(guar) = guar {
118 guar.raise_fatal();
119 }
120}
121
122pub fn filename_for_metadata(sess: &Session, outputs: &OutputFilenames) -> OutFileName {
123 let out_filename = outputs.path(OutputType::Metadata);
124 if let OutFileName::Real(ref path) = out_filename {
125 check_file_is_writeable(path, sess);
126 }
127 out_filename
128}
129
130pub fn filename_for_input(
131 sess: &Session,
132 crate_type: CrateType,
133 crate_name: Symbol,
134 outputs: &OutputFilenames,
135) -> OutFileName {
136 let libname = format!("{}{}", crate_name, sess.opts.cg.extra_filename);
137
138 match crate_type {
139 CrateType::Rlib => {
140 OutFileName::Real(outputs.out_directory.join(&format!("lib{libname}.rlib")))
141 }
142 CrateType::Cdylib | CrateType::ProcMacro | CrateType::Dylib => {
143 let (prefix, suffix) = (&sess.target.dll_prefix, &sess.target.dll_suffix);
144 OutFileName::Real(outputs.out_directory.join(&format!("{prefix}{libname}{suffix}")))
145 }
146 CrateType::Staticlib => {
147 let (prefix, suffix) = (&sess.target.staticlib_prefix, &sess.target.staticlib_suffix);
148 OutFileName::Real(outputs.out_directory.join(&format!("{prefix}{libname}{suffix}")))
149 }
150 CrateType::Executable => {
151 let suffix = &sess.target.exe_suffix;
152 let out_filename = outputs.path(OutputType::Exe);
153 if let OutFileName::Real(ref path) = out_filename {
154 if suffix.is_empty() {
155 out_filename
156 } else {
157 OutFileName::Real(path.with_extension(&suffix[1..]))
158 }
159 } else {
160 out_filename
161 }
162 }
163 }
164}
165
166pub fn default_output_for_target(sess: &Session) -> CrateType {
176 if !sess.target.executables { CrateType::Staticlib } else { CrateType::Executable }
177}
178
179pub fn invalid_output_for_target(sess: &Session, crate_type: CrateType) -> bool {
181 if let CrateType::Cdylib | CrateType::Dylib | CrateType::ProcMacro = crate_type {
182 if !sess.target.dynamic_linking {
183 return true;
184 }
185 if sess.crt_static(Some(crate_type)) && !sess.target.crt_static_allows_dylibs {
186 return true;
187 }
188 }
189 if let CrateType::ProcMacro | CrateType::Dylib = crate_type
190 && sess.target.only_cdylib
191 {
192 return true;
193 }
194 if let CrateType::Executable = crate_type
195 && !sess.target.executables
196 {
197 return true;
198 }
199
200 false
201}
202
203pub const CRATE_TYPES: &[(Symbol, CrateType)] = &[
204 (sym::rlib, CrateType::Rlib),
205 (sym::dylib, CrateType::Dylib),
206 (sym::cdylib, CrateType::Cdylib),
207 (sym::lib, config::default_lib_output()),
208 (sym::staticlib, CrateType::Staticlib),
209 (sym::proc_dash_macro, CrateType::ProcMacro),
210 (sym::bin, CrateType::Executable),
211];
212
213pub fn categorize_crate_type(s: Symbol) -> Option<CrateType> {
214 Some(CRATE_TYPES.iter().find(|(key, _)| *key == s)?.1)
215}
216
217pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec<CrateType> {
218 if session.opts.test {
221 return vec![CrateType::Executable];
222 }
223
224 #[allow(rustc::bad_opt_access)]
229 let mut base = session.opts.crate_types.clone();
230 if base.is_empty() {
231 let attr_types = attrs.iter().filter_map(|a| {
232 if a.has_name(sym::crate_type)
233 && let Some(s) = a.value_str()
234 {
235 categorize_crate_type(s)
236 } else {
237 None
238 }
239 });
240 base.extend(attr_types);
241 if base.is_empty() {
242 base.push(default_output_for_target(session));
243 } else {
244 base.sort();
245 base.dedup();
246 }
247 }
248
249 base.retain(|crate_type| {
250 if invalid_output_for_target(session, *crate_type) {
251 session.dcx().emit_warn(errors::UnsupportedCrateTypeForTarget {
252 crate_type: *crate_type,
253 target_triple: &session.opts.target_triple,
254 });
255 false
256 } else {
257 true
258 }
259 });
260
261 base
262}