How to interpret the $# symbol in a makefile loop - makefile

In a makefile I have this piece of code
all clean dep depend print:
for dir in $(DIRS); do \
if $(MAKE) $(MAKE_FLAGS) -C $$dir $#; then \
true; \
else \
exit 1; \
fi; \
done
What is the meaning of $# in the line
if $(MAKE) $(MAKE_FLAGS) -C $$dir $#; then \
I know this is an Automatic Variable that matches the file name of the target of the rule. Here the target appears to be a command like cancel:
cancell:
rm -rf *.o

It is an Automatic variable that expands to the name of the target which caused the recipe to run. In your case, if you type make all, it expands to all. If you type make all clean, it will run the recipes twice -- the first time, $# will expand to all, and the second time it will expand to clean.
See documentation here

Related

Conditional Makefile

I'm working on a Makefile with changeable sources and compiler.
Basically, what I want it to do is display a message in green if the compilation worked out well and in red otherwise. Additionally I want to avoid displaying the usual error messages (and compilation messages) a Makefile produces. (As I tried to do with all the '#')
Here's what I have for now :
COMP = gcc
NAME = test
RM = rm -f
SRC = main.c
OBJS = $(SRC:.c=.o)
CFLAGS = -Werror
all: $(NAME)
$(NAME):
#$(COMP) -c $(SRC)
#$(COMP) -o $(NAME) $(OBJS)
ifeq ($?, 0)
#echo -e '\033[1;32;40mCompilation : OK\033[0m'
else
#echo -e '\033[1;31;40mCompilation : ERROR\033[0m'
endif
clean:
#$(RM) $(OBJS)
fclean: clean
#$(RM) $(NAME)
re: fclean all
.PHONY: all clean fclean re
All it does is display "Compilation : ERROR" when it compiles well, but I thought that if $? equals 0 that meant it worked out, so I can't find any explanation.
Would you know how to make it do what I'm looking for?
Thanks a lot.
EDIT : Wonderful help from many of you, I'm still looking into the recipe but I've found a way to simply display when it succeeded and when it failed.
$(NAME): $(OBJS)
#$(COMP) $(OBJS) -o $(NAME) && echo -e "\033[32mCompilation: OK\033[0m" || echo -e "\033[31mCompilation: ERROR\033[0m"
I'll keep on digging, thanks.
$? is a shell variable not a make variable, you are testing a make variable with that if statement and the recipe for your target only ever has the one echo line in it.
(See the output from make -qp to see what I mean.)
To do what you want you would need a shell test on $? however realize that make will exit on the first failing command so you will never see the failure echo output this way (unless you capture/hide the failure from make with an if or similar construct on the compilation command).
Something like this will work for capturing/hiding the exit status from the compilation from make but allow you to use it.
#if $(COMP) -c $(SRC); then \
echo -e '\033[1;32;40mCompilation : OK\033[0m'; \
else \
_ret=$$?; \
echo -e '\033[1;31;40mCompilation : ERROR\033[0m'; \
exit $$_ret;\
fi
The bit with _ret is to have make exit with the exit code of the compilation and not a static exit 1 or whatever.
What you want is something like this:
$(NAME):
#$(COMP) -c $(SRC)
#$(COMP) -o $(NAME) $(OBJS); \
if [ $$? == 0 ]; then \
echo -e '\033[1;32;40mCompilation : OK\033[0m'; \
else \
echo -e '\033[1;31;40mCompilation : ERROR\033[0m'; \
fi; \
true
Notice that I used the '\' to concatinate the bash command with the if statement, so they all appear as a single recipe. I also added a true statement to the end such that no matter what the if statement returns, the concatenated recipe will return true, and make will not fail. You could also do:
$(NAME):
#$(COMP) -o $(NAME) $(OBJS) || echo ....
where the echo would only print if the previous command failed.

Makefile: rule that match multiple patterns

I have this rule in my Makefile, that responds to flags I pass:
$(BUILD_DIR)/disable_%:
mkdir -p $(BUILD_DIR)
touch $(BUILD_DIR)/disable_$*
rm -f $(BUILD_DIR)/enable_$*
cd $(BUILD_DIR) && rm -f Makefile
$(BUILD_DIR)/enable_%:
mkdir -p $(BUILD_DIR)
touch $(BUILD_DIR)/enable_$*
rm -f $(BUILD_DIR)/disable_$*
cd $(BUILD_DIR) && rm -f Makefile
What this means is that when changing the flags by which I invoke the makefile, I can trigger some recompilations that could depend on these flags.
The code presented above is a bit redundant: you see that I remove a file, touch another and remove a Makefile in both cases. The only thing that changes is the name of the files that I touch/remove, and they are related.
For instance,
make clean
make enable_debug=yes enable_video=no # will compile from zero
make enable_debug=no enable_video=no # flag change detected -> recompile some submodules that depend on this flag
Provided that the only thing that changes between the two rules ( [en|dis]able ), what I would like is to only have 1 generic rule, something like that:
# match 2 parts in the rule
$(BUILD_DIR)/%ble_%:
mkdir -p $(BUILD_DIR)
touch $(BUILD_DIR)/(???)ble_$* # should be $#
rm -f $(BUILD_DIR)/(???)able_$* # should be disable if $# is enable and inverse
cd $(BUILD_DIR) && rm -f Makefile
Is this possible ?
PS: Sorry if I didn't get the title correctly, I couldn't figure how to explain it better.
$(BUILD_DIR)/enable_% $(BUILD_DIR)/disable_%:
mkdir -p $(BUILD_DIR)
rm -f $(BUILD_DIR)/*able_$*
touch $#
cd $(BUILD_DIR) && rm -f Makefile
Not literally what you wanted (multi-wildcards are forbidden in make), but does quite the same.

Setting target variable for the name of the makefile from a subdirectory

How can I have the variable for $(MAKEFILE) be defined during target execution?
Basically I have a few make files in subdirectories that are named for a specific platform "Makefile.aix" and just Makefile in all other directories. I would like to set a variable for $(MAKEFILE) that gets defined in each subdirectory. Code would look something like this.
MAKEFILE = Makefile
SUBDIR = ./sub ./sub2
ifneq ($(wildcard Makefile),)
MAKEFILE = Makefile
else
MAKEFILE = Makefile.$(PLATFORM)
endif
all:;
#for i in $(SUBDIR);\
do (\
echo Making $$i ...;\
cd $$i;\
make -f $(MAKEFILE)\
); done
Is there just one Makefile.$(PLATFORM) in each subdirectory, or are there several, for different platforms?
In the first case, you could do something like this:
SUBDIR = ./sub ./sub2
define script
cd $(1); \
$(MAKE) -f Makefile*
endef
all:
$(foreach dir, $(SUBDIR), $(call script,$(dir)))
(The empty line inside the define is significant. It can be omitted, if you add a semicolon at the end of the line $(MAKE) ..., leading to one long command line, containing the commands for all directories, which will then be executed in one chunk.)
An alternative script would be (just a matter of personal preference which you like better):
define script
$(MAKE) -C $(1) -f $(notdir $(wildcard $(1)/Makefile*))
endef
If there are several Makefile.$(PLATFORM) files in a directory it becomes more difficult. I'll have to think about that one some more.
UPDATE: In response to your comment, something like this should work:
define script
$(MAKE) -C $(1) -f $(notdir $(wildcard $(1)/Makefile $(1)/Makefile.$(PLATFORM)))
endef
Following your logic, I'd propose update do () section with:
do (\
echo Making $$i ...;\
cd $$i;\
if [ -f Makefile.$(PLATFORM) ] \
then\
make -f Makefile.$(PLATFORM) \
else\
make -f Makefile\
fi\
); done
This is actually not a make style, but I can't suggest anything better without specific of your project
You can do most of this, including the loop over directories, using GNU make's built-in functions. Put the following in a central place, say $(TOP_DIR)/mk/subdir.mk:
makefile-for-dir = \
$(if $(wildcard $(1)/Makefile),Makefile,Makefile.$(PLATFORM))
make-recursive = \
$(foreach _d,$(1),$(MAKE) -C $(_d) -f $(call makefile-for-dir,$(_d)) && ) :
In each makefile that start recursive makes, use
include $(TOP_DIR)/mk/subdir.mk
SUBDIRS = dir1 dir2 dir3
.PHONY: all
all:
+#$(call make-recursive,$(SUBDIRS))

Explaining makefile % as well as $< and $#

xpi_built := $(build_dir)/$(install_rdf) \
$(build_dir)/$(chrome_manifest) \
$(chrome_jar_file) \
$(default_prefs)
xpi_built_no_dir := $(subst $(build_dir)/,,$(xpi_built))
$(xpi_file): $(build_dir) $(xpi_built)
#echo "Creating XPI file."
cd $(build_dir); $(ZIP) ../$(xpi_file) $(xpi_built_no_dir)
#echo "Creating XPI file. Done!"
$(build_dir)/%: %
cp -f $< $#
$(build_dir):
#if [ ! -x $(build_dir) ]; \
then \
mkdir $(build_dir); \
fi
can anyone explain me this makefile part? particularly interested in
$(build_dir)/%: % as well as $< and $# directives
two labels $(build_dir) exists, I guess both are executed, but in which order?
$(build_dir)/%: %
cp -f $< $#
This is a static pattern rule which uses automatic variables in its command; $< expands to the leftmost prerequisite, $# expands to the target. If you try to make $(build_dir)/foo (whatever $(build_dir) is), Make will treat this rule as
$(build_dir)/foo: foo
cp -f foo $(build_dir)/foo
The next rule,
$(build_dir):
#if [ ! -x $(build_dir) ]; \
then \
mkdir $(build_dir); \
fi
is for $(build_dir) itself, and is unnecessarily complicated. It says "if $(build_dir) doesn't exist, then mkdir it", and it could be written this way:
$(build_dir):
mkdir $#
It looks as if your primary target is $(xpi_file):
$(xpi_file): $(build_dir) $(xpi_built)
So Make will first make $(build_dir) (if necessary), then the members of the list %(xpi_built), which includes a couple of things of the form $(build_dir)/%. Once those are done, it will execute the commands of this rule: it will cd into $(build_dir), zip some things up, and echo a couple of messages.
See Pattern Rules and Automatic Variables in the GNU make documentation. The first rule matches files inside $(build_dir), not $(build_dir) itself. $< expands to the list of prerequisites of the current rule, $# is the target for the current rule.

makefile internal calls to target

How can I distinguish in makefile, which targets and how(when) they are called internally? I have a makefile with number of targets which are actually variables.
UPD: here is an example
build_dir := $(bin_dir)/build
xpi_built := $(build_dir)/$(install_rdf) \
$(build_dir)/$(chrome_manifest) \
$(chrome_jar_file) \
$(default_prefs)/*
xpi_built_no_dir := $(subst $(build_dir)/,,$(xpi_built))
.PHONY: install
install: $(build_dir) $(xpi_built)
#echo "Installing in profile folder: $(profile_location)"
#cp -Rf $(build_dir)/* $(profile_location)
#echo "Installing in profile folder. Done!"
#echo
$(xpi_file): $(build_dir) $(xpi_built)
#echo "Creating XPI file."
#cd $(build_dir); $(ZIP) -r ../$(xpi_file) $(xpi_built_no_dir)
#echo "Creating XPI file. Done!"
#cp update.rdf $(bin_dir)/
#cp -u *.xhtml $(bin_dir)/
#cp -Rf $(default_prefs) $(build_dir)/; \
$(build_dir)/%: %
cp -f $< $#
$(build_dir):
#if [ ! -x $(build_dir) ]; \
then \
mkdir $(build_dir); \
fi
If you specify a target on the command line, as in make clean Make will attempt to build that target. If you don't (that is, if you just run make), Make will attempt to build the default target; the default target is the first target in the makefile (in your case install) unless you set it to something else with the .DEFAULT_GOAL variable.
When Make tries to build a target, it first builds that target's prerequisites, if necessary. (When is it necessary? When a target is a file or directory that does not exist real (unless it's .PHONY, but that's an advanced topic), or when one of it's prerequisites is newer than the target (unless it's "order-only", but that's an advanced topic)). So if Make is trying to build your all, it will first try to build $(build_dir) and $(xpi_built), which have been defined elsewhere in the makefile.
If you're trying to figure out what Make will do and when, there are tricks you can use. For example, you can run make -n, and Make would tell you what it would do, instead of doing it. Or you can put a command like #echo now making $# in a rule, to tell you what it's doing. Or for even more information:
some_target: preq another_preq and_another
#echo Making $#. The prerequisites are $^. Of those, $? are newer than $#.
other_commands...

Resources