revive_llvm_builder/
lib.rs

1//! The revive LLVM builder library.
2
3pub mod build_type;
4pub mod builtins;
5pub mod ccache_variant;
6pub mod llvm_path;
7pub mod llvm_project;
8pub mod lock;
9pub mod platforms;
10pub mod sanitizer;
11pub mod target_env;
12pub mod target_triple;
13pub mod utils;
14
15pub use self::build_type::BuildType;
16pub use self::llvm_path::LLVMPath;
17pub use self::lock::Lock;
18pub use self::platforms::Platform;
19
20use std::collections::HashSet;
21use std::path::{Path, PathBuf};
22use std::process::Command;
23pub use target_env::TargetEnv;
24pub use target_triple::TargetTriple;
25
26/// Executes the LLVM repository cloning.
27pub fn clone(lock: Lock, deep: bool, target_env: TargetEnv) -> anyhow::Result<()> {
28    utils::check_presence("git")?;
29
30    if target_env == TargetEnv::Emscripten {
31        utils::install_emsdk()?;
32    }
33
34    let destination_path = PathBuf::from(LLVMPath::DIRECTORY_LLVM_SOURCE);
35    if destination_path.exists() {
36        log::warn!(
37            "LLVM repository directory {} already exists, falling back to checkout",
38            destination_path.display()
39        );
40        return checkout(lock, false);
41    }
42
43    let mut clone_args = vec!["clone", "--branch", lock.branch.as_str()];
44    if !deep {
45        clone_args.push("--depth");
46        clone_args.push("1");
47    }
48
49    utils::command(
50        Command::new("git")
51            .args(clone_args)
52            .arg(lock.url.as_str())
53            .arg(destination_path.to_string_lossy().as_ref()),
54        "LLVM repository cloning",
55    )?;
56
57    if let Some(r#ref) = lock.r#ref {
58        utils::command(
59            Command::new("git")
60                .args(["checkout", r#ref.as_str()])
61                .current_dir(destination_path.to_string_lossy().as_ref()),
62            "LLVM repository commit checking out",
63        )?;
64    }
65
66    Ok(())
67}
68
69/// Executes the checkout of the specified branch.
70pub fn checkout(lock: Lock, force: bool) -> anyhow::Result<()> {
71    let destination_path = PathBuf::from(LLVMPath::DIRECTORY_LLVM_SOURCE);
72
73    utils::command(
74        Command::new("git")
75            .current_dir(destination_path.as_path())
76            .args(["fetch", "--all", "--tags"]),
77        "LLVM repository data fetching",
78    )?;
79
80    if force {
81        utils::command(
82            Command::new("git")
83                .current_dir(destination_path.as_path())
84                .args(["clean", "-d", "-x", "--force"]),
85            "LLVM repository cleaning",
86        )?;
87    }
88
89    utils::command(
90        Command::new("git")
91            .current_dir(destination_path.as_path())
92            .args(["checkout", "--force", lock.branch.as_str()]),
93        "LLVM repository data pulling",
94    )?;
95
96    if let Some(r#ref) = lock.r#ref {
97        let mut checkout_command = Command::new("git");
98        checkout_command.current_dir(destination_path.as_path());
99        checkout_command.arg("checkout");
100        if force {
101            checkout_command.arg("--force");
102        }
103        checkout_command.arg(r#ref);
104        utils::command(&mut checkout_command, "LLVM repository checking out")?;
105    }
106
107    Ok(())
108}
109
110/// Executes the building of the LLVM framework for the platform determined by the cfg macro.
111/// Since cfg is evaluated at compile time, overriding the platform with a command-line
112/// argument is not possible. So for cross-platform testing, comment out all but the
113/// line to be tested, and perhaps also checks in the platform-specific build method.
114#[allow(clippy::too_many_arguments)]
115pub fn build(
116    build_type: BuildType,
117    target_env: TargetEnv,
118    targets: HashSet<Platform>,
119    llvm_projects: HashSet<llvm_project::LLVMProject>,
120    enable_rtti: bool,
121    default_target: Option<TargetTriple>,
122    enable_tests: bool,
123    enable_coverage: bool,
124    extra_args: &[String],
125    ccache_variant: Option<ccache_variant::CcacheVariant>,
126    enable_assertions: bool,
127    sanitizer: Option<sanitizer::Sanitizer>,
128    enable_valgrind: bool,
129) -> anyhow::Result<()> {
130    log::trace!("build type: {build_type:?}");
131    log::trace!("target env: {target_env:?}");
132    log::trace!("targets: {targets:?}");
133    log::trace!("llvm projects: {llvm_projects:?}");
134    log::trace!("enable rtti: {enable_rtti:?}");
135    log::trace!("default target: {default_target:?}");
136    log::trace!("eneable tests: {enable_tests:?}");
137    log::trace!("enable_coverage: {enable_coverage:?}");
138    log::trace!("extra args: {extra_args:?}");
139    log::trace!("sanitzer: {sanitizer:?}");
140    log::trace!("enable valgrind: {enable_valgrind:?}");
141
142    if !PathBuf::from(LLVMPath::DIRECTORY_LLVM_SOURCE).exists() {
143        log::error!(
144            "LLVM project source directory {} does not exist (run `revive-llvm --target-env {target_env} clone`)",
145            LLVMPath::DIRECTORY_LLVM_SOURCE
146        )
147    }
148
149    std::fs::create_dir_all(llvm_path::DIRECTORY_LLVM_TARGET.get().unwrap())?;
150
151    if cfg!(target_arch = "x86_64") {
152        if cfg!(target_os = "linux") {
153            if target_env == TargetEnv::MUSL {
154                platforms::x86_64_linux_musl::build(
155                    build_type,
156                    targets,
157                    llvm_projects,
158                    enable_rtti,
159                    default_target,
160                    enable_tests,
161                    enable_coverage,
162                    extra_args,
163                    ccache_variant,
164                    enable_assertions,
165                    sanitizer,
166                    enable_valgrind,
167                )?;
168            } else if target_env == TargetEnv::GNU {
169                platforms::x86_64_linux_gnu::build(
170                    build_type,
171                    targets,
172                    llvm_projects,
173                    enable_rtti,
174                    default_target,
175                    enable_tests,
176                    enable_coverage,
177                    extra_args,
178                    ccache_variant,
179                    enable_assertions,
180                    sanitizer,
181                    enable_valgrind,
182                )?;
183            } else if target_env == TargetEnv::Emscripten {
184                platforms::wasm32_emscripten::build(
185                    build_type,
186                    targets,
187                    llvm_projects,
188                    enable_rtti,
189                    default_target,
190                    enable_tests,
191                    enable_coverage,
192                    extra_args,
193                    ccache_variant,
194                    enable_assertions,
195                    sanitizer,
196                    enable_valgrind,
197                )?;
198            } else {
199                anyhow::bail!("Unsupported target environment for x86_64 and Linux");
200            }
201        } else if cfg!(target_os = "macos") {
202            platforms::x86_64_macos::build(
203                build_type,
204                targets,
205                llvm_projects,
206                enable_rtti,
207                default_target,
208                enable_tests,
209                enable_coverage,
210                extra_args,
211                ccache_variant,
212                enable_assertions,
213                sanitizer,
214            )?;
215        } else if cfg!(target_os = "windows") {
216            platforms::x86_64_windows_msvc::build(
217                build_type,
218                targets,
219                llvm_projects,
220                enable_rtti,
221                default_target,
222                enable_tests,
223                enable_coverage,
224                extra_args,
225                ccache_variant,
226                enable_assertions,
227                sanitizer,
228            )?;
229        } else {
230            anyhow::bail!("Unsupported target OS for x86_64");
231        }
232    } else if cfg!(target_arch = "aarch64") {
233        if cfg!(target_os = "linux") {
234            if target_env == TargetEnv::MUSL {
235                platforms::aarch64_linux_musl::build(
236                    build_type,
237                    targets,
238                    llvm_projects,
239                    enable_rtti,
240                    default_target,
241                    enable_tests,
242                    enable_coverage,
243                    extra_args,
244                    ccache_variant,
245                    enable_assertions,
246                    sanitizer,
247                    enable_valgrind,
248                )?;
249            } else if target_env == TargetEnv::GNU {
250                platforms::aarch64_linux_gnu::build(
251                    build_type,
252                    targets,
253                    llvm_projects,
254                    enable_rtti,
255                    default_target,
256                    enable_tests,
257                    enable_coverage,
258                    extra_args,
259                    ccache_variant,
260                    enable_assertions,
261                    sanitizer,
262                    enable_valgrind,
263                )?;
264            } else {
265                anyhow::bail!("Unsupported target environment for aarch64 and Linux");
266            }
267        } else if cfg!(target_os = "macos") {
268            if target_env == TargetEnv::Emscripten {
269                platforms::wasm32_emscripten::build(
270                    build_type,
271                    targets,
272                    llvm_projects,
273                    enable_rtti,
274                    default_target,
275                    enable_tests,
276                    enable_coverage,
277                    extra_args,
278                    ccache_variant,
279                    enable_assertions,
280                    sanitizer,
281                    enable_valgrind,
282                )?;
283            } else {
284                platforms::aarch64_macos::build(
285                    build_type,
286                    targets,
287                    llvm_projects,
288                    enable_rtti,
289                    default_target,
290                    enable_tests,
291                    enable_coverage,
292                    extra_args,
293                    ccache_variant,
294                    enable_assertions,
295                    sanitizer,
296                )?;
297            }
298        } else {
299            anyhow::bail!("Unsupported target OS for aarch64");
300        }
301    } else {
302        anyhow::bail!("Unsupported target architecture");
303    }
304
305    crate::builtins::build(
306        build_type,
307        target_env,
308        default_target,
309        extra_args,
310        ccache_variant,
311        sanitizer,
312    )?;
313
314    Ok(())
315}
316
317/// Executes the build artifacts cleaning.
318pub fn clean() -> anyhow::Result<()> {
319    let remove_if_exists = |path: &Path| {
320        if !path.exists() {
321            return Ok(());
322        }
323        log::info!("deleting {}", path.display());
324        std::fs::remove_dir_all(path)
325    };
326
327    remove_if_exists(
328        llvm_path::DIRECTORY_LLVM_TARGET
329            .get()
330            .expect("target_env is always set because of the default value")
331            .parent()
332            .expect("target_env parent directory is target-llvm"),
333    )?;
334    remove_if_exists(&PathBuf::from(LLVMPath::DIRECTORY_EMSDK_SOURCE))?;
335    remove_if_exists(&PathBuf::from(LLVMPath::DIRECTORY_LLVM_SOURCE))?;
336    remove_if_exists(&PathBuf::from(LLVMPath::DIRECTORY_LLVM_HOST_SOURCE))?;
337
338    Ok(())
339}