liblingo/backends/
cmake_cpp.rs

1use std::fs;
2use std::io::Write;
3
4use std::process::Command;
5
6use crate::package::App;
7use crate::util::execute_command_to_build_result;
8
9use crate::backends::{
10    BatchBackend, BatchBuildResults, BuildCommandOptions, BuildProfile, BuildResult, CommandSpec,
11};
12
13pub struct CmakeCpp;
14
15fn gen_cmake_files(app: &App, options: &BuildCommandOptions) -> BuildResult {
16    let build_dir = app.output_root.join("build");
17    fs::create_dir_all(&build_dir)?;
18
19    // location of the cmake file
20    let app_build_folder = app.src_gen_dir().join(&app.main_reactor_name);
21    let cmake_file = app_build_folder.clone().join("CMakeLists.txt");
22
23    // create potential files that come from the target properties
24    app.properties.write_artifacts(&app_build_folder)?;
25
26    // we need to modify the cmake file here to include our generated cmake files
27    let src_gen_dir = app.src_gen_dir();
28
29    // read file and append cmake include to generated cmake file
30    let mut content = fs::read_to_string(&cmake_file)?;
31    let include_statement = format!(
32        "\ninclude({}/aggregated_cmake_include.cmake)",
33        app_build_folder.display()
34    );
35    content += &*include_statement;
36
37    // overwrite cmake file
38    let mut f = fs::OpenOptions::new().write(true).open(&cmake_file)?;
39    f.write_all(content.as_ref())?;
40    f.flush()?;
41
42    // cmake args
43    let mut cmake = Command::new("cmake");
44    cmake.arg(format!(
45        "-DCMAKE_BUILD_TYPE={}",
46        if options.profile == BuildProfile::Release {
47            "RELEASE"
48        } else {
49            "DEBUG"
50        }
51    ));
52    cmake.arg(format!(
53        "-DCMAKE_INSTALL_PREFIX={}",
54        app.output_root.display()
55    ));
56    cmake.arg("-DCMAKE_INSTALL_BINDIR=bin");
57    cmake.arg("-DREACTOR_CPP_VALIDATE=ON");
58    cmake.arg("-DREACTOR_CPP_TRACE=OFF");
59    cmake.arg("-DREACTOR_CPP_LOG_LEVEL=3");
60    cmake.arg(format!(
61        "-DLF_SRC_PKG_PATH={}",
62        app.src_dir_path()
63            .expect("not a valid main reactor path")
64            .display()
65    ));
66    cmake.arg(src_gen_dir);
67    cmake.arg(format!("-B {}", build_dir.display()));
68    cmake.current_dir(&build_dir);
69
70    execute_command_to_build_result(cmake)
71}
72
73fn do_cmake_build(results: &mut BatchBuildResults, options: &BuildCommandOptions) {
74    // configure keep going parameter
75    results.keep_going(options.keep_going);
76
77    // start code-generation
78    super::lfc::LFC::do_parallel_lfc_codegen(options, results, false);
79
80    // stop process if the user request code-generation only
81    if !options.compile_target_code {
82        return;
83    }
84
85    results
86        // generate all CMake files ahead of time
87        .map(|app| gen_cmake_files(app, options))
88        // Run cmake to build everything.
89        .gather(|apps| {
90            let build_dir = apps[0].output_root.join("build");
91
92            // compile everything
93            let mut cmake = Command::new("cmake");
94            cmake.current_dir(&build_dir);
95            cmake.args(["--build", "."]);
96            for app in apps {
97                // add one target arg for each app
98                let name = app.main_reactor.file_stem().unwrap();
99                cmake.arg("--target");
100                cmake.arg(name);
101            }
102            // note: by parsing CMake stderr we would know which specific targets have failed.
103            execute_command_to_build_result(cmake)
104        })
105        .map(|app| {
106            let build_dir = app.output_root.join("build");
107            // installing
108            let mut cmake = Command::new("cmake");
109            cmake.current_dir(&build_dir);
110            cmake.args(["--install", "."]);
111            execute_command_to_build_result(cmake)
112        })
113        .map(|app| {
114            let cmake_binary_name = app.main_reactor.file_stem().unwrap();
115            // cleanup: rename executable to match the app name
116            let bin_dir = app.output_root.join("bin");
117            fs::rename(bin_dir.join(cmake_binary_name), app.executable_path())?;
118            Ok(())
119        });
120}
121
122impl BatchBackend for CmakeCpp {
123    fn execute_command(&mut self, command: &CommandSpec, results: &mut BatchBuildResults) {
124        match command {
125            CommandSpec::Build(options) => do_cmake_build(results, options),
126            CommandSpec::Clean => {
127                results.par_map(|app| {
128                    crate::util::default_build_clean(&app.output_root)?;
129                    Ok(())
130                });
131            }
132            _ => todo!(),
133        }
134    }
135}