From 79dba89fe178da46e5f62e0c1cc7b7b7c2194c29 Mon Sep 17 00:00:00 2001 From: Bart Louwers Date: Sat, 8 Feb 2025 14:04:46 +0100 Subject: [PATCH] Create build_support crate --- platform/rust/Cargo.toml | 7 +++ platform/rust/build.rs | 61 +++++---------------- platform/rust/build_support/Cargo.toml | 7 +++ platform/rust/build_support/src/lib.rs | 76 ++++++++++++++++++++++++++ platform/rust/src/bin/render.rs | 2 +- platform/rust/src/deps_parser.rs | 68 +++++++++++++++++++++++ platform/rust/src/map_renderer.rs | 10 +--- 7 files changed, 174 insertions(+), 57 deletions(-) create mode 100644 platform/rust/build_support/Cargo.toml create mode 100644 platform/rust/build_support/src/lib.rs create mode 100644 platform/rust/src/deps_parser.rs diff --git a/platform/rust/Cargo.toml b/platform/rust/Cargo.toml index 62153922e16..d068a073d90 100644 --- a/platform/rust/Cargo.toml +++ b/platform/rust/Cargo.toml @@ -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", + "." +] diff --git a/platform/rust/build.rs b/platform/rust/build.rs index 2e22da5df97..122fea6c633 100644 --- a/platform/rust/build.rs +++ b/platform/rust/build.rs @@ -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"); @@ -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); } // ------------------------------------------------------------------------ @@ -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={}", diff --git a/platform/rust/build_support/Cargo.toml b/platform/rust/build_support/Cargo.toml new file mode 100644 index 00000000000..4cc6dc352bc --- /dev/null +++ b/platform/rust/build_support/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "build_support" +version = "0.1.0" +edition = "2021" +publish = false + +[dependencies] diff --git a/platform/rust/build_support/src/lib.rs b/platform/rust/build_support/src/lib.rs new file mode 100644 index 00000000000..7e0932e6c78 --- /dev/null +++ b/platform/rust/build_support/src/lib.rs @@ -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 { + 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); + } +} diff --git a/platform/rust/src/bin/render.rs b/platform/rust/src/bin/render.rs index 4406a0d6031..19f78e1c25d 100644 --- a/platform/rust/src/bin/render.rs +++ b/platform/rust/src/bin/render.rs @@ -1,5 +1,5 @@ use clap::Parser; -use maplibre_native::{MapRenderer, TileServerOptions}; +use maplibre_native::MapRenderer; use std::fs; /// MapLibre Native render tool diff --git a/platform/rust/src/deps_parser.rs b/platform/rust/src/deps_parser.rs new file mode 100644 index 00000000000..80ecbab8faf --- /dev/null +++ b/platform/rust/src/deps_parser.rs @@ -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 { + 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); + } +} diff --git a/platform/rust/src/map_renderer.rs b/platform/rust/src/map_renderer.rs index 2622a2e1396..af9ddc8cc6a 100644 --- a/platform/rust/src/map_renderer.rs +++ b/platform/rust/src/map_renderer.rs @@ -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; @@ -32,8 +28,4 @@ impl Default for MapRenderer { } #[cfg(test)] -mod tests { - use insta::assert_debug_snapshot; - - use super::*; -} +mod tests {}