Makefile - get directory of itself from within included file - makefile

Say a makefile includes multiple other makefiles. How can these included makefiles get to know the path to themselves relative to the main makefile?
An example structure is as follows:
main make:
include ../dir1/dir2/make1.mk
include dir3/dir4/dir5/make2.mk
.PHONY: print_paths
print_paths:
#echo $(dir1) && #echo $(dir2)
make1
dir1 = <some code>
make2
dir2 = <some code>
My expected output would be:
../dir1/dir2
dir3/dir4/dir5
I was able to solve this for a single include file through:
dir1 = $(dir $(lastword $(MAKEFILE_LIST)))
However, this does not seem to work for multiple files, as both dir1 and dir2 will be set equal to the directory of makefile two. (which is fair I guess? It is the last file included after all)
Alternatively them getting to know their absolute path would be fine as well.

You could simply add:
dir1 := $(dir $(lastword $(MAKEFILE_LIST)))
at the beginning of ../dir1/dir2/make1.mk and:
dir2 := $(dir $(lastword $(MAKEFILE_LIST)))
at the beginning of dir3/dir4/dir5/make2.mk.

Related

Make doesn't realize that it's done

This is my first ever post to stackoverflow. It ought to be straightforward, but I have this problem whenever I try to write a makefile and I've never been able to figure out a satisfactory solution. Apologies if there is already a solution somewhere on the site. I couldn't find one.
What I'm trying to do is as follows:
Search my src directory for matching source files.
Compile the src code into a sandbox.
Here's my directory structure:
makefile
src1
file1.src
file2.src
src2
file3.src
subfolder
src3
file4.src
file5.src
And here's my makefile:
BUILDDIR := ./sandbox
SRC_DIRS := ./
SRCS := $(shell find $(SRC_DIRS) -name *.src)
OBJS := $(addprefix $(BUILDDIR)/o., $(notdir $(SRCS) ) )
# make print-X prints value of variable X
print-%: ; #echo $* = $($*)
.PHONY: help
help:
#echo "make <all|clean>"
.PHONY: all
all: $(OBJS)
#echo "compilation done"
$(OBJS) : $(SRCS) $(BUILDDIR)/.create
#echo "\"compiling\" $< to produce $#"
cp $< $#
$(BUILDDIR)/.create:
#echo "creating sandbox"
mkdir -p $(BUILDDIR) && cd $(BUILDDIR)
#touch $#
.PHONY: clean
clean:
#echo "deleting sandbox"
#rm -rf $(BUILDDIR)
If I type make all, the file works as expected. However, if I type make all again, instead of saying everything is up to data, I end up with the following contents in the sandbox:
o.file1.src o.file2.src o.file3.src o.file4.src o.file5.src o.o.file1.src o.o.file2.src o.o.file3.src o.o.file4.src o.o.file5.src
And the process of creating objects of objects continues recursively as many times as I type make.
Any help would be appreciated.
Incidentally, please don't post solutions that rely on the build in compile functions of make. I'm looking for a general solution that can be used for any task. For example, in this instance, I'm trying to read the source files into a tool using its command line interface.
Well, first, by having your sandbox as a subdirectory of your source directory, then using find on the source directory, every time you run it you're going to find all the files in both the source directory and all its subdirectories, including the sandbox. If the built files in the sandbox have the same names as the files in the source directories, the find will find them all.
Maybe instead of:
SRCS := $(shell find $(SRC_DIRS) -name *.src)
you want something like:
SRCS := $(shell find $(SRC_DIRS) -name $(notdir $(BUILDDIR)) -prune -o -name *.src -print)
Or, alternatively, don't make your sandbox a subdirectory of your source directory. Or, make sure that whatever name you give to the files in the sandbox directory won't match the *.src pattern you give to find.
But beyond that this is wrong:
$(OBJS) : $(SRCS) $(BUILDDIR)/.create
Suppose SRCS is foo.src bar.src, which means OBJS is sandbox/o.foo.src sandbox/o.bar.src. Then the above expands to this:
sandbox/o.foo.src sandbox/o.bar.src : foo.src bar.src sandbox/.create
This is a common mistake; people seem to think that make will go through the targets and prerequisites and match them up, so the first target depends on the first prerequisite and the second target depends on the second one etc. but of course this cannot work correctly and that's not how make works. Make treats the above as if you'd written one rule for each target, with the same prerequisites; like this:
sandbox/o.foo.src : foo.src bar.src sandbox/.create
sandbox/o.bar.src : foo.src bar.src sandbox/.create
You can see this won't do what you want at all, since the $< will always be foo.src which is clearly wrong.
You need to write a single pattern rule that will build ONE target. Then make sure the pattern applies to all the targets.
You have made things hard for yourself by trying to "flatten" a directory structure of multiple source subdirectories, into a single level of target directory (by using the $(notdir $(SRCS))). Because of this, there's no pattern that will match the same target and directory, unless you write a separate rule for every subdirectory.
Luckily there is a solution for this: VPATH. This should work for you:
VPATH := $(sort $(dir $(SRCS))
$(BUILDDIR)/o.%.src : %.src $(BUILDDIR)/.create
#echo "\"compiling\" $< to produce $#"
cp $< $#
The VPATH tells make to go look in all the directories that it found any sources in, whenever it can't find one to build.
The basic problem is that your SRCS is all files in all subdirectories that match the pattern *.src (when you run make). That means that all your object files ($(OBJS)) also match, so they copied as well.
The solution is to change your SRCS pattern so it does not match the "object" files in the build directory. Possibilities:
SRCS := $(wildcard *.src)
or
SRCS := $(shell find $(SRC_DIRS) -name $(notdir $(BUILDDIR)) -prune -false -o -name *.src)
or change the names of your "object" files so they don't end in .src
If I type make all, the file works as expected.
By which I take you to mean that directory ./sandbox exists and contains these files:
o.file1.src
o.file2.src
o.file3.src
o.file4.src
o.file5.src
However, if I type make all again, instead of saying everything is up to data, I end up with the following contents in the sandbox:
o.file1.src o.file2.src o.file3.src o.file4.src o.file5.src o.o.file1.src o.o.file2.src o.o.file3.src o.o.file4.src o.o.file5.src
Of course you do, because everything is not up to date at that point, according to the target list you create. This line ...
SRCS := $(shell find $(SRC_DIRS) -name *.src)
... defines SRCS as a list of all the files under path ./ (which is the value of SRC_DIRS) that ends in .src. That includes any such files in ./sandbox, which include all the files placed there by the first make run. You generate a corresponding target file to build for each source, and those targets corresponding to sources built by the previous make run will not, in general, exist yet. So make builds them, just as you instructed it.
The best solution, short of abandoning that automatic source identification altogether, would probably be to change your naming scheme so that outputs cannot be mistaken so easily for sources. For example, if you want the output names to have the form foo.src, then have the corresponding input named something like foo.src.in. In that particular case, you could convert from source names to target names with
OBJS := $(addprefix $(BUILDDIR)/o., $(basename $(notdir $(SRCS) ) ) )
Alternatively, you could modify the find command to skip the sandbox directory, maybe by modifying SRC_DIRS:
SRC_DIRS = src1 src2 subfolder/src3
(These specific alternatives are not mutually exclusive.)

On Makefile, how to list folders where source files reside?

For my specific case, I have many folders and subfolders with .v files. I want to compile them folder by folder. I have made this:
# find folders and subfolders to work on
DIRS := $(wildcard */)
DIRS += $(shell find $(DIRS) -type d)
# compiler flags
SRC += *.v
COMPILE = $(general_compiler_flags) $(SRC)
# run the compiler
run:
$(foreach d, $(DIRS), $(shell cd $d && $(COMPILE)))
But this takes all folders that do not have .v files and compiles them anyway. How do I make it so on my DIRS I have only the folders that house .v files?
The way you use make functions (especially shell) in your recipes is not the recommended way to use make. Assuming you have simple directory names (no spaces, no special characters), you could use something like the following, instead:
VDIRS := $(sort $(foreach d,$(DIRS),$(if $(wildcard $(d)/*.v),$(d),)))
run:
for d in $(VDIRS); do ( cd $$d && $(COMPILE); ); done
An even more make-ish solution would rely on make to iterate over all source directories instead of doing so inside a recipe:
VDIRS := $(sort $(foreach d,$(DIRS),$(if $(wildcard $(d)/*.v),$(d),)))
RUN-VDIRS := $(addprefix run-,$(VDIRS))
.PHONY: run $(RUN-VDIRS)
run: $(RUN-VDIRS)
$(RUN-VDIRS): run-%:
cd $* && $(COMPILE)
And, finally, we should consider how to avoid useless re-compilations, but this is another, more complex, story.

Why does including a file in my Makefile change my Makefile-directory variable?

In order invoke my Makefile from different locations without messing up relative paths, I reference paths using a Makefile variable as given in another answer:
DIR=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
I get that MAKEFILE_LIST differs when I include other files, but since I store its value in a variable before making any includes, I am surprised that the variable value differs.
Example:
$ tree .
.
├── another_file
└── subdirectory
└── Makefile
$ cat Makefile
DIR=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
test:
#echo $(DIR)
#include $(DIR)/../another_file
$ make
/subdirectory
Just as expected. But if I uncomment the include line, I get
$ make
/
Which does not make sense to me, because another_file is still included without errors indicating that the value of $(DIR) is /subdirectory.
Note that the make target is placed before the include statement, and the behavior does not change when the order is switched. Guess this is due to preprocessing, but it still does not explain to me why $(DIR) seems to have different values.
$ make --version
GNU Make 3.81
...
This program built for i386-apple-darwin11.3.0
this is because the value of MAKEFILE_LIST changes after include and the expansion of the variable DIR happens at use time.
I sprinkled your Makefile with info for demonstration
DIR=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
$(info list1 $(MAKEFILE_LIST))
$(info dir1 $(DIR))
test:
#echo $(DIR)
include $(DIR)/../another_file
$(info list2 $(MAKEFILE_LIST))
$(info dir2 $(DIR))
output
$ make
list1 Makefile
dir1 /home/lesmana/tmp/maek/subdir
list2 Makefile /home/lesmana/tmp/maek/subdir/../another_file
dir2 /home/lesmana/tmp/maek
/home/lesmana/tmp/maek
note how the value of MAKEFILE_LIST changed after the include and with it the value of DIR.
one way to fix this is by forcing immediate expansion of DIR by using := instead of =
DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
that way the value of DIR is calculated once and does not change even if MAKEFILE_LIST changed.
another way would be to use firstword instead of lastword.
also note that the expansion of DIR in the recipe for test happens just before executing that recipe. That is why it does not matter where the include happens relative to test.
read here for more info:
https://www.gnu.org/software/make/manual/html_node/Special-Variables.html
https://www.gnu.org/software/make/manual/html_node/Flavors.html
What is the difference between the GNU Makefile variable assignments =, ?=, := and +=?
I do not know how to feel about this shell construct to get the dir of the Makefile. Usually Makefiles from higher up include the Makefile from the subdirs. But you have your use case. I will not argue about that here. I hope my explanation of the flavors of variable helps.

How to compile files that reside in different directory in Makefile?

I have seen this questions asked before but was not able to decipher those answers.
Lets say I reside in working directory, lets call it proj and this proj directory contains src folder which contains all the *.cpp files. I want to compile those file staying on the proj directory because in future I will be creating bin directory and placing the *.o and binary in bin.
So my proj directory currently contains : Makefile and src
What I have done so far is :
SOURCE = src
# This gives the path to the proj directory
CURRENT_DIR = $(shell pwd)
# This gives list of all the *.cpp files
SRC = $(shell cd $(SOURCE) && echo *.cpp)
# Here all the name of the files stored in SRC are converted from *.cpp to *.o
OBJS = $(SRC:.cpp=.o)
.PHONY: all
all: $(TARGE)
# use the content of SRC to compile
$(TARGET): $(OBJS)
$(info $(OBJS))
$(OBJS): $(SRC)
$(CC) $(FLAGS) -c $?
When I try to run the make command it says
make: *** No rule to make target 'xxx.cpp', needed by 'xxx.o'. Stop
Now I know what it is trying to say. It gives error because although it knows the name of the file, since the file is not in the current directory makefile does not know about src folder and hence have no clue about the *.cpp files.
So my question is: Is there any macros or trick to use in makefile to make sure makefile see the xxx.cpp in src folder while staying in the current directory( I don't want to specify the folder by hand here)?

How to define a pattern which is valid for all files in the directory including subdirectories?

I'm writing a pattern for compiling all .c file in the test directory.
Details of the directory is as follows:
./prj/makefile
./prj/test
./prj/test/test1/a.c
./prj/test/test1/b.c
./prj/test/test2/c.c
./prj/test/test2/d.c
./prj/test/e.c
...
Just a demo. This is my content of makefile:
# Find all files
rwildcard := $(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2))
# All .c files
SRC_FILES := $(call rwildcard,test,*.c)
# All .o files
OBJ_FILES := $(SRC_FILES:.o=.c)
all : $(OBJ_FILES)
echo $(OBJ_FILES)
%.o : %.c
echo $# $<
Make prints No rule to make target '...'. I think make need know path of .o files and .c files. But I don't know how to setting the path, Since there is so many .c files in my prj.
Because OBJ_FILES has includes all .o files. Then I guess the pattern should be like this:
$(output_dir)/%.o : $(input_dir)/%.c
echo $# $
Since here may have many directories in ./prj/test, I cann't hardcoded it in makefile
Thanks for another friend, the above approach is right. since % can match many Multi-level directories。
We can't really solve your problem because you still have not specified where the object files should go. All in a specific directory? Always in the parent directory of the source file? Somewhere else?
Regardless of how you resolve that, you can add all your source directories to VPATH and have Make resolve the precise location while figuring out the dependencies.
VPATH=test:test/test1:test/test2
experiment: a.c d.c
echo $^
will echo test/test1/a.c test/test2/d.c with full paths.
So with this you can remove the hard-coded directory names from your %.o rule and simply specify which .o files you want built for the all target.
You can use this to get all c files in subdirectories:
$(wildcard */*.c)
Or also this to get all c files in subdirectories at any depth:
$(wildcard **/*.c)

Resources