Directory wildcard in Makefile pattern rule - makefile

I'm trying to create a Makefile that will compile terminfo files residing in a directory via tic. tic also copies the termcap files it creates automatically to a system- or user-specific destination folder. For a regular user if the terminfo file is e.g. screen-256color-bce-s.terminfo, it will be compiled and copied to ~/.terminfo/s/screen-256color-bce-s. So it will look something like this:
terminfo/screen-256color-bce-s.terminfo => /home/user/.terminfo/s/screen-256color-bce-s
terminfo/screen-256color-s.terminfo => /home/user/.terminfo/s/screen-256color-s
If I put something like this into my Makefile:
TISRC = $(wildcard terminfo/*.terminfo)
TIDST = $(foreach x, $(TISRC), $(HOME)/.terminfo/$(shell basename $x|cut -c 1)/$(shell basename $x .terminfo))
$(HOME)/.terminfo/s/%: terminfo/%.terminfo
#echo "$< => $#"
#tic $<
install: $(TIDST)
it works. However, I'd like to make it general, and use a wildcard in the target, i.e.:
$(HOME)/.terminfo/**/%: terminfo/%.terminfo
#echo "$< => $#"
#tic $<
to be able to add terminfo files to my local repository. The above, however, does not work. How can I specify a wildcard directory in a pattern rule?

You can do that with GNU Make Secondary Expansion feature:
all : ${HOME}/.terminfo/x/a
all : ${HOME}/.terminfo/y/b
.SECONDEXPANSION:
${HOME}/.terminfo/%: terminfo/$$(notdir $$*).terminfo
#echo "$< ---> $#"
Output:
[~/tmp] $ make
terminfo/a.terminfo ---> /home/max/.terminfo/x/a
terminfo/b.terminfo ---> /home/max/.terminfo/y/b
As a side note, make provides some path manipulation functions, so that you don't really need to invoke the shell for that.

I don't think you can use wildcards the way you're trying to, but if you don't mind using eval trickery, you can get the effect you're shooting for without having to spell out all the directory paths explicitly:
TISRC = $(wildcard terminfo/*.terminfo)
BASENAMES = $(notdir $(basename ${TISRC}))
MKDST = ${HOME}/.terminfo/$(shell echo $1 | cut -c 1)/$1
TIDST := $(foreach s,${BASENAMES},$(call MKDST,$s))
DIRLTRS = $(notdir $(patsubst %/,%,$(sort $(dir ${TIDST}))))
install: ${TIDST}
# $1 - Directory Name
# $2 - File name
define T
${HOME}/.terminfo/$1/$2 : terminfo/$2.terminfo
#echo "$$< => $$#"
tic $$<
endef
# This is the tricky part: use template T to make the rules you need.
$(foreach d,${DIRLTRS},$(foreach f,${BASENAMES},$(eval $(call T,$d,$f))))

Related

Using different list elements for each usage of implicit recipe

There was a regular Makefile. Now I'm trying to enhance it to execute implicit recipe on different hosts via ssh. I have to do that using regular make/gmake as it's prohibited to install any 3rd-party packages on machines in that network.
Here is the concept:
SHELL := bash
SOURCES = $(wildcard *.in)
TARGETS = $(SOURCES:.in=.out)
TARGET_HOSTS := localhost foo bar
ONE_OF_TARGET_HOSTS := localhost
ifeq (${PROCESS_MODE},)
PROCESS_MODE := LOCAL
endif
all: $(TARGETS)
ifeq (${PROCESS_MODE},LOCAL)
%.out: %.in
echo "Running $# on host $(shell hostname)"
echo $* > $#
# some other stuff to run
else ifeq (${PROCESS_MODE},SSH)
DONT_PASS_ENVIRONMENT_VARIABLES := BASH.*|DISPLAY|EC_.+|HOST|HOSTNAME|MACHTYPE|OSTYPE|PROCESS_MODE|PPID|PWD|SHELL|SHLVL|SHELLOPTS|SSH_.+|TZ
SHELL_ENV := $(shell export -p | cut -b 12- | grep = | grep -Ev "^($(DONT_PASS_ENVIRONMENT_VARIABLES))")
%.out: %.in
echo "Pushing job $# on remote host $(ONE_OF_TARGET_HOSTS)"
#ssh $(ONE_OF_TARGET_HOSTS) 'cd "$(shell pwd)" && env $(SHELL_ENV) $(MAKE) PROCESS_MODE=LOCAL $#'
endif
It works in general. By default it executes everything locally (as it did before). If I run make PROCESS_MODE=SSH it executes rule for each .in file via ssh.
The problem is - right now it temporarily uses the same host (which is stored in ONE_OF_TARGET_HOSTS variable) for all ssh spawns. But I need to run each instance of rule on different hosts, which are defined in TARGET_HOSTS variable.
Let's say there are several files: a.in, b.in, c.in, d.in, e.in, etc. I want them to be processed as:
`a.in` - on localhost,
`b.in` - on foo,
`c.in` - on bar,
`d.in` - on localhost,
`e.in` - on foo,
and so on...
(the actual files order/assignment does not matter actually; they simply should be different and use all hosts evenly)
Is that possible? Or maybe is there any other way to achieve that?
In the following GNU make solution we first build a balanced list of [name.out]host tokens from the list of name.out targets. You can use any strings X and Y instead of [ and ], as long as for any name.out, Xname.outY is not a prefix of another token. Adapt to your situation.
In the recipe of %.out we recover the corresponding token with $(filter [$#]%,$(TOKEN_LIST)) and extract the hostname part with $(patsubst [$#]%,%,...). We assign it to shell variable host and use this variable in the echo and ssh commands.
There are 2 important aspects to remember:
make expands the recipe before passing it to the shell. So, when using the value of a shell variable we must write $$host instead of $host. After the make expansion it will become $host, what we want to pass to the shell, and no just ost.
Each line of a recipe is executed by a different shell. In order to use a shell variable in several recipe lines we must join them together with ; (or &&, as you wish) such that they become one single line, executed by one single shell. But for better readability we can use the line continuation (with a trailing \).
TOO_LONG_HOST_LIST := $(foreach t,$(TARGETS),$(TARGET_HOSTS))
HOST_LIST := $(wordlist 1,$(words $(TARGETS)),$(TOO_LONG_HOST_LIST))
TOKEN_LIST := $(join $(patsubst %,[%],$(TARGETS)),$(HOST_LIST))
# ...
%.out: %.in
#host=$(patsubst [$#]%,%,$(filter [$#]%,$(TOKEN_LIST))); \
echo "Pushing job $# on remote host $$host"; \
ssh $$host 'cd "$(CURDIR)" && env $(SHELL_ENV) $(MAKE) PROCESS_MODE=LOCAL $#'
Demo:
$ touch a.in b.in c.in d.in e.in f.in g.in
$ make TARGETS="a.out b.out c.out d.out e.out f.out g.out" TARGET_HOSTS="1 2 3"
Pushing job a.out on remote host 1
Pushing job b.out on remote host 2
Pushing job c.out on remote host 3
Pushing job d.out on remote host 1
Pushing job e.out on remote host 2
Pushing job f.out on remote host 3
Pushing job g.out on remote host 1
Note: remember that the $$ and the line continuations (the trailing ; \) are essential to guarantee the proper expansion of the shell variable host and its availability in all lines of the recipe.
Note: using the shell make function in a recipe, which is already a shell script, is almost always wrong; I replaced $(shell pwd) by the GNU make variable $(CURDIR).
Meanwhile I came out to this solution:
COUNT_TARGETS := $(words $(TARGETS))
# repeat hosts in the list to have there enough hosts for all targets
define expand-hosts
$(eval COUNT_TARGET_HOSTS := $(words $(TARGET_HOSTS)))
$(if $(filter $(shell test $(COUNT_TARGET_HOSTS) -lt $(COUNT_TARGETS) && echo $$?),0), \
$(eval TARGET_HOSTS += $(TARGET_HOSTS)) \
$(call expand-hosts) \
)
endef
$(eval $(call expand-hosts))
# assign host to all targets
assign-hosts = $(1): SSH_TARGET_HOST = $(2)
$(foreach i, $(shell seq $(words $(TARGETS))), \
$(eval $(call assign-hosts,$(word $(i), $(TARGETS)),$(word $(i), $(TARGET_HOSTS)))) \
)
...
%.out: %.in
#echo "Pushing job $# on remote host $(SSH_TARGET_HOST)"
#ssh $(SSH_TARGET_HOST) 'cd "$(shell pwd)" && env $(SHELL_ENV) $(MAKE) -j1 PROCESS_MODE=LOCAL $#'
This way allows me to have all weird stuff outside the recipe.
But I will likely adopt the way of hosts list expansion from #RenaudPacalet answer.

Makefile: building LaTeX files in subdirectories with two versions of each file

I have the following folder structure
1st-grade-math-class/
common/
mystyle.sty
mysubstyle.sty
fonts/
font1.ttf
font2.ttf
font3.ttf
week01/
handout.tex
image1.pdf
image2.pdf
week02/
handout.tex
image1.pdf
image2.pdf
...
week13/
handout.tex
output/
[empty]
And I would like to create a Makefile - in the best way - to do the following:
make sure I include the common directory properly in TEXINPUTS
compile the handout.tex into a PDF (using either pdflatex or xelatex) and have it in the output directory as week01-handout-student.pdf
compile the handout.tex with a line of LaTeX prepended to the beginning of the file (that sets a flag) into a PDF and have it in the output directory as week01-handout-teacher.pdf
clean everything up (the log, aux, etc. files)
I am not sure I know how to this in any other way than manually duplicating an elementary Makefile/bash script in every subdirectory, then calling each of them one by one with a for loop.
I would appreciate help on how to build this process, ideally with a single Makefile in the root directory. Thanks.
UPDATE: I purposefully did not want to give any details about how I compile LaTeX, in case somebody has a better suggestion than my current usage. Right now I am using Latexmk (which is already a make-like wrapper of LaTeX):
latexmk -pdf file.tex generates file.pdf
to add the line of code, I do a simple echo "line of code" > temp.tex and cat handout.tex >> temp.tex, then the same latexmk command
latexmk -c file.tex in a directory cleans all temporary files used to compile file.tex
TEXINPUTS is the TeX path variable, to let TeX find (in its path) the style files: I do TEXINPUTS=full-path-to/common and then export TEXINPUTS before compiling anything.
If anybody has a better suggestion, I am a willing taker.
Something like this should do what you want I believe:
OUTDIR := output
# Tell make to export this environment variable to all recipe lines it runs.
export TEXINPUTS := $(abspath common)
# Get the list of all of our week directories.
weekdirs := $(wildcard week*)
#$(info weekdirs:$(weekdirs))
# Create student output filenames from week directory names.
STUDENT_HANDOUTS := $(patsubst %,$(OUTDIR)/%-handout-student.pdf,$(weekdirs))
#$(info STUDENT_HANDOUTS:$(STUDENT_HANDOUTS))
# Create teacher output filenames from week directory names.
TEACHER_HANDOUTS := $(patsubst %,$(OUTDIR)/%-handout-teacher.pdf,$(weekdirs))
#$(info TEACHER_HANDOUTS:$(TEACHER_HANDOUTS))
# Default target depends on all output files.
all: $(STUDENT_HANDOUTS) $(TEACHER_HANDOUTS)
# Pattern rule for building pdf files.
%.pdf:
#echo + Making $# from $^
#echo cd $(#D) && echo latexmx -pdf $(abspath $<)
#echo cd $(#D) && echo latexmk -c $(abspath $<)
# Static pattern rule mapping student output files to input files.
$(STUDENT_HANDOUTS) : $(OUTDIR)/%-handout-student.pdf : %/handout.tex
# Pattern rule to generate temporary input files from original input files.
%/handout-tmp.tex: %/handout.tex
#echo echo 'line of code' '>' $#
#echo cat $^ '>>' $#
# Static pattern rule mapping teacher output files to (temporary) input files.
$(TEACHER_HANDOUTS) : $(OUTDIR)/%-handout-teacher.pdf : %/handout-tmp.tex
Uncomment the $(info) lines to see a bit of how the variables are put together.
This uses latexmk -c to clean up auxiliary files after creating the output files.
Technique 1. The echo foo >$#; cat $< >>$# thing is something I've done before; I think it's reasonable.
Technique 2. Another technique is to have your .tex document, or a package file it uses, include a line like:
\InputIfFileExists{lecturenotes.config}{}{}
That allows you to make certain adjustments in a makefile, as in for example:
# any bits of configuration that should be in all the .config files
SWITCHES=\makeindex
%-notes.pdf: %.tex
printf '$(SWITCHES)\\ExecuteOptions{sidenotes}\n' >lecturenotes.config
TEXINPUTS=styles: pdflatex $<
mv ${<:.tex=.pdf} $#
rm lecturenotes.config
%-single.pdf: %.tex
printf '$(SWITCHES)\\ExecuteOptions{oneside}\n' >lecturenotes.config
TEXINPUTS=styles: pdflatex $<
mv ${<:.tex=.pdf} $#
rm lecturenotes.config
Technique 3. A third technique for controlling LaTeX from outside is to include in your document (or in a package file):
\newif\ifdemonstrator
\expandafter\ifx\csname demonstrator\endcsname\relax
\demonstratorfalse
\else
\demonstratortrue
\fi
...
\ifdemonstrator
\typeout{Demonstrator is TRUE}
\else
\typeout{Demonstrator is FALSE}
\fi
Then call latex with:
%-plain.pdf: %.tex
latex $<
mv ${<:.tex=.pdf} $#
%-demo.pdf: %.tex
latex --jobname ${<:.tex=} '\def\demonstrator{x}\input{$<}`
mv ${<:.tex=.pdf} $#
Technique 1 is a bit of a blunt instrument, but if that's all that's needed, it's pretty lightweight.
Technique 2 is probably the most neatly engineered, but it is slightly more effort.
Technique 3 is probably the one I use most in this sort of circumstance.

How to get current relative directory of your Makefile?

I have a several Makefiles in app specific directories like this:
/project1/apps/app_typeA/Makefile
/project1/apps/app_typeB/Makefile
/project1/apps/app_typeC/Makefile
Each Makefile includes a .inc file in this path one level up:
/project1/apps/app_rules.inc
Inside app_rules.inc I'm setting the destination of where I want the binaries to be placed when built. I want all binaries to be in their respective app_type path:
/project1/bin/app_typeA/
I tried using $(CURDIR), like this:
OUTPUT_PATH = /project1/bin/$(CURDIR)
but instead I got the binaries buried in the entire path name like this: (notice the redundancy)
/project1/bin/projects/users/bob/project1/apps/app_typeA
What can I do to get the "current directory" of execution so that I can know just the app_typeX in order to put the binaries in their respective types folder?
The shell function.
You can use shell function: current_dir = $(shell pwd).
Or shell in combination with notdir, if you need not absolute path:
current_dir = $(notdir $(shell pwd)).
Update.
Given solution only works when you are running make from the Makefile's current directory.
As #Flimm noted:
Note that this returns the current working directory, not the parent directory of the Makefile. For example, if you run cd /; make -f /home/username/project/Makefile, the current_dir variable will be /, not /home/username/project/.
Code below will work for Makefiles invoked from any directory:
mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
current_dir := $(notdir $(patsubst %/,%,$(dir $(mkfile_path))))
As taken from here;
ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
Shows up as;
$ cd /home/user/
$ make -f test/Makefile
/home/user/test
$ cd test; make Makefile
/home/user/test
If you are using GNU make, $(CURDIR) is actually a built-in variable. It is the location where the Makefile resides the current working directory, which is probably where the Makefile is, but not always.
OUTPUT_PATH = /project1/bin/$(notdir $(CURDIR))
See Appendix A Quick Reference in http://www.gnu.org/software/make/manual/make.html
THIS_DIR := $(dir $(abspath $(firstword $(MAKEFILE_LIST))))
I like the chosen answer, but I think it would be more helpful to actually show it working than explain it.
/tmp/makefile_path_test.sh
#!/bin/bash -eu
# Create a testing dir
temp_dir=/tmp/makefile_path_test
proj_dir=$temp_dir/dir1/dir2/dir3
mkdir -p $proj_dir
# Create the Makefile in $proj_dir
# (Because of this, $proj_dir is what $(path) should evaluate to.)
cat > $proj_dir/Makefile <<'EOF'
path := $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
cwd := $(shell pwd)
all:
#echo "MAKEFILE_LIST: $(MAKEFILE_LIST)"
#echo " path: $(path)"
#echo " cwd: $(cwd)"
#echo ""
EOF
# See/debug each command
set -x
# Test using the Makefile in the current directory
cd $proj_dir
make
# Test passing a Makefile
cd $temp_dir
make -f $proj_dir/Makefile
# Cleanup
rm -rf $temp_dir
Output:
+ cd /tmp/makefile_path_test/dir1/dir2/dir3
+ make
MAKEFILE_LIST: Makefile
path: /private/tmp/makefile_path_test/dir1/dir2/dir3
cwd: /tmp/makefile_path_test/dir1/dir2/dir3
+ cd /tmp/makefile_path_test
+ make -f /tmp/makefile_path_test/dir1/dir2/dir3/Makefile
MAKEFILE_LIST: /tmp/makefile_path_test/dir1/dir2/dir3/Makefile
path: /tmp/makefile_path_test/dir1/dir2/dir3
cwd: /tmp/makefile_path_test
+ rm -rf /tmp/makefile_path_test
NOTE: The function $(patsubst %/,%,[path/goes/here/]) is used to strip the trailing slash.
I tried many of these answers, but on my AIX system with gnu make 3.80 I needed to do some things old school.
Turns out that lastword, abspath and realpath were not added until 3.81. :(
mkfile_path := $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
mkfile_dir:=$(shell cd $(shell dirname $(mkfile_path)); pwd)
current_dir:=$(notdir $(mkfile_dir))
As others have said, not the most elegant as it invokes a shell twice, and it still has the spaces issues.
But as I don't have any spaces in my paths, it works for me regardless of how I started make:
make -f ../wherever/makefile
make -C ../wherever
make -C ~/wherever
cd ../wherever; make
All give me wherever for current_dir and the absolute path to wherever for mkfile_dir.
The simple, correct, modern way:
For GNU make >= 3.81, which was introduced in 2006
ROOT_DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST))))
MAKEFILE_LIST changes as include files come in and out of scope. The last item is the current file.
lastword plucks the last item (Makefile name, relative to pwd)
realpath is built-in to make, and resolves to a canonical path from filesystem root
dir trims off the filename, leaving just the directory.
Here is one-liner to get absolute path to your Makefile file using shell syntax:
SHELL := /bin/bash
CWD := $(shell cd -P -- '$(shell dirname -- "$0")' && pwd -P)
And here is version without shell based on #0xff answer:
CWD := $(abspath $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST))))))
Test it by printing it, like:
cwd:
#echo $(CWD)
As far as I'm aware this is the only answer here that works correctly with spaces:
space:=
space+=
CURRENT_PATH := $(subst $(lastword $(notdir $(MAKEFILE_LIST))),,$(subst $(space),\$(space),$(shell realpath '$(strip $(MAKEFILE_LIST))')))
It essentially works by escaping space characters by substituting ' ' for '\ ' which allows Make to parse it correctly, and then it removes the filename of the makefile in MAKEFILE_LIST by doing another substitution so you're left with the directory that makefile is in. Not exactly the most compact thing in the world but it does work.
You'll end up with something like this where all the spaces are escaped:
$(info CURRENT_PATH = $(CURRENT_PATH))
CURRENT_PATH = /mnt/c/Users/foobar/gDrive/P\ roje\ cts/we\ b/sitecompiler/
Example for your reference, as below:
The folder structure might be as:
Where there are two Makefiles, each as below;
sample/Makefile
test/Makefile
Now, let us see the content of the Makefiles.
sample/Makefile
export ROOT_DIR=${PWD}
all:
echo ${ROOT_DIR}
$(MAKE) -C test
test/Makefile
all:
echo ${ROOT_DIR}
echo "make test ends here !"
Now, execute the sample/Makefile, as;
cd sample
make
OUTPUT:
echo /home/symphony/sample
/home/symphony/sample
make -C test
make[1]: Entering directory `/home/symphony/sample/test'
echo /home/symphony/sample
/home/symphony/sample
echo "make test ends here !"
make test ends here !
make[1]: Leaving directory `/home/symphony/sample/test'
Explanation, would be that the parent/home directory can be stored in the environment-flag, and can be exported, so that it can be used in all the sub-directory makefiles.
use {} instead of ()
cur_dir=${shell pwd}
parent_dir=${shell dirname ${shell pwd}}}
Solution found here : https://sourceforge.net/p/ipt-netflow/bugs-requests-patches/53/
The solution is : $(CURDIR)
You can use it like that :
CUR_DIR = $(CURDIR)
## Start :
start:
cd $(CUR_DIR)/path_to_folder
update 2018/03/05
finnaly I use this:
shellPath=`echo $PWD/``echo ${0%/*}`
# process absolute path
shellPath1=`echo $PWD/`
shellPath2=`echo ${0%/*}`
if [ ${shellPath2:0:1} == '/' ] ; then
shellPath=${shellPath2}
fi
It can be executed correct in relative path or absolute path.
Executed correct invoked by crontab.
Executed correct in other shell.
show example, a.sh print self path.
[root#izbp1a7wyzv7b5hitowq2yz /]# more /root/test/a.sh
shellPath=`echo $PWD/``echo ${0%/*}`
# process absolute path
shellPath1=`echo $PWD/`
shellPath2=`echo ${0%/*}`
if [ ${shellPath2:0:1} == '/' ] ; then
shellPath=${shellPath2}
fi
echo $shellPath
[root#izbp1a7wyzv7b5hitowq2yz /]# more /root/b.sh
shellPath=`echo $PWD/``echo ${0%/*}`
# process absolute path
shellPath1=`echo $PWD/`
shellPath2=`echo ${0%/*}`
if [ ${shellPath2:0:1} == '/' ] ; then
shellPath=${shellPath2}
fi
$shellPath/test/a.sh
[root#izbp1a7wyzv7b5hitowq2yz /]# ~/b.sh
/root/test
[root#izbp1a7wyzv7b5hitowq2yz /]# /root/b.sh
/root/test
[root#izbp1a7wyzv7b5hitowq2yz /]# cd ~
[root#izbp1a7wyzv7b5hitowq2yz ~]# ./b.sh
/root/./test
[root#izbp1a7wyzv7b5hitowq2yz ~]# test/a.sh
/root/test
[root#izbp1a7wyzv7b5hitowq2yz ~]# cd test
[root#izbp1a7wyzv7b5hitowq2yz test]# ./a.sh
/root/test/.
[root#izbp1a7wyzv7b5hitowq2yz test]# cd /
[root#izbp1a7wyzv7b5hitowq2yz /]# /root/test/a.sh
/root/test
[root#izbp1a7wyzv7b5hitowq2yz /]#
old:
I use this:
MAKEFILE_PATH := $(PWD)/$({0%/*})
It can show correct if executed in other shell and other directory.
One line in the Makefile should be enough:
DIR := $(notdir $(CURDIR))

How can I evaluate a variable created inside a rule? (with eval)

I'm new to makefiles, and I'm stuck in this problem: how to create symbolic links to all the files in a directory found, all this inside a rule:
I want to know if a call to "find" retrieves any result. If I assign the result to a variable with "eval" (what is the only way I know to set a variable inside a rule), "ifdef" evaluation doesn't work.
I have also tried comparing to "" with "ifneq" But doesn't work.
Probably I'm following a wrong direction, so if someone has an alternative approach, I'll be very thankful.
Here the whole Makefile: (interesting line marked with ######)
Thank you a lot!
DEFAULT_YCS_ROOT := /opt/yujin
all:
ifdef YCS_ROOT
#echo Looking for Yujin maps in ${YCS_ROOT}
else
$(eval YCS_ROOT := ${DEFAULT_YCS_ROOT})
#echo WARNING: YCS_ROOT is undefined. Looking for Yujin maps in default path ${YCS_ROOT}
endif
$(eval MAPS_DIR := $(shell find ${YCS_ROOT} -name yujin_maps))
#echo ${MAPS_DIR} "${MAPS_DIR}"
ifdef MAPS_DIR ######
###### misguided alternative.... ifneq (${MAPS_DIR}, " ")
$(eval DEST_DIR := $(shell pwd)/resources/yaml/stage/maps)
#echo Creating symbolic links on ${DEST_DIR} for every PGM map on ${MAPS_DIR}/maps
rm ${DEST_DIR}/*.pgm
for f in ${MAPS_DIR}/maps/*.pgm; do ln -s $$f ${DEST_DIR}/; done
else
#echo ERROR: Yujin maps package not found in ${YCS_ROOT}. Define YCS_ROOT variable properly
endif
First, assuming you are using GNUMake, there is an easier way to handle YCS_ROOT and DEFAULT_YCS_ROOT:
DEFAULT_YCS_ROOT := /opt/yujin
YCS_ROOT ?= DEFAULT_YCS_ROOT
This will assign the value of DEFAULT_YCS_ROOT to YCS_ROOT if and only if YCS_ROOT is undefined.
For the variables MAPS_DIR and DEST_DIR, you are mixing Make syntax with shell syntax. I think the easiest approach is to define both variables outside the rule:
MAPS_DIR := $(shell find ${YCS_ROOT} -name yujin_maps)
DEST_DIR := $(shell pwd)/resources/yaml/stage/maps
You should test all of this to make certain that the variables have the values you expect. It seems that you expect only one directory in MAPS_DIR; I will follow that convention, but it is unsafe. Once the makefile so far is working, you can add the rule. You can use a shell loop:
all:
rm ${DEST_DIR}/*.pgm
for f in ${MAPS_DIR}/maps/*.pgm; do echo ln -s $$f ${DEST_DIR}/; done
or use Make syntax (which gives some advantages):
MAPS := $(wildcard $(MAPS_DIR)/maps/*.pgm)
DEST_MAPS := $(patsubst $(MAPS_DIR)/maps/%.pgm, ${DEST_DIR}/%.pgm, $(MAPS))
all: $(DEST_MAPS)
${DEST_DIR}/%.pgm: $(MAPS_DIR)/maps/%.pgm
rm $#
ln -s $< ${DEST_DIR}/

GNU Make (running program on multiple files)

I'm stuck trying to figure out how to run a program, on a set of files, using GNU Make:
I have a variable that loads some filenames alike this:
FILES=$(shell ls *.pdf)
Now I'm wanting to run a program 'p' on each of the files in 'FILES', however I can't seem to figure how to do exactly that.
An example of the 'FILES' variable would be:
"a.pdf k.pdf omg.pdf"
I've tried the $(foreach,,) without any luck, and #!bin/bash like loops seem to fail.
You can do a shell loop within the command:
all:
for x in $(FILES) ; do \
p $$x ; \
done
(Note that only the first line of the command must start with a tab, the others can have any old whitespace.)
Here's a more Make-style approach:
TARGETS = $(FILES:=_target)
all: $(TARGETS)
#echo done
.PHONY: $(TARGETS)
$(TARGETS): %_target : %
p $*

Resources