Makefile loses configuration - makefile

I'm pretty much a makefile novice so I don't even know the terminology I'm looking for. I'm trying to build the latest valgrind release alongside other 3rdparty tools my company uses. I basically have
../3rdparty/
/Makefile <- What gets called to recursively build everything
/valgrind/Makefile <- What I'm pasting below
/valgrind/valgrind-3.16.1/Makefile <- what gets configure'd
So I can go into ../3rdparty/valgrind/valgrind-3.16.0/ and call...
./configure --host=arm-linux-gnueabihf
make
...and have it succeed without issue. However, when I try to build it from the Makefile in ../3rdparty/valgrind I get errors due to configuration generated variables being lost. I can see it clean up everything, I can see configuration succeed, but when the make process starts I get warnings that aren't seen using the process above.
cc1: warning: switch -mcpu=cortex-a8 conflicts with -march=armv7ve switch
Which eventually leads to an error
<command-line>:0:5: error: expected identifier or ‘(’ before numeric constant pub_core_basics.h:78:12: note: in expansion of macro ‘ARM’
I basically copy pasted what is used for other 3rd party libs in our codebase and made changes where applicable....
include ../common.mak
VERSION=valgrind-3.16.1
all: configure build #install
configure: configure_$(TARGET)
configure_$(TARGET):
$(MAKE) distclean
#echo -e "\nConfiguring $(VERSION) for $(TARGET)...\n"
pushd $(VERSION)/ \
&& bash configure --host=${TARGET} \
&& popd
touch $#
#echo -e "\nConfiguration $(VERSION) complete for $(TARGET)...\n"
build: configure
$(MAKE) "-SC" $(VERSION)
install: build
$(MAKE) -SC $(VERSION) $#
# call folder's makefile targets verbatim
clean distclean:
test -f $(VERSION)/Makefile && $(MAKE) -SC $(VERSION) $# || :
rm -f configure_*
uninstall:
$(MAKE) -SC $(VERSION) $#
I'm guessing it's a one line thing, but I'd also be interested in any docs or websites that would be
useful. A lot of makefile tutorials go over the same super basic stuff.

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 a way to run a job from another job in GNU Make? [duplicate]

I have a makefile structured something like this:
all :
compile executable
clean :
rm -f *.o $(EXEC)
I realized that I was consistently running "make clean" followed by "clear" in my terminal before running "make all". I like to have a clean terminal before I try and sift through nasty C++ compilation errors. So I tried to add a 3rd target:
fresh :
rm -f *.o $(EXEC)
clear
make all
This works, however this runs a second instance of make (I believe). Is there a right way to get the same functionality without running a 2nd instance of make?
Actually you are right: it runs another instance of make.
A possible solution would be:
.PHONY : clearscr fresh clean all
all :
compile executable
clean :
rm -f *.o $(EXEC)
fresh : clean clearscr all
clearscr:
clear
By calling make fresh you get first the clean target, then the clearscreen which runs clear and finally all which does the job.
EDIT Aug 4
What happens in the case of parallel builds with make’s -j option?
There's a way of fixing the order. From the make manual, section 4.2:
Occasionally, however, you have a situation where you want to impose a specific ordering on the rules to be invoked without forcing the target to be updated if one of those rules is executed. In that case, you want to define order-only prerequisites. Order-only prerequisites can be specified by placing a pipe symbol (|) in the prerequisites list: any prerequisites to the left of the pipe symbol are normal; any prerequisites to the right are order-only: targets : normal-prerequisites | order-only-prerequisites
The normal prerequisites section may of course be empty. Also, you may still declare multiple lines of prerequisites for the same target: they are appended appropriately. Note that if you declare the same file to be both a normal and an order-only prerequisite, the normal prerequisite takes precedence (since they are a strict superset of the behavior of an order-only prerequisite).
Hence the makefile becomes
.PHONY : clearscr fresh clean all
all :
compile executable
clean :
rm -f *.o $(EXEC)
fresh : | clean clearscr all
clearscr:
clear
EDIT Dec 5
It is not a big deal to run more than one makefile instance since each command inside the task will be a sub-shell anyways. But you can have reusable methods using the call function.
log_success = (echo "\x1B[32m>> $1\x1B[39m")
log_error = (>&2 echo "\x1B[31m>> $1\x1B[39m" && exit 1)
install:
#[ "$(AWS_PROFILE)" ] || $(call log_error, "AWS_PROFILE not set!")
command1 # this line will be a subshell
command2 # this line will be another subshell
#command3 # Use `#` to hide the command line
$(call log_error, "It works, yey!")
uninstall:
#[ "$(AWS_PROFILE)" ] || $(call log_error, "AWS_PROFILE not set!")
....
$(call log_error, "Nuked!")
You already have a sequential solution which could be rewritten as:
fresh:
$(MAKE) clean
clear
$(MAKE) all
This is correct and a very safe approach.
Sequential target execution is possible in GNU make with a proper dependency graph:
fresh: _all
_all: _clear
Recipe for all
_clear: _clean
Recipe for clear
_clean:
Recipe for clean
The above rules define the following dependency graph: fresh <- _all <- _clear <- _clean which guarantees the following recipe execution order: Recipe for clean, Recipe for clear, Recipe for all.
Recipes can be shared with multiple targets using:
target1 target2 target…:
recipe1
Merging your script with the above concepts results in:
all _all :
compile executable
clean _clean :
rm -f *.o $(EXEC)
clear _clear :
clear
fresh: _all
_all: _clear
_clear: _clean
With syntactic sugar using chains.mk from https://github.com/pkoper/mk/ you can write:
all all#fresh :
compile executable
clean clean#fresh :
rm -f *.o $(EXEC)
clear clear#fresh :
clear
#fresh = clean clear all
include chains.mk
fresh: #fresh
Or better:
all: compile
#fresh = clean clear compile
include chains.mk
fresh: #fresh
compile compile#fresh:
compile executable
clear clear#fresh:
clear
clean clean#fresh:
rm -f *.o $(EXEC)
If you removed the make all line from your "fresh" target:
fresh :
rm -f *.o $(EXEC)
clear
You could simply run the command make fresh all, which will execute as make fresh; make all.
Some might consider this as a second instance of make, but it's certainly not a sub-instance of make (a make inside of a make), which is what your attempt seemed to result in.

Why does this make file only call one target?

I am new to make and I am trying to make a super simple build script. This is what I have:
.PHONY: all main
all:
mkdir -p build && cd build
main: main.o install
g++ -o main main.o
main.o: ../src/main.cpp
g++ -c ../src/main.cpp
.PHONY: install
install:
mkdir -p build
mv main.o build
.PHONY: clean
clean:
rm -r build/
I would expect it to call all followed by main. In actuality, here's what happens:
$ make
mkdir -p build && cd build
Only all is called and main is not ran. Why? I have main as a prerequisite after all in the .PHONY line. And help?
.PHONY is not a real target (it is a special make construct), and does not cause its prerequisites to be run. Instead, the first real target mentioned is all, and if you just type make, it will invoke the all as the default target. Because all is not dependent on anything, it is the only target that is run.
You can add a line at the very top:
default: all main
which will cause both all and main to run (don't forget to add default to .PHONY. Notice though that you are not guaranteed that all will run before main. If you want to guarantee this, you would also have to add
main: all
which would force all to be run before main

makefile parallel clean+compile issue

I have a simple makefile with 3 build rules:
clean (that cleans the .o)
debug (compiles my code with debgging stuff)
release (compiles my code with optimization stuff)
sometimes I want to switch between debug mode and release so I would issue this
make clean debug -j8
or
make clean release -j8
that has a drawback because while it's doing the clean stuff, the -j8 allows make to jump some command since the .o are still there Then those .o are removed by the clean rule and the compiler complains because it can't find those .o
I could do something like
make clean; make debug -j8
but since I use an odd makefile in another dir, the command becomes
make -C ../src -f nMakefile clean ; make -C ../src -f nMakefile -j8 release
that is more annoying. I was wondering if there was an hiddedn-guru-mode-rule that allows me to do it in one line
Hope it's clear enough...
I needed to solve this very same problem, and the solution I came up was to parse the MAKECMDGOALS for clean, and dispatch a shell command to do the actual cleaning work; RATHER than clean the build as a target. This way, any MAKECMDGOALS that include "clean" will clean the build as part of that build, first, sequentially, rather than clean running asynchronously as its own target.
-include $(deps)
bin/%.o : %.cpp
#mkdir -p $#D
g++ $(flags) $(includes) -MMD -c $< -o $#
.PHONY : clean
clean:
#echo rm -rf bin/
ifneq ($(filter clean,$(MAKECMDGOALS)),)
$(shell rm -rf bin/)
endif
As I stated above, the normal practice is to have different sub directories for the object files. As you are running in parallel I would think you need to enforce serial execution so that clean is completed before release. One way of doing it could be:
clean_release: clean
+#$(MAKE) -s --no-print-directory release
or if you prefer
clean_release:
+#$(MAKE) -s --no-print-directory clean && $(MAKE) -s --no-print-directory release

makefile execute another target

I have a makefile structured something like this:
all :
compile executable
clean :
rm -f *.o $(EXEC)
I realized that I was consistently running "make clean" followed by "clear" in my terminal before running "make all". I like to have a clean terminal before I try and sift through nasty C++ compilation errors. So I tried to add a 3rd target:
fresh :
rm -f *.o $(EXEC)
clear
make all
This works, however this runs a second instance of make (I believe). Is there a right way to get the same functionality without running a 2nd instance of make?
Actually you are right: it runs another instance of make.
A possible solution would be:
.PHONY : clearscr fresh clean all
all :
compile executable
clean :
rm -f *.o $(EXEC)
fresh : clean clearscr all
clearscr:
clear
By calling make fresh you get first the clean target, then the clearscreen which runs clear and finally all which does the job.
EDIT Aug 4
What happens in the case of parallel builds with make’s -j option?
There's a way of fixing the order. From the make manual, section 4.2:
Occasionally, however, you have a situation where you want to impose a specific ordering on the rules to be invoked without forcing the target to be updated if one of those rules is executed. In that case, you want to define order-only prerequisites. Order-only prerequisites can be specified by placing a pipe symbol (|) in the prerequisites list: any prerequisites to the left of the pipe symbol are normal; any prerequisites to the right are order-only: targets : normal-prerequisites | order-only-prerequisites
The normal prerequisites section may of course be empty. Also, you may still declare multiple lines of prerequisites for the same target: they are appended appropriately. Note that if you declare the same file to be both a normal and an order-only prerequisite, the normal prerequisite takes precedence (since they are a strict superset of the behavior of an order-only prerequisite).
Hence the makefile becomes
.PHONY : clearscr fresh clean all
all :
compile executable
clean :
rm -f *.o $(EXEC)
fresh : | clean clearscr all
clearscr:
clear
EDIT Dec 5
It is not a big deal to run more than one makefile instance since each command inside the task will be a sub-shell anyways. But you can have reusable methods using the call function.
log_success = (echo "\x1B[32m>> $1\x1B[39m")
log_error = (>&2 echo "\x1B[31m>> $1\x1B[39m" && exit 1)
install:
#[ "$(AWS_PROFILE)" ] || $(call log_error, "AWS_PROFILE not set!")
command1 # this line will be a subshell
command2 # this line will be another subshell
#command3 # Use `#` to hide the command line
$(call log_error, "It works, yey!")
uninstall:
#[ "$(AWS_PROFILE)" ] || $(call log_error, "AWS_PROFILE not set!")
....
$(call log_error, "Nuked!")
You already have a sequential solution which could be rewritten as:
fresh:
$(MAKE) clean
clear
$(MAKE) all
This is correct and a very safe approach.
Sequential target execution is possible in GNU make with a proper dependency graph:
fresh: _all
_all: _clear
Recipe for all
_clear: _clean
Recipe for clear
_clean:
Recipe for clean
The above rules define the following dependency graph: fresh <- _all <- _clear <- _clean which guarantees the following recipe execution order: Recipe for clean, Recipe for clear, Recipe for all.
Recipes can be shared with multiple targets using:
target1 target2 target…:
recipe1
Merging your script with the above concepts results in:
all _all :
compile executable
clean _clean :
rm -f *.o $(EXEC)
clear _clear :
clear
fresh: _all
_all: _clear
_clear: _clean
With syntactic sugar using chains.mk from https://github.com/pkoper/mk/ you can write:
all all#fresh :
compile executable
clean clean#fresh :
rm -f *.o $(EXEC)
clear clear#fresh :
clear
#fresh = clean clear all
include chains.mk
fresh: #fresh
Or better:
all: compile
#fresh = clean clear compile
include chains.mk
fresh: #fresh
compile compile#fresh:
compile executable
clear clear#fresh:
clear
clean clean#fresh:
rm -f *.o $(EXEC)
If you removed the make all line from your "fresh" target:
fresh :
rm -f *.o $(EXEC)
clear
You could simply run the command make fresh all, which will execute as make fresh; make all.
Some might consider this as a second instance of make, but it's certainly not a sub-instance of make (a make inside of a make), which is what your attempt seemed to result in.

Resources