compiler_llvm_builder/
lib.rs

1//!
2//! The ZKsync LLVM builder library.
3//!
4
5pub mod build_type;
6pub mod ccache_variant;
7pub mod llvm_path;
8pub mod llvm_project;
9pub mod lock;
10pub mod platforms;
11pub mod sanitizer;
12pub mod target_env;
13pub mod target_triple;
14pub mod utils;
15
16pub use self::build_type::BuildType;
17pub use self::llvm_path::LLVMPath;
18pub use self::lock::Lock;
19pub use self::platforms::Platform;
20pub use self::target_triple::TargetTriple;
21
22use std::collections::HashSet;
23use std::path::PathBuf;
24use std::process::Command;
25
26///
27/// Executes the LLVM host repository cloning for stage 1 MUSL builds.
28///
29pub fn clone_host() -> anyhow::Result<()> {
30    let destination_path = PathBuf::from(LLVMPath::DIRECTORY_LLVM_HOST_SOURCE);
31    if destination_path.exists() {
32        eprintln!("The host repository is already cloned at {destination_path:?}. Skipping...",);
33        return Ok(());
34    }
35
36    utils::command(
37        Command::new("git").args([
38            "clone",
39            "--depth",
40            "1",
41            "--branch",
42            utils::LLVM_HOST_SOURCE_TAG,
43            utils::LLVM_HOST_SOURCE_URL,
44            destination_path.to_string_lossy().as_ref(),
45        ]),
46        "LLVM host repository cloning",
47    )?;
48
49    Ok(())
50}
51
52///
53/// Executes the LLVM repository cloning.
54///
55pub fn clone(lock: Lock, deep: bool, target_env: target_env::TargetEnv) -> anyhow::Result<()> {
56    utils::exists("git")?;
57
58    // Clone the host repository if the target is musl.
59    if cfg!(target_os = "linux") && target_env == target_env::TargetEnv::MUSL {
60        clone_host()?;
61    }
62
63    let destination_path = PathBuf::from(LLVMPath::DIRECTORY_LLVM_SOURCE);
64    if destination_path.exists() {
65        anyhow::bail!(
66            "The repository is already cloned at {destination_path:?}. Use `checkout` instead",
67        );
68    }
69
70    let mut clone_args = vec!["clone", "--branch", lock.branch.as_str()];
71    if !deep {
72        clone_args.push("--depth");
73        clone_args.push("1");
74    }
75
76    utils::command(
77        Command::new("git")
78            .args(clone_args)
79            .arg(lock.url.as_str())
80            .arg(destination_path.to_string_lossy().as_ref()),
81        "LLVM repository cloning",
82    )?;
83
84    if let Some(r#ref) = lock.r#ref {
85        utils::command(
86            Command::new("git")
87                .args(["checkout", r#ref.as_str()])
88                .current_dir(destination_path.to_string_lossy().as_ref()),
89            "LLVM repository commit checking out",
90        )?;
91    }
92
93    Ok(())
94}
95
96///
97/// Executes the checkout of the specified branch.
98///
99pub fn checkout(lock: Lock, force: bool) -> anyhow::Result<()> {
100    let destination_path = PathBuf::from(LLVMPath::DIRECTORY_LLVM_SOURCE);
101
102    utils::command(
103        Command::new("git")
104            .current_dir(destination_path.as_path())
105            .args(["fetch", "--all", "--tags"]),
106        "LLVM repository data fetching",
107    )?;
108
109    if force {
110        utils::command(
111            Command::new("git")
112                .current_dir(destination_path.as_path())
113                .args(["clean", "-d", "-x", "--force"]),
114            "LLVM repository cleaning",
115        )?;
116    }
117
118    utils::command(
119        Command::new("git")
120            .current_dir(destination_path.as_path())
121            .args(["checkout", "--force", lock.branch.as_str()]),
122        "LLVM repository data pulling",
123    )?;
124
125    if let Some(r#ref) = lock.r#ref {
126        let mut checkout_command = Command::new("git");
127        checkout_command.current_dir(destination_path.as_path());
128        checkout_command.arg("checkout");
129        if force {
130            checkout_command.arg("--force");
131        }
132        checkout_command.arg(r#ref);
133        utils::command(&mut checkout_command, "LLVM repository checking out")?;
134    }
135
136    Ok(())
137}
138
139///
140/// Executes the building of the LLVM framework for the platform determined by the cfg macro.
141/// Since cfg is evaluated at compile time, overriding the platform with a command-line
142/// argument is not possible. So for cross-platform testing, comment out all but the
143/// line to be tested, and perhaps also checks in the platform-specific build method.
144///
145#[allow(clippy::too_many_arguments)]
146pub fn build(
147    build_type: BuildType,
148    target_env: target_env::TargetEnv,
149    targets: HashSet<Platform>,
150    llvm_projects: HashSet<llvm_project::LLVMProject>,
151    enable_rtti: bool,
152    default_target: Option<TargetTriple>,
153    enable_tests: bool,
154    enable_coverage: bool,
155    extra_args: Vec<String>,
156    ccache_variant: Option<ccache_variant::CcacheVariant>,
157    enable_assertions: bool,
158    sanitizer: Option<sanitizer::Sanitizer>,
159    enable_valgrind: bool,
160    valgrind_options: Vec<String>,
161) -> anyhow::Result<()> {
162    std::fs::create_dir_all(LLVMPath::DIRECTORY_LLVM_TARGET)?;
163
164    if cfg!(target_arch = "x86_64") {
165        if cfg!(target_os = "linux") {
166            if target_env == target_env::TargetEnv::MUSL {
167                platforms::x86_64_linux_musl::build(
168                    build_type,
169                    targets,
170                    llvm_projects,
171                    enable_rtti,
172                    default_target,
173                    enable_tests,
174                    enable_coverage,
175                    extra_args,
176                    ccache_variant,
177                    enable_assertions,
178                    sanitizer,
179                    enable_valgrind,
180                    valgrind_options,
181                )?;
182            } else if target_env == target_env::TargetEnv::GNU {
183                platforms::x86_64_linux_gnu::build(
184                    build_type,
185                    targets,
186                    llvm_projects,
187                    enable_rtti,
188                    default_target,
189                    enable_tests,
190                    enable_coverage,
191                    extra_args,
192                    ccache_variant,
193                    enable_assertions,
194                    sanitizer,
195                    enable_valgrind,
196                    valgrind_options,
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_gnu::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 == 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                    valgrind_options,
249                )?;
250            } else if target_env == target_env::TargetEnv::GNU {
251                platforms::aarch64_linux_gnu::build(
252                    build_type,
253                    targets,
254                    llvm_projects,
255                    enable_rtti,
256                    default_target,
257                    enable_tests,
258                    enable_coverage,
259                    extra_args,
260                    ccache_variant,
261                    enable_assertions,
262                    sanitizer,
263                    enable_valgrind,
264                    valgrind_options,
265                )?;
266            } else {
267                anyhow::bail!("Unsupported target environment for aarch64 and Linux");
268            }
269        } else if cfg!(target_os = "macos") {
270            platforms::aarch64_macos::build(
271                build_type,
272                targets,
273                llvm_projects,
274                enable_rtti,
275                default_target,
276                enable_tests,
277                enable_coverage,
278                extra_args,
279                ccache_variant,
280                enable_assertions,
281                sanitizer,
282            )?;
283        } else {
284            anyhow::bail!("Unsupported target OS for aarch64");
285        }
286    } else {
287        anyhow::bail!("Unsupported target architecture");
288    }
289
290    Ok(())
291}
292
293///
294/// Executes the build artifacts cleaning.
295///
296pub fn clean() -> anyhow::Result<()> {
297    std::fs::remove_dir_all(PathBuf::from(LLVMPath::DIRECTORY_LLVM_TARGET))?;
298    Ok(())
299}