parabuild/lib.rs
1//! Parabuild is a Rust tool that helps you compile complex (single file) projects in parallel,
2//! such as some C++/CUDA projects that heavily use templates (cannot achieve the best
3//! performance through `make -j`).
4//!
5//! # Quick Start
6//!
7//! The following is an example of how to use parabuild-rust to compile a C++ project.
8//!
9//! We use [handlebars templating language](https://handlebarsjs.com/) to generate source file, here is an example:
10//!
11//! ```cpp
12//! #include <iostream>
13//!
14//! template <int n>
15//! void print()
16//! {
17//! std::cout << n << std::endl;
18//! }
19//!
20//! int main()
21//! {
22//! print<{{N}}>();
23//! return 0;
24//! }
25//! ```
26//!
27//! Main body:
28//!
29//! ```rust
30//! use parabuild::Parabuilder;
31//! use serde_json::{json, to_string_pretty, Value as JsonValue};
32//!
33//! fn main() {
34//! let project_path = "tests/example_cmake_project"; // your project path
35//! let workspaces_path = "workspaces"; // where to store the workspaces, executables, etc.
36//! let template_path = "src/main.cpp.template"; // template file in the project
37//! let target_executable_file = "build/main"; // target executable file
38//! let datas = vec![json!({"N": "10"}), json!({"N": "20"})];
39//! let mut parabuilder = Parabuilder::new(
40//! project_path,
41//! workspaces_path,
42//! template_path,
43//! &[target_executable_file],
44//! );
45//! parabuilder.set_datas(datas).unwrap();
46//! parabuilder.init_workspace().unwrap();
47//! let (run_data, _compile_error_datas, _processed_data_ids): (
48//! JsonValue,
49//! Vec<JsonValue>,
50//! Vec<usize>,
51//! ) = parabuilder.run().unwrap();
52//! println!("{}", to_string_pretty(&run_data).unwrap());
53//! /*
54//! [
55//! {
56//! "data": {
57//! "N": "10"
58//! },
59//! "status": 0,
60//! "stderr": "",
61//! "stdout": "10\n"
62//! },
63//! {
64//! "data": {
65//! "N": "20"
66//! },
67//! "status": 0,
68//! "stderr": "",
69//! "stdout": "20\n"
70//! }
71//! ]
72//! */
73//! }
74//! ```
75//!
76//! We return `compute_error_datas` to indicate the data with compilation errors. Compilation errors are common in debugging projects that heavily use templates.
77//!
78//! ## Advanced Usage
79//!
80//! For more advanced usage, please refer to the [documentation](https://docs.rs/parabuild) and [examples/complete_usage.rs](examples/complete_usage.rs).
81//!
82//! # Best Practices
83//!
84//! We mainly share how to make your normal work compatible with parabuild and avoid maintaining two sets of code at the same time.
85//!
86//! ## CMake-project
87//!
88//! You need to define a macro to use normal code when not parabuild.
89//!
90//! `CMakelists.txt`:
91//!
92//! ```CMakeLists.txt
93//! cmake_minimum_required(VERSION 3.12)
94//!
95//! project(ExampleProject)
96//!
97//! set(CMAKE_CXX_STANDARD 11)
98//!
99//! if (PARABUILD STREQUAL "ON")
100//! add_compile_definitions(PARABUILD=ON)
101//! endif()
102//!
103//! add_executable(main src/main.cpp)
104//! ```
105//!
106//! `main.cpp`:
107//!
108//! ```cpp
109//! #include <iostream>
110//!
111//! template <int n>
112//! void print()
113//! {
114//! std::cout << n << std::endl;
115//! }
116//!
117//! int main()
118//! {
119//! #ifndef PARABUILD
120//! print<42>();
121//! #else
122//! print<{{default N 42}}>();
123//! #endif
124//! return 0;
125//! }
126//! ```
127//!
128//! run script:
129//!
130//! ```shell
131//! parabuild \
132//! tests/example_cmake_project \
133//! build/main \
134//! -t src/main.cpp \
135//! --data '[{"N": 10}, {"N": 20}]'
136//! ```
137//!
138//! output:
139//!
140//! ```shell
141//! [
142//! {
143//! "data": {
144//! "N": 10
145//! },
146//! "status": 0,
147//! "stderr": "",
148//! "stdout": "10\n"
149//! },
150//! {
151//! "data": {
152//! "N": 20
153//! },
154//! "status": 0,
155//! "stderr": "",
156//! "stdout": "20\n"
157//! }
158//! ]
159//! ```
160//!
161//! ## Makefile-project
162//!
163//! For this type of project, we provide a way to replace data by modifying macro definitions through `CPPFLAGS`
164//!
165//! `main.cpp`:
166//!
167//! ```cpp
168//! #include <iostream>
169//! #ifndef N
170//! #define N 42
171//! #endif
172//!
173//! template <int n>
174//! void print()
175//! {
176//! std::cout << n << std::endl;
177//! }
178//!
179//! int main()
180//! {
181//! print<N>();
182//! return 0;
183//! }
184//! ```
185//!
186//! `Makefile`:
187//!
188//! ```Makefile
189//! CXX=g++
190//!
191//! CXXFLAGS=-std=c++11
192//!
193//! main: src/main.cpp
194//! $(CXX) $(CXXFLAGS) $(CPPFLAGS) -o $@ $^
195//! ```
196//!
197//! run script:
198//!
199//! ```shell
200//! parabuild \
201//! tests/example_makefile_project \
202//! main \
203//! --data '[{"N": 10}, {"N": 20}]' \
204//! --makefile
205//! ```
206//!
207//! Or specify `.enable_cppflags(true)` when using the rust library.
208//!
209//! # Features
210//!
211//! - Use handlebars template language to generate source file.
212//! - Ignore `.gitignore` files in the project, which may speed up the copying process.
213//! - Support multi-threading compilation/executing, these two parts can share threads, meaning they can be executed immediately after compilation, or they can be separated. For example, four threads can be used for compilation and one thread for execution. This is suitable for scenarios where only one executable file should be active in the system, such as when testing GPU performance. In this case, multiple CPU threads compile in the background while one CPU thread is responsible for execution.
214//! - [TODO] Support multiple template files.
215//!
216//! # Notes
217//!
218//! Due to the fact that system time is not monotonous , when the program executes quickly, there may be older timestamps in subsequent file modifications, which may cause `make` to not be able to track program modifications correctly. Please be aware that when writing compilation scripts, try to forcefully ignore timestamp compilation.
219//!
220//! [SystemTime](https://doc.rust-lang.org/std/time/struct.SystemTime.html):
221//!
222//! > A measurement of the system clock, useful for talking to external entities like the file system or other processes.
223//! >
224//! > Distinct from the Instant type, this time measurement is not monotonic. This means that you can save a file to the file system, then save another file to the file system, and the second file has a SystemTime measurement earlier than the first. In other words, an operation that happens after another operation in real time may have an earlier SystemTime!
225
226mod cuda_utils;
227mod filesystem_utils;
228mod handlebars_helper;
229mod parabuilder;
230pub use cuda_utils::get_cuda_mig_device_uuids;
231pub use parabuilder::{
232 CompliationErrorHandlingMethod, Parabuilder, RunMethod, IGNORE_ON_ERROR_DEFAULT_RUN_FUNC,
233 PANIC_ON_ERROR_DEFAULT_RUN_FUNC,
234};
235
236#[cfg(test)]
237pub mod test_constants {
238 pub const EXAMPLE_CMAKE_PROJECT_PATH: &str = "tests/example_cmake_project";
239 pub const EXAMPLE_MAKEFILE_PROJECT_PATH: &str = "tests/example_makefile_project";
240}