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!(
33            "The host repository is already cloned at {:?}. Skipping...",
34            destination_path
35        );
36        return Ok(());
37    }
38
39    utils::command(
40        Command::new("git").args([
41            "clone",
42            "--depth",
43            "1",
44            "--branch",
45            utils::LLVM_HOST_SOURCE_TAG,
46            utils::LLVM_HOST_SOURCE_URL,
47            destination_path.to_string_lossy().as_ref(),
48        ]),
49        "LLVM host repository cloning",
50    )?;
51
52    Ok(())
53}
54
55///
56/// Executes the LLVM repository cloning.
57///
58pub fn clone(lock: Lock, deep: bool, target_env: target_env::TargetEnv) -> anyhow::Result<()> {
59    utils::check_presence("git")?;
60
61    // Clone the host repository if the target is musl.
62    if cfg!(target_os = "linux") && target_env == target_env::TargetEnv::MUSL {
63        clone_host()?;
64    }
65
66    let destination_path = PathBuf::from(LLVMPath::DIRECTORY_LLVM_SOURCE);
67    if destination_path.exists() {
68        anyhow::bail!(
69            "The repository is already cloned at {:?}. Use `checkout` instead",
70            destination_path
71        );
72    }
73
74    let mut clone_args = vec!["clone", "--branch", lock.branch.as_str()];
75    if !deep {
76        clone_args.push("--depth");
77        clone_args.push("1");
78    }
79
80    utils::command(
81        Command::new("git")
82            .args(clone_args)
83            .arg(lock.url.as_str())
84            .arg(destination_path.to_string_lossy().as_ref()),
85        "LLVM repository cloning",
86    )?;
87
88    if let Some(r#ref) = lock.r#ref {
89        utils::command(
90            Command::new("git")
91                .args(["checkout", r#ref.as_str()])
92                .current_dir(destination_path.to_string_lossy().as_ref()),
93            "LLVM repository commit checking out",
94        )?;
95    }
96
97    Ok(())
98}
99
100///
101/// Executes the checkout of the specified branch.
102///
103pub fn checkout(lock: Lock, force: bool) -> anyhow::Result<()> {
104    let destination_path = PathBuf::from(LLVMPath::DIRECTORY_LLVM_SOURCE);
105
106    utils::command(
107        Command::new("git")
108            .current_dir(destination_path.as_path())
109            .args(["fetch", "--all", "--tags"]),
110        "LLVM repository data fetching",
111    )?;
112
113    if force {
114        utils::command(
115            Command::new("git")
116                .current_dir(destination_path.as_path())
117                .args(["clean", "-d", "-x", "--force"]),
118            "LLVM repository cleaning",
119        )?;
120    }
121
122    utils::command(
123        Command::new("git")
124            .current_dir(destination_path.as_path())
125            .args(["checkout", "--force", lock.branch.as_str()]),
126        "LLVM repository data pulling",
127    )?;
128
129    if let Some(r#ref) = lock.r#ref {
130        let mut checkout_command = Command::new("git");
131        checkout_command.current_dir(destination_path.as_path());
132        checkout_command.arg("checkout");
133        if force {
134            checkout_command.arg("--force");
135        }
136        checkout_command.arg(r#ref);
137        utils::command(&mut checkout_command, "LLVM repository checking out")?;
138    }
139
140    Ok(())
141}
142
143///
144/// Executes the building of the LLVM framework for the platform determined by the cfg macro.
145/// Since cfg is evaluated at compile time, overriding the platform with a command-line
146/// argument is not possible. So for cross-platform testing, comment out all but the
147/// line to be tested, and perhaps also checks in the platform-specific build method.
148///
149#[allow(clippy::too_many_arguments)]
150pub fn build(
151    build_type: BuildType,
152    target_env: target_env::TargetEnv,
153    targets: HashSet<Platform>,
154    llvm_projects: HashSet<llvm_project::LLVMProject>,
155    enable_rtti: bool,
156    default_target: Option<TargetTriple>,
157    enable_tests: bool,
158    enable_coverage: bool,
159    extra_args: Vec<String>,
160    ccache_variant: Option<ccache_variant::CcacheVariant>,
161    enable_assertions: bool,
162    sanitizer: Option<sanitizer::Sanitizer>,
163    enable_valgrind: bool,
164    valgrind_options: Vec<String>,
165) -> anyhow::Result<()> {
166    std::fs::create_dir_all(LLVMPath::DIRECTORY_LLVM_TARGET)?;
167
168    if cfg!(target_arch = "x86_64") {
169        if cfg!(target_os = "linux") {
170            if target_env == target_env::TargetEnv::MUSL {
171                platforms::x86_64_linux_musl::build(
172                    build_type,
173                    targets,
174                    llvm_projects,
175                    enable_rtti,
176                    default_target,
177                    enable_tests,
178                    enable_coverage,
179                    extra_args,
180                    ccache_variant,
181                    enable_assertions,
182                    sanitizer,
183                    enable_valgrind,
184                    valgrind_options,
185                )?;
186            } else if target_env == target_env::TargetEnv::GNU {
187                platforms::x86_64_linux_gnu::build(
188                    build_type,
189                    targets,
190                    llvm_projects,
191                    enable_rtti,
192                    default_target,
193                    enable_tests,
194                    enable_coverage,
195                    extra_args,
196                    ccache_variant,
197                    enable_assertions,
198                    sanitizer,
199                    enable_valgrind,
200                    valgrind_options,
201                )?;
202            } else {
203                anyhow::bail!("Unsupported target environment for x86_64 and Linux");
204            }
205        } else if cfg!(target_os = "macos") {
206            platforms::x86_64_macos::build(
207                build_type,
208                targets,
209                llvm_projects,
210                enable_rtti,
211                default_target,
212                enable_tests,
213                enable_coverage,
214                extra_args,
215                ccache_variant,
216                enable_assertions,
217                sanitizer,
218            )?;
219        } else if cfg!(target_os = "windows") {
220            platforms::x86_64_windows_gnu::build(
221                build_type,
222                targets,
223                llvm_projects,
224                enable_rtti,
225                default_target,
226                enable_tests,
227                enable_coverage,
228                extra_args,
229                ccache_variant,
230                enable_assertions,
231                sanitizer,
232            )?;
233        } else {
234            anyhow::bail!("Unsupported target OS for x86_64");
235        }
236    } else if cfg!(target_arch = "aarch64") {
237        if cfg!(target_os = "linux") {
238            if target_env == target_env::TargetEnv::MUSL {
239                platforms::aarch64_linux_musl::build(
240                    build_type,
241                    targets,
242                    llvm_projects,
243                    enable_rtti,
244                    default_target,
245                    enable_tests,
246                    enable_coverage,
247                    extra_args,
248                    ccache_variant,
249                    enable_assertions,
250                    sanitizer,
251                    enable_valgrind,
252                    valgrind_options,
253                )?;
254            } else if target_env == target_env::TargetEnv::GNU {
255                platforms::aarch64_linux_gnu::build(
256                    build_type,
257                    targets,
258                    llvm_projects,
259                    enable_rtti,
260                    default_target,
261                    enable_tests,
262                    enable_coverage,
263                    extra_args,
264                    ccache_variant,
265                    enable_assertions,
266                    sanitizer,
267                    enable_valgrind,
268                    valgrind_options,
269                )?;
270            } else {
271                anyhow::bail!("Unsupported target environment for aarch64 and Linux");
272            }
273        } else if cfg!(target_os = "macos") {
274            platforms::aarch64_macos::build(
275                build_type,
276                targets,
277                llvm_projects,
278                enable_rtti,
279                default_target,
280                enable_tests,
281                enable_coverage,
282                extra_args,
283                ccache_variant,
284                enable_assertions,
285                sanitizer,
286            )?;
287        } else {
288            anyhow::bail!("Unsupported target OS for aarch64");
289        }
290    } else {
291        anyhow::bail!("Unsupported target architecture");
292    }
293
294    Ok(())
295}
296
297///
298/// Executes the build artifacts cleaning.
299///
300pub fn clean() -> anyhow::Result<()> {
301    std::fs::remove_dir_all(PathBuf::from(LLVMPath::DIRECTORY_LLVM_TARGET))?;
302    Ok(())
303}