I somehow struggle to understand the intermediate files concept of make.
Consider this example of a make process:
Input files:
myProgram.code
myGraphic.picture
Build steps:
Cut off the borders of myGraphic.picture
Convert the cut graphic to a special format
Compile the code, including the converted graphic.
This is the concept:
In a Makefile, this would look like this:
all: myProgram.exe
myProgram.exe: myProgram.code myGraphic.picture.cut.converted
compiler -code myProgram.code -graphic myGraphic.picture.cut.converted
myGraphic.picture.cut.converted: myGraphic.picture.cut
converter -in myGraphic.picture.cut -out myGraphic.picture.cut.converted
myGraphic.picture.cut: myGraphic.picture
cutter -in myGraphic.picture -out myGraphic.picture.cut
As far as I understand, after running make, I will have the compiled program, as well as the intermediate files .cut.converted and .cut.
Is there a way to delete those files automatically? And if so, is make intelligent enough to not to recreate all of them when the original picture is unchanged?
Nothing easier. Just add this target:
.INTERMEDIATE: myGraphic.picture.cut myGraphic.picture.cut.converted
If the target is specifically named in the Makefile, and not a dependency of the magic target .INTERMEDIATE, then it will be kept.
So add an .INTERMEDIATE: line, and/or rephrase your conversions to be pattern rules:
%.cut.converted: %.cut
converter -in $< -out $#
%.picture.cut: %.picture
cutter -in $< -out $#
This has a bonus of being easier to read, too.
You might be able to eliminate the need for at least some of the temporary files if the tools can be used as filters in a pipeline, of course.
Yes. You can delete files if you don't need them. For example:
results.txt : testzipf.py isles.dat abyss.dat last.dat
python $^ *.dat > $#
rm -f *.dat
.PONY : dats
dats : isles.dat abyss.dat last.dat
%.dat : books/%.txt countwords.py
python countwords.py $< $#
This file create a results.txt from dats and finally delete the dat files rm -f *.dat
If you run make command again it will create the intermediate files again and delete them after utilising them to produce a target.
Your make script will look like
all: myProgram.exe
rm -f *.cut
myProgram.exe: myProgram.code myGraphic.picture.cut.converted
compiler -code myProgram.code -graphic myGraphic.picture.cut.converted
myGraphic.picture.cut.converted: myGraphic.picture.cut
converter -in myGraphic.picture.cut -out myGraphic.picture.cut.converted
myGraphic.picture.cut: myGraphic.picture
cutter -in myGraphic.picture -out myGraphic.picture.cut
Related
I'm on a mac using GNU Make to manage my dotfiles. There's a directory with my emacs config files and a corresponding target in the Makefile:
all: _emacs
.PHONY: all list $(MAKECMDGOALS)
....
EMACS_SOURCE_DIR := $(abspath ./emacs)
EMACS_TARGET_DIR := $(abspath $(HOME)/.emacs.d)
EMACS_CONFIG_FILES := $(wildcard $(EMACS_SOURCE_DIR)/*.el)
_emacs: | $(EMACS_TARGET_DIR)
#echo $(call message,"Setting up config files for emacs")
$(foreach file, \
$(EMACS_CONFIG_FILES), \
ln -sf $(file) $(addsuffix /, $(EMACS_TARGET_DIR)))
$(EMACS_TARGET_DIR):
#echo "Creating directory $(EMACS_TARGET_DIR)"
#mkdir -p $#
When I run make _emacs it creates some extra links:
.emacs.d -> /Users/xxx/.emacs.d/
-sf -> -sf
early-init.el -> /Users/xxx/Projects/bootstrap/emacs/early-init.el
init-org.el -> /Users/xxx/Projects/bootstrap/emacs/init-org.el
init-pkgs.el -> /Users/xxx/Projects/bootstrap/emacs/init-pkgs.el
init.el -> /Users/xxx/Projects/bootstrap/emacs/init.el
ln -> ln
I'm struggling to understand what exactly is happening and how to avoid having the first two and the last soft links created.
In general it's a bad idea to try to construct a complex shell command using make functions inside a recipe. You should simply use shell constructs: for example use the shell for loop, not the make foreach loop.
Let's see what your recipe does:
_emacs: | $(EMACS_TARGET_DIR)
$(foreach file, \
$(EMACS_CONFIG_FILES), \
ln -sf $(file) $(addsuffix /, $(EMACS_TARGET_DIR)))
Make functions manipulate text. They don't know anything about commands, shell syntax, etc. Before make invokes the shell it first expands the script. What will this result in? This:
ln -sf .../emacs/early-init.el /Users/xxx/.emacs.d/ ln -sf .../emacs/init-org.el /Users/xxx/.emacs.d/ ln -sf ...
Maybe now you can see the problem.
If you wanted to do this using make's foreach you have to add a shell delimiter so the shell knows where one command ends and the next begins; something like:
$(foreach file, \
$(EMACS_CONFIG_FILES), \
ln -sf $(file) $(addsuffix /, $(EMACS_TARGET_DIR)) ; )
(note the ; at the end). Now when make expands it will look like this:
ln -sf .../emacs/early-init.el /Users/xxx/.emacs.d/ ; ln -sf .../emacs/init-org.el /Users/xxx/.emacs.d/ ; ln -sf ...
If I were you, I'd instead use a shell loop. It's just much simpler to understand:
for file in $(EMACS_CONFIG_FILES); do \
ln -sf $$file $(addsuffix /, $(EMACS_TARGET_DIR)) ; \
done
But, for what you want to do you don't need a loop at all; you can link multiple files into the same directory with one command:
ln -sf $(EMACS_CONFIG_FILES) $(EMACS_TARGET_DIR)
(I'm not sure what the addsuffix was needed for, or why you couldn't just write it as $(EMACS_TARGET_DIR)/ without the addsuffix)
I am using visual code on mac for python and very new to make file environment.
I have a make file command as below,
mkdir mydir
zip mydir/test test1111/mypython.py
How should I parameterize test1111? Thanks.
ZIP = zip
test_CONTENT = \
test1111/mypython.py
mydir/test.zip: ${test_CONTENT}
#mkdir -p ${#D}
${ZIP} $# $^
I'm trying to make a Makefile that exports a markdown file to a pdf file that uses the same filename as the original markdown file. I used "basename" command but it produces "inputfile.md.pdf" instead of "inputfile.pdf".
Please see my code below (I adapted a code I found on the Internet. Thank you!):
.PHONY: pdf docx apa format
FILES := $(wildcard ./*.md)
pdf:
for file in $(FILES); do \
pandoc $$file \
--bibliography mypath \
--csl mypath \
--filter pandoc-citeproc \
--template eisvogel \
-o $(basename $$file).pdf; \
open $(basename $$file).pdf; \
done
Anyone who can help me? I'm a novice in Makefile (and programming in general) so any detailed help would be very much appreciated.
I also tried these codes below, but they generated an error message:
-o $(basename -s ".md" $$file).pdf; \
-o $(basename -s .md $$file).pdf; \
The way you write $(basename …) you get the basename make function. This would normally the right thing, but you try to reference a shell variable file in its argument, which is unavailable at the make layer.
In this case, it is probably easiest to call the basename shell utility, at the shell level. Therefore, you need to escape the $ to get shell substitution, like this:
-o "$$(basename -s .md $$file)".pdf; \
open "$$(basename -s .md $$file)".pdf; \
Alternatively, you could try to move the loop to the make layer, perhaps using foreach.
This is my makefile
file1:
uglifyjs myfile1.js -c | gzip -c -9 > myfile1.min.js
file2:
uglifyjs myfile2.js -c | gzip -c -9 > myfile2.min.js
How can I change my makefile to remove duplicate code:
file1:
FILE=myfile1.js
#How to call build target?
file2:
FILE=myfile2.js
build:
uglifyjs $(FILE).js -c | gzip -c -9 > $(FILE).min.js
I know I can use make build but is there another way to do this without invoking make recursively?
Use automatic variables:
file1 file2:
uglifyjs my$#.js -c | gzip -c -9 > my$#1.min.js
I don't know why you're using targets like file1 when the file you're actually building is myfile1.min.js. That's not a good makefile.
But, that's not the question you asked.
Use a pattern rule to run the command, and then make your targets depend on the files you want:
file1: myfile1.min.js
file2: myfile2.min.js
%.min.js: %.js
uglifyjs $< -c | gzip -c -9 >$#
The pattern rule tells make how to build a .min.js file from a .js file, and the other rules tell it to build specific files.
i'm getting this error:
make:24: *** missing separator. Stop.
Although i changed all space character with tab in line 24.
Line24:arm_v5t_le-gcc $FILES $INCLUDES $LIBS -o $TARGET
Here is the code:
#DM_serial2_make
export PATH="$PATH:/opt/mv_pro_5.0/montavista/pro/devkit/arm/v5t_le/bin:/opt/mv_pro_5.0/montavista/pro/bin:/opt/mv_pro_5.0/montavista/common/bin"
TARGET="/home/cilem/Desktop/06.05.2012/DM_serial2"
INCLUDES=" -I /home/cilem/Desktop/06.05.2012/libxml2 \
-I /home/cilem/Desktop/06.05.2012/gstreamer-0.10 \
-I /home/cilem/Desktop/06.05.2012/gstreamer-0.10/gst/interfaces \
-I /home/cilem/Desktop/06.05.2012/glib-2.0 \
-I /home/cilem/Desktop/06.05.2012/glib-2.0/include"
LIBS=" -L /home/cilem/Desktop/06.05.2012/lib/ -l:libgstreamer-0.10.so.0 \
-L /home/cilem/Desktop/06.05.2012/lib/ -l:libgstinterfaces-0.10.so.0 \
-L /home/cilem/Desktop/06.05.2012/lib/ -l:libgobject-2.0.so.0 \
-L /home/cilem/Desktop/06.05.2012/lib/ -l:libgmodule-2.0.so.0 \
-L /home/cilem/Desktop/06.05.2012/lib/ -l:libxml2.so.2 \
-L /home/cilem/Desktop/06.05.2012/lib/ -l:libgthread-2.0.so.0 \
-L /home/cilem/Desktop/06.05.2012/lib/ -l:libglib-2.0.so.0"
FILES="DM_serial2.c"
arm_v5t_le-gcc $FILES $INCLUDES $LIBS -o $TARGET
That looks like a shell script. Shell scripts are not makefiles, and vice versa. You need to find a good tutorial on make, or read the GNU make manual.
For example, you should not have any quoting in your variable values.
Second, variable expansions in make require the variables to be surrounded by parens or curly braces: $(FILES) or ${FILES}.
Third, as piokuc says, that line is not a valid make rule. A make rule has the form:
<target> : <dependencies...>
<commands...>
where the indentation of the commands... must be TAB characters. This rule says "you can build target whenever it's older than any of dependencies... by running commands...". The target and dependencies must (usually) be files, so you definitely don't want to use $(INCLUDES) or $(LIBS) in that list as those are compiler flags.
You probably want something like this, although it could be improved:
$(TARGET): $(FILES)
arm_v5t_le-gcc $(FILES) $(INCLUDES) $(LIBS) -o $(TARGET)
You've got other weird things here. You don't need to provide the same directory over and over with the -L flag. Once is enough. Also I'm not familiar with the -l:libfoo.a construct; usually it's just -lfoo.
I think the last line should be replaced with something like:
$TARGET: $FILES $INCLUDES $LIBS
arm_v5t_le-gcc $FILES $INCLUDES $LIBS -o $TARGET
The above line (the one starting with arm_v5t_le-gcc) should start with a tab, not spaces.