Make: run code before a dependency is checked - makefile

I have a target that depends on an external library:
output.txt: library/build.txt main.txt
cat library/build.txt main.txt >output.txt
(This is just an example, my actual makefile is much more complicated)
However, library/build.txt is generated by a separate makefile.
I need to run the library makefile (to potentially update build.txt) before make checks if library/build.txt is newer than output.txt
I could just have it run $(MAKE) -C library, every time, but I'm hoping to only call that when it is needed (meaning, when you try to make a target which depends on library/build.txt)
Is this possible? (maybe there's a way to get a list of dependencies and then run something if that list contains library/build.txt?)

You can try things like :
FORCE:
library/build.txt: FORCE
#echo make to build $#
output.txt: library/build.txt main.txt
cat $^ > $#

Related

Refreshing makefile midway through interpretation

I'm working with a c++ project that someone else wrote, and the makefile is quite convoluted. The only way that I can get a certain dependency to build is by having it build and then using sed to change a portion of the makefile. However, this means running the make command twice. This honestly is the only way to solve the issue and I've been tearing my hair out over it.
Is there any way that I can have make refresh the makefile so that it sees the updated makefile before continuing interpretation?
Example of what needs doing:
.PHONY: target1 target2
target1:
#sed -i 's/Brian/World/' Makefile
# At this point, make needs to refresh the contents of the makefile to see the changes made
target2: target1
#echo "Hello Brian!"
# After the makefile has been refreshed, this will print "Hello World!"
I would write this with a separate temporary copy of the makefile:
Makefile.world: Makefile
#sed 's/Brian/World/' $< >$#
target1: Makefile.world
$(MAKE) -f $<
but if the modification never changes (like this), there's little reason to make it a temporary. Unless you're frequently editing Makefile and want to make sure Makefile.world stays up to date.

Why does this makefile loop infinitely, but only on one machine?

Consider the following makefile:
-include target.d
build:
%.d: %.c
#echo ">>> %.d: %.c"
touch $#
target.d: dependency
# #echo ">>> target.d: dependency"
dependency:
#echo ">>> dependency"
.PHONY: build
If you place this in a directory with two (empty) files, target.d and target.c and run make, then on one VM I use it produces the following output:
$ make
>>> dependency
>>> %.d: %.c
touch target.d
>>> dependency
>>> %.d: %.c
touch target.d
make: Nothing to be done for `build'.
On another VM, it loops infinitely. Both VMs are running Centos7, and both of them are using the same version of GNU make (3.82).
(Note: if you uncomment the commented out line below the target.d target, then both produce the exact same output; that behavior makes sense to me, at least)
I am aware that by adding the recipe to the target.d: dependency makes it prioritise that recipe over the generic one, while simply adding a dependency. But what I don't understand is why one system causes an infinite loop to occur while another extremely similar system does not.
What is the reason for this strange behaviour?
(EDIT: I found out I can simplify the makefile and still see the same behaviour)
I think the issue here is with having a target called target.d that is the same as the include file target.d such that when you edit target.d by touching it this somehow invokes an "undefined" behaviour. It seems like in this case it is triggering a re-call of the makefile. I can distil your problem down to this:
-include target.d
target.d: dep
touch target.d
dep:
#echo dep
With the only files that exist being: makefile. target.d is created on the first run of make.
$ ls
makefile
$ make
dep
touch target.d
dep
touch target.d
dep
touch target.d
:
etc
:
$ ls
makefile target.d
I do not believe it is correct to have a target that is also an item you include in your makefile. Usually with .d (e.g. auto generated dep files with gcc) you would generate these files in a compile rule like %.o: %.c ... which creates the .o and .d files. Then you have your include %.d type line. But you should not have a target to generate .d files directly. I do not think this is valid - please someone correct me if I am wrong here.
E.g. makefile that I think you intend:
-include target.d
.PHONY: build
build: target.o
# Simulates a compile line that generates and object file (.o) and dependency file (.d)
target.o: target.c dependency
touch target.o
touch target.d
.PHONY: dependency
dependency:
#echo ">>> dependency"
output:
$ ls
makefile target.c
$ make
>>> dependency
touch target.o
touch target.d
$ ls
makefile target.c target.d target.o
So as #code_fodder's answer suggested, the entire makefile can be simplified heavily to
-include a
a: b
touch a
b:
#echo b
There is a valid question as to whether or not the inclusion of a makefile that you create is a good idea; As stated in the comments above, this is a distillation of part of a build system that I am working with but do not have control over, so c'est la vie.
So there remains the question as to why this behaves differently on two different systems?
The problem turned out to be the following: one VM was run on a remote server, and the other VM was being run locally. In particular, my host machine is a mac and I was sharing some of the file system between the mac and the VM. It appears that the filesystem that I am using on my Mac only keeps timestamps to second precision, while that on the VMs is kept to sub-millisecond precision. As a consequence the Mac doesn't pick up the "updated" file.
I was able to see this on the VM that shares files with the mac by adding sleep commands of various lengths:
-include a
a: b
sleep 0.8
touch a
b:
#echo b
which would cause it to run a finite, but quasi-random number of times. Or, more simply, running make; make which yields
$ make; make
b
touch a
b
touch a
make: `a' is up to date.
b
touch a
make: `a' is up to date.
i.e. running make twice in short succession lets the second run see the updated timestamp of the first, and so it doesn't trigger a second time.

How to determine if Make target is a PHONY?

I have a make target that depends on a variable, which contains both PHONY and real targets.
This target needs to depend only on the real targets in the variable.
How can I test a variable to determine if it is a PHONY or not, so I can filter them out?
(I can test for a file's existence inside the recipe, but I don't want my target to be triggered by execution of any of the PHONY targets.)
Thanks!
There is a way to do it, but I would strongly recommend against it. First of, phony targets can be also file targets. And there is no way to tell a phony file target from a non-phony file target.
It looks like the question implies that the phony targets the author wants to ignore are all non-file targets. In this case see the example below.
.PHONY: phony_target .FORCE
.FORCE:
ALL_TARGETS = phony_target file_target undetermined_target
-include detect_phony.inc
all: final_target
# All done
final_target: $(REAL_TARGETS)
# create $# triggered by $?
#touch $#
ifeq (,$(MAKE_RESTARTS))
# Generate the list of real file targets in make include file
detect_phony.inc: .FORCE
#echo 'REAL_TARGETS = ' `ls $(ALL_TARGETS) 2>/dev/null` > $# |:
endif
file_target:
touch $#
undetermined_target phony_target:
# process $#
clean:
rm -f file_target final_target
Here are the test results:
$make clean
rm -f file_target final_target
$ make
# create final_target triggered by
# All done
$ touch file_target
$ make
# create final_target triggered by file_target
# All done
$ make
# All done
As you can see it only triggers the final target when the file target is updated.
Before you criticize - Here are the flaws of this implementation:
make is always called twice, updating the generated detect_phony.inc include file at every run
if detect_phony.inc gets corrupted somehow, make execution will be locked by syntax errors, until you manually delete it.
it can't handle phony file targets as I mentioned before
if another generated include is added in this makefile that requires another restart before detect_phony.inc this functionality will break.
So it this method is hacky and has several gotchas. I would not use it in production environment. I would insist on changing the top level Makefile first.

Makefile stops running at the middle [duplicate]

Hopefully this is a very simple question. I have a makefile pattern rule that looks like this:
%.so : %.f %.pyf
f2py -c -L${LAPACK_DIR} ${GRASPLIBS} -m $* $^ ${SOURCES} --opt='-02' --f77flags='-fcray-pointer' >> silent.txt
I want the makefile to build a number of .so files, so I tried to get it to build two files (radgrd_py.so and lodiso_py.so) by doing this:
radgrd_py.so lodiso_py.so:
%.so : %.f %.pyf
f2py -c -L${LAPACK_DIR} ${GRASPLIBS} -m $* $^ ${SOURCES} --opt='-02' --f77flags='-fcray-pointer' >> silent.txt
and then tried this:
radgrd_py.so:
lodiso_py.so:
%.so : %.f %.pyf
f2py -c -L${LAPACK_DIR} ${GRASPLIBS} -m $* $^ ${SOURCES} --opt='-02' --f77flags='-fcray-pointer' >> silent.txt
But in each case, it only builds the first target that I specify. If I run 'make radgrd_py.so' it works fine, I'm just not sure how to specify a list of files that need to be built so I can just run 'make'.
The usual trick is to add a 'dummy' target as the first that depends on all targets you want to build when running a plain make:
all: radgrd_py.so lodiso_py.so
It is a convention to call this target 'all' or 'default'. For extra correctness, let make know that this is not a real file by adding this line to your Makefile:
.PHONY: all
Best way is to add:
.PHONY: all
.DEFAULT: all
all: radgrd_py.so lodiso_py.so
Explanations:
make uses the first target appearing when no .DEFAULT is specified.
.PHONY informs make that the targets (a coma-separated list, in fact) don't create any file or folder.
all: as proposed by schot

Change make's working directory without long command line

I would like to change the working directory of a makefile.
(Extraneous info: I have a legacy makefile that I mostly want to reuse, though many targets and generated deps files make assume that the working directory will not be different. My idea is to create a makefile for my newer project, which is in a different directory, and include the old one, and set the working directory to the old directory.)
I easily can do this from the command line with
make -f /path/to/new/makefile -C /path/to/old/makefile
The users of this makefile would like not to type that out every time.
Attempt 1
Use MAKEFLAGS in the makefile itself. But neither of these seem to have any effect. (I understand why -f couldn't have an effect; I'm really wondering about -C.)
I've looked at http://www.gnu.org/software/make/manual/html_node/Options-Summary.html, but I can't find anything about what is allowed in MAKEFLAGS and what isn't.
Attempt 2
Create a makefile2 with the new targets
include path/to/old/makefile
foo: bar
and then makefile passes everything through
%:
$(MAKE) -f $(abspath makefile2) -C path/to/old/makfele /$*
I don't get nice autocompletion, parallel jobs don't work, and debug options (dry run) doesn't work.
So
(1) Why doesn't -C work MAKEFLAGS (it does work, but I made a mistake; it doesn't work, and it is documented; it doesn't work, and it is not documented but it is intentional; it doesn't work, and it is a bug)?
(2) Is there a better way of change a makefile's working directory?
Some things are wrong here :
make -f /path/to/new/makefile -C /path/to/old/makefile
The -f options specifies the name of the Makefile to be found when searched in the directory specified with -C (or the current directory if not provided). So it is more :
make -C /path/to/old/Makefile -f name_of_old_makefile
If the name is simply Makefile or makefile, there is no need to provide the -foption.
The MAKEFLAGS variable does not contains -f or -C in the called sub-Makefile.
To be able to pass multiple targets to another makefile, you need the MAKECMDGOALS variable.
Ultimately, all you have to do in your new Makefile is to have someting like this :
all:
$(MAKE) $(MAKEFLAGS) -C /path/to/old/Makefile -f old_Makefile_name $#
%:
$(MAKE) $(MAKEFLAGS) -C /path/to/old/Makefile -f old_Makefile_name $(MAKECMDGOALS)

Resources