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.
Related
I'm extremely new to makefiles and just spent a full day trying to figure out how to automate my testing. For this project I have one program, main.c and it accepts as parameters an input and an output file. The input file is of the form "test-{filler}.txt" so an example file could be "test-invalid-opcode.txt". The output file would be of the form "out-{filler}.txt", so it would be of the form "out-invalid-opcode.txt". I would then want to compare the output with the correct output saved in "correct-{filler}.txt", so this would be "correct-invalid-opcode.txt". Each test would therefore have an input, output, and correct output. I would want to check if the output and correct output have any differences and I would want this to run on every test file with the prefix "test-". I read through a lot of the makefile spec and a lot of different examples, but I'm really confused as to how to handle this. Any help would be really appreciated.
Here is what I have to run one test:
CC=gcc
CFLAGS=-o
main.o: main.c
$(CC) $(CFLAGS) main.o $<
.PHONY: test
test: main.o test-provided.txt test-out.cs
./program test-provided.txt test-out.txt
diff -q test-out.txt out-provided.txt
The layout of what I want the automatic test to be is something like
.PHONY: autotest
autotest: program
$(foreach <file with "test-" prefix> run:
main.o <test-name> <out-name>
diff -q <correct-name> <out-name>
But I really am lost on how to go about implementing that.
Thank you and sorry I couldn't post more code. I tried a lot of different things, but none of them were even close enough to be worth posting.
Edit to show working final version:
program: main.c
$(CC) $(CFLAGS) $a $<
TEST_INPUTS := $(wildcard test-*.txt)
.PHONY: autotest $(TEST_INPUTS)
autotest: $(TEST_INPUTS)
$(TEST_INPUTS): test-%.txt: program
echo '' > out-$*.txt
./$< $# out-$*.txt
diff -q correct-$*.txt out-$*.txt
In general using loops with make is not very "make-ish". A makefile is an entire language fundamentally based on iteration and recursion so trying to do "extra" iteration inside a recipe is often redundant.
If you need to iterate over something, most especially when that something is files, you should attempt to work with make by taking advantage of its target/prerequisite organization. Above you give an algorithm:
$(foreach <file with "test-" prefix> run:
main.o <test-name> <out-name>
diff -q <correct-name> <out-name>
which is perfectly suited to make's default behavior. To translate this into a makefile you write a rule which invokes a single iteration of the loop, then use prerequisites to run it for the files you want. Something like this:
TEST_INPUTS := $(wildcard test-*.txt)
.PHONY: autotest $(TEST_INPUTS)
autotest: $(TEST_INPUTS)
$(TEST_INPUTS): test-%.txt: program
$< $# test-out.txt
diff -q test-out.txt out-$*.txt
Not only is this much more make-like but it has other advantages: for example you can run make test-provided.txt and it will run just that one test instead of all the tests.
You have to use static pattern rules here not normal pattern rules because .PHONY targets can't work with pattern rules.
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.
I have a large project I'm working on, in which I want to perform include to some .mak file, but only after I make change to this file content via a command inside the original makefile. Since it's a large project it will be hard to write code, so I will give this ridiculous example instead:
I have some small C project that all it's C and header files are in the same directory, and I need to write a makefile. I'm not allowed to use clean rule in the makefile I write, but I have a file named file.mak that I can include in my makefile. Content of file.mak:
.PHONY: clean
cleam:
$(RM) $(objs) test
The problem here is that the rule is cleam and not clean. I'm also not allowed to change manually file.mak , but I'm allowed to do this with a command inside the original makefile. This can be done easily by:
sed -i 's/cleam/clean/g' file.mak
So I thought of writing the makefile like this:
CC = gcc
srcs = $(wildcard ./*.c)
objs = $(srcs:.c=.o)
test: $(objs) change_file include_file
$(CC) $^ -o $#
%.o: %.c
$(CC) -c $< -o $#
change_file:
$(shell sed -i 's/cleam/clean/g' file.mak)
include_file: change_file
include file.mak
But I get the following error:
include: Command not found
So I understand that there is a problem of using include inside a rule, so is there a way to achieve what I want?
(GNU) make has a feature Remaking Makefiles that can be used for scenarios like this, but your approach is wrong. include is a directive and can't be used in a recipe.
Instead, when you include a file, make first checks for rules creating this exact file and executes them. As in your case, the file you want to include already exists, you have to make this rule .PHONY to force its execution. It would look like this:
.PHONY: file.mak
file.mak:
sed -i 's/cleam/clean/g' file.mak
include file.mak
As a more robust alternative (without the need for a phony rule), consider creating a fixed version (copy) and include this:
file_fixed.mak: file.mak
sed -e 's/cleam/clean/g' <file.mak >file_fixed.mak
include file_fixed.mak
I am working on a makefile on OS X and I'm having issues. I've made everything as I am supposed to (I think) but only part of it is working. When I try :
make file1
or:
make file2
everything seems fine but when I try :
make all
or:
make clean
I get the typical :
*** No rule to make target
Here's the code, I hope you can spot the issue(s) :
CC = gcc
CFLAGS=-Wall
FILES=file1 file2
all: $(FILES)
file1: file1.c
$(CC) $(CFLAGS) file1.c -o file1
file2: file2.c
$(CC) $(CFLAGS) file2.c -o file2
clean:
$(RM) $(FILES)
Cheers !
Transferring comments into an answer, mainly so the question can be closed.
There's no obvious reason why the syntax shown would cause that problem, so I suspect that make file1 is working without any makefile and coming up with a default rule to do the compilation (and probably doesn't use the -Wall option when compiling).
What's the name of the makefile you created?
The makefile is named makefile, and you are indeed right: -Wall isn't used (I just noticed).
That's curious. Take a close look at the name: ls m* | od -c. See if the letters are what you expect, and there's no trailing space at the end of the name or anything untoward like that.
But the 'no -Wall' observation practically confirms that the trouble is with the name of the makefile, though it isn't yet obvious why it is not working. You could try make -f makefile all and see whether that makes any difference. If the name is wrong, you should get told that it can't open the makefile.
I tried remaking a makefile from scratch containing exactly the same information and it runs smoothly. You were definitely right about the name issue.
That sort of problem is bizarre and confusing — it was sensible to get help.
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)