I've got two targets that are almost the same:
# install node modules from package.json and bring npm-shrinkwrap.json up to date
npm-install:
ifndef SHRINKWRAP_BIN
$(error `npm-shrinkwrap` not found. Please run `sudo npm install -g npm-shrinkwrap`)
endif
$(NPM_BIN) install --no-shrinkwrap --loglevel=error --no-optional
$(NPM_BIN) prune --no-shrinkwrap --loglevel=error
$(NPM_BIN) dedupe --no-shrinkwrap --loglevel=error
npm-shrinkwrap --dev
touch $(NPM_TIMESTAMP)
# update npm dependencies to their latest version given the semver constraints and re-write npm-shrinkwrap file
npm-update:
ifndef SHRINKWRAP_BIN
$(error `npm-shrinkwrap` not found. Please run `sudo npm install -g npm-shrinkwrap`)
endif
$(NPM_BIN) update --save-dev --loglevel=error --no-optional
$(NPM_BIN) prune --no-shrinkwrap --loglevel=error
$(NPM_BIN) dedupe --no-shrinkwrap --loglevel=error
npm-shrinkwrap --dev
touch $(NPM_TIMESTAMP)
Is there any way I can remove some duplication by having them both call another target? I can't just add a prerequisite with the common part to both of them because prerequisites are ran before the commands and the install/update bit has to be ran first (before prune/dedupe/shrinkwrap).
Assuming I read this correctly and the difference between the two is just the word install in npm-install and update in npm-update then the solution here is to just use the target (or part of it) in the commands you are running.
Something like this:
# install node modules from package.json and bring npm-shrinkwrap.json up to date
npm-install npm-update:
ifndef SHRINKWRAP_BIN
$(error `npm-shrinkwrap` not found. Please run `sudo npm install -g npm-shrinkwrap`)
endif
$(NPM_BIN) $(subst npm-,,$#) --no-shrinkwrap --loglevel=error --no-optional
$(NPM_BIN) prune --no-shrinkwrap --loglevel=error
$(NPM_BIN) dedupe --no-shrinkwrap --loglevel=error
npm-shrinkwrap --dev
touch $(NPM_TIMESTAMP)
You could also use $(word 2,$(subst -, ,$#)) or $(patsubst npm-%,%,$#) or as the above doesn't switch --no-shrinkwrap for --save-dev you could use something like this (or combine the $# usage above with an arg variable like this):
npm-install: command := install
npm-install: arg := --no-shrinkwrap
npm-update: command := update
npm-update: arg :=--save-dev
npm-install npm-update:
....
$(NPM_BIN) $(command) $(arg) --loglevel=error --no-optional
....
I have several ideas about this. The first is to just use text processing within the Makefile to reduce the duplication explicitly. Define a multi-line macro and then call it.
define NPM_COMMON_STEPS
$(NPM_BIN) prune ...
$(NPM_BIN) dedupe ...
...
endef
Since this has no parameters, we don't have to use the $(call ...) operator. Just simply, in the recipe we call it as:
$(NPM_COMMON_STEPS)
Then there are other ways. You could make the dummy prerequisite target handle all the logic, and just switch some part of it around based on who is "calling". How can we know that? Why, via target-specific variables!
This can be illustrated via a complete Makefile:
.PHONY: all common-target a-target b-target
all: a-target b-target
common-target:
$(if $(CALLED_FOR_A), echo called for a-target)
$(if $(CALLED_FOR_B), echo called for b-target)
echo common recipe
a-target: CALLED_FOR_A := y
a-target: common-target
b-target: CALLED_FOR_B := y
b-target: common-target
Tests:
$ make
echo called for a-target
called for a-target
echo common recipe
common recipe
$ make a-target
echo called for a-target
called for a-target
echo common recipe
common recipe
$ make b-target
echo called for b-target
called for b-target
echo common recipe
common recipe
As you can see, there is a drawback here in that if we update the target all, then GNU Make only executes the shared common rule once. When that rule has been run on behalf of a-target, it's considered updated and isn't run for b-target.
This doesn't matter if we don't update both targets in the same run, but all the same, it's a potential snag:
$ make a-target b-target
echo called for a-target
called for a-target
echo common recipe
common recipe
make: Nothing to be done for `b-target'.
Thus I'd think twice before using this type of trick. If you would never do an npm-update and npm-install in the same invocation, then this could be used.
Here is a complete sample of the textual substitution solution:
.PHONY: all a-target b-target
all: a-target b-target
define COMMON
echo common recipe
endef
define COMMON_WITH_ARG
echo common recipe with arg 1 == $(1)
endef
a-target:
echo a-target
$(COMMON)
$(call COMMON_WITH_ARG,a)
echo a-done
b-target:
echo b-target
$(COMMON)
$(call COMMON_WITH_ARG,b)
echo b-done
Run:
$ make
echo a-target
a-target
echo common recipe
common recipe
echo common recipe with arg 1 == a
common recipe with arg 1 == a
echo a-done
a-done
echo b-target
b-target
echo common recipe
common recipe
echo common recipe with arg 1 == b
common recipe with arg 1 == b
echo b-done
b-done
Related
I try to use dkms to install realtek r8125 driver.
The official driver autorun script use this to compile source code
make $# all 1>>log.txt || exit 1
the top level makefile is
KFLAG := 2$(shell uname -r | sed -ne 's/^2\.[4]\..*/4/p')x
all: clean modules install
modules:
ifeq ($(KFLAG),24x)
$(MAKE) -C src/ -f Makefile_linux24x modules
else
$(MAKE) -C src/ modules
endif
clean:
ifeq ($(KFLAG),24x)
$(MAKE) -C src/ -f Makefile_linux24x clean
else
$(MAKE) -C src/ clean
endif
install:
ifeq ($(KFLAG),24x)
$(MAKE) -C src/ -f Makefile_linux24x install
else
$(MAKE) -C src/ install
endif
my dkms.conf is
PACKAGE_NAME="realtek-r8125"
PACKAGE_VERSION="9.009.01"
MAKE="'make' $# all"
CLEAN="'make' clean"
BUILT_MODULE_NAME[0]="r8125"
BUILT_MODULE_LOCATION[0]="src"
DEST_MODULE_LOCATION[0]="/kernel/driver/net/ethernet/realtek"
AUTOINSTALL="yes"
REMAKE_INITRD=no
when I build this project, I get an error
make: *** No rule to make target 'CLEAN'. Stop.
when I change dkms.conf like this
PACKAGE_NAME="realtek-r8125"
PACKAGE_VERSION="9.009.01"
MAKE="'make' all"
CLEAN="'make' clean"
BUILT_MODULE_NAME[0]="r8125"
BUILT_MODULE_LOCATION[0]="src"
DEST_MODULE_LOCATION[0]="/kernel/driver/net/ethernet/realtek"
AUTOINSTALL="yes"
REMAKE_INITRD=no
it run success
why DKMS say "No rule to make target 'CLEAN'" even if there is a clean target in makefile
what is the difference between make all and make $# all
I'm pretty much a makefile novice so I don't even know the terminology I'm looking for. I'm trying to build the latest valgrind release alongside other 3rdparty tools my company uses. I basically have
../3rdparty/
/Makefile <- What gets called to recursively build everything
/valgrind/Makefile <- What I'm pasting below
/valgrind/valgrind-3.16.1/Makefile <- what gets configure'd
So I can go into ../3rdparty/valgrind/valgrind-3.16.0/ and call...
./configure --host=arm-linux-gnueabihf
make
...and have it succeed without issue. However, when I try to build it from the Makefile in ../3rdparty/valgrind I get errors due to configuration generated variables being lost. I can see it clean up everything, I can see configuration succeed, but when the make process starts I get warnings that aren't seen using the process above.
cc1: warning: switch -mcpu=cortex-a8 conflicts with -march=armv7ve switch
Which eventually leads to an error
<command-line>:0:5: error: expected identifier or β(β before numeric constant pub_core_basics.h:78:12: note: in expansion of macro βARMβ
I basically copy pasted what is used for other 3rd party libs in our codebase and made changes where applicable....
include ../common.mak
VERSION=valgrind-3.16.1
all: configure build #install
configure: configure_$(TARGET)
configure_$(TARGET):
$(MAKE) distclean
#echo -e "\nConfiguring $(VERSION) for $(TARGET)...\n"
pushd $(VERSION)/ \
&& bash configure --host=${TARGET} \
&& popd
touch $#
#echo -e "\nConfiguration $(VERSION) complete for $(TARGET)...\n"
build: configure
$(MAKE) "-SC" $(VERSION)
install: build
$(MAKE) -SC $(VERSION) $#
# call folder's makefile targets verbatim
clean distclean:
test -f $(VERSION)/Makefile && $(MAKE) -SC $(VERSION) $# || :
rm -f configure_*
uninstall:
$(MAKE) -SC $(VERSION) $#
I'm guessing it's a one line thing, but I'd also be interested in any docs or websites that would be
useful. A lot of makefile tutorials go over the same super basic stuff.
I want
I am trying to compile some latex that has snippets of python code and the output of those snippets. I need the document to be always updated with the last changes made in the snippets and in their outputs, so the idea is maintain a makefile that could monitor this changes and generate the updated outputs.
So if I modify the file a/11.py, I want make to execute it to generate a new output a/11.out.
I have
This is my makefile
DOC=myPdf
STY=st
PY_DIR=a/
TEX=pdflatex -shell-escape -interaction=batchmode -file-line-error
$(DOC).pdf: $(PY_DIR)11.out $(PY_DIR)12.out $(DOC).tex $(STY).sty
$(TEX) $(DOC).tex
$(PY_DIR)11.out:
$(cd PY_DIR && python3 11.py > 11.out)
$(PY_DIR)12.out:
$(cd PY_DIR && python3 12.py > 12.out)
.PHONY: clean
clean:
rm *.aux *.log > /dev/null 2>&1
I wonder
Even when the file a/11.out doesn't exist, and I instruct make a/11.out make says: make: 'a/11.out' is up to date. (I am still learning make, so I probably have more mistakes).
I saw
Make in subfolder, but because I am not using $(MAKE), I cannot use it.
Similar question, but I don't think it is the same.
Thank you for your time :)
Update
This is my new version, based in the answer of Renaud (thanks for your help), some python scripts are intended to output text (xxxt.py), and others to plot images (xxxi.py), so there is no redirection for them:
DOC :=myPdf
STY :=st
PY_DIR :=a/
TEX :=pdflatex -shell-escape -interaction=batchmode -file-line-error
PYS := $(wildcard $(PY_DIR)*.py)
OUTS := $(patsubst %.py,%.out,$(PYS))
.PHONY: all clean
all: $(DOC).pdf
%.pdf: %.tex $(STY).sty $(OUTS)
$(TEX) $<
$(PY_DIR)%.out: $(PY_DIR)%t.py
cd $(PY_DIR) && python3 $*t.py > $*.out
$(PY_DIR)%.png: $(PY_DIR)%i.py
cd $(PY_DIR) && python3 $*i.py
clean:
rm *.aux *.log > /dev/null 2>&1
The directory looks like this:
./st.sty
./myPdf.tex
./myPdf.pdf
./a/11t.py
./a/11.out
./a/12i.py
./a/12.png
./a/21t.py
./a/...
However, now right after modifying myPdf.tex, make says make: Nothing to be done for 'all'.
What am I doing wrong?
Your recipes are wrong. Make expands the recipes before passing them to the shell. As there is no make variable named cd PY_DIR && python3 11.py > 11.out, $(cd PY_DIR && python3 11.py > 11.out) expands as the empty string and make considers that there is nothing to do for $(PY_DIR)11.out. Just write your recipes as plain shell (and fix the other bug with the unexpanded PY_DIR):
$(PY_DIR)11.out:
cd $(PY_DIR) && python3 11.py > 11.out
$(PY_DIR)12.out:
cd $(PY_DIR) && python3 12.py > 12.out
Note: if you want make to re-run the recipes when your python scripts change you should let him know that the output files depend on the python scripts. The best is probably to use a pattern rule instead of one specific rule per file:
$(PY_DIR)%.out: $(PY_DIR)%.py
cd $(PY_DIR) && python3 $*.py > $*.out
($* is a make automatic variable, it expands as the stem of the pattern).
A few more improvements:
You could ask make to find alone the python scripts, compute the names of the output files and store all this in make variables that you can used in your other rules.
You can use a pattern rule for the xx.tex -> xx.pdf process. And use another make automatic variable for it: $< that expands as the first prerequisite.
DOC := myPdf
STY := st
PY_DIR := a/
TEX := pdflatex -shell-escape -interaction=batchmode -file-line-error
PYS := $(wildcard $(PY_DIR)*.py)
OUTS := $(patsubst %.py,%.out,$(PYS))
.PRECIOUS: $(OUTS)
.PHONY: all clean
all: $(DOC).pdf
%.pdf: %.tex $(OUTS) $(STY).sty
$(TEX) $<
$(PY_DIR)%.out: $(PY_DIR)%.py
cd $(PY_DIR) && python3 $*.py > $*.out
.PHONY: clean
clean:
rm *.aux *.log > /dev/null 2>&1
Note: I declared $(OUTS) as precious such that make does not delete them when it is done with the building of $(DOC).pdf.
Update with the new specifications and separated python scripts for xx.out and xx.png production:
DOC := myPdf
STY := st
PY_DIR := a
TEX := pdflatex -shell-escape -interaction=batchmode -file-line-error
PYTS := $(wildcard $(PY_DIR)/*t.py)
PYIS := $(wildcard $(PY_DIR)/*i.py)
OUTS := $(patsubst $(PY_DIR)/%t.py,$(PY_DIR)/%.out,$(PYTS))
PNGS := $(patsubst $(PY_DIR)/%i.py,$(PY_DIR)/%.png,$(PYIS))
.PRECIOUS: $(OUTS) $(PNGS)
.PHONY: all clean
all: $(DOC).pdf
%.pdf: %.tex $(STY).sty $(OUTS) $(PNGS)
$(TEX) $<
$(PY_DIR)/%.out: $(PY_DIR)/%t.py
cd $(PY_DIR) && python3 $*t.py > $*.out
$(PY_DIR)/%.png: $(PY_DIR)/%i.py
cd $(PY_DIR) && python3 $*i.py
clean:
rm -f *.aux *.log > /dev/null 2>&1
Notes:
I slightly modified the definition of PY_DIR such that, when used in other parts of the Makefile, it is clear that it is a directory path. Just a matter of taste, I guess.
I added the -f option to your clean recipe such that it doesn't fail if the files to delete do not exist.
Update:
As noted by MadScientist in a comment, using $* is less generic than referring to the target ($#) and the prerequisite ($<). But as we are operating not directly on them but on their directory ($(PY_DIR)) and base file names (xx[it].py, xx.out, xx.png), switching from $* to other, more generic, automatic variables is not that simple.
But make has some more tricks that can help here: $#, $<... have variants ($(#F), $(#D)...) that expand to just the directory part or the file part. Note that, according the GNU make manual:
These variants are semi-obsolete in GNU make since the functions dir
and notdir can be used to get a similar effect.
Anyway, if we wanted to avoid $* here is what we could use instead:
$(PY_DIR)/%.out: $(PY_DIR)/%t.py
cd $(#D) && python3 $(<F) > $(#F)
$(PY_DIR)/%.png: $(PY_DIR)/%i.py
cd $(#D) && python3 $(<F)
Or (modern version):
$(PY_DIR)/%.out: $(PY_DIR)/%t.py
cd $(dir $#) && python3 $(notdir $<) > $(notdir $#)
$(PY_DIR)/%.png: $(PY_DIR)/%i.py
cd $(dir $#) && python3 $(notdir $<)
In my project, I have a set of programs that are build from sources:
SRC_FILES = $(wildcard $(SRC_DIR)/*.cpp)
TARGETS = $(patsubst $(SRC_DIR)/%.cpp,$(BIN_DIR)/%,$(SRC_FILES))
My build target is simple, and works fine:
all: $(TARGETS)
#echo "- Done target $#"
Now, I want a run target so that all these programs are run from the shell on request. Say, if I have 3 files, I want make to run automatically:
>$ ./test1
>$ ./test2
>$ ./test3
Or
>$ ./test1 && ./test2 && ./test3
I tried this:
run: $(TARGETS)
$(addsuffix && ,$(TARGETS))
That generates the following command:
./test1&& ./test2&&
but it fails, due to the trailing &&
(Of course, I want these to be generated automatically as there can be 3... or 30.)
Edit: actually, the && separator is not required, so something like this:
>$ ./test1; ./test2; ./test3;
will be fine too.
Have some .PHONY line near start of Makefile with
.PHONY: all run
You might have
run: $(TARGETS)
$(addsuffix && ,$(TARGETS)) true
but it is a dirty trick.
Maybe you want to produce the output of test2 into test2.out then you might have
TESTSCRIPTS= $(wildcard test*[0-9])
run: $(patsubst %, %.out, $(TESTSCRIPTS))
test%.out: test%
# here some command to run the test%
As alternatives to Basile Starynkevitch's entirely correct answer here there are (at least) two other options as well.
You can avoid the need to run an unnecessary command (builtin though it might be) to end the list by manually pulling off the first entry (this may in fact be more costly then the shell builtin though).
run: $(TARGETS)
$< $(addprefix &&,$(wordlist 2,$(words $^),$^))
A better option I think, assuming that connecting the commands with && isn't a necessity would be to use $(foreach) to generate the command to be run.
run: $(TARGETS)
$(foreach t,$^,$t;)
The trailing ; in that is crucial as the output from $(foreach) is a single line and you need ; to terminate each shell command (or it is seen as one long command with arguments).
I've made a simple Makefile for an application and after install I need to restart udev rules.
INSTALLDIR=/pkt/bin
OS:=$(shell uname -v)
LBITS:=$(shell getconf LONG_BIT)
LIBDIR=/usr/lib
ifeq ($(LBITS),64)
LIBDIR64=/usr/lib64
else
LIBDIR64=/usr/lib
endif
all: usbupdater
configuracion.o: configuracion.cpp
g++ -c configuracion.cpp
main.o: main.cpp
g++ -c main.cpp
usbupdater: main.o configuracion.o
#echo "$(PATH)"
#echo "$(LIBDIR)"
g++ main.o configuracion.o $(LIBDIR)/libReadINI.a $(LIBDIR64)/chilkat/li
bchilkat-9.4.1.a -lpthread -lresolv -o usbupdater
clean:
rm -rf *.o *.cgh $(INSTALLDIR)/usbupdater
install:
mv usbupdater $(INSTALLDIR)/usbupdater
cp -rf 99-persistent-usb.rules /etc/udev/rules.d/99-persistent-usb.rules
postinstall:
#echo "$(OS)"
ifeq ($(findstring Debian,$(OS)),Debian) \
#echo "Estoy dentro del if"
$(shell '/etc/init.d/udev' restart) \
else \
#echo "Estoy dentro del else"
$(shell ls -l) \
endif
The problem is that when I type make postinstall is shows me this error:
#1 SMP Debian 3.2.46-1+deb7u1
ifeq (Debian,Debian) \
#echo "Estoy dentro del if"
/bin/sh: 1: Syntax error: word unexpected (expecting ")")
make: *** [postinstall] Error 2
I don't know where the problem is. I compare the result of uname -v with Debian to perform udev restart or udevcontrol reload_rules if it is an Opensuse OS.
Thanks in advance and sorry for my English.
ifeq is a make command. All lines in the makefile (in a recipe context) are passed to the shell. So make is passing ifeq to the shell and the shell is telling you that it has no idea what you're talking about.
You should write this using shell syntax, not make syntax.
Also it's rarely useful to use $(shell ...) functions inside a make recipe. The make recipe will be run by the shell, so when you use $(shell ...) you're just doubling the amount of shells that are running. Plus a command like $(shell ls -l) is not going to work, because $(shell ...) is like backtick and replaces the function with the stdout of the command. That'll be an error in this situation.
I would write it as:
postinstall:
#echo "$(OS)"
#case '$(OS)' in \
(*Debian*) \
echo "Estoy dentro del if"; \
/etc/init.d/udev restart ;; \
(*) \
echo "Estoy dentro del else"; \
ls -l ;; \
esac
You can't use make internal commands (like ifeq) within rule definition block. Use either shell's if or use ifeq outside of rule to generate some variables values, like
ifeq($(blah-blah), blah)
BUILD_CMD:=foo
endif
Also worth noting that else statement isn't exactly standard and may be missing in some versions of make.
Why do you want this, btw? I'd consider really bad practice if make install does something other then just installing (copying) files.