Relative directory path GNU make - gnu-make

I have defined a source.mk with include path at make for header files. I have to give the absolute file path to INCLUDES or an error of file not found appear.
# Add your include paths to this variable
INCLUDES = -I/home/ecee/C1M1-Abubaker/include/common \
my make file is in :
~/home/ecee/C1M1-Abubaker/
and the header file need to be included in :
~/home/ecee/C1M1-Abubaker/include/common
The question is how to find a path dynamically from another directory.

You can use the realpath make function:
$(realpath names…)
For each file name in names return the canonical absolute name. A canonical name does not contain any . or .. components, nor any repeated path separators (/) or symlinks. In case of a failure the empty string is returned. Consult the realpath(3) documentation for a list of possible failure causes.
In your case, that would probably be $(realpath include/common).

The OP said
… or an error of file not found appear
Something like:
mustexist = $(or $(realpath $1),$(error Path [$1] does not exist!))
1.o: 1.cpp
gcc -I$(call mustexist,${INCLUDES}) $< -o $#
If $(realpath …) expands to nothing (the path does not exist),
then make expands the $(error …) and the build stops.
As a refinement,
if ${INCLUDES} can expand to more than one folder (as implied by its name),
then you should test each path individually.
mustexist = $(or $(realpath $1),$(error Path [$1] does not exist!))
includes = $(foreach _,$1,-I$(call mustexist,$_))
1.o: 1.cpp
gcc $(call includes,${INCLUDES}) $< -o $#

To search for file in a directory above you can use -I./include/commonand for two directories above: -I../include/common

Below suggestion is the straightforward approach to add one folder.
Try like this, this should solve your problem.
INCLUDES:=/home/ecee/C1M1-Abubaker/include/common
<compiler and your source files> -I../$(realpath $(INCLUDES))

Related

Makefile - generating thumbnails from pictures

I'm trying to make a Makefile. I have one folder called pictures and one called thumbs. In pictures there are pictures called pic1.jpg, pic2.jpg, pic3.jpg, and so on. I want to make thumbnails from these pictures and place them in the thumbs folder. This is what I have tried:
infiles = $(wildcard pictures/*.jpg)
outfiles = $(subst pictures,thumbs, $(infiles))
all : $(outfiles)
convert -thumbnail 100 pictures/*.jpg thumbs/*.jpg
Anyone knows what I'm doing wrong?
Your makefile says "here is how to crate all; it can be made when all the outfiles are up to date", but you don't say how to create those files.
A more idiomatic apprach is to specify a dependency for each individual file, and then Make can take it from there. In other words, say that all depends on outfiles just like you did, but then separately specify how each outfile depends on its respective infile.
infiles = $(wildcard pictures/*.jpg)
outfiles = $(subst pictures,thumbs, $(infiles))
.PHONY: all
all : $(outfiles)
thumbs/%.jpg: pictures/%.jpg
mkdir -p thumbs
# Guesswork here; probably update the command
convert -thumbnail 100 $< $#
The .PHONY declaration marks all as just a target name, not a file which needs to be built only if it doesn't exist, or is older than its dependencies.

GNU Makefile, use a variable containing the dependent directories

I am trying to create a Makefile
I have a Makefile variable containing dependent directories like
DEPDIR := temp foo bar
I need the absolute path of each directory. So, I wrote a rule:
DIRS := $(foreach dirs, $(DEPDIR), $(shell find $(HOME) -name "$$dirs"))
and I also tried:
DIRS := $(notdir $(foreach dirs, $(DEPDIR), $(shell find $(HOME) -name "$$dirs")))
But variable DIRS in both case is empty.
Am I missing something here?
Using find for this is completely wacky. There may be directories elsewhere in the directory tree with the same name, and anyway, presumably, the ones you want are in the current directory.
If they can be nested within other directories, running find on the current directory might make some sense, but you are probably simply looking for
$(patsubst %,${PWD}/%,$(DIRS))
I've no idea why did you write double $: $$dirs. Try this:
DIRS := $(foreach dir, $(DEPDIR), $(shell find $(HOME) -type d -name "$(dir)" ))
Consider narrowing down the search criteria, since $(HOME) by definition can contain anything.
I guess you will not argue that it'd be better to keep track of the project dependencies using a dedicated directory. And there are many ways to build such kind of directories(generating symlinks, downloading/extracting compressed packages, cloning repositories, using package managers etc.). Therefore, I'll suggest the following pattern as well.
MAKEFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
PROJECT_DIR := $(dir $(MAKEFILE_PATH))
DIRS := $(realpath $(addprefix $(PROJECT_DIR)deps/, dir1 dir2 dir3))
Here we extract filename of the current makefile from MAKEFILE_LIST, then store its absolute path in MAKEFILE_PATH variable. Then the directory part of the path to the makefile is stored in PROJECT_DIR. By means of addprefix we prepend $(PROJECT_DIR)/deps to the front of each dependent directory(dir1 dir2 dir3). Then we resolve symlinks, and construct canonical absolute names for the dependent directories, then store them in DIRS.

How to write a makefile executing make one directory level up

Can I write a wrapper makefile that will cd one level up and execute there make with all the command options I have given the wrapper?
In more detail:
Directory project contains a real Makefile with some different targets.
Directory project/resources contains the wrapper Makefile which should call Makefile in project.
When I am in my shell in directory project/resources, I execute
make TARGET
and the Makefile there just cds one directory up and calls
make TARGET
in the directory project.
Is this possible? And how?
You could use a very simple Makefile for all your sub-directories:
%:
$(MAKE) -C .. $#
% is a last resort match-anything pattern rule that will match any target... for which there is no implicit rule (GNU make has an incredibly large number of implicit rules). So, if none of your targets are covered by an implicit rule, this should work. Else you will have to tell make not to use the implicit rules it knows. This can be done (with GNU make) by calling make with the -r option:
cd project/resources
make -r <anything>
will call make in project for target <anything>. The main drawback is that the -r flag is passed to the sub-make and so the implicit rules will not apply neither in project, which can be a problem. If it is you can obtain the same effect by adding an empty .SUFFIXES target to theMakefile in project/resources:
.SUFFIXES:
%:
$(MAKE) -C .. $#
With my version of GNU make (3.82) it works like a charm and the sub-make has all the default implicit rules.
Yes, you can have a makefile which works for "any" target.
The GNU make manual discusses this in the Overriding Part of Another Makefile section:
Sometimes it is useful to have a makefile that is mostly just like another makefile. You can often use the ‘include’ directive to include one in the other, and add more targets or variable definitions. However, it is invalid for two makefiles to give different recipes for the same target. But there is another way.
In the containing makefile (the one that wants to include the other), you can use a match-anything pattern rule to say that to remake any target that cannot be made from the information in the containing makefile, make should look in another makefile. See Pattern Rules, for more information on pattern rules.
For example, if you have a makefile called Makefile that says how to make the target ‘foo’ (and other targets), you can write a makefile called GNUmakefile that contains:
foo:
frobnicate > foo
%: force
#$(MAKE) -f Makefile $#
force: ;
If you say ‘make foo’, make will find GNUmakefile, read it, and see that to make foo, it needs to run the recipe ‘frobnicate > foo’. If you say ‘make bar’, make will find no way to make bar in GNUmakefile, so it will use the recipe from the pattern rule: ‘make -f Makefile bar’. If Makefile provides a rule for updating bar, make will apply the rule. And likewise for any other target that GNUmakefile does not say how to make.
The way this works is that the pattern rule has a pattern of just ‘%’, so it matches any target whatever. The rule specifies a prerequisite force, to guarantee that the recipe will be run even if the target file already exists. We give the force target an empty recipe to prevent make from searching for an implicit rule to build it—otherwise it would apply the same match-anything rule to force itself and create a prerequisite loop!
One option: use a wrapper file to execute the commands to do that. Just be sure your target make files don't include the child directory that has the wrapper, or else you can create an endless loop. For example,
clean:
pushd .. && make clean && popd
Using the comment of user Renaud Pacalet and the answer to a different question the following one-liner is as close as I could get. The whole Makefile reads:
IGNORE := $(shell $(MAKE) -C .. $(MAKECMDGOALS))
This solutions comes with a few caveats:
Command line option -B does not get passed through to the subsequent make call.
The output of the subsequently called make process (in the project directory) is not printed to stdout.
The wrapper make process reports for any given target at the end :
make: *** No rule to make target TARGET. Stop.

How does GNU make's "file" function work?

I am thinking I may need to use the file function in GNU make, and just can not follow the example they give. I have looked online, but don't see any post with more explanation. Here is the example they give:
program: $(OBJECTS)
$(file >$#.in,$^)
$(CMD) $(CMDFLAGS) #$#.in
#rm $#.in
I think I know what it is doing at a high level as it is explained in the manual.
$#.in
is a list of all the target files
$^
is a list of the source files
I am not sure how #$#.in is used on the third line or what there is an # sign at the beginning. What does that mean please? What does it supposed to do?
The key to the operation of that recipe is given in the prose immediately preceding it in the manual:
Many commands use the convention that an argument prefixed with an # specifies a file containing more arguments. Then you might write your recipe in this way:
program: $(OBJECTS)
$(file >$#.in,$^)
$(CMD) $(CMDFLAGS) #$#.in
#rm $#.in
$# is the target file (there is only one of those in any given recipe)
$#.in is the target file with .in added to the end of the name.
$^ is the "list" of the all the prerequisites of the target.
#$#.in is the name of the target with .in at the end and # at the start.
So the $(file ...) call in that recipe writes the list of prerequisites of the target into a file called program.in in "overwrite" mode and then passes that file name to the $(CMD) command using the #filename convention that was mentioned.

Makefile rule depend on directory content changes

Using Make is there a nice way to depend on a directories contents.
Essentially I have some generated code which the application code depends on. The generated code only needs to change if the contents of a directory changes, not necessarily if the files within change their content. So if a file is removed or added or renamed I need the rule to run.
My first thought is generate a text file listing of the directory and diff that with the last listing. A change means rerun the build. I think I will have to pass off the generate and diff part to a bash script.
I am hoping somehow in their infinite intelligence might have an easier solution.
Kudos to gjulianm who got me on the right track. His solution works perfect for a single directory.
To get it working recursively I did the following.
ASSET_DIRS = $(shell find ../../assets/ -type d)
ASSET_FILES = $(shell find ../../assets/ -type f -name '*')
codegen: ../../assets/ $(ASSET_DIRS) $(ASSET_FILES)
generate-my-code
It appears now any changes to the directory or files (add, delete, rename, modify) will cause this rule to run. There is likely some issue with file names here (spaces might cause issues).
Let's say your directory is called dir, then this makefile will do what you want:
FILES = $(wildcard dir/*)
codegen: dir # Add $(FILES) here if you want the rule to run on file changes too.
generate-my-code
As the comment says, you can also add the FILES variable if you want the code to depend on file contents too.
A disadvantage of having the rule depend on a directory is that any change to that directory will cause the rule to be out-of-date — including creating generated files in that directory. So unless you segregate source and target files into different directories, the rule will trigger on every make.
Here is an alternative approach that allows you to specify a subset of files for which additions, deletions, and changes are relevant. Suppose for example that only *.foo files are relevant.
# replace indentation with tabs if copy-pasting
.PHONY: codegen
codegen:
find . -name '*.foo' |sort >.filelist.new
diff .filelist.current .filelist.new || cp -f .filelist.new .filelist.current
rm -f .filelist.new
$(MAKE) generate
generate: .filelist.current $(shell cat .filelist.current)
generate-my-code
.PHONY: clean
clean:
rm -f .filelist.*
The second line in the codegen rule ensures that .filelist.current is only modified when the list of relevant files changes, avoiding false-positive triggering of the generate rule.

Resources