]> git.lizzy.rs Git - rust.git/blob - src/librustc_codegen_utils/link.rs
Rollup merge of #57856 - lzutao:fix-old-first-edition, r=steveklabnik
[rust.git] / src / librustc_codegen_utils / link.rs
1 use rustc::session::config::{self, OutputFilenames, Input, OutputType};
2 use rustc::session::Session;
3 use std::path::{Path, PathBuf};
4 use syntax::{ast, attr};
5 use syntax_pos::Span;
6
7 pub fn out_filename(sess: &Session,
8                 crate_type: config::CrateType,
9                 outputs: &OutputFilenames,
10                 crate_name: &str)
11                 -> PathBuf {
12     let default_filename = filename_for_input(sess, crate_type, crate_name, outputs);
13     let out_filename = outputs.outputs.get(&OutputType::Exe)
14                               .and_then(|s| s.to_owned())
15                               .or_else(|| outputs.single_output_file.clone())
16                               .unwrap_or(default_filename);
17
18     check_file_is_writeable(&out_filename, sess);
19
20     out_filename
21 }
22
23 // Make sure files are writeable.  Mac, FreeBSD, and Windows system linkers
24 // check this already -- however, the Linux linker will happily overwrite a
25 // read-only file.  We should be consistent.
26 pub fn check_file_is_writeable(file: &Path, sess: &Session) {
27     if !is_writeable(file) {
28         sess.fatal(&format!("output file {} is not writeable -- check its \
29                             permissions", file.display()));
30     }
31 }
32
33 fn is_writeable(p: &Path) -> bool {
34     match p.metadata() {
35         Err(..) => true,
36         Ok(m) => !m.permissions().readonly()
37     }
38 }
39
40 pub fn find_crate_name(sess: Option<&Session>,
41                        attrs: &[ast::Attribute],
42                        input: &Input) -> String {
43     let validate = |s: String, span: Option<Span>| {
44         rustc_metadata::validate_crate_name(sess, &s, span);
45         s
46     };
47
48     // Look in attributes 100% of the time to make sure the attribute is marked
49     // as used. After doing this, however, we still prioritize a crate name from
50     // the command line over one found in the #[crate_name] attribute. If we
51     // find both we ensure that they're the same later on as well.
52     let attr_crate_name = attr::find_by_name(attrs, "crate_name")
53         .and_then(|at| at.value_str().map(|s| (at, s)));
54
55     if let Some(sess) = sess {
56         if let Some(ref s) = sess.opts.crate_name {
57             if let Some((attr, name)) = attr_crate_name {
58                 if name != &**s {
59                     let msg = format!("--crate-name and #[crate_name] are \
60                                        required to match, but `{}` != `{}`",
61                                       s, name);
62                     sess.span_err(attr.span, &msg);
63                 }
64             }
65             return validate(s.clone(), None);
66         }
67     }
68
69     if let Some((attr, s)) = attr_crate_name {
70         return validate(s.to_string(), Some(attr.span));
71     }
72     if let Input::File(ref path) = *input {
73         if let Some(s) = path.file_stem().and_then(|s| s.to_str()) {
74             if s.starts_with("-") {
75                 let msg = format!("crate names cannot start with a `-`, but \
76                                    `{}` has a leading hyphen", s);
77                 if let Some(sess) = sess {
78                     sess.err(&msg);
79                 }
80             } else {
81                 return validate(s.replace("-", "_"), None);
82             }
83         }
84     }
85
86     "rust_out".to_string()
87 }
88
89 pub fn filename_for_metadata(sess: &Session,
90                              crate_name: &str,
91                              outputs: &OutputFilenames) -> PathBuf {
92     let libname = format!("{}{}", crate_name, sess.opts.cg.extra_filename);
93
94     let out_filename = outputs.single_output_file.clone()
95         .unwrap_or_else(|| outputs.out_directory.join(&format!("lib{}.rmeta", libname)));
96
97     check_file_is_writeable(&out_filename, sess);
98
99     out_filename
100 }
101
102 pub fn filename_for_input(sess: &Session,
103                           crate_type: config::CrateType,
104                           crate_name: &str,
105                           outputs: &OutputFilenames) -> PathBuf {
106     let libname = format!("{}{}", crate_name, sess.opts.cg.extra_filename);
107
108     match crate_type {
109         config::CrateType::Rlib => {
110             outputs.out_directory.join(&format!("lib{}.rlib", libname))
111         }
112         config::CrateType::Cdylib |
113         config::CrateType::ProcMacro |
114         config::CrateType::Dylib => {
115             let (prefix, suffix) = (&sess.target.target.options.dll_prefix,
116                                     &sess.target.target.options.dll_suffix);
117             outputs.out_directory.join(&format!("{}{}{}", prefix, libname,
118                                                 suffix))
119         }
120         config::CrateType::Staticlib => {
121             let (prefix, suffix) = (&sess.target.target.options.staticlib_prefix,
122                                     &sess.target.target.options.staticlib_suffix);
123             outputs.out_directory.join(&format!("{}{}{}", prefix, libname,
124                                                 suffix))
125         }
126         config::CrateType::Executable => {
127             let suffix = &sess.target.target.options.exe_suffix;
128             let out_filename = outputs.path(OutputType::Exe);
129             if suffix.is_empty() {
130                 out_filename
131             } else {
132                 out_filename.with_extension(&suffix[1..])
133             }
134         }
135     }
136 }
137
138 /// Returns default crate type for target
139 ///
140 /// Default crate type is used when crate type isn't provided neither
141 /// through cmd line arguments nor through crate attributes
142 ///
143 /// It is CrateType::Executable for all platforms but iOS as there is no
144 /// way to run iOS binaries anyway without jailbreaking and
145 /// interaction with Rust code through static library is the only
146 /// option for now
147 pub fn default_output_for_target(sess: &Session) -> config::CrateType {
148     if !sess.target.target.options.executables {
149         config::CrateType::Staticlib
150     } else {
151         config::CrateType::Executable
152     }
153 }
154
155 /// Checks if target supports crate_type as output
156 pub fn invalid_output_for_target(sess: &Session,
157                                  crate_type: config::CrateType) -> bool {
158     match crate_type {
159         config::CrateType::Cdylib |
160         config::CrateType::Dylib |
161         config::CrateType::ProcMacro => {
162             if !sess.target.target.options.dynamic_linking {
163                 return true
164             }
165             if sess.crt_static() && !sess.target.target.options.crt_static_allows_dylibs {
166                 return true
167             }
168         }
169         _ => {}
170     }
171     if sess.target.target.options.only_cdylib {
172         match crate_type {
173             config::CrateType::ProcMacro | config::CrateType::Dylib => return true,
174             _ => {}
175         }
176     }
177     if !sess.target.target.options.executables {
178         if crate_type == config::CrateType::Executable {
179             return true
180         }
181     }
182
183     false
184 }