1

My project is a firmware that has a common logic part which is device independent and a device dependent part. Now I want one (phoney) target to build device A and one (phoney) target to build device B. Both targets shall ideally produce the same named binary. Obviously both targets depend on the common device independent code and their own individual sources. target_A: $(COMMON_OBJ) $(A_OBJ)

I tried to set target dependent variables but the fact that they are only evaluated in the recipe makes it impossible to create a dependency list based on a shared Variable between the targets.

This doesn't work:

target_A:
    DEV_SRC = foo_a.c bar_a.c
target_A: $(COMMON_OBJ) $(DEV_SRC:.c=.o)

The situation I have now is this: I need to write the same recipe for each target and that is what I like to avoid. Can this be combined into one single target/recipe?

target_A: $(COMMON_OBJ) $(A_OBJ)
    <build recipe>
target_B: $(COMMON_OBJ) $(B_OBJ)
    <build recipe>

For a bit more background: I like to make the Makefile simple enough so that fellow engineers can for example add another target, define it's sources and maybe add the target or variable to an existing list. All this I consider doable for every programmer but to write recipes and extend the Makefiles logic should be avoided to minimize the error potential especially for people with no experience with make.

Semo
  • 111
  • 1
  • 10
  • "Both targets shall ideally produce the same named binary". That way lies madness. You are breaking with two fundamental assumptions of `make`: a) That targets are files, the creation of which relies on some dependency files which, should they change, can trigger a rebuild of the target. b) That a file is created by exactly a single rule. If I were you, I'd move the products into device-specific sub-directories, so that you can define rules for `a/target` and `b/target`. – cmaster - reinstate monica May 07 '20 at 14:06
  • What in the build recipe takes a different path depending on whether you come from target_A or target_B? Can't you use the `$^` automatic variables? – Vroomfondel May 08 '20 at 12:29
  • @Vroomfondel The Recipe needs a list of target specific objects for its $(CC) command. These objects are a prerequisite to the specific target. I can crate a target dependent variable that resolves within the recipe but not within the prerequisite list. So the object files would never be build. One way around would be to just build ALL object files every time. But that is also not really what make intents to do. – Semo May 12 '20 at 08:28

1 Answers1

1

With the help of secondary expansion, I would make it as easy as:

$ cat Makefile
TARGETS := target_A target_B

target_A_SRC := target_A.c
target_B_SRC := target_B.c

COMMON_OBJ := common.o

$(foreach target,$(TARGETS),$(eval $(target)_OBJ := $(addsuffix .o, $(basename $($(target)_SRC)))))

.SECONDEXPANSION:

.PHONY: all
all: $(TARGETS)

.PHONY: clean
clean:
        -rm -f $(TARGETS) $(foreach target,$(TARGETS),$($(target)_OBJ)) $(COMMON_OBJ)

$(TARGETS): $(COMMON_OBJ) $$($$@_OBJ)
        $(LINK.o) $(OUTPUT_OPTION) $^

In order to add new target it would be enough to add new target into $(TARGETS) and define *_SRC variable accordingly. All the rest (build and clean) would be handled by already existing recipes.

Sample output:

$ make
cc    -c -o common.o common.c
cc    -c -o target_A.o target_A.c
cc   -o target_A common.o target_A.o
cc    -c -o target_B.o target_B.c
cc   -o target_B common.o target_B.o

$ make clean
rm -f target_A target_B target_A.o target_B.o common.o
raspy
  • 3,995
  • 1
  • 14
  • 18
  • Notice: This is not supported by gnu make 3.81 or below – Semo Jul 30 '20 at 15:57
  • That is correct. GNU make 3.81 was released in 2006 though, so unless you are running digital excavations, this should not be an issue. – raspy Jul 31 '20 at 20:06
  • @raspy Well, I just tried "brew install automake" on macOS Big Sur, the latest and greatest bleeding edge on Mac and most uptodate what is easily available for Mac users and when I do "make --version", what do I get: "GNU Make 3.81" – nyholku Jul 14 '21 at 04:34
  • Please note that `automake` and `make` are two different programs. – raspy Jul 14 '21 at 07:39
  • @raspy You are right, but "brew install make" makes no difference. – nyholku Jul 14 '21 at 12:07
  • I don't have a Mac, but [Brew homepage](https://formulae.brew.sh/formula/make) claims that it provides 4.3. Note that it mentions: _GNU "make" has been installed as "gmake". If you need to use it as "make", you can add a "gnubin" directory to your PATH from your bashrc like: PATH="$(brew --prefix)/opt/make/libexec/gnubin:$PATH"_ – raspy Jul 14 '21 at 12:28