Conditional Makefile - makefile

I'm working on a Makefile with changeable sources and compiler.
Basically, what I want it to do is display a message in green if the compilation worked out well and in red otherwise. Additionally I want to avoid displaying the usual error messages (and compilation messages) a Makefile produces. (As I tried to do with all the '#')
Here's what I have for now :
COMP = gcc
NAME = test
RM = rm -f
SRC = main.c
OBJS = $(SRC:.c=.o)
CFLAGS = -Werror
all: $(NAME)
$(NAME):
#$(COMP) -c $(SRC)
#$(COMP) -o $(NAME) $(OBJS)
ifeq ($?, 0)
#echo -e '\033[1;32;40mCompilation : OK\033[0m'
else
#echo -e '\033[1;31;40mCompilation : ERROR\033[0m'
endif
clean:
#$(RM) $(OBJS)
fclean: clean
#$(RM) $(NAME)
re: fclean all
.PHONY: all clean fclean re
All it does is display "Compilation : ERROR" when it compiles well, but I thought that if $? equals 0 that meant it worked out, so I can't find any explanation.
Would you know how to make it do what I'm looking for?
Thanks a lot.
EDIT : Wonderful help from many of you, I'm still looking into the recipe but I've found a way to simply display when it succeeded and when it failed.
$(NAME): $(OBJS)
#$(COMP) $(OBJS) -o $(NAME) && echo -e "\033[32mCompilation: OK\033[0m" || echo -e "\033[31mCompilation: ERROR\033[0m"
I'll keep on digging, thanks.

$? is a shell variable not a make variable, you are testing a make variable with that if statement and the recipe for your target only ever has the one echo line in it.
(See the output from make -qp to see what I mean.)
To do what you want you would need a shell test on $? however realize that make will exit on the first failing command so you will never see the failure echo output this way (unless you capture/hide the failure from make with an if or similar construct on the compilation command).
Something like this will work for capturing/hiding the exit status from the compilation from make but allow you to use it.
#if $(COMP) -c $(SRC); then \
echo -e '\033[1;32;40mCompilation : OK\033[0m'; \
else \
_ret=$$?; \
echo -e '\033[1;31;40mCompilation : ERROR\033[0m'; \
exit $$_ret;\
fi
The bit with _ret is to have make exit with the exit code of the compilation and not a static exit 1 or whatever.

What you want is something like this:
$(NAME):
#$(COMP) -c $(SRC)
#$(COMP) -o $(NAME) $(OBJS); \
if [ $$? == 0 ]; then \
echo -e '\033[1;32;40mCompilation : OK\033[0m'; \
else \
echo -e '\033[1;31;40mCompilation : ERROR\033[0m'; \
fi; \
true
Notice that I used the '\' to concatinate the bash command with the if statement, so they all appear as a single recipe. I also added a true statement to the end such that no matter what the if statement returns, the concatenated recipe will return true, and make will not fail. You could also do:
$(NAME):
#$(COMP) -o $(NAME) $(OBJS) || echo ....
where the echo would only print if the previous command failed.

Related

problems with kernel not being loaded

I'm creating an OS and when I compile the code nothing happened, simply nothing(No errors, warnings or anything) I"m thinking that the make file has some issues.
Makefile:
build_kernel:
echo "Building kernel..."
${ASM} ./src/kernel/kernel_entry.asm -f elf64 -o ${BUILD_DIR}/kernel_entry.o
${C_COMPILER} -c ./src/kernel/kernel.c -o ${BUILD_DIR}/kernel_start.o
${C_COMPILER} -c ./src/kernel/drivers/printutils.c -o ${BUILD_DIR}/kernel_printutils.o
${C_COMPILER} -c ./src/kernel/drivers/port.c -o ${BUILD_DIR}/kernel_ports.o
echo "kernel build complete."
link:
echo "Linking..."
${LINKER} -o ${BUILD_DIR}/kernel.bin ${BUILD_DIR}/kernel_ports.o \
${BUILD_DIR}/kernel_printutils.o ${BUILD_DIR}/kernel_start.o \
${BUILD_DIR}/kernel_entry.o -Ttext 0x1000 --oformat binary
echo "Linking complete"
run:
echo "Running qemu..."
qemu-system-x86_64 -fda ${BUILD_DIR}/os.bin
merge_binary:
echo "Merging binary..."
cat ${BUILD_DIR}/boot.bin ${BUILD_DIR}/kernel.bin > ${BUILD_DIR}/os.bin
echo "Binary merged."
post_build:
rm -f ${BUILD_DIR}/boot.bin
rm -f ${BUILD_DIR}/kernel.bin
rm -f ${BUILD_DIR}/kernel.o
rm -f ${BUILD_DIR}/kernel_entry.o
rm -f ${BUILD_DIR}/kernel_ports.o
rm -f ${BUILD_DIR}/kernel_printutils.o
rm -f ${BUILD_DIR}/kernel_start.o
I'm wondering what is happening with the makefile and is this the correct way to compile all the code into object files and link them.
Any help will be appreciated.
I'm just commenting on your make style -- this does not answer why you are outputting nothing (and it is unclear what you mean by that -- if you run make, it should output at least echo "Building kernel..."...). As far as the makefile style goes, this seems to be built using a scripting mentality rather than a make mentality. Consider your first part:
build_kernel:
echo "Building kernel..."
${ASM} ./src/kernel/kernel_entry.asm -f elf64 -o ${BUILD_DIR}/kernel_entry.o
${C_COMPILER} -c ./src/kernel/kernel.c -o ${BUILD_DIR}/kernel_start.o
${C_COMPILER} -c ./src/kernel/drivers/printutils.c -o ${BUILD_DIR}/kernel_printutils.o
${C_COMPILER} -c ./src/kernel/drivers/port.c -o ${BUILD_DIR}/kernel_ports.o
echo "kernel build complete."
This has several issues. First is the name -- this looks to build a bunch of artifacts rather than building the kernel. Also, the recipe never produces a file named build_kernel, thus this should have been a phony target. Next, this is actually a script, which builds four separate things. These could be separated out into four separate rules, each which builds one thing, and then the main target would be dependent on this. Thus, it might look like:
.PHONY: build_kernel_objs
build_kernel_objs: ${C_OBJS} ${ASM_OBJS}
#echo done building $#
${BUILD_DIR}/kernel_start.o : ./src/kernel/kernel.c
${C_COMPILER} -c $< -o $#
${BUILD_DIR}/kernel_printutils.o : ./src/kernel/kernel_printutils.c
${C_COMPILER} -c $< -o $#
${BUILD_DIR}/kernel_ports.o : ./src/kernel/kernel_ports.c
${C_COMPILER} -c $< -o $#
Note that the above is repetitive, and if you have hundreds of files, will bolat very quickly. This can also be done using static pattern rules:
C_FILES := \
./src/kernel/kernel_start.c
./src/kernel/kernel_printutils.c
./src/kernel/kernel_ports.c
ASM_FILES := \
./src/kernel/kernel_entry.asm
C_OBJS := ${C_FILES :./src/kernel/%.c=${BUILD_DIR}/%.o}
ASM_OBJS := ${ASM_FILES :./src/kernel/%.asm=${BUILD_DIR}/%.o}
${C_OBJS} : ${BUILD_DIR}/%.o : ./src/kernel/%.c
${C_COMPILER} -c $< $#
.PHONY: build_kernel_objs
build_kernel_objs: ${C_OBJS} ${ASM_OBJS}
#echo "done building $#"
These have several advantages over what you've done -- first, make will only ever build the objects that are out of date, so it doesn't do needless work. It can also build the files in parallel if a -j option is specified on the make command line. Next, it's more maintainable -- if you have to add extra files, you can do it in one place, and everything works out. Also, the .PHONY prevents the make from failing if you happen to have a file named build_kernel_objs in your make directory. Lastly, the # in front of the echo lines prevents the actual echo command from being echoed, which will look nicer.
On caveat to this is that it does not handle modification of header files (as written, if a header file is updated, c files that depend on it would not be rebuilt. See here for some notes about getting around that.
The next section, link, the makefile recipe should reflect the target.
.PHONY: link
link : ${BUILD_DIR}/kernel.bin
${BUILD_DIR}/kernel.bin: ${C_OBJS} ${ASM_OBJS}
${LINKER} -o $# $^ -Ttext 0x1000 --oformat binary
This creates a phony target link, so you can type make link. It will only do the link if any of the C objects or ASM objects have been updated. The same concept applies to your merge_binary target
For run, this seems to be somewhat contentious, but a common rule of thumb is that a make should be used to make an executable, not to run it. A separate shell script is better suited if you want to invoke your built target with specific parameters.
Lastly, your post_build rule should likely be renamed to CLEAN, and declared as a phony.

How to interpret the $# symbol in a makefile loop

In a makefile I have this piece of code
all clean dep depend print:
for dir in $(DIRS); do \
if $(MAKE) $(MAKE_FLAGS) -C $$dir $#; then \
true; \
else \
exit 1; \
fi; \
done
What is the meaning of $# in the line
if $(MAKE) $(MAKE_FLAGS) -C $$dir $#; then \
I know this is an Automatic Variable that matches the file name of the target of the rule. Here the target appears to be a command like cancel:
cancell:
rm -rf *.o
It is an Automatic variable that expands to the name of the target which caused the recipe to run. In your case, if you type make all, it expands to all. If you type make all clean, it will run the recipes twice -- the first time, $# will expand to all, and the second time it will expand to clean.
See documentation here

Makefile is skipping certain dependencies

So I am writing a makefile that will take some files (*.in) as input to my C++ program and compare their output (results.out) to given correct output (*.out).
Specifically I have files t01.in, t02.in, t03.in, t04.in, and t05.in.
I have verified that $TESTIN = t01.in t02.in t03.in t04.in t05.in.
The problem is that it seems to run the %.in: %.out block only for three of these files, 1,3, and 4. Why is it doing this?
OUTPUT = chart
COMPILER = g++
SOURCES = chart.cpp
HEADERS =
OBJS = $(SOURCES:.cpp=.o)
TESTIN = tests/*.in
all: $(OUTPUT)
$(OUTPUT): $(OBJS)
$(COMPILER) *.o -o $(OUTPUT)
%.o: %.cpp
clear
$(COMPILER) -c $< -o $#
test: $(TESTIN)
%.in: %.out
./$(OUTPUT) < $# > tests/results.out
printf "\n"
ifeq ($(diff $< tests/results.out), )
printf "\tTest of "$#" succeeded for stdout.\n"
else
printf "\tTest of "$#" FAILED for stdout!\n"
endif
Additionally, if there is a better way of accomplishing what I am trying to do, or any other improvements I could make to this makefile (as I am rather new at this), suggestions would be greatly appreciated.
EDIT: If I add a second dependency to the block (%.in: %.out %.err), it runs the block for all five files. Still no idea why it works this way but not the way before.
First, I don't see how TESTIN can be correct. This line:
TESTIN = tests/*.in
is not a valid wildcard statement in Make; it should give the variable TESTIN the value tests/*.in. But let's suppose it has the value t01.in t02.in t03.in t04.in t05.in or tests/t01.in tests/t02.in tests/t03.in tests/t04.in tests/t05.in, or wherever these files actually are.
Second, as #OliCharlesworth points out, this rule:
%.in: %.out
...
is a rule for building *.in files, which is not what you intend. As for why it runs some tests and not others, here is my theory:
The timestamp of t01.out is later than that of t01.in, so Make decides that it must "rebuild" t01.in; likewise t03.in and t04.in. But the timestamp of t02.out is earlier than that of t02.in, so Make does not attempt to "rebuild" t02.in; likewise t05.in. The timestamps of t02.err and t05.err are later than those of t02.in and t05.in, respectively, so when you add the %.err prerequisite, Make runs all tests. You can test this theory by checking the timestamps and experimenting with touch.
Anyway, let's rewrite it. We need a new target for a new rule:
TESTS := $(patsubst %.in,test_%,$(TESTIN)) # test_t01 test_t02 ...
.PHONY: $(TESTS) # because there will be no files called test_t01, test_t02,...
$(TESTS): test_%: %.in %.out
./$(OUTPUT) < $< > tests/results.out
Now for the conditional. Your attempted conditional is in Make syntax; Make will evaluate it before executing any rule, so tests/result.out will not yet exist, and variables like $< will not yet be defined. We must put the conditional inside the command, in shell syntax:
$(TESTS): test_%: %.in %.out
./$(OUTPUT) < $< > tests/results.out
if diff $*.out tests/results.out >/dev/null; then \
echo Test of $* succeeded for stdout.; \
else echo Test of $* FAILED for stdout!; \
fi
(Note that only the first line of the conditional must begin with a TAB.)

If condition inside a target in a Makefile

I want to do something like this, where I want to run a svn commit if a file has changed. The file has a timestamp which always changes. So if anything more than timestamp changes , then I want to commit the file.
The makefile will have something similar to this. But the If condition is not working properly. It is getting executed even when if is not satisfied. Can someone help me out what is the problem in If here.
UPDATE_STATE_FILE :
$(eval NO_LINES_CHANGES_IN_STATE = $(shell svn di STATE/build.state --diff-cmd=diff -x --normal | grep "^[<>]" | wc -l))
#echo $(NO_LINES_CHANGES_IN_STATE)
ifneq ($(strip $(NO_LINES_CHANGES_IN_STATE)), 2)
ifneq ($(strip $(NO_LINES_CHANGES_IN_STATE)), 0)
#echo $(NO_LINES_CHANGES_IN_STATE)
$(SVN) commit;
$(SVN) update;
endif
endif
You cannot mix make conditionals inside command rules. Make conditionals are like preprocessor statements in C or C++; they are handled as the file is read in, before any processing (like running rules) is performed.
If you want conditionals inside the rules you have to write the rule using shell conditionals, not make conditionals:
UPDATE_STATE_FILE :
#NO_LINES_CHANGES_IN_STATE=`svn di STATE/build.state --diff-cmd=diff -x --normal | grep "^[<>]" | wc -l`; \
echo $$NO_LINES_CHANGES_IN_STATE; \
if [ $$NO_LINES_CHANGES_IN_STATE -ne 2 ] && [ $$NO_LINES_CHANGES_IN_STATE -ne 0 ]; then \
echo $$NO_LINES_CHANGES_IN_STATE; \
$(SVN) commit; \
$(SVN) update; \
fi
After reading the answer given by #MadScientist, I came up with a different approach.
Not knowing if shell conditionals will work in any environment (windows vs. linux), I wrapped the rules inside the conditional instead of having a conditional inside the rule. i.e.
ifdef MY_FLAG
%.o: %.c
$(CC) -o $# -c $^ $(CFLAGS)
else
%.o: %.c
#$(CC) -o $# -c $^ $(CFLAGS)
endif
Good luck to anyone visiting here.
EDIT
As pointed out by James Moore in the comments, this different approach requires care and notice as to when and how variables are defined in relation to the placement of the if statement in the control flow.

Explaining makefile % as well as $< and $#

xpi_built := $(build_dir)/$(install_rdf) \
$(build_dir)/$(chrome_manifest) \
$(chrome_jar_file) \
$(default_prefs)
xpi_built_no_dir := $(subst $(build_dir)/,,$(xpi_built))
$(xpi_file): $(build_dir) $(xpi_built)
#echo "Creating XPI file."
cd $(build_dir); $(ZIP) ../$(xpi_file) $(xpi_built_no_dir)
#echo "Creating XPI file. Done!"
$(build_dir)/%: %
cp -f $< $#
$(build_dir):
#if [ ! -x $(build_dir) ]; \
then \
mkdir $(build_dir); \
fi
can anyone explain me this makefile part? particularly interested in
$(build_dir)/%: % as well as $< and $# directives
two labels $(build_dir) exists, I guess both are executed, but in which order?
$(build_dir)/%: %
cp -f $< $#
This is a static pattern rule which uses automatic variables in its command; $< expands to the leftmost prerequisite, $# expands to the target. If you try to make $(build_dir)/foo (whatever $(build_dir) is), Make will treat this rule as
$(build_dir)/foo: foo
cp -f foo $(build_dir)/foo
The next rule,
$(build_dir):
#if [ ! -x $(build_dir) ]; \
then \
mkdir $(build_dir); \
fi
is for $(build_dir) itself, and is unnecessarily complicated. It says "if $(build_dir) doesn't exist, then mkdir it", and it could be written this way:
$(build_dir):
mkdir $#
It looks as if your primary target is $(xpi_file):
$(xpi_file): $(build_dir) $(xpi_built)
So Make will first make $(build_dir) (if necessary), then the members of the list %(xpi_built), which includes a couple of things of the form $(build_dir)/%. Once those are done, it will execute the commands of this rule: it will cd into $(build_dir), zip some things up, and echo a couple of messages.
See Pattern Rules and Automatic Variables in the GNU make documentation. The first rule matches files inside $(build_dir), not $(build_dir) itself. $< expands to the list of prerequisites of the current rule, $# is the target for the current rule.

Resources