radix_clis/utils/
cargo.rs

1use std::ffi::OsStr;
2use std::io;
3use std::path::Path;
4use std::path::PathBuf;
5use std::process::Command;
6use std::process::ExitStatus;
7
8use radix_engine::utils::*;
9use radix_engine_interface::types::Level;
10use scrypto_compiler::*;
11
12#[derive(Debug)]
13pub enum BuildError {
14    ScryptoCompilerError(ScryptoCompilerError),
15
16    IOErrorAtPath(io::Error, PathBuf),
17
18    SchemaExtractionError(ExtractSchemaError),
19
20    SchemaEncodeError(sbor::EncodeError),
21
22    BuildArtifactsEmpty,
23
24    WorkspaceNotSupported,
25
26    EnvParsingError,
27}
28
29#[derive(Debug)]
30pub enum TestError {
31    NotCargoPackage,
32
33    BuildError(BuildError),
34
35    IOError(io::Error),
36
37    CargoFailure(ExitStatus),
38}
39
40#[derive(Debug)]
41pub enum FormatError {
42    BuildError(BuildError),
43
44    IOError(io::Error),
45
46    CargoFailure(ExitStatus),
47}
48
49/// Builds a package.
50pub fn build_package<P: AsRef<Path>>(
51    base_path: P,
52    disable_wasm_opt: bool,
53    log_level: Level,
54    coverage: bool,
55    env: impl IntoIterator<Item = (String, String)>,
56) -> Result<Vec<(PathBuf, PathBuf)>, BuildError> {
57    let mut compiler_builder = ScryptoCompiler::builder();
58    compiler_builder
59        .manifest_path(base_path.as_ref())
60        .log_level(log_level);
61
62    if disable_wasm_opt {
63        compiler_builder.optimize_with_wasm_opt(None);
64    }
65    if coverage {
66        compiler_builder.coverage();
67
68        let mut target_path = PathBuf::from(base_path.as_ref());
69        target_path.push("coverage");
70        compiler_builder.target_directory(target_path);
71    }
72    env.into_iter().for_each(|(name, value)| {
73        compiler_builder.env(name.as_ref(), EnvironmentVariableAction::Set(value));
74    });
75
76    let build_results = compiler_builder
77        .compile()
78        .map_err(BuildError::ScryptoCompilerError)?;
79
80    Ok(build_results
81        .iter()
82        .map(|item| {
83            (
84                item.wasm.path.to_owned(),
85                item.package_definition.path.to_owned(),
86            )
87        })
88        .collect())
89}
90
91/// Runs tests within a package.
92pub fn test_package<P: AsRef<Path>, I, S, E, K, V>(
93    path: P,
94    args: I,
95    coverage: bool,
96    locked: bool,
97    env_vars: E,
98) -> Result<(), TestError>
99where
100    I: IntoIterator<Item = S>,
101    S: AsRef<OsStr>,
102    E: IntoIterator<Item = (K, V)>,
103    K: AsRef<OsStr>,
104    V: AsRef<OsStr>,
105{
106    if !coverage {
107        build_package(&path, false, Level::Trace, false, []).map_err(TestError::BuildError)?;
108    }
109
110    let mut cargo = path.as_ref().to_owned();
111    cargo.push("Cargo.toml");
112    if cargo.exists() {
113        let status = Command::new("cargo")
114            .arg("test")
115            .arg("--release")
116            .arg("--manifest-path")
117            .arg(cargo.to_str().unwrap())
118            .args({
119                if coverage {
120                    vec!["--features", "scrypto-test/coverage"]
121                } else {
122                    vec![]
123                }
124            })
125            .args({
126                if locked {
127                    vec!["--locked"]
128                } else {
129                    vec![]
130                }
131            })
132            .arg("--")
133            .args(args)
134            .envs(env_vars)
135            .status()
136            .map_err(TestError::IOError)?;
137        if !status.success() {
138            return Err(TestError::CargoFailure(status));
139        }
140        Ok(())
141    } else {
142        Err(TestError::NotCargoPackage)
143    }
144}
145
146/// Format a package.
147pub fn fmt_package<P: AsRef<Path>>(path: P, check: bool, quiet: bool) -> Result<(), FormatError> {
148    let mut cargo = path.as_ref().to_owned();
149    cargo.push("Cargo.toml");
150    if cargo.exists() {
151        let status = Command::new("cargo")
152            .arg("fmt")
153            .arg("--manifest-path")
154            .arg(cargo.to_str().unwrap())
155            .args({
156                let mut args = Vec::new();
157                if check {
158                    args.push("--check")
159                }
160                if quiet {
161                    args.push("--quiet")
162                }
163                args
164            })
165            .status()
166            .map_err(FormatError::IOError)?;
167
168        if status.success() {
169            Ok(())
170        } else {
171            Err(FormatError::CargoFailure(status))
172        }
173    } else {
174        Ok(())
175    }
176}