]> git.lizzy.rs Git - rust.git/blob - src/bootstrap/bin/main.rs
Rollup merge of #106856 - vadorovsky:fix-atomic-annotations, r=joshtriplett
[rust.git] / src / bootstrap / bin / main.rs
1 //! rustbuild, the Rust build system
2 //!
3 //! This is the entry point for the build system used to compile the `rustc`
4 //! compiler. Lots of documentation can be found in the `README.md` file in the
5 //! parent directory, and otherwise documentation can be found throughout the `build`
6 //! directory in each respective module.
7
8 use std::env;
9
10 use bootstrap::{t, Build, Config, Subcommand, VERSION};
11
12 fn main() {
13     let args = env::args().skip(1).collect::<Vec<_>>();
14     let config = Config::parse(&args);
15
16     let mut build_lock;
17     let _build_lock_guard;
18     if cfg!(any(unix, windows)) {
19         let path = config.out.join("lock");
20         build_lock = fd_lock::RwLock::new(t!(std::fs::File::create(&path)));
21         _build_lock_guard = match build_lock.try_write() {
22             Ok(lock) => lock,
23             err => {
24                 drop(err);
25                 if let Some(pid) = get_lock_owner(&path) {
26                     println!("warning: build directory locked by process {pid}, waiting for lock");
27                 } else {
28                     println!("warning: build directory locked, waiting for lock");
29                 }
30                 t!(build_lock.write())
31             }
32         };
33     } else {
34         println!("warning: file locking not supported for target, not locking build directory");
35     }
36
37     // check_version warnings are not printed during setup
38     let changelog_suggestion =
39         if matches!(config.cmd, Subcommand::Setup { .. }) { None } else { check_version(&config) };
40
41     // NOTE: Since `./configure` generates a `config.toml`, distro maintainers will see the
42     // changelog warning, not the `x.py setup` message.
43     let suggest_setup = config.config.is_none() && !matches!(config.cmd, Subcommand::Setup { .. });
44     if suggest_setup {
45         println!("warning: you have not made a `config.toml`");
46         println!(
47             "help: consider running `./x.py setup` or copying `config.toml.example` by running \
48             `cp config.toml.example config.toml`"
49         );
50     } else if let Some(suggestion) = &changelog_suggestion {
51         println!("{}", suggestion);
52     }
53
54     let pre_commit = config.src.join(".git").join("hooks").join("pre-commit");
55     Build::new(config).build();
56
57     if suggest_setup {
58         println!("warning: you have not made a `config.toml`");
59         println!(
60             "help: consider running `./x.py setup` or copying `config.toml.example` by running \
61             `cp config.toml.example config.toml`"
62         );
63     } else if let Some(suggestion) = &changelog_suggestion {
64         println!("{}", suggestion);
65     }
66
67     // Give a warning if the pre-commit script is in pre-commit and not pre-push.
68     // HACK: Since the commit script uses hard links, we can't actually tell if it was installed by x.py setup or not.
69     // We could see if it's identical to src/etc/pre-push.sh, but pre-push may have been modified in the meantime.
70     // Instead, look for this comment, which is almost certainly not in any custom hook.
71     if std::fs::read_to_string(pre_commit).map_or(false, |contents| {
72         contents.contains("https://github.com/rust-lang/rust/issues/77620#issuecomment-705144570")
73     }) {
74         println!(
75             "warning: You have the pre-push script installed to .git/hooks/pre-commit. \
76                   Consider moving it to .git/hooks/pre-push instead, which runs less often."
77         );
78     }
79
80     if suggest_setup || changelog_suggestion.is_some() {
81         println!("note: this message was printed twice to make it more likely to be seen");
82     }
83 }
84
85 fn check_version(config: &Config) -> Option<String> {
86     let mut msg = String::new();
87
88     let suggestion = if let Some(seen) = config.changelog_seen {
89         if seen != VERSION {
90             msg.push_str("warning: there have been changes to x.py since you last updated.\n");
91             format!("update `config.toml` to use `changelog-seen = {}` instead", VERSION)
92         } else {
93             return None;
94         }
95     } else {
96         msg.push_str("warning: x.py has made several changes recently you may want to look at\n");
97         format!("add `changelog-seen = {}` at the top of `config.toml`", VERSION)
98     };
99
100     msg.push_str("help: consider looking at the changes in `src/bootstrap/CHANGELOG.md`\n");
101     msg.push_str("note: to silence this warning, ");
102     msg.push_str(&suggestion);
103
104     Some(msg)
105 }
106
107 /// Get the PID of the process which took the write lock by
108 /// parsing `/proc/locks`.
109 #[cfg(target_os = "linux")]
110 fn get_lock_owner(f: &std::path::Path) -> Option<u64> {
111     use std::fs::File;
112     use std::io::{BufRead, BufReader};
113     use std::os::unix::fs::MetadataExt;
114
115     let lock_inode = std::fs::metadata(f).ok()?.ino();
116     let lockfile = File::open("/proc/locks").ok()?;
117     BufReader::new(lockfile).lines().find_map(|line| {
118         //                       pid--vvvvvv       vvvvvvv--- inode
119         // 21: FLOCK  ADVISORY  WRITE 359238 08:02:3719774 0 EOF
120         let line = line.ok()?;
121         let parts = line.split_whitespace().collect::<Vec<_>>();
122         let (pid, inode) = (parts[4].parse::<u64>().ok()?, &parts[5]);
123         let inode = inode.rsplit_once(':')?.1.parse::<u64>().ok()?;
124         if inode == lock_inode { Some(pid) } else { None }
125     })
126 }
127
128 #[cfg(not(target_os = "linux"))]
129 fn get_lock_owner(_: &std::path::Path) -> Option<u64> {
130     // FIXME: Implement on other OS's
131     None
132 }