Skip to content
Advertisement

Makefiles with source / object files in different directories

I have a project with the following directory structure:

         $tests/              $doc/
          |                     |-makefile 
   +------+-------+             |
   |      |       |           tests/
  test1/ test2/  test3/         |
   |      |       |       test1.rst, test2.rst, test3.rst
 test1.e test2.e test3.e

A file in the $doc/tests directory e.g test1.rst is created from $tests/test1/test1.e. I’m having problems with the makefile specifying that the source files are in $tests/*/*.e and the destination files are in $doc/tests/*.rst.

I’ve seen several similar questions but haven’t been able to workout the correct makefile syntax.

This makefile works for a single file example

SOURCES = $(wildcard $(tests)/*/*.e)
OBJECTS = $(addprefix $(doc)/tests/,$(notdir $(SOURCES:.e=.rst)))

# single file trial
SRC = $(tests)/test1/test1.e
OBJ = $(doc)/tests/test1.rst

$(OBJ): $(SRC)

debugvars:
    @echo SOURCES=$(SOURCES)
    @echo OBJECTS=$(OBJECTS)

# define how to create any RST file from a testcase
%.rst:
    $(scripts)/wr_rst.py --infile $<

# define how to create an RST file from a testcase
%.rst: %.e
    $(scripts)/wr_rst.py --infile $<

.e.rst:
    $(scripts)/wr_rst.py --infile $<

.SUFFIXES: .e .rst

I’m having trouble when using the full list of objects i.e.

all: $(OBJECTS)

$(OBJECTS): $(SOURCES)
    $(scripts)/wr_rst.py --infile $<

test1.rst is generated 3 times and test2,3.rst are ignored. The $(SOURCES) and $(OBJECTS) are correct. I suspect that $< does not iterate over the (SOURCES)

Here is some of the output from make -d

     No implicit rule found for `$tests/test3/test3.e'.                                   
 Finished prerequisites of target file `$tests/test3/test3.e'.                        
No need to remake target `$tests/test3/test3.e'.                                      
Considering target file `tests/test3.rst'.
 File `tests/test3.rst' does not exist.
  Pruning file `$tests/test1/test1.e'.
  Pruning file `$tests/test2/test2.e'.
  Pruning file `$tests/test3/test3.e'.
 Finished prerequisites of target file `tests/test3.rst'.
Must remake target `tests/test3.rst'.
$scripts/wr_rst.py --inile $tests/test1/test1.e
Putting child 0x00ee6420 (tests/test3.rst) PID 11720 on the chain.
Live child 0x00ee6420 (tests/test3.rst) PID 11720
Writing RST file $doc/tests/test1.rst
Reaping winning child 0x00ee6420 PID 11720
Removing child 0x00ee6420 PID 11720 from chain.
Successfully remade target file `tests/test3.rst'.

Advertisement

Answer

(This question looks very familiar– I’d almost swear that one essentially the same has been asked and answered.)

Let’s take this in stages. We could write the rules one at a time:

$(doc)/tests/test1.rst: $(tests)/test1/test1.e
    ...

but that’s tedious. It’s the kind of situation that cries out for a wildcard solution, such as a pattern rule, but one of Make’s serious shortcomings is its crude handling of wildcards. A pattern rule in which the wildcard is repeated:

$(doc)/tests/%.rst: $(tests)/%/%.e
    ...

is not allowed. But we could write the rules using eval:

define template
$(doc)/tests/$(1).rst: $(tests)/$(1)/$(1).e
    use some tool to build $$@ from $$<
endef

$(eval $(call template,test1))
$(eval $(call template,test2))
...

Then instead of writing all of those eval statements, we can delegate that job to foreach:

TESTS := test1 test2 ...

$(foreach TEST,$(TESTS),$(eval $(call template,$(TEST)))

Then instead of writing that list of tests, we can delegate that to wildcard, and use the same list to construct a list of target files:

TESTS := $(notdir $(wildcard $(tests)/*))
TARGETS := $(patsubst %,$(doc)/tests/%.rst,$(TESTS))

all: $(TARGETS)

Putting all of these together is straightforward, but this answer is getting long.

User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement