#ninja-build #ninja #build-system #build #syntax #generator

无需 std 构建 ninja-writer

用于编写 Ninja 构建文件的简单而直观的库

2 个不稳定版本

0.2.0 2023 年 12 月 10 日
0.1.2 2023 年 12 月 8 日
0.1.1 2023 年 12 月 8 日
0.1.0 2023 年 12 月 8 日

#359构建工具

MIT 许可证

56KB
885 代码行

ninja-writer

Build Badge Version Badge License Badge Issue Badge

用于编写以简洁性和易用性为重点的 ninja 构建文件的库。

为什么还要另一个?

我发现现有的库文档编写得不好,我想自己探索和学习 ninja 的语法。

使用方法

请参阅 文档

贡献

欢迎贡献。请提出问题和 PR :)


lib.rs:

ninja-writer

Build Badge Version Badge License Badge Issue Badge

用于编写以简洁性和易用性为重点的 ninja 构建文件的库。

然而,一个小缺点是 Rust 需要在作用域中有一个 trait 才能使用。因此,建议从 crate 中导入 *,这样所有 trait 都在作用域中。

use ninja_writer::*;

为什么还要另一个?

我发现现有的库文档编写得不好,我想自己探索和学习 ninja 的语法。

示例

Ninja 结构是编写 ninja 文件的主要入口点。它用于创建顶层声明,例如变量和规则。它实现了 Display,以便可以将其转换为字符串,写入文件等。

以下是一个构建简单 C 程序的 ninja 文件的完整示例。有关所有可用方法的详细信息,请参阅 Ninja

use ninja_writer::*;

// Create writer
let ninja = Ninja::new();
// Create a variable
ninja.variable("cflags", "-Wall -Wextra -Werror");
// Create the cc rule
let cc = ninja.rule("cc", "gcc -MD -MF $depfile $cflags -c $in -o $out")
    .description("Compiling $out")
    .depfile("$out.d")
    .deps_gcc();
// Create the ld rule
let ld = ninja.rule("ld", "gcc -o $out $in")
    .description("Linking $out");

// Create build edges using the rules
cc.build(["foo.o"]).with(["foo.c"]);
cc.build(["bar.o"]).with(["bar.c"])
    .variable("cflags", "-Wall -DDEBUG");

ld.build(["app"]).with(["foo.o", "bar.o"]);

ninja.defaults(["app"]);

let ninja_file: String = ninja.to_string();
assert_eq!(ninja_file, r###"
cflags = -Wall -Wextra -Werror

rule cc
  command = gcc -MD -MF $depfile $cflags -c $in -o $out
  description = Compiling $out
  depfile = $out.d
  deps = gcc

rule ld
  command = gcc -o $out $in
  description = Linking $out

build foo.o: cc foo.c
build bar.o: cc bar.c
  cflags = -Wall -DDEBUG
build app: ld foo.o bar.o

default app
"###);

std 功能

您可以通过禁用 std 功能使库与 no_std 兼容。我不知道您为什么想这样做,但这里只是为了以防万一。

线程安全

默认情况下,API 不是线程安全的。但是,您可以通过启用 thread-safe 功能来确保线程安全,该功能使用 ArcRwLock

以下是一个使用 2 个线程配置 200 个规则的示例。(这是一个高度理论性的例子。有关更实际的例子,请参阅 Rule,其中多个线程在同一规则上配置构建边)

use ninja_writer::*;
use std::sync::Arc;

let ninja = Arc::new(Ninja::new());
let ninja1 = Arc::clone(&ninja);
let ninja2 = Arc::clone(&ninja);
let t1 = std::thread::spawn(move || {
    for i in 0..100 {
        ninja1.rule("example", "...");
    }
});
let t2 = std::thread::spawn(move || {
    for i in 0..100 {
        ninja2.rule("example", "...");
    }
});
t1.join().unwrap();
t2.join().unwrap();

assert_eq!(ninja.stmts.inner().len(), 200);

除非您启用 thread-safe 功能,否则此示例无法编译。

转义

存在一个可以用来根据 ninja 的行为对字符串进行转义的 escape 函数。

use ninja_writer::escape;

assert_eq!(escape("foo"), "foo");
assert_eq!(escape("$foo"), "$$foo");
assert_eq!(escape("foo bar"), "foo bar");
assert_eq!(escape("foo: bar"), "foo: bar");

由于只需要对路径列表中的空格进行转义,因此可以使用 escape_path 来实现这一点

use ninja_writer::escape_path;
assert_eq!(escape_path("foo bar"), "foo$ bar");

同样,escape_build 可以用来转义空格和 :,用于指定输出。

use ninja_writer::escape_build;
assert_eq!(escape_build("foo: bar"), "foo$:$ bar");

参数列表

对于接受参数列表的函数(例如 build),由于 Rust 的类型系统限制,切片中元素的类型必须相同。

// This won't compile
let foo = "foo".to_string();
let args = [foo, "bar"];

您可以选择对每个元素调用 .as_ref() 将其转换为 &str,或者定义一个简单的宏来自动执行此操作,避免到处使用 .as_ref()

macro_rules! refs {
    ($($x:expr),* $(,)?) => {
         vec![$($x.as_ref()),*]
    }
}

如果您的自定义类型实现了 AsRef<str>,这将非常有用。

重复变量

由于 ninja 允许重复,因此不会检查重复。

use ninja_writer::Ninja;

let mut ninja = Ninja::new();
ninja.variable("foo", "bar");
ninja.variable("foo", "bar again");

assert_eq!(ninja.to_string(), r###"
foo = bar
foo = bar again
"###);

语句顺序

语句的顺序被保留。Ninja 的变量在规则之外会立即展开,因此语句的顺序很重要。

无运行时依赖

特性