2 个不稳定版本
0.2.0 | 2023 年 12 月 10 日 |
---|---|
0.1.2 | 2023 年 12 月 8 日 |
0.1.1 |
|
0.1.0 |
|
#359 在 构建工具
56KB
885 代码行
ninja-writer
用于编写以简洁性和易用性为重点的 ninja 构建文件的库。
为什么还要另一个?
我发现现有的库文档编写得不好,我想自己探索和学习 ninja 的语法。
使用方法
请参阅 文档。
贡献
欢迎贡献。请提出问题和 PR :)
lib.rs
:
ninja-writer
用于编写以简洁性和易用性为重点的 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 功能来确保线程安全,该功能使用 Arc 和 RwLock。
以下是一个使用 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 的变量在规则之外会立即展开,因此语句的顺序很重要。