changing the directory and executing the makefile commands - makefile

I have the following directory structure
project
|-- aws-cdk
|-- data
|-- some_project_files
|-- Makefile
Using the Makefile, I'm executing the directory some_project_files and this is working fine. In addition to that, I want to execute aws-cdk as well. But I do not see a way to change the directory within the makefile. I tried cd aws-cdk and then execute but I keep on getting error make: *** No rule to make target. Stop.
How do I go back/change directory and then execute Makefile

Within one makefile you can change directory inside the rules, but you can't say this set of rules runs in one directory and this one runs in the other. Which does not prevent you running rules on files in a different directory by simply giving their path. The data/Makefile can contain rules for ../aws-cdk/something and even ../aws-cdk/%.out: ../aws-cdk/%.in and that's just fine.
If you do want to actually change directory, you need a separate makefile. I strongly recommend just putting it in the directory where it will run, so aws-cdk/Makefile and then you can just call it from the one in data like $(MAKE) -C ../aws-cdk. You can also put it anywhere else and give make an explicit argument like $(MAKE) -C ../aws-cdk -f aws-cdk.make, but putting it in the directory will be easier to understand when it needs fixing two years down the line and/or by someone who's never seen it.

Related

Include in Makefile another Makefile with relative path

I have a directory tree like this with some "shared targets" in the file rules.Makefile:
├── Makefile
├── rules.Makefile
└── my_subdir
└── Makefile
I would like to invoke these "shared targets" in both the Makefile(s) in the parent directory and the child directory.
Also the "custom targets" in the Makefile in the child directory should be callable from the Makefile in the parent directory.
For some reason I am able to call the targets in rules.Makefile only from the sibling Makefile (the one in the parent directory). When using relative paths in the Makefile in the child directory trying to access the rules.Makefile in the parent directory I get some errors.
The content of the Makefile in the parent directory:
RULES_MAKEFILE_PATH=$(PWD)/rules.Makefile
include $(RULES_MAKEFILE_PATH)
foo-parent:
#echo $(RULES_MAKEFILE_PATH)
The content of the Makefile in the child directory (please note that double dot ..):
RULES_MAKEFILE_PATH=$(PWD)/../rules.Makefile
include "$(RULES_MAKEFILE_PATH)"
foo-child:
#echo $(RULES_MAKEFILE_PATH)
When calling from the parent directory make foo-parent then I see the expected path.
When calling from the child directyr make foo-child then I see this error:
$ make foo-child
Makefile:9: "/<PARENT_PATH>/my_subdir/../rules.Makefile": No such file or directory
make: *** No rule to make target '"/<PARENT_PATH>/my_subdir/../rules.Makefile"'. Stop.
How can I make the relative paths work in the "child directory"?
Also how can I call the targets defined in the Makefile in child directory (e.g. foo-child) from the Makefile in the parent directory?
Well first, $(PWD) is not a special variable to make. It's just a normal variable, that's imported from your shell. So it will always have the same value everywhere in your makefile and in all included makefiles, it won't change just because you're including a makefile from a different directory.
Second, even for $(CURDIR) (which is a special variable and is set by make to be the current directory when make starts), it is never reset when you include a makefile from another directory.
And, all paths in include lines are evaluated based on the directory make was in when it started, not on a path relative to the currently-parsed makefile. So if Makefile includes foo/Makefile, then foo/Makefile has an include bar.mk, make will look for bar.mk not foo/bar.mk.
The point about $(PWD) above is true. However, if that is not a concern, you could still use the shell function to execute commands to get paths of another Makefile to include: $(shell pwd)
I did something similar for a project that benefited from having the same Makefile used in many places, using git rev-parse --show-toplevel.
MAKEFILE := $(shell git rev-parse --show-toplevel)/makefiles/example.def
include $(MAKEFILE)

Makefile pattern rules not working

I am learning makefiles, and can't just wrap my head around this problem i am having, and would like to understand how/why this fail.
I have half a dozen erlang files in a src directory. I want to compile these into a ebin directory, without having to define a rule for each and every one of them. According to the Gnu make documentation, pattern rules should be right up my alley.
However, with the following makefile, all I get from make is make: *** No targets. Stop. Why is that?
ebin/%.beam: src/%.erl
mkdir -p ebin
erlc -o ebin $<
Edit: Based on this answer, I now understand that i would have to explicitly declare the targets, for instance by using make ebin/cmplx.beam. However, i still do not understand how i should write my makefile to get my desired behaviour - since I have half a dozen targets (and in other projects even more), this seems like an unnecessary hassle. Is there not a way to define targets based on the source file names?
The target rule tells make that whenever it needs to produce a beam file in the ebin directory, and there exists a corresponding erl file in the src directory, it can use erlc.
However, this doesn't tell make that this is what it needs to do. You could explicitly tell make what it needs to do by giving it a target on the command line:
make ebin/foo.beam
If you don't give a target on the command line, make will pick the first non-pattern rule in the makefile as its target. However, your makefile doesn't have any non-pattern rules, so there is no target.
What you probably want is that for each existing erl file in src, make should consider the corresponding beam file in ebin to be a target. You can achieve that by calling wildcard and patsubst:
erl_files=$(wildcard src/*.erl)
beam_files=$(patsubst src/%.erl,ebin/%.beam,$(erl_files))
ebin/%.beam: src/%.erl
mkdir -p ebin
erlc -o ebin $<
all: $(beam_files)
(The indented lines need to be actual physical tabs, not spaces.)
That way, running make will rebuild all beam files that are out of date. all gets chosen as the default target, and it in turn depends on all beam existing or potential, each of which in turn depends on the corresponding erl file.
This trick is described in the GNU make manual.

Makefile rule that detects any changed file in a directory (and subdirs)

I want to create a Makefile rule that runs whenever anything is changed inside a directory (which contains multiple source files in different languages, and at different subdirectory levels).
As an example, take this Makefile:
newest: src
touch newest
with a tree like:
src/
src/a
scr/subdir/
scr/subdir/c
First time I run make, newest is created all right. But if I now touch src/subdir/b, make does nothing.
Is it possible at all to create such a rule?
I think you would need to use something like FILES := $(shell find src -type f) and a rule of newest: $(FILES) to get the sort of behavior you want.

Makefile rule that depends on all files under a directory (including within subdirectories)

One rule in my Makefile zips an entire directory (res/) into a ZIP file. Obviously, this rule needs to execute when any file under the res/ directory changes. Thus, I want the rule to have as a prerequisite all files underneath that directory. How can I implement this rule?
In Bash with the globstar option enabled, you can obtain a list of all the files in that directory using the wildcard pattern res/**/*. However, it doesn't seem to work if you specify it as a prerequisite in the Makefile:
filename.jar: res/**/*
Even after touching a file in res/, Make still reports
make: `filename.jar' is up to date.
so clearly it is not recognizing the pattern.
If I declare the directory itself as a prerequisite:
filename.jar: res
then Make will not re-execute when a file is modified (I think make only looks at the modified date of the directory itself, which only changes when immediate children are added, removed, or renamed).
This:
filename.jar: $(wildcard res/**/*)
seems to work, at least on some platforms.
EDIT:
Or better, just cut the knot:
filename.jar: $(shell find res -type f)

How do you run a command prior to including a component Makefile?

I am trying to create a subdirectory in my project (let's call it $PROJECT/child) that needs to pull in a Makefile (let's call it ../Makefile.inc) from its parent, $PROJECT/Makefile.inc. Later I want to copy $PROJECT/child somewhere else so it can run independently of $PROJECT.
There is a common Makefile that needs to be included in both projects and shipped when the subdirectory is copied, and I want it to be included in both cases. So I thought I would link it in during the child build, if it isn't found. (I don't want to just include ../Makefile.inc, because this will disappear when I copy the project, and I don't want the calling build system to be responsible for putting the Makefile.inc in place.)
With those constraints, here's a horrible hack that I've come up with to do this, within $PROJECT/child/Makefile:
HACK = $(shell test -f Makefile.inc || ln -f ../Makefile.inc .)
include $(HACK)Makefile.inc
Notice the extra special duct tape on that second command. I have to actually include $(HACK) even though it's going to end up empty, so that the $(shell ...) will be evaluated. ;-)
Is there a cleaner way to make this happen?
Give a rule to build Makefile.inc. (make will complain that Makefile.inc doesn't exist when it parses the include line, but it will go on parsing the main makefile, apply any rule to build included files, and go back and re-parse the main makefile with the included files.)
include Makefile.inc
Makefile.inc:
ln ../Makefile.inc $#

Resources