Getting Quiet Make to echo command lines on error - makefile

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.

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.

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, declare variable in executable

This is a simple question for a starter like me, but what can I do to do like the following
all: run
run:
DIR=bin/
$(CC) $(LIBRARY) $(INCLUDE) run.o -o $(DIR)$#
Thanks.
Why not go like this?
DIR=bin/
all: $(DIR)/run
$(DIR)/run:
$(CC) $(LIBRARY) $(INCLUDE) run.o -o $#
As written, you have an assignment to the shell variable DIR on one command line. On the next line, you have the expansion of a make variable DIR. This doesn't work because the two lines are executed by different shells, and in any case, make expands $(DIR) before running the shell and DIR is not a make variable.
You could make it work like this:
all: run
run:
DIR=bin/; \
$(CC) $(LIBRARY) $(INCLUDE) run.o -o $${DIR}$#
The backslash means the two lines are treated as one (so much so that the semicolon is needed). The $${DIR} notation is expanded by make to ${DIR} (more precisely, $$ expands to $ and make ignores the {DIR}), and then the shell expands ${DIR} from the value set previously. You could, of course, omit the braces.
However, the answer by BeSerK is probably what you're looking for.

MinGW/MSYS shell colors

I'd like for my makefile output to be color-coded.
But I can't get the ANSI color codes to work on this terminal. It should be possible though, ls --color gives me colorful output, and my shell prompt is also colored:
$ echo $PS1
\[\033]0;$MSYSTEM:\w\007 \033[32m\]\u#\h \[\033[33m\w\033[0m\] $
I suspect maybe the first section puts the terminal in a special mode so that it will accept color codes. Can somebody explain?
I solved it.
The command to use is echo -e.
So, in the makefile:
foo.o: foo.c
#echo -e "\033[32mCompiling foo.c\033[0m"
$(CC) $(CFLAGS) -c -o $# $<
I would imagine this works just fine in bash as well.

How do I shut up make when it doesn't need to do anything?

How I stop make from saying make: Nothing to be done for 'all'. or make: 'file' is up to date? I'd like my build to be silent when it's not doing anything - there are other places where echo is called to track build progress, so this message is just cluttering things up. I am currently silencing it like this:
all: dependency1 dependency2
#:
Something tells me there must be a better way. Any ideas?
Edit:
I would like to keep command echo working when it does need to build something, however. A good example of what I'm hoping for is along the lines of --no-print-directory, but I can't find any other flags to shut up selected messages.
Maybe make -s?
So after a couple days of reading around the web, it looks like there isn't any better way than what I'm doing. Some people recommended something along the lines of:
all: dependency1 dependency2 | silent
silent:
#:
That is, just depending on the silent target would be enough to quiet things down. Since I didn't come up with any other workable solutions, I'm going with what I have.
You might try...
$ make -q || make
The advantage of doing it this way is that nothing is printed when there is nothing to do but make produces the normal output when it does need to proceed...
To quote (from memory) from the old make(1) man page, BUGS section: There are some things you can't get make to shut up about. Meanwhile, the -s or --silent option may help.
You can set the -s commandline argument to make in the makefile itself, by setting MAKEFLAGS. Nothing is printed unless you explicitely print it, so I use the following makefile to echo invoked commands.
MAKEFLAGS += -s
PROJECT = progname
CC = g++
SDIR = src
ODIR = obj
BDIR = bin
IDIR = include
OBJS = $(patsubst $(SDIR)/%.cc,$(ODIR)/%.o,$(wildcard $(SDIR)/*.cc))
.PHONY: all debug clean
all: $(BDIR)/$(PROJECT)
debug: CFLAGS += -g -Wall -Wextra
debug: all
$(BDIR)/$(PROJECT): $(OBJS)
#mkdir -p $(BDIR)
#echo LINKING $<
#$(CC) -o $# $(OBJS) -I$(IDIR)
$(ODIR)/%.o: $(SDIR)/%.cc
#mkdir -p $(ODIR)
#echo "COMPILING $<"
#$(CC) -o $# -c $< $(CFLAGS)
clean:
#echo "CLEAN"
#rm -rf $(BDIR) $(ODIR)
Removing the MAKEFLAGS variable will print all invoked commands. The Makefile compiles any c++ project where source files (with .cc extension) are put into the src directory and header files are put into the include directory.
make 2>&1 | egrep -v 'Nothing to be done|up to date'

Resources