About execution order in makefile - makefile

in this Makefile:
ifeq ($(shell uname),Darwin)
LDFLAGS := -Wl,-dead_strip
else
LDFLAGS := -Wl,--gc-sections -lpthread -ldl
endif
all: target/double
target/double
target:
mkdir -p $#
target/double: target/main.o target/debug/libdouble_input.a
$(CC) -o $# $^ $(LDFLAGS)
target/debug/libdouble_input.a: src/lib.rs Cargo.toml
cargo build
target/main.o: src/main.c | target
$(CC) -o $# -c $<
clean:
rm -rf target
when i excute make all, get this output:
hello_c_use_rust [master] ⚡ make all
mkdir -p target
cc -o target/main.o -c src/main.c
cargo build
Compiling hello_c_use_rust v0.1.0 (/Users/jelly/code/own/hello_rust/hello_c_use_rust)
Finished dev [unoptimized + debuginfo] target(s) in 0.20s
cc -o target/double target/main.o target/debug/libdouble_input.a -Wl,-dead_strip
target/double
4 * 2 = 8
Please tell me why this is the execution order ? txs ^_^.
What puzzled me was why the first step was mkdir -p target;

Your goal is all. all depends on target/double that must thus be done first. In turn target/double depends on target/main.o and target/debug/libdouble_input.a. So target/main.o and target/debug/libdouble_input.a must be done first. Here, you are lucky (we'll see why later): make tries to build target/main.o first. As target/main.o has target as a prerequisite, target must be done first and it is. Qed.
Note: target is an order-only prerequisite of target/main.o, not a regular prerequisite (the | sign starts the list of order-only prerequisites). It means that make pays only attention to its existence. It ignores its last modification time, which is good as last modification times of directories are usually not relevant in a build process.
Why is target a prerequisite of target/main.o? Because you cannot build target/main.o if the target directory does not exist yet. The build would simply fail. So the order-only prerequisite tells make that the directory must exist first.
Why are you lucky? Because if make had tried to build target/debug/libdouble_input.a first and if cargo build does not create the destination directory, it would have failed. Even if you know that target/main.o is built first because it is the first prerequisite of target/double, you should not count on this. One day or another somebody could try to use parallel make (make -j) and things could go wrong. Moreover target could exist but not target/debug...
Note: even if you know that cargo build takes care of creating the destination directory it is probably wise to add one more order-only prerquisite to your Makefile. Just in case something changes one day or another. And also to show readers of your Makefile that you know what you are doing here:
target target/debug:
mkdir -p $#
target/debug/libdouble_input.a: src/lib.rs Cargo.toml | target/debug
cargo build
It is not a big deal and could save you some errors.

Related

Makefile does not recognize pattern rule

I'm really struggling in understanding why the following makefile won't work:
all: buildFolders main.out
mv main.out build/
-echo "File compiled"
buildFolders:
mkdir -p build src
cp *.c src/
%.s: %.c
gcc -S $< -o $#
%.out: src/%.s
gcc $< -o $#
It is executed in a folder containing only the makefile and a main.c file. It should build the src and build folder, copy the main.c in the src folder and then start compiling the main.out. Unfortunately it throws the error "no rule to make target 'main.out'". Since I have the %.out that matches 'main.out' I don't see why it gives me that error. Instead it should look for the src/main.s file, create it and then use it to generate the main.out.
What am I doing wrong? Thanks
You have a number of problems.
First, listing prerequisites in order doesn't create a dependency relationship. If, for example, you ever wanted to enable parallel builds then this:
all: buildFolders main.out
doesn't force the buildFolders target to be built before main.out. These two targets both must be built before all but this doesn't tell make that there's any relationship between buildFolders and main.out. If buildFolders must be completed before main.out can be built then main.out must list buildFolders as a prerequisite.
Second, you haven't told make how to build a file src/main.c. It's built as a side-effect of the buildFolders target, but make can't know that. You need to explain to make that this file can exist. I recommend adding a rule:
src/%.c: %.c
mkdir -p src
cp $< $#
and removing the buildFolders target altogether.
However, I really question why you want to do this anyway. What's the point of copying the source files in the current directory to some sub-directory to build them? It's dangerous and confusing to have multiple copies of source files lying around because they can get out of sync with each other, then you're building older versions and you spend hours trying to understand why something doesn't work. It's a really bad idea.

Make rebuilds everytime

I have a Makefile as :
BUILD_DIR= $(BASE_DIR)/build
_OBJ := a.o b.o
CLEAN_OBJECTS := $(_OBJ)
.PHONY: clean
create_code:
python ../script/my_script.py
all: create_code $(_OBJ)
$(_OBJ): %.o: %.c
mkdir -p $(BUILD_DIR)
$(CC) $(CFLAGS) $(INCLUDE_PATH) -c $< -o $#
cp *.o $(BUILD_DIR)
clean:
rm -f $(CLEAN_OBJECTS)
The target create_code executes the python script and generates a set of .c/.h files.
The target _obj compiles them.
Every time I run make all , create_code target is run even though there is no change to .c/.h generated earlier .
Any suggestions on why this is happening and how to make this create_code target run only if make clean was done earlier .
The underlying problem is that you have one or more generated files that depend on something other than the underlying file system -- namely the contents of your database.
One possibility would be to take advantage of the fact that make, having invoked a rule to rebuild a target will, nonetheless, always check the update time of that target when it is specified as a prerequisite in any other rule.
So, given the rule (untested)...
.PHONY: FORCE
%.c: FORCE
command-to-generate-source $#.tmp
diff -q $#.tmp $# || cp $#.tmp $#
Invoking make foo.c from the command line. make will run the commands...
command-to-generate-source foo.c.tmp
diff -q foo.c.tmp foo.c || cp foo.c.tmp foo.c
where command-to-generate-source foo.c.tmp is expected to leave its output in foo.c.tmp. If the newly generated output file is different than the existing file the cp operation will be run and, hence, the target timestamp will be updated and anything dependent on the target will be updated accordingly.
If, however, the newly generated output file is the same as the existing one then no cp will be run, the target file will be left untouched and make will not consider it to be changed when it appears as a prerequisite in other rules.
This is just one possibility but it's the obvious one given that you already have most (if not all) of the required logic in the command python ../script/my_script.py.

Makefile skips dependency

I've created a makefile for my little project
.SUFFIXES:
%.cpp:
$(COMP) -c -o $(subst .cpp,.o,$#) $(SRCDIR)$# $(CFLAGS)
platformL: COMP:=gcc
platformL: $(FILES)
$(COMP) -o $(NAME) $(subst .cpp,.o,$(FILES)) $(CFLAGS)
rm $(subst .cpp,.o,$(FILES))
platformW: COMP:=wine gcc
platformW: $(FILES)
$(COMP) -o $(NAME).exe $(subst .cpp,.o,$(FILES)) $(CFLAGS)
rm $(subst .cpp,.o,$(FILES))
default: platformL platformW
echo Done!
Everything worked fine until I branched to 2 different platforms, 'make' command executes only my platformL branch. After spending some time with it I discovered that adding '.PHONY' won't fix the problem. Also, it appears that only the first branch from the top gets executed (I have put the lines of platformW before platformL and only Windows compilation was performed).
How can I make it execute both branches?
Make always builds the first explicit target (and all prerequisites of the first explicit target) in the makefile, by default. That's all it will build by default.
You can either specify multiple things to build on the command line, like make platformL platformW, or you can add a new first target that depends on all the other targets you want built. By tradition that target is named all but you can call it whatever you want:
all: platformL platformW
.PHONY: all
...
platformL: ...
...
platformW: ...

Without .PHONY target is getting built when a file named as target already exists in current directory

While going through MakeFiles I found that when the file named as target is present even then without using .PHONY, target is getting built.
But when I am doing the same with another target i.e. clean, then target is not getting built and saying "clean is up-to-date" which is OK.
I just want to know why the other target is getting built when the file exists in the current directory.
makefile:
CC:= gcc
CCFLAGS:=-Wall -Wextra
hello: hellomake.o hellofunc.o
$(CC) $(CCFLAGS) hellomake.c hellofunc.c -o file
hellomake.o : hellomake.c
$(CC) $(CCFLAGS) -c hellomake.c
hellofunc.o : hellofunc.c
$(CC) $(CCFLAGS) -c hellofunc.c
clean:
rm -rf *.o
rm -rf file
My current directory has file named same as the target, as "hello".
It should give result as "hello is up-to-date" but it is not doing it and giving the output as :
make hello
gcc -Wall -Wextra hellomake.c hellofunc.c -o file
Please tell why is this building the target when the TARGET IS NOT .PHONY AND THE FILE NAMED AS TARGET ALREADY EXISTS IN CURRENT DIRECTORY.
Because make looks at the last-modified times to decide what to build. From the make manual:
The make program uses the makefile database and the last-modification times of the files to decide which of the files need to be updated.
The command make examines the time relationships between the target and its prerequisites. If the prerequisites have been modified after the target it means that the target is out of date and the rebuild is triggered even if the file is present. So most likely your dependencies have been modified after the target.
In order to avoid this behavior, you can:
Use touch to change the target timestamp.
Use make -t hello or make --touch hello before invoking make hello. From the docs:
‘-t’
‘--touch’
Marks targets as up to date without actually changing them. In other words,
make pretends to update the targets but does not really change their
contents; instead only their modified times are updated.

Adding dependencies changes the target to the first C file

Consider the following makefile:
.SUFFIXES:
SRC:=../Src
OBJ:=../Obj
# Sources
SOURCES := $(SRC)/App/a.c $(SRC)/App/b.c $(SRC)/App/c.c
HEADERS := $(wildcard $(SRC)/App/*.h)
# Directories
INC_DIRS := $(SRC)/App
OBJ_INC_DIRS := $(INC_DIRS:$(SRC)/%=$(OBJ)/%)
# Objects
OBJECTS := $(SOURCES:$(SRC)%=$(OBJ)%.obj)
# Dependencies
DEPS := $(SOURCES:$(SRC)%.c=$(OBJ)%.d)
-include $(DEPS)
GCC_INCLUDES := $(foreach directory, $(INC_DIRS), -I$(directory))
all: target
target: $(OBJECTS)
touch target
#Objects
$(OBJ)%.c.obj: $(SRC)%.c
#echo Compiling $#
#touch $#
# Dependencies
$(OBJ)%.d: $(SRC)%.c
#echo Checking dependencies for $<
#gcc -MM $< $(GCC_INCLUDES) -MT '$(patsubst %.d,%.c.obj,$#)' -MT '$#' -MF '$#'
#[ ! -s $# ] && rm -f $#
# Creating directory tree before checking dependencies
$(DEPS):|$(OBJ_INC_DIRS)
$(OBJ_INC_DIRS):
#mkdir $#
clean:
echo clean
#rm $(OBJ_INC_DIRS)
When running the first time, I get:
Checking dependencies for ../Src/App/a.c
Checking dependencies for ../Src/App/b.c
Checking dependencies for ../Src/App/c.c
clean
Compiling ../Obj/App/a.c.obj
Compiling ../Obj/App/b.c.obj
Compiling ../Obj/App/c.c.obj
touch target
It's ok, but now, make again (without modifying any file):
make: `../Obj/App/a.c.obj' is up to date.
Now if I modify the file a.c
Checking dependencies for ../Src/App/a.c
Compiling ../Obj/App/a.c.obj
target isn't remade !
It's like my file a.c is the target but it isn't... Can someone explain me what's wrong here?
If I remove the include to the DEPS, I observe the expected behavior...
Thanks
EDIT
By putting the include at the end as mentioned by #Beta works but now I added the target clean and show the result...
I'll have to do some experiments to be sure, but I think the problem is:
-include $(DEPS)
...
all: target
You include $(DEPS) before the first target. So if you modify a.c, Make sees that it must rebuild a.d, then since it includes that file it must start over, and now a.c.obj is an earlier target than all.
Try moving -include $(DEPS) to the end of the makefile.
EDIT:
(Two small points: your clean rule is incorrect, since it tries to rm a directory, and I would do make clean; make all rather than make all, since I am not certain that Make promises to build targets in the given order in all cases.)
Yes, this makefile will rebuild the DEPS even when running clean. The makefile includes those files and has a rule for them, so if they are missing or out of date it must rebuild them and restart, no matter what the target is. The best way to deal with this is by Advanced Auto-Dependency Generation; basically, the commands that build dependency files go in the %.obj rule, so that a.d is a side effect of building a.c.obj. It's a sophisticated technique, not obvious, but it works beautifully. (Let us know if you try this and have trouble setting it up.)

Resources