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.
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
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.
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
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
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)
Related
I am trying to statically link two COBOL files together using GnuCobol (Windows 10) following the example listed here: https://open-cobol.sourceforge.io/historical/open-cobol/Static-Linking.html but cannot seem to get it to work.
I am running the following:
cobc -free -c InterpFunc.cob
cobc -free -c -fmain Integrator.cob
cobc -x -o .\\dist\\integrator Integrator.o InterpFunc.o
The '.o' files compile correctly, but the binary never builds with the following errors:
H:\Programs\COBAL\cobc\bin\cobc.exe: unrecognized option '-fmain'
h:/programs/cobal/cobc/bin/../lib/gcc/mingw32/6.3.0/../../../libmingw32.a(main.o):(.text.startup+0xa0): undefined reference to `WinMain#16'
collect2.exe: error: ld returned 1 exit status
The terminal process terminated with exit code: 1
I have tried a few different things such as leaving out the '-fmain' or leaving out the '-x', but all seem to produce different errors.
Is this perhaps an issue with my compiler/system setup or am I misunderstanding how to statically link files?
I'm quite sure you do not use a compiler matching that old documentation (having "historical" in its URL). I'm quite sure it'll work the way the current manual says:
The easiest way of combining multiple files is to compile them into a single executable.
One way is to compile all the files in one command:
$ cobc -x -o prog main.cob subr1.cob subr2.cob
Another way is to compile each file with the option -c, and link them at the end. > The top-level program must be compiled with the option -x.
$ cobc -c subr1.cob
$ cobc -c subr2.cob
$ cobc -c -x main.cob
$ cobc -x -o prog main.o subr1.o subr2.o
we have a huge project with very time consuming sub-tasks running on Unix. The whole make process runs multiple hours. So building in parallel is essential for us. The expensive jobs are preformed on a remote server via ssh. Everything works fine. But I'm afraid that accidentally a team member could forget to specify the number for the -j flag. The --load-average flag uses the load on the local server so no matter how busy the remote server is it would generate hundreds of sub-tasks on the remote server which would slow down the entire company. Please note that I don't have admin rights on either server. We could also live with limiting the number of jobs (hardcoded).
Thanks a lot in Advance
Karl
If you have GNU make 4.2 or better, you can check that the user didn't give a raw -j by looking at MAKEFLAGS:
ifeq (-j,$(filter -j,$(MAKEFLAGS)))
$(error You cannot use -j without specifying a number of jobs)
endif
If you have an earlier version of GNU make you can do this by checking if both -j was given AND the jobserver is active:
ifneq (,$(filter -j,$(MAKEFLAGS)))
ifneq (,$(filter --jobserver-%,$(MAKEFLAGS)))
$(error You cannot use -j without specifying a number of jobs)
endif
endif
(this version will work for newer versions as well).
It's possible this won't work with versions of GNU make <4.0; I didn't test it.
For older make versions you can consider the following. That is not pure makefile way, but works.
Solution
ifneq (0,$(shell ps --pid $$PPID -o args= | grep --perl-regexp -c '^make.*-j(?!\s*[0-9]+\b)'))
$(error You cannot use -j without specifying a number of jobs)
endif
# Or oneliner:
#$(if $(shell ps --pid $$PPID -o args= | grep --perl-regexp -c '^make.*-j(?!\s*[0-9]+\b)' | grep -v '^0$$'),$(error You cannot use -j without specifying a number of jobs))
# Below part just for testing
all a 1: ; #exit 0
Testing
$ make
$ make a
$ make 1
$ make -j1
$ make -j1 a
$ make -j1 1
$ make -j 1
$ make -j 1a
Makefile:6: *** You cannot use -j without specifying a number of jobs. Stop.
$ make -j a
Makefile:6: *** You cannot use -j without specifying a number of jobs. Stop.
$ make a -j
Makefile:6: *** You cannot use -j without specifying a number of jobs. Stop.
$ make 1 -j a
Makefile:2: *** You cannot use -j without specifying a number of jobs. Stop.
How it works
It is taking information from parent process (that is make process) using ps utility, and later filter out arguments using grep (regular expression is more or less: find make command with -j parameter that is not followed by a number).
Based on to the answer from Kuchara above I needed the following enhancement, since I call some subsystems with:
$(MAKE) $(MFLAGS) ...
and this will add the jobserver and remove the number from the -j flag in the subsystem:
yy:
#echo yy mflags: $(MFLAGS)
#echo yy ps: $(shell ps --pid $$PPID -o args=)
$(MAKE) $(MFLAGS) zz
zz:
#echo zz mflags: $(MFLAGS)
#echo zz ps: $(shell ps --pid $$PPID -o args=)
Testing:
make -j 3 yy
yy mflags: - --jobserver-fds=3,4 -j
yy ps: make -j 3 yy
zz mflags: -w --jobserver-fds=3,4 - --jobserver-fds=3,4 -j
zz ps: make - --jobserver-fds=3,4 -j zz
I needed to combine the answers from MadScientist and Kuchara:
ifneq (0,$(shell ps --pid $$PPID -o args= | grep --perl-regexp -c '^make.*-j(?!\s*[0-9]+\b)'))
ifeq (0,$(shell ps --pid $$PPID -o args= | grep --perl-regexp -c '^make.*--jobserver'))
$(error You cannot use -j without specifying a number of jobs)
endif
endif
Thanks a lot again at both for your great ideas.
I'm having an issue with a makefile that I'm writing for a data processing pipeline. It's saying that there is no rule to make a target, but I don't understand why because I have such a rule. Here is the makefile:
1 ifdef $(BROAD)
2 target=$(MARK)_peaks.broadPeak
3 else
4 target=$(MARK)_peaks.narrowPeak
5 endif
6
7 all: $(target)
8
9 $(MARK)_peaks.broadPeak : $(MARK).filtered.bam $(INPUT).filtered.bam
10 macs2 callpeak -g $(GSIZE) -q 0.05 -c $(INPUT).filtered.bam -t $(MARK).filtered.bam -f BAM -n $(MA RK) -B --broad
11
12 $(MARK)_peaks.narrowPeak : $(MARK).filtered.bam $(INPUT).filtered.bam
13 macs2 callpeak -g $(GSIZE) -q 0.01 -c $(INPUT).filtered.bam -t $(MARK).filtered.bam -f BAM -n $(MA RK) -B
14
15 %.filtered.bam : %.duplicate-marked.bam
16 samtools view -b -q 15 $< > $#
17
18 %.duplicate-marked.bam : %.sorted.bam
19 picard-tools MarkDuplicates INPUT=$< OUTPUT=$# METRICS_FILE=output.dup_metrics REMOVE_DUPLICATES=f alse ASSUME_SORTED=true VALIDATION_STRINGENCY=SILENT
And the output with debugging on is:
$ make --debug=verbose --dry-run -f chipseq-pe.makefile MARK=H3K27me3 INPUT=input ASSEMBLY=/home/ckern/galGal4.dna_sm.fa GSIZE=1.057e9 BROAD=Y
GNU Make 3.81
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
This program built for x86_64-pc-linux-gnu
Reading makefiles...
Reading makefile `chipseq-pe.makefile'...
Updating goal targets....
Considering target file `all'.
File `all' does not exist.
Considering target file `H3K27me3_peaks.narrowPeak'.
File `H3K27me3_peaks.narrowPeak' does not exist.
Considering target file `H3K27me3.filtered.bam'.
File `H3K27me3.filtered.bam' does not exist.
Finished prerequisites of target file `H3K27me3.filtered.bam'.
Must remake target `H3K27me3.filtered.bam'.
make: *** No rule to make target `H3K27me3.filtered.bam', needed by `H3K27me3_peaks.narrowPeak'. Stop.
I have a pattern rule to make any *.filtered.bam file, so I don't understand why it's saying there is no rule to make that target.
Make is one of those technologies where I go back and forth between whether or not I understand it.
This is certainly one instance where I know I must be doing something wrong, since Make was developed to make these tasks less repetitive.
all: 24.1 24.2 24.3
24.1:
evm install emacs-24.1-bin || true
emacs --version
emacs --batch -L . -l ert -l test/tests.el -f ert-run-tests-batch-and-exit
24.2:
evm install emacs-24.2-bin || true
emacs --version
emacs --batch -L . -l ert -l test/tests.el -f ert-run-tests-batch-and-exit
24.3:
evm install emacs-24.3-bin || true
emacs --version
emacs --batch -L . -l ert -l test/tests.el -f ert-run-tests-batch-and-exit
How can I edit this Makefile to only lay out the test sequence once but be able to test against multiple versions?
Try this:
VERSIONS = 24.1 24.2 24.3
all :: $(VERSIONS)
$(VERSIONS) ::
evm install emacs-$#-bin || true
emacs --version
emacs --batch -L . -l ert -l test/tests.el -f ert-run-tests-batch-and-exit
The :: is a special kind of rule, that puts the target as phony (and has other properties, too).
How about:
all: 24.1 24.2 24.3
%:
evm install emacs-$#-bin || true
emacs --version
emacs --batch -L . -l ert -l test/tests.el -f ert-run-tests-batch-and-exit
I have to admit that turning to ‘last resort’ strategies always makes me queasy: it feels as if going against the grain of the tool. BSD make on the other hand allows explicit looping constructs, hence getting rid of the repetitive rules is straightforward:
VERSIONS = 24.1 24.2 24.3
all: ${VERSIONS}
.for VERSION in ${VERSIONS}
${VERSION}:
evm install emacs-${VERSION}-bin || true
emacs --version
emacs --batch -L . -l ert -l test/tests.el -f ert-run-tests-batch-and-exit
.endfor
I’m well aware that this solution almost surely won’t help you at all; switching make implementation is almost certainly out of the question. BSD make is sorely underrepresented though, so I thought it might be useful for other people to have an alternative approach documented.
As MadScientist correctly pointed out, GNU make doesn’t support any of the ‘dot constructs’ like .for, which are special to BSD make. However, this question suggests a few other looping techniques that might be applicable to GNU make: How to write loop in a Makefile?
I have the following code to untar all the files in a directory and move it to build directory. If I call make multiple times, it tries to execute "build" target everytime even if build directory already exists. Has anyone comes across this?
I found this question but it is not the same.
Makefile always running target
OS: Ubuntu 12.04
Program: GNU Make 3.81
build: mkBuildDir untar
chmod 700 build
.PHONY: mkBuildDir untar
mkBuildDir:
mkdir build
untar: *.tar.gz
for prefix in *.tar.gz; do \
tar xvf $$prefix --directory=build; \
done
clean:
rm -Rf build
This is pretty much the same as the question you've linked. You never create a file called mkBuildDir, so it's always out-of-date, so build is always out of date.
Your mkBuildDir target isn't doing anything useful (though I presume this is a cut-down makefile). If instead you did
# it'd be better to list the TARFILES explicitly, though this will probably work
TARFILES=`ls *.tar.gz`
all: build untar
build: $(TARFILES)
test -d build || mkdir build
chmod 700 build
for prefix in $(TARFILES); do \
tar xvf $$prefix --directory=build; \
done
clean:
rm -Rf build
that would probably accomplish what you're looking for.
Having too many phony targets in a Makefile is usually a makefile 'code smell'. They are rarely the best/idiomatic way of doing things.