Six, multiple goals

There can be more than one target in the rule of a Makefile, which supports multiple targets, and it is possible that our multiple targets depend on a file at the same time and generate commands that are broadly similar. So we can combine them. , of course, the rules of the formation of multiple target execute commands are the same, it may bring us trouble, but we can use an automatic variable “$@” (about the automatic variables, will be about later), the variable said all the targets set in the current rules, that may be abstract, let’s look at an example.

bigoutput littleoutput : text.g
    generate text.g -$(subst output,,$@) > $@
Copy the code

The above rules are equivalent to:

bigoutput : text.g
    generate text.g -big > bigoutput
littleoutput : text.g
    generate text.g -little > littleoutput
Copy the code

-$(subst output, $@) $(subst output, $@) $(subst output, $@) Functions will be discussed later. “$@” represents a collection of targets, just like an array. “$@” takes the targets in turn and executes the command.

Static mode

Static mode makes it easier to define multi-objective rules, making our rules more flexible and flexible. Let’s look at the syntax first:

<targets ... >: <target-pattern>: <prereq-patterns ... > <commands> ...Copy the code

Targets defines a series of target files that can have wildcards. It’s a collection of targets.

Target-parrtern specifies the targets mode, that is, the target set mode.

Prereq-parrterns is a dependency pattern for the target, which again relies on the target definition for the pattern formed by target-parrtern.

That’s probably not clear enough to describe these three things, but let me give you an example.

If our

is defined as “%. O”, which means that all of our

collections end with “.o “, and if our

is defined as “%.c”, is a second definition of the target set formed by taking the “%” of the pattern (that is, removing the [. O] ending) and adding the [. C] ending to it.

Therefore, our “target mode” or “dependent mode” should have the “%” character. If your file name has “%”, you can use a backslash “/” to escape to indicate the real “%” character.

Look at an example:

objects = foo.o bar.o
all: $(objects)
$(objects): %.o: %.c
    $(CC) -c $(CFLAGS) $< -o $@
Copy the code

In the above example, we specify that our target is fetched from $object. “%. O” indicates that we want all targets ending in “.o “, i.e. “foo. Add a “. C “suffix to it, so our dependency target is” foo.c bar.c “. The “$<” and “$@” in the command are automated variables, with “$<” representing all dependent target sets (i.e. “foo.c bar.c”) and “$@” representing target sets (i.e. “foo.o bar.o”). Thus, when expanded, the above rule is equivalent to the following rule:

foo.o : foo.c
    $(CC) -c $(CFLAGS) foo.c -o foo.o
bar.o : bar.c
    $(CC) -c $(CFLAGS) bar.c -o bar.o
Copy the code

If we had hundreds of “%. O”, it would be much more efficient to write a bunch of rules using simple “static mode rules”. “Static mode rules” can be used flexibly and can be a powerful feature if used well. Here’s another example:

files = foo.elc bar.o lose.o
$(filter %.o,$(files)): %.o: %.c
    $(CC) -c $(CFLAGS) $< -o $@
$(filter %.elc,$(files)): %.elc: %.el
    emacs -f batch-byte-compile $<
Copy the code

$(filter %. O,$(files)) means to call the filter function of the Makefile to filter the “$filter” set as long as the content in the mode of “%. O”. I don’t need to say much about its content. This example demonstrates greater flexibility in makefiles.

Automatically generate dependencies

In makefiles, our dependencies might need to include a series of headers. For example, if we had a “#include “defs.h” in main.c, our dependencies would read:

main.o : main.c defs.h
Copy the code

However, if it’s a large project, you’ll need to know which C files contain which headers, and you’ll need to carefully modify makefiles when adding or removing headers, which is a very non-maintainable task.

To avoid this onerous and error-prone thing, we can use a feature of the C/C++ compiler.

Most C/C++ compilers support a “-m” option that automatically finds headers contained in source files and generates a dependency. For example, if we execute the following command:

cc -M main.c
Copy the code

The output is:

main.o : main.c defs.h
Copy the code

So the compiler automatically generates the dependencies, so you don’t have to write the dependencies of several files by hand, but the compiler automatically generates them. A word of caution: if you use the GNU C/C++ compiler, you use the “-mm” argument; otherwise, the “-m” argument will include some of the standard library headers.

GCC -m main.c

main.o: main.c defs.h /usr/include/stdio.h /usr/include/features.h / /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h / / usr/lib/GCC - lib/i486 - suse Linux / 2.95.3 / include/stddef. H / / usr/include/bits/types. The h/usr/include/bits/pthreadtypes h / /usr/include/bits/sched.h /usr/include/libio.h / /usr/include/_G_config.h /usr/include/wchar.h / The/usr/include/bits/wchar. H/usr/include/gconv. H / / usr/lib/GCC - lib/i486 - suse Linux / 2.95.3 / include/stdarg. H / /usr/include/bits/stdio_lim.hCopy the code

Gcc-mm main.c:

main.o: main.c defs.h
Copy the code

So how does this function of the compiler relate to our Makefile?

Because then our makefiles will also be regenerated from these sources, making the Makefiles dependent on the source files themselves?

This isn’t practical, but there are other ways to do it in a roundabout way.

The GNU organization recommends putting the dependencies automatically generated by the compiler for each source file in a single file. For each “name.c” file, a “name.d” Makefile is generated, and the [.d] file contains the dependencies for the corresponding [.c] file.

So we can write out the dependencies for the [.c] and [.d] files and have make update or generate the [.d] files automatically and include them in our main Makefile so that we can automatically generate the dependencies for each file.

Here, we present a pattern rule to generate [.d] files:

%.d: %.c
    @set -e; rm -f $@; \
    $(CC) -M $(CPPFLAGS) $< > $@.; \
    sed 's/\($*\)\.o[ :]*/\1.o $@ : /g' < $@.> $@; \
    rm -f $@.
Copy the code

This rule means that all [.d] files depend on [.c] files,"The rm - f $@"[.d] delete all dependent files for each dependent file.If there is a C file named name.c, then “%” is “name “.”.” means a random number. The second line might generate a file named name.d.12345. See the usage documentation for the sed command. The fourth line is to delete temporary files.

In a nutshell, what this pattern does is add the dependencies of the [.d] file to the compiler-generated dependencies.

main.o : main.c defs.h
Copy the code

To:

main.o main.d : main.c defs.h
Copy the code

As a result, our [.d] files will be automatically updated and generated, and of course you can add not only dependencies to the [.d] file, but also generated commands, so that each [.d] file contains a dead rule. Once we’ve done this, we’ll then put these auto-generated rules into our main Makefile. We can use the Makefile “include” command to introduce other makefiles (as described above), for example:

sources = foo.c bar.c
include $(sources:.c=.d)
Copy the code

$(sources:.c=.d); $(sources) =.c=.d; $(sources) =.d; Of course, you have to pay attention to the order, because include loads the file once, and the target in the first [.d] file to load becomes the default target.


The original link: blog.csdn.net/haoel/artic…