make dependency on all files in a subdirectory (git submodule) - makefile

I want to write a (GNU) make rule that depends on the existence of some file (any arbitrary file) in a subdirectory. In my specific case, that subdirectory is a git submodule.
This is what I have:
DEP = submod/.git
$(DEP):
git submodule update --init $(#D)
submod/%: | $(DEP)
# # why do I need this?
install: submod/junk.c
echo installing
If I then type make install, the git command is run (change it to mkdir -p $(#D); touch $# for a non-git-specific test), and make doesn't complain.
I have two questions. Primarily, are there any side effects to the dummy recipe for submod/% I should be concerned about? Secondly, and more interestingly, why do I need that recipe at all? If I remove it, make errors out:
make: *** No rule to make target 'submod/junk.c', needed by 'install'. Stop.
My only hypothesis is that make is doing some optimization based on the files that exist at startup, and with the no-recipe version, it thinks that it can just expect the file to exist when it's needed, but with any recipe there, it can't do that optimization, and it reevaluates when it's needed. If that's true, is it a bug, or something that should be changed?

Related

Make: "nothing to be done for target" when invoking two phony targets at once

Simple makefile:
.PHONY:\
gitbranch-foo
gitbranch-foo: dir=./foo
gitbranch-foo: git-spawn-branch
.PHONY:\
gitbranch-bar
gitbranch-bar: dir=./bar
gitbranch-bar: git-spawn-branch
.PHONY:\
git-spawn-branch
git-spawn-branch:
#$(call git_branch,$(dir),$(branch))
#echo > /dev/null
define git_branch
cd $(1); \
git checkout -b $(2) || true; \
git push -u origin $(2) || true;
endef
Invoking two rules at once:
make gitbranch-foo gitbranch-bar branch=something/blah/blah -rRd
As you can see 'foo' repo switches successfully but 'bar' repo is not even being processed by make. Why?
Switched to a new branch 'something/blah/blah' <-- 'foo' switches successfully
Total 0 (delta 0), reused 0 (delta 0)
remote:
remote: Create pull request for something/blah/blah:
remote: ....
remote:
To xyz.net:foo
* [new branch] master -> something/blah/blah
Branch 'something/blah/blah' set up to track remote branch 'something/blah/blah' from 'origin'.
make: Nothing to be done for 'gitbranch-bar'. <-------- 'bar' is not even processed why?
The debugging output of 'make' looks like so:
Branch 'something/blah/blah' set up to track remote branch 'something/blah/blah' from 'origin'.
Everything up-to-date
Reaping winning child 0x563e010f42d0 PID 2092
Live child 0x563e010f42d0 (git-spawn-branch) PID 2096
Reaping winning child 0x563e010f42d0 PID 2096
Removing child 0x563e010f42d0 PID 2096 from chain.
Successfully remade target file 'git-spawn-branch'.
Finished prerequisites of target file 'gitbranch-foo'.
Must remake target 'gitbranch-foo'.
Successfully remade target file 'gitbranch-foo'.
Considering target file 'gitbranch-bar'.
File 'gitbranch-bar' does not exist.
Pruning file 'git-spawn-branch'.
Finished prerequisites of target file 'gitbranch-bar'.
Must remake target 'gitbranch-bar'.
Successfully remade target file 'gitbranch-bar'.
make: Nothing to be done for 'gitbranch-bar'.
Can't make heads or tails out of it though. Any insight as to why 'make' behaves this way is appreciated.
Note: I'm aware I can refactor the makefile a bit like below to get it to do my bidding - I just want to understand why the original approach didn't work as intended
.PHONY:\
gitbranch-foo
gitbranch-foo:
#$(call git_branch,./foo,$(branch))
#echo > /dev/null
.PHONY:\
gitbranch-bar
gitbranch-bar:
#$(call git_branch,./bar,$(branch))
#echo > /dev/null
define git_branch
cd $(1); \
git checkout -b $(2) || true; \
git push -u origin $(2) || true;
endef
The answer is strictly due to make's rules and has nothing to do with any of the other tags here (I'll remove all the others). The makefile that doesn't work says:
to build gitbranch-foo, we must build git-spawn-branch
to build gitbranch-bar, we must build git-spawn-branch
(plus of course the code for building git-spawn-branch itself and the other associated stuff, but let's stop here).
You then run make, telling it to build both gitbranch-foo and gitbranch-bar.
Make picks one of these to build—gitbranch-foo, in this case—and begins building. Oh hey, says make to itself, this needs git-spawn-branch to be built, and we have not done that yet. Off goes make, which builds git-spawn-branch according to the rules. Now it's built! Make can go back to building gitbranch-foo. This executes the remaining recipe, which is empty.
If parallel builds are allowed, make can also now begin building gitbranch-bar while gitbranch-foo builds. If not, we wait for gitbranch-foo to be fully built at this point. Either way we very quickly get around to buildling gitbranch-bar. Hm, says make to itself, this needs git-spawn-branch to be built ... but fortunately, I have done that already! Onward! Let's make the rest of gitbranch-bar now! That requires executing the empty recipe, which goes very quickly.
Make is now done. It has built everything required.
(The makefile that works uses $call sensibly, directly from each rule, so that the commands that $call expands to are required to be run for each target, rather than hidden away in a third target that can be built just once and never needs to be run any further.)
(Note that the gmake documentation mentions that a .PHONY rule is run every time. This means once per invocation of make, not once per invocation of the rule.)

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.

Alias target name in Makefile

The Problem:
Is it possible to give a target a different name or alias, such that it can be invoked using either the original target name or the alias.
For example something like
/very/long/path/my_binary: dep_a dep_b dep_c
# Compile
# Desired command
ALIAS my_binary = /very/long/path/my_binary
# NOTE: Notice the use of 'my_binary' in the dependencies
data1: my_binary datafile
# Build data file using compiled my_binary
Attempt 1: .PHONY
I have tried using a .PHONY target:
.PHONY: my_binary
my_binary: /very/long/path/my_binary
This works great when invoked from the command-line:
# Runs rule 'my_binary' and then *only* runs rule '/very/long/path/my_binary'
# if the rule '/very/long/path/my_binary' needs updating.
make my_binary
However, this does not work well when the alias my_binary is listed as a dependency:
# *Always* thinks that rule 'data1' needs updating, because it always thinks that
# the .PHONY target 'my_binary' "needs updating". As a result, 'data1' is
# rebuilt every time.
make /very/long/path/my_binary
Possible hack?
A possible hack is to use an empty target as suggested in an answer to this question, but that would require introducing fake files with names corresponding to the alias:
my_binary: /very/long/path/my_binary
touch my_binary
This will clutter the main directory with files! Placing the fake files in a sub-directory would defeat the purpose, as the alias would have to be referred to as 'directory/my_binary'
Okay, I needed something similar. The path to my output artifacts were quite long, but I wanted short target names and also benefit easily from bash-completion.
Here is what I'm came up with:
os := [arbitrary long path to an artifact]
platform := [arbitrary long path to a differ artifact]
packer := [common parts of my packer build command]
.PHONY: all
all: $(platform)
.PHONY: platform
platform: $(platform)
$(platform): platform.json $(os)
#$(packer) $<
.PHONY: os
os: $(os)
$(os): os.json
#$(packer) $<
.PHONY: clean
clean:
rm -fr build/
With the Makefile above you can say:
$ make os
$ make platform
Which will be aliases for the long artifact names. I've made the snippet above quite long, because it's important to see the relationships between the .PHONY aliases and the real targets. I hope that works for you.
Note: I did not delete the clean target from the above example, because many people does not make that a .PHONY target. However, semantically it should be.
I don't think there's any way to do it so that you can use the alias from within your makefile as well as the command line, except by creating those temporary files.
Why can't you just set a variable in the makefile, like:
my_binary = /very/long/path/my_binary
then use $(my_binary) everywhere in the makefile? I don't see any point in creating a real alias target for use inside the makefile.
I had a somewhat similar need. I wanted users of my makefile to be able to enter any of the following to accomplish the same result, such that the following were effectively synonyms of each other:
make hit list
make hitlist
make hit_list
What I did in my makefile was the following:
hit_list:
#echo Got here
<the real recipe goes here>
hit: hit_list
hitlist: hit_list
.PHONY: list
list:
#echo > /dev/null
Then, when I tested it using any of the commands "make hit list", "make hitlist", or "make hit_list", I got identical results, as intended.
By extension, if one of your targets was the one with the long name but you used this approach whereby a simple short name identified the target with the long name as a prerequisite, I think that you should be able to say "make short_name" and accomplish what you're asking about.
This differs from your Approach 1 in that none of the synonyms is defined as a phony target (considering that "make hit list" is a command to make two targets, the second being effectively a noop), so the complication that you described would not arise.

implicit rule not run although its depedency is missing

I have a Makefile similar to this:
.PRECIOUS: do/%.build
do/%.install: do/%.build
touch $#
do/%.build:
touch $#
My intention is that all do/.install targets depend on the do/.build target, and that the rule for the build target is run if the stamp file for the build target is missing, and that the install target is run if the build stamp is newer. This works fine during the first run:
$ make do/foo.install
touch do/foo.build
touch do/foo.install
It also works fine if the build stamp is newer:
$ touch do/foo.build
$ make do/foo.install
touch do/foo.install
However, it doesn't work as intended if the install stamp is present and the build stamp is missing:
$ rm do/foo.build
$ make do/foo.install
make: `do/foo.install' is up to date.
The install target is not run. What should I do? Does this have something to do with the fact that I have to add the .PRECIOUS line to avoid the automatic deletion of the build stamp?
Regards,
Tino
From the manual:
"If an ordinary file b does not exist, and make considers a target that depends on b, it invariably creates b and then updates the target from b. But if b is an intermediate file [i.e inferred from a pattern rule], then make can leave well enough alone. It won't bother updating b, or the ultimate target, unless some prerequisite of b is newer than that target or there is some other reason to update that target."
There are a couple of ways to solve this problem, but I know of no really clean way. Do any other targets depend on the build or install files?

How do I check dependencies when invoking a sub-make to build when there are changes?

If I have a makefile that calls another makefile, how to I get the master makefile to correctly check if the dependencies of the subordinate makefile have changed?
For example, if I have the rule
server:
#cd $(SERVERDIR) && $(MAKE)
That invokes make in the subdirectory in which I build an executable "server". However, if I change one of the files that make up server, the parent make doesn't see the changes and refuses to rebuild server - "make: `server' is up to date."
How can I get the master makefile to correctly detect when there's a change in one of the dependent files (something like $(SERVERDIR)/server.c, for example?
It looks like you want to use a phony target
.PHONY: server
server:
#cd $(SERVERDIR) && $(MAKE)
There's a detailed description of the Phony target here, but the short description is you're telling the makefile that there will never be a file that corresponds with this server target, and therefore it won't consider server up to date if there is a file named server in the directory.
Your target name matches the name of one of the files or directories in your main Makefile directory.
Assuming you need to build everything in a subdirectory called server, this rule:
server:
$(MAKE) -C server
will not work, as the target server is a directory, has no source files and doesn't need to be built then.
This one:
srv:
$(MAKE) -C server
will work, as long as there is no file or directory called srv.
You don't:
Recursive Make Considered Harmful
Implementing non-recursive make
But yes, if you have no choice, e.g. because you don't control the sub-makefile, a .PHONY target is what you are looking for.

Resources