${COMPILE_PATH}/%.c.d:%.cpp
$(CC) $(CFLAGS) ${CFLAGS_NDBG} -MM -E $^ > $#
#sed 's/.*\.o/$(subst /,\/, $(dir $#))&/g' $# >$#.tmp
#mv $#.tmp $#
$(subst /,\/, $(dir $#)), I don't understand this statement, subst's second argument is \/, why? change / to \/?
The slashes in the path need to be escaped because the sed script uses slashes as regex separators. An unescaped slash would terminate the substitution expression prematurely.
However, a better way to solve this would be to use an alternate separator -- sed helpfully allows you to use any puncuation character here; slash is just a common default.
The temporary file is also a bit of a wart which can easily be avoided.
${COMPILE_PATH}/%.c.d:%.cpp
$(CC) $(CFLAGS) ${CFLAGS_NDBG} -MM -E $^ \
| sed 's|.*\.o|$(dir $#)/&|g' >$#
(I'm guessing the g flag is actually also completely superfluous here.)
Because it is being used in a sed command where a bare / would be seen as the end of the s/// command.
That being said an alternate solution (assuming you could make any assurances about what characters are legal in the directory name) would have been to have used a different delimiter for s/// such as ; (i.e. s;.*\.o;$(dir $#)&;g) or ! (i.e. s!.*\.o!$(dir $#)&!g)
Looks like they're changing the / to the escaped version.
Related
I am having to quickly grok a Makefile, to understand some issues I'm having. I have come across these lines, and I want to make sure I'm understanding their use here correctly.
cp $< $#
sed -i -e 's#{{DEPLOY}}#https://example.com/#g' $#
To be clear, I know that sed is being used to make a (global?) string substitution here, but the use of bash special characters make this perhaps more confusing than it should be.
Would appreciate explanation on what the two lines above are actually doing.
They are GNU Make automatic variables:
$#
The file name of the target of the rule. If the target is an archive member, then ‘$#’ is the name of the archive file. In a pattern rule that has multiple targets (see Introduction to Pattern Rules), ‘$#’ is the name of whichever target caused the rule’s recipe to be run.
$< The name of the first prerequisite. If the target got its recipe from an implicit rule, this will be the first prerequisite added by the implicit rule (see Implicit Rules).
See what targets and prerequisites are.
sed -i -e 's#{{DEPLOY}}#https://example.com/#g' $#
Replaces all occurrences of {{DEPLOY}} with https://example.com/ in $#.
Putting all together, a simpler and more robust version of
cp $< $#
sed -i -e 's#{{DEPLOY}}#https://example.com/#g' $#
is:
sed -e 's#{{DEPLOY}}#https://example.com/#g' $< > $#
I have Makefile from an older project which I have to modify for a new system. It has the following global definitions for its source and object files:
SRCFILES = $(wildcard *.c) $(wildcard tasks/*.c)
SRCDIR = .
OBJDIR = objdir
OBJFILES = $(SRCFILES:%.c=${OBJDIR}/%.o)
The Makefile was a mess and I had to modify it heavily. I manged to compile the code and generate the .o files from the .c source code. The expected .elf file is also compiled correctly.
the problem is that I can do not understand what the code is doing in the following lines I searched so many websites but no luck. any ideas?
sed 's,^\(.*\)\.o[ :]*,$(#D)/\1.o $(#D)/\1.d : ,g' < $#.$$$$ > $#; \
rm -f $#.$$$$
The makefile is as follows
lwip.elf: build_echo $(OBJFILES)
#set -e; echo Linking the object files...; \
sparc-elf-gcc $(OBJFILES) -o $#;
#echo Successfully linked the files
#echo lwip.elf file ready to load...
build_echo: httpserver_raw/fsdata.c
#echo Building the source files first...
#echo The Modified or New source code to be build are :
clean:
- rm -f lwip.elf
$(OBJDIR)/%.o: $(SRCDIR)/%.c
#set -e; echo ' $<'; \
sparc-elf-gcc-4.4.2 -Wall -O2 -g -msoft-float -mcpu=v8 -c -o $# $(CFLAGS) -I. -I lwip/include -I lwip/include/ipv4 -I lwip/leon3 $<
$(OBJDIR)/%.d: $(SRCDIR)/%.c
#set -e; rm -f $#; mkdir -p $(#D); \
sparc-elf-gcc -MM -I. -I lwip/include -I lwip/include/ipv4 -I lwip/leon3 $< > $#.$$$$; \
sed 's,^\(.*\)\.o[ :]*,$(#D)/\1.o $(#D)/\1.d : ,g' < $#.$$$$ > $#; \
rm -f $#.$$$$
-include $(patsubst %.c,$(OBJDIR)/%.d,$(SRCFILES))
Note: The sparc-elf-gcc is a special cross-compiler used for the sparcV8 architecture.
There's a lot going on in there:
sed 's,^\(.*\)\.o[ :]*,$(#D)/\1.o $(#D)/\1.d : ,g' < $#.$$$$ > $#; \
rm -f $#.$$$$
Let's break it down.
There's the sed command itself: sed 's,^\(.*\)\.o[ :]*,$(#D)/\1.o $(#D)/\1.d : ,g',
whose input is redirected (<) from a file designated by #.$$$$, and
whose output is redirected (>) to a file designated by $#, then
there's a second command, rm -f $#.$$$$.
Furthermore, that's in the context of a larger list of commands, which set it up with
#set -e; rm -f $#; mkdir -p $(#D); \
sparc-elf-gcc -MM -I. -I lwip/include -I lwip/include/ipv4 -I lwip/leon3 $< > $#.$$$$;
In interpreting that, you have to first recognize several make implicit variables:
$#, which represents the name of the target of the rule
$(#D), which represents the directory of the target name (i.e. the target's dirname)
$<, which represents the first prerequisite
Next, you need to recognize that the dollar sign has special meaning to make, so if you want a literal $ you need to double it. The $$$$ simply repeat this, so that what gets passed to the shell is just two dollar signs ($$). The shell, in turn, replaces that with its own process ID -- a relatively common (but nevertheless insecure) idiom for generating unique file names.
So, the part of the command that you omitted from your callout ensures that the target's destination directory exists, then runs a (cross-?) gcc with flags that cause it to emit information about header dependencies. These are captured in a file, something like objdir/src.d.12345, then in the part you called out, that file is redirected into sed, with the output going to the final target, something like objdir/src.d, after which the intermediate file is removed.
The sed command itself is performing a substitution (s) on all lines, replacing every occurrence (g) of the pattern ^\(.*\)\.o[ :]* with a replacement that I'll come back to in a moment. First the pattern. The ^ anchors the pattern to the beginning of a line. The \(\) are metacharacters delimiting a capturing group, and the .* inside matches any number of any characters. Following that must be a literal decimal point, \., an o, and zero or more occurrences or the space and colon characters ([ :]*). For example, that would match this, up to but not including the header name:
objdir/tasks/t1.o : my_header.h
The replacement uses the aforementioned $(#D) -- which is expanded by make, not the shell or sed -- and \1, which sed expands to the text matched by the first capturing group. Thus, that would transform the above example line to this:
objdir/tasks/t1.o objdir/tasks/t1.d : my_header.h
In short, then, all that to say that it munges the computed dependency list to make the dependency file that is the target of the rule list itself as depending on all the same files that the corresponding object file does.
I have the following rules
define compile_c
$(ECHO) "CC $<"
$(Q)$(CC) $(CFLAGS) -c -MD -o $# $<
## The following fixes the dependency file.
## See http://make.paulandlesley.org/autodep.html for details.
## Regex adjusted from the above to play better with Windows paths, etc.
#$(CP) $(#:.o=.d) $(#:.o=.P); \
$(SED) -e 's/#.*//' -e 's/^.*: *//' -e 's/ *\\$$//' \
-e '/^$$/ d' -e 's/$$/ :/' < $(#:.o=.d) >> $(#:.o=.P); \
$(RM) -f $(#:.o=.d)
endef
vpath %.c . $(TOP)
$(BUILD)/%.o: %.c $(BUILD)/%.pp
$(call compile_c)
vpath %.c . $(TOP)
$(BUILD)/%.pp: %.c
$(ECHO) "PreProcess $<"
$(Q)$(CC) $(CFLAGS) -E -Wp,-C,-dD,-dI -o $# $<
When the build finishes, GNU make says
Removing intermediate files... and deletes all the .pp files which I do NOT want.
Why is it doing this?
How do I stop it?
Since you're using GNU Make, you can make the following adjustment to your Makefile:
.PRECIOUS: $(BUILD)/%.pp # ADD THIS LINE
$(BUILD)/%.pp: %.c
$(ECHO) "PreProcess $<"
$(Q)$(CC) $(CFLAGS) -E -Wp,-C,-dD,-dI -o $# $<
The documentation has this to say about .PRECIOUS directives:
The targets which .PRECIOUS depends on are given the following special treatment: if make is killed or interrupted during the execution of their recipes, the target is not deleted.
[...]
Also, if the target is an intermediate file, it will not be deleted after it is no longer needed, as is normally done.
[...]
You can also list the target pattern of an implicit rule (such as ‘%.o’) as a prerequisite file of the special target .PRECIOUS to preserve intermediate files created by rules whose target patterns match that file's name.
This has the benefit of not creating an unwanted additional rule. It's also clearer what you're trying to do: keep the precious intermediate files that might be expensive to recreate.
I think the best solution is to use the .SECONDARY special target. Just add this line:
.SECONDARY:
Quoting the manual:
.SECONDARY with no prerequisites causes all targets to be treated as secondary (i.e., no target is removed because it is considered intermediate).
Why is this better than making the targets prerequisites of a throw-away target? That's more clutter, and has to be done explicitly for every set of files that might be generated with pattern rules.
Why is this better than .PRECIOUS? That causes files to be retained even if their recipe fails when using .DELETE_ON_ERROR. The latter is important to avoid failing recipes leaving behind bad outputs that are then treated as current by subsequent make invocations. IMO, you always want .DELETE_ON_ERROR, but .PRECIOUS breaks it.
If you search for "gnu make intermediate files" you'll immediately find the answer as to why it's happening, in the GNU make manual section Chains of Implicit Rules.
It also tells you how to avoid it: a file cannot be intermediate if it is mentioned in the makefile as a target or prerequisite.
So, just list your .pp files as a prerequisite of some rule, somewhere. It doesn't have to be a rule that's ever invoked. You don't give enough of your makefile here for us to provide a complete answer, but it would be something like:
all_pps: $(ALL_OBJECTS:.o=.pp)
assuming you had a variable ALL_OBJECTS containing all your .o files.
Here is a detail that finally got PRECIOUS working for me. The pattern that you give to PRECIOUS has to be exactly the pattern that is being used in the rule that creates the intermediate file. I want to save all files prefixed by moc_. At first I used
.PRECIOUS: moc_%
to no avail. Then I tried
.PRECIOUS: moc_%.cpp
and this did the trick.
I'm trying to improve my Makefile by using define as functions, but I have a little problem with Make functions in defines. Let me show you a demo
OBJ=obj/main.o \
obj/prompt.o
define buildMessage
src="$(patsubst $(OBJ_PREFIX)/%.o,%.c,$(1))"; \
obj="$(patsubst $(OBJ_PREFIX)/%.o,%.o,$(1))"; \
echo "$$src => $$obj"
endef
$(OBJ):
$(call buildMessage, "$#")
$(CC) -o $# -c $(patsubst $(OBJ_PREFIX)/%.o, $(SRC_PREFIX)/%.c, $#) $(CFLAGS)
So, yeah, I want the define to print the build step with fancy style, but it seems that the patsubst isn't applied in the define. It will output
obj/main.o => obj/main.o
obj/prompt.o => obj/prompt.o
It is exactly like if the patsubst was handled but without changing nor matching anything. Any suggestions?
Thank you for your time
Alex
I'm assuming that, although you didn't show it, the value of OBJ_PREFIX is obj.
Make doesn't care anything about quotes. When you write:
$(call buildMessage, "$#")
the value of $1 will be, literally, "obj/main.o" including the quotes. That string doesn't match the pattern obj/%.o and so the pattern doesn't apply, leaving the original string untouched.
You should write this:
$(call buildMessage,$#)
This is a simple question for a starter like me, but what can I do to do like the following
all: run
run:
DIR=bin/
$(CC) $(LIBRARY) $(INCLUDE) run.o -o $(DIR)$#
Thanks.
Why not go like this?
DIR=bin/
all: $(DIR)/run
$(DIR)/run:
$(CC) $(LIBRARY) $(INCLUDE) run.o -o $#
As written, you have an assignment to the shell variable DIR on one command line. On the next line, you have the expansion of a make variable DIR. This doesn't work because the two lines are executed by different shells, and in any case, make expands $(DIR) before running the shell and DIR is not a make variable.
You could make it work like this:
all: run
run:
DIR=bin/; \
$(CC) $(LIBRARY) $(INCLUDE) run.o -o $${DIR}$#
The backslash means the two lines are treated as one (so much so that the semicolon is needed). The $${DIR} notation is expanded by make to ${DIR} (more precisely, $$ expands to $ and make ignores the {DIR}), and then the shell expands ${DIR} from the value set previously. You could, of course, omit the braces.
However, the answer by BeSerK is probably what you're looking for.