makefilegnu-make

how to create macros in makefile to insert the symbols not available in one variable


In a makefile, I want to have a macro that inserts some symbols/source code from one variable into another one if those symbols/source code do not exist in the destination variable:

the way I want to call it is something like this:

$( call insert_non_existing_symbols, source1.o $(OBJS_SYMBOLS_1) $(OBJS_SYMBOLS_2) another_source.o, $(DESINATION_VARIABLE), $(DESINATION_VARIABLE) )

I'm currently defining my macro as follows:

################################################################################
# Macro to insert symbols in one variable not existing in another variable.
# From the symbols in $(1), check that the symbol is not available in $(2), and 
#inserts in $(3) the symbols not available 
insert_non_existing_symbols = \
   for i in $(1); do                               \
      exist = 0;                                   \
      $(call member,$i,$(2),$(ret_val))            \
      ifeq $ret_val,0                              \
         $(3) += $i;                               \
      endif                                        \
done

################################################################################
#  Macro to determine if a symbol $(1) exisit in another variable/symbol $(2)
#  returns $(3) = 1 if it does.

member = \
   $(3) = 0;                                          \
   for i in $(2); do                                  \
      ifeq $(1),$i                                    \
         $(3)=1;                                      \
      endif                                           \
   done

I have the following symbol/variable OBJS_COLL := obj1.o obj2.o, and DESINATION_VARIABLE := obj3.o obj4.o so that after calling $( call insert_non_existing_symbols, $( OBJS_COLL), $(DESINATION_VARIABLE), $(DESINATION_VARIABLE) ) DESINATION_VARIABLE will be DESINATION_VARIABLE := obj1.o obj2.o obj3.o obj4.o since the $(call insert_non_existing_symbols,..) will add to DESINATION_VARIABLE the symbols/source code not exiting previously on it and that exist on OBJS_COLL . You can see that at the beginning DESINATION_VARIABLE was DESINATION_VARIABLE := obj3.o obj4.o

--- Edit

Sorry for the confusion here is a new edit to my question. This is part of what I want to achieve. I think this complete example is much better explanation:

VARIABLE_2 := source1.cpp source2.cpp

DEST_VAR_1 := source3.cpp

DEST_VAR_2 := source4.cpp

SOURCE_VAR := source

SOURCE_VAR_2 := no_source

ifeq ("$(filter source,$(SOURCE_VAR))","source")
DEST_VAR_1 := $(call insert_non_existing_symbols, $(VARIABLE_2),$(DEST_VAR_1))
endif


ifeq ("$(filter source,$(SOURCE_VAR_2))","source")
DEST_VAR_2 := $(call insert_non_existing_symbols, $(VARIABLE_2),$(DEST_VAR_2))
endif


my_exe_source : $(DEST_VAR_1)
    $(CXX) -o $@ $(filter %.o,$^)

my_exe_no_source : $(DEST_VAR_2)
    $(CXX) -o $@ $(filter %.o,$^)

$(call insert_non_existing_symbols is defined as @Renaud Pacalet shows in his answer below.

Edit 2

The following lines illustrate two makefile I'm using for my case:

Makefile

.PHONY : all
all: my_exe_source my_exe_no_source

include terminal.mk
MAKEFILES += terminal.mk

VARIABLE_2 := source1.cpp source2.cpp

DEST_VAR_1b := $(DEST_VAR_1)

DEST_VAR_2b := $(DEST_VAR_2)

SOURCE_VAR := source.cpp

SOURCE_VAR_2 := no_source.cpp

insert_non_existing_symbols = \
   $1 $(filter-out $1,$2)

ret_val := $(filter source.cpp,$(SOURCE_VAR))
ifeq ($(ret_val),source.cpp)
DEST_VAR_1 := $(call insert_non_existing_symbols, $(VARIABLE_2),$(DEST_VAR_1))
endif

ret_val := $(filter source.cpp,$(SOURCE_VAR_2))
ifeq ($(ret_val),source.cpp)
DEST_VAR_2 := $(call insert_non_existing_symbols, $(VARIABLE_2),$(DEST_VAR_2))
endif

my_exe_source : $(DEST_VAR_1b)
        @echo $@ $(filter %.cpp,$^)
        @echo $(DEST_VAR_1b)

my_exe_no_source : $(DEST_VAR_2b)
        @echo $@ $(filter %.cpp,$^)
        @echo $(DEST_VAR_2b)

terminal.mk

DEST_VAR_1 := source3.cpp
DEST_VAR_2 := source4.cpp

while running make I got:

$ make
my_exe_source source3.cpp
source3.cpp
my_exe_no_source source4.cpp
source4.cpp

This is due to the fact that I have

DEST_VAR_1b := $(DEST_VAR_1)
DEST_VAR_2b := $(DEST_VAR_2)

and that I'm using := which means immediate translation of $(DEST_VAR_1) and $(DEST_VAR_2) and it is not anymore translated. But if I use =:

DEST_VAR_1b = $(DEST_VAR_1)
DEST_VAR_2b = $(DEST_VAR_2)

it gets recursive translation so it will get reinterpreted to the latest value when I use it. Which gives me:

$ make
my_exe_source source1.cpp source2.cpp source3.cpp
source1.cpp source2.cpp source3.cpp
my_exe_no_source source4.cpp
source4.cpp

Explanation is here Difference between :=, and = in makefile

Any ideas or thoughts on how to do this is welcome and appreciated.

Thanks


Solution

  • You are mixing shell (for i in ...; do ... done) and make (call, ifeq) constructs. This cannot work. Make and shell are 2 different languages. Make variables and shell variables, for instance, are completely different things, while you try to use some variables interchangeably as shell ($ret_val) or make ($(ret_val)) variables.

    Note: in a Makefile the recipes of rules are frequently a mixture of shell and make syntax but it is a special case. Make first expands the recipes, the result shall be correct shell syntax, and then it passes this to the shell. In other parts of a Makefile mixing the 2 languages is simply not working.

    But you don't need a complex macro for this. GNU make functions are powerful enough:

    DESINATION_VARIABLE := $(OBJS_COLL) $(filter-out $(OBJS_COLL),$(DESINATION_VARIABLE))
    

    $(filter-out pattern…,text) Returns all whitespace-separated words in text that do not match any of the pattern words, removing the words that do match one or more

    This simply concatenates the value of OBJS_COLL (make variable) and all words in the value of DESINATION_VARIABLE (make variable) that are not already in the value of OBJS_COLL. The result is assigned to make variable DESINATION_VARIABLE. If there are duplicates in OBJS_COLL or DESINATION_VARIABLE they are kept. The order is preserved.

    If there are no duplicates in OBJS_COLL and DESINATION_VARIABLE, or if removing them is OK, and if you do not care about the final order, you can also use the sort make function:

    $(sort list) Sorts the words of list in lexical order, removing duplicate words. The output is a list of words separated by single spaces. So:

    DESINATION_VARIABLE := $(sort $(OBJS_COLL) $(DESINATION_VARIABLE))
    

    Finally, if you really want to define a macro:

    insert_non_existing_symbols = $1 $(filter-out $1,$2)
    

    Or:

    insert_non_existing_symbols = $(sort $1 $2)
    

    And then:

    DESINATION_VARIABLE := $(call insert_non_existing_symbols,$(OBJS_COLL),$(DESINATION_VARIABLE))