GNU make: how to ensure pattern to match only filename without directory - makefile

I just want to copy files from other directories (before doing something). Because it's tedious to write the copy command for each file, I tried
%: ../src1/%
#echo cp $^ .
%: ../src2/%
#echo cp $^ .
all: file1 file2 file3 file4
# do something
but this doesn't work because make tries to look into ../src2/../src2/../src2/../ . . . . (I included echo for testing to prevent actual copying from happening. I keep forgetting what the "dry run" command line options is . . .)
I naïvely thought that there must be a way to force matches only to filenames that don't include directories.
Is there a way?

You can mark the "make anything" rules terminal with a double colon:
%:: ../src1/%
#echo cp $^ .
%:: ../src2/%
#echo cp $^ .
This does not answer your specific question of how to get Make to match only filenames without directories, but it does get Make to do what you want.
There is another approach that works and is closer to what you asked for: add pattern rules to satisfy Make:
../src1/%:
#: # do nothing
%: ../src1/%
#echo cp $^ .
EDIT: Or better still, us one dummy pattern rule to cover all source directories:
../%:
#: # do nothing

Related

Makefile no rule to make target index.html needed by all?

Need a fresh pair of eyes on this. Thanks in advance. Isn't the rule '%.html' ? I'm wanting to find all my wiki files and convert them with pandoc to html.
SOURCES = $(shell find $$wiki -name "*.wiki")
HTML=$(patsubst %.wiki,%.html,$(SOURCES))
all: $(HTML)
%.html: $(SOURCES)
#echo -e "Converting wiki file $# to html."
Problem - It was finding other files in directories that had files with spaces.
Solution - Restricted the search with the find command.

Makefile dir function doesn't expand as expected in for loop

I want to iterate through a list of files, execute cd in their respective directories before executing a command.
So I would like to use $(dir $(FILES)) function to get the directory, but it seems not to work properly in the for loop.
FILES=../dir1/file1 ../dir2/file2
.PHONY: all
all:
#for f in $(FILES); do \
echo $$f in $(dir $${f}) ; \
done
outputs
../dir1/file1 in ./
../dir2/file2 in ./
The $(dir $${f}) gets expanded to ./ instead of ../dirN.
Note :
Writing only echo $(dir $(FILES)) outside of the for loop outputs ../dir1/ ../dir2/ as expected.
using $(abspath ...) doesn't work either.
What am I missing ?
You are mixing make constructs (dir) and shell constructs. Unfortunately they are not evaluated at the same time. Make evaluates its constructs before passing the recipe to the shell, once for all, not during the execution of the recipe by the shell. When expanding your recipe (before passing it to the shell) it transforms it into:
for f in ../dir1/file1 ../dir2/file2; do echo $f in ./; done
because when make expands $(dir $${f}) it first expands the parameter:
$(dir ${f})
and then, as there is no / in the string ${f}, the expansion of the dir function replaces it with ./. From the GNU make manual:
$(dir names…)
Extracts the directory-part of each file name in names. The directory-part of the file name is everything up through (and
including) the last slash in it. If the file name contains no slash,
the directory part is the string ./. For example,
$(dir src/foo.c hacks)
produces the result src/ ./.
Anyway, mixing make and shell constructs is usually not what you want to do (there are exceptions). You could use only shell constructs:
all:
#for f in $(FILES); do \
echo $$f in $$(dirname $$f) ; \
done
Or you could use a static pattern rule to get completely rid of the shell loop and let make iterate over a list of similar targets:
FILES = ../dir1/file1 ../dir2/file2
FOOS = $(addsuffix .foo,$(FILES))
.PHONY: all $(FOOS)
all: $(FOOS)
$(FOOS): %.foo:
#echo $* in $(dir $*)

simple loop over files in some directory makefile

I can easily print all the files inside some directory from bash:
$ cat go.sh
BASEDIR=~/Downloads
MYDIR=${BASEDIR}/ddd
for f in $(ls ${MYDIR}); do echo $f; done
$ ./go.sh
m.txt
d.txt
When I try to do a similar thing from makefile it doesn't work well:
$ cat makefile
BASEDIR = ${HOME}/Downloads
MYDIR = ${BASEDIR}/ddd
all:
for f in $(ls ${MYDIR}); do echo ${f}; done
$ make
for f in ; do echo ; done
And here is another trial that doesn't work:
$ cat makefile
BASEDIR = ${HOME}/Downloads
MYDIR = ${BASEDIR}/ddd
all:
for f in $(shell ls ${MYDIR}); do echo ${f}; done
$ make
for f in d.txt m.txt; do echo ; done
Maybe you can do it purely Makefile way?
MYDIR = .
list: $(MYDIR)/*
#echo $^
You can still run command from Makefile like this
MYDIR = .
list: $(MYDIR)/*
for file in $^ ; do \
echo "Hello" $${file} ; \
done
If I were you, I'd rather not mix Makefile and bash loops based on $(shell ...). I'd rather pass dir name to some script and run loop there - inside script.
Also almost "true way" from documentation
TEMPLATES_DIR = ./somedir
list:
$(foreach file, $(wildcard $(TEMPLATES_DIR)/*), echo $(file);)
Here is the edited answer based on #Oo.oO:
$ cat makefile
BASEDIR = ${HOME}/Downloads
MYDIR = ${BASEDIR}/ddd
all:
#for f in $(shell ls ${MYDIR}); do echo $${f}; done
$ make
d.txt
m.txt
There is a little problem with #Oo.oO's answer.
If there is any file/folder has the same name with a target in makefile, and that target has some prerequisites, and you want to loop through that folder, you will get that target recipe being executed.
For example: if you have a folder named build, and you have a rule like:
build: clean server client
clean:
#echo project cleaned!
server:
#echo server built!
client:
#echo client built!
To loop through the folder contains that special build folder, let's says you have the following rules:
MYDIR = .
ls: $(MYDIR)/*
#echo $^
The result will be:
$ make ls
project cleaned!
server built!
client built!
build Makefile
I would suggest to use #Mike Pylypyshyn's solution. According to the make documentation, the foreach function is more suitable in this case.

Makefile loop results from ls command

I have a list of text files, which when executing the following command:
ls *.txt
Will result in something like:
foo.txt bar.txt baz.txt
Now if I want to have the output be something like:
File: foo.txt
File: bar.txt
File: baz.txt
How would I achieve that in a Makefile?
I've been trying:
txtfiles = $$(ls *.txt)
list: $(txtfiles)
$(txtfiles):
echo "File:" $#
But when running this with make list, it results with:
File: $
File: $
I was thinking of trying to achieve this using placeholders % but wasn't sure how to do that.
Note: instead of $$(ls *.txt) I'm guessing I could use $(wildcard *.txt) ?
Note: instead of $$(ls *.txt) I'm guessing I could use $(wildcard *.txt)?
You have to.
Also add .PHONY: $(txtfiles) before $(txtfiles): rule to make an explicit request.
OK so I figured this out:
my_list = $(addsuffix .dep, $(wildcard *.txt))
print_list: $(my_list)
%.dep: %
#echo "Text File:" $<
Run it with make print_list to get back the expected result.

Using make to place message files in correct directory structure

I have an existing project where I am adding gettext support. Unfortunately, due to the project's structure and limitations, I cannot go through the recommended route of changing the project over to automake. Also unfortunately, my make-fu is pretty weak and I'm having troubles implementing rules to build my output archive:
Take all .po files in the msg subdir and run msgfmt on them to produce .mo files (in my target dir)
Put the .po files in the directory structure expected by gettext: (dir)/(locale)/LC_MESSAGES/(domainname).mo
Here's what I have so far
MSGSRC=msg/*.po
MSGOBJ=$(addprefix $(TARGET_BUILD_PATH)/$(target)/,$(MSG_SRC:.po=.mo))
$(TARGET_BUILD_PATH)/$(target)/msg/%.mo: msg/%.po
msgfmt -c $< -o $#
# Put in correct place
mkdir -p $(TARGET_BUILD_PATH)/$(target)/msg/$(*F)/LC_MESSAGES
cp $# $(TARGET_BUILD_PATH)/$(target)/msg/$(*F)/LC_MESSAGES/myapp.mo
archive: $(MSGOBJ) (other objs....)
(make the archive tarball...)
The problem with the existing code is that for some reason $(*F) comes out just * instead of the locale name (the .po files are named en_US.po, etc). It also seems incorrect because the target should be the real target, not the hacky msgfmt and copy I have. The directory structure is important because the makefile is run a few times for different cross-compiles ($(target)) and the output is archived into a tarball for installation on the devices.
I assume you can use GNU make.
First of all, let make expand the wildcards. This is important for later postprocessing:
MSGSRC=$(wildcard msg/*.po)
Now you should get lists of file names in MSGSRC and MSGOBJ. Additionally, the make manual marks $(F) as obsolete, and $ (the stem of the name) should contain just the locale. So,
mkdir -p $(TARGET_BUILD_PATH)/$(target)/msg/$*/LC_MESSAGES
should do the trick just fine, the same for the cp rule.
I do it slightly different. Here are my po files:
$ find msg -type f
msg/bg_BG.po
msg/de_DE.po
Here's the Makefile:
MSGLANGS=$(notdir $(wildcard msg/*po))
MSGOBJS=$(addprefix locale/,$(MSGLANGS:.po=.UTF-8/LC_MESSAGES/appname.mo))
gettext: $(MSGOBJS)
locale/%.UTF-8/LC_MESSAGES/appname.mo: msg/%.po
mkdir -p $(dir $#)
msgfmt -c -o $# msg/$*.po
And these are the resulting mo files:
$ find locale -type f
locale/bg_BG.UTF-8/LC_MESSAGES/appname.mo
locale/de_DE.UTF-8/LC_MESSAGES/appname.mo

Resources