Skip to content

Commit 6fdf7ea

Browse files
authored
Changes for esp-idf-sys cmake build (#14)
* cmd* macros: Add ability to specify argument collection with `@<expression>` * Add fs utilities Add dependency `remove_dir_all` Note: On windows `std::fs::remove_dir_all` doesn't really work (rust-lang/rust#29497). * Allow errors to be turned into cargo warnings * Add git utilities Publicly export the `which` crate Remove `log` dependency of `cmd*` macros. * Add initial cmake tools Add dependency `cmake` Add initial subset of cmake file API Add function to extract defined variables in cmake script * Add cmake-file-API cache object * Refactor `bindgen`, add `from_cmake` contructor * cmake: Add toolchains object * Add type alias `NativeCommandArgs` * Make `LinkArgsBuilder` methods usable * Construct `LinkArgsBuilder`, `CInclArgs` from cmake build info * Add `CfgArgs::try_from_json` * Don't detect bindgen linker from cache Fix kconfig json parse bug * Fix clippy warnings * Fix `git::Repository::apply` Add `git::Repository::apply_once` Add `git::Repository::is_applied` Publicly export `anyhow` (hidden from docs) because some macros need it. Fix `cmd_spawn` Add `cmd` variation that returns `Result<ExitStatus>` * Improve cmake-file-api error messages Check the cmake version for support of a specific file-api object kind.
1 parent 0ccb600 commit 6fdf7ea

File tree

16 files changed

+1577
-196
lines changed

16 files changed

+1577
-196
lines changed

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,5 @@ bindgen = "0.57"
2828
xmas-elf = "0.8"
2929
bitflags = "1.3"
3030
shlex = "1.0"
31+
remove_dir_all = "0.7"
32+
cmake = "0.1"

src/bindgen.rs

Lines changed: 146 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,102 @@
1+
use std::ffi::OsStr;
12
use std::path::{Path, PathBuf};
2-
use std::process::Command;
33
use std::{env, fs};
44

55
use anyhow::*;
66

7-
use crate::pio::project::SconsVariables;
87
use crate::utils::OsStrExt;
8+
use crate::{cargo, cli, cmake, cmd, cmd_output, pio};
99

1010
pub const VAR_BINDINGS_FILE: &str = "EMBUILD_GENERATED_BINDINGS_FILE";
1111

12-
#[cfg(windows)]
13-
const EXE_SUFFIX: &str = ".exe";
14-
15-
#[cfg(not(windows))]
16-
const EXE_SUFFIX: &str = "";
17-
18-
#[cfg(windows)]
19-
const FS_CASE_INSENSITIVE: bool = true;
20-
21-
#[cfg(not(windows))]
22-
const FS_CASE_INSENSITIVE: bool = false;
23-
2412
#[derive(Clone, Default, Debug)]
2513
pub struct Factory {
2614
pub clang_args: Vec<String>,
2715
pub linker: Option<PathBuf>,
2816
pub mcu: Option<String>,
17+
pub force_cpp: bool,
18+
pub sysroot: Option<PathBuf>,
2919
}
3020

3121
impl Factory {
32-
pub fn from_scons_vars(scons_vars: &SconsVariables) -> Result<Self> {
22+
pub fn from_scons_vars(scons_vars: &pio::project::SconsVariables) -> Result<Self> {
23+
let clang_args = cli::NativeCommandArgs::new(&scons_vars.incflags)
24+
.chain(cli::NativeCommandArgs::new(
25+
scons_vars
26+
.clangargs
27+
.as_deref()
28+
.unwrap_or_default(),
29+
))
30+
.collect();
31+
3332
Ok(Self {
34-
clang_args: Self::get_pio_clang_args(
35-
&scons_vars.incflags,
36-
scons_vars.clangargs.clone(),
37-
),
33+
clang_args,
3834
linker: Some(scons_vars.full_path(scons_vars.link.clone())?),
3935
mcu: Some(scons_vars.mcu.clone()),
36+
force_cpp: false,
37+
sysroot: None,
4038
})
4139
}
4240

43-
pub fn builder(&self) -> Result<bindgen::Builder> {
41+
pub fn from_cmake(compile_group: &cmake::codemodel::target::CompileGroup) -> Result<Self> {
42+
use crate::cmake::codemodel::Language;
43+
assert!(
44+
compile_group.language == Language::C || compile_group.language == Language::Cpp,
45+
"Generating bindings for languages other than C/C++ is not supported"
46+
);
47+
48+
let clang_args = compile_group
49+
.defines
50+
.iter()
51+
.map(|d| format!("-D{}", d.define))
52+
.chain(
53+
compile_group
54+
.includes
55+
.iter()
56+
.map(|i| format!("-I{}", &i.path)),
57+
)
58+
.collect();
59+
60+
Ok(Self {
61+
clang_args,
62+
linker: None,
63+
force_cpp: compile_group.language == Language::Cpp,
64+
mcu: None,
65+
sysroot: compile_group.sysroot.as_ref().map(|s| s.path.clone()),
66+
})
67+
}
68+
69+
/// Set the linker used to determine the sysroot to be used for generating bindings.
70+
pub fn with_linker(mut self, linker: impl Into<PathBuf>) -> Self {
71+
self.linker = Some(linker.into());
72+
self
73+
}
74+
75+
pub fn builder(self) -> Result<bindgen::Builder> {
4476
self.create_builder(false)
4577
}
4678

47-
pub fn cpp_builder(&self) -> Result<bindgen::Builder> {
79+
pub fn cpp_builder(self) -> Result<bindgen::Builder> {
4880
self.create_builder(true)
4981
}
5082

51-
fn create_builder(&self, cpp: bool) -> Result<bindgen::Builder> {
52-
let sysroot = self.get_sysroot()?;
83+
fn create_builder(self, cpp: bool) -> Result<bindgen::Builder> {
84+
let cpp = self.force_cpp || cpp;
85+
let sysroot = self
86+
.sysroot
87+
.clone()
88+
.map_or_else(|| try_get_sysroot(&self.linker), Ok)?;
89+
90+
let sysroot_args = [
91+
format!("--sysroot={}", sysroot.try_to_str()?),
92+
format!("-I{}", sysroot.join("include").try_to_str()?),
93+
];
94+
95+
let cpp_args = if cpp {
96+
get_cpp_includes(&sysroot)?
97+
} else {
98+
vec![]
99+
};
53100

54101
let builder = bindgen::Builder::default()
55102
.use_core()
@@ -58,130 +105,25 @@ impl Factory {
58105
.derive_default(true)
59106
//.ctypes_prefix(c_types)
60107
.clang_arg("-D__bindgen")
61-
.clang_arg(format!("--sysroot={}", sysroot.display()))
62-
.clang_arg(format!("-I{}", sysroot.join("include").try_to_str()?))
108+
.clang_args(sysroot_args)
63109
.clang_args(&["-x", if cpp { "c++" } else { "c" }])
64-
.clang_args(if cpp {
65-
Self::get_cpp_includes(sysroot)?
66-
} else {
67-
Vec::new()
68-
})
110+
.clang_args(cpp_args)
69111
.clang_args(&self.clang_args);
70112

71-
eprintln!(
113+
log::debug!(
72114
"Bindgen builder factory flags: {:?}",
73115
builder.command_line_flags()
74116
);
75117

76118
Ok(builder)
77119
}
78-
79-
fn get_sysroot(&self) -> Result<PathBuf> {
80-
let linker = if let Some(linker) = self.linker.as_ref() {
81-
linker
82-
.clone()
83-
.into_os_string()
84-
.into_string()
85-
.map_err(|_| anyhow!("Cannot convert the linker variable to String"))?
86-
} else if let Ok(linker) = env::var("RUSTC_LINKER") {
87-
linker
88-
} else {
89-
bail!("No explicit linker, and env var RUSTC_LINKER not defined either");
90-
};
91-
92-
let gcc = format!("gcc{}", EXE_SUFFIX);
93-
let gcc_suffix = format!("-{}", gcc);
94-
95-
let linker_canonicalized = if FS_CASE_INSENSITIVE {
96-
linker.to_lowercase()
97-
} else {
98-
linker.clone()
99-
};
100-
101-
let linker = if linker_canonicalized == gcc || linker_canonicalized.ends_with(&gcc_suffix) {
102-
// For whatever reason, --print-sysroot does not work with GCC
103-
// Change it to LD
104-
format!("{}ld{}", &linker[0..linker.len() - gcc.len()], EXE_SUFFIX)
105-
} else {
106-
linker
107-
};
108-
109-
let output = Command::new(linker).arg("--print-sysroot").output()?;
110-
111-
let path_str = String::from_utf8(output.stdout)?;
112-
113-
Ok(PathBuf::from(path_str.trim()))
114-
}
115-
116-
fn get_cpp_includes(sysroot: impl AsRef<Path>) -> Result<Vec<String>> {
117-
let sysroot = sysroot.as_ref();
118-
let cpp_includes_root = sysroot.join("include").join("c++");
119-
120-
let cpp_version = fs::read_dir(&cpp_includes_root)?
121-
.map(|dir_entry_r| dir_entry_r.map(|dir_entry| dir_entry.path()))
122-
.fold(None, |ao: Option<PathBuf>, sr: Result<PathBuf, _>| {
123-
if let Some(a) = ao.as_ref() {
124-
sr.ok()
125-
.map_or(ao.clone(), |s| if a >= &s { ao.clone() } else { Some(s) })
126-
} else {
127-
sr.ok()
128-
}
129-
});
130-
131-
if let Some(cpp_version) = cpp_version {
132-
let mut cpp_include_paths = vec![
133-
format!("-I{}", cpp_version.try_to_str()?),
134-
format!("-I{}", cpp_version.join("backward").try_to_str()?),
135-
];
136-
137-
if let Some(sysroot_last_segment) = fs::canonicalize(sysroot)?.file_name() {
138-
cpp_include_paths.push(format!(
139-
"-I{}",
140-
cpp_version.join(sysroot_last_segment).try_to_str()?
141-
));
142-
}
143-
144-
Ok(cpp_include_paths)
145-
} else {
146-
Ok(Vec::new())
147-
}
148-
}
149-
150-
fn get_pio_clang_args(
151-
incflags: impl AsRef<str>,
152-
extra_args: Option<impl AsRef<str>>,
153-
) -> Vec<String> {
154-
let mut result = incflags
155-
.as_ref()
156-
.split(' ')
157-
.map(str::to_string)
158-
.collect::<Vec<_>>();
159-
160-
if let Some(extra_args) = extra_args {
161-
result.append(
162-
&mut extra_args
163-
.as_ref()
164-
.split(' ')
165-
.map(str::to_string)
166-
.collect::<Vec<_>>(),
167-
);
168-
}
169-
170-
result
171-
}
172120
}
173121

174122
pub fn run(builder: bindgen::Builder) -> Result<()> {
175123
let output_file = PathBuf::from(env::var("OUT_DIR")?).join("bindings.rs");
176-
177124
run_for_file(builder, &output_file)?;
178125

179-
println!(
180-
"cargo:rustc-env={}={}",
181-
VAR_BINDINGS_FILE,
182-
output_file.display()
183-
);
184-
126+
cargo::set_rustc_env(VAR_BINDINGS_FILE, output_file.try_to_str()?);
185127
Ok(())
186128
}
187129

@@ -199,12 +141,77 @@ pub fn run_for_file(builder: bindgen::Builder, output_file: impl AsRef<Path>) ->
199141

200142
// Run rustfmt on the generated bindings separately, because custom toolchains often do not have rustfmt
201143
// Hence why we need to use the rustfmt from the stable buildchain (where the assumption is, it is already installed)
202-
Command::new("rustup")
203-
.arg("run")
204-
.arg("stable")
205-
.arg("rustfmt")
206-
.arg(output_file)
207-
.status()?;
208-
144+
cmd!("rustup", "run", "stable", "rustfmt", output_file)?;
209145
Ok(())
210146
}
147+
148+
fn try_get_sysroot(linker: &Option<impl AsRef<Path>>) -> Result<PathBuf> {
149+
let linker = if let Some(ref linker) = linker {
150+
linker.as_ref().to_owned()
151+
} else if let Some(linker) = env::var_os("RUSTC_LINKER") {
152+
PathBuf::from(linker)
153+
} else {
154+
bail!("Could not determine linker: No explicit linker and `RUSTC_LINKER` not set");
155+
};
156+
157+
let gcc_file_stem = linker
158+
.file_stem()
159+
.and_then(OsStr::to_str)
160+
.filter(|&s| s == "gcc" || s.ends_with("-gcc"));
161+
162+
// For whatever reason, --print-sysroot does not work with GCC
163+
// Change it to LD
164+
let linker = if let Some(stem) = gcc_file_stem {
165+
let mut ld_linker =
166+
linker.with_file_name(format!("{}{}", stem.strip_suffix("gcc").unwrap(), "ld"));
167+
if let Some(ext) = linker.extension() {
168+
ld_linker.set_extension(ext);
169+
}
170+
ld_linker
171+
} else {
172+
linker
173+
};
174+
175+
cmd_output!(linker, "--print-sysroot")
176+
.with_context(|| {
177+
anyhow!(
178+
"Could not determine sysroot from linker '{}'",
179+
linker.display()
180+
)
181+
})
182+
.map(PathBuf::from)
183+
}
184+
185+
fn get_cpp_includes(sysroot: impl AsRef<Path>) -> Result<Vec<String>> {
186+
let sysroot = sysroot.as_ref();
187+
let cpp_includes_root = sysroot.join("include").join("c++");
188+
189+
let cpp_version = fs::read_dir(&cpp_includes_root)?
190+
.map(|dir_entry_r| dir_entry_r.map(|dir_entry| dir_entry.path()))
191+
.fold(None, |ao: Option<PathBuf>, sr: Result<PathBuf, _>| {
192+
if let Some(a) = ao.as_ref() {
193+
sr.ok()
194+
.map_or(ao.clone(), |s| if a >= &s { ao.clone() } else { Some(s) })
195+
} else {
196+
sr.ok()
197+
}
198+
});
199+
200+
if let Some(cpp_version) = cpp_version {
201+
let mut cpp_include_paths = vec![
202+
format!("-I{}", cpp_version.try_to_str()?),
203+
format!("-I{}", cpp_version.join("backward").try_to_str()?),
204+
];
205+
206+
if let Some(sysroot_last_segment) = fs::canonicalize(sysroot)?.file_name() {
207+
cpp_include_paths.push(format!(
208+
"-I{}",
209+
cpp_version.join(sysroot_last_segment).try_to_str()?
210+
));
211+
}
212+
213+
Ok(cpp_include_paths)
214+
} else {
215+
Ok(Vec::new())
216+
}
217+
}

src/build.rs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -137,17 +137,22 @@ pub struct LinkArgsBuilder {
137137
}
138138

139139
impl LinkArgsBuilder {
140-
pub fn force_ldproxy(&mut self, value: bool) -> &mut Self {
140+
pub fn force_ldproxy(mut self, value: bool) -> Self {
141141
self.force_ldproxy = value;
142142
self
143143
}
144+
145+
pub fn linker(mut self, path: impl Into<PathBuf>) -> Self {
146+
self.linker = Some(path.into());
147+
self
148+
}
144149

145-
pub fn working_directory(&mut self, dir: impl AsRef<Path>) -> &mut Self {
150+
pub fn working_directory(mut self, dir: impl AsRef<Path>) -> Self {
146151
self.working_directory = Some(dir.as_ref().to_owned());
147152
self
148153
}
149154

150-
pub fn dedup_libs(&mut self, dedup: bool) -> &mut Self {
155+
pub fn dedup_libs(mut self, dedup: bool) -> Self {
151156
self.dedup_libs = dedup;
152157
self
153158
}
@@ -166,11 +171,11 @@ impl LinkArgsBuilder {
166171
.unwrap_or(false);
167172

168173
if self.force_ldproxy && !detected_ldproxy {
169-
print_warning(concat!(
170-
"The linker arguments force the usage of `ldproxy` but the linker used ",
171-
"by cargo is different. Please set the linker to `ldproxy` in your cargo config ",
172-
"or set `force_ldproxy` to `false`."
173-
));
174+
print_warning(
175+
"The linker arguments force the usage of `ldproxy` but the linker used \
176+
by cargo is different. Please set the linker to `ldproxy` in your cargo config \
177+
or set `force_ldproxy` to `false`."
178+
);
174179
}
175180

176181
if self.force_ldproxy || detected_ldproxy {

0 commit comments

Comments
 (0)