Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generate dynamically Makefile rules

Tags:

makefile

I have a Makefile which I use to call different sub-Makefiles.

I have several rules:

  • all
  • clean
  • fclean
  • re

I can already use those rules, which will call every sub makefile with the same rule.

I have several project, and I would like to generate rules with that format:

$(PROJECT_NAME)-$(RULES)

With that, I would like to have each rule for each project:

project1-all

project1-clean

...

project2-all

project2-clean

...

This way, I would be able to call a specific rule, for a specific project, like project1-fclean.

I tried that:

RULES=    all clean fclean re

PROJECTS= project1 project2

define NEWLINE

endef

$(foreach _rule, $(RULES),                                              \
    $(foreach _proj, $(PROJECTS),                                           \
$(_proj)-$(_rule):                                           $(NEWLINE) \
            $(MAKE) $(ARGS) $(PROJECT_DIR)$(_proj) $(_rule) $(NEWLINE)  \
    )                                                                     \
)

But it doesn't seems to work. I have searched, but I haven't found advanced makefile techniques to achieve that. Plz help.

like image 792
Ludonope Avatar asked Apr 12 '17 21:04

Ludonope


2 Answers

The problem is that when you combine lines together with line continuations like that, it compresses out all the newlines and other extraneous whitespace (including those newlines you're trying to insert with $(NEWLINE)) resulting in a huge mess on a single line, rather than multiple lines with multiple patterns. To do this correctly, you need to write your rule as a macro with arguments and then call it:

define PROJ_RULE
$(1)-$(2):
        $(MAKE) $(ARGS) $(PROJECT_DIR)$(1) $(2)
endef

$(foreach _rule, $(RULES),
    $(foreach _proj, $(PROJECTS),
        $(eval $(call PROJ_RULE, $(_proj), $(_rule)))))

note that all this define and foreach stuff in GNU make specific -- other make flavors do not support it.

like image 142
Chris Dodd Avatar answered Nov 26 '22 04:11

Chris Dodd


Okay, so I finally managed to do it this way:

$(foreach _rule, $(RULES), $(addsuffix -$(_rule),$(PROJECTS))):
            $(eval _rule := $(lastword $(subst -, ,$@)))
            $(eval _proj := $(@:%-$(_rule)=%))
            @$(MAKE) $(ARGS) $(PROJECT_DIR)$(_proj) $(_rule)

I will decompose it for a better explanation:

$(foreach _rule, $(RULES), ...)):

We loop on every RULES and store it in _rule.

$(addsuffix -$(_rule),$(PROJECTS))

We add that rule as a prefix to each of our project. This part generate a rule with every "composed rules". With projet1 and project2 it should result in:

project1-all project2-all project1-clean project2-clean project1-fclean project2-fclean project1-re project2-re:

This way, for any of those rules name, it will be the same rule executed.

$(eval _rule := $(lastword $(subst -, ,$@)))

Here we take the target (if I call project2-clean, $@ will be project2-clean), we replace - by a space to obtain project2 clean and take the last work, wich will be clean here. We then evaluate it to store that into _rule.

$(eval _proj := $(@:%-$(_rule)=%))

We use the same technique to store the project name into _proj. We just use a pattern replacement, to remove the rule name and the dash.

@$(MAKE) $(ARGS) $(PROJECT_DIR)$(_proj) $(_rule)

Finally, we call our submakefile we the right path and right rule!

like image 39
Ludonope Avatar answered Nov 26 '22 06:11

Ludonope