2 use libloading
::Library
;
4 use rustc_codegen_ssa
::traits
::CodegenBackend
;
5 use rustc_data_structures
::fx
::{FxHashMap, FxHashSet}
;
6 use rustc_errors
::registry
::Registry
;
7 use rustc_parse
::validate_attr
;
8 use rustc_session
as session
;
9 use rustc_session
::config
::CheckCfg
;
10 use rustc_session
::config
::{self, CrateType}
;
11 use rustc_session
::config
::{ErrorOutputType, Input, OutputFilenames}
;
12 use rustc_session
::lint
::{self, BuiltinLintDiagnostics, LintBuffer}
;
13 use rustc_session
::parse
::CrateConfig
;
14 use rustc_session
::{early_error, filesearch, output, Session}
;
15 use rustc_span
::edition
::Edition
;
16 use rustc_span
::lev_distance
::find_best_match_for_name
;
17 use rustc_span
::source_map
::FileLoader
;
18 use rustc_span
::symbol
::{sym, Symbol}
;
20 use std
::env
::consts
::{DLL_PREFIX, DLL_SUFFIX}
;
22 use std
::path
::{Path, PathBuf}
;
23 use std
::sync
::atomic
::{AtomicBool, Ordering}
;
24 use std
::sync
::OnceLock
;
27 /// Function pointer type that constructs a new CodegenBackend.
28 pub type MakeBackendFn
= fn() -> Box
<dyn CodegenBackend
>;
30 /// Adds `target_feature = "..."` cfgs for a variety of platform
31 /// specific features (SSE, NEON etc.).
33 /// This is performed by checking whether a set of permitted features
34 /// is available on the target machine, by querying LLVM.
35 pub fn add_configuration(
36 cfg
: &mut CrateConfig
,
38 codegen_backend
: &dyn CodegenBackend
,
40 let tf
= sym
::target_feature
;
42 let unstable_target_features
= codegen_backend
.target_features(sess
, true);
43 sess
.unstable_target_features
.extend(unstable_target_features
.iter().cloned());
45 let target_features
= codegen_backend
.target_features(sess
, false);
46 sess
.target_features
.extend(target_features
.iter().cloned());
48 cfg
.extend(target_features
.into_iter().map(|feat
| (tf
, Some(feat
))));
50 if sess
.crt_static(None
) {
51 cfg
.insert((tf
, Some(sym
::crt_dash_static
)));
55 pub fn create_session(
56 sopts
: config
::Options
,
57 cfg
: FxHashSet
<(String
, Option
<String
>)>,
59 file_loader
: Option
<Box
<dyn FileLoader
+ Send
+ Sync
+ '
static>>,
60 input_path
: Option
<PathBuf
>,
61 lint_caps
: FxHashMap
<lint
::LintId
, lint
::Level
>,
62 make_codegen_backend
: Option
<
63 Box
<dyn FnOnce(&config
::Options
) -> Box
<dyn CodegenBackend
> + Send
>,
65 descriptions
: Registry
,
66 ) -> (Session
, Box
<dyn CodegenBackend
>) {
67 let codegen_backend
= if let Some(make_codegen_backend
) = make_codegen_backend
{
68 make_codegen_backend(&sopts
)
72 sopts
.unstable_opts
.codegen_backend
.as_ref().map(|name
| &name
[..]),
76 // target_override is documented to be called before init(), so this is okay
77 let target_override
= codegen_backend
.target_override(&sopts
);
79 let bundle
= match rustc_errors
::fluent_bundle(
80 sopts
.maybe_sysroot
.clone(),
82 sopts
.unstable_opts
.translate_lang
.clone(),
83 sopts
.unstable_opts
.translate_additional_ftl
.as_deref(),
84 sopts
.unstable_opts
.translate_directionality_markers
,
88 early_error(sopts
.error_format
, &format
!("failed to load fluent bundle: {e}"));
92 let mut sess
= session
::build_session(
102 codegen_backend
.init(&sess
);
104 let mut cfg
= config
::build_configuration(&sess
, config
::to_crate_config(cfg
));
105 add_configuration(&mut cfg
, &mut sess
, &*codegen_backend
);
107 let mut check_cfg
= config
::to_crate_check_config(check_cfg
);
108 check_cfg
.fill_well_known();
110 sess
.parse_sess
.config
= cfg
;
111 sess
.parse_sess
.check_config
= check_cfg
;
113 (sess
, codegen_backend
)
116 const STACK_SIZE
: usize = 8 * 1024 * 1024;
118 fn get_stack_size() -> Option
<usize> {
119 // FIXME: Hacks on hacks. If the env is trying to override the stack size
120 // then *don't* set it explicitly.
121 env
::var_os("RUST_MIN_STACK").is_none().then_some(STACK_SIZE
)
124 #[cfg(not(parallel_compiler))]
125 pub(crate) fn run_in_thread_pool_with_globals
<F
: FnOnce() -> R
+ Send
, R
: Send
>(
130 // The "thread pool" is a single spawned thread in the non-parallel
131 // compiler. We run on a spawned thread instead of the main thread (a) to
132 // provide control over the stack size, and (b) to increase similarity with
133 // the parallel compiler, in particular to ensure there is no accidental
134 // sharing of data between the main thread and the compilation thread
135 // (which might cause problems for the parallel compiler).
136 let mut builder
= thread
::Builder
::new().name("rustc".to_string());
137 if let Some(size
) = get_stack_size() {
138 builder
= builder
.stack_size(size
);
141 // We build the session globals and run `f` on the spawned thread, because
142 // `SessionGlobals` does not impl `Send` in the non-parallel compiler.
144 // `unwrap` is ok here because `spawn_scoped` only panics if the thread
145 // name contains null bytes.
147 .spawn_scoped(s
, move || rustc_span
::create_session_globals_then(edition
, f
))
153 Err(e
) => std
::panic
::resume_unwind(e
),
158 #[cfg(parallel_compiler)]
159 pub(crate) fn run_in_thread_pool_with_globals
<F
: FnOnce() -> R
+ Send
, R
: Send
>(
164 use rustc_data_structures
::jobserver
;
165 use rustc_middle
::ty
::tls
;
166 use rustc_query_impl
::{deadlock, QueryContext, QueryCtxt}
;
168 let mut builder
= rayon
::ThreadPoolBuilder
::new()
169 .thread_name(|_
| "rustc".to_string())
170 .acquire_thread_handler(jobserver
::acquire_thread
)
171 .release_thread_handler(jobserver
::release_thread
)
172 .num_threads(threads
)
173 .deadlock_handler(|| {
174 // On deadlock, creates a new thread and forwards information in thread
175 // locals to it. The new thread runs the deadlock handler.
176 let query_map
= tls
::with(|tcx
| {
177 QueryCtxt
::from_tcx(tcx
)
178 .try_collect_active_jobs()
179 .expect("active jobs shouldn't be locked in deadlock handler")
181 let registry
= rustc_rayon_core
::Registry
::current();
182 thread
::spawn(move || deadlock(query_map
, ®istry
));
184 if let Some(size
) = get_stack_size() {
185 builder
= builder
.stack_size(size
);
188 // We create the session globals on the main thread, then create the thread
189 // pool. Upon creation, each worker thread created gets a copy of the
190 // session globals in TLS. This is possible because `SessionGlobals` impls
191 // `Send` in the parallel compiler.
192 rustc_span
::create_session_globals_then(edition
, || {
193 rustc_span
::with_session_globals(|session_globals
| {
196 // Initialize each new worker thread when created.
197 move |thread
: rayon
::ThreadBuilder
| {
198 rustc_span
::set_session_globals_then(session_globals
, || thread
.run())
200 // Run `f` on the first thread in the thread pool.
201 move |pool
: &rayon
::ThreadPool
| pool
.install(f
),
208 fn load_backend_from_dylib(path
: &Path
) -> MakeBackendFn
{
209 let lib
= unsafe { Library::new(path) }
.unwrap_or_else(|err
| {
210 let err
= format
!("couldn't load codegen backend {:?}: {}", path
, err
);
211 early_error(ErrorOutputType
::default(), &err
);
214 let backend_sym
= unsafe { lib.get::<MakeBackendFn>(b"__rustc_codegen_backend") }
215 .unwrap_or_else(|e
| {
216 let err
= format
!("couldn't load codegen backend: {}", e
);
217 early_error(ErrorOutputType
::default(), &err
);
220 // Intentionally leak the dynamic library. We can't ever unload it
221 // since the library can make things that will live arbitrarily long.
222 let backend_sym
= unsafe { backend_sym.into_raw() }
;
228 /// Get the codegen backend based on the name and specified sysroot.
230 /// A name of `None` indicates that the default backend should be used.
231 pub fn get_codegen_backend(
232 maybe_sysroot
: &Option
<PathBuf
>,
233 backend_name
: Option
<&str>,
234 ) -> Box
<dyn CodegenBackend
> {
235 static LOAD
: OnceLock
<unsafe fn() -> Box
<dyn CodegenBackend
>> = OnceLock
::new();
237 let load
= LOAD
.get_or_init(|| {
238 let default_codegen_backend
= option_env
!("CFG_DEFAULT_CODEGEN_BACKEND").unwrap_or("llvm");
240 match backend_name
.unwrap_or(default_codegen_backend
) {
241 filename
if filename
.contains('
.'
) => load_backend_from_dylib(filename
.as_ref()),
242 #[cfg(feature = "llvm")]
243 "llvm" => rustc_codegen_llvm
::LlvmCodegenBackend
::new
,
244 backend_name
=> get_codegen_sysroot(maybe_sysroot
, backend_name
),
248 // SAFETY: In case of a builtin codegen backend this is safe. In case of an external codegen
249 // backend we hope that the backend links against the same rustc_driver version. If this is not
250 // the case, we get UB.
254 // This is used for rustdoc, but it uses similar machinery to codegen backend
255 // loading, so we leave the code here. It is potentially useful for other tools
256 // that want to invoke the rustc binary while linking to rustc as well.
257 pub fn rustc_path
<'a
>() -> Option
<&'a Path
> {
258 static RUSTC_PATH
: OnceLock
<Option
<PathBuf
>> = OnceLock
::new();
260 const BIN_PATH
: &str = env
!("RUSTC_INSTALL_BINDIR");
262 RUSTC_PATH
.get_or_init(|| get_rustc_path_inner(BIN_PATH
)).as_ref().map(|v
| &**v
)
265 fn get_rustc_path_inner(bin_path
: &str) -> Option
<PathBuf
> {
266 sysroot_candidates().iter().find_map(|sysroot
| {
267 let candidate
= sysroot
.join(bin_path
).join(if cfg
!(target_os
= "windows") {
272 candidate
.exists().then_some(candidate
)
276 fn sysroot_candidates() -> Vec
<PathBuf
> {
277 let target
= session
::config
::host_triple();
278 let mut sysroot_candidates
= vec
![filesearch
::get_or_default_sysroot()];
279 let path
= current_dll_path().and_then(|s
| s
.canonicalize().ok());
280 if let Some(dll
) = path
{
281 // use `parent` twice to chop off the file name and then also the
282 // directory containing the dll which should be either `lib` or `bin`.
283 if let Some(path
) = dll
.parent().and_then(|p
| p
.parent()) {
284 // The original `path` pointed at the `rustc_driver` crate's dll.
285 // Now that dll should only be in one of two locations. The first is
286 // in the compiler's libdir, for example `$sysroot/lib/*.dll`. The
287 // other is the target's libdir, for example
288 // `$sysroot/lib/rustlib/$target/lib/*.dll`.
290 // We don't know which, so let's assume that if our `path` above
291 // ends in `$target` we *could* be in the target libdir, and always
292 // assume that we may be in the main libdir.
293 sysroot_candidates
.push(path
.to_owned());
295 if path
.ends_with(target
) {
296 sysroot_candidates
.extend(
297 path
.parent() // chop off `$target`
298 .and_then(|p
| p
.parent()) // chop off `rustlib`
299 .and_then(|p
| p
.parent()) // chop off `lib`
300 .map(|s
| s
.to_owned()),
306 return sysroot_candidates
;
309 fn current_dll_path() -> Option
<PathBuf
> {
310 use std
::ffi
::{CStr, OsStr}
;
311 use std
::os
::unix
::prelude
::*;
314 let addr
= current_dll_path
as usize as *mut _
;
315 let mut info
= mem
::zeroed();
316 if libc
::dladdr(addr
, &mut info
) == 0 {
317 info
!("dladdr failed");
320 if info
.dli_fname
.is_null() {
321 info
!("dladdr returned null pointer");
324 let bytes
= CStr
::from_ptr(info
.dli_fname
).to_bytes();
325 let os
= OsStr
::from_bytes(bytes
);
326 Some(PathBuf
::from(os
))
331 fn current_dll_path() -> Option
<PathBuf
> {
332 use std
::ffi
::OsString
;
334 use std
::os
::windows
::prelude
::*;
337 use winapi
::um
::libloaderapi
::{
338 GetModuleFileNameW
, GetModuleHandleExW
, GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
,
342 let mut module
= ptr
::null_mut();
343 let r
= GetModuleHandleExW(
344 GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
,
345 current_dll_path
as usize as *mut _
,
349 info
!("GetModuleHandleExW failed: {}", io
::Error
::last_os_error());
352 let mut space
= Vec
::with_capacity(1024);
353 let r
= GetModuleFileNameW(module
, space
.as_mut_ptr(), space
.capacity() as u32);
355 info
!("GetModuleFileNameW failed: {}", io
::Error
::last_os_error());
359 if r
>= space
.capacity() {
360 info
!("our buffer was too small? {}", io
::Error
::last_os_error());
364 let os
= OsString
::from_wide(&space
);
365 Some(PathBuf
::from(os
))
370 fn get_codegen_sysroot(maybe_sysroot
: &Option
<PathBuf
>, backend_name
: &str) -> MakeBackendFn
{
371 // For now we only allow this function to be called once as it'll dlopen a
372 // few things, which seems to work best if we only do that once. In
373 // general this assertion never trips due to the once guard in `get_codegen_backend`,
374 // but there's a few manual calls to this function in this file we protect
376 static LOADED
: AtomicBool
= AtomicBool
::new(false);
378 !LOADED
.fetch_or(true, Ordering
::SeqCst
),
379 "cannot load the default codegen backend twice"
382 let target
= session
::config
::host_triple();
383 let sysroot_candidates
= sysroot_candidates();
385 let sysroot
= maybe_sysroot
387 .chain(sysroot_candidates
.iter())
389 filesearch
::make_target_lib_path(sysroot
, target
).with_file_name("codegen-backends")
392 info
!("codegen backend candidate: {}", f
.display());
395 let sysroot
= sysroot
.unwrap_or_else(|| {
396 let candidates
= sysroot_candidates
398 .map(|p
| p
.display().to_string())
402 "failed to find a `codegen-backends` folder \
403 in the sysroot candidates:\n* {}",
406 early_error(ErrorOutputType
::default(), &err
);
408 info
!("probing {} for a codegen backend", sysroot
.display());
410 let d
= sysroot
.read_dir().unwrap_or_else(|e
| {
412 "failed to load default codegen backend, couldn't \
417 early_error(ErrorOutputType
::default(), &err
);
420 let mut file
: Option
<PathBuf
> = None
;
422 let expected_names
= &[
423 format
!("rustc_codegen_{}-{}", backend_name
, release_str().expect("CFG_RELEASE")),
424 format
!("rustc_codegen_{}", backend_name
),
426 for entry
in d
.filter_map(|e
| e
.ok()) {
427 let path
= entry
.path();
428 let Some(filename
) = path
.file_name().and_then(|s
| s
.to_str()) else { continue }
;
429 if !(filename
.starts_with(DLL_PREFIX
) && filename
.ends_with(DLL_SUFFIX
)) {
432 let name
= &filename
[DLL_PREFIX
.len()..filename
.len() - DLL_SUFFIX
.len()];
433 if !expected_names
.iter().any(|expected
| expected
== name
) {
436 if let Some(ref prev
) = file
{
438 "duplicate codegen backends found\n\
445 early_error(ErrorOutputType
::default(), &err
);
447 file
= Some(path
.clone());
451 Some(ref s
) => load_backend_from_dylib(s
),
453 let err
= format
!("unsupported builtin codegen backend `{}`", backend_name
);
454 early_error(ErrorOutputType
::default(), &err
);
459 pub(crate) fn check_attr_crate_type(
461 attrs
: &[ast
::Attribute
],
462 lint_buffer
: &mut LintBuffer
,
464 // Unconditionally collect crate types from attributes to make them used
465 for a
in attrs
.iter() {
466 if a
.has_name(sym
::crate_type
) {
467 if let Some(n
) = a
.value_str() {
468 if categorize_crate_type(n
).is_some() {
472 if let ast
::MetaItemKind
::NameValue(spanned
) = a
.meta_kind().unwrap() {
473 let span
= spanned
.span
;
474 let lev_candidate
= find_best_match_for_name(
475 &CRATE_TYPES
.iter().map(|(k
, _
)| *k
).collect
::<Vec
<_
>>(),
479 if let Some(candidate
) = lev_candidate
{
480 lint_buffer
.buffer_lint_with_diagnostic(
481 lint
::builtin
::UNKNOWN_CRATE_TYPES
,
484 "invalid `crate_type` value",
485 BuiltinLintDiagnostics
::UnknownCrateTypes(
487 "did you mean".to_string(),
488 format
!("\"{}\"", candidate
),
492 lint_buffer
.buffer_lint(
493 lint
::builtin
::UNKNOWN_CRATE_TYPES
,
496 "invalid `crate_type` value",
501 // This is here mainly to check for using a macro, such as
502 // #![crate_type = foo!()]. That is not supported since the
503 // crate type needs to be known very early in compilation long
504 // before expansion. Otherwise, validation would normally be
505 // caught in AstValidator (via `check_builtin_attribute`), but
506 // by the time that runs the macro is expanded, and it doesn't
508 validate_attr
::emit_fatal_malformed_builtin_attribute(
518 const CRATE_TYPES
: &[(Symbol
, CrateType
)] = &[
519 (sym
::rlib
, CrateType
::Rlib
),
520 (sym
::dylib
, CrateType
::Dylib
),
521 (sym
::cdylib
, CrateType
::Cdylib
),
522 (sym
::lib
, config
::default_lib_output()),
523 (sym
::staticlib
, CrateType
::Staticlib
),
524 (sym
::proc_dash_macro
, CrateType
::ProcMacro
),
525 (sym
::bin
, CrateType
::Executable
),
528 fn categorize_crate_type(s
: Symbol
) -> Option
<CrateType
> {
529 Some(CRATE_TYPES
.iter().find(|(key
, _
)| *key
== s
)?
.1)
532 pub fn collect_crate_types(session
: &Session
, attrs
: &[ast
::Attribute
]) -> Vec
<CrateType
> {
533 // Unconditionally collect crate types from attributes to make them used
534 let attr_types
: Vec
<CrateType
> = attrs
537 if a
.has_name(sym
::crate_type
) {
538 match a
.value_str() {
539 Some(s
) => categorize_crate_type(s
),
548 // If we're generating a test executable, then ignore all other output
549 // styles at all other locations
550 if session
.opts
.test
{
551 return vec
![CrateType
::Executable
];
554 // Only check command line flags if present. If no types are specified by
555 // command line, then reuse the empty `base` Vec to hold the types that
556 // will be found in crate attributes.
557 // JUSTIFICATION: before wrapper fn is available
558 #[allow(rustc::bad_opt_access)]
559 let mut base
= session
.opts
.crate_types
.clone();
561 base
.extend(attr_types
);
563 base
.push(output
::default_output_for_target(session
));
570 base
.retain(|crate_type
| {
571 let res
= !output
::invalid_output_for_target(session
, *crate_type
);
574 session
.warn(&format
!(
575 "dropping unsupported crate type `{}` for target `{}`",
576 *crate_type
, session
.opts
.target_triple
586 pub fn build_output_filenames(
588 odir
: &Option
<PathBuf
>,
589 ofile
: &Option
<PathBuf
>,
590 temps_dir
: &Option
<PathBuf
>,
591 attrs
: &[ast
::Attribute
],
593 ) -> OutputFilenames
{
596 // "-" as input file will cause the parser to read from stdin so we
597 // have to make up a name
598 // We want to toss everything after the final '.'
599 let dirpath
= (*odir
).as_ref().cloned().unwrap_or_default();
601 // If a crate name is present, we use it as the link name
606 .or_else(|| rustc_attr
::find_crate_name(sess
, attrs
).map(|n
| n
.to_string()))
607 .unwrap_or_else(|| input
.filestem().to_owned());
609 OutputFilenames
::new(
614 sess
.opts
.cg
.extra_filename
.clone(),
615 sess
.opts
.output_types
.clone(),
619 Some(ref out_file
) => {
620 let unnamed_output_types
=
621 sess
.opts
.output_types
.values().filter(|a
| a
.is_none()).count();
622 let ofile
= if unnamed_output_types
> 1 {
624 "due to multiple output types requested, the explicitly specified \
625 output file name will be adapted for each output type",
629 if !sess
.opts
.cg
.extra_filename
.is_empty() {
630 sess
.warn("ignoring -C extra-filename flag due to -o flag");
632 Some(out_file
.clone())
635 sess
.warn("ignoring --out-dir flag due to -o flag");
638 OutputFilenames
::new(
639 out_file
.parent().unwrap_or_else(|| Path
::new("")).to_path_buf(),
640 out_file
.file_stem().unwrap_or_default().to_str().unwrap().to_string(),
643 sess
.opts
.cg
.extra_filename
.clone(),
644 sess
.opts
.output_types
.clone(),
650 /// Returns a version string such as "1.46.0 (04488afe3 2020-08-24)"
651 pub fn version_str() -> Option
<&'
static str> {
652 option_env
!("CFG_VERSION")
655 /// Returns a version string such as "0.12.0-dev".
656 pub fn release_str() -> Option
<&'
static str> {
657 option_env
!("CFG_RELEASE")
660 /// Returns the full SHA1 hash of HEAD of the Git repo from which rustc was built.
661 pub fn commit_hash_str() -> Option
<&'
static str> {
662 option_env
!("CFG_VER_HASH")
665 /// Returns the "commit date" of HEAD of the Git repo from which rustc was built as a static string.
666 pub fn commit_date_str() -> Option
<&'
static str> {
667 option_env
!("CFG_VER_DATE")