How do I wildcard a rule with SECONDEXPANSION in GNU Make? - makefile

# Docker Chain
src_files:=$(shell find src/ -name '*.py')
image_tags=$(shell find dkr/ -name '$(PROJECT).*.dockerfile' | xargs -n 1 basename -s '.dockerfile')
dkr/%.dockerfile: $(src_files)
docker build . -f $# -t $*
.SECONDEXPANSION:
$(PROJECT).base: dkr/$$#.dockerfile
.SECONDEXPANSION:
$(PROJECT).foo: $(PROJECT).base | dkr/$$#.dockerfile
.SECONDEXPANSION:
$(PROJECT).bar: $(PROJECT).base | dkr/$$#.dockerfile
publish-%:
$(MAKE) $*
./scripts/$#.sh $(RESOURCE_PREFIX) $* $(REGION)
publish:
$(foreach tag, $(image_tags), $(MAKE) publish-$(tag))
I would like to write the above as:
# Docker Chain
src_files:=$(shell find src/ -name '*.py')
image_tags=$(shell find dkr/ -name '$(PROJECT).*.dockerfile' | xargs -n 1 basename -s '.dockerfile')
dkr/%.dockerfile: $(src_files)
docker build . -f $# -t $*
.SECONDEXPANSION:
$(PROJECT).base: dkr/$$#.dockerfile
.SECONDEXPANSION:
$(PROJECT).%: $(PROJECT).base | dkr/$$#.dockerfile
publish-%:
$(MAKE) $*
./scripts/$#.sh $(RESOURCE_PREFIX) $* $(REGION)
publish:
$(foreach tag, $(image_tags), $(MAKE) publish-$(tag))
However, the wildcarding in the rule (via '%') does not work. I suppose I have misunderstood rule substitution, and am guessing it is due to the SECONEXPANSION.
But, my make intuition is telling me this should work, and it should be good.
How would I perform substitution in this scenario?

Related

String manipulation in makefiles

I've replaced my many .sh files from my previous question with a makefile, of sorts. I don't know much about makefiles, admittedly, but this seems to work:
MainPackage=jnitest
Main=SimpleJNITest
cFileName=rtm_simple
targetDir=classes
libDir=lib
srcDir=src
jdkDir="/home/user/local/java/jdk1.7.0_65"
java="$(jdkDir)/bin/java"
javah="$(jdkDir)/bin/javah"
javac="$(jdkDir)/bin/javac"
JAVAC_FLAGS=-d "$(targetDir)" -sourcepath "$(srcDir)" -cp "$(targetDir):$(libDir)/*"
JAVAH_FLAGS=-d "$(ccodeDir)" -cp "$(targetDir)" -jni
JAVA_FLAGS=-Djava.library.path="$(LD_LIBRARY_PATH):$(libDir)" -cp "$(targetDir):$(libDir)/*"
ccodeDir=$(srcDir)/ccode
CC=gcc
CC_FLAGS=-g -shared -fpic -I "$(jdkDir)/include" -I "$(jdkDir)/include/linux"
cFile="$(ccodeDir)/$(cFileName).c"
soFile="$(libDir)/lib$(cFileName).so"
dirs:
mkdir -p "$(targetDir)"
mkdir -p "$(libDir)"
java: dirs
$(javac) $(JAVAC_FLAGS) "$(srcDir)/$(MainPackage)/$(Main).java"
header:
$(javah) $(JAVAH_FLAGS) "$(MainPackage).$(Main)"
c:
$(CC) $(CC_FLAGS) $(cFile) -o $(soFile)
all:
$(MAKE) java
$(MAKE) header
$(MAKE) c
run:
$(java) $(JAVA_FLAGS) "$(MainPackage).$(Main)"
clean:
rm -rf classes
rm -rf lib
rm -f $(ccodeDir)/$(MainPackage)_$(Main).h
My problem, now, lies with MainPackage=jnitest.
So long as MainPackage is a single word, everything is fine.
However, when it is not, I'll be needing it once in slash notation for
$(javac) $(JAVAC_FLAGS) "$(srcDir)/$(MainPackage)/$(Main).java"
and once in dot notation for
$(java) $(JAVA_FLAGS) "$(MainPackage).$(Main)"
In bash, you could do something like
MainPackage_slashed=$(echo "$MainPackage" | tr '.' '/')
How do I insert one such transformation into a makefile?
You're looking for the subst function, see the GNU make manual.
Example:
foo=x.y.z
bar=$(subst .,/,$(foo))
$(info $(bar))
prints x/y/z.
You will need to use shell function in your Makefile like this:
MainPackage_slashed := $(shell echo "$(MainPackage)" | tr '.' '/')

How can I create the necessary out-directories for targets with make?

Say, in a Makefile, I have the following targets:
EXES=dir1/subdir1/abc dir1/subdir1/def dir1/subdir2/ghi dir1/subdir2/jkl dir2/subdir3/mno dir2/subdir3/pqr
Each item in $(EXES) represents a binary to be created. I want to make sure that the necessary directories (in the example: dir1/subdir1, dir1/subdir2, dir2/subdir3) are created if they are not existent.
How would I achieve this with gnu-make?
Use order-only prerequisites:
target: prerequisites | order-only-prerequisites
order-only-prerequisites:
recipe
Their recipe is executed only if they do not exist yet. Example:
$(BUILDDIR)/foo.o: src/foo.c | $(BUILDDIR)
$(CC) $(CFLAGS) -o $# $<
$(BUILDDIR):
mkdir -p $#
And if you want to extract the list of directories to create from the definition of your EXES variable:
$(EXES): | $(dir $(EXES))
$(dir $(EXES)):
mkdir -p $#
Or, to instantiate exactly one rule per target:
define DIR_rule
$(1): | $$(dir $(1))
endef
$(foreach e,$(EXES),$(eval $(call DIR_rule,$(e))))
$(dir $(EXES)):
mkdir -p $#
I finally found a solution with $(dir ...)
EXE_DIRS=$(dir $(EXES))
EXE_DIRS_UNIQUE=$(shell for DIR in $(EXE_DIRS); do echo $$DIR; done | sort | uniq)
$(shell for DIR in $(EXE_DIRS_UNIQUE); do if [ ! -d $$DIR ]; then mkdir -p $$DIR; fi; done)
all: $(EXES)

bash: cannot execute binary file while trying mesh_view in trimesh2

I am trying to run trimesh2 on my 64-bit debian,
but whenever I try to run mesh_view or any other mesh command, it says
cannot execute binary file
What should I do to run the mesh? The Makefile looks like this:
all win32 linux32 linux64 darwin32 darwin64 clean:
$(MAKE) -C libsrc $#
$(MAKE) -C gluit $#
$(MAKE) -C utilsrc $#
debug:
$(MAKE) -C libsrc DEBUG=y
$(MAKE) -C gluit DEBUG=y
$(MAKE) -C utilsrc DEBUG=y
FINDCMD = find trimesh2 -name 'OBJ*' -prune -o -name CVS -prune -o -type f -print
tar:
cd .. && tar zcvf trimesh2.tar.gz `$(FINDCMD) | sort`
zip:
cd .. && $(FINDCMD) | sort | zip -9 trimesh2 -#
.PHONY : all clean debug default tar win32 zip

Depend on subdirectory creation in makefile rule

I have a project with sources in the src/ directory and its subdirectories (e.g. src/foo/ and src/bar/), and the objects in the obj directory and the matching subdirectories (e.g. obj/foo/ and obj/bar/).
I use the following (smimplified) Makefile:
SOURCES=$(shell find src/ -type f -name '*.c')
OBJECTS=$(patsubst src/%.c,obj/%.o,$(SOURCES))
all: $(OBJECTS)
obj/%.o: src/%.c
gcc -c $< -o $#
The problem
The problem is that if obj/ or one of its subdirectories doesn't exist, I get the following error:
Fatal error: can't create obj/foo/f1.o: No such file or directory
How can I tell make that %.o files depend on the creation of their containing directory?
What I tried
One solution when there are no subdirectories is to use "order only prerequisites":
$(OBJECTS): | obj
obj:
mkdir $#
But that fixes the problem only with obj/, but not obj/foo and obj/bar/. I thought about using $(#D), but I don't know how to get all this together.
I have also used hidden marker files in each directory, but that's just a hack, and I have also put a mkdir -p just before the GCC command but that also feels hacky. I'd rather avoid using recursive makefiles, if that were a potential solution.
Minimal example
To create a minimal project similar to mine you can run:
mkdir /tmp/makefile-test
cd /tmp/makefile-test
mkdir src/ src/foo/ src/bar/
echo "int main() { return 0; }" > src/main.c
touch src/foo/f1.c src/bar/b1.c src/bar/b2.c
I don't know why you consider adding mkdir -p before each compiler operation to be "hacky"; that's probably what I'd do. However, you can also do it like this if you don't mind all the directories created all the time:
First, you should use := for assigning shell variables, not =. The former is far more efficient. Second, once you have a list of filenames it's easy to compute the list of directories. Try this:
SOURCES := $(shell find src/ -type f -name '*.c')
OBJECTS := $(patsubst src/%.c,obj/%.o,$(SOURCES))
# Compute the obj directories
OBJDIRS := $(sort $(dir $(OBJECTS))
# Create all the obj directories
__dummy := $(shell mkdir -p $(OBJDIRS))
If you really want to have the directory created only when the object is about to be, then you'll have to use second expansion (not tested):
SOURCES := $(shell find src/ -type f -name '*.c')
OBJECTS := $(patsubst src/%.c,obj/%.o,$(SOURCES))
# Compute the obj directories
OBJDIRS := $(sort $(dir $(OBJECTS))
.SECONDEXPANSION:
obj/%.o : src/%.c | $$(#D)
$(CC) -c $< -o $#
$(OBJDIRS):
mkdir -p $#
I'd do it this way:
SOURCES=$(shell find src -type f -name '*.c') # corrected small error
...
obj/%.o: src/%.c
if [ ! -d $(dir $#) ]; then mkdir -p $(dir $#); fi
gcc -c $< -o $#

Finding makefile dependencies

I have several widgets denoted by a config.xml in their root in a directory layout.
The GNUmakefile I have here is able to build them. Though if I update the folders, the dependencies aren't tracked. I don't want to depend on a clean target obviously, so how do I track the contents of each folder?
WGTS := $(shell find -name 'config.xml' | while read wgtdir; do echo `dirname $$wgtdir`.wgt; done )
all: $(WGTS)
%.wgt:
#cd $* && zip -q -r ../$(shell basename $*).wgt .
#echo Created $#
clean:
rm -f $(WGTS)
I hoped something like:
%.wgt: $(shell find $* -type f)
Would work, but it doesn't. Help.
Combining Beta's idea with mine:
WGTS := $(shell find -name config.xml)
WGTS := $(WGTS:/config.xml=.wgt)
WGTS_d := $(WGTS:.wgt=.wgt.d)
all: $(WGTS)
clean:
rm -f $(WGTS) $(WGTS_d)
-include $(WGTS_d)
define WGT_RULE
$(1): $(shell find $(1:.wgt=))
$(1:.wgt=)/%:
#
endef
$(foreach targ,$(WGTS),$(eval $(call WGT_RULE,$(targ))))
%.wgt:
#echo Creating $#
#(echo -n "$#: "; find $* -type f | tr '\n' ' ') > $#.d
#cd $* && zip -q -r ../$(shell basename $*).wgt .
Example:
$ mkdir -p foo bar/nested
$ touch {foo,bar/nested}/config.xml
$ make
Creating bar/nested.wgt
Creating foo.wgt
$ make
make: Nothing to be done for `all'.
$ touch foo/a
$ make
Creating foo.wgt
$ rm foo/a
$ make
Creating foo.wgt
$ make
make: Nothing to be done for `all'.
The only potential problem here is the dummy rule that lets make ignore targets it doesn't know how to build which are nested inside the directories. (foo/a in my example.) If those are real targets that make needs to know how to build, the duplicate recipe definition may be a problem.
Probably the best way to do this is to create the prerequisite lists explicitly, beforehand:
define WGT_RULE
$(1).wgt: $(wildcard $(1)/*)
endef
$(foreach targ,$(WGTS),$(eval $(call WGT_RULE,$(targ))))
There is another way that's very clever (a phrase that makes a good programmer wary). Years ago I came up with a left-handed kludge for treating a directory as a prerequisite. I'll see if I can dig up my old notebooks if the above isn't good enough.
EDIT:
Sorry, I didn't consider subdirectories. Here's a complete makefile (I left out the clean rule) that should do the trick.
WGTS := $(shell find -name 'config.xml' | while read wgtdir; do echo `dirname $\
$wgtdir`.wgt; done )
all: $(WGTS)
# This constructs a rule without commands ("foo.wgt: foo/bar.txt foo/baz.dat...").
define WGT_RULE
$(1).wgt: $(shell find $(1))
endef
# This invokes the above to create a rule for each widget.
$(foreach targ,$(WGTS),$(eval $(call WGT_RULE,$(targ))))
%.wgt:
#cd $* && zip -q -r ../$(shell basename $*).wgt .
#echo Created $#

Resources