1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
use std::process::Output;

use anyhow::Context;
use cargo_metadata::{Metadata, MetadataCommand};
use cargo_util::ProcessBuilder;
use tracing::{debug, info};

use crate::args::BuildArgs;
use crate::CIResult;

/// Run `cargo build`.
pub fn build(args: &BuildArgs) -> CIResult<Output> {
    info!("running cargo build");

    let mut cmd = ProcessBuilder::new("cargo");
    cmd.arg("build");

    // release mode
    if args.release {
        cmd.arg("--release");
    }

    // target
    if let Some(target) = &args.target {
        cmd.arg("--target");
        cmd.arg(target);
    }

    // print the internal linker invocation
    cmd.env("RUSTC_LOG", "rustc_codegen_ssa::back::link=info");

    // TODO: cargo uses RUSTFLAGS first, hence overriding flags in config.toml
    // find an alternative way to respect end-user's rustc flags
    // https://doc.rust-lang.org/cargo/reference/config.html#buildrustflags
    // moreover, adding external flags will trigger full re-compilation
    // when end-user executes normal `cargo build`

    // `--emit=llvm-ir` to emit LLVM IR bitcode
    // `-C save-temps` to save temporary files during the compilation
    // `-C passes` to pass extra LLVM passes to the compilation
    // https://doc.rust-lang.org/rustc/codegen-options/index.html

    // for some reason `env` does not escape quote in string literal...
    let rustflags = [
        "--emit=llvm-ir",
        "-Csave-temps",
        "-Crelocation-model=static",
        "-Cpasses=postdomtree",
        "-Cpasses=mem2reg",
        "-Cpasses=indvars",
        "-Cpasses=loop-simplify",
        "-Cpasses=branch-prob",
        "-Cpasses=scalar-evolution",
    ];
    cmd.env("RUSTFLAGS", rustflags.join(" "));

    debug!("args: {:?}", cmd.get_args());
    debug!("envs: {:?}", cmd.get_envs());

    cmd.exec_with_output()
}

/// Run `cargo metadata`.
pub fn metadata() -> CIResult<Metadata> {
    info!("running cargo metadata");
    let mut cmd = MetadataCommand::new();
    cmd.no_deps();
    let metadata = cmd.exec().context("failed to execute `cargo metadata`")?;
    Ok(metadata)
}