Here's what my makefile looks like:
.PHONY: all clean
all : 1wchaotic.png
1wchaotic.png : plot-script1.gp duffing stroboscopic points1.data
gnuplot "$<" > "$#"
points1.data : duffing
./duffing -a 1 -b 1 -w 1 -u 0.1 -A 35.5 -t 1000 | ./stroboscopic > points1.data
duffing : duffing.c Makefile
make
clean :
rm *~ *.png *.data
This is what I get when running the makefile:
user#ubuntu:~/HW$ make -f makefile2
./duffing -a 1 -b 1 -w 1 -u 0.1 -A 35.5 -t 1000 | ./stroboscopic > points1.data
make: *** [points1.data] Error 255
Related
I'm using $(shell ...) gnu make function in a Makefile recipe, and it runs first before the preceding rows. Why?
A very simple example:
.PHONY: all
all:
#echo 1
#echo $(eval a=$(shell echo 2a 1>&2))2b
#echo 3 $(a)
The output is:
2a
1
2b
3
First runs the $(shell ...) line (2a), then the other lines.
How can I manage to run the $(shell ...) function when its row runs in the recipe, in this order?
1
2a
2b
3
Edit:
Without $(shell ...) it works as I expected:
.PHONY: all
all:
#echo 1
$(eval a=a)
#echo 2 $(a)
$(eval a=b)
#echo 3 $(a)
Output:
1
2 a
3 b
Edit 2:
Here is a part of the original Makefile. The pieces at >>> show the essence of my problem: I want to put the output of udisksctl into a make variable instead of file $#.loop (and do the same with $#.mount).
$(HDIMG): $(BOOTBLOCK_MBR_BIN) $(BOOTBLOCK_EXT2_BIN) $(LOADER_BIN) | $(DESTDIR)
dd if=/dev/zero of=$#.ext2 bs=1 seek=$(PSIZEB) count=0 2>/dev/null
$(MKFSEXT2) -F $#.ext2 >/dev/null
dd if=$(word 2,$^) of=$#.ext2 conv=notrunc 2>/dev/null
cp $< $#
dd if=/dev/zero of=$# bs=1 seek=$(HDSIZEB) count=0 2>/dev/null
echo $(PFDISK) | $(TR) | $(FDISK) $# >/dev/null
dd if=$#.ext2 of=$# bs=512 seek=$(PSTART) conv=sparse,notrunc iflag=fullblock 2>/dev/null
>>> udisksctl loop-setup --file $# --offset $(PSTARTB) --size $(PSIZEB) >$#.loop
sed -i -e 's/.* //;s/\.//' $#.loop
cat $#.loop
>>> udisksctl mount --block-device $$(cat $#.loop) >$#.mount
sed -i -e 's/.* //;s/\.//' $#.mount
cat $#.mount
#
mkdir -p $$(cat $#.mount)/boot/
cp $(word 2,$^) $$(cat $#.mount)/boot/
#/sbin/filefrag -b512 -e /
#
udisksctl unmount --block-device $$(cat $#.loop)
udisksctl loop-delete --block-device $$(cat $#.loop)
rm $#.loop
When make runs a recipe, it first expands all variables/functions in it, and then runs the shell commands line by line.
You can work around that by executing every shell command with $(shell ...), or, in your case, by using $(info ...) instead of echo:
.PHONY: all
all:
$(info 1)
$(info $(eval a=$(shell echo 2a 1>&2))2b)
$(info 3 $(a))
Output:
1
2a
2b
3
make: Nothing to be done for 'all'.
The Nothing to be done for 'all'. part is caused by the fact that after expanding the functions, the recipe is effectively empty (has 0 shell commands). Adding a no-op command (e.g. #true) to it removes the message.
I need to build a bash command in a script depending on some cuote or normal parameters. For example:
BAYES)
class="weka.classifiers.bayes.BayesNet"
A="-D -Q weka.classifiers.bayes.net.search.local.K2 -- -P 1 -S BAYES -E"
B="weka.classifiers.bayes.net.estimate.SimpleEstimator -- -A 0.5" ;;
LOGISTIC)
class="weka.classifiers.functions.Logistic"
A="-R 1.0E-8 -M -1 -num-decimal-places 4" ;;
SIMPLELOG)
class="weka.classifiers.functions.SimpleLogistic"
A="-I 0 -M 500 -H 50 -W 0.0" ;;
SMO)
class="weka.classifiers.functions.SMO"
A="-C 1.0 -L 0.001 -P 1.0E-12 -N 0 -V -1 -W 1 -K"
A1="weka.classifiers.functions.supportVector.PolyKernel -C 250007 -E 1.0" ;;
IBK)
class="weka.classifiers.lazy.IBk"
A="-K 1 -W 0 -A "
A1="weka.core.neighboursearch.LinearNNSearch -A"
A2="weka.core.EuclideanDistance -R first-last" ;;
KSTAR)
class="weka.classifiers.lazy.KStar"
A="-B 20 -M a" ;;
...
java -Xmx"$mem"m -cp "$WEKA_INSTALL_DIR/weka.jar" $class -s $i -t "$file" $A "$A1" $B "$B1"
However, my problem is that in some conditions, when $A1 is empty, the "$A1" parameter is not valid. The same with "$B1". And the parameter could be in any combination ($A1 with $B1, $A1 without $B2, ...).
Also I've tried include $A1 in $A as following:
A="-C 1.0 -L 0.001 -P 1.0E-12 -N 0 -V -1 -W 1 -K \"weka.classifiers.functions.supportVector.PolyKernel -C 250007 -E 1.0\""
and execute:
java -Xmx"$mem"m -cp "$WEKA_INSTALL_DIR/weka.jar" $class -s $i -t "$file" $A
but this doesn't work.
You cannot safely and reliably store multiple arguments in a single string; you need to use arrays; this is their intended use case. Make sure to initialize any arrays that won't be used, so that they "disappear" when expanded.
# If A is undefined, "${A[#]}" is an empty string.
# But if A=(), then "${A[#]}" simply disappears from the command line.
A=()
B=()
A1=()
A2=()
case $something in
BAYES)
class="weka.classifiers.bayes.BayesNet"
A=(-D -Q weka.classifiers.bayes.net.search.local.K2 -- -P 1 -S BAYES -E)
B=(weka.classifiers.bayes.net.estimate.SimpleEstimator -- -A 0.5);;
LOGISTIC)
class="weka.classifiers.functions.Logistic"
A=(-R 1.0E-8 -M -1 -num-decimal-places 4);;
SIMPLELOG)
class="weka.classifiers.functions.SimpleLogistic"
A=(-I 0 -M 500 -H 50 -W 0.0) ;;
SMO)
class="weka.classifiers.functions.SMO"
A=(-C 1.0 -L 0.001 -P 1.0E-12 -N 0 -V -1 -W 1 -K)
A1=(weka.classifiers.functions.supportVector.PolyKernel -C 250007 -E 1.0) ;;
IBK)
class="weka.classifiers.lazy.IBk"
A=(-K 1 -W 0 -A)
A1=(weka.core.neighboursearch.LinearNNSearch -A)
A2=(weka.core.EuclideanDistance -R first-last);;
KSTAR)
class="weka.classifiers.lazy.KStar"
A=(-B 20 -M a) ;;
esac
and always quote parameter expansions.
java -Xmx"$mem"m -cp "$WEKA_INSTALL_DIR/weka.jar" \
"$class" -s "$i" -t "$file" "${A[#]}" "${A1[#]}" "${B[#]}" "${B1[#]}"
SOLUTION:
I solved all my problems using only a parameter A like this:
BAYES)
class="weka.classifiers.bayes.BayesNet"
A=(-D -Q weka.classifiers.bayes.net.search.local.K2 -- -P 1 -S BAYES -E weka.classifiers.bayes.net.estimate.SimpleEstimator -- -A 0.5);;
SMO)
class="weka.classifiers.functions.SMO"
A=(-C 1.0 -L 0.001 -P 1.0E-12 -N 0 -V -1 -W 1 -K "weka.classifiers.functions.supportVector.PolyKernel -C 250007 -E 1.0");;
java -Xmx"$mem"m -cp "$WEKA_INSTALL_DIR/weka.jar" $class -s $i -t "$file" "${A[#]}"
From your question, I did:
Initialized the variables
Completed the case statement
Removed some not required double quotes
Defined some variables for which you did not provide values for
backslash your double quotes if you must have then in the java command
If you need double quotes for certain variables, put these in the variables. This way you will not have "" in your java command if the variables is empty. I did this for A1 in case IBK.
This will get you started, modify as required:
#!/bin/bash
#
mem="512"
WEKA_INSTALL_DIR='/opt/weka'
class=""
i="value-of-i"
A=""
A1=""
B=""
B1=""
file="SOMEFILE"
case $1 in
'BAYES')
class="weka.classifiers.bayes.BayesNet"
A="-D -Q weka.classifiers.bayes.net.search.local.K2 -- -P 1 -S BAYES -E"
B="weka.classifiers.bayes.net.estimate.SimpleEstimator -- -A 0.5"
;;
'LOGISTIC')
class="weka.classifiers.functions.Logistic"
A="-R 1.0E-8 -M -1 -num-decimal-places 4"
;;
'SIMPLELOG')
class="weka.classifiers.functions.SimpleLogistic"
A="-I 0 -M 500 -H 50 -W 0.0"
;;
'SMO')
class="weka.classifiers.functions.SMO"
A="-C 1.0 -L 0.001 -P 1.0E-12 -N 0 -V -1 -W 1 -K"
A1="weka.classifiers.functions.supportVector.PolyKernel -C 250007 -E 1.0"
;;
'IBK')
class="weka.classifiers.lazy.IBk"
A="-K 1 -W 0 -A "
A1="\"weka.core.neighboursearch.LinearNNSearch -A\""
A2="weka.core.EuclideanDistance -R first-last"
;;
'KSTAR')
class="weka.classifiers.lazy.KStar"
A="-B 20 -M a"
;;
*)
# default options
;;
esac
echo java -Xmx${mem}m -cp $WEKA_INSTALL_DIR/weka.jar $class -s $i -t $file $A $A1 $B $B1
Example:
./test.bash LOGISTIC
java -Xmx512m -cp /opt/weka/weka.jar weka.classifiers.functions.Logistic -s value-of-i -t SOMEFILE -R 1.0E-8 -M -1 -num-decimal-places 4
./test.bash IBK
java -Xmx512m -cp /opt/weka/weka.jar weka.classifiers.lazy.IBk -s value-of-i -t SOMEFILE -K 1 -W 0 -A "weka.core.neighboursearch.LinearNNSearch -A"
I'm having difficulties with makefiles.
So in a recipe, I'm making a file (with a name and a .ujc extension) in a for loop and would like to have a text file at the end which contains all the created files. Purpose is to feed it to an application.
For example, in a semi high-level example,
List= [Class1,Class2,Class3]
foreach(Class C in List) {
#do operations on C > outputs a ClassX.ujc file
# add name of file to a text file named "list_of_files"
}
At the end I should have a text file, list_of_files.txt, which contains the following string:
Class1.ujc Class2.ujc Class3.ujc
As a reference, the code I have at the moment (and which does a bit of the stuff above but does not work is) is:
pc: $(APP)
$(foreach C, $(shell echo $(CLASS) | tr ',' ' '), \
make -C BUILDENV CLASS=$(C) BUILD=just_filelist OUTPUT=filelist.txt SKIPSELF=yes && \
../classCvt/classCvt <./Applications/$(C).class> ./Applications/$(C).ujc && \
cat app_file_list.txt | xargs echo ./Applications/$(C).ujc >app_file_list.txt && \
) true
time -p ./$(APP) `cat app_file_list.txt` `cat filelist.txt`
The internal make does make a filelist which is fed to the app, but I'd also like to feed the app_file_list but its construction goes totally wrong.
Probably simple, but I'm not getting there.
Edit:
The code below does what I want:
pc: $(APP)
rm -f cat app_file_list.txt
$(foreach C, $(shell echo $(CLASS) | tr ',' ' '), \
make -C BUILDENV CLASS=$(C) BUILD=just_filelist OUTPUT=filelist.txt SKIPSELF=yes && \
../classCvt/classCvt <./Applications/$(C).class> ./Applications/$(C).ujc && \
cat app_file_list.txt | echo ./Applications/$(C).ujc >>app_file_list.txt && \
) true
time -p ./$(APP) `cat app_file_list.txt` `cat filelist.txt`
Notable mistake I made was the xargs.
(Also in the post)
The solution turned out to be not-so-difficult. I needed to remove the xargs command and do the correct operation (i.e., >> instead of >) in the 'cat app_file_list.txt | etc...' line.
The code below does what I want:
pc: $(APP)
rm -f cat app_file_list.txt
$(foreach C, $(shell echo $(CLASS) | tr ',' ' '), \
make -C BUILDENV CLASS=$(C) BUILD=just_filelist OUTPUT=filelist.txt SKIPSELF=yes && \
../classCvt/classCvt <./Applications/$(C).class> ./Applications/$(C).ujc && \
cat app_file_list.txt | echo ./Applications/$(C).ujc >>app_file_list.txt && \
) true
time -p ./$(APP) `cat app_file_list.txt` `cat filelist.txt`
Notable mistake I made was the xargs which caused strings to repeat into the .txt file.
Currently, I am working on a makefile that takes a parameter "CLASS=xxx" and then compiles and does stuff with that value.
In the end, it runs an application ($APP) on a bunch of files.
I enter this command:
make default CLASS=Test_UART
and the makefile processes it thusly:
pc: $(APP)
make -C BUILDENV CLASS=$(CLASS) BUILD=just_filelist OUTPUT=filelist.txt SKIPSELF=yes
../classCvt/classCvt <./Applications/$(CLASS).class> ./Applications/$(CLASS).ujc
time -p ./$(APP) ./Applications/$(CLASS).ujc `cat filelist.txt`
Hence it calls a makefile in my BUILDENV folder which does the following:
#USAGE: make -C <PATH_TO_THIS_FILES_PARENT_DIR> CLASS=<MY_JAVA_FILE_NAME_WITHOUT_JAVA_EXTENSION> OUT=<OUTPUT_FILE_NAME>
SELF := $(dir $(CURDIR)/$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)))
CLASS ?= PLEASE_SPECIFY_CLASS_PARAM
DIR := $(PWD)#print working directory
CCVT ?= $(SELF)/../../classCvt/classCvt
TOBIN ?= $(SELF)/../../classCvt/tobin
OUTPUT ?=
### Comment: Defining CMD.
ifeq ($(BUILD), just_filelist)
CMD = echo
else
ifeq ($(BUILD), PC)
CMD = echo
else
ifeq ($(BUILD), unopt)
CMD = $(TOBIN)
else
### Comment: Optimized CMD = tobin -c ccvt
CMD = $(TOBIN) -c $(CCVT)
endif
endif
endif
ifeq ($(OUTPUT), )
OUT = &1
else
OUT = $(DIR)/$(OUTPUT)
endif
ifeq ($(SKIPSELF), yes)
MYCLASS =
else
MYCLASS = $(DIR)/Applications/$(CLASS).class
endif
all:
CLASSPATH=$(SELF)/RT/real:$(SELF)/RT/fake:$(DIR) javac $(DIR)/Applications/$(CLASS).java
find $(SELF)/RT/real -iname "*.class" -type f > $(SELF)/files
ls $(DIR)/Applications/*.class | grep -v "$(CLASS)\.class" >> $(SELF)/files || true
cat $(SELF)/files | xargs $(CMD) $(MYCLASS) >$(OUT)
rm -f $(SELF)/files
What I would like to do is give a command like:
make default CLASS=Test1,Test2,Test3
and the makefile to process it for the 3 classes and put the given classes in a .txt and the default classes in a different .txt, something like this like this:
pc: $(APP)
make -C BUILDENV default_classes BUILD=list_default_classes OUTPUT=list_default_classes.txt
# make -C BUILDENV given_classes BUILD=list_given_classes OUTPUT=list_given_classes.txt CLASS=$(CLASS) SKIPSELF=yes
../classCvt/classCvt `cat list_given_classes.txt`./Applications/$(CLASS).ujc
#here the list_given_classes should now contain the .ujc files
time -p ./$(APP) `cat list_given_classes.txt` `cat list_default_classes.txt`
and for the makefile in the BUILDENV, I expect something like:
default_classes:
CLASSPATH=$(SELF)/RT/real:$(SELF)/RT/fake:$(DIR)
find $(SELF)/RT/real -iname "*.class" -type f > $(SELF)/files
ls $(DIR)/Applications/*.class | grep -v "$(CLASS1)\.class" "$(CLASS2)\.class">> $(SELF)/files || true
cat $(SELF)/files | xargs $(CMD) >$(OUT)
rm -f $(SELF)/files
given_classes:
javac $(DIR)/Applications/$(CLASS).java
find $(SELF)/RT/real -iname "*.class" -type f > $(SELF)/files
ls $(DIR)/Applications/*.class | grep -v "$(CLASS)\.class" >> $(SELF)/files || true
cat $(SELF)/files | xargs $(CMD) $(MYCLASS) >$(OUT)
rm -f $(SELF)/files
However, I'm not sure how to do this for a CLASS parameter containing multiple classes.
I'm thinking to try and parse the Test1,Test2,Test3 value into a list of 1,2,3 and then iterating over it. But no clue if this is a good way and even on how to do it.
What do you guys suggest?
Pretty way:
pc: $(APP)
define BUILD_CLASS
pc: pc-$(CLASS_SPLIT)
pc-$(CLASS_SPLIT):
make -C BUILDENV CLASS=$(CLASS_SPLIT) BUILD=just_filelist OUTPUT=filelist.txt SKIPSELF=yes
../classCvt/classCvt <./Applications/$(CLASS_SPLIT).class> ./Applications/$(CLASS_SPLIT).ujc
time -p ./$(APP) ./Applications/$(CLASS_SPLIT).ujc `cat filelist.txt`
endef
CLASSES := $(shell echo $(CLASS) | tr ',' ' ')
$(foreach CLASS_SPLIT, $(CLASSES), $(eval $(BUILD_CLASS)))
Simple way:
pc: $(APP)
$(foreach C, $(shell echo $(CLASS) | tr ',' ' '), \
make -C BUILDENV CLASS=$(C) BUILD=just_filelist OUTPUT=filelist.txt SKIPSELF=yes && \
../classCvt/classCvt <./Applications/$(C).class> ./Applications/$(C).ujc && \
time -p ./$(APP) ./Applications/$(C).ujc `cat filelist.txt` &&) true
I'm trying to add an extra dependency to a rule in a parallel makefile. I might have found a way, but I'm in doubt. (I haven't written the original makefile and I'm not an expert in make.)
The original makefile looks like this:
VER = busybox-1.16.2
URL = http://busybox.net/downloads/$(VER).tar.bz2
export KBUILD_OUTPUT = $(ROOTDIR)/user/busybox/build-$(VER)
all: build-$(VER)/.config depmod.pl
$(MAKE) -C build-$(VER)
build-$(VER)/.config: $(ROOTDIR)/config/.config
mkdir -p build-$(VER)
sed -n \
-e '/_CROSS_COMPILER_PREFIX=/s:=.*:="$(CROSS_COMPILE)":' \
-e '/CONFIG_USER_BUSYBOX_/s:CONFIG_USER_BUSYBOX_:CONFIG_:p' \
$< > $#.uclinux-dist.new
set -e ; \
if [ ! -e $# ] || ! cmp -s $#.uclinux-dist.new $#.uclinux-dist.old ; then \
cp $#.uclinux-dist.new $#.uclinux-dist.old ; \
cp $#.uclinux-dist.old $# ; \
yes "" | $(MAKE) -C $(VER) oldconfig ; \
fi
depmod.pl: $(VER)/examples/depmod.pl
ln -sf $< $#
I want to add a 'download' rule to the make.
$(VER)/: $(VER).tar.bz2
tar -jxvf $(VER).tar.bz2
touch $#
$(VER).tar.bz2:
wget $(URL)
touch $#
This rule must be executed before anything else. The parallel build prevent constructs like
all: |$(VER)/ build-$(VER)/.config depmod.pl
(This works in single threaded builds.)
My solution so far is this:
VER = busybox-1.18.5
URL = http://busybox.net/downloads/$(VER).tar.bz2
export KBUILD_OUTPUT = $(ROOTDIR)/user/busybox/build-$(VER)
all: build-$(VER)/.config depmod.pl
$(MAKE) -C build-$(VER)
$(VER)/: $(VER).tar.bz2
tar -jxvf $(VER).tar.bz2
touch $#
$(VER).tar.bz2:
wget $(URL)
touch $#
build-$(VER)/.config: $(ROOTDIR)/config/.config | $(VER)/
mkdir -p build-$(VER)
sed -n \
-e '/_CROSS_COMPILER_PREFIX=/s:=.*:="$(CROSS_COMPILE)":' \
-e '/CONFIG_USER_BUSYBOX_/s:CONFIG_USER_BUSYBOX_:CONFIG_:p' \
$< > $#.uclinux-dist.new
set -e ; \
if [ ! -e $# ] || ! cmp -s $#.uclinux-dist.new $#.uclinux-dist.old ; then \
cp $#.uclinux-dist.new $#.uclinux-dist.old ; \
cp $#.uclinux-dist.old $# ; \
yes "" | $(MAKE) -C $(VER) oldconfig ; \
fi
depmod.pl: $(VER)/examples/depmod.pl
ln -sf $< $#
$(VER)/examples/depmod.pl: | $(VER)/
Problem is, I don't really know what kind of magic the depmod.pl rule is. Is it executed correctly, now that I've added an explicit empty rule?
I'd hate to answer my own question, but I think I've found the answer.
The dependency of the depmod.pl rule is not real/relevant in the original script.
The code:
depmod.pl: $(VER)/examples/depmod.pl
ln -sf $< $#
Ought to be written:
depmod.pl:
ln -sf $(VER)/examples/depmod.pl $#
So my extra empty rule makes no difference. In the new script, the dependency almost makes sence though.