Why does make not run this phony target? - makefile

I have a run target defined in my Makefile, which is really just there so I don't need to type it all the time. It looks like this:
proto: ...
# generate protobuf python definitions from proto files
.PHONY: run
run: proto
$(shell python examples/run_simulation.py)
But make run yields
make: Nothing to be done for `run'.
I was under the assumption that the .PHONY thing makes make think the target is always out of date (various SO answers state this), but it does not seem to work. What am I missing?

Related

Makefile: no target error even when target and dependency exist

My makefile:
./corpus/%.spacy : ./assets/%.json
python3 ./scripts/convert.py $< $#
Two questions:
Even if A.spacy and A.json exist and A.json is updated more recently than A.spacy, I get the following output.
$ make
$ make: *** No targets. Stop.
What to add to have it make A.spacy if only A.json exists? I tried the below code, which didn't work and I feel I'm not fully understanding targets and dependencies in makefiles.
convert : ./corpus/%.spacy
python3 ./scripts/convert.py $(./scripts/$*.json) $<
./corpus/%.spacy: ./assets/%.json
echo $?
Didn't work as in gave the following output
$ make convert
$ make: *** No rule to make target `corpus/%.spacy', needed by `convert'. Stop.
You seem to be thinking that declaring a pattern rule will cause make to go spelunking your directory looking for all possible ways to use that pattern, as if it were a wildcard or something akin to ls *.spacy.
That's not what a pattern rule is.
A pattern rule is a template that make can apply if it wants to build a given target and it doesn't know how to build that target.
If you have the above makefile it just tells make "hey, if you happened to want to create a target that matches the pattern ./corpus/%.spacy then here's a way to do it". If you type make with no arguments, then you haven't told make that you want to build anything so it won't use your pattern rule.
If you type:
$ make ./corpus/A.spacy
now you've told make you want to actually build something (./corpus/A.spacy), so now make will try to build that thing, and it will see your pattern rule and it will try to use it.
As for the other, this:
convert : ./corpus/%.spacy
python3 ./scripts/convert.py $(./scripts/$*.json) $<
is not a pattern rule. A pattern rule must have a pattern character (%) in the target; this is defining a target convert that depends on a file named, explicitly, ./corpus/%.spacy of which you don't have any file with that name, so you get that error.
You didn't actually describe what you wanted to do, but I think maybe you want to do something like this:
# Find all the .json files
JSONS := $(wildcard ./assets/*.json)
# Now figure out all the output files we want
SPACYS := $(patsubst ./assets/%.json,./corpus/%.spacy,$(JSONS))
# Now create a target that depends on the stuff we want to create
all: $(SPACYS)
# And here's a pattern that tells make how to create ONE spacy file:
./corpus/%.spacy : ./assets/%.json
python3 ./scripts/convert.py $< $#

Makefile not updating when dependency changed

all: ./data/for_analysis.csv ./data/tables/*.docx
./data/for_analysis.csv : ./src/convert-xls-to-gold-standard.py ./data/ED-TRAUMA-DELTA-STUDY_3_2019_total.xlsx
python3 $< --rawDataPath $(word 2,$^) --fieldCodesPath ./data/excel_field_codes.json --processedDataPath ./data/for_analysis.csv --logDir ./logs
./data/tables/%.docx : ./src/make-%.py ./data/for_analysis.csv
python3 $< --fieldCodesPath ./data/excel_field_codes.json --processedDataPath ./data/for_analysis.csv --logDir ./logs --tablesDir ./data/tables
When I update ./src/make-table-2.py, the second target isn't updated. This behavior doesn't depend on whether ./data/table/table-2.docx exists or not.
When I run make or make all even after updating the py file, I get the message make: Nothing to be done for 'all'.
It's not exactly clear from your question what the state of your targets is before you run make. But:
all: ./data/for_analysis.csv ./data/tables/*.docx
this can't really work, in general. This tells make, "go find all the files that exist with the filename matching the wildcard ./data/tables/*.docx". E.g., that's the same thing you'd get if you run ls ./data/tables/*.docx before you started make.
But of course, if you haven't built anything yet then there are no files matching that pattern, because that's what you're asking make to build. So this expands to nothing and make won't do anything with them.
You have to list the targets that you want to build explicitly, or else convert them from the source files you want them to be built from, so you can tell make what it should be building.
For example, maybe:
all: ./data/for_analysis.csv $(patsubst ./src/make-%.py,./data/tables/%.docx,$(wildcard ./src/make-*.py))

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.

explicity chain dependency in Makefile

What's the pattern to follow when specialized Makefiles in a directory depends on the main one in a parent dir?
i have:
/
/Makefile
/src
/src/Makefile
/tests
/tests/Makefile
in /Makefile i have:
TESTING_COMMAND=something
dotest1:
make -C tests/ $#
in /tests/makefile i have
dotest1:
$(TESTING_COMMAND) $?
if i run:
me#host:/ $ Make dotest1
it works. but if i execute from the tests dir:
me#host:/tests/ $ Make dotest1
it will try to execute the test file in the shell, because $(TESTING_COMMAND) is empty, so it's first argument became the command passed to the shell.
I don't necessarily need that to work if executed in the /tests/ or /src/ dir, but need a way to gracefully fail.
Trying to send everything through the command line (or environment) seems like a bad idea to me. That's what inclusion was invented for. Put your common values into a separate file, something like config.mk, then in all your makefiles just use:
include config.mk
to get them included.
Your design scares me, but this will do the trick in the main Makefile:
TESTING_COMMAND=something
dotest1:
make -C tests/ $# TESTING_COMMAND=$(TESTING_COMMAND)
If you want tests/Makefile to fail well, you have a couple of options. If only that one target depends on TESTING_COMMAND, you can have it print a warning and do nothing:
ifdef TESTING_COMMAND
dotest1:
$(TESTING_COMMAND) $?
else
dotest1:
#echo warning: TESTING_COMMAND not defined
endif
Or if the whole Makefile depends on it, you can have Make print a warning or abort:
ifndef TESTING_COMMAND
$(warning TESTING_COMMAND is undefined, but Make will try to us it anyway)
$(error TESTING_COMMAND is undefined, Make will now abort)
endif
You can also have it abort the sub-make (the one that runs tests/Makefile) but still continue running the Make process that invoked it, but that's kind of a pain.

Resources