0
votes

I am using make with the version below:

$ make -v
GNU Make 4.3
Built for x86_64-pc-cygwin
Copyright (C) 1988-2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

I have a makefile with multiple targets as below. I use the “& :” syntax from the manual https://www.gnu.org/software/make/manual/html_node/Multiple-Targets.html

hello.asm.mk

src_prefix = ../../src/hello/


hellomain.s sayhello.s &: $(src_prefix)hellomain.c $(src_prefix)sayhello.c
    gcc -S $(src_prefix)hellomain.c -o hellomain.s
    gcc -S $(src_prefix)sayhello.c -o sayhello.s

.PHONY: clean
clean : 
    -rm hellomain.s sayhello.s

But it seems that it does not work, as below.

$ ll
total 1
-rwxrwx---+ 1 Payne None 250 2020-08-29 17:48:54.837841300 +0800 hello.asm.mk


$ make -f hello.asm.mk
gcc -S ../../src/hello/hellomain.c -o hellomain.s
gcc -S ../../src/hello/sayhello.c -o sayhello.s


$ ll
total 3
-rwxrwx---+ 1 Payne None 250 2020-08-29 17:48:54.837841300 +0800 hello.asm.mk
-rw-rw-r--+ 1 Payne None 463 2020-08-30 18:21:22.863042900 +0800 hellomain.s
-rw-rw-r--+ 1 Payne None 449 2020-08-30 18:21:23.045053400 +0800 sayhello.s


$ rm sayhello.s


$ ll
total 2
-rwxrwx---+ 1 Payne None 250 2020-08-29 17:48:54.837841300 +0800 hello.asm.mk
-rw-rw-r--+ 1 Payne None 463 2020-08-30 18:21:22.863042900 +0800 hellomain.s


$ make -f hello.asm.mk
make: 'hellomain.s' is up to date.

Could someone give me any advice? I am new to makefile and am still learning it.

3

3 Answers

2
votes

In the first place, make recognizes a default target, not a default rule. The default target is the first one appearing in the file that does not start with a . and a capital letter. If you run make without designating a target to build, then it assumes you want the default target built, and in your case, that is hellomain.s. (And only hellomain.s, even though that appears in a rule that designates more than one target.)

The behavior that make exhibits for you is thus utterly normal, notwithstanding any use of a grouped-target rule (see below). You run make without designating a target, so it interprets you to want to build the default target. That target already exists and is up to date. That another target described in the makefile is missing is irrelevant. If you want to rebuild it with your current makefile then you could tell make so explicitly:

make sayhello.s

Now a few words about grouped-target rules. Note well that support for these is

  1. specific to rather recent versions of GNU make, and not available on most make implementations, including, for example, the versions of GNU make included in many Linux distributions as of the time of this writing.

  2. not intended for the use to which you are putting it.

Grouped-target rules address a longstanding issue of make's design: how to describe targets that are unavoidably created together. The canonical example would probably be the outputs of yacc or bison: a C source file and corresponding header file, both generated together by one run of the same program. You probably haven't yet the experience with make to appreciate how hard it is to describe that properly to traditional makes, but it is simple in GNU make 4.3 and later.

You have created a similar situation artificially. Don't do that. If you want to build multiple targets via one rule, then the idiomatic way to do it is to give them their own rules, and to name them as prerequisites to the same rule. Moreover, it is conventional, albeit not obligatory, to name the default target "all" in such cases. Example:

src_prefix = ../../src/hello

all: hellomain.s sayhello.s

hellomain.s : $(src_prefix)/hellomain.c
    gcc -S $(src_prefix)/hellomain.c -o hellomain.s

sayhello.s : $(src_prefix)/sayhello.c
    gcc -S $(src_prefix)/sayhello.c -o sayhello.s

clean : 
    -rm -f hellomain.s sayhello.s

.PHONY : all clean

Not that that makefile really exhibits good form generally. I've kept it pretty close to your original for didactic purposes, but if I were writing it, and assuming GNU make, then I would probably go with something more like this:

src_prefix = ../../src/hello

# A variable naming the targets, so I don't need to repeat the list, or risk
# different copies getting out of sync:
ASSEMBLY_TARGETS = hellomain.s sayhello.s

# specify the assembler command as a variable, near the top, to enable it to be
# easily changed:
AS = gcc -S

# default target named "all", with all targets I want to build as its prerequisites    
all: $(ASSEMBLY_TARGETS)

# One pattern rule covering both targets.  Pattern rules are GNU-specific, but
# I already stipulated that I'm writing for GNU make
%.s : $(src_prefix)/%.c
    $(AS) $^ -o $@

# The clean rule also relies on the ASSEMBLY_TARGETS variable, so that we don't need
# to repeat the target list explicitly here.
clean : 
    -rm -f $(ASSEMBLY_TARGETS)

.PHONY : all clean
0
votes

You should always provide the version of GNU make you're using and the operating system you're running on.

My best guess is that you're using an older version of GNU make that doesn't support the &: syntax. You need GNU make 4.3 to be able to use this feature.

It's always best to read the version of the GNU make manual that comes with your operating system, since that will be the version associated with the version of GNU make you're using. Also you can check the NEWS file for when features were added:

http://git.savannah.gnu.org/cgit/make.git/tree/NEWS

0
votes

One example using a pattern rule:

SRC_DIR := ../../src/hello
ASM_TARGETS := hellomain.s sayhello.s

.PHONY: all clean

all: $(ASM_TARGETS)

# build .s file from .c file placed in a specified source dir
%.s: $(SRC_DIR)/%.c
    gcc -S $^ -o $@

clean:
    rm -f $(ASM_TARGETS)