Makefile: what does $#~ mean? - makefile

I am working on an old makefile that contains the following snippet to generate a shared library:
lib$(LIBNAME).so.$(SOLIBREV): $(OBJS)
$(RM) $#~
#SONAME=`echo $# | sed 's/\.[^\.]*$$//'`; set -x; \
$(CC) -o ./$#~ -shared -Wl,-soname,$$SONAME $(OBJS) $(SOEXTRALIBS) -lc;
$(MV) $#~ $#
$(MV) $# lib$(LIBNAME).so
Now I need to modify that. I know that $# specifies the target but what meaning does the tilde in "$#~" have?
By the way SOLIBREV stands for so-library-revision.

It doesn't mean anything special. It's just $# followed by a literal ~. A ~ suffix on filenames is often used for temporary files, so this recipe is using a temporary file named after the target but with the extra ~ suffix.

Related

Makefile Syntax unclear

This is my first Makefile, and I am can't figure out some of the syntax used. The questions are marked below.
$(BUILD_DIR)/%.o: %.c $(BUILD_DIR)
$(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $#
What is the usage of "$(BUILD_DIR)" in the dependency?
What is the meaning of "$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $#" in the role?
As with most computer languages the syntax of make cannot be clear if you don't know it. If you are using GNU make the GNU make manual is your friend. In the following explanations I will assume that BUILD_DIR = build and that one of the source files is bar/foo.c.
$(BUILD_DIR) in the list of prerequisites (dependencies) tells make that the build directory (in which object files are supposed to go) must exist before the recipe is executed; logical. There must be another rule somewhere to create the directory if it does not exist yet. Something like:
$(BUILD_DIR):
mkdir -p $#
But unless you forgot to copy an important character, this dependency is terribly sub-optimal. As the last modification time of a directory changes each time its content changes (files or sub-directories added or removed), it will force the re-compilation of all source files every time the directory changes, which is not what you want. A better dependency would be order-only:
$(BUILD_DIR)/%.o: %.c | $(BUILD_DIR)
that tells make to consider only the existence of $(BUILD_DIR), not its last modification time, when deciding to re-build or not.
$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $# is just a combination of make automatic variables and functions.
$< and $# expand as the first prerequisite (bar/foo.c) and the target (build/bar/foo.o) respectively.
$(<:.c=.lst) replaces .c by .lst in $<: bar/foo.lst.
$(notdir $(<:.c=.lst)) removes the directory part: foo.lst.
All in all, for a bar/foo.c source file, and with BUILD_DIR = build, the pattern rule would be equivalent to:
build/bar/foo.o: bar/foo.c | build
$(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=build/foo.lst bar/foo.c -o build/bar/foo.o
Note that there are two different situations to consider:
All your source files are in the same directory as the Makefile (no bar/foo.c, just foo.c). Then you can simplify your recipe:
$(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(<:.c=.lst) $< -o $#
because the $(notdir...) is useless.
Your source files can be in sub-directories (bar/foo.c). Then you need the $(notdir...) in your recipe. But be warned that if you have two source files with the same base name (bar/foo.c and baz/foo.c) you will have a name conflict for $(BUILD_DIR)/foo.lst and your Makefile will not work as expected. Moreover, the order-only prerequisite of the rule should be equivalent to build/bar (or build/baz), not just build. And there should be a rule to create it if needed. If it is your case I suggest to change your pattern rule for:
$(BUILD_DIR)/%.o: %.c
mkdir -p $(dir $#)
$(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $#
There are other solutions (secondary expansion...) but there are a bit too complicated for this already too long answer.

Create dependency files in Makefile

I am using a Makefile including a rule to create a dependency file. The compiler is GCC.
%.d: %.c
mkdir -p $(#D)
$(CC) $(CFLAGS) $(CPPFLAGS) -M $< | \
sed 's,\($(notdir $*)\.o\) *:,$(dir $#)\1 $#: ,' > $#.tmp
mv $#.tmp $#
I am quite new in Makefile technique and I find it a little bit difficult to understand this rule composed of a mixture of several options.
Can somebody give a simple explanation how this rule works?
Thanks in advance.
%.d: %.c
All files ending in .d can be made using this rule, as long as there is a .c file with the same directory and stem relative to the current directory or one of the vpaths.
mkdir -p $(#D)
Create the topmost and all intermediate directories for $(#D), which is an automatic make variable that expands to the directory part of the current target.
$(CC) $(CFLAGS) $(CPPFLAGS) -M $< | \
Invoke the C compiler on the first prerequisite (whatever.c) and tell it to output a list of make dependencies to the standard output. Pipe this output to
sed 's,\($(notdir $*)\.o\) *:,$(dir $#)\1 $#: ,' > $#.tmp
sed. The rules in the output will have the same path as the source files, but whoever wrote this rule wants the objects files in a different directory so we need sed to substitute the paths.
Sed captures any rules with targets ending with .o that match $(notdir $*). $* is another automatic variable that expands to the pattern that matches % in the current rule, and notdir will strip any directory parts so you're left with the filename stem with no extension.
Sed then prepends $(dir $#) to the object file target, which is the same thing as $(#D) we saw above, and adds the dependency file itself ($#) as a target of the same prerequisites. This output is redirected to a file with the name of the current target + .tmp.
mv $#.tmp $#
Moves the previous file to the real target of the rule.
Side note: if you don't mind the dependency files being generated in the same directory as the object files then these recipes are outdated, you can achieve the same thing with something like:
sources := src/somefile1.c src/somefile2.c
objects := $(sources:src/%.c=obj/%.o)
deps := $(objects:.o=.d)
CFLAGS := -MMD -MP
.PHONY: all
all $(objects)
$(objects): obj/%.o: src/%.c
$(COMPILE.c) $(OUTPUT_OPTION) $<
-include $(deps)

Object file directory per compiler option combinations

I was reading gnu make section 10.5.4 "How patterns match" and it does not sound like I can do what I want.
I want to setup a directory structure where my source code is in one directory, and there are sub-directories to hold object files.
One sub-directory for each build configuration.
So I might have these files
a.c
debug/a.o # compiled with -g
release/a.o # compiled with -O
So I would like to make rules like this
debug/%.o : %.c
gcc -c -g %.c -o $#
release/%.o : %.c
gcc -c -O %.c -o $#
But section 10.5.4 tells me a match on "debug/a.o" will make the stem be "debug/a" so gnu make
will look for the source file at "debug/a.c" which is not what I want.
Is there a way to get GNU make to help me ?
Your makefile will work as written.
From that section of the manual:
When the target pattern does not contain a slash (and it usually does
not), directory names in the file names are removed from the file name
before it is compared with the target prefix and suffix. After the
comparison of the file name to the target pattern, the directory
names, along with the slash that ends them, are added on to the
prerequisite file names generated from the pattern rule's prerequisite
patterns... [bold added]
Your target patterns do contain slashes.
Try it if you don't believe me.
EDIT:
Correction: in the commands you should use $< rather than %.c.
CC=gcc
DEBUGFLAGS=-g
RELEASEFLAGS=-O
debug/%.o : %.c
$(CC) $(DEBUGFLAGS) -c $< -o $#
release/%.o : %.c
$(CC) $(RELEASEFLAGS) -c $< -o $#

Makefile target matching

I'm having troubles with my Makefile :-(
I have a mix of assembly and C sourcecode that I need to link together. I need different build-instructions for those two types. Since both the assembler and C compiler output *.o files, I cannot use the general %.o:%.c construction often found in example Makefiles
This what I'm trying now:
Get a list of all C files and their resulting output files:
C_SRCFILES := $(shell find $(SRCDIRS) -type -f -name "*.c")
C_OBJFILES := $(patsub %.c,%.o,$(C_SRCFILES))
Get a list of all asm files and their resulting output files:
A_SRCFILES := $(shell find $(SRCDIRS) -type -f -name "*.asm")
A_OBJFILES := $(patsub %.asm,%.o,$(A_SRCFILES))
When I echo those vars to the screen, they seem to be correct, but how I do define my targets now?
I tried something like this
$(A_OBJFILES): ($A_SRCFILES)
$(AS) $(AFLAGS) -o $# $*
$(C_OBJFILES): ($C_SRCFILES)
$(CC) $(CFLAGS) -c -o $# $*
all: $(A_OBJFILES) $(C_OBJFILES)
$(LD) $(LDFLAGS) $(A_OBJFILES) $(C_OBJFILES) -o $(TARGET_OUTPUT)
but ofcourse, this doesn't work...
Any suggestions?
First problem: a misplaced parenthesis or two.
$(A_OBJFILES): ($A_SRCFILES)
Notice that you have the $ inside the ( in ($A_SRCFILES). Make expands $A, which is nothing, and things go downhill. I think you meant $(A_SRCFILES), and the same thing in the other rule.
Second problem: I don't know the syntax of the assembler, but the syntax of the compiler command is wrong:
$(CC) $(CFLAGS) -c -o $# $*
The variable $* is nothing if we're not in a pattern rule, which we're not (yet). And anyway, if we were in a pattern rule and you were trying to build foo.o, this command would look for the source file foo, and there's no such file. Do it this way:
$(CC) $(CFLAGS) -c -o $# $<
Third problem: each object file depends on all source files (in each rule). Try this instead:
$(A_OBJFILES): %.o : %.asm
...
$(C_OBJFILES): %.o : %.c
...
(Now it's a pattern rule.)
Fourth problem: a lot of redundancy in the last rule. Change it to this:
all: $(A_OBJFILES) $(C_OBJFILES)
$(LD) $(LDFLAGS) $^ -o $(TARGET_OUTPUT)
or better still:
all: $(TARGET_OUTPUT)
$(TARGET_OUTPUT): $(A_OBJFILES) $(C_OBJFILES)
$(LD) $(LDFLAGS) $^ -o $#
Since both the assembler and C compiler output *.o files, I cannot use the general %.o:%.c construction often found in example Makefiles
Sure you can:
%.o : %.c
# commands to make .o from a corresponding .c
%.o : %.asm
# commands to make .o from a corresponding .asm

Using sed in a makefile; how to escape variables?

I am writing a generic makefile to build static libraries. It seems to work well so far, except for the line calling sed:
# Generic makefile to build a static library
ARCH = linux
CFLAGS = -O3 -Wall
SOURCES = src
BUILD_DIR = build/$(ARCH)
TARGET = $(BUILD_DIR)/libz.a
CFILES = $(foreach dir,$(SOURCES),$(wildcard $(dir)/*.c))
OBJECTS = $(addprefix $(BUILD_DIR)/,$(CFILES:.c=.o))
# Pull in the dependencies if they exist
# http://scottmcpeak.com/autodepend/autodepend.html
-include $(OBJECTS:.o=.dep)
default: create-dirs $(TARGET)
$(TARGET): $(OBJECTS)
$(AR) -rc $(TARGET) $^
$(BUILD_DIR)/%.o: %.c
$(CC) $(CFLAGS) -c $< -o $#
$(CC) -M $(CFLAGS) $*.c > $(BUILD_DIR)/$*.tmp
sed s/.*:/$(BUILD_DIR)\\/$*.o:/ $(BUILD_DIR)/$*.tmp > $(BUILD_DIR)/$*.dep
#rm $(BUILD_DIR)/$*.tmp
.PHONY: create-dirs
create-dirs:
#for p in $(SOURCES); do mkdir -p $(BUILD_DIR)/$$p; done
.PHONY: clean
clean:
rm -fr $(BUILD_DIR)
sed is used to replace the path/name of the object file with the full path of where the object actually is. e.g. 'src/foo.o:' is replaced with 'build/linux/src/foo.o:' in this example.
$(BUILD_DIR) and $* in the replacement string both contain forward slashes when expanded - how do I pass them to sed?
Note: This might have been answered here before, but I am so far unable to apply those answers to my specific problem!
You can use anything else than forward slashes as separator in sed. E.g. sed s~foo~bar~g
You can use double quotes " (at least in the shell), and variables will still be expanded: echo "Hello $PLANET"
If you want the path to expand into a file you use
sed -i "s/TO_REPLACE/$(subst, /,\/,${PATH_VARIABLE})/g" filename.txt
If your variable PATH_VARIABLE looked like /opt/path/user/home/etc
it will now inflate to:
\ /opt\ /path\ /user\ /home\ /etc
This should allow sed to insert the ' / ' correctly.
-Matt

Resources