3.Cmake介绍
Makefile Syntax
A Makefile consists of a set of rules. A rule generally looks like this:
targets : prerequisities
command
command
command
- The targets are file names, seperated by spaces. Typically, there is only one per rule.
- The commands are a series of steps typically used to make the target(s). These need to start with a tab character, not spaces.
- The prerequisites are also file names, seperated by spaces. These files need to exist before the commands for the target are run.
Make Overview
The main use of Make is to list out a set of directions to compile some c or c++ files, although it can solve other similar problems. The user gives Make some goal, say “generate the file hello”. The Makefile specifies how to make this file. Here’s an example:
# Since the blah target is first, it is the default target and will be run when we run "make"
blah: blah.o
cc blah.o -o blah
blah.o: blah.c
cc -c blah.c -o blah.o
blah.c:
echo "int main() { return 0; }" > blah.c
clean:
rm -f blah.o blah.c blah
Running the Examples
I made a quick video to show how to run these examples. You’ll need a terminal and “make” installed. For each example, put the contents in a file called Makefile
, and in that directory run the command make
. Here is the output of running the above example:
$ make
echo "int main() { return 0; }" > blah.c
cc -c blah.c -o blah.o
cc blah.o -o blah
Some examples, like the above, have a target called “clean”. Run it via make clean
to delete the files that make
generated:
$ make clean
rm -f blah.o blah.c blah
Simple Examples
This makefile has a single target, called some_file
. The default target is the first target, so in this case some_file
will run.
some_file:
echo "This line will always print"
This file will make some_file
the first time, and the second time notice it’s already made, resulting in make: 'some_file' is up to date.
some_file:
echo "This line will only print once"
touch some_file
Alternative syntax: same line
some_file: ; touch some_file
The backslash (“\”) character gives us the ability to use multiple lines when the commands are too long
some_file:
echo This line is too long, so \
it is broken up into multiple lines
Here, the target some_file
“depends” on other_file
. When we run make
, the default target (some_file
, since it’s first) will get called. It will first look at its list of dependencies, and if any of them are older, it will first run the targets for those dependencies, and then run itself. The second time this is run, neither target will run because both targets exist.
some_file: other_file
echo "This will run second, because it depends on other_file"
touch some_file
other_file:
echo "This will run first"
touch other_file
This will always make both targets, because some_file
depends on other_file, which is never created.
some_file: other_file
touch some_file
other_file:
echo "nothing"
“clean” is often used as a target that removes the output of other targets.
some_file: clean
touch some_file
clean:
rm -f some_file
Adding .PHONY
to a target will prevent make from confusing the phony target with a file name. In this example, if the file “clean” is created, make clean will still be run. .PHONY
is great to use, but I’ll skip it in the rest of the examples for simplicity.
some_file:
touch some_file
touch clean
.PHONY: clean
clean:
rm -f some_file
rm -f clean
Variables (Section 2.4)
Variables can only be strings. Here’s an example of using them:
files = file1 file2
some_file: $(files)
echo "Look at this variable: " $(files)
touch some_file
file1:
touch file1
file2:
touch file2
clean:
rm -f file1 file2 some_file
Magic Implicit Commands (Section 2.5)
Probably one of the most confusing parts about Make is the hidden coupling between Make and GCC. Make was largely made for GCC, and so makes compiling C/C++ programs “easy”.
# Implicit command of: "cc blah.o -o blah"
# Note: Do not put a comment inside of the blah.o rule; the implicit rule will not run!
blah:
# Implicit command of: "cc -c blah.c -o blah.o"
blah.o:
blah.c:
echo "int main() { return 0; }" > blah.c
clean:
rm -f blah.o blah blah.c
Using Wildcard Characters (Section 4.2)
We can use wildcards in the target, prerequisites, or commands.
Valid wildcards are *, ?, [...]
some_file: *.c
# create the binary
*.c:
touch f1.c
touch f2.c
clean:
rm -f *.c
The Wildcard Function (Section 4.2.3)
We CANNOT use wildcards in other places, like variable declarations or function arguments
Use the wildcard function instead.
wrong = *.o # Wrong
objects := $(wildcard *.c) # Right
some_binary:
touch f1.c
touch f2.c
echo $(wrong)
echo $(objects)
clean:
rm -f *.c
The vpath Directive (Section 4.3.2)
Use vpath to specify where some set of prerequisites exist. The format is vpath <pattern> <directories, space/colon seperated>
<pattern>
can have a %
, which matches any zero or more characters.
You can also do this globallyish with the variable VPATH
vpath %.h ../headers ../other-directory
some_binary: ../headers blah.h
touch some_binary
../headers:
mkdir ../headers
blah.h:
touch ../headers/blah.h
clean:
rm -rf ../headers
rm -f some_binary
The all target (Section 4.4)
Making multiple targets and you want all of them to run? Make a all
target and designate it as .PHONY
all: one two three
.PHONY: all
one:
touch one
two:
touch two
three:
touch three
clean:
rm -f one two three
Multiple targets (Section 4.8)
When there are multiple targets for a rule, the commands will be run for each target$@
is a automatic variable that contains the target name.
all: f1.o f2.o
f1.o f2.o:
echo $@
# Equivalent to:
# f1.o
# echo $@
# f2.o
# echo $@
Multiple targets via wildcards (Section 4.8)
We can use the wildcard % in targets, that captures zero or more of any character. Note we do not use *.o, because that is just the string *.o, which might be useful in the commands,
but is only one target and does not expand.
all: f1.o f2.o
.PHONY: all
%.o:
echo $@
Static Pattern Rules (Section 4.10)
Make loves c compilation. And every time it expresses its love, things get confusing. Here’s the syntax for a new type of rule called a static pattern:
targets ...: target-pattern: prereq-patterns ...
commands
The essence is that the a given target is matched by the target-pattern (via a %
wildcard). Whatever was matched is called the stem. The stem is then substituted into the prereq-pattern, to generate the target’s prereqs.
A typical use case is to compile .c
files into .o
files. Here’s the manual way:
all: foo.o bar.o
.PHONY: all
# The automatic variable $@ matches the target, and $< matches the prerequisite
foo.o: foo.c
echo "Call gcc to generate $@ from $<"
bar.o: bar.c
echo "Call gcc to generate bar.o from bar.c"
# Matches all .c files and creates them if they don't exist
%.c:
touch $@
clean:
rm -f foo.c bar.c
Here’s the more efficient way, using a static pattern rule:
# This Makefile uses less hard coded rules, via static pattern rules
objects = foo.o bar.o
all: $(objects)
.PHONY: all
# Syntax - targets ...: target-pattern: prereq-patterns ...
# In the case of the first target, foo.o, the target-pattern matches foo.o and sets the "stem" to be "foo".
# It then replaces that stem with the wilecard pattern in prereq-patterns
$(objects): %.o: %.c
echo "Call gcc to generate $@ from $<"
%.c:
touch $@
clean:
rm -f foo.c bar.c
Static Pattern Rules and Filter (Section 4.10)filter
can be used in Static pattern rules to match the correct files
obj_files = foo.elc bar.o lose.o
src_files = foo.el bar.c lose.c
all: $(obj_files)
$(filter %.o,$(obj_files)): %.o: %.c
echo "target: " $@ "prereq: " $<
$(filter %.elc,$(obj_files)): %.elc: %.el
echo "target: " $@ "prereq: " $<
%.c %.el:
touch %@
clean:
rm -f $(src_files)
Double-Colon Rules (Section 4.11)
Double-Colon Rules are rarely used, but allow the same target to run commands from multiple targets.
If these were single colons, an warning would be printed and only the second set of commands would run.
all: blah
blah::
echo "hello"
blah::
echo "hello again"
clean:
rm -f $(src_files)
Command Echoing/Silencing (Section 5.1)
Add an @ before a command to stop it from being printed
You can also run make with -s to add an @ before each line
all:
@echo "This make line will not be printed"
echo "But this will"
Command Execution (Section 5.2)
Each command is run in a new shell (or at least the affect is as such)
all:
cd ..
# The cd above does not affect this line, because each command is effectively run in a new shell
echo `pwd`
# This cd command affects the next because they are on the same line
cd ..;echo `pwd`
# Same as above
cd ..; \
echo `pwd`
Default Shell (Section 5.2)
The default shell is /bin/sh
. You can change this by changing the variable SHELL:
SHELL=/bin/bash
cool:
echo "Hello from bash"
DELETE_ON_ERROR (Section 5.4)
The make tool will stop running a rule (and will propogate back to prerequisites) if a command returns a nonzero exit status.DELETE_ON_ERROR
will delete the target of a rule if the rule fails in this manner. This will happen for all targets, not just the one it is before like PHONY. It’s a good idea to always use this, even though make does not for historical reasons.
.DELETE_ON_ERROR:
all: one two
one:
touch one
false
two:
touch two
false
Error handling with -k
, -i
, and i
(Section 5.4)
Add -k
when running make to continue running even in the face of errors. Helpful if you want to see all the errors of Make at once.
Add a “-“ before a command to suppress the error
Add “-i” to make to have this happen for every command.
Handling errors with -
and -i
(Section 5.4)
one:
# This error will be printed but ignored, and make will continue to run
-false
touch one
Interrupting or killing make (Section 5.5)
Note only: If you ctrl+c make, it will delete the newer targets it just made.
Recursive use of make (Section 5.6)
Recursively call a makefile. Use the special $(MAKE) instead of “make”
because it will pass the make flags for you and won’t itself be affected by them.
new_contents = "hello:\n\ttouch inside_file"
all:
mkdir -p subdir
echo $(new_contents) | sed -e 's/^ //' > subdir/makefile
cd subdir && $(MAKE)
clean:
rm -rf subdir
Use export
for recursive make (Section 5.6)
The export directive takes a variable and makes it accessible to sub-make commands.
In this example, “cooly” is exported such that the makefile in subdir can use it.
Note: export has the same syntax as sh, but they aren’t related (although similar in function)
new_contents = "hello:\n\\techo \$$(cooly)"
all:
mkdir -p subdir
echo $(new_contents) | sed -e 's/^ //' > subdir/makefile
@echo "---MAKEFILE CONTENTS---"
@cd subdir && cat makefile
@echo "---END MAKEFILE CONTENTS---"
cd subdir && $(MAKE)
# Note that variables and exports. They are set/affected globally.
cooly = "The subdirectory can see me!"
export cooly
# This would nullify the line above: unexport cooly
clean:
rm -rf subdir
Another export example (Section 5.6)
You need to export variables to have them run in the shell as well.
one=this will only work locally
export two=we can run subcommands with this
.PHONY: all
all:
@echo $(one)
@echo $$one
@echo $(two)
@echo $$two
EXPORT_ALL_VARIABLES
(Section 5.6)EXPORT_ALL_VARIABLES
does what you might expect
.EXPORT_ALL_VARIABLES:
new_contents = "hello:\n\techo \$$(cooly)"
cooly = "The subdirectory can see me!"
# This would nullify the line above: unexport cooly
all:
mkdir -p subdir
echo $(new_contents) | sed -e 's/^ //' > subdir/makefile
@echo "---MAKEFILE CONTENTS---"
@cd subdir && cat makefile
@echo "---END MAKEFILE CONTENTS---"
cd subdir && $(MAKE)
clean:
rm -rf subdir
Variables (Section 6.1)
Reference variables using ${} or $()
x = dude
.PHONY: all
all:
echo $(x)
echo ${x}
# Bad practice, but works
echo $x
Variables (Section 6.2)
Two flavors of variables:
- recursive (use
=
) - only looks for the variables when the command is used, not when it’s defined. - simply expanded (use
:=
) - like normal imperative programming – only those defined so far get expanded
# Recursive variable. This will print "later" below
one = one ${later_variable}
# Simply expanded variable. This will not print "later" below
two := two ${later_variable}
later_variable = later
.PHONY: all
all:
echo $(one)
echo $(two)
Variables (Section 6.2)
Simply expanded allows you to append to a variable. Recursive definitions will give an infinite loop error.
one = hello
# one gets defined as a simply expanded variable (:=) and thus can handle appending
one := ${one} there
.PHONY: all
all:
echo $(one)
Variables and ?=
(Section 6.2)
?= only sets variables if they have not yet been set
one = hello
one ?= will not be set
two ?= will be set
.PHONY: all
all:
echo $(one)
echo $(two)
Variables: watch out for end-of-line spaces (Section 6.2)
Spaces at the end of a line are not stripped, ones at the start are
To make a variable with a single space, have a variable guard
with_spaces = hello # with_spaces has many spaces after "hello"
after = $(with_spaces)there
nullstring =
space = $(nullstring) # Make a variable with a single space.
.PHONY: all
all:
echo "$(after)"
echo start"$(space)"end
Variables: Text replacement (Section 6.3)
You can text replace at the end of each space seperated word using $(var:a=b)
Note: don’t put spaces in between anything; it will be seen as a search or replacement term
Note: This is shorthand for using make’s patsubst
expansion function
foo := a.o b.o c.o
# bar becomes a.c b.c c.c
bar := $(foo:.o=.c)
.PHONY: all
all:
echo $(bar)
Variables: Text replacement (Section 6.3)
You can use % as well to grab some text!
foo := a.o b.o c.o
bar := $(foo:%.o=%)
.PHONY: all
all:
echo $(bar)
Undefined Variables (Section 6.5)
An undefined variable is actually an empty string!
.PHONY: all
all:
# Undefined variables are just empty strings!
echo $(nowhere)
Variable appending (Section 6.6)
Use += to append
foo := start
foo += more
.PHONY: all
all:
echo $(foo)
Command line arguments and override
(Section 6.7)
You can override variables that come from the command line by using “override”. Here we ran make with make some_option=hi
# Overrides command line arguments
override option_one = did_override
# Does not override command line arguments
option_two = not_override
.PHONY: all
all:
echo $(option_one)
echo $(option_two)
List of commands and define
(Section 6.8)
“define” is actually just a list of commands. It has nothing with being a function.
Note here that it’s a bit different than having a semi-colon between commands, because each is run in a seperate shell, as expected.
one = export blah="I was set!"; echo $$blah
define two
export blah=set
echo $$blah
endef
# One and two are different.
.PHONY: all
all:
@echo "This prints 'I was set'"
@$(one)
@echo "This does not print 'I was set' because each command runs in a seperate shell"
@$(two)
Target-specific variables (Section 6.10)
Variables can be assigned for specific targets
all: one = cool
.PHONY: all
all:
echo one is defined: $(one)
.PHONY: other
other:
echo one is nothing: $(one)
Pattern-specific variables (Section 6.11)
You can assign variables for specific target patterns
%.c: one = cool
blah.c:
echo one is defined: $(one)
.PHONY: other
other:
echo one is nothing: $(one)
Conditional if/else (Section 7.1)
foo = ok
all:
ifeq ($(foo), ok)
echo "foo equals ok"
else
echo "nope"
endif
Check if a variable is empty (Section 7.2)
nullstring =
foo = $(nullstring) # end of line; there is a space here
all:
ifeq ($(strip $(foo)),)
echo "foo is empty after being stripped"
endif
ifeq ($(nullstring),)
echo "nullstring doesn't even have spaces"
endif
ifdef
(Section 7.2)
ifdef does not expand variable references; it just sees if something is defined at all
bar =
foo = $(bar)
all:
ifdef foo
echo "foo is defined"
endif
ifdef bar
echo "but bar is not"
endif
Testing make flags with findstring
and MAKEFLAG
(Section 7.3)
Run this example with make -i
to see it print out the echo statement.
bar =
foo = $(bar)
all:
# Search for the "-i" flag. MAKEFLAGS is just a list of single characters, one per flag. So look for "i" in this case.
ifneq (,$(findstring i, $(MAKEFLAGS)))
echo "i was passed to MAKEFLAGS"
endif
Functions (Section 8.1)
Functions are mainly just for text processing. Call functions with $(fn, arguments)
or ${fn, arguments}
. You can make your own using the call builtin function. Make has a decent amount of builtin functions.
bar := ${subst not, totally, "I am not superman"}
all:
@echo $(bar)
Functions (Section 8.1)
If you want to replace spaces or commas, use variables
comma := ,
empty:=
space := $(empty) $(empty)
foo := a b c
bar := $(subst $(space),$(comma),$(foo))
all:
@echo $(bar)
Functions (Section 8.1)
Do NOT include spaces in the arguments after the first. That will be seen as part of the string.
comma := ,
empty:=
space := $(empty) $(empty)
foo := a b c
bar := $(subst $(space), $(comma) , $(foo))
all:
# Output is ", a , b , c". Notice the spaces introduced
@echo $(bar)
The foreach function (Section 8.4)
The foreach function looks like this: $(foreach var,list,text)
. It converts one list of words (seperated by speces) to another. var
is set to each word in list, and text
is expanded for each word.
This appends an exclamation after each word:
foo := who are you
# For each "word" in foo, output that same word with an exclamation after
bar := $(foreach wrd,$(foo),$(wrd)!)
all:
# Output is "who! are! you!"
@echo $(bar)
The if function (Section 8.5)if
checks if the first argument is nonempty. If so runs the second argument, otherwise runs the third.
foo := $(if this-is-not-empty,then!,else!)
empty :=
bar := $(if $(empty),then!,else!)
all:
@echo $(foo)
@echo $(bar)
The call function (Section 8.6)Call
: $(call variable,param,param)
Sets each of the params as $(1), $(2), etc.
$(0) is set as the variable name
sweet_new_fn = Variable Name: $(0) First: $(1) Second: $(2) Empty Variable: $(3)
all:
# Outputs "Variable Name: sweet_new_fn First: go Second: tigers Empty Variable:"
@echo $(call sweet_new_fn, go, tigers)
The shell function (Section 8.8)
shell - This calls the shell, but it replaces newlines with spaces!
all:
@echo $(shell ls -la) # Very ugly because the newlines are gone!
Arguments to make (Section 9)
There’s a nice list of options that can be run from make. Check out --dry-run
, --touch
, --old-file
.
You can have multiple targets to make, i.e. make clean run test
runs the ‘clean’ goal, then ‘run’, and then ‘test’.
Implicit Rules (Section 10)
Perhaps the most confusing part of make is the magic rules and variables that are made. Here’s a list of implicit rules:
- Compiling a C program:
n.o
is made automatically fromn.c
with a command of the form$(CC) -c $(CPPFLAGS) $(CFLAGS)
- Compiling a C++ program:
n.o
is made automatically fromn.cc
orn.cpp
with a command of the form$(CXX) -c $(CPPFLAGS) $(CXXFLAGS)
- Linking a single object file:
n
is made automatically fromn.o
by running the command$(CC) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS)
As such, the important variables used by implicit rules are:
CC
: Program for compiling C programs; default ccCXX
: Program for compiling C++ programs; default G++CFLAGS
: Extra flags to give to the C compilerCXXFLAGS
: Extra flags to give to the C++ compilerCPPFLAGS
: Extra flags to give to the C preprosessorLDFLAGS
: Extra flags to give to compilers when they are supposed to invoke the linker
CC = gcc # Flag for implicit rules
CFLAGS = -g # Flag for implicit rules. Turn on debug info
# Implicit rule #1: blah is built via the C linker implicit rule
# Implicit rule #2: blah.o is built via the C++ compilation implicit rule, because blah.cpp exists
blah: blah.o
blah.c:
echo "int main() { return 0; }" > blah.c
clean:
rm -f blah*
Automatic Variables (Section 10.5)
There are many automatic variables, but often only a few show up:
hey: one two
# Outputs "hey", since this is the first target
echo $@
# Outputs all prerequisites older than the target
echo $?
# Outputs all prerequisites
echo $^
touch hey
one:
touch one
two:
touch two
clean:
rm -f hey one two
10.5.3 Automatic Variables
Suppose you are writing a pattern rule to compile a ‘.c’ file into a ‘.o’ file: how do you write the ‘cc’ command so that it operates on the right source file name? You cannot write the name in the recipe, because the name is different each time the implicit rule is applied.
What you do is use a special feature of make
, the automatic variables. These variables have values computed afresh for each rule that is executed, based on the target and prerequisites of the rule. In this example, you would use ‘$@’ for the object file name and ‘$<’ for the source file name.
It’s very important that you recognize the limited scope in which automatic variable values are available: they only have values within the recipe. In particular, you cannot use them anywhere within the target list of a rule; they have no value there and will expand to the empty string. Also, they cannot be accessed directly within the prerequisite list of a rule. A common mistake is attempting to use $@
within the prerequisites list; this will not work. However, there is a special feature of GNU make
, secondary expansion (see Secondary Expansion), which will allow automatic variable values to be used in prerequisite lists.
Here is a table of 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 target member name, when the target is an archive member. See Archives. For example, if the target is foo.a(bar.o) then ‘$%’ is bar.o and ‘$@’ is foo.a. ‘$%’ is empty when the target is not an archive member.
$<
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).
$?
The names of all the prerequisites that are newer than the target, with spaces between them. If the target does not exist, all prerequisites will be included. For prerequisites which are archive members, only the named member is used (see Archives).
$^
The names of all the prerequisites, with spaces between them. For prerequisites which are archive members, only the named member is used (see Archives). A target has only one prerequisite on each other file it depends on, no matter how many times each file is listed as a prerequisite. So if you list a prerequisite more than once for a target, the value of $^
contains just one copy of the name. This list does not contain any of the order-only prerequisites; for those see the ‘$|’ variable, below.
$+
This is like ‘$^’, but prerequisites listed more than once are duplicated in the order they were listed in the makefile. This is primarily useful for use in linking commands where it is meaningful to repeat library file names in a particular order.
$|
The names of all the order-only prerequisites, with spaces between them.
$*
The stem with which an implicit rule matches (see How Patterns Match). If the target is dir/a.foo.b and the target pattern is a.%.b then the stem is dir/foo. The stem is useful for constructing names of related files.
In a static pattern rule, the stem is part of the file name that matched the ‘%’ in the target pattern.
In an explicit rule, there is no stem; so ‘$*’ cannot be determined in that way. Instead, if the target name ends with a recognized suffix (see Old-Fashioned Suffix Rules), ‘$*’ is set to the target name minus the suffix. For example, if the target name is ‘foo.c’, then ‘$*’ is set to ‘foo’, since ‘.c’ is a suffix. GNU make
does this bizarre thing only for compatibility with other implementations of make
. You should generally avoid using ‘$*’ except in implicit rules or static pattern rules.
If the target name in an explicit rule does not end with a recognized suffix, ‘$*’ is set to the empty string for that rule.
‘$?’ is useful even in explicit rules when you wish to operate on only the prerequisites that have changed. For example, suppose that an archive named lib is supposed to contain copies of several object files. This rule copies just the changed object files into the archive:
lib: foo.o bar.o lose.o win.o ar r lib $?
Of the variables listed above, four have values that are single file names, and three have values that are lists of file names. These seven have variants that get just the file’s directory name or just the file name within the directory. The variant variables’ names are formed by appending ‘D’ or ‘F’, respectively. The functions dir
and notdir
can be used to obtain a similar effect (see Functions for File Names). Note, however, that the ‘D’ variants all omit the trailing slash which always appears in the output of the dir
function. Here is a table of the variants:
‘$(@D)’
The directory part of the file name of the target, with the trailing slash removed. If the value of ‘$@’ is dir/foo.o then ‘$(@D)’ is dir. This value is . if ‘$@’ does not contain a slash.
‘$(@F)’
The file-within-directory part of the file name of the target. If the value of ‘$@’ is dir/foo.o then ‘$(@F)’ is foo.o. ‘$(@F)’ is equivalent to ‘$(notdir $@)’.
‘$(*D)’
‘$(*F)’
The directory part and the file-within-directory part of the stem; dir and foo in this example.
‘$(%D)’
‘$(%F)’
The directory part and the file-within-directory part of the target archive member name. This makes sense only for archive member targets of the form archive(member) and is useful only when member may contain a directory name. (See Archive Members as Targets.)
‘$(<D)’
‘$(<F)’
The directory part and the file-within-directory part of the first prerequisite.
‘$(^D)’
‘$(^F)’
Lists of the directory parts and the file-within-directory parts of all prerequisites.
‘$(+D)’
‘$(+F)’
Lists of the directory parts and the file-within-directory parts of all prerequisites, including multiple instances of duplicated prerequisites.
‘$(?D)’
‘$(?F)’
Lists of the directory parts and the file-within-directory parts of all prerequisites that are newer than the target.
Note that we use a special stylistic convention when we talk about these automatic variables; we write “the value of ‘$<’”, rather than “the variable <
” as we would write for ordinary variables such as objects
and CFLAGS
. We think this convention looks more natural in this special case. Please do not assume it has a deep significance; ‘$<’ refers to the variable named <
just as ‘$(CFLAGS)’ refers to the variable named CFLAGS
. You could just as well use ‘$(<)’ in place of ‘$<’.