1pub mod compile;
2pub mod compile_commands;
3pub mod include;
4pub mod link;
5pub mod tools;
6
7use std::{
8 fs::{self},
9 path::{Path, PathBuf},
10 process::Command,
11};
12
13use anyhow::Result;
14use compile_commands::CompileDatabase;
15
16use crate::{
17 cli::{args::BuildCommand, pretty},
18 config::{brick::BrickKind, overrides::OverrideDatabase, Config},
19};
20
21pub fn build(
22 config: &Config,
23 build_command: BuildCommand,
24 override_cmd: Option<String>,
25) -> Result<Option<PathBuf>> {
26 if let Some(override_cmd) = override_cmd {
27 pretty::msg("build", &override_cmd);
28
29 let mut cmd = match std::env::consts::OS {
30 "windows" => {
31 let mut cmd = Command::new("powershell.exe");
32 cmd.args(["-NoLogo", "-NonInteractive", "-NoProfile"]);
33 cmd.arg("/C");
34 cmd
35 }
36 _ => {
37 let mut cmd = Command::new("sh");
38 cmd.arg("-c");
39 cmd
40 }
41 };
42 cmd.current_dir(build_command.path);
43 cmd.arg(override_cmd);
44 cmd.status()?;
45
46 return Ok(None);
47 }
48
49 let mut compile_paths = vec![];
50
51 let src_path = Path::new(&build_command.path).join("src");
52
53 let override_path = Path::new(&build_command.path)
54 .join("build")
55 .join("overrides.json");
56 let override_file = match fs::read_to_string(override_path) {
57 Ok(v) => v,
58 Err(_) => {
59 pretty::warning(format!(
60 "no `build/overrides.json` found in {}. defaulting to no overrides.",
61 &build_command.path
62 ));
63 "{}".to_string()
64 }
65 };
66 let override_db: OverrideDatabase = serde_json::from_str(&override_file)?;
67
68 let mut compile_db = CompileDatabase::new();
69
70 for entry in walkdir::WalkDir::new(&src_path).follow_links(true) {
71 let Some((path, compile_cmd)) = compile::compile(
72 config,
73 entry,
74 &override_db,
75 build_command.force,
76 build_command.silent,
77 )?
78 else {
79 continue;
80 };
81 compile_paths.push(path);
82 compile_db.push(compile_cmd);
83 }
84
85 let mut ldflags = config.brick.ldflags.to_string();
86 ldflags += &match config.brick.platform() {
87 Some(platform) => match &platform.ldflags {
88 Some(v) => format!(" {}", v),
89 None => "".to_string(),
90 },
91 None => "".to_string(),
92 };
93
94 let build_result = match config.brick.kind {
95 BrickKind::Binary => link::binary(
96 &config.libs,
97 &compile_paths,
98 &Path::new(&build_command.path)
99 .join("build")
100 .join(&config.brick.name),
101 &override_db,
102 &ldflags,
103 config.brick.lang,
104 build_command.silent,
105 ),
106 BrickKind::Library => link::library(
107 &config.libs,
108 &compile_paths,
109 &Path::new(&build_command.path)
110 .join("build")
111 .join("lib")
112 .join(String::from("lib") + &config.brick.name + ".a"),
113 &override_db,
114 &ldflags,
115 config.brick.lang,
116 build_command.silent,
117 ),
118 };
119
120 if build_command.emit_compile_commands {
121 if !build_command.silent {
122 pretty::msg("emit", "build/compile_commands.json");
123 }
124 let comp_path = Path::new(&build_command.path)
125 .join("build")
126 .join("compile_commands.json");
127 let comp_file = fs::File::create(comp_path)?;
128 serde_json::to_writer(comp_file, &compile_db)?;
129 };
130
131 include::copy_headers(&src_path, &config.brick.name)?;
132
133 build_result
134}
135
136#[cfg(test)]
137mod tests {
138 use crate::build::compile::src_to_build_path;
139 use std::path::Path;
140
141 #[test]
142 fn check_src_to_build_path() {
143 assert_eq!(
144 src_to_build_path(Path::new("./src/main.c")),
145 Path::new("./build/main.o")
146 );
147 assert_eq!(
148 src_to_build_path(Path::new("./src/utils/crazy/main.c")),
149 Path::new("./build/utils/crazy/main.o")
150 );
151 assert_eq!(
152 src_to_build_path(Path::new("./main.c")),
153 Path::new("./main.o")
154 );
155 }
156}