1 use libloading
::Library
;
3 use rustc_codegen_ssa
::traits
::CodegenBackend
;
4 use rustc_data_structures
::fx
::{FxHashMap, FxHashSet}
;
5 #[cfg(parallel_compiler)]
6 use rustc_data_structures
::jobserver
;
7 use rustc_data_structures
::sync
::Lrc
;
8 use rustc_errors
::registry
::Registry
;
9 #[cfg(parallel_compiler)]
10 use rustc_middle
::ty
::tls
;
11 use rustc_parse
::validate_attr
;
12 #[cfg(parallel_compiler)]
13 use rustc_query_impl
::QueryCtxt
;
14 use rustc_session
as session
;
15 use rustc_session
::config
::CheckCfg
;
16 use rustc_session
::config
::{self, CrateType}
;
17 use rustc_session
::config
::{ErrorOutputType, Input, OutputFilenames}
;
18 use rustc_session
::lint
::{self, BuiltinLintDiagnostics, LintBuffer}
;
19 use rustc_session
::parse
::CrateConfig
;
20 use rustc_session
::{early_error, filesearch, output, DiagnosticOutput, Session}
;
21 use rustc_span
::edition
::Edition
;
22 use rustc_span
::lev_distance
::find_best_match_for_name
;
23 use rustc_span
::source_map
::FileLoader
;
24 use rustc_span
::symbol
::{sym, Symbol}
;
26 use std
::env
::consts
::{DLL_PREFIX, DLL_SUFFIX}
;
27 use std
::lazy
::SyncOnceCell
;
29 #[cfg(not(parallel_compiler))]
31 use std
::path
::{Path, PathBuf}
;
32 use std
::sync
::atomic
::{AtomicBool, Ordering}
;
36 /// Function pointer type that constructs a new CodegenBackend.
37 pub type MakeBackendFn
= fn() -> Box
<dyn CodegenBackend
>;
39 /// Adds `target_feature = "..."` cfgs for a variety of platform
40 /// specific features (SSE, NEON etc.).
42 /// This is performed by checking whether a set of permitted features
43 /// is available on the target machine, by querying LLVM.
44 pub fn add_configuration(
45 cfg
: &mut CrateConfig
,
47 codegen_backend
: &dyn CodegenBackend
,
49 let tf
= sym
::target_feature
;
51 let target_features
= codegen_backend
.target_features(sess
);
52 sess
.target_features
.extend(target_features
.iter().cloned());
54 cfg
.extend(target_features
.into_iter().map(|feat
| (tf
, Some(feat
))));
56 if sess
.crt_static(None
) {
57 cfg
.insert((tf
, Some(sym
::crt_dash_static
)));
61 pub fn create_session(
62 sopts
: config
::Options
,
63 cfg
: FxHashSet
<(String
, Option
<String
>)>,
65 diagnostic_output
: DiagnosticOutput
,
66 file_loader
: Option
<Box
<dyn FileLoader
+ Send
+ Sync
+ '
static>>,
67 input_path
: Option
<PathBuf
>,
68 lint_caps
: FxHashMap
<lint
::LintId
, lint
::Level
>,
69 make_codegen_backend
: Option
<
70 Box
<dyn FnOnce(&config
::Options
) -> Box
<dyn CodegenBackend
> + Send
>,
72 descriptions
: Registry
,
73 ) -> (Lrc
<Session
>, Lrc
<Box
<dyn CodegenBackend
>>) {
74 let codegen_backend
= if let Some(make_codegen_backend
) = make_codegen_backend
{
75 make_codegen_backend(&sopts
)
79 sopts
.debugging_opts
.codegen_backend
.as_ref().map(|name
| &name
[..]),
83 // target_override is documented to be called before init(), so this is okay
84 let target_override
= codegen_backend
.target_override(&sopts
);
86 let mut sess
= session
::build_session(
96 codegen_backend
.init(&sess
);
98 let mut cfg
= config
::build_configuration(&sess
, config
::to_crate_config(cfg
));
99 add_configuration(&mut cfg
, &mut sess
, &*codegen_backend
);
101 let mut check_cfg
= config
::to_crate_check_config(check_cfg
);
102 check_cfg
.fill_well_known();
103 check_cfg
.fill_actual(&cfg
);
105 sess
.parse_sess
.config
= cfg
;
106 sess
.parse_sess
.check_config
= check_cfg
;
108 (Lrc
::new(sess
), Lrc
::new(codegen_backend
))
111 const STACK_SIZE
: usize = 8 * 1024 * 1024;
113 fn get_stack_size() -> Option
<usize> {
114 // FIXME: Hacks on hacks. If the env is trying to override the stack size
115 // then *don't* set it explicitly.
116 env
::var_os("RUST_MIN_STACK").is_none().then_some(STACK_SIZE
)
119 /// Like a `thread::Builder::spawn` followed by a `join()`, but avoids the need
120 /// for `'static` bounds.
121 #[cfg(not(parallel_compiler))]
122 fn scoped_thread
<F
: FnOnce() -> R
+ Send
, R
: Send
>(cfg
: thread
::Builder
, f
: F
) -> R
{
123 // SAFETY: join() is called immediately, so any closure captures are still
125 match unsafe { cfg.spawn_unchecked(f) }
.unwrap().join() {
127 Err(e
) => panic
::resume_unwind(e
),
131 #[cfg(not(parallel_compiler))]
132 pub fn run_in_thread_pool_with_globals
<F
: FnOnce() -> R
+ Send
, R
: Send
>(
137 let mut cfg
= thread
::Builder
::new().name("rustc".to_string());
139 if let Some(size
) = get_stack_size() {
140 cfg
= cfg
.stack_size(size
);
143 let main_handler
= move || rustc_span
::create_session_globals_then(edition
, f
);
145 scoped_thread(cfg
, main_handler
)
148 /// Creates a new thread and forwards information in thread locals to it.
149 /// The new thread runs the deadlock handler.
150 /// Must only be called when a deadlock is about to happen.
151 #[cfg(parallel_compiler)]
152 unsafe fn handle_deadlock() {
153 let registry
= rustc_rayon_core
::Registry
::current();
155 let context
= tls
::get_tlv();
156 assert
!(context
!= 0);
157 rustc_data_structures
::sync
::assert_sync
::<tls
::ImplicitCtxt
<'_
, '_
>>();
158 let icx
: &tls
::ImplicitCtxt
<'_
, '_
> = &*(context
as *const tls
::ImplicitCtxt
<'_
, '_
>);
160 let session_globals
= rustc_span
::with_session_globals(|sg
| sg
as *const _
);
161 let session_globals
= &*session_globals
;
162 thread
::spawn(move || {
163 tls
::enter_context(icx
, |_
| {
164 rustc_span
::set_session_globals_then(session_globals
, || {
165 tls
::with(|tcx
| QueryCtxt
::from_tcx(tcx
).deadlock(®istry
))
171 #[cfg(parallel_compiler)]
172 pub fn run_in_thread_pool_with_globals
<F
: FnOnce() -> R
+ Send
, R
: Send
>(
177 let mut config
= rayon
::ThreadPoolBuilder
::new()
178 .thread_name(|_
| "rustc".to_string())
179 .acquire_thread_handler(jobserver
::acquire_thread
)
180 .release_thread_handler(jobserver
::release_thread
)
181 .num_threads(threads
)
182 .deadlock_handler(|| unsafe { handle_deadlock() }
);
184 if let Some(size
) = get_stack_size() {
185 config
= config
.stack_size(size
);
188 let with_pool
= move |pool
: &rayon
::ThreadPool
| pool
.install(f
);
190 rustc_span
::create_session_globals_then(edition
, || {
191 rustc_span
::with_session_globals(|session_globals
| {
192 // The main handler runs for each Rayon worker thread and sets up
193 // the thread local rustc uses. `session_globals` is captured and set
194 // on the new threads.
195 let main_handler
= move |thread
: rayon
::ThreadBuilder
| {
196 rustc_span
::set_session_globals_then(session_globals
, || thread
.run())
199 config
.build_scoped(main_handler
, with_pool
).unwrap()
204 fn load_backend_from_dylib(path
: &Path
) -> MakeBackendFn
{
205 let lib
= unsafe { Library::new(path) }
.unwrap_or_else(|err
| {
206 let err
= format
!("couldn't load codegen backend {:?}: {}", path
, err
);
207 early_error(ErrorOutputType
::default(), &err
);
210 let backend_sym
= unsafe { lib.get::<MakeBackendFn>(b"__rustc_codegen_backend") }
211 .unwrap_or_else(|e
| {
212 let err
= format
!("couldn't load codegen backend: {}", e
);
213 early_error(ErrorOutputType
::default(), &err
);
216 // Intentionally leak the dynamic library. We can't ever unload it
217 // since the library can make things that will live arbitrarily long.
218 let backend_sym
= unsafe { backend_sym.into_raw() }
;
224 /// Get the codegen backend based on the name and specified sysroot.
226 /// A name of `None` indicates that the default backend should be used.
227 pub fn get_codegen_backend(
228 maybe_sysroot
: &Option
<PathBuf
>,
229 backend_name
: Option
<&str>,
230 ) -> Box
<dyn CodegenBackend
> {
231 static LOAD
: SyncOnceCell
<unsafe fn() -> Box
<dyn CodegenBackend
>> = SyncOnceCell
::new();
233 let load
= LOAD
.get_or_init(|| {
234 let default_codegen_backend
= option_env
!("CFG_DEFAULT_CODEGEN_BACKEND").unwrap_or("llvm");
236 match backend_name
.unwrap_or(default_codegen_backend
) {
237 filename
if filename
.contains('
.'
) => load_backend_from_dylib(filename
.as_ref()),
238 #[cfg(feature = "llvm")]
239 "llvm" => rustc_codegen_llvm
::LlvmCodegenBackend
::new
,
240 backend_name
=> get_codegen_sysroot(maybe_sysroot
, backend_name
),
244 // SAFETY: In case of a builtin codegen backend this is safe. In case of an external codegen
245 // backend we hope that the backend links against the same rustc_driver version. If this is not
246 // the case, we get UB.
250 // This is used for rustdoc, but it uses similar machinery to codegen backend
251 // loading, so we leave the code here. It is potentially useful for other tools
252 // that want to invoke the rustc binary while linking to rustc as well.
253 pub fn rustc_path
<'a
>() -> Option
<&'a Path
> {
254 static RUSTC_PATH
: SyncOnceCell
<Option
<PathBuf
>> = SyncOnceCell
::new();
256 const BIN_PATH
: &str = env
!("RUSTC_INSTALL_BINDIR");
258 RUSTC_PATH
.get_or_init(|| get_rustc_path_inner(BIN_PATH
)).as_ref().map(|v
| &**v
)
261 fn get_rustc_path_inner(bin_path
: &str) -> Option
<PathBuf
> {
262 sysroot_candidates().iter().find_map(|sysroot
| {
263 let candidate
= sysroot
.join(bin_path
).join(if cfg
!(target_os
= "windows") {
268 candidate
.exists().then_some(candidate
)
272 fn sysroot_candidates() -> Vec
<PathBuf
> {
273 let target
= session
::config
::host_triple();
274 let mut sysroot_candidates
= vec
![filesearch
::get_or_default_sysroot()];
275 let path
= current_dll_path().and_then(|s
| s
.canonicalize().ok());
276 if let Some(dll
) = path
{
277 // use `parent` twice to chop off the file name and then also the
278 // directory containing the dll which should be either `lib` or `bin`.
279 if let Some(path
) = dll
.parent().and_then(|p
| p
.parent()) {
280 // The original `path` pointed at the `rustc_driver` crate's dll.
281 // Now that dll should only be in one of two locations. The first is
282 // in the compiler's libdir, for example `$sysroot/lib/*.dll`. The
283 // other is the target's libdir, for example
284 // `$sysroot/lib/rustlib/$target/lib/*.dll`.
286 // We don't know which, so let's assume that if our `path` above
287 // ends in `$target` we *could* be in the target libdir, and always
288 // assume that we may be in the main libdir.
289 sysroot_candidates
.push(path
.to_owned());
291 if path
.ends_with(target
) {
292 sysroot_candidates
.extend(
293 path
.parent() // chop off `$target`
294 .and_then(|p
| p
.parent()) // chop off `rustlib`
295 .and_then(|p
| p
.parent()) // chop off `lib`
296 .map(|s
| s
.to_owned()),
302 return sysroot_candidates
;
305 fn current_dll_path() -> Option
<PathBuf
> {
306 use std
::ffi
::{CStr, OsStr}
;
307 use std
::os
::unix
::prelude
::*;
310 let addr
= current_dll_path
as usize as *mut _
;
311 let mut info
= mem
::zeroed();
312 if libc
::dladdr(addr
, &mut info
) == 0 {
313 info
!("dladdr failed");
316 if info
.dli_fname
.is_null() {
317 info
!("dladdr returned null pointer");
320 let bytes
= CStr
::from_ptr(info
.dli_fname
).to_bytes();
321 let os
= OsStr
::from_bytes(bytes
);
322 Some(PathBuf
::from(os
))
327 fn current_dll_path() -> Option
<PathBuf
> {
328 use std
::ffi
::OsString
;
330 use std
::os
::windows
::prelude
::*;
333 use winapi
::um
::libloaderapi
::{
334 GetModuleFileNameW
, GetModuleHandleExW
, GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
,
338 let mut module
= ptr
::null_mut();
339 let r
= GetModuleHandleExW(
340 GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
,
341 current_dll_path
as usize as *mut _
,
345 info
!("GetModuleHandleExW failed: {}", io
::Error
::last_os_error());
348 let mut space
= Vec
::with_capacity(1024);
349 let r
= GetModuleFileNameW(module
, space
.as_mut_ptr(), space
.capacity() as u32);
351 info
!("GetModuleFileNameW failed: {}", io
::Error
::last_os_error());
355 if r
>= space
.capacity() {
356 info
!("our buffer was too small? {}", io
::Error
::last_os_error());
360 let os
= OsString
::from_wide(&space
);
361 Some(PathBuf
::from(os
))
366 fn get_codegen_sysroot(maybe_sysroot
: &Option
<PathBuf
>, backend_name
: &str) -> MakeBackendFn
{
367 // For now we only allow this function to be called once as it'll dlopen a
368 // few things, which seems to work best if we only do that once. In
369 // general this assertion never trips due to the once guard in `get_codegen_backend`,
370 // but there's a few manual calls to this function in this file we protect
372 static LOADED
: AtomicBool
= AtomicBool
::new(false);
374 !LOADED
.fetch_or(true, Ordering
::SeqCst
),
375 "cannot load the default codegen backend twice"
378 let target
= session
::config
::host_triple();
379 let sysroot_candidates
= sysroot_candidates();
381 let sysroot
= maybe_sysroot
383 .chain(sysroot_candidates
.iter())
385 filesearch
::make_target_lib_path(sysroot
, target
).with_file_name("codegen-backends")
388 info
!("codegen backend candidate: {}", f
.display());
391 let sysroot
= sysroot
.unwrap_or_else(|| {
392 let candidates
= sysroot_candidates
394 .map(|p
| p
.display().to_string())
398 "failed to find a `codegen-backends` folder \
399 in the sysroot candidates:\n* {}",
402 early_error(ErrorOutputType
::default(), &err
);
404 info
!("probing {} for a codegen backend", sysroot
.display());
406 let d
= sysroot
.read_dir().unwrap_or_else(|e
| {
408 "failed to load default codegen backend, couldn't \
413 early_error(ErrorOutputType
::default(), &err
);
416 let mut file
: Option
<PathBuf
> = None
;
418 let expected_names
= &[
419 format
!("rustc_codegen_{}-{}", backend_name
, release_str().expect("CFG_RELEASE")),
420 format
!("rustc_codegen_{}", backend_name
),
422 for entry
in d
.filter_map(|e
| e
.ok()) {
423 let path
= entry
.path();
424 let Some(filename
) = path
.file_name().and_then(|s
| s
.to_str()) else { continue }
;
425 if !(filename
.starts_with(DLL_PREFIX
) && filename
.ends_with(DLL_SUFFIX
)) {
428 let name
= &filename
[DLL_PREFIX
.len()..filename
.len() - DLL_SUFFIX
.len()];
429 if !expected_names
.iter().any(|expected
| expected
== name
) {
432 if let Some(ref prev
) = file
{
434 "duplicate codegen backends found\n\
441 early_error(ErrorOutputType
::default(), &err
);
443 file
= Some(path
.clone());
447 Some(ref s
) => load_backend_from_dylib(s
),
449 let err
= format
!("unsupported builtin codegen backend `{}`", backend_name
);
450 early_error(ErrorOutputType
::default(), &err
);
455 pub(crate) fn check_attr_crate_type(
457 attrs
: &[ast
::Attribute
],
458 lint_buffer
: &mut LintBuffer
,
460 // Unconditionally collect crate types from attributes to make them used
461 for a
in attrs
.iter() {
462 if a
.has_name(sym
::crate_type
) {
463 if let Some(n
) = a
.value_str() {
464 if categorize_crate_type(n
).is_some() {
468 if let ast
::MetaItemKind
::NameValue(spanned
) = a
.meta_kind().unwrap() {
469 let span
= spanned
.span
;
470 let lev_candidate
= find_best_match_for_name(
471 &CRATE_TYPES
.iter().map(|(k
, _
)| *k
).collect
::<Vec
<_
>>(),
475 if let Some(candidate
) = lev_candidate
{
476 lint_buffer
.buffer_lint_with_diagnostic(
477 lint
::builtin
::UNKNOWN_CRATE_TYPES
,
480 "invalid `crate_type` value",
481 BuiltinLintDiagnostics
::UnknownCrateTypes(
483 "did you mean".to_string(),
484 format
!("\"{}\"", candidate
),
488 lint_buffer
.buffer_lint(
489 lint
::builtin
::UNKNOWN_CRATE_TYPES
,
492 "invalid `crate_type` value",
497 // This is here mainly to check for using a macro, such as
498 // #![crate_type = foo!()]. That is not supported since the
499 // crate type needs to be known very early in compilation long
500 // before expansion. Otherwise, validation would normally be
501 // caught in AstValidator (via `check_builtin_attribute`), but
502 // by the time that runs the macro is expanded, and it doesn't
504 validate_attr
::emit_fatal_malformed_builtin_attribute(
514 const CRATE_TYPES
: &[(Symbol
, CrateType
)] = &[
515 (sym
::rlib
, CrateType
::Rlib
),
516 (sym
::dylib
, CrateType
::Dylib
),
517 (sym
::cdylib
, CrateType
::Cdylib
),
518 (sym
::lib
, config
::default_lib_output()),
519 (sym
::staticlib
, CrateType
::Staticlib
),
520 (sym
::proc_dash_macro
, CrateType
::ProcMacro
),
521 (sym
::bin
, CrateType
::Executable
),
524 fn categorize_crate_type(s
: Symbol
) -> Option
<CrateType
> {
525 Some(CRATE_TYPES
.iter().find(|(key
, _
)| *key
== s
)?
.1)
528 pub fn collect_crate_types(session
: &Session
, attrs
: &[ast
::Attribute
]) -> Vec
<CrateType
> {
529 // Unconditionally collect crate types from attributes to make them used
530 let attr_types
: Vec
<CrateType
> = attrs
533 if a
.has_name(sym
::crate_type
) {
534 match a
.value_str() {
535 Some(s
) => categorize_crate_type(s
),
544 // If we're generating a test executable, then ignore all other output
545 // styles at all other locations
546 if session
.opts
.test
{
547 return vec
![CrateType
::Executable
];
550 // Only check command line flags if present. If no types are specified by
551 // command line, then reuse the empty `base` Vec to hold the types that
552 // will be found in crate attributes.
553 let mut base
= session
.opts
.crate_types
.clone();
555 base
.extend(attr_types
);
557 base
.push(output
::default_output_for_target(session
));
564 base
.retain(|crate_type
| {
565 let res
= !output
::invalid_output_for_target(session
, *crate_type
);
568 session
.warn(&format
!(
569 "dropping unsupported crate type `{}` for target `{}`",
570 *crate_type
, session
.opts
.target_triple
580 pub fn build_output_filenames(
582 odir
: &Option
<PathBuf
>,
583 ofile
: &Option
<PathBuf
>,
584 temps_dir
: &Option
<PathBuf
>,
585 attrs
: &[ast
::Attribute
],
587 ) -> OutputFilenames
{
590 // "-" as input file will cause the parser to read from stdin so we
591 // have to make up a name
592 // We want to toss everything after the final '.'
593 let dirpath
= (*odir
).as_ref().cloned().unwrap_or_default();
595 // If a crate name is present, we use it as the link name
600 .or_else(|| rustc_attr
::find_crate_name(sess
, attrs
).map(|n
| n
.to_string()))
601 .unwrap_or_else(|| input
.filestem().to_owned());
603 OutputFilenames
::new(
608 sess
.opts
.cg
.extra_filename
.clone(),
609 sess
.opts
.output_types
.clone(),
613 Some(ref out_file
) => {
614 let unnamed_output_types
=
615 sess
.opts
.output_types
.values().filter(|a
| a
.is_none()).count();
616 let ofile
= if unnamed_output_types
> 1 {
618 "due to multiple output types requested, the explicitly specified \
619 output file name will be adapted for each output type",
623 if !sess
.opts
.cg
.extra_filename
.is_empty() {
624 sess
.warn("ignoring -C extra-filename flag due to -o flag");
626 Some(out_file
.clone())
629 sess
.warn("ignoring --out-dir flag due to -o flag");
632 OutputFilenames
::new(
633 out_file
.parent().unwrap_or_else(|| Path
::new("")).to_path_buf(),
634 out_file
.file_stem().unwrap_or_default().to_str().unwrap().to_string(),
637 sess
.opts
.cg
.extra_filename
.clone(),
638 sess
.opts
.output_types
.clone(),
644 #[cfg(not(target_os = "linux"))]
645 pub fn non_durable_rename(src
: &Path
, dst
: &Path
) -> std
::io
::Result
<()> {
646 std
::fs
::rename(src
, dst
)
649 /// This function attempts to bypass the auto_da_alloc heuristic implemented by some filesystems
650 /// such as btrfs and ext4. When renaming over a file that already exists then they will "helpfully"
651 /// write back the source file before committing the rename in case a developer forgot some of
652 /// the fsyncs in the open/write/fsync(file)/rename/fsync(dir) dance for atomic file updates.
654 /// To avoid triggering this heuristic we delete the destination first, if it exists.
655 /// The cost of an extra syscall is much lower than getting descheduled for the sync IO.
656 #[cfg(target_os = "linux")]
657 pub fn non_durable_rename(src
: &Path
, dst
: &Path
) -> std
::io
::Result
<()> {
658 let _
= std
::fs
::remove_file(dst
);
659 std
::fs
::rename(src
, dst
)
662 /// Returns a version string such as "1.46.0 (04488afe3 2020-08-24)"
663 pub fn version_str() -> Option
<&'
static str> {
664 option_env
!("CFG_VERSION")
667 /// Returns a version string such as "0.12.0-dev".
668 pub fn release_str() -> Option
<&'
static str> {
669 option_env
!("CFG_RELEASE")
672 /// Returns the full SHA1 hash of HEAD of the Git repo from which rustc was built.
673 pub fn commit_hash_str() -> Option
<&'
static str> {
674 option_env
!("CFG_VER_HASH")
677 /// Returns the "commit date" of HEAD of the Git repo from which rustc was built as a static string.
678 pub fn commit_date_str() -> Option
<&'
static str> {
679 option_env
!("CFG_VER_DATE")