How to compile a test snippet nicely with gnu make? - makefile

I don't have a configure stage in my baseline (for various reasons), but I need to check whether my C compiler can support the -mavx2 flag or not.
If I have an empty file, call it test.cc then I can do something like:
$(CC) -mavx2 -c test.cc -o test.o
And check the return status code. Obviously I don't want to leave those test.* files lying around though, but can't think of a good way to generate/test/delete them outside of a recipe. Does anyone know of a good pattern to do this?

Or, you could just take input from stdin, and output to /dev/null:
SUPPORTS_MAVX2:=$(shell echo 'void main(){}' | \
gcc -x c -maxv3 -o /dev/null - 2>/dev/null; \
echo $$?)
Then there are no artifact files to be deleted. The -x c is necessary to tell gcc what language it is (as it can't determine that from the filename in this case), and you need the trailing - as well.

Turns out you can use "eval" to remove the temporary file and return the status code, so this works:
AVX2 = $(shell touch test.cc; $(CC) -mavx2 -c test.cc -o /dev/null >& /dev/null; eval "rm -f test.cc; echo $$?")
Edit and without the temporary file (assuming a sane system):
AVX2 = $(shell $(CC) -mavx2 -c /usr/include/stdlib.h -o /dev/null >& /dev/null; echo $?)

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.

Using Makefile to run multiple programs

Whenever I try to run this, the only output I get is "make: foo.o is up to date." It seems as if the rest of the program does not run and I do not know why. My instructions are as follows: "Compile a C program. Run a C program. Run a Python program. Compile and run a java program. Check for a README, display it. Compare 2 files. Clean up intermediary files."
cc = gcc
EXE = foo
JAVAC = javac
JRE = java
PAGER = less
TEST_OUT = test.out
EXP_OUT = expected.out
foo.o: foo.c foo.h
$(cc) -c foo.c
main.o: main.c foo.h
$(cc) -c main.c
$(EXE): foo.o main.o
$(cc) -o$(EXE) main.o foo.o
run-c: $(EXE)
./$(EXE)
run-py:
./foo.py
read: README
$(PAGER)
foo.class: foo.java
$(JAVAC) foo.java
run-java: foo.cass
$(JRE) foo
save-java:
./(run-java) >> $(TEST_OUT)
test-java: $(TEST_OUT) $(EXP_OUT)
#if diff $(TEST_OUT) $(EXP_OUT) &> /dev/null ; then \
echo "Passed!" ;\
else \
echo "Not the same!" ;\
fi
clean:
-rm test.out
Whenever I try to run this, the only output I get is "make: foo.o is up to date."
By default, make runs the topmost rule when no target is specified. You have to run for example make run-c to invoke a corresponding recipe, or you can just put an all rule before any others which depends on and does all the things.
read: README
$(PAGER)
I suspect you might have missed putting $# after $(PAGER) as the argument.
save-java:
./(run-java) >> $(TEST_OUT)
You can't just "include" other recipes this way. Instead, repeat what's in run-java and append the redirection.
If you want to specify "pseudo" targets, I recommend you to specify them as .PHONY, such as:
.PHONY: all run-c run-py run-java save-java test-java clean
To mark some targets as intermediate files, use the .INTERMEDIATE directive. GNU Make manual (texinfo) is available both online and via the info command.

How is this complex generic Makefile rule constructed

Several modules each are tested independently with their own test_$(MODULE).c.
A shared library has been generated $(LIBRARY) containing modules without coverage. $(basename $<).o should override the one in $(LIBRARY). For some reason, I get results as if they are not overridden. Can someone review this and make suggestions on fixes? Currently I have non-generic gcov rules for each of the five objects. These gcovs work correctly. Below I show the generic rule and one specific use of the rule.
SHARED_OPTS=-O0 -Wall -Wextra -fPIC
GOPTS=$(SHARED_OPTS) -g -coverage -pg
%.gcov : %
#echo "\t$# generic (needs work)"
#-gcc $(GOPTS) -c -o test_$(basename $<).o test_$<
#-gcc $(GOPTS) -c -o $(basename $<).o $<
#-gcc $(GOPTS) -o gcov_test_$(basename $<) \
test_$(basename $<).o \
$(basename $<).o \
-L . -l $(LIBRARY)
#-./gcov_test_$(basename $<)
#-gcov $< >$#.out 2>&1
#echo "no Mac gprof: -gprof gcov_test_$(basename $<) gmon.out > $<.prof"
#$(call timestamp,$#)
Unicode.c.gcov: Unicode.c
If anyone is interested in collaborating on high efficiency high quality Unicode lexing/parsing support by developing a shared library, I would love to have reviewers or contributors.
The Makefile fragment shown above is in a github repository:
https://github.com/jlettvin/Unicode Specifically down the c subdirectory.
While you're trying to find problems in your makefile you should avoid using #, as it hides the command line and so you can't see issues. Also you should avoid - here: if any of those commands fail you certainly don't want to continue to run the rest of the recipe, I wouldn't expect.
I don't know if it's a cut/paste problem but I have to assume that these lines, at least, are wrong:
#-gcc $(GOPTS) -c -o test_$(basename $<).o test_$<
#-gcc $(GOPTS) -c -o $(basename $<).o $<
As far as I can tell from your makefile, the last words on these lines should be test_$<.c and $<.c respectively.

Redirecting input separated with spaces

Hi I've a question about feeding inputs to this simple bash script I wrote. All it does is add a set of flags to my compile operation to save me having to write them in myself every time. I can run it by using echo myprogram.c -o myprogram -llibrary | ./Compile.
But I can't find a way to run it in the way I expected to be able to, ./Compile < myprogram.c -o myprogram -llibrary
I've tried a few combinations of quotes and brackets to no avail, can anyone tell me how to feed the same input as produced by echo using the redirect input command.
#!/bin/bash
# File name Compile
#Shortcut to compile with all the required flags, name defaulting to
#first input ending in .c
echo "Enter inputs: "
read inputs
gcc -Wall -W -pedantic -std=c89 -g -O $inputs
exit 0
Just change your shell to:
#!/bin/bash
gcc -Wall -W -pedantic -std=c89 -g -O "$#"
Then you can only write(no redirection needed):
./Compile myprogram.c -o myprogram -llibrary
BTW, don't explicitly write exit 0 at end of this shell. It is redundant when gcc succeeds, and is wrong when gcc fails(exit code 1 will be overwritten).
You can use process substitution:
./Compile < <( echo myprogram.c -o myprogram -llibrary )
the above line produces the same result as your original command:
echo myprogram.c -o myprogram -llibrary | ./Compile

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