Makefile can we add a comment on a target line? - makefile

I realized that in with vim a comment is not highlighted when it is on the same line as a target:
mytarget: # A comment not highlighted
#echo foo
However, it seems my GNU make 3.82 treat them correctly.
I checked the manual, but I didn't see anything related to a comment in a target line.
Why vim or Notepad++ doesn't highlight these comments?

vim (or any other editor's) highlighting is only as good as the highlighting code written for the editor. It doesn't necessarily reflect what is or isn't legal for any given version of the language/etc. in question.
That being said I have no idea if a comment at that point is actually legal or not. The documentation doesn't appear to say specifically.
That being said, empirically, it appears that all of make 3.81, 3.82, 4.0 and 4.1 accept a comment in that location.
$ cat comment.mk
all: prereq # this is a comment
#echo '$#: $^'
$ /root/make/make-3.81/make -f comment.mk
all: prereq
$ /root/make/make-3.82/make -f comment.mk
all: prereq
$ /root/make/make-4.0/make -f comment.mk
all: prereq
$ /root/make/make-4.1/make -f comment.mk
all: prereq

Related

Print the prerequisites to a target

This is easily a duplicate of this question, but it has not been answered, for what I can see.
Here is my goal: to be able to print the prerequisites to a target.
I have some kind of a solution but it feels like a hack to me.
Say the target is all, and it only has prerequisites, in a file named makefile-1:
all: foo
all: bar baz
I can use another makefile named makefile-2:
all:
ifeq ($(SHOW),yes)
echo $^
else
cat $^
endif
This kind of gives me what I need, when invoked properly:
$ make -f makefile-1 -f makefile-2
cat foo bar baz
$ make -s SHOW=yes -f makefile-1 -f makefile-2
foo bar baz
But I am not sure what to do if the prerequisite actually has a recipe in the original makefile, or if there is a better/cleaner way.
An easier way to show the prerequisites to a target without building the target would probably be to use the -p option (as suggested here) and -q options together, as suggested on the GNU-Make manual page (long name for the option is --print-data-base):
make -qp | grep '^all:'
This will print the database and select only the line which has the target all and its prerequisites. If there is a rule for that target, it would be at the end of the same paragraph (if I am understanding the format of the output correctly).
If multiple makefiles are used, one can specify all of them with the -f option:
make -f makefile-1 -f makefile-2 -qp
This will of course collect prerequisites for the same target from all makefiles, but the target cannot have a rule specified in more than one file.

Allow space in target of GCC makefile

Is there a way to get spaces inside target names working when using make.exe? It seems to be impossible if this really ancient bug report is correct:
http://savannah.gnu.org/bugs/?712
For reference, the big problem is that pieces of makefile commands like:
"foo bar baz": $(OBJ)
$(CPP) $(LINKOBJ) -o $(BIN) $(LIBS)
... seem to get treated as three separate commands: one to build "foo (note the included "), one to build bar, and lastly, one to build baz" (again, including "). This is because make.exe seems to be using space as a delimiter.
However, it's reasonable to assume that one might want to build "Hello World.exe" for example. This doesn't seem to be possible. Double quotes don't work, and neither does escaping the separate words (I've read that somewhere, don't remember the link):
"foo\\ bar\\ baz": $(OBJ)
$(CPP) $(LINKOBJ) -o $(BIN) $(LIBS)
Is there any other way to fix this? The official manual only confirms the tokenize-by-spaces stuff, but doesn't provide a way to use space for any other purpose:
http://www.gnu.org/software/make/manual/make.html#Rule-Syntax
Edit: as suggested, I've tried single slashes too, but these have the exact same effect as double slashes. Make complains it can't find rules for the first word:
mingw32-make.exe: *** No rule to make target `foo', needed by `all'. Stop.
The executable "foo bar baz.exe" is correctly produced though, but linking is done each time per word.
as Matthias said, it's a matter of "\ ", but of double quote too. Here is how I succeded into this :
EXECUTABLE=foo\ bar\ baz
all: $(SOURCES) $(EXECUTABLE)
$(EXECUTABLE): $(OBJECTS)
$(CC) $(OBJECTS) -o "$#" $(LDFLAGS)
Note the double quotes around the $#
It seems to me that when make reach the target "$(EXECUTABLE)" it expands the "\ ", so the command line becomes
gcc file.o -o foo bar baz -LFlags
which is not what you want, you want double quotes around the name of the file.
Now you are on windows and I don't remember how it deals with spaces in names, so as Matthias said, first check how "cmd.exe" deals with spaces (except by surrounding name with double quotes...)
Instead of double backslash use single ones. The following Makefile works (at least for gnu make):
goal: foo\ bar
foo\ bar:
gcc -o "foo bar" "foo bar.c"
Maybe easier to consider a simpler Makefile, which ignores the dependencies in the standard targetRule: dependencies invocation. The OP is for Windows, and the below is done on Linux in the bash shell; but it should probably be applicable to Windows using the bash shell via cygwin, I guess. I have the following in my Makefile (note, there should be a TAB before the echo commands, which SO converts to spaces):
testA:
echo "testA"
unrelated:
echo "unrelated"
testA\ clean:
echo "testA clean"
clean:
echo "clean"
If I call make with targets clean or testA in the bash shell terminal, I get the expected results:
$ make testA
echo "testA"
testA
$ make clean
echo "clean"
clean
Now, if I call make testA clean just written as is, the bash shell will split the arguments at spaces, so make will receive two arguments, and will run them separately:
$ make testA clean
echo "testA"
testA
echo "clean"
clean
... but if I wrap the target supplied to make in quotes - or if I escape the space - the shell will understand that it is supposed to be a single argument with a space inside, and will propagate it as such to make, which will proceed to execute what is written as the testA\ clean target rule:
$ make "testA clean"
echo "testA clean"
testA clean
$ make testA\ clean
echo "testA clean"
testA clean
One consequence of this, is that unfortunately you cannot "TAB" at the command line for autocompletion of testA\ clean; if you type make teTAB at the command line, only testA will be autocompleted automatically (and the testA\ clean will not be shown as an autocomplete option).

How to replace text in a makefile in a way that works with make AND dmake?

I've got this makefile:
ALL = ../lib/Mo.pm \
../lib/Mo/builder.pm \
../lib/Mo/default.pm \
../lib/Mo/has.pm \
all: $(ALL)
$(ALL): Mo.pm compress.pl Makefile
perl compress.pl $(#:../lib/%=%) > $#
What it's meant to do is something like this:
$ make -n
perl compress.pl Mo.pm > ../lib/Mo.pm
perl compress.pl Mo/builder.pm > ../lib/Mo/builder.pm
perl compress.pl Mo/default.pm > ../lib/Mo/default.pm
perl compress.pl Mo/has.pm > ../lib/Mo/has.pm
However with dmake on Windows this happens:
d:\mo-pm\src>dmake -n
perl compress.pl ..\lib\Mo.pm > ..\lib\Mo.pm
perl compress.pl ..\lib\Mo\builder.pm > ..\lib\Mo\builder.pm
perl compress.pl ..\lib\Mo\default.pm > ..\lib\Mo\default.pm
perl compress.pl ..\lib\Mo\has.pm > ..\lib\Mo\has.pm
I've been trying out various combinations of s/// and subst to make it work in dmake, and found out that it wants the path to have \s, which means a double substitution against both variants of the path (../lib/ and ..\lib) could work, but i can't figure out how to make it work for both make variants.
Any ideas or other ways to do this?
It's not only that the dir separator chars are different for both versions, moreover the dmake syntax seems to be deliberately designed to be incompatible with GNU make. The only part of the syntax that is actually compatible is pattern substitution, so this is the way to go:
all: $(ALL)
$(ALL) : Makefile compress.pl
../lib/%.pm : %.pm
perl compress.pl $< > $#
dmake actually substitutes the / for directory separator chars for you here. I've tested this Makefile with an echo instead, and it writes to the right directory.
Explanation: The pattern rules define rules for a particular file to be re-made when it matches a regular expression (the ../lib/%.pm part) and a prerequisite of a similar name is found (%.pm). The % in the prerequisite is replaced by the matching part of the % in the target. The extra rule with Makefile and compress.pl is needed because dmake doesn't like extra prerequisites in a pattern rule. As usual, $< and $# are make's special variables for source and target file.
So, the core difference is that your original rule said "the files named in this list can be made with the following rule), while the pattern rule says "any file looking like ../lib/%.pm can be made from a matching file in the current directory" and then gives a list of pm files to make.
Pattern rules are actually quite powerful, useful to know. Unfortunately, some makes don't know them, only the older suffix rules.
Further details of what's going on can be obtained by running
make -rdn

Is there a way to tell automake not to interpret part of the automakefile?

Is there a way to tell automake not to interpret part of the Makefile.am?
Specifically, I am trying to encode a Makefile conditional in a Makefile.am. As other people have noticed, this doesn't work because automake interprets the endif construct.
Is there any way to escape or quote strings in Makefile.am files so that automake copies them verbatim into the destination Makefile? Specifically I don't want it to interpret the endif in something like:
ifeq "$(SOMEVAR)" ""
SOMEVAR="default_value"
endif
The reason automake does conditionals is because some dinosaur makes don't. That said, if you really must do this, you could define your snippet as a variable in configure.ac and AC_SUBST it into your Makefile. That way, automake won't get a chance to see it. Remember to use AM_SUBST_NOTMAKE to avoid creating a line like FOO = #FOO#.)
dnl In configure.ac:
snippet='
ifeq ($(somevar),Y)
foo
endif
'
AC_SUBST([snippet])
AM_SUBST_NOTMAKE([snippet])
and:
## In Makefile.am:
#snippet#
I sincerely hope there's a better way than this.
I managed to find a different solution. You can put your to-be-escaped bits in a separate file and then do:
$(eval include $(srcdir)/Include.Makefile)
Since automake doesn't understand $(eval it just leaves the entire line intact. Thus you can put whatever you want into the other file and GNU make will happily read it. Note you can't just use include directly since Automake does understand that and will go into the other file.
The simplest way to do so is to insert a space before the ifdef, ifeq, ifneq, else and endif. That way, these keywords are not recognized by the automake parser. Be sure to insert a space, it won't work with a tab.
source: https://patchwork.kernel.org/patch/6709921/
How about:
SOMEVAR=$(if $(SOMEVAR),$(SOMEVAR),"default_value")
In automake (GNU automake) 1.16.5 simple conditionals ifeq ... endif do pass through automake into Makefile when I isolate the keywords with newline and spaces, like so:
if %?INSTALL%
# 1 preceding newline
ifeq (true,$(call direction,uninstall %DIR% PROGRAMS %BASE%)) # 2 preceding spaces
## -------------- ##
## Uninstalling. ##
## -------------- ##
.PHONY uninstall-am: uninstall-%DIR%PROGRAMS-$(%NDIR%_STATUS)
uninstall-%DIR%PROGRAMS: uninstall-%DIR%PROGRAMS-$(%NDIR%_STATUS)
uninstall-%DIR%PROGRAMS-supported: uninstall-%DIR%PROGRAMS
uninstall-%DIR%PROGRAMS-unsupported:
#echo "%NDIR% (PROGRAMS) not installed" >&2
uninstall-%DIR%PROGRAMS: $(install_%DIR%_PROGRAMS)
#test $< \
&& $(NORMAL_UNINSTALL) \
&& $(autotoolsdir)/uninstall -P PROGRAMS -LT ?LIBTOOL? -I $? >&2
endif # 2 preceding spaces
# 1 subsequent newline
endif %?INSTALL%
That is especially the case if I mix automake conditionals with make conditionals, like shown above.

Makefile and rm -f file.{ext1,ext2,ext3} issue

Could you explain me, why Makefile rule:
clean:
rm -f foo.{bar1,bar2,bar3}
does not result in removing files: foo.bar1 foo.bar2 and foo.bar3?
I believe I saw pattern like that many times in various Makefiles, but I'm currently writing my own Makefile and can't make that rule work correctly (no files are removed).
I'm using:
gnu make 3.81
gnu bash 4.1.5
Bash evals that pattern as I suspect:
$ echo test.{a,b,c}
test.a test.b test.c
Thanks!
UPDATE
Thank to David's hint I found solution for the problem described above.
The gnu make uses the /bin/sh by default and that is why a.{1,2,3} isn't evaluated to a.1 a.2 a.3.
To make 'make' use bash instead of sh add following line to your Makefile:
SHELL=/bin/bash
from now a.{1,2,3} will be considered as a.1 a.2 a.3
Is there a file named clean in the directory? If so, make will consider that target up to date and won't run the corresponding command. To fix that, add this line to your makefile:
.PHONY: clean
If when you run make clean you get the output
make: `clean' is up to date.
then that's probably your problem.
It's because the shell isn't being invoked here, but rather rm is directly. Since the shell does the {} substitution, what rm 'sees' is the raw foo.{bar1,bar2,bar3} string. As there's no such file, nothing happens.
You should use one of GNUmake's string macros to have it perform the expansion for you.

Resources