I am trying to run a Makefile from under WSL that contains the following lines:
debug: create_soft_links
#mkdir -p Debug64
#echo -e 'all: bld' > Debug64/Makefile
#echo >> Debug64/Makefile
#echo -e '%.o: ../../%.c' >> Debug64/Makefile
#echo -e '\tgcc -g $$(CFLAGS) $$(INCLUDE) $$< -o $$#' >> Debug64/Makefile
Problem is that the resulting Debug64/Makefile file looks like this:
-e all: bld
-e %.o: ../../%.c
-e gcc -O3 $(CFLAGS) $(INCLUDE) $< -o $#
A colleague just showed me on an actual Linux machine that the make command works correctly there, and the preceding -e flag is not printed in the generated Debug64/Makefile. What am I doing wrong?
Use instead of echo the printf(1) command. So your last line would be
#printf "\tgcc -g %s %s $$< -o $$#\n" $$(CFLAGS) $$(INCLUDE)
BTW, if you generate your build automation script, consider switching to ninja. You might use Guile or Python or GNU awk as such a generator.
Related
I have inherited a project file that has a Makefile in it that is doing something I have never seen before--It is injecting a rm command. I cannot find any reason for the rm command, so I am missing something very obvious or very esoteric.
Thanks
The results of running make are:
bison --defines --xml --graph=calc.gv -o calc.c calc.y
Bison flags =
cc -c -o calc.o calc.c
Making BASE = calc
cc -o calc calc.o
Done making BASE
rm calc.c <======== WHERE IS THIS COMING FROM?
The Makefile is:
BASE = calc
BISON = bison
XSLTPROC = xsltproc
all: $(BASE)
%.c %.h %.xml %.gv: %.y
$(BISON) $(BISONFLAGS) --defines --xml --graph=$*.gv -o $*.c $<
#echo "Bison flags = " $(BISONFLAGS)
$(BASE): $(BASE).o
#echo "Making BASE = " $(BASE)
$(CC) $(CFLAGS) -o $# $^
#echo "Done making BASE"
run: $(BASE)
#echo "Type arithmetic expressions. Quit with ctrl-d."
./$<
html: $(BASE).html
%.html: %.xml
$(XSLTPROC) $(XSLTPROCFLAGS) -o $# $$($(BISON) --print-datadir)/xslt/xml2xhtml.xsl $<
CLEANFILES = $(BASE) *.o $(BASE).[ch] $(BASE).output $(BASE).xml $(BASE).html $(BASE).gv
clean:
#echo "Running clean" $(CLEANFILES)
rm -f $(CLEANFILES)
See https://www.gnu.org/software/make/manual/make.html#Chained-Rules:
The second difference is that if make does create b in order to update something else, it deletes b later on after it is no longer needed. Therefore, an intermediate file which did not exist before make also does not exist after make. make reports the deletion to you by printing a rm -f command showing which file it is deleting.
I have a makefile like this:
default:
%:
#$(MAKE) -i -C subdir1 $*
#$(MAKE) -i -C subdir2 $*
#$(MAKE) -i -C subdir3 $*
#$(MAKE) -i -C subdir4 $*
#$(MAKE) -i -C subdir5 $*
The basic concept is that I have 5 (or more) sub projects which I will call make on sequentially. I use "-i" flag so that the make can continue to the end and the "-C dir" flag to call make in a sub-directory.
So, lets say that sub project 2 and 5 are failing, then at the end I want to be able to print something like:
3 projects built ok, 2 projects have errors.
So I think I want a counter of some sort, but I have no idea how I can set/increment it on an error. Any ideas?
As every call to $(MAKE) spawns its own subprocess, I can't think of a way to record these numbers easily with an ordinary make variable. You can, however, log the return value of each invocation to a (possibly hidden) file and then grep for your build stats like this:
errLog = .errLog
default:
%:
#$(MAKE) -i -C subdir1 $*; echo $$? > $(errLog)
#$(MAKE) -i -C subdir2 $*; echo $$? >> $(errLog)
#$(MAKE) -i -C subdir3 $*; echo $$? >> $(errLog)
#$(MAKE) -i -C subdir4 $*; echo $$? >> $(errLog)
#$(MAKE) -i -C subdir5 $*; echo $$? >> $(errLog)
#echo "`grep -c '^0' $(errLog)` built ok, `grep -c '^[^0]' $(errLog)` have errors."
Note that the first output redirection must be a single > to overwrite previous return codes in the file, while all others should be two > to not overwrite the file content.
I am trying to analyze the following makefile and reproduce its "behavior" step by step.
Although I type "make all" it seems this makefile skips the "all:" line and jumps straight to "build/*.o" (hence the echo's).
The file and its corresponding output:
TOOLCHAIN ?= arm-none-eabi-
SOURCES = Demo/main.c \
Demo/startup.c \
Demo/Drivers/rpi_gpio.c \
Demo/Drivers/rpi_irq.c \
Source/tasks.c \
Source/list.c \
Source/portable/GCC/RaspberryPi/port.c \
Source/portable/GCC/RaspberryPi/portisr.c \
Source/portable/MemMang/heap_4.c
OBJECTS = $(patsubst %.c,build/%.o,$(SOURCES))
INCDIRS = Source/include Source/portable/GCC/RaspberryPi \
Demo/Drivers Demo/
CFLAGS = -Wall $(addprefix -I ,$(INCDIRS))
CFLAGS += -D RPI2
CFLAGS += -march=armv7-a -mtune=cortex-a7 -mfloat-abi=hard -mfpu=neon-vfpv4
ASFLAGS += -march=armv7-a -mcpu=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard
LDFLAGS =
.PHONY: all clean
all: $(MOD_NAME)
echo "in all"
$(MOD_NAME): $(OBJECTS)
echo "in mod name"
ld -shared $(LDFLAGS) $< -o $#
build/%.o: %.c
echo -e "\nin build/*.o:*.c\n"
mkdir -p $(dir $#)
$(TOOLCHAIN)gcc -c $(CFLAGS) $< -o $#
build/%.o: %.s
echo -e "in build/*.o:*.s\n"
mkdir -p $(dir $#)
$(TOOLCHAIN)as $(ASFLAGS) $< -o $#
all: kernel7.list kernel7.img kernel7.syms kernel7.hex
echo -e"in kernel all\n"
$(TOOLCHAIN)size kernel7.elf
kernel7.img: kernel7.elf
$(TOOLCHAIN)objcopy kernel7.elf -O binary $#
echo -e "in kernel7.img\n"
kernel7.list: kernel7.elf
echo -e "kernel7.list\n"
$(TOOLCHAIN)objdump -D -S kernel7.elf > $#
kernel7.syms: kernel7.elf
echo -e "kernel7.syms\n"
$(TOOLCHAIN)objdump -t kernel7.elf > $#
kernel7.hex : kernel7.elf
echo -e "kernel7.hex\n"
$(TOOLCHAIN)objcopy kernel7.elf -O ihex $#
kernel7.elf: $(OBJECTS)
echo -e "kernel7.elf\n"
$(TOOLCHAIN)ld $^ -static -Map kernel7.map -o $# -T Demo/raspberrypi.ld
clean:
rm -f $(OBJECTS)
rm -f kernel7.list kernel7.img kernel7.syms
rm -f kernel7.elf kernel7.hex kernel7.map
rm -rf build
echo -e "cleaning \n"
I tried to replicate this behaviour myself with a tiny piece of code. But it doesn't seem to work:
SOURCES = Demo/Drivers/rpi_irq.c \
Demo/Drivers/rpi_gpio.c
OBJECTS = $(patsubst %.c,build/%.o,$(SOURCES))
.PHONY: all clean
all: $(MOD_NAME)
echo "making all"$(SOURCES)
$(MOD_NAME): $(OBJECTS)
echo "MOD_NAME"
build/%.o:%.c
mkdir -p $(dir $#)
arm-none-eabi-gcc -march=armv7-a -mcpu=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=har $< -o $#
As you can see thanks to the echo's my code just doesn't even build my source code. I'd expect it to go from all->MOD_NAME->build. (This is all the output I get)
So my questions are:
How does the makefile I am analyzing manage to go straight to build/*.o?
Why does my implementation, which I think should do the same doesn't even compile my source code?
The Makefile that you copied contains 2 rules for "all".
The first depends on $(MOD_NAME) which might be empty.
The second rule depends on multiple files "kernel7.*" which themselves depend on "kernel7.elf".
Finally "kernel7.elf" depends on $(OBJECTS).
This last rule is responsible that all your source files will be compiled.
The first rule with $(MOD_NAME) does not need to cause any compilation at all.
In your own Makefile you only have a rule for "all" depending on $(MOD_NAME).
If $(MOD_NAME) is empty in your Makefile as well, you do not have any dependency for "all" at all.
If "all" does not depend on anything, no source files will be compiled.
To solve your problem you need to provide some content for $(MOD_NAME).
all is trying to build $(MOD_NAME), which has dependencies of $(OBJECTS), which it is trying to build.
There are two all's here, which is a problem.
i have this makefile:
SHELL=/bin/bash
COMPILER_VERSION = "Intel 64 Compiler 16.0.0.109 Build 20150815"
SOURCES = \
ron1.f \
ron2.f \
ron3.f \
ron4.f
OBJECTS = $(SOURCES:.f=.o)
TARGET = mylib.a
FC = gfortran
FFLAGS = -O3
linux: $(TARGET)
#echo
#echo " " \
ar r $(TARGET) $(OBJECTS)
#echo
#echo " " \
ranlib $(TARGET)
#echo
$(TARGET): $(OBJECTS)
$(OBJECTS):$(SOURCES)
cleanall:
#echo
rm -f $(OBJECTS) $(TARGET)
#echo
clean:
#echo
rm -f $(OBJECTS)
#echo
.f.o:
#echo " " \
$(FC) -c $(FFLAGS) $*.f
It results the below output:
prompt> make cleanall
rm -f ron1.o ron2.o ron3.o ron4.o mylib.a
prompt> make
gfortran -c -O3 ron1.f
gfortran -c -O3 ron2.f
gfortran -c -O3 ron3.f
gfortran -c -O3 ron4.f
ar r mylib.a ron1.o ron2.o ron3.o ron4.o
ranlib mylib.a
prompt>
what i am looking to do is create a space between "prompt> make" and the first happening of gfortran.
and ideally i would like the output on the screen to first print out the contents of my COMPILER_VERSION variable before the first gfortran happens, such that the output would look like
prompt> make
makefile written for: Intel 64 Compiler 16.0.0.109 Build 20150815
gfortran -c -O3 ron1.f
gfortran -c -O3 ron2.f
gfortran -c -O3 ron3.f
and so on...
any help much appreciated.
You should add to the 'linux' target some prerequisite like 'ECHO' here:
linux: ECHO $(TARGET)
ar r $(TARGET) $(OBJECTS)
#echo
#echo " " \
ranlib $(TARGET)
#echo
ECHO:
#echo "\n\n\n\n Makefile written for the compiler version ${COMPILER_VERSION}"
thanks a bunch, that worked.
your
linux: ECHO $(TARGET)
worked great, only thing i did different than what you typed was this syntax for ECHO: which i placed at the bottom of the makefile. It allowed me to space out the screen output exactly how i wanted it.
ECHO:
#echo
#echo "Makefile written for compiler version ${COMPILER_VERSION}"
#echo
You can use echo with flag -e
#echo -e "\n"
I have written a Makefile which works fine; I am just posting the part of it under investigation:
BUILD_PRINT = #echo -e "\e[1;34mBuilding $<\e[0m"
COMPILE_cpp = $(CXX) $(CFLAGS) -o $# -c $< $(MAKEDEP) $(INCLUDES)
%.o : %.cpp
$(BUILD_PRINT)
$(COMPILE_cpp)
.SUFFIXES: .o .cpp
I would like to highlight the warnings and errors given by the compiler without using external tools (such as colorgcc or CMake); I thought that a good way to hack it was via "bash script tricks". Looking at the solution posted in How Can I highlight the warning and error lines in the make output? I have tried the following:
pathpat="(/[^/]*)+:[0-9]+"
ccred=$(echo -e "\033[0;31m")
ccyellow=$(echo -e "\033[0;33m")
ccend=$(echo -e "\033[0m")
BUILD_PRINT = #echo -e "\e[1;34mBuilding $<\e[0m"
COMPILE_cpp = $(CXX) $(CFLAGS) -o $# -c $< $(MAKEDEP) $(INCLUDES)
%.o : %.cpp
$(BUILD_PRINT)
$(COMPILE_cpp) 2>&1 | sed -e "/[Ee]rror[: ]/ s%$pathpat%$ccred&$ccend%g" -e "/[Ww]arning[: ]/ s%$pathpat%$ccyellow&$ccend%g" echo "${PIPESTATUS[0]}"
.SUFFIXES: .o .cpp
but it is not working. I get the following output
Building main.cpp
g++ -o main.o -c main.cpp 2>&1 | sed -e "/[Ee]rror[: ]/ s%athpat%cred&cend%g" -e "/[Ww]arning[: ]/ s%athpat%cyellow&cend%g" echo ""
sed: can't read echo: No such file or directory
sed: can't read : No such file or directory
Thanks in advance!
In make context the following lines do not behave the way they would in the shell:
ccred=$(echo -e "\033[0;31m")
ccyellow=$(echo -e "\033[0;33m")
ccend=$(echo -e "\033[0m")
In the shell those would put the echoed output into those variables. In make that tries to run the echo command, which doesn't exist, and ends up creating empty variables.
Those lines should either be
ccred=$(shell echo -e "\033[0;31m") to run the commands through the shell and store the output and then used as $(ccred) in the body
ccred=\033[0;31m to store the string in the variable and then used as $$(echo -e '$(ccred)') in the body
ccred=echo -e "\033[0;31m" to store the command in the variable and then used as $$($(ccred) in the body
Either of the first or second options is likely fine. (Use := instead of = in the first option to have make only run the echo command once, at make parse time, instead of every time ccred is used.)
make and shell variables share a prefix sigil $. As such to use shell variables in make contexts requires escaping the $ in shell contexts by doubling it to $$. As such s%$pathpat%$ccred&$ccend%g needs to be s%$$pathpat%$$ccred&$$ccend%g, etc.
Each line of a make rule body is executed as a distinct shell command, as such the commands cannot interact with each other. In order to use constructs like echo "${PIPESTATUS[0]}" meaningfully therefore requires that they be on the same command line in make. As such the compile line in that pattern rule would need to be $(COMPILE_cpp) 2>&1 | sed ...; echo "${PIPESTATUS[0]}".
However, even that isn't going to do what you want since you don't need to echo the exit status from the compilation you need to exit with it, so you probably want ; exit "${PIPESTATUS[0]}" there instead.
Got it working.
First of all thanks #EtanReisner and #rici. Here's the code:
BUILD_PRINT = \e[1;34mBuilding $<\e[0m
COMPILE_cpp = $(CXX) $(CFLAGS) -o $# -c $< $(MAKEDEP) $(INCLUDES)
COMPILE_cpp_OUT=$$($(COMPILE_cpp) 2>&1 | sed -e 's/error/\\\e[1;31merror\\\e[0m/g' -e s/warning/\\\e[1;33mwarning\\\e[0m/g')
%.o : %.cpp
#echo -e "$(BUILD_PRINT)\n$(COMPILE_cpp)\n$(COMPILE_cpp_OUT)"
.SUFFIXES: .o .cpp
All the commands are invoked by only one echo because I want all the outputs (command string and warnings/errors) coherently grouped for each file built when I launch a parallel build with make -j.
$(BUILD_PRINT) just prints out the path of the file currently being built.
$(COMPILE_cpp) prints out the string of the compiler, so that I can see the command with all the flags/dependencies/etc...
$(COMPILE_cpp_OUT) stores the output of the compiler and change some relevant word colour via sed command.