0
votes

I'm trying to setup a Makefile to compile my Rust project. To speed things up I don't want to recompile the whole project all at once. Rust allows you to create libraries that can then be linked into the main executable. Given two files src/iomrascalai.rs (the main program) and src/board/mod.rs (the library) to compile it by hand it would work like this:

$ rustc --out-dir lib src/board/mod.rs
$ ls lib/
libboard-d085aa56-0.0.rlib
$ rustc --out-dir bin -L lib src/iomrascalai.rs
$ ls bin/
iomrascalai

What I'm trying to do in the Makefile is to define dynamic rules to compile the libraries. I'm trying to do it like that as the mapping from source file (src/board/mod.rs) to library name (lib/libboard-d085aa56-0.0.rlib) is non-trivial. However, I can't seem to make it work:

MAIN   = src/iomrascalai.rs
CRATES = src/board/mod.rs

LIBS   = $(foreach crate, $(CRATES), $(call TO_LIB, $(crate)))

all: exe

exe: $(MAIN) $(LIBS)
    rustc --out-dir bin -L lib $(MAIN)

TO_LIB = $(addprefix lib/, $(shell rustc --crate-file-name $(1)))

define COMPILE_CRATE
$(call TO_LIB, $(1)): $(1)
    rustc --out-dir lib $(1)
endef

$(foreach crate, $(CRATES), $(eval $(call COMPILE_CRATE, $(crate))))

Running make results in the following error:

$ make
rustc --out-dir bin -L lib src/iomrascalai.rs
src/iomrascalai.rs:22:1: 22:20 error: can't find crate for `board`
src/iomrascalai.rs:22 extern crate board;
                  ^~~~~~~~~~~~~~~~~~~
error: aborting due to previous error
make: *** [exe] Error 101

So the libraries aren't compiled which means that the main program can't be compiled. When I try to run make to compile the lib target then it works:

$ rustc --crate-file-name src/board/mod.rs
libboard-d085aa56-0.0.rlib
$ make lib/libboard-d085aa56-0.0.rlib
rustc --out-dir lib  src/board/mod.rs

So for some reason the exe doesn't honor the prerequisites even though the rules are defined ...

1
If I add libs: $(LIBS) and call make libs I get make: libs is up to date.. So for some reason even though the libraries aren't compiled make things that there's nothing to do. - ujh
Library crate roots should by convention be named lib.rs. mod.rs is for non-root modules. - Chris Morgan
Thanks Chris. My idea here is to simulate the C approach of compiling each file into an object file and then just recompiling the individual files as needed. That's why I used mod.rs as I'm not really building stand alone libraries. I wonder however if this the right approach to speed up the compilation process. - ujh
Such a model is certainly not idiomatic in Rust. Also, while at present it will improve performance in some situations, it will probably not have a net positive effect on performance and will, in the long run, certainly be counterproductive. - Chris Morgan
I wish to clarify my earlier statement a little. I do not mean to say that a completely monolithic approach is the right way, that an entire codebase should be one crate; often it makes sense to split it up into a few, especially if there are completely distinct portions or if some parts can be used separately in other places (where you may end up with a DAG rather than a tree as your crate dependency structure). But don’t go overboard with dozens of tiny crates. - Chris Morgan

1 Answers

2
votes

Recursively expanded make variables (those which are assigned with =) are expanded whenever the variable is substituted. That means, other variables you use need to be defined before. In your case, you need to move up the definition of TO_LIB a few lines (at least above the exe rule since the expansion of LIBS need to have TO_LIB available there)