Using makefile wildcard command for file names with spaces - makefile

I have a makefile that I use to compress pictures:
src=$(wildcard Photos/*.jpg) $(wildcard Photos/*.JPG)
out=$(subst Photos,Compressed,$(src))
all : $(out)
clean:
#rmdir -r Compressed
Compressed:
#mkdir Compressed
Compressed/%.jpg: Photos/%.jpg Compressed
#echo "Compressing $<"
#convert "$<" -scale 20% "$#"
Compressed/%.JPG: Photos/%.JPG Compressed
#echo "Compressing $<"
#convert "$<" -scale 20% "$#"
However, when I have a picture with a space in its name, for example Piper PA-28-236 Dakota.JPG, I get this error:
make: *** No rule to make target `Compressed/Piper', needed by `all'. Stop.
I think this is a problem in the wildcard command, but I'm not sure what to change to get it to work.
How can I modify my makefile to allow for spaces in file names?

Generally having spaces in file names is a bad idea with make, but for your case this may work:
src=$(shell find Photos/ -iname '*.JPG' | sed 's/ /\\ /g')
out=$(subst Photos,Compressed,$(src))
all : $(out)
Compressed:
#mkdir Compressed
Compressed/%: Photos/% Compressed
#echo "Compressing $<"
#convert "$<" -scale 20% "$#"

Related

multiple implicit (or pattern) rules for the same recipe

I have a command that can take multiple types of input files and generate relevant output. IT does something similar to producing thumbnails from images. I want to write the recipe once and have a list of implicit rules that each call the recipe, e.g.:
%.png : %.jpg
%.png : %.jpeg
%.png : %.svg
%.png : %.gif
convert $< -resize 100x100 $#
This works for .gif but for the other file types I get "No rule to make target".
Is there a way to express this set of rules without duplicating the recipe?
The following should be close to what you want:
PICTURES := $(wildcard *.jpg *.jpeg *.svg *.gif)
THUMBNAILS := $(addsuffix .png,$(basename $(PICTURES)))
.PHONY: all
all: $(THUMBNAILS)
$(THUMBNAILS):
convert $< -resize 100x100 $#
%.png: %.jpg
%.png: %.jpeg
%.png: %.svg
%.png: %.gif
You could add one rule with no prerequisites:
%.png : %.jpg
%.png : %.jpeg
%.png : %.svg
%.png : %.gif
%.png :
convert $< -resize 100x100 $#
THE DRAWBACK is that if you try to build foo.png when none of the prereqs exist, Make will gamely attempt to build it with no input file. You could put a test in the recipe, to get a more graceful exit in that case, but it wouldn't be very elegant.

Makefile: what does $#~ mean?

I am working on an old makefile that contains the following snippet to generate a shared library:
lib$(LIBNAME).so.$(SOLIBREV): $(OBJS)
$(RM) $#~
#SONAME=`echo $# | sed 's/\.[^\.]*$$//'`; set -x; \
$(CC) -o ./$#~ -shared -Wl,-soname,$$SONAME $(OBJS) $(SOEXTRALIBS) -lc;
$(MV) $#~ $#
$(MV) $# lib$(LIBNAME).so
Now I need to modify that. I know that $# specifies the target but what meaning does the tilde in "$#~" have?
By the way SOLIBREV stands for so-library-revision.
It doesn't mean anything special. It's just $# followed by a literal ~. A ~ suffix on filenames is often used for temporary files, so this recipe is using a temporary file named after the target but with the extra ~ suffix.

Makefile rule for copying files with multiple extensions into target directory

I would like make to copy files from the source directory into a target directory. And i would like to avoid copying unchanged files. Therefore, I am trying to utilize make function of checking for newer files with a %.:%. rule.
Now, in this instance, the source and target EXTENSION are the same. There are object files elsewhere but not for the graphical assets.
I use make to copy images.
TARGET := target
SOURCE := source
GRAPHICS := $(foreach dir,$(SOURCE), $(wildcard $(dir)/*.jpg ) $(wildcard $(dir)/**/*.jpg ) $(wildcard $(dir)/*.png ) $(wildcard $(dir)/**/*.png ) $(wildcard $(dir)/*.gif ) $(wildcard $(dir)/**/*.gif ) )
JPG = $(GRAPHICS:.jpg=.tmp)
PNG = $(GRAPHICS:.png=.tmp)
GIF = $(GRAPHICS:.gif=.tmp)
And then use the following rule to copy files into target directory:
%.tmp:%.jpg
find $< | cpio -p -d -v $(TARGET)
Questions
Is it possible to string replace the TARGET directory here
and thereby use the make newer capability?
I tried
JPG = $(GRAPHICS:$(TARGET).jpg=.tmp)
But that fails with No rule to make target. Is it only possible to compare source and object in the same directory?
Can one make a rule such that the source and object are the same extension?
%.jpg:%.jpg
The closest I can come up with is:
$(TARGET)%.jpg:%.jpg
but that never runs. Even after a clean.
Here is the solution.
This 'setup' remains the same.
TARGET := target
SOURCE := source GRAPHICS := $(foreach dir,$(SOURCE), $(wildcard $(dir)/*.jpg )
$(wildcard $(dir)/**/*.jpg ) $(wildcard $(dir)/*.png ) $(wildcard $(dir)/**/*.png )
$(wildcard $(dir)/*.gif ) $(wildcard $(dir)/**/*.gif ) )
Now add a prefix to ALL (space separated) values in the $GRAPHICS string
TARGET_GRAPHICS := $(addprefix $(TARGET)/, $(GRAPHICS) )
Note that patsubst aka -- TARGET_GRAPHICS := $(patsubst %, $(TARGET)/%, $(GRAPHICS) ) -- only worked for the first entry. Perhaps I was doing it incorrectly.
Provide a pattern match for each file type. Here do whatever you wish. In my case, copy the input into the target directory. cpio will make the needed directories. cp -p might also work as needed.
$(TARGET)/%.jpg : %.jpg
#echo "--- .jpg copying " $< " to " $# " into dir " $(<D)
#find $< | cpio -p -d -v $(TARGET)
$(TARGET)/%.png : %.png
#echo "--- .png copying " $< " to " $#
#find $< | cpio -p -d -v $(TARGET)
$(TARGET)/%.gif : %.gif
#echo "--- .gif Copying " $< " to " $#
#find $< | cpio -p -d -v $(TARGET)
So you want the copy of d1/d2/f.png into out/d1/d2/f.png to make use of make's dependency checking?
out/d1/d2/f.png: d1/d2/f.png
out/d1/d2/f.png:
cp $< $#
Adding a jpg file d3/g.jpg say,
out/d3/g.jpg: d3/g.jpg
out/d1/d2/f.png: d1/d2/f.png
out/d1/d2/f.png out/d3/g.jpg:
cp $< $#
We can express this more cleanly with a static pattern rule.
out/d3/g.jpg out/d1/d2/f.png: out/% %
cp $< $#
Nice. So adding your ${GRAPHICS} and fleshing things out a bit
outdir := out/
targets := $(addprefix ${outdir},${GRAPHICS})
.PHONY: all
all: ${targets} ; : $# Success
${targets}: ${outdir}%: %
echo $< | cpio -d -p ${outdir}
Parallel safe too, so make -j9 will exercise your 8 CPUs nicely.

Wildcard in GNU makefile

I have many text files in a markdown text format and I want to use GNU make for generating HTML output. I have this Makefile that works with only one file:
MARKDOWN=markdown2
all: my_article_1242323266.html
%.html: %.markdown
$(MARKDOWN) $< $#
clean:
rm -f *html
Now, I want to get it working with all the markdown/html files, but I do not want to list all my files in the Makefile. I tried $(wildcard *.html) but it cannot not work as I dont have the "source" files in the directory yet. How to do that?
MARKDOWN = markdown2
HTMLS = $(patsubst %.markdown,%.html,$(wildcard *.markdown))
all: $(HTMLS)
%.html: %.markdown
$(MARKDOWN) $< $#
clean:
rm -f $(HTMLS)
Use something like this SOURCES=$(shell find . -name "*.cpp" -print | sort). This will get all the cpp files
Thank v01d, I modified his solution into this:
MARKDOWN=markdown2
SOURCES=$(shell find . -name "*.markdown" | sed 's/markdown/html/')
all: $(SOURCES)
%.html: %.markdown
$(MARKDOWN) $< $#
clean:
rm -f *html

Using sed in a makefile; how to escape variables?

I am writing a generic makefile to build static libraries. It seems to work well so far, except for the line calling sed:
# Generic makefile to build a static library
ARCH = linux
CFLAGS = -O3 -Wall
SOURCES = src
BUILD_DIR = build/$(ARCH)
TARGET = $(BUILD_DIR)/libz.a
CFILES = $(foreach dir,$(SOURCES),$(wildcard $(dir)/*.c))
OBJECTS = $(addprefix $(BUILD_DIR)/,$(CFILES:.c=.o))
# Pull in the dependencies if they exist
# http://scottmcpeak.com/autodepend/autodepend.html
-include $(OBJECTS:.o=.dep)
default: create-dirs $(TARGET)
$(TARGET): $(OBJECTS)
$(AR) -rc $(TARGET) $^
$(BUILD_DIR)/%.o: %.c
$(CC) $(CFLAGS) -c $< -o $#
$(CC) -M $(CFLAGS) $*.c > $(BUILD_DIR)/$*.tmp
sed s/.*:/$(BUILD_DIR)\\/$*.o:/ $(BUILD_DIR)/$*.tmp > $(BUILD_DIR)/$*.dep
#rm $(BUILD_DIR)/$*.tmp
.PHONY: create-dirs
create-dirs:
#for p in $(SOURCES); do mkdir -p $(BUILD_DIR)/$$p; done
.PHONY: clean
clean:
rm -fr $(BUILD_DIR)
sed is used to replace the path/name of the object file with the full path of where the object actually is. e.g. 'src/foo.o:' is replaced with 'build/linux/src/foo.o:' in this example.
$(BUILD_DIR) and $* in the replacement string both contain forward slashes when expanded - how do I pass them to sed?
Note: This might have been answered here before, but I am so far unable to apply those answers to my specific problem!
You can use anything else than forward slashes as separator in sed. E.g. sed s~foo~bar~g
You can use double quotes " (at least in the shell), and variables will still be expanded: echo "Hello $PLANET"
If you want the path to expand into a file you use
sed -i "s/TO_REPLACE/$(subst, /,\/,${PATH_VARIABLE})/g" filename.txt
If your variable PATH_VARIABLE looked like /opt/path/user/home/etc
it will now inflate to:
\ /opt\ /path\ /user\ /home\ /etc
This should allow sed to insert the ' / ' correctly.
-Matt

Resources