In a makefile, is there a way to redirect $(warning) or $(info) statements to file - debugging

Note that I do not want to redirect all make output to file. I only want the output from a $(warning) command to file.
someTarget:
$(warning building $# using $?) >> someLogFile.txt
My example above does not redirect the output from $(warning to someLogFile. Is there a way to do it? Maybe redirect it to a variable and then echo that to a file?
Thanks.

is there a way to redirect $(warning) or $(info) statements to file?
Here's one for GNU Make, but it's not pretty:
Makefile
LOG := log.txt
TARGET_ACQUIRED = \
$(shell echo 'NO_SUCH_TARGET:' | $(MAKE) --eval='$$(info Target acquired: $#...)' -s -f - >> $(LOG))
target_a: target_b
$(TARGET_ACQUIRED)
touch $#
target_b:
$(TARGET_ACQUIRED)
touch $#
clean:
rm -f target_* $(LOG)
With which you'll get:
$ make
touch target_b
touch target_a
$ cat log.txt
Target acquired: target_b...
Target acquired: target_a...
To understand this ruse, see the GNU make commandline options.
If you want this for the purpose of debugging a makefile, you'd probably
fare better with GNU Make's --debug options, documented at the same place.

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.

Is there any way to make multiple targets as series of single make invocations?

I have the following Makefile:
ifneq ($(MAKECMDGOALS),clean)
-include generated.mk
endif
FOO ?= foo
all: a.txt
a.txt:
echo $(GEN_FOO) > $#
generated.mk: Makefile
echo GEN_FOO = $(FOO) > $#
.PHONY: clean
clean:
$(RM) a.txt
$(RM) generated.mk
It works OK when building single targets:
$ make clean
rm -f a.txt
rm -f generated.mk
$ make all
echo GEN_FOO = foo > generated.mk
echo foo > a.txt
However when I try to build multiple targets at once things go not so smooth:
$ make clean all
rm -f a.txt
rm -f generated.mk
echo foo > a.txt
$ make all
echo GEN_FOO = foo > generated.mk
make: Nothing to be done for 'all'.
It gets even worse if variables were provided:
$ make clean
rm -f a.txt
rm -f generated.mk
$ make FOO=bar clean all
echo GEN_FOO = bar > generated.mk
rm -f a.txt
rm -f generated.mk
echo bar > a.txt
$ make all
echo GEN_FOO = foo > generated.mk
make: Nothing to be done for 'all'.
$ make FOO=bar clean all
rm -f a.txt
rm -f generated.mk
echo foo > a.txt
Are there any ways to fix such incorrect behavior?
Make is doing exactly what you told it to do, and you haven't told us what you want it to do that's different than what you told it to do (saying fix such incorrect behavior doesn't really help us when you don't define what's incorrect about the behavior), so we can't help you very much.
You are probably getting confused about the interaction between included makefiles and comparing $(MAKECMDGOALS). Please note:
ifneq ($(MAKECMDGOALS),clean)
this will not match unless you specify exactly one target: clean. In situations where you specify multiple targets, one of which is clean, that will match because clean all is not equal to clean. So, when you run make clean all make will include the generated makefile, and will generate it if it doesn't exist.
Because generated include files are only rebuilt once, when the makefile is first parsed, it's not possible to say something like: "first run rule X (e.g., clean) then rebuild the included makefiles, then reinvoke make".
However, it's pretty much always a bad idea to invoke make with clean all. This is because if you were to ever try to add -j for parallelism, the clean and the build would be running in parallel and corrupt everything.
One semi-common option is to provide a different rule that will do both, something like this:
rebuild:
$(MAKE) clean
$(MAKE) all
then run make rebuild instead.
You can certainly force the behavior with the help of the shell. For instance, in bash you could use
for target in "clean" "all";
do
make $target;
done
and if you were going to re-do the procedure a lot you could either make it an executable script or wrap it in a shell function.

Gnu make: read recipe output into make variable

Is it possible to take a part of the output of a recipe line and use to to set a make variable? For example:
%.o: %.cc
$(eval __time_$* := $(shell date "+%s.%N"))
$(COMPILE.cc) -o $# $<
#echo `date +%s.%N` - $(__time_$*) | bc | xargs printf "%s compile time %6.3f sec\n" $#
Instead of echo'ing the time I want to capture it in a make variable. However, when I use eval and shell instead of the 3rd line above, similar to the 1st line, make appears to read all lines at once, and then schedule the actual recipe shell calls for later. So the result is that the recipe lines are all evaluated at once, so there is little time difference.
The only way I can see to do what you want is to run the compiler inside a $(shell). One problem with doing this is that Make doesn't seem to see the errors, when the command in $(shell) fails. Other than that, here's what I think you wanted:
%.o: %.cc
$(eval __start_$* := $(shell date "+%s.%N"))
#echo $(shell $(COMPILE.cc) -o $# $<)
$(eval __dur_$* := $(shell echo `date +%s.%N` - $(__time_$*) | bc | xargs printf "%s compile time %6.3f sec\n" $#))
Due to the caveat mentioned above, I recommend using the 'time' command to collect these stats, as #wojtow said. If you have different requirements, consider wrapping compilation with a script that collects your timing data and logs it to a file. Then, process the contents of the file as a post-processing step.
The only way I can see to do what you want is to run the compiler
inside a $(shell). One problem with doing this is that Make doesn't
seem to see the errors, when the command in $(shell) fails.
Droid Coder presented a clever approach. A way to solve the problem of make not seeing errors (thereby not aborting on error) is to pass the exit status via the output of the command, while the original compiler output (if any) is redirected to the standard error stream. The rule then is e. g.:
%.o: %.cc
$(eval __start_$* := $(shell date "+%s.%N"))
exit $(shell $(COMPILE.cc) -o $# $< >&2; echo $$?)
$(eval __dur_$* := $(shell echo `date +%s.%N` - $(__time_$*) | bc …))

Makefile: creating a file before building any target

( my question is different from Force Makefile to execute script before building targets )
I've got the following Makefile:
.PHONY: dump_params all
all: tmpA
tmpA: tmpB tmpC dump_params
cat $(filter tmp%,$^) > $#
tmpB: dump_params
touch $#
tmpC: dump_params
touch $#
dump_params:
echo "Makefile was run." >> config.txt
with the target dump_params I want to create/append a file each time a new target is invoked (to keep track of the version of the tools used). However, when I call
make tmpA
all the targets are built from scratch
$ make tmpA
echo "Makefile was run " >> config.txt
touch tmpB
touch tmpC
cat tmpB tmpC > tmpA
$ make tmpA
echo "Makefile was run." >> config.txt
touch tmpB
touch tmpC
cat tmpB tmpC > tmpA
How can I prevent Make to re-build everything because of that target 'dump_params'? Is there a another way to create this kind of log file ?
EDIT: I'm using a parallel make (option -j). Defining a macro to create the file in the statements section is not an option.
Use order-only prerequisites?
.PHONY: dump_params all
all: tmpA
tmpA: tmpB tmpC | dump_params
cat $(filter tmp%,$^) > $#
tmpB: | dump_params
touch $#
tmpC: | dump_params
touch $#
dump_params:
echo "Makefile was run." >> config.txt
Another option is to use immediately expanded shell functions, like:
__dummy := $(shell echo "Makefile was run." >> config.txt)
Since it's immediately expanded the shell script will be invoked once, as the makefile is read in. There's no need to define a dump_params target or include it as a prerequisite. This is more old-school, but has the advantage that it will run for every invocation of make, without having to go through and ensure every target has the proper order-only prerequisite defined.
Non-answer, but snakemake (and there are others, I suspect) supports tracking of rules (the code), parameters, inputs, and executable versions.
https://bitbucket.org/johanneskoester/snakemake

Getting Quiet Make to echo command lines on error

I have a Makefile building many C files with long long command lines and we've cleaned up the output by having rules such as:
.c${MT}.doj:
#echo "Compiling $<";\
$(COMPILER) $(COPTS) -c -o $# $<
Now this is great as the # suppresses the compilation line being emitted.
But when we get an error, all we get is the error message, no command line.
Can anyone think of a "neat" way to emit the command line?
All I can think of doing is echoing it to a file and have a higher level make catch the error and cat the file. Hacky I know.
Tested and it worked (GNU make in Linux):
.c${MT}.doj:
#echo "Compiling $<";\
$(COMPILER) $(COPTS) -c -o $# $< \
|| echo "Error in command: $(COMPILER) $(COPTS) -c -o $# $<" \
&& false
This question is pretty old, but for those of you Googling, I think what I’ll do in this situation is alias make to make -s (silent mode) in my shell, and only put the # prefix before lines where echo or other diagnostic commands are being invoked. When I want the full output from make, I will override my alias by calling it as \make.
Also note that in this situation that you’ll need to do the typical thing and put the #echo on its own line, with the actual rule commands on separate lines and without #’s.
A simple solution would be to use a simple script abc like the following:
#!/bin/bash
$#
code=$?
if (( code )); then
echo error running $#
fi
exit $code
Then you can write abc $(COMPILER) $(COPTS) -c -o $# $< in your Makefile. Do note that this does not work when you have pipes or redirects (as they will be applied to abc instead of the command you want to run).
You can also just put similar code directly in the Makefile if that's preferable.
I recently used a utility called logtext for the likes of tracking what output had occurred during the course of a bat file executing. Check it out, you may find this pretty useful if you want to know what error occurred where.

Resources