Skip to content

Commit

Permalink
Create build_support crate
Browse files Browse the repository at this point in the history
  • Loading branch information
louwers committed Feb 8, 2025
1 parent c94cb23 commit 79dba89
Show file tree
Hide file tree
Showing 7 changed files with 174 additions and 57 deletions.
7 changes: 7 additions & 0 deletions platform/rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,15 @@ cmake = "0.1"
cxx-build = "1.0.138"
rustversion = "1.0.19"
walkdir = "2.5.0"
build_support = { path = "./build_support" }

[features]
metal = [] # default on Apple platforms, do not add to default features
opengl = []
vulkan = [] # default on other platforms

[workspace]
members = [
"build_support",
"."
]
61 changes: 14 additions & 47 deletions platform/rust/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,40 @@ use std::fs;
use std::path::{Path, PathBuf};
use walkdir::WalkDir;

use build_support::parse_deps;

/// Helper that returns a new cmake::Config with common settings.
/// It selects the renderer based on Cargo features: the user must enable exactly one of:
/// "metal", "opengl", or "vulkan". If none are explicitly enabled, on iOS/macOS the default is metal,
/// and on all other platforms the default is vulkan.
fn create_cmake_config(project_root: &Path) -> cmake::Config {
let mut cfg = cmake::Config::new(project_root);
cfg.generator("Ninja");
cfg.define("CMAKE_C_COMPILER_LAUNCHER", "ccache");
cfg.define("CMAKE_CXX_COMPILER_LAUNCHER", "ccache");
cfg.define("MLN_DRAWABLE_RENDERER", "ON");
cfg.define("MLN_WITH_OPENGL", "OFF");

// Check Cargo feature flags.
let (metal_enabled, opengl_enabled, vulkan_enabled) = {
let metal = env::var("CARGO_FEATURE_METAL").is_ok();
let opengl = env::var("CARGO_FEATURE_OPENGL").is_ok();
let vulkan = env::var("CARGO_FEATURE_VULKAN").is_ok();
if !metal && !opengl && !vulkan {
// No renderer feature explicitly enabled:
if cfg!(target_os = "ios") || cfg!(target_os = "macos") {
(true, false, false) // default to Metal on Apple platforms
(true, false, false)
} else {
(false, false, true) // default to Vulkan otherwise
(false, false, true)
}
} else {
(metal, opengl, vulkan)
}
};

// Ensure that only one of the renderer features is enabled.
let num_enabled = (metal_enabled as u8) + (opengl_enabled as u8) + (vulkan_enabled as u8);
if num_enabled > 1 {
panic!(
"Features 'metal', 'opengl', and 'vulkan' are mutually exclusive. \
Please enable only one renderer."
);
panic!("Features 'metal', 'opengl', and 'vulkan' are mutually exclusive. Please enable only one.");
}

// Configure renderer-specific options.
if opengl_enabled {
cfg.define("MLN_WITH_OPENGL", "ON");
cfg.define("MLN_WITH_METAL", "OFF");
Expand Down Expand Up @@ -74,41 +74,10 @@ fn main() {
let deps_contents = fs::read_to_string(&deps_file)
.unwrap_or_else(|_| panic!("Failed to read {}", deps_file.display()));

// Parse linker flags from the deps file.
let tokens: Vec<&str> = deps_contents.split_whitespace().collect();
let mut token_iter = tokens.iter().peekable();
let mut added_search_paths = HashSet::new();

while let Some(&token) = token_iter.next() {
if token == "-framework" {
if let Some(&framework) = token_iter.next() {
println!("cargo:rustc-link-lib=framework={}", framework);
} else {
panic!("Expected a framework name after '-framework'");
}
} else if token.starts_with("-l") {
let libname = &token[2..];
println!("cargo:rustc-link-lib={}", libname);
} else if token.ends_with(".a") {
let lib_path = Path::new(token);
let file_stem = lib_path.file_stem().expect("Library file has no stem");
let file_stem = file_stem.to_str().expect("Library file stem is not UTF-8");
let lib_name = file_stem.strip_prefix("lib").unwrap_or(file_stem);

// The .a libraries are located relative to the build_dir's "build" subdirectory.
let static_lib_base = deps_build_dir.join("build");
let search_dir = match lib_path.parent() {
Some(parent) if !parent.as_os_str().is_empty() => static_lib_base.join(parent),
_ => static_lib_base.clone(),
};
if added_search_paths.insert(search_dir.clone()) {
println!("cargo:rustc-link-search=native={}", search_dir.display());
}
println!("cargo:rustc-link-lib=static={}", lib_name);
} else {
// Pass any other token directly to the linker.
println!("cargo:rustc-link-arg={}", token);
}
// Parse the deps file into a list of Cargo instructions.
let instructions = parse_deps(&deps_contents, &deps_build_dir.join("build"));
for instr in instructions {
println!("{}", instr);
}

// ------------------------------------------------------------------------
Expand All @@ -117,8 +86,6 @@ fn main() {
let core_build_dir = create_cmake_config(&project_root)
.build_target("mbgl-core")
.build();

// Static libraries are placed in the "build" subdirectory.
let static_lib_base = core_build_dir.join("build");
println!(
"cargo:rustc-link-search=native={}",
Expand Down
7 changes: 7 additions & 0 deletions platform/rust/build_support/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "build_support"
version = "0.1.0"
edition = "2021"
publish = false

[dependencies]
76 changes: 76 additions & 0 deletions platform/rust/build_support/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
use std::collections::HashSet;
use std::path::{Path, PathBuf};

/// Parses the contents of mbgl-core-deps.txt and returns Cargo linker instructions.
///
/// # Arguments
///
/// * `deps_contents` - The contents of the dependency file as a string.
/// * `static_lib_base` - The base directory where the static libraries reside.
pub fn parse_deps(deps_contents: &str, static_lib_base: &Path) -> Vec<String> {
let mut instructions = Vec::new();
let mut added_search_paths = HashSet::new();
let tokens: Vec<&str> = deps_contents.split_whitespace().collect();
let mut token_iter = tokens.iter().peekable();

while let Some(&token) = token_iter.next() {
if token == "-framework" {
if let Some(&framework) = token_iter.next() {
instructions.push(format!("cargo:rustc-link-lib=framework={}", framework));
} else {
panic!("Expected a framework name after '-framework'");
}
} else if token.starts_with("-l") {
let libname = &token[2..];
instructions.push(format!("cargo:rustc-link-lib={}", libname));
} else if token.ends_with(".a") {
let lib_path = Path::new(token);
let file_stem = lib_path.file_stem().expect("Library file has no stem");
let file_stem = file_stem
.to_str()
.expect("Library file stem is not valid UTF-8");
let lib_name = file_stem.strip_prefix("lib").unwrap_or(file_stem);

let search_dir = match lib_path.parent() {
Some(parent) if !parent.as_os_str().is_empty() => static_lib_base.join(parent),
_ => static_lib_base.to_path_buf(),
};
if added_search_paths.insert(search_dir.clone()) {
instructions.push(format!(
"cargo:rustc-link-search=native={}",
search_dir.display()
));
}
instructions.push(format!("cargo:rustc-link-lib=static={}", lib_name));
} else {
instructions.push(format!("cargo:rustc-link-arg={}", token));
}
}
instructions
}

#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;

#[test]
fn test_parse_deps() {
// Simulate a deps file with:
// - "-lsqlite3" (link sqlite3)
// - "libmbgl-core.a" (a static library with no parent directory)
// - "-framework AppKit"
// - "some_arg" (an extra linker argument)
let deps_content = "-lsqlite3 libmbgl-core.a -framework AppKit some_arg";
let base_dir = PathBuf::from("/build_dir/build");
let instructions = parse_deps(deps_content, &base_dir);
let expected = vec![
"cargo:rustc-link-lib=sqlite3".to_string(),
format!("cargo:rustc-link-search=native={}", base_dir.display()),
"cargo:rustc-link-lib=static=mbgl-core".to_string(),
"cargo:rustc-link-lib=framework=AppKit".to_string(),
"cargo:rustc-link-arg=some_arg".to_string(),
];
assert_eq!(instructions, expected);
}
}
2 changes: 1 addition & 1 deletion platform/rust/src/bin/render.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use clap::Parser;
use maplibre_native::{MapRenderer, TileServerOptions};
use maplibre_native::MapRenderer;
use std::fs;

/// MapLibre Native render tool
Expand Down
68 changes: 68 additions & 0 deletions platform/rust/src/deps_parser.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use std::collections::HashSet;
use std::path::{Path, PathBuf};

/// Given the contents of mbgl-core-deps.txt and the base directory where static libraries are found,
/// parse the whitespace-separated tokens and return a vector of Cargo linker instructions.
pub fn parse_deps(deps_contents: &str, static_lib_base: &Path) -> Vec<String> {
let mut instructions = Vec::new();
let mut added_search_paths = HashSet::new();
let tokens: Vec<&str> = deps_contents.split_whitespace().collect();
let mut token_iter = tokens.iter().peekable();

while let Some(&token) = token_iter.next() {
if token == "-framework" {
if let Some(&framework) = token_iter.next() {
instructions.push(format!("cargo:rustc-link-lib=framework={}", framework));
} else {
panic!("Expected a framework name after '-framework'");
}
} else if token.starts_with("-l") {
let libname = &token[2..];
instructions.push(format!("cargo:rustc-link-lib={}", libname));
} else if token.ends_with(".a") {
let lib_path = Path::new(token);
let file_stem = lib_path.file_stem().expect("Library file has no stem");
let file_stem = file_stem.to_str().expect("Library file stem is not valid UTF-8");
let lib_name = file_stem.strip_prefix("lib").unwrap_or(file_stem);

let search_dir = match lib_path.parent() {
Some(parent) if !parent.as_os_str().is_empty() => static_lib_base.join(parent),
_ => static_lib_base.to_path_buf(),
};
if added_search_paths.insert(search_dir.clone()) {
instructions.push(format!("cargo:rustc-link-search=native={}", search_dir.display()));
}
instructions.push(format!("cargo:rustc-link-lib=static={}", lib_name));
} else {
instructions.push(format!("cargo:rustc-link-arg={}", token));
}
}
instructions
}

#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;

#[test]
fn test_parse_deps() {
// This sample mimics a deps file with:
// - a dynamic library flag (-lsqlite3)
// - a static library file (libmbgl-core.a)
// - a framework flag (-framework AppKit)
// - an extra linker argument ("some_arg")
let deps_content = "-lsqlite3 libmbgl-core.a -framework AppKit some_arg";
// Assume that the static libraries are found in "/build_dir/build"
let base_dir = PathBuf::from("/build_dir/build");
let instructions = parse_deps(deps_content, &base_dir);
let expected = vec![
"cargo:rustc-link-lib=sqlite3".to_string(),
format!("cargo:rustc-link-search=native={}", base_dir.display()),
"cargo:rustc-link-lib=static=mbgl-core".to_string(),
"cargo:rustc-link-lib=framework=AppKit".to_string(),
"cargo:rustc-link-arg=some_arg".to_string(),
];
assert_eq!(instructions, expected);
}
}
10 changes: 1 addition & 9 deletions platform/rust/src/map_renderer.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
use std::ffi::{c_char, CStr, CString};
use std::fmt;
use std::fmt::Debug;

use cxx::{CxxVector, UniquePtr};

use crate::ffi;
Expand Down Expand Up @@ -32,8 +28,4 @@ impl Default for MapRenderer {
}

#[cfg(test)]
mod tests {
use insta::assert_debug_snapshot;

use super::*;
}
mod tests {}

0 comments on commit 79dba89

Please sign in to comment.