dirname strange behavior with file list & Makefile - makefile

My directory :
$ ls *
Makefile
dir1:
file1.txt file2.txt
dir2:
file1.txt
The Makefile :
A = $(wildcard ./dir1/*)
A += $(wildcard ./dir1/*)
B = $(dirname $(A))
print-% : ; #echo $* = $($*)
Outputs of print rule :
$ make print-A
A = ./dir1/file1.txt ./dir1/file2.txt ./dir1/file1.txt ./dir1/file2.txt
$ make print-B
B =
I would like the B variable to have the list of my sub directory relative paths (./dir1/ and ./dir2/) why this don't work ? It is ok if there is only on file in A.

How is the $(dirname ...) macro defined? $(dir ...) may do what you expect.

Related

Dollar in conditional variable assignment operator in Makefile

Is it possible to pass value with single dollar from shell to Makefile, or I it is only way to put double dollar in bash and then to call make?
Makefile is:
HASH ?= $$6$$salt$$val
.PHONY: tst
tst:
echo '$(HASH)'
Command to run:
> make HASH='$6$salt$val'
echo 'altal'
altal
If I use double quotes, all is fine:
> make HASH='$$6$$salt$$val'
echo '$6$salt$val'
$6$salt$val
But is it possible do not make substitution $ to $$ in bash?
How about writing the initialisation within the file identical to the one coming from the command line? The below script demonstrates how to rewrite a variable with the override directive:
quote-one-level = $(eval override $1=$(subst $,$$$$,$(value $1)))
var-info = $(info $1=$(value $1) flavour=$(flavor $1) origin=$(origin $1))
A ?= $abc
$(call var-info,A)
$(call quote-one-level,A)
$(call var-info,A)
$(call var-info,B)
$(call quote-one-level,B)
$(call var-info,B)
export A
export B
all:
#echo A = '$(A)'
#echo B = '$(B)'
ifeq ($(MAKELEVEL),0)
$(MAKE)
endif
Inflating one $ to $$$$ (and not just $$) is necessary because the eval command literally generates make code, thereby obviously reducing the quoting level by one. Resulting output:
$ make B='$abc'
A=$abc flavour=recursive origin=file
A=$$abc flavour=recursive origin=override
B=$abc flavour=recursive origin=command line
B=$$abc flavour=recursive origin=override
A = $abc
B = $abc
make
make[1]: Entering directory
A=$abc flavour=recursive origin=environment
A=$$abc flavour=recursive origin=override
B=$abc flavour=recursive origin=command line
B=$$abc flavour=recursive origin=override
A = $abc
B = $abc
make[1]: Leaving directory
Try this:
In console:
export HASH='$6$salt$val'; make
in Makefile:
.PHONY: tst
tst:
#echo "$$HASH"
Result:
$6$salt$val

grep and sed in a Fortran Makefile

I have a Fortran code that can be compiled by the following Makefile:
FC = mpif90
FFLAGS := ...
TARGET = run1
SRC = main.f90 param.f90 ...
OBJ = $(SRC:.f90=.o)
all: $(TARGET)
$(TARGET): $(OBJ) $(FC) $(FFLAGS)
...
In the above, the TARGET can be run1 or run2 or run3, depending on the value of a variable in my script. For example, if I have the following in param.f90:
character(len=1), parameter :: case_num = "1"
Then, I want the TARGET variable to be set as run1. Now, the question is how to do that automatically without manually changing the Makefile every time?
I have tried something like
num = $(grep 'case_num = "' param.f90 | sed -n -e 's/^.*case_num = "//p' | cut -c1-1)
TARGET = run$num
in the Makefile, which doesn't work. I have checked that the grep and sed sentence is correct in a regular bash script. How to do this in a Fortran Makefile? Any help is appreciated.
Your makefile appears to a GNU Make makefile. To assign the output of a
shell command to a make variable, you need to use the $(shell ...) function,
like:
num := $(shell grep 'case_num = "' param.f90 | sed -n -e 's/^.*case_num = "//p' | cut -c1-1)
TARGET := run$(num)

Variable substitution in Makefile target dependency

I have a Makefile with targets that have associated dependencies. So I use lookup table like:
APPS = a b c
dependency.lookup.a := x
dependency.lookup.b := y
dependency.lookup.c := z
$(APPS): %: path/$(dependency.lookup.%).datafile
do something with $(dependency.lookup.$#)
This makefile gives me error.
*** No rule to make target 'path/.datafile'
Constraints: Only MinGW. can't use shell/MSYS. Also support FreeBSD.
This requires using Secondary Expansion feature:
.SECONDEXPANSION:
$(APPS): %: path/$$(dependency.loopup.$$*).datafile
#echo "$# depends on $^"
%.datafile : # Create one on demand for testing.
mkdir -p ${#D}
touch $#
Outputs:
mkdir -p path/
touch path/x.datafile
a depends on path/x.datafile
Alternatively, use regular dependencies:
a : path/x.datafile
b : path/y.datafile
c : path/z.datafile
$(APPS): % :
#echo "$# depends on $^"
The top 3 lines only add dependencies, the rule is specified separately. The output is the same.
I had the same problem but with rather long names. I didn't want to repeat them (in APPS = ...), so I used a generated auxiliary makefile.
Makefile:
all: build
include deps.mk
deps.mk: deps.txt deps.sh
./deps.sh <$< >$#
build: $(DEPS)
echo "DEPS are up-to-date." # or whatever
deps.sh:
#!/bin/bash
echo "# This file is generated by $0"
echo "DEPS ="
while read -r dst src; do
echo
echo "DEPS += $dst"
echo "$dst: $src"
echo -e '\t''cp $< $#' # or whatever
done
deps.txt (now only this file needs to be updated for new dependencies):
a x
b y
c z

Makefile. Multidimensional list?

I need to write a pattern rule for the following case:
There are 2 folders: A and B
Running the command python gen.py --a=A/file1.foo --b=file2.bar --c=file3.bar generates B/file1.foo
file1, file2 and file3 are different strings
Is there a way to group those filenames in some kind of a multidimensional array, so that all files are written exactly once (I'll use python syntax):
files = [["a1.foo", "a2.bar", "a3.bar"],
#...200 other groups...
["b1.foo", "b2.bar", "b3.bar"]]
and then the rule looks like this:
$(files): B/{reference 1 elem}: A/{1 elem} {2 elem} {3 elem}
python gen.py --a=A/{1 elem} --b={2 elem} --c={3 elem}
Any ideas how to archive it?
You can use standard make syntax for that:
all :
targets :=
define add_target
B/${1}: A/${1} ${2} ${3}
targets += B/${1}
endef
# Build dependencies.
$(eval $(call add_target,a1.foo,a2.bar,a3.bar))
# ...
$(eval $(call add_target,b1.foo,b2.bar,b3.bar))
# One generic rule for all ${targets}
${targets} : % :
#echo Making $# from $^
all : ${targets}
.PHONY: all
Note that these $(eval $(call add_target,...) are white-space sensitive, do not insert spaces in there.
If you would like make to create the directory for outputs automatically do:
${targets} : % : | B
B :
mkdir $#
Sometimes a little repetition isn't so bad really
targets := B/a1.foo B/b1.foo
.PHONY: all
all: $(targets)
$(targets): B/%: A/%
python gen.py --a=$< --b=$(word 2,$^) --c=$(word 3,$^)
B/a1.foo: a2.bar a3.bar
B/b1.foo: b2.bar b3.bar

Handling file names with space with GNU Make

I am using GNU Make (v3.80) and I must deal with file names with space. (There is nothing that I can do about the file names per se.) My makefile includes "normal" rules as well as static pattern rules. How can I deal with these files without changing too much my makefile?
I have read and studied previous related questions (here, here, here, and there) but none handle static pattern rules.
A solution combines the "question mark" trick by Mecklenburg combined with shell commands (find and sed) and not using the automatic variable $< but rather $*.
Imagine that you want to copy any files found in a data directory into a target directory, here are the rules (including a static pattern rule). Below is the complete makefile, here are the interesting bits:
EXEDIR = bin/vbcc-classic/AmiModRadio/
DATADIR = $(EXEDIR)data/
DATA = $(addprefix $(DATADIR), $(shell find data/ -mindepth 1 -maxdepth 1 -printf "%f\n" | sed 's/ /?/g'))
In the above, the last variable declaration, DATA contains the set of file names available in data/ with any space in their file names replaced by a ?. Each file name is prefixed by the target directory DATADIR and delimited by spaces.
space :=
space +=
replaceQuestionBySpace = $(subst ?,$(space),$1)
replaceSpaceByQuestion = $(subst $(space),?,$1)
In the above, following Mecklenburg, two functions are defined to replace spaces with question marks and vice-versa.
$(DATA) : $(DATADIR)% : $(wildcard data/%)
cp -f -R "$(call replaceQuestionBySpace,data/$*)" "$(call replaceQuestionBySpace,$#)"
The above is the important static pattern rule: the prerequisite of the rule $(wildcard data/%) ensures that Make will not copy the files in data at each call, but only if they have changed or disappeared from $(DATADIR).
$(wildcard data/%) works because % is replaced by the stem built by $(DATA) : $(DATADIR)% and because $(DATA) contains a set of file names without space (but question marks) and prefixed by $(DATADIR).
In the recipe, question marks are replaced by spaces to get the original file names back. $< cannot be used because it would match to $(wildcard data/%), which would result in an empty string. But, because the target contains $(DATADIR) and the file names, $* can be used (the file names) at the cost of adding data/ in front.
The complete makefile for example.
#
# AmiModRadio
# All of Aminet modules at your fingertips
#
#
#
# Copyright 2015, 2016 Tygre <tygre#chingu.asia>
#
# This file is part of AmiModRadio.
#
# AmiModRadio is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# AmiModRadio is distributed in the hope fthat it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with AmiModRadio. If not, see <http://www.gnu.org/licenses/>.
#
#
#
# Entirely developed on an Amiga!
# (Except for source code versioning...)
# tygre#chingu.asia
#
# Main paths
EXEDIR = bin/vbcc-classic/debug/
OBJECTSDIR = o/vbcc-classic/debug/
# Main paths for release ONLY
ifeq "$(strip $(filter dist, $(MAKECMDGOALS)))" "dist"
EXEDIR = bin/vbcc-classic/AmiModRadio/
OBJECTSDIR = o/vbcc-classic/release/
endif
# Secondary paths
EXE = $(EXEDIR)AmiModRadio
DATADIR = $(EXEDIR)data/
ICONSDIR = $(EXEDIR)icons/
IMAGESDIR = $(EXEDIR)images/
MODULESDIR = $(EXEDIR)modules/
# (Re)Source files
SOURCES = $(wildcard *.c)
OBJECTS = $(addprefix $(OBJECTSDIR), $(SOURCES:.c=.o))
DATA = $(addprefix $(DATADIR), $(shell find data/ -mindepth 1 -maxdepth 1 -printf "%f\n" | sed 's/ /?/g'))
ICONS = $(addprefix $(ICONSDIR), $(shell find icons/ -mindepth 1 -maxdepth 1 -printf "%f\n" | sed 's/ /?/g'))
IMAGES = $(addprefix $(IMAGESDIR), $(shell find images/ -mindepth 1 -maxdepth 1 -printf "%f\n" -type d | sed 's/ /?/g'))
# compiler and linker
CC = vbcc:bin/vc
LD = vbcc:bin/vc
# Because Make does not support spaces properly...
space :=
space +=
replaceQuestionBySpace = $(subst ?,$(space),$1)
replaceSpaceByQuestion = $(subst $(space),?,$1)
# ----------------------------------------
# target 'all' (default target, for debug)
all : CFLAGS += -DFORTIFY
all : $(EXEDIR)\
$(OBJECTSDIR)\
$(EXE)\
data\
images
# target 'dist' for release
dist : mostlyclean\
$(EXEDIR)\
$(OBJECTSDIR)\
$(EXE)\
data\
icons\
images\
modules\
$(EXEDIR:/=).lha
# target 'mostlyclean'
mostlyclean :
-rm $(EXE)
-rm $(OBJECTSDIR)*
# target 'clean'
clean : mostlyclean
-rm $(DATADIR)*
-rm $(ICONSDIR)*
-rm $(IMAGESDIR)*
-rm $(MODULESDIR)*
# ----------------------------------------
# Directories
$(EXEDIR) :
mkdir "$#"
$(OBJECTSDIR) :
mkdir "$#"
# Objects and executable
$(OBJECTS) : $(OBJECTSDIR)%.o : %.c
$(CC) $(shell vbccprefs) $(CFLAGS) -c $< -o $#
$(EXE) : $(OBJECTS)
$(LD) $(shell vbccprefs) -o $(EXE) $(OBJECTS)
# Data
data : $(DATADIR)\
$(DATA)\
$(DATADIR) :
mkdir "$#"
$(DATA) : $(DATADIR)% : $(wildcard data/%)
cp -f -R "$(call replaceQuestionBySpace,data/$*)" "$(call replaceQuestionBySpace,$#)"
# Icons
icons : $(ICONSDIR)\
$(ICONS)\
$(EXE).info\
$(EXEDIR:/=).info
$(ICONSDIR) :
mkdir "$#"
$(ICONS) : $(ICONSDIR)% : $(wildcard icons/%)
cp -f -R "$(call replaceQuestionBySpace,icons/$*)" "$(call replaceQuestionBySpace,$#)"
$(EXE).info :
cp -f -R "$(ICONSDIR)AmiModRadio2.tool.info" "$#"
$(EXEDIR:/=).info :
cp -f -R "$(ICONSDIR)AmiModRadio3.drawer.info" "$#"
# Images
images : $(IMAGESDIR)\
$(IMAGES)\
$(IMAGESDIR) :
mkdir "$#"
$(IMAGES) : $(IMAGESDIR)% : $(wildcard images/%)
cp -f -R "$(call replaceQuestionBySpace,images/$*)" "$(call replaceQuestionBySpace,$#)"
# Modules
modules : $(MODULESDIR)
$(MODULESDIR) :
mkdir "$#"
# LHA archive of 'dist'
$(EXEDIR:/=).lha : PARENTDIR = $(dir $(patsubst %/,%,$(EXEDIR)))
$(EXEDIR:/=).lha : EXENAME = $(notdir $(patsubst %/,%,$(EXEDIR)))
$(EXEDIR:/=).lha :
rm -f RAM:$(EXENAME).lha
/C/LHA -r -e a RAM:$(EXENAME).lha $(PARENTDIR) $(EXENAME).info $(EXENAME)/*

Resources