在一个简单的嵌入式项目中,我有两个文件main.rs
和module.rs
。为了构建项目,我使用了类似这样的代码:
all: main.o
$(CC) main.o $(LDFLAGS)
%.o: %.rs
$(RUSTC) $(RUSTFLAGS) -o ${@} ${<}
如果只有module.rs
被改变,make all
不会重新编译我的Rust代码。我该如何解决这个问题?
我发布了一个次优的自我回答作为第一步,但希望看到更好的方法。
使用Make的最佳方法是将每个依赖项编码到Makefile中。这就是让Make知道为了达到目标状态需要重建什么的力量。
要在C项目中执行此操作,您通常会使用GCC命令行选项-M
。这就把编译器引入了其中,因为它是解析C源代码和理解代码之间依赖关系的最佳工具文件。
对于Rust编译器rustc实际上有一个等效的开关:--emit=dep-info
。当您在源文件上运行此命令时,它将输出一个扩展名为.d
的文件,其中包含一个几乎与makefile兼容的依赖项列表。如果您有一个main.rs
引用模块foo.rs
,它将输出如下内容:
main.d: main.rs foo.rs
稍微调整一下,你就可以很好地发挥它。然后,您可以将其包含在Makefile中:
main.o:
rustc -o $@ $<
main.d: main.rs
rustc --emit=dep-info $<
# Add the object file as a rule
gsed 's/:/ $(@:.d=.o):/' -i $@
-include main.d
在这里,我在几个部分中指定了main
,但我相信您可以轻松地将它们修改为模式规则。
实用的解决方案是只使用Cargo, Rust构建工具和包管理器。让它处理依赖(本地模块和其他crate)。
libbar.dylib: target/debug/libbar.dylib
cp $< $@
.PHONY: target/debug/libbar.dylib
target/debug/libbar.dylib:
cargo build --verbose
这里,我将规则标记为PHONY
,表示"始终运行此规则"。我已经添加了--verbose
,让Cargo打印出它正在做什么,这样您就可以在重建时验证。
我建议放弃cp
步骤,如果可以,而只是使用嵌套路径,但如果其他东西依赖于当前位置,则可能需要复制
样式
%.o: %.rs
在构建C项目时很常见,但这并不是编写目标的唯一方式。特定于上面的设置,这将修复以下情况:
main.o: main.rs module.rs
$(RUSTC) $(RUSTFLAGS) -o main.o main.rs
与原始代码的一个值得注意的区别是,输入的名称对命令来说并不是真正重要的。我们可以归纳如下:
main.o: $(wildcard *.rs)
$(RUSTC) $(RUSTFLAGS) -o ${@} ${@:.o=.rs}
这是一个开始,但它仍然有一些我无法摆脱的缺点:
-
main.o:
部分是硬编码。如果有多个顶层模块需要编译,就会出现代码重复 - 由于通配符,所有Rust文件将被考虑用于所有顶级模块。换句话说,更改任何Rust文件都需要完全重新编译。