Making a target out-of-date if compiler options differ respect to the previously used ones - linux

Consider the following directory tree for a made-up toy example that illustrates the problem to solve:
- Makefile
- lib.c
+ foo/
|-- config.mk
+ bar/
|-- config.mk
Content of foo/config.mk:
CFLAGS += -DFOO
Content of bar/config.mk:
CFLAGS += -DBAR
Calling make with Makefile for the targets foo and bar results in the files foo/config.mk and bar/config.mk being included (by means of the include directive), respectively, and lib.o being built, i.e.:
# build lib.o with the macro FOO defined
$ make foo
# build lib.o with the macro BAR defined
$ make bar
# build lib.o with both the macros FOO and BAR defined
$ make foo bar
$ make bar foo
The default rule for building lib.o uses the variable COMPILE.c, which is defined (according to the output obtained by calling make with the option --print-data-base) as:
COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
The expansion of COMPILE.c depends, among other things, on the value of the variable CFLAGS, which, in turn, depends on whether foo/config.mk or bar/config.mk were included, since these makefiles modify the CFLAGS variable.
What I would like to achieve is to treat the target lib.o as an out-of-date target if the expansion of the variable COMPILE.c that is currently used is not the same as the one used for the previous build of lib.o. For example:
$ make foo
# It shouldn't rebuild anything since lib.o should be up-to-date
$ make foo
# It should rebuild lib.o since it should be out-of-date
$ make bar
# It should rebuild lib.o since it is again out-of-date
$ make foo bar
# It shouldn't rebuild lib.o since it is up-to-date
$ make bar foo
This solution explains how I've implemented this behaviour so far. Any suggestion is welcomed.

I would dump the variable's value in another included makefile and check whether the current value is different from the one from the included makefile. Something like:
ifeq ($(filter foo,$(MAKECMDGOALS)),foo)
include foo/config.mk
endif
ifeq ($(filter bar,$(MAKECMDGOALS)),bar)
include bar/config.mk
endif
-include old_compile.mk
COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
ifneq ($(COMPILE.c),$(OLD_COMPILE.c))
FORCE := force
endif
lib.o: lib.c $(FORCE)
$(COMPILE.c) $< -o $#
echo 'OLD_COMPILE.c := $(COMPILE.c)' > old_compile.mk
.PHONY: foo bar all force
foo bar all: lib.o
Demo:
$ make foo
cc -DFOO -c lib.c -o lib.o
echo 'OLD_COMPILE.c := cc -DFOO -c' > old_compile.mk
$ make foo
make: Nothing to be done for 'foo'.
$ make foo bar
cc -DFOO -DBAR -c lib.c -o lib.o
echo 'OLD_COMPILE.c := cc -DFOO -DBAR -c' > old_compile.mk
make: Nothing to be done for 'bar'.
$ make bar foo
make: Nothing to be done for 'bar'.
make: Nothing to be done for 'foo'.

Here's my crude mockup that encodes compiler/linker flags directly in filenames. It's just an idea, an actual implementaation should be a bit more robust.
empty:=
space:= $(empty) $(empty)
comma:= ,
LDFLAGS_NS=$(subst $(space),$(comma),$(LDFLAGS))
CFLAGS_NS=$(subst $(space),$(comma),$(CFLAGS))
EXEDIR=./test-exedir-cflags=$(CFLAGS_NS)-ldflags=$(LDFLAGS_NS)
EXEDIR_PAT=./test-exedir-*
OBJDIR=./test-objdir-cflags=$(CFLAGS_NS)
OBJDIR_PAT=./test-objdir-*
test.exe: $(EXEDIR)/test.exe
rm -f test
ln -s $< $#
$(EXEDIR)/test.exe: test.o
rm -rf $(EXEDIR_PAT)
mkdir $(EXEDIR)
cc $(LDFLAGS) -o $# $<
test.o: $(OBJDIR)/test.o
rm -f test.o
ln -s $< $#
$(OBJDIR)/test.o: test.c
rm -rf $(OBJDIR_PAT)
mkdir $(OBJDIR)
cc -c $(CFLAGS) -o $# $<
Tested with a hello world test.c file and these commands:
make test.exe
make test.exe
make test.exe CFLAGS="-g -Wall"
make test.exe CFLAGS="-g -Wall"
make test.exe

My approach, so far, consists of generating a file, cc-options-dump, with the contents of the expansion of the COMPILE.c variable everytime lib.o is built.
The MD5 hash resulting of the expansion of the current COMPILE.c variable is compared against the one that was used for the previous build, i.e., the one whose contents are stored in the file cc-options-dump, if any (i.e., if the file does exist).
.PHONY: foo bar require-rebuild cc-options-dump
# include the xxx/config.mk files
# sort built-in function to make it independent of the order (i.e., "foo bar" or "bar foo")
$(foreach opt,$(sort $(MAKECMDGOALS)),$(eval include $(opt)/config.mk))
# obtain the MD5 hash of the previously used flags
ifneq (,$(wildcard cc-options-dump))
prev-cc-options-hash := $(shell md5sum cc-options-dump | cut -f1 -d' ')
endif
# obtain the MD5 hash of the current flags
curr-cc-options-hash := $(shell echo "$(COMPILE.c)" | md5sum | cut -f1 -d' ')
# Do these hashes differ?
ifneq ($(prev-cc-options-hash),$(curr-cc-options-hash))
# keep track of the fact that a rebuilt needs to be triggered
does-need-rebuild := require-rebuild
# just for displaying information
$(info Rebuild required)
else
$(info Rebuild not required)
endif
# make both targets foo and bar dependent on the file with the flags
# so that it has to be generated, since it is a phony target as well
foo bar: cc-options-dump
# lib.o has now an additional prerequisite for determining whether it need to be rebuilt
lib.o: $(does-need-rebuild)
# save the used compiler flags for comparing them with the future flags
cc-options-dump: lib.o
#echo '$(COMPILE.c)' >$#
The behaviour of this makefile when calling make for the targets foo and/or bar corresponds, as far as I can see, to the desired one:
$ make foo
Rebuild required
cc -DFOO -c -o lib.o lib.c
$ make foo
Rebuild not required
$ make bar
Rebuild required
cc -DBAR -c -o lib.o lib.c
$ make bar
Rebuild not required
$ make foo bar
Rebuild required
cc -DBAR -DFOO -c -o lib.o lib.c
$ make bar foo
Rebuild not required
The use of the sort built-in function is crucial for the last two cases above to work correctly.
It would be great if someone could come with a more elegant solution.

Related

Compiling with SDCC fails with -Wall option

I'm compiling a 8051 project with SDCC but has a problem with Makefile, the following sources:
TARGET = test
CC = sdcc
CFLAGS = -Wall -I.
RM = rm -rf
SRCS = $(wildcard *.c)
RELS = $(patsubst %.c,%.rel,$(SRCS))
$(TARGET).bin: $(TARGET).hex
objcopy -I ihex -O binary $< $#
$(TARGET).hex: $(TARGET).ihx
packihx $< > $#
$(TARGET).ihx: $(RELS)
#echo Linking ...
$(CC) $(CFLAGS) $< -o $#
#echo Build finish!
%.rel: %.c
#echo Compiling ...
$(CC) $(CFLAGS) -c $< -o $#
.PHONY: clean
clean:
#echo Removing ...
$(RM) *.rel *.ihx *.lk *.lst *.map *.mem *.rst *.sym *.asm $(TARGET)
#echo Removed!
When I run make it has errors:
minh#PCDESIGN:~/workspaces/programMSC51/test$ make
Compiling ...
sdcc -Wall -I. -c main.c -o main.rel
sdas Assembler V02.00 + NoICE + SDCC mods (Intel 8051)
Copyright (C) 2012 Alan R. Baldwin
This program comes with ABSOLUTELY NO WARRANTY.
Usage: [-Options] file
Usage: [-Options] outfile file1 [file2 file3 ...]
-d Decimal listing
-q Octal listing
-x Hex listing (default)
-g Undefined symbols made global
-a All user symbols made global
-b Display .define substitutions in listing
-bb and display without .define substitutions
-c Disable instruction cycle count in listing
-j Enable NoICE Debug Symbols
-y Enable SDCC Debug Symbols
-l Create list file/outfile[.lst]
-o Create object file/outfile[.rel]
-s Create symbol file/outfile[.sym]
-p Disable automatic listing pagination
-u Disable .list/.nlist processing
-w Wide listing format for symbol table
-z Disable case sensitivity for symbols
-f Flag relocatable references by ` in listing file
-ff Flag relocatable references by mode in listing file
-I Add the named directory to the include file
search path. This option may be used more than once.
Directories are searched in the order given.
removing
make: *** [Makefile:22: main.rel] Error 1
How can I fix this?
Unlike other compilers, SDCC does not have a -Wall option. You should remove it from CFLAGS = -Wall -I. in the Makefile.
It also does not have a replacement. There are options --less-pedantic and -Werror, which gives you fewer warnings, or treats warnings as errors, respectively, but there is no option for creating more warnings.
The manual mentions --more-pedantic, but
Actually this is not a SDCC compiler option but if you want more warnings you can use a separate tool dedicated to syntax checking [...]
See SDCC Compiler User Guide (version 4.1.12), section 3.3.4.

Linux make command is deleting a source file

I have inherited a project file that has a Makefile in it that is doing something I have never seen before--It is injecting a rm command. I cannot find any reason for the rm command, so I am missing something very obvious or very esoteric.
Thanks
The results of running make are:
bison --defines --xml --graph=calc.gv -o calc.c calc.y
Bison flags =
cc -c -o calc.o calc.c
Making BASE = calc
cc -o calc calc.o
Done making BASE
rm calc.c <======== WHERE IS THIS COMING FROM?
The Makefile is:
BASE = calc
BISON = bison
XSLTPROC = xsltproc
all: $(BASE)
%.c %.h %.xml %.gv: %.y
$(BISON) $(BISONFLAGS) --defines --xml --graph=$*.gv -o $*.c $<
#echo "Bison flags = " $(BISONFLAGS)
$(BASE): $(BASE).o
#echo "Making BASE = " $(BASE)
$(CC) $(CFLAGS) -o $# $^
#echo "Done making BASE"
run: $(BASE)
#echo "Type arithmetic expressions. Quit with ctrl-d."
./$<
html: $(BASE).html
%.html: %.xml
$(XSLTPROC) $(XSLTPROCFLAGS) -o $# $$($(BISON) --print-datadir)/xslt/xml2xhtml.xsl $<
CLEANFILES = $(BASE) *.o $(BASE).[ch] $(BASE).output $(BASE).xml $(BASE).html $(BASE).gv
clean:
#echo "Running clean" $(CLEANFILES)
rm -f $(CLEANFILES)
See https://www.gnu.org/software/make/manual/make.html#Chained-Rules:
The second difference is that if make does create b in order to update something else, it deletes b later on after it is no longer needed. Therefore, an intermediate file which did not exist before make also does not exist after make. make reports the deletion to you by printing a rm -f command showing which file it is deleting.

specifying the filename in a makefile

I'm new to this and trying to create a makefile where I could, for example, run:
make -f mymakefile testfile
and the makefile would find testfile.java (which exists in the directory I'm running from), compile it, and run the code.
Instead, I must be confused with how automatic variables work and after working all afternoon I still get the error:
make: Nothing to be done for `testfile'.
Any help would be appreciated and my code is below:
JC=javac
JVM=java
JFLAGS= -g
RM = rm -f
CFLAGS =
CXX = gcc
NAME = *
.SUFFIXES: .java .class
all: run
NAME:
$(CXX) $(CFLAGS) -o $^ $#
echo $(NAME)
$(NAME).class: $(NAME)
$(JC) $(JFLAGS) $(NAME).java
run: $(NAME).class
$(JVM) $(NAME)
.PHONY: clean
clean:
$(RM) $(NAME).class
I've tried just having it echo 'testfile' to better understand how automatic variables work, but I couldn't get that to work correctly either.
The arguments on the make command line select the targets to build. You can't pass values to variables in the same way you would with a shell script (like you're trying to do with "NAME".)
If you really want to pass a value for a variable, the command would be:
NAME=testfile make -f mymakefile
You can use pattern rules to create rules from arbitrary names. For example:
%.bin : %.cpp
$(CXX) $(CXXFLAGS) -o $# $<
When you call make test.bin, this matches pattern rule %.bin, where % matches test. Then automatic variables $< substitutes the source file and $# substitutes the target file. What actually runs is something like g++ -O3 -o test.bin test.cpp.

Phony targets in makefile

I was trying to execute the example in this link.
all : prog1 prog2 prog3
.PHONY : all
prog1 : prog1.o utils.o
cc -o prog1 prog1.o utils.o
prog2 : prog2.o
cc -o prog2 prog2.o
prog3 : prog3.o sort.o utils.o
cc -o prog3 prog3.o sort.o utils.o
With or without PHONY (if i comment out the .PHONY line), the outputs are the same. What is the real use of PHONY here?
The article mentions this
When one directory contains multiple programs, it is most convenient to describe all of the programs in one makefile
Since the target remade by default will be the first one in the makefile, it is common to make this a phony target named ‘all’ and give it, as prerequisites, all the individual programs
I cant understand what is the real use of PHONY here. Can someone point me a scenario in this example in which the program would behave differently without PHONY?
without .PHONY "all" is just a regular make target which depends on prog1, prog2, prog3. This means that if a file named "all" would happen to exist in the directory that is newer than prog1, prog2 and prog3, they will not be built (try it!) - but this is obviously not what you had in mind.
.PHONY tells GNU make the "all" target is phony - you don't REALLY intended for a file called "all" to be created and it should build the dependencies regardless if a file called "all" exists or not.
Added later:
My example with all and prog1 above was not correct although the general idea is true. Here is a much simple example
all: prog
prog: prog.c
clean:
$(RM) prog prog.o
giladb#xxx:~/tmp$ ls
Makefile prog.c
giladb#xxx:~/tmp$ make
cc prog.c -o prog
giladb#xxx:~/tmp$ make clean
rm -f prog prog.o
giladb#nps06:~/tmp$ make
cc prog.c -o prog
giladb#xxx:~/tmp$ touch clean
giladb#xxx:~/tmp$ make clean
make: `clean' is up to date.
At least for GNU Make in combination with this example the behavior regarding the output is the same, no matter if the target "all" is a phony target or not. Even if there is a file named "all".
BUT as described in the manual behind your link the internal behavior of GNU Make is different. If "all" is not a phony target, GNU Make checks whether a file named "all" exists and is older than its prerequisites. Try "make -d" and you will see the difference.
Another important point is that the recipe of a phony target is always executed. If you take this example:
all: goal
echo "Done"
.PHONY : clean
goal:
echo "Hello World!" > $(#)
clean:
rm -f goal all
... and execute this:
$ make clean
rm -f goal all
$ make
echo "Hello World!" > goal
echo "Done"
Done
$ make
echo "Done"
Done
$ touch all
$ make
make: 'all' is up to date.
... the recipe will not be executed after creating the file "all".
With "all" being a phony target:
all: goal
echo "Done"
.PHONY : all clean
goal:
echo "Hello World!" > $(#)
clean:
rm -f goal all
... the recipe of "all" will be executed always independent of the existence of the file "all".
$ make clean
rm -f goal all
$ make
echo "Hello World!" > goal
echo "Done"
Done
$ make
echo "Done"
Done
$ make
echo "Done"
Done
$ touch all
$ make
echo "Done"
Done
$ make
echo "Done"
Done

Compiling C++ and C files in one makefile

I have a makefile that looks like this:
CS := a.c b.c
CPPS := foo.cpp bar.cpp
SOURCES := $(CS) $(CPPS)
OBJS := $(CS:%.c=$(OBJSDIR)/%.o) $(CPPS:%.cpp=$(OBJSDIR)/%.o)
I want to create a single rule to compile them all. But the only option I can think of is this:
$(OBJSDIR)/%.o: %.cpp
$(GXX) $(GXXFLAGS) -c $< -o $#
But of course it doesn't work because some of the object files don't have a matching C++ source file.
Any idea?
suppose you have a.cc, b.cc and c.cc, and on the other side, d.c, e.c and f.c
program_objs = a.o b.o c.o d.o e.o f.o
program: $(program_objs)
$(CC) $(LDFLAGS) -o $# $(program_objs)
You don't need anything more, as make will automatically detect which files are c++ and which ones are plain c and will select the proper compiler.
in case you want something special, not included in makefile, you can add some suffixes (file types) with the rule:
.SUFFIXES: .a .b .o
and then use the following rules to compile them to .o
.a.o:
$(COMPILER_A) $(COMPILER_A_FLAGS) -c $# -o $<
.b.o:
$(COMPILER_B) $(COMPILER_B_FLAGS) -c $# -o $<
and let makefile select the proper compiler (the one stored in variables COMPILER_A or COMPILER_B) to do the work.
Of course, you can compile something to a .o file with an explicit rule, as in:
a.o: a.cc
g++ -o a.o -c a.cc
b.o: b.cc
g++ -o b.o -c b.cc
c.o: c.cc
g++ -o c.o -c c.cc
d.o: d.c
gcc -o d.o -c d.c
e.o: e.c
gcc -o e.o -c e.c
f.o: f.c
gcc -o f.o -c f.c
Note #1:
Some sugestions have been made on GNU make % pattern to construct implicit rules for targets. Below is a rewritting of the implicit rules above to do the same thing:
%.o: %.a
$(COMPILER_A) $(COMPILER_A_FLAGS) -c $# -o $<
%.o: %.b
$(COMPILER_B) $(COMPILER_B_FLAGS) -c $# -o $<
As always, $# means the target of the rule and $< (you can use also $* for the file name without any matching suffixes) the left needed file. For a complete list of automatic variables that can be used, I suggest you to read your make manual. Take into account that the old suffix syntax is reversed from the new one (the target suffix appears last in the old syntax, the new syntax being more similar to a normal makefile rule with the target on the left side of the colon)
I want to create a single rule to compile them all.
As Etan Reisner said in the comment section, make already has implicit rules to compile .c and .cpp file, so the real answer to your question is:
Do not write anything to compile your object files.
That said, you should now remove those two variables: $(GXX) and $(GXXFLAGS).
To provide flags to cc or gcc one should use the CFLAGS built-in variable.
To provide flags to g++ one should use the CXXFLAGS built-in variable.
To provide flags to the preprocessor (cpp) one should use the CPPFLAGS variable.
Since you're mixing C and C++ source files, you should use the CXX variable as the linker command.

Resources