make rule for files in subdirectory with the working directory set to this subdirectory - makefile

I have some source files in a subdirectory:
$ ls subdir
foo.x bar.x other_file
Now I want to create a rule like
subdir/%.y: subdir/%.x
cmd $< -a option -o $#
just that cmd needs the working directory set to subdir.
I want something that if subdir/bar.y is required it is generated like
cd subdir && cmd bar.x -a option -o bar.y
Is there for example a way to say $< and $# but without the leading subdir/?

If you're using GNU make, you can use $(notdir $<) and $(notdir $#) to get the filenames without a directory. And you can use $(dir $#) to get the directory part:
subdir/%.y: subdir/%.x
cd $(dir $#) && cmd $(notdir $<) -a option -o $(notdir $#)
Or hardcode subdir, if you prefer.

You can use the F or D suffix for the file and directory part respectively.
subdir/%.y: subdir/%.x
cd $(#D) && cmd $(<F) -a option -o $(#F)
See the manual for details.

What about using $(patsubst) ?
subdir/%.y: subdir/%.x
cd subdir && cmd $(patsubst subdir/%,%,$<) -a option -o $(patsubst subdir/%,%,$#)
Or, the equivalent but simpler:
subdir/%.y: subdir/%.x
cd subdir && cmd $(<:subdir/%=%) -a option -o $(#:subdir/%=%)

Related

Force make to look at files before conclude to a circular dependency

I have a circular dependency using make:
CC = gcc
IFLAGS = -Iinclude
CFLAGS = -Wall -g -c -fPIC -pedantic
AFLAGS = -shared
LFLAGS =
VERSION = $(shell cat desc/major).$(shell cat desc/minor).$(shell cat desc/patch)
DFLAGS = -D_XOPEN_SOURCE=700 -DLTKVER=\"$(VERSION)\"
OBJECTS = $(patsubst src/%.c,tmp/%.o, $(shell ls -1 src/*.c))
#OUTPUT = tmp/$(lastword $(subst /, ,$(shell pwd)))
OUT_BIN = install/usr/lib/libLTK.so
OUT_MAN = $(patsubst man/%,install/usr/share/man/%.gz, $(shell find man -type f))
PATH_INCLUDE = install/usr/include/LTK-$(VERSION)
OUT_INCLUDE = $(patsubst %,$(PATH_INCLUDE)/%, $(shell find include -type f -printf "%f\n"))
PC = %
all: $(OUT_BIN) $(OUT_MAN) $(OUT_INCLUDE)
# chmod 755 install/usr/lib/libLTK.so.$(VERSION)
ln -sf install/usr/lib/libLTK.so.$(VERSION) install/usr/lib/libLTK.so
# chmod 755 install/usr/include/LTK-$(VERSION)
# chmod 644 install/usr/include/LTK-$(VERSION)/*
ln -sf install/usr/include/LTK-$(VERSION) install/usr/include/LTK
$(OUT_BIN): $(OBJECTS)
mkdir -p $(shell dirname $#)
$(CC) $(AFLAGS) -o $#.$(VERSION) $^ $(LFLAGS)
tmp/%.o : src/%.c
mkdir -p $(shell dirname $#)
$(CC) $(CFLAGS) -o $# $< $(DFLAGS) $(IFLAGS)
install/usr/share/%.gz : %
mkdir -p $(shell dirname $#)
gzip -c $< > $#
.SECONDEXPANSION:
%.h : $$(patsubst $(PATH_INCLUDE)/$$(PC),include/$$(PC),$$#)
mkdir -p $(shell dirname $$#)
cp $< $$#
clean:
rm -rf tmp install
At second expansion header files, prerequisites are generated from second expansions.
But it's a header that generates another and this new one can be found on the disk.
But make rather prefers to consider a circular dependency and ignore it.
How can I force make to see that the file exists before it searches a target to generate it?
Circular dependences are completely independent of what exists on the disk or doesn't exist on the disk. When make runs it parses the makefile and constructs a graph representing the dependency relationship between targets. This graph must be acyclic, because make will walk the graph looking for whether targets are out of date. If there's a cycle in the graph, then make would recurse forever trying to walk the graph.
For example:
a: b ; touch $#
b: a ; touch $#
It doesn't matter whether these files exist or not: make still needs to be sure that "a" is newer than "b" to satisfy the first dependency, and that "b" is newer than "a" to satisfy the second dependency.
This cannot ever be true, obviously.
Finally resolved by substituing $(OUT_INCLUDE) to %.h.
The goal to auto copy include files is preserved.
Substitued this:
.SECONDEXPANSION:
$(OUT_INCLUDE) : $$(patsubst $(PATH_INCLUDE)/$$(PERCENT),include/$$(PERCENT),$$#)
mkdir -p $(shell dirname $#)
cp $< $#
For this:
.SECONDEXPANSION:
%.h : $$(patsubst $(PATH_INCLUDE)/$$(PC),include/$$(PC),$$#)
mkdir -p $(shell dirname $$#)
cp $< $$#
But I'm still asking myself on "is there anything to force file before dependency".
After looking at the code it looks like no, unless I omit something.

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 '.' '/')

Create directories when generating object files in gcc

gcc -o abc/def.o def.c generates def.o file in a directory abc; only when there exists a directory abc.
Is there a way to make gcc to create a directory when the enclosing directory of the generated object file does not exist? If not, what could be the easiest way to make the directory in advance automatically, especially for Makefile?
From this post, it seems like that there is no way to create a directory from gcc.
For makefile, I can use this code snippet.
OBJDIR = obj
MODULES := src src2
...
OBJDIRS := $(patsubst %, $(OBJDIR)/%, $(MODULES))
build: $(OBJDIRS)
echo $^
$(OBJDIRS):
mkdir -p $#
make build will create directories, and echo the results.
We also can make the object files are created automatically without invoking the make build.
PROG := hellomake
LD := gcc
...
$(PROG): $(OBJDIRS) obj/src/hellofunc.o obj/src/hellomake.o
$(LD) $(filter %.o, $^) -o $(PROG)
The way I do it is I find out what the paths will be, and create the dirs for them:
SOURCES:=$(shell find $(SRC)/ -type f -name '*.c') #Get all the c files
#Get the files they're gonna be compiled to
OBJECTS:=$(patsubst $(SRC)/%.c, $(OBJ)/%.o, $(SOURCES))
#Get just the paths so you can create them in advance
OBJDIRS:=$(dir $(OBJECTS))
#call mkdir with the directories (using a dummy var because too much
#trouble to deal with priorities) someone could prob do better
#--parents ignores the repeated and already existing errors
DUMMY:=$(shell mkdir --parents $(OBJDIRS))
Sauce:
https://www.gnu.org/software/make/manual/html_node/File-Name-Functions.html
In addition to precedent solutions, you can create the directories in your rule for building your .o files
$(OBJ_DIR)/%.o: $(SRCS_DIR)/%.c
#mkdir -p $(shell dirname $#)
$(CC) $(CFLAGS) $(INC) -c $< -o $#
The "#" before mkdir silent the output of mkdir (we don't want a message for each directory created).
The "-p" option tell mkdir to create all intermediate directories in path if they do not exist.
The "dirname" method takes the file path ("$#") and keep only the file directory path.

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 $#

how to prevent "directory already exists error" in a makefile when using mkdir

I need to generate a directory in my makefile and I would like to not get the "directory already exists error" over and over even though I can easily ignore it.
I mainly use mingw/msys but would like something that works across other shells/systems too.
I tried this but it didn't work, any ideas?
ifeq (,$(findstring $(OBJDIR),$(wildcard $(OBJDIR) )))
-mkdir $(OBJDIR)
endif
Looking at the official make documentation, here is a good way to do it:
OBJDIR := objdir
OBJS := $(addprefix $(OBJDIR)/,foo.o bar.o baz.o)
$(OBJDIR)/%.o : %.c
$(COMPILE.c) $(OUTPUT_OPTION) $<
all: $(OBJS)
$(OBJS): | $(OBJDIR)
$(OBJDIR):
mkdir -p $(OBJDIR)
You should see here the usage of the | pipe operator, defining an order only prerequisite.
Meaning that the $(OBJDIR) target should be existent (instead of more recent) in order to build the current target.
Note that I used mkdir -p. The -p flag was added compared to the example of the docs.
See other answers for another alternative.
On UNIX Just use this:
mkdir -p $(OBJDIR)
The -p option to mkdir prevents the error message if the directory exists.
You can use the test command:
test -d $(OBJDIR) || mkdir $(OBJDIR)
Here is a trick I use with GNU make for creating compiler-output directories. First define this rule:
%/.d:
mkdir -p $(#D)
touch $#
Then make all files that go into the directory dependent on the .d file in that directory:
obj/%.o: %.c obj/.d
$(CC) $(CFLAGS) -c -o $# $<
Note use of $< instead of $^.
Finally prevent the .d files from being removed automatically:
.PRECIOUS: %/.d
Skipping the .d file, and depending directly on the directory, will not work, as the directory modification time is updated every time a file is written in that directory, which would force rebuild at every invocation of make.
If having the directory already exist is not a problem for you, you could just redirect stderr for that command, getting rid of the error message:
-mkdir $(OBJDIR) 2>/dev/null
Inside your makefile:
target:
if test -d dir; then echo "hello world!"; else mkdir dir; fi
On Windows
if not exist "$(OBJDIR)" mkdir $(OBJDIR)
On Unix | Linux
if [ ! -d "$(OBJDIR)" ]; then mkdir $(OBJDIR); fi
ifeq "$(wildcard $(MY_DIRNAME) )" ""
-mkdir $(MY_DIRNAME)
endif
$(OBJDIR):
mkdir $#
Which also works for multiple directories, e.g..
OBJDIRS := $(sort $(dir $(OBJECTS)))
$(OBJDIRS):
mkdir $#
Adding $(OBJDIR) as the first target works well.
It works under mingw32/msys/cygwin/linux
ifeq "$(wildcard .dep)" ""
-include $(shell mkdir .dep) $(wildcard .dep/*)
endif
If you explicitly ignore the return code and dump the error stream then your make will ignore the error if it occurs:
mkdir 2>/dev/null || true
This should not cause a race hazard in a parallel make - but I haven't tested it to be sure.
A little simpler than Lars' answer:
something_needs_directory_xxx : xxx/..
and generic rule:
%/.. : ;#mkdir -p $(#D)
No touch-files to clean up or make .PRECIOUS :-)
If you want to see another little generic gmake trick, or if you're interested in non-recursive make with minimal scaffolding, you might care to check out Two more cheap gmake tricks and the other make-related posts in that blog.

Resources