mirror of
https://github.com/slint-ui/slint.git
synced 2025-07-17 18:15:27 +00:00

Some checks are pending
autofix.ci / format_fix (push) Waiting to run
autofix.ci / lint_typecheck (push) Waiting to run
CI / python_test (windows-2022) (push) Blocked by required conditions
CI / mcu (pico2-st7789, thumbv8m.main-none-eabihf) (push) Blocked by required conditions
CI / files-changed (push) Waiting to run
CI / build_and_test (--exclude ffmpeg --exclude gstreamer-player, macos-14, stable) (push) Blocked by required conditions
CI / cpp_cmake (ubuntu-22.04, stable) (push) Blocked by required conditions
CI / build_and_test (--exclude ffmpeg --exclude gstreamer-player, windows-2022, 1.85) (push) Blocked by required conditions
CI / build_and_test (--exclude ffmpeg --exclude gstreamer-player, windows-2022, beta) (push) Blocked by required conditions
CI / build_and_test (ubuntu-22.04, nightly) (push) Blocked by required conditions
CI / node_test (macos-14) (push) Blocked by required conditions
CI / node_test (ubuntu-22.04) (push) Blocked by required conditions
CI / node_test (windows-2022) (push) Blocked by required conditions
CI / build_and_test (--exclude ffmpeg --exclude gstreamer-player, windows-2022, stable) (push) Blocked by required conditions
CI / build_and_test (ubuntu-22.04, 1.85) (push) Blocked by required conditions
CI / python_test (macos-14) (push) Blocked by required conditions
CI / python_test (ubuntu-22.04) (push) Blocked by required conditions
CI / cpp_test_driver (macos-13) (push) Blocked by required conditions
CI / cpp_test_driver (ubuntu-22.04) (push) Blocked by required conditions
CI / cpp_test_driver (windows-2022) (push) Blocked by required conditions
CI / cpp_cmake (macos-14, 1.85) (push) Blocked by required conditions
CI / cpp_cmake (windows-2022, nightly) (push) Blocked by required conditions
CI / cpp_package_test (push) Blocked by required conditions
CI / vsce_build_test (push) Blocked by required conditions
CI / mcu (pico-st7789, thumbv6m-none-eabi) (push) Blocked by required conditions
CI / mcu (stm32h735g, thumbv7em-none-eabihf) (push) Blocked by required conditions
CI / mcu-embassy (push) Blocked by required conditions
CI / ffi_32bit_build (push) Blocked by required conditions
CI / docs (push) Blocked by required conditions
CI / wasm (push) Blocked by required conditions
CI / wasm_demo (push) Blocked by required conditions
CI / tree-sitter (push) Blocked by required conditions
CI / updater_test (0.3.0) (push) Blocked by required conditions
CI / fmt_test (push) Blocked by required conditions
CI / esp-idf-quick (push) Blocked by required conditions
CI / android (push) Blocked by required conditions
CI / miri (push) Blocked by required conditions
CI / test-figma-inspector (push) Blocked by required conditions
560 lines
19 KiB
Rust
560 lines
19 KiB
Rust
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
|
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
|
|
|
|
/*!
|
|
This crate serves as a companion crate of the slint crate.
|
|
It is meant to allow you to compile the `.slint` files from your `build.rs` script.
|
|
|
|
The main entry point of this crate is the [`compile()`] function
|
|
|
|
## Example
|
|
|
|
In your Cargo.toml:
|
|
|
|
```toml
|
|
[package]
|
|
...
|
|
build = "build.rs"
|
|
|
|
[dependencies]
|
|
slint = "1.12"
|
|
...
|
|
|
|
[build-dependencies]
|
|
slint-build = "1.12"
|
|
```
|
|
|
|
In the `build.rs` file:
|
|
|
|
```ignore
|
|
fn main() {
|
|
slint_build::compile("ui/hello.slint").unwrap();
|
|
}
|
|
```
|
|
|
|
Then in your main file
|
|
|
|
```ignore
|
|
slint::include_modules!();
|
|
fn main() {
|
|
HelloWorld::new().run();
|
|
}
|
|
```
|
|
*/
|
|
#![doc(html_logo_url = "https://slint.dev/logo/slint-logo-square-light.svg")]
|
|
#![warn(missing_docs)]
|
|
|
|
#[cfg(not(feature = "default"))]
|
|
compile_error!(
|
|
"The feature `default` must be enabled to ensure \
|
|
forward compatibility with future version of this crate"
|
|
);
|
|
|
|
use std::collections::HashMap;
|
|
use std::env;
|
|
use std::io::{BufWriter, Write};
|
|
use std::path::Path;
|
|
|
|
use i_slint_compiler::diagnostics::BuildDiagnostics;
|
|
|
|
/// The structure for configuring aspects of the compilation of `.slint` markup files to Rust.
|
|
#[derive(Clone)]
|
|
pub struct CompilerConfiguration {
|
|
config: i_slint_compiler::CompilerConfiguration,
|
|
}
|
|
|
|
/// How should the slint compiler embed images and fonts
|
|
///
|
|
/// Parameter of [`CompilerConfiguration::embed_resources()`]
|
|
#[derive(Clone, PartialEq)]
|
|
pub enum EmbedResourcesKind {
|
|
/// Paths specified in .slint files are made absolute and the absolute
|
|
/// paths will be used at run-time to load the resources from the file system.
|
|
AsAbsolutePath,
|
|
/// The raw files in .slint files are embedded in the application binary.
|
|
EmbedFiles,
|
|
/// File names specified in .slint files will be loaded by the Slint compiler,
|
|
/// optimized for use with the software renderer and embedded in the application binary.
|
|
EmbedForSoftwareRenderer,
|
|
}
|
|
|
|
impl Default for CompilerConfiguration {
|
|
fn default() -> Self {
|
|
Self {
|
|
config: i_slint_compiler::CompilerConfiguration::new(
|
|
i_slint_compiler::generator::OutputFormat::Rust,
|
|
),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl CompilerConfiguration {
|
|
/// Creates a new default configuration.
|
|
pub fn new() -> Self {
|
|
Self::default()
|
|
}
|
|
|
|
/// Create a new configuration that includes sets the include paths used for looking up
|
|
/// `.slint` imports to the specified vector of paths.
|
|
#[must_use]
|
|
pub fn with_include_paths(self, include_paths: Vec<std::path::PathBuf>) -> Self {
|
|
let mut config = self.config;
|
|
config.include_paths = include_paths;
|
|
Self { config }
|
|
}
|
|
|
|
/// Create a new configuration that sets the library paths used for looking up
|
|
/// `@library` imports to the specified map of paths.
|
|
///
|
|
/// Each library path can either be a path to a `.slint` file or a directory.
|
|
/// If it's a file, the library is imported by its name prefixed by `@` (e.g.
|
|
/// `@example`). The specified file is the only entry-point for the library
|
|
/// and other files from the library won't be accessible from the outside.
|
|
/// If it's a directory, a specific file in that directory must be specified
|
|
/// when importing the library (e.g. `@example/widgets.slint`). This allows
|
|
/// exposing multiple entry-points for a single library.
|
|
///
|
|
/// Compile `ui/main.slint` and specify an "example" library path:
|
|
/// ```rust,no_run
|
|
/// let manifest_dir = std::path::PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR").unwrap());
|
|
/// let library_paths = std::collections::HashMap::from([(
|
|
/// "example".to_string(),
|
|
/// manifest_dir.join("third_party/example/ui/lib.slint"),
|
|
/// )]);
|
|
/// let config = slint_build::CompilerConfiguration::new().with_library_paths(library_paths);
|
|
/// slint_build::compile_with_config("ui/main.slint", config).unwrap();
|
|
/// ```
|
|
///
|
|
/// Import the "example" library in `ui/main.slint`:
|
|
/// ```slint,ignore
|
|
/// import { Example } from "@example";
|
|
/// ```
|
|
#[must_use]
|
|
pub fn with_library_paths(self, library_paths: HashMap<String, std::path::PathBuf>) -> Self {
|
|
let mut config = self.config;
|
|
config.library_paths = library_paths;
|
|
Self { config }
|
|
}
|
|
|
|
/// Create a new configuration that selects the style to be used for widgets.
|
|
#[must_use]
|
|
pub fn with_style(self, style: String) -> Self {
|
|
let mut config = self.config;
|
|
config.style = Some(style);
|
|
Self { config }
|
|
}
|
|
|
|
/// Selects how the resources such as images and font are processed.
|
|
///
|
|
/// See [`EmbedResourcesKind`]
|
|
#[must_use]
|
|
pub fn embed_resources(self, kind: EmbedResourcesKind) -> Self {
|
|
let mut config = self.config;
|
|
config.embed_resources = match kind {
|
|
EmbedResourcesKind::AsAbsolutePath => {
|
|
i_slint_compiler::EmbedResourcesKind::OnlyBuiltinResources
|
|
}
|
|
EmbedResourcesKind::EmbedFiles => {
|
|
i_slint_compiler::EmbedResourcesKind::EmbedAllResources
|
|
}
|
|
EmbedResourcesKind::EmbedForSoftwareRenderer => {
|
|
i_slint_compiler::EmbedResourcesKind::EmbedTextures
|
|
}
|
|
};
|
|
Self { config }
|
|
}
|
|
|
|
/// Sets the scale factor to be applied to all `px` to `phx` conversions
|
|
/// as constant value. This is only intended for MCU environments. Use
|
|
/// in combination with [`Self::embed_resources`] to pre-scale images and glyphs
|
|
/// accordingly.
|
|
#[must_use]
|
|
pub fn with_scale_factor(self, factor: f32) -> Self {
|
|
let mut config = self.config;
|
|
config.const_scale_factor = factor as f64;
|
|
Self { config }
|
|
}
|
|
|
|
/// Configures the compiler to bundle translations when compiling Slint code.
|
|
///
|
|
/// It expects the path to be the root directory of the translation files.
|
|
///
|
|
/// If given a relative path, it will be resolved relative to `$CARGO_MANIFEST_DIR`.
|
|
///
|
|
/// The translation files should be in the gettext `.po` format and follow this pattern:
|
|
/// `<path>/<lang>/LC_MESSAGES/<crate>.po`
|
|
#[must_use]
|
|
pub fn with_bundled_translations(
|
|
self,
|
|
path: impl Into<std::path::PathBuf>,
|
|
) -> CompilerConfiguration {
|
|
let mut config = self.config;
|
|
config.translation_path_bundle = Some(path.into());
|
|
Self { config }
|
|
}
|
|
|
|
/// Configures the compiler to emit additional debug info when compiling Slint code.
|
|
///
|
|
/// This is the equivalent to setting `SLINT_EMIT_DEBUG_INFO=1` and using the `slint!()` macro
|
|
/// and is primarily used by `i-slint-backend-testing`.
|
|
#[doc(hidden)]
|
|
#[must_use]
|
|
pub fn with_debug_info(self, enable: bool) -> Self {
|
|
let mut config = self.config;
|
|
config.debug_info = enable;
|
|
Self { config }
|
|
}
|
|
|
|
/// Configures the compiler to use Signed Distance Field (SDF) encoding for fonts.
|
|
///
|
|
/// This flag only takes effect when `embed_resources` is set to [`EmbedResourcesKind::EmbedForSoftwareRenderer`],
|
|
/// and requires the `sdf-fonts` cargo feature to be enabled.
|
|
///
|
|
/// [SDF](https://en.wikipedia.org/wiki/Signed_distance_function) reduces the binary size by
|
|
/// using an alternative representation for fonts, trading off some rendering quality
|
|
/// for a smaller binary footprint.
|
|
/// Rendering is slower and may result in slightly inferior visual output.
|
|
/// Use this on systems with limited flash memory.
|
|
#[cfg(feature = "sdf-fonts")]
|
|
#[must_use]
|
|
pub fn with_sdf_fonts(self, enable: bool) -> Self {
|
|
let mut config = self.config;
|
|
config.use_sdf_fonts = enable;
|
|
Self { config }
|
|
}
|
|
}
|
|
|
|
/// Error returned by the `compile` function
|
|
#[derive(derive_more::Error, derive_more::Display, Debug)]
|
|
#[non_exhaustive]
|
|
pub enum CompileError {
|
|
/// Cannot read environment variable CARGO_MANIFEST_DIR or OUT_DIR. The build script need to be run via cargo.
|
|
#[display("Cannot read environment variable CARGO_MANIFEST_DIR or OUT_DIR. The build script need to be run via cargo.")]
|
|
NotRunViaCargo,
|
|
/// Parse error. The error are printed in the stderr, and also are in the vector
|
|
#[display("{_0:?}")]
|
|
CompileError(#[error(not(source))] Vec<String>),
|
|
/// Cannot write the generated file
|
|
#[display("Cannot write the generated file: {_0}")]
|
|
SaveError(std::io::Error),
|
|
}
|
|
|
|
struct CodeFormatter<Sink> {
|
|
indentation: usize,
|
|
/// We are currently in a string
|
|
in_string: bool,
|
|
/// number of bytes after the last `'`, 0 if there was none
|
|
in_char: usize,
|
|
/// In string or char, and the previous character was `\\`
|
|
escaped: bool,
|
|
sink: Sink,
|
|
}
|
|
|
|
impl<Sink> CodeFormatter<Sink> {
|
|
pub fn new(sink: Sink) -> Self {
|
|
Self { indentation: 0, in_string: false, in_char: 0, escaped: false, sink }
|
|
}
|
|
}
|
|
|
|
impl<Sink: Write> Write for CodeFormatter<Sink> {
|
|
fn write(&mut self, mut s: &[u8]) -> std::io::Result<usize> {
|
|
let len = s.len();
|
|
while let Some(idx) = s.iter().position(|c| match c {
|
|
b'{' if !self.in_string && self.in_char == 0 => {
|
|
self.indentation += 1;
|
|
true
|
|
}
|
|
b'}' if !self.in_string && self.in_char == 0 => {
|
|
self.indentation -= 1;
|
|
true
|
|
}
|
|
b';' if !self.in_string && self.in_char == 0 => true,
|
|
b'"' if !self.in_string && self.in_char == 0 => {
|
|
self.in_string = true;
|
|
self.escaped = false;
|
|
false
|
|
}
|
|
b'"' if self.in_string && !self.escaped => {
|
|
self.in_string = false;
|
|
false
|
|
}
|
|
b'\'' if !self.in_string && self.in_char == 0 => {
|
|
self.in_char = 1;
|
|
self.escaped = false;
|
|
false
|
|
}
|
|
b'\'' if !self.in_string && self.in_char > 0 && !self.escaped => {
|
|
self.in_char = 0;
|
|
false
|
|
}
|
|
b' ' | b'>' if self.in_char > 2 && !self.escaped => {
|
|
// probably a lifetime
|
|
self.in_char = 0;
|
|
false
|
|
}
|
|
b'\\' if (self.in_string || self.in_char > 0) && !self.escaped => {
|
|
self.escaped = true;
|
|
// no need to increment in_char since \ isn't a single character
|
|
false
|
|
}
|
|
_ if self.in_char > 0 => {
|
|
self.in_char += 1;
|
|
self.escaped = false;
|
|
false
|
|
}
|
|
_ => {
|
|
self.escaped = false;
|
|
false
|
|
}
|
|
}) {
|
|
let idx = idx + 1;
|
|
self.sink.write_all(&s[..idx])?;
|
|
self.sink.write_all(b"\n")?;
|
|
for _ in 0..self.indentation {
|
|
self.sink.write_all(b" ")?;
|
|
}
|
|
s = &s[idx..];
|
|
}
|
|
self.sink.write_all(s)?;
|
|
Ok(len)
|
|
}
|
|
fn flush(&mut self) -> std::io::Result<()> {
|
|
self.sink.flush()
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn formatter_test() {
|
|
fn format_code(code: &str) -> String {
|
|
let mut res = Vec::new();
|
|
let mut formatter = CodeFormatter::new(&mut res);
|
|
formatter.write_all(code.as_bytes()).unwrap();
|
|
String::from_utf8(res).unwrap()
|
|
}
|
|
|
|
assert_eq!(
|
|
format_code("fn main() { if ';' == '}' { return \";\"; } else { panic!() } }"),
|
|
r#"fn main() {
|
|
if ';' == '}' {
|
|
return ";";
|
|
}
|
|
else {
|
|
panic!() }
|
|
}
|
|
"#
|
|
);
|
|
|
|
assert_eq!(
|
|
format_code(r#"fn xx<'lt>(foo: &'lt str) { println!("{}", '\u{f700}'); return Ok(()); }"#),
|
|
r#"fn xx<'lt>(foo: &'lt str) {
|
|
println!("{}", '\u{f700}');
|
|
return Ok(());
|
|
}
|
|
"#
|
|
);
|
|
|
|
assert_eq!(
|
|
format_code(r#"fn main() { ""; "'"; "\""; "{}"; "\\"; "\\\""; }"#),
|
|
r#"fn main() {
|
|
"";
|
|
"'";
|
|
"\"";
|
|
"{}";
|
|
"\\";
|
|
"\\\"";
|
|
}
|
|
"#
|
|
);
|
|
|
|
assert_eq!(
|
|
format_code(r#"fn main() { '"'; '\''; '{'; '}'; '\\'; }"#),
|
|
r#"fn main() {
|
|
'"';
|
|
'\'';
|
|
'{';
|
|
'}';
|
|
'\\';
|
|
}
|
|
"#
|
|
);
|
|
}
|
|
|
|
/// Compile the `.slint` file and generate rust code for it.
|
|
///
|
|
/// The generated code code will be created in the directory specified by
|
|
/// the `OUT` environment variable as it is expected for build script.
|
|
///
|
|
/// The following line need to be added within your crate in order to include
|
|
/// the generated code.
|
|
/// ```ignore
|
|
/// slint::include_modules!();
|
|
/// ```
|
|
///
|
|
/// The path is relative to the `CARGO_MANIFEST_DIR`.
|
|
///
|
|
/// In case of compilation error, the errors are shown in `stderr`, the error
|
|
/// are also returned in the [`CompileError`] enum. You must `unwrap` the returned
|
|
/// result to make sure that cargo make the compilation fail in case there were
|
|
/// errors when generating the code.
|
|
///
|
|
/// Please check out the documentation of the `slint` crate for more information
|
|
/// about how to use the generated code.
|
|
///
|
|
/// This function can only be called within a build script run by cargo.
|
|
pub fn compile(path: impl AsRef<std::path::Path>) -> Result<(), CompileError> {
|
|
compile_with_config(path, CompilerConfiguration::default())
|
|
}
|
|
|
|
/// Same as [`compile`], but allow to specify a configuration.
|
|
///
|
|
/// Compile `ui/hello.slint` and select the "material" style:
|
|
/// ```rust,no_run
|
|
/// let config =
|
|
/// slint_build::CompilerConfiguration::new()
|
|
/// .with_style("material".into());
|
|
/// slint_build::compile_with_config("ui/hello.slint", config).unwrap();
|
|
/// ```
|
|
pub fn compile_with_config(
|
|
relative_slint_file_path: impl AsRef<std::path::Path>,
|
|
config: CompilerConfiguration,
|
|
) -> Result<(), CompileError> {
|
|
let path = Path::new(&env::var_os("CARGO_MANIFEST_DIR").ok_or(CompileError::NotRunViaCargo)?)
|
|
.join(relative_slint_file_path.as_ref());
|
|
|
|
let absolute_rust_output_file_path =
|
|
Path::new(&env::var_os("OUT_DIR").ok_or(CompileError::NotRunViaCargo)?).join(
|
|
path.file_stem()
|
|
.map(Path::new)
|
|
.unwrap_or_else(|| Path::new("slint_out"))
|
|
.with_extension("rs"),
|
|
);
|
|
|
|
let paths_dependencies =
|
|
compile_with_output_path(path, absolute_rust_output_file_path.clone(), config)?;
|
|
|
|
for path_dependency in paths_dependencies {
|
|
println!("cargo:rerun-if-changed={}", path_dependency.display());
|
|
}
|
|
|
|
println!("cargo:rerun-if-env-changed=SLINT_STYLE");
|
|
println!("cargo:rerun-if-env-changed=SLINT_FONT_SIZES");
|
|
println!("cargo:rerun-if-env-changed=SLINT_SCALE_FACTOR");
|
|
println!("cargo:rerun-if-env-changed=SLINT_ASSET_SECTION");
|
|
println!("cargo:rerun-if-env-changed=SLINT_EMBED_RESOURCES");
|
|
println!("cargo:rerun-if-env-changed=SLINT_EMIT_DEBUG_INFO");
|
|
println!("cargo:rerun-if-env-changed=SLINT_LIVE_RELOAD");
|
|
|
|
println!(
|
|
"cargo:rustc-env=SLINT_INCLUDE_GENERATED={}",
|
|
absolute_rust_output_file_path.display()
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Similar to [`compile_with_config`], but meant to be used independently of cargo.
|
|
///
|
|
/// Will compile the input file and write the result in the given output file.
|
|
///
|
|
/// Both input_slint_file_path and output_rust_file_path should be absolute paths.
|
|
///
|
|
/// Doesn't print any cargo messages.
|
|
///
|
|
/// Returns a list of all input files that were used to generate the output file. (dependencies)
|
|
pub fn compile_with_output_path(
|
|
input_slint_file_path: impl AsRef<std::path::Path>,
|
|
output_rust_file_path: impl AsRef<std::path::Path>,
|
|
config: CompilerConfiguration,
|
|
) -> Result<Vec<std::path::PathBuf>, CompileError> {
|
|
let mut diag = BuildDiagnostics::default();
|
|
let syntax_node = i_slint_compiler::parser::parse_file(&input_slint_file_path, &mut diag);
|
|
|
|
if diag.has_errors() {
|
|
let vec = diag.to_string_vec();
|
|
diag.print();
|
|
return Err(CompileError::CompileError(vec));
|
|
}
|
|
|
|
let mut compiler_config = config.config;
|
|
compiler_config.translation_domain = std::env::var("CARGO_PKG_NAME").ok();
|
|
|
|
let syntax_node = syntax_node.expect("diags contained no compilation errors");
|
|
|
|
// 'spin_on' is ok here because the compiler in single threaded and does not block if there is no blocking future
|
|
let (doc, diag, loader) =
|
|
spin_on::spin_on(i_slint_compiler::compile_syntax_node(syntax_node, diag, compiler_config));
|
|
|
|
if diag.has_errors()
|
|
|| (!diag.is_empty() && std::env::var("SLINT_COMPILER_DENY_WARNINGS").is_ok())
|
|
{
|
|
let vec = diag.to_string_vec();
|
|
diag.print();
|
|
return Err(CompileError::CompileError(vec));
|
|
}
|
|
|
|
let output_file =
|
|
std::fs::File::create(&output_rust_file_path).map_err(CompileError::SaveError)?;
|
|
let mut code_formatter = CodeFormatter::new(BufWriter::new(output_file));
|
|
let generated = i_slint_compiler::generator::rust::generate(&doc, &loader.compiler_config)
|
|
.map_err(|e| CompileError::CompileError(vec![e.to_string()]))?;
|
|
|
|
let mut dependencies: Vec<std::path::PathBuf> = Vec::new();
|
|
|
|
for x in &diag.all_loaded_files {
|
|
if x.is_absolute() {
|
|
dependencies.push(x.clone());
|
|
}
|
|
}
|
|
|
|
// print warnings
|
|
diag.diagnostics_as_string().lines().for_each(|w| {
|
|
if !w.is_empty() {
|
|
println!("cargo:warning={}", w.strip_prefix("warning: ").unwrap_or(w))
|
|
}
|
|
});
|
|
|
|
write!(code_formatter, "{generated}").map_err(CompileError::SaveError)?;
|
|
dependencies.push(input_slint_file_path.as_ref().to_path_buf());
|
|
|
|
for resource in doc.embedded_file_resources.borrow().keys() {
|
|
if !resource.starts_with("builtin:") {
|
|
dependencies.push(Path::new(resource).to_path_buf());
|
|
}
|
|
}
|
|
|
|
Ok(dependencies)
|
|
}
|
|
|
|
/// This function is for use the application's build script, in order to print any device specific
|
|
/// build flags reported by the backend
|
|
pub fn print_rustc_flags() -> std::io::Result<()> {
|
|
if let Some(board_config_path) =
|
|
std::env::var_os("DEP_MCU_BOARD_SUPPORT_BOARD_CONFIG_PATH").map(std::path::PathBuf::from)
|
|
{
|
|
let config = std::fs::read_to_string(board_config_path.as_path())?;
|
|
let toml = config.parse::<toml_edit::DocumentMut>().expect("invalid board config toml");
|
|
|
|
for link_arg in
|
|
toml.get("link_args").and_then(toml_edit::Item::as_array).into_iter().flatten()
|
|
{
|
|
if let Some(option) = link_arg.as_str() {
|
|
println!("cargo:rustc-link-arg={option}");
|
|
}
|
|
}
|
|
|
|
for link_search_path in
|
|
toml.get("link_search_path").and_then(toml_edit::Item::as_array).into_iter().flatten()
|
|
{
|
|
if let Some(mut path) = link_search_path.as_str().map(std::path::PathBuf::from) {
|
|
if path.is_relative() {
|
|
path = board_config_path.parent().unwrap().join(path);
|
|
}
|
|
println!("cargo:rustc-link-search={}", path.to_string_lossy());
|
|
}
|
|
}
|
|
println!("cargo:rerun-if-env-changed=DEP_MCU_BOARD_SUPPORT_MCU_BOARD_CONFIG_PATH");
|
|
println!("cargo:rerun-if-changed={}", board_config_path.display());
|
|
}
|
|
|
|
Ok(())
|
|
}
|