meson_next/
lib.rs

1//! ## Build Example
2//!
3//! ```text
4//! .
5//! ├── build.rs
6//! ├── Cargo.toml
7//! ├── clib
8//! │   ├── meson.build
9//! │   ├── squid.h
10//! │   └── squid.c
11//! └── src
12//!     └── lib.rs
13//! ```
14//!
15//! build.rs:
16//!
17//! ```
18//! extern crate meson_next as meson;
19//! use std::env;
20//! use std::path::PathBuf;
21//! use std::collections::HashMap;
22//!
23//! fn main() {
24//!     let build_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("build");
25//!     let build_path = build_path.to_str().unwrap();
26//!
27//!     let mut options = HashMap::new();
28//!     options.insert("key", "value");
29//!
30//!     let config = meson::Config::new().options(options);
31//!
32//!     println!("cargo:rustc-link-lib=squid");
33//!     println!("cargo:rustc-link-search=native={}", build_path);
34//!     meson::build("clib", build_path, config);
35//! }
36//! ```
37//!
38//! Cargo.toml:
39//!
40//! ```toml
41//! # ...
42//!
43//! [build-dependencies]
44//! meson-next = "1"
45//! ```
46//!
47//! meson.build:
48//!
49//! ```text
50//! project('squid', 'c')
51//! shared_library('squid', 'squid.c')
52//! ```
53
54use std::path::PathBuf;
55use std::process::Command;
56use std::{env, vec};
57
58pub use config::Config;
59pub mod config;
60
61/// Runs meson and/or ninja to build a project.
62pub fn build(project_dir: &str, build_dir: &str, config: Config) {
63    run_meson(project_dir, build_dir, config);
64}
65
66fn run_meson(lib: &str, dir: &str, config: Config) {
67    if !is_configured(dir) {
68        let profile: &str = match env::var("PROFILE").unwrap().as_str() {
69            "release" => "release",
70            "debug" => "debug",
71            _ => unreachable!(),
72        };
73
74        let mut args: Vec<String> = vec!["setup", "--buildtype", profile, dir]
75            .iter()
76            .map(|a| a.to_string())
77            .collect();
78
79        // Apply build options
80        if let Some(options) = config.options {
81            let options: Vec<String> = options
82                .keys()
83                .into_iter()
84                .map(|key| format!("-D{}={}", key, options.get(key).unwrap()))
85                .collect();
86
87            for option in options {
88                args.insert(3, option);
89            }
90        }
91
92        // Apply native file
93        if let Some(native_file) = config.native_file {
94            args.insert(
95                3,
96                native_file.into_os_string().to_str().unwrap().to_string(),
97            );
98            args.insert(3, "--native-file".to_string())
99        }
100
101        // convert owned strings into string slices for run_command
102        let args: Vec<&str> = args.iter().map(|s| &**s).collect();
103
104        run_command(lib, "meson", &args)
105    }
106    run_command(dir, "ninja", &[]);
107}
108
109fn run_command(dir: &str, name: &str, args: &[&str]) {
110    let mut cmd = Command::new(name);
111    cmd.current_dir(dir);
112    if args.len() > 0 {
113        cmd.args(args);
114    }
115    let status = cmd.status().expect(&format!("cannot run command {name}"));
116    assert!(status.success());
117}
118
119fn is_configured(dir: &str) -> bool {
120    let path = PathBuf::from(dir).join("build.ninja");
121    return path.as_path().exists();
122}