scrypto_test/ledger_simulator/
compile.rs

1use crate::prelude::*;
2use scrypto_compiler::*;
3use std::path::Path;
4
5pub enum CompileProfile {
6    /// Uses default compilation options - same as `scrypto build`. Should be used in all cases which requires
7    /// compilation results to be as close to production as possible (for instance costing related tests).
8    Standard,
9    /// Same as Standard with enabled all logs from error to trace level.
10    StandardWithTraceLogs,
11    /// Disables WASM optimization to speed-up compilation process, by default used by SDK PackageFactory.
12    Fast,
13    /// Disables WASM optimization and enables all logs from error to trace level, by default used by Ledger Simulator.
14    FastWithTraceLogs,
15    /// Disables WASM optimization and disables all logs.
16    FastWithNoLogs,
17}
18
19pub struct Compile;
20
21impl Compile {
22    pub fn compile<P: AsRef<Path>>(
23        package_dir: P,
24        compile_profile: CompileProfile,
25    ) -> (Vec<u8>, PackageDefinition) {
26        Self::compile_with_env_vars(
27            package_dir,
28            DEFAULT_ENVIRONMENT_VARIABLES
29                .clone()
30                .into_iter()
31                .filter_map(|(key, value)| match value {
32                    EnvironmentVariableAction::Set(value) => Some((key, value)),
33                    EnvironmentVariableAction::Unset => None,
34                })
35                .collect(),
36            compile_profile,
37            true,
38        )
39    }
40
41    // required for compile-blueprints-at-build-time feature in radix-engine-tests
42    pub fn compile_with_env_vars<P: AsRef<Path>>(
43        package_dir: P,
44        env_vars: sbor::rust::collections::BTreeMap<String, String>,
45        compile_profile: CompileProfile,
46        _use_coverage: bool,
47    ) -> (Vec<u8>, PackageDefinition) {
48        // Initialize compiler
49        let mut compiler_builder = ScryptoCompiler::builder();
50        compiler_builder.manifest_path(package_dir.as_ref());
51
52        match compile_profile {
53            CompileProfile::Standard => (),
54            CompileProfile::StandardWithTraceLogs => {
55                compiler_builder.log_level(Level::Trace); // all logs from error to trace
56            }
57            CompileProfile::Fast => {
58                compiler_builder.optimize_with_wasm_opt(None);
59            }
60            CompileProfile::FastWithTraceLogs => {
61                compiler_builder.optimize_with_wasm_opt(None);
62                compiler_builder.log_level(Level::Trace); // all logs from error to trace
63            }
64            CompileProfile::FastWithNoLogs => {
65                compiler_builder.optimize_with_wasm_opt(None);
66                compiler_builder.disable_logs();
67            }
68        }
69
70        env_vars.iter().for_each(|(name, value)| {
71            compiler_builder.env(name, EnvironmentVariableAction::Set(value.clone()));
72        });
73
74        #[cfg(feature = "coverage")]
75        if _use_coverage {
76            compiler_builder.coverage();
77
78            let coverage_dir = std::path::PathBuf::from(package_dir.as_ref())
79                .join("target")
80                .join("coverage");
81            compiler_builder.target_directory(coverage_dir);
82        }
83
84        let mut compiler = compiler_builder
85            .build()
86            .unwrap_or_else(|err| panic!("Failed to initialize Scrypto Compiler {:?}", err));
87
88        #[cfg(feature = "coverage")]
89        // Check if binary exists in coverage directory, if it doesn't only then build it
90        if _use_coverage {
91            let manifest = compiler.get_main_manifest_definition();
92            if manifest.target_output_binary_rpd_path.exists()
93                && manifest.target_phase_2_build_wasm_output_path.exists()
94            {
95                let code = std::fs::read(&manifest.target_phase_2_build_wasm_output_path)
96                    .unwrap_or_else(|err| {
97                        panic!(
98                            "Failed to read built WASM from path {:?} - {:?}",
99                            &manifest.target_phase_2_build_wasm_output_path, err
100                        )
101                    });
102                let definition = std::fs::read(&manifest.target_output_binary_rpd_path)
103                    .unwrap_or_else(|err| {
104                        panic!(
105                            "Failed to read package definition from path {:?} - {:?}",
106                            &manifest.target_output_binary_rpd_path, err
107                        )
108                    });
109                let definition = manifest_decode::<ManifestPackageDefinition>(&definition)
110                    .unwrap_or_else(|err| {
111                        panic!(
112                            "Failed to parse package definition from path {:?} - {:?}",
113                            &manifest.target_output_binary_rpd_path, err
114                        )
115                    })
116                    .try_into_typed()
117                    .unwrap_or_else(|err| {
118                        panic!(
119                            "Failed to parse package definition from path {:?} - {:?}",
120                            &manifest.target_output_binary_rpd_path, err
121                        )
122                    });
123                return (code, definition);
124            }
125        }
126
127        // Build
128        let mut build_artifacts = compiler.compile().unwrap_or_else(|error| {
129            if let ScryptoCompilerError::CargoBuildFailure(exit_code) = &error {
130                eprintln!("Package compilation error:\n{:?}", exit_code)
131            }
132
133            panic!(
134                "Failed to compile package: {:?}, error: {:?}",
135                package_dir.as_ref(),
136                error
137            );
138        });
139
140        if !build_artifacts.is_empty() {
141            let build_artifact = build_artifacts.remove(0); // take first element
142            (
143                build_artifact.wasm.content,
144                build_artifact.package_definition.content,
145            )
146        } else {
147            panic!("Build artifacts list is empty: {:?}", package_dir.as_ref(),);
148        }
149    }
150}
151
152#[cfg(test)]
153mod tests {
154    use super::{Compile, CompileProfile};
155    use std::process::Command;
156
157    fn compile_blueprint(additional_args: &[&str]) -> Vec<u8> {
158        // Build `scrypto` cli
159        Command::new("cargo")
160            .arg("build")
161            .arg("--release")
162            .arg("--bin")
163            .arg("scrypto")
164            .current_dir(concat!(env!("CARGO_MANIFEST_DIR"), "/../radix-clis"))
165            .output()
166            .inspect_err(|e| println!("Scrypto cli build failed: {}", e))
167            .unwrap();
168
169        // Run `scrypto build` for example blueprint
170        Command::new(concat!(
171            env!("CARGO_MANIFEST_DIR"),
172            "/../target/release/scrypto"
173        ))
174        .arg("build")
175        .args(additional_args)
176        .current_dir(concat!(
177            env!("CARGO_MANIFEST_DIR"),
178            "/tests/blueprints/tuple-return"
179        ))
180        .output()
181        .inspect_err(|e| println!("Blueprint compilation falied: {}", e))
182        .unwrap();
183
184        let output_file = concat!(
185            env!("CARGO_MANIFEST_DIR"),
186            "/tests/blueprints/target/wasm32-unknown-unknown/release/tuple_return.wasm"
187        );
188        std::fs::read(output_file)
189            .inspect_err(|e| println!("Failed to load file: {}, error: {}", output_file, e))
190            .unwrap()
191    }
192
193    #[test]
194    fn validate_compile_profile_standard() {
195        // Compile blueprint using `scrypto compile` command
196        let output_file_content = compile_blueprint(&[]);
197
198        // Compile same blueprint using Compile object
199        let (bin, _) = Compile::compile(
200            concat!(env!("CARGO_MANIFEST_DIR"), "/tests/blueprints/tuple-return"),
201            CompileProfile::Standard,
202        );
203
204        // Assert
205        assert_eq!(
206            output_file_content.len(),
207            bin.len(),
208            "Wasm files should have same size."
209        );
210        assert_eq!(
211            output_file_content, bin,
212            "Wasm files should have same content."
213        )
214    }
215
216    #[test]
217    fn validate_compile_profile_standard_with_logs() {
218        // Compile blueprint using `scrypto compile` command
219        let output_file_content = compile_blueprint(&[]);
220
221        // Compile same blueprint using Compile object
222        let (bin, _) = Compile::compile(
223            concat!(env!("CARGO_MANIFEST_DIR"), "/tests/blueprints/tuple-return"),
224            CompileProfile::StandardWithTraceLogs,
225        );
226
227        // Assert
228        assert!(
229            output_file_content.len() < bin.len(),
230            "Size of Wasm file compiled by `scrypto build` command should be smaller."
231        );
232    }
233
234    #[test]
235    fn validate_compile_profile_fast() {
236        // Compile blueprint using `scrypto compile` command
237        let output_file_content = compile_blueprint(&[]);
238
239        // Compile same blueprint using Compile object
240        let (bin, _) = Compile::compile(
241            concat!(env!("CARGO_MANIFEST_DIR"), "/tests/blueprints/tuple-return"),
242            CompileProfile::Fast,
243        );
244
245        // Assert
246        assert!(
247            output_file_content.len() < bin.len(),
248            "Size of Wasm file compiled by `scrypto build` command should be smaller."
249        );
250    }
251
252    #[test]
253    fn validate_compile_profile_fast_with_logs() {
254        // Compile blueprint using `scrypto compile` command
255        let output_file_content = compile_blueprint(&[]);
256
257        // Compile same blueprint using Compile object
258        let (bin, _) = Compile::compile(
259            concat!(env!("CARGO_MANIFEST_DIR"), "/tests/blueprints/tuple-return"),
260            CompileProfile::FastWithTraceLogs,
261        );
262
263        // Assert
264        assert!(
265            output_file_content.len() < bin.len(),
266            "Size of Wasm file compiled by `scrypto build` command should be smaller."
267        );
268    }
269
270    #[test]
271    fn verify_scrypto_build_with_args_for_compile_profile_standard_with_logs() {
272        // Compile blueprint using `scrypto compile` command
273        let output_file_content = compile_blueprint(&["--log-level", "TRACE"]);
274
275        // Compile same blueprint using Compile object
276        let (bin, _) = Compile::compile(
277            concat!(env!("CARGO_MANIFEST_DIR"), "/tests/blueprints/tuple-return"),
278            CompileProfile::StandardWithTraceLogs,
279        );
280
281        // Assert
282        assert_eq!(
283            output_file_content.len(),
284            bin.len(),
285            "Wasm files should have same size."
286        );
287        assert_eq!(
288            output_file_content, bin,
289            "Wasm files should have same content."
290        )
291    }
292
293    #[test]
294    fn verify_scrypto_build_with_args_for_compile_profile_fast() {
295        // Compile blueprint using `scrypto compile` command
296        let output_file_content = compile_blueprint(&["--disable-wasm-opt"]);
297
298        // Compile same blueprint using Compile object
299        let (bin, _) = Compile::compile(
300            concat!(env!("CARGO_MANIFEST_DIR"), "/tests/blueprints/tuple-return"),
301            CompileProfile::Fast,
302        );
303
304        // Assert
305        assert_eq!(
306            output_file_content.len(),
307            bin.len(),
308            "Wasm files should have same size."
309        );
310        assert_eq!(
311            output_file_content, bin,
312            "Wasm files should have same content."
313        )
314    }
315
316    #[test]
317    fn verify_scrypto_build_with_args_for_compile_profile_fast_with_logs() {
318        // Compile blueprint using `scrypto compile` command
319        let output_file_content =
320            compile_blueprint(&["--disable-wasm-opt", "--log-level", "TRACE"]);
321
322        // Compile same blueprint using Compile object
323        let (bin, _) = Compile::compile(
324            concat!(env!("CARGO_MANIFEST_DIR"), "/tests/blueprints/tuple-return"),
325            CompileProfile::FastWithTraceLogs,
326        );
327
328        // Assert
329        assert_eq!(
330            output_file_content.len(),
331            bin.len(),
332            "Wasm files should have same size."
333        );
334        assert_eq!(
335            output_file_content, bin,
336            "Wasm files should have same content."
337        )
338    }
339}