How to use notdir, wildcard and patsubst in a Makefile? - gnu-make

I have the following makefile:
#.SUFFIXES:
#.SUFFIXES: .F90 .cuf .o
ROOT = /home/ccevallos/finalMIT
SRCDIR := $(ROOT)/external/lib_eigesolve
#F90SRC = $(notdir $(wildcard $(SRCDIR)/*.F90))
#F90OBJS = $(patsubst %.F90,%.o,$(F90SRC))
F90OBJS = eigsolve_vars.o toolbox.o zhegst_gpu.o zhemv_gpu.o zhetd2_gpu.o zhetrd_gpu.o zheevd_gpu.o zhegvdx_gpu.o \
dsygst_gpu.o dsymv_gpu.o dsytd2_gpu.o dsytrd_gpu.o dsyevd_gpu.o dsygvdx_gpu.o
#CUFSRC = $(notdir $(wildcard $(SRCDIR)/*.cuf))
#CUFOBJS = $(patsubst %.cuf,%.o,$(CUFSRC))
CUFOBJS = cusolverDn_m.o
FLAGS = -O3 -mp -pgf90libs -Mcuda=cc60,cuda9.1,ptxinfo -Mlarge_arrays
FLAGS2 = -O3 -mp -pgf90libs -Mcuda=cc60,cuda9.1,ptxinfo,maxregcount:64 -Mlarge_arrays
FLAGS3 = -O3 -mp -pgf90libs -Mcuda=cc60,cuda9.1,ptxinfo,nordc,maxregcount:255 -Mlarge_arrays
.PHONY: all
all: lib_eigsolve.a
zhetd2_gpu.o : zhetd2_gpu.F90
pgf90 -c ${FLAGS2} ${OPTFLAGS} $*.F90 -o $*.o
zhemv_gpu.o : zhemv_gpu.F90
pgf90 -c ${FLAGS3} ${OPTFLAGS} $*.F90 -o $*.o
dsytd2_gpu.o : dsytd2_gpu.F90
pgf90 -c ${FLAGS2} ${OPTFLAGS} $*.F90 -o $*.o
dsymv_gpu.o : dsymv_gpu.F90
pgf90 -c ${FLAGS3} ${OPTFLAGS} $*.F90 -o $*.o
cusolverDn_m.o: cusolverDn_m.cuf
pgf90 -c ${FLAGS} ${OPTFLAGS} $*.cuf -o $*.o
%.o: %.F90
pgf90 -c ${FLAGS} ${OPTFLAGS} $*.F90 -o $*.o
lib_eigsolve.a: $(F90OBJS) $(CUFOBJS)
ar rcs $# $^
PHONY: clean
clean:
rm -f lib_eigsolve.a *.mod *.o
This makefile compiles perfectly well, however I basically want to uncomment the lines with # to make this simpler, but when I do that only
ar rcs lib_eigsolve.a
appears in the terminal, with no object file created, and thus lib_eigsolve.a is empty...
Why is it not compiling the object files?
P.S. You can find this Makefile with some modifications here https://github.com/NVIDIA/Eigensolver_gpu

You have a simple typo in $(SRCDIR): the source directory inside Eigensolver_gpu is called lib_eigsolve not lib_eigesolve.
$ cd ~workarea/playground
$ git clone https://github.com/NVIDIA/Eigensolver_gpu.git
$ ls Eigensolver_gpu/
lib_eigsolve LICENSE README.md test_driver
I then proceeded to create a dummy makefile by copy & pasting the relevant lines from your question:
ROOT := $(HOME)/workarea/playground/Eigensolver_gpu
ifndef FIXED
SRCDIR := $(ROOT)/lib_eigesolve
else
SRCDIR := $(ROOT)/lib_eigsolve
endif
F90SRC = $(notdir $(wildcard $(SRCDIR)/*.F90))
$(info F90SRC '$(F90SRC)')
F90OBJS = $(patsubst %.F90,%.o,$(F90SRC))
$(info F90OBJS '$(F90OBJS)')
CUFSRC = $(notdir $(wildcard $(SRCDIR)/*.cuf))
$(info CUFSRC '$(CUFSRC)')
CUFOBJS = $(patsubst %.cuf,%.o,$(CUFSRC))
$(info CUFOBJS '$(CUFOBJS)')
lib_eigsolve.a: $(F90OBJS) $(CUFOBJS)
echo ar rcs $# $^
Test runs:
$ make
F90SRC ''
F90OBJS ''
CUFSRC ''
CUFOBJS ''
echo ar rcs lib_eigsolve.a
ar rcs lib_eigsolve.a
$ make FIXED=1
F90SRC 'dsyevd_gpu.F90 ... zhemv_gpu.F90'
F90OBJS 'dsyevd_gpu.o ... zhemv_gpu.o'
CUFSRC 'cusolverDn_m.cuf'
CUFOBJS 'cusolverDn_m.o'
make: *** No rule to make target 'dsyevd_gpu.o', needed by 'lib_eigsolve.a'. Stop.

Ensure that $(SRCDIR) has no spaces:
SRCDIR := $(strip ${SRCDIR})
ifneq (1,$(words ${SRCDIR}))
$(error Not without further fiddling, friend)
endif

Specifying the path in the wildcard expression looks strange. When you are executing make directly in the srcdir (make -C lib_eigsolve), you should write
F90SRC = $(notdir $(wildcard *.F90))
or
srcdir = .
F90SRC = $(notdir $(wildcard ${srcdir}/*.F90))
when you want to keep your option for out-of-tree builds.

Related

I have touble when including files with makefile for a stm32f4xx

I'am starting using Linux and I have some trouble using some stm32f4 libraries for my project gcc-testing.
All the CMSIS programs needed are inside the STM32F4_Discovery_FW_V1.1.0 and the project is arranged like this:
~
|__
| |
| STM32F4XX
| |
src STM32F4_Discovery_FW_V1.1.0
|
ggc-testing
|
makefile
main.c
stm32f4xx_config.h
stm32_flash.ld
system_stm32f4xx.c
The main.c and the system_stm32f4xx.c use the stm32f4xx.h file that is inside the STM32F4_Discovery_FW_V1.1.0/Libraries/CMSIS/ST/STM32F4xx/Include
The error I get from the terminal is:
Make file console error
My make file:
PROJ_NAME=main
STM_DIR=~/STM32F4XX/STM32F4-Discovery_FW_V1.1.0
STM_SRC = $(STM_DIR)/Libraries/STM32F4xx_StdPeriph_Driver/src
vpath %.c $(STM_SRC)
SRCS = main.c
SRCS += system_stm32f4xx.c
SRCS += $(STM_DIR)/Libraries/CMSIS/ST/STM32F4xx/Source/Templates/TrueSTUDIO/startup_stm32f4xx.s
INC_DIRS = $(STM_DIR)/Utilities/STM32F4-Discovery
INC_DIRS += $(STM_DIR)/Libraries/CMSIS/Include
INC_DIRS += $(STM_DIR)/Libraries/CMSIS/ST/STM32F4xx/Include
INC_DIRS += $(STM_DIR)/Libraries/STM32F4xx_StdPeriph_Driver/inc
INC_DIRS += .
TOOLS_DIR = /opt/gcc-arm-embedded/gcc-arm-none-eabi-4_7-2013q1/bin
CC = arm-none-eabi-gcc
OBJCOPY = arm-none-eabi-objcopy
GDB = arm-none-eabi-gdb
INCLUDE = $(addprefix -I,$(INC_DIRS))
DEFS = -DUSE_STDPERIPH_DRIVER
CFLAGS = -ggdb
CFLAGS += -O0
CFLAGS += -Wall -Wextra -Warray-bounds
CFLAGS += -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork
CFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16
LFLAGS = -Tstm32_flash.ld
.PHONY: $(PROJ_NAME)
$(PROJ_NAME): $(PROJ_NAME).elf
$(PROJ_NAME).elf: $(SRCS)
$(CC) $(INCLUDE) $(DEFS) $(CFLAGS) $(LFLAGS) $^ -o $#
$(OBJCOPY) -O ihex $(PROJ_NAME).elf $(PROJ_NAME).hex
$(OBJCOPY) -O binary $(PROJ_NAME).elf $(PROJ_NAME).bin
clean:
rm -f *.o $(PROJ_NAME).elf $(PROJ_NAME).hex $(PROJ_NAME).bin
# Flash the STM32F4
flash:
st-flash write $(PROJ_NAME).bin 0x8000000
.PHONY: debug
debug:
# before you start gdb, you must start st-util
$(GDB) $(PROJ_NAME).elf
Hope enyne can help with this.
After gnu make generates a cmd-line (eg gcc), use of a ~ would rely on make executing a shell and the shell expanding an arg with a leading unquoted ~. Based on your line with $(addprefix, try adding a space between -I and ,$(INC_DIRS)
However, the overhead from executing a shell can be undesirable and the extra exec is often easy to avoid; for this case, replace ~ with $(HOME)
Example Makefile :
SUBDIR := some/incdir
TILDE_SUBDIR := ~/$(SUBDIR)
NOT_DELIM := $(addprefix -I,$(TILDE_SUBDIR))
DELIM_INC := $(addprefix -I ,$(TILDE_SUBDIR))
HOME_SUBDIR := $(HOME)/$(SUBDIR)
PHONY: all
all:
#echo NOT_DELIM $(NOT_DELIM)
#echo DELIM_INC $(DELIM_INC)
#echo HOME_SUBDIR $(HOME_SUBDIR)
Output :
NOT_DELIM -I~/some/incdir
DELIM_INC -I /home/user1/some/incdir
HOME_SUBDIR /home/user1/some/incdir

Generating GNU Makefile Rules

So in my project I have a src directory and an obj directory. I'm recursively finding the .c and .cpp files in my src directory, and then its corresponding .o file gets put right in the obj directory. So for example if I have a .cpp file: src/dir1/dir2/file.cpp, its corresponding .o file would be obj/file.o. Then I'm generating the rule to get the .o file from the .cpp file using a make foreach function using this code:
rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2)$(filter $(subst *,%,$2),$d))
src = $(call rwildcard,src/,*.cpp *.c)
obj = $(patsubst %,obj/%.o,$(basename $(notdir $(src))))
$(info src: [$(src)])
$(info obj: [$(obj)])
game.exe: $(obj)
g++ $^ -o $#
define objFromSrc
$(1): $(2)
$(info $(1) $(2))
g++ -c $(2) -o $(1)
endef
$(foreach t,$(src),$(call objFromSrc,$(patsubst %,obj/%.o,$(basename $(notdir $(t)))),$(t)))
Here is the output for some example files:
src: [src/dir/main.cpp src/dir/dir2/other3.cpp src/dir/other2.cpp src/other.c]
obj: [obj/main.o obj/other3.o obj/other2.o obj/other.o]
obj/main.o src/dir/main.cpp
obj/other3.o src/dir/dir2/other3.cpp
obj/other2.o src/dir/other2.cpp
obj/other.o src/other.c
makefile:20: *** multiple target patterns. Stop.
You can see the obj variable correctly holds the corresponding .o file names. And the objFromSrc function generates a rule where the target and dependency are correct, but yet I get a multiple target patterns error.
Why am I getting this error and how can I fix it?
You are missing the $(eval) to parse the generated makefile code:
$(eval $(foreach t,$(src),...))
I would also suggest to add an empty line at the end of the multi-line define. Leaving this out is usually calling for trouble when $(eval)uating dynamically generated makefile code.
define objFromSrc
$(1): $(2)
$(info $(1) $(2))
g++ -c $(2) -o $(1)
endef
$(info eval $(foreach t,$(src),...))
BONUS CODE: your recipe is a constant so there is no need to re-generate it for every rule. Use a static pattern rule for $(obj) instead:
.DEFAULT_GOAL := game.exe
obj :=
define objFromSrc
$(1): $(2)
obj += $(1)
endef
$(eval $(foreach t,$(src),...))
$(info obj: [$(obj)])
$(obj): %.o:
g++ -o $# -c $<
game.exe: $(obj)
g++ $^ -o $#
Why am I getting this error and how can I fix it?
All these define and $(call ...) in make produce simple strings. You have to eval it to make the make do what you've ordered (i.e. to create the rule $1 : $2):
$(foreach t,$(src),$(eval $(call objFromSrc,$(patsubst %,obj/%.o,$(basename $(notdir $(t)))),$(t))))

Qt using "mingw32-make" in command line with .exe file generation error

I have used qmake -project and qmake in command line so far with the configuration of .pro file, Makefile.Debug and Makefile.Release has been worked out. But error happens when I try to use mingw32-make in command line to generate .exe for my project.
Errors imply that in line:
64 ****missing separator ...Stop
But in this case, I've set all the environment variables well for both mingw32-make.exe and qmake.exe.
Someone says I should add a Tab instead of a space in makefile.release at where the error happens(the error place only has a "<<"), but other errors happen when I make the change. And I think since the Makefiles are all generated automatically by the qmake command, so maybe that's not the real problem...
Here's the Error info and part of the Makefile.Release file for reference
:
Setting up environment for Qt usage...
Remember to call vcvarsall.bat to complete environment setup!
D:\qt5.10\5.9.2\msvc2017_64>cd D:\qtProgramme\helloworld
D:\qtProgramme\helloworld>mingw32-make
mingw32-make -f Makefile.Release
mingw32-make[1]: Entering directory 'D:/qtProgramme/helloworld'
Makefile.Release:64: *** missing separator. Stop.
mingw32-make[1]: Leaving directory 'D:/qtProgramme/helloworld'
Makefile:36: recipe for target 'release' failed
mingw32-make: *** [release] Error 2
D:\qtProgramme\helloworld>
And here is the Makefile.Release:
#############################################################################
# Makefile for building: helloworld
# Generated by qmake (3.1) (Qt 5.9.2)
# Project: helloworld.pro
# Template: app
#############################################################################
MAKEFILE = Makefile.Release
####### Compiler, tools and options
CC = cl
CXX = cl
DEFINES = -DUNICODE -D_UNICODE -DWIN32 -DWIN64 -DQT_DEPRECATED_WARNINGS -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG
CFLAGS = -nologo -Zc:wchar_t -FS -Zc:strictStrings -O2 -MD -W3 -w44456 -w44457 -w44458 $(DEFINES)
CXXFLAGS = -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -O2 -MD -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -EHsc $(DEFINES)
INCPATH = -I. -I. -I..\..\qt5.10\5.9.2\msvc2017_64\include -I..\..\qt5.10\5.9.2\msvc2017_64\include\QtWidgets -I..\..\qt5.10\5.9.2\msvc2017_64\include\QtGui -I..\..\qt5.10\5.9.2\msvc2017_64\include\QtANGLE -I..\..\qt5.10\5.9.2\msvc2017_64\include\QtCore -Irelease -I..\..\qt5.10\5.9.2\msvc2017_64\mkspecs\win32-msvc
LINKER = link
LFLAGS = /NOLOGO /DYNAMICBASE /NXCOMPAT /INCREMENTAL:NO /SUBSYSTEM:WINDOWS "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'"
LIBS = /LIBPATH:D:\qt5.10\5.9.2\msvc2017_64\lib D:\qt5.10\5.9.2\msvc2017_64\lib\qtmain.lib /LIBPATH:C:\utils\my_sql\my_sql\lib /LIBPATH:C:\utils\postgresql\pgsql\lib shell32.lib D:\qt5.10\5.9.2\msvc2017_64\lib\Qt5Widgets.lib D:\qt5.10\5.9.2\msvc2017_64\lib\Qt5Gui.lib D:\qt5.10\5.9.2\msvc2017_64\lib\Qt5Core.lib
QMAKE = D:\qt5.10\5.9.2\msvc2017_64\bin\qmake.exe
IDC = idc
IDL = midl /NOLOGO
ZIP = zip -r -9
DEF_FILE =
RES_FILE =
COPY = copy /y
SED = $(QMAKE) -install sed
COPY_FILE = copy /y
COPY_DIR = xcopy /s /q /y /i
DEL_FILE = del
DEL_DIR = rmdir
MOVE = move
CHK_DIR_EXISTS= if not exist
MKDIR = mkdir
INSTALL_FILE = copy /y
INSTALL_PROGRAM = copy /y
INSTALL_DIR = xcopy /s /q /y /i
QINSTALL = D:\qt5.10\5.9.2\msvc2017_64\bin\qmake.exe -install qinstall
QINSTALL_PROGRAM = D:\qt5.10\5.9.2\msvc2017_64\bin\qmake.exe -install qinstall -exe
####### Output directory
OBJECTS_DIR = release
####### Files
SOURCES = main.cpp
OBJECTS = release\main.obj
DIST = main.cpp
QMAKE_TARGET = helloworld
DESTDIR = release\ #avoid trailing-slash linebreak
TARGET = helloworld.exe
DESTDIR_TARGET = release\helloworld.exe
####### Implicit rules
.SUFFIXES: .c .cpp .cc .cxx
{.}.cpp{release\}.obj::
$(CXX) -c $(CXXFLAGS) $(INCPATH) -Forelease\ #<<
$<
<<
{.}.cc{release\}.obj::
$(CXX) -c $(CXXFLAGS) $(INCPATH) -Forelease\ #<<
$<
<<
{.}.cxx{release\}.obj::
$(CXX) -c $(CXXFLAGS) $(INCPATH) -Forelease\ #<<
$<
<<
{.}.c{release\}.obj::
$(CC) -c $(CFLAGS) $(INCPATH) -Forelease\ #<<
$<
<<
####### Build rules
first: all
all: Makefile.Release $(DESTDIR_TARGET)
$(DESTDIR_TARGET): $(OBJECTS)
$(LINKER) $(LFLAGS) /MANIFEST:embed /OUT:$(DESTDIR_TARGET) #<<
release\main.obj
$(LIBS)
<<
qmake: FORCE
#$(QMAKE) -o Makefile.Release helloworld.pro
qmake_all: FORCE
You are using a MSVC build of Qt. That can be deduced by the installation path (D:\qt5.10\5.9.2\msvc2017_64) and the output "Remember to call vcvarsall.bat to complete environment setup!".
QMake from an MSVC build of Qt produces MSVC-compatible makefiles (by default at least).
Install the MinGW build of Qt, and you can use the MinGW toolchain for your project.

GNU make: Generate rules with transformed targets (without eval)

For instance, from variable:
files = dirx/a.cc diry/b.cc dirz/b.cc
I want to effectively have these rules: (without resorting to define/eval)
a_a.o: dirx/a.cc
b_b.o: diry/b.cc
c_c.o: dirz/c.cc
The problem seems unlikely, but this makefile does what you want:
Makefile
VPATH := dirx:diry:dirz
SRCS := a.cc b.cc c.cc
OBJS := $(foreach src,$(SRCS),$(basename $(src))_$(basename $(src)).o)
.PHONY: all clean
.SECONDEXPANSION:
%.o: $$(basename $$(subst _,.,$$*)).cc
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $# $<
all: $(OBJS)
clean:
rm -f $(OBJS)
With directories and files set up as per your example:
$ ls -R
.:
dirx diry dirz Makefile
./dirx:
a.cc
./diry:
b.cc
./dirz:
c.cc
it runs like:
$ make
g++ -c -o a_a.o dirx/a.cc
g++ -c -o b_b.o diry/b.cc
g++ -c -o c_c.o dirz/c.cc
Cribs: -
VPATH
foreach
basename
subst
.SECONDEXPANSION

Overwiting target directory in catch-all rule in GNU Makefile

Consider the following:
SRCS = somefile.c util/someother.c ../src/more.c
%.o : %.c
$(GCC) -MD -c $< -o $# -Wp,-MD,.deps/$*.d
#cp .deps/$*.d .deps/$*.P; \
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
-e '/^$$/ d' -e 's/$$/ :/' < .deps/$*.d >> .deps/$*.P; \
rm -f .deps/$*.d
-include $(SRCS:%.c=.deps/%.P)
How would I change the above so that the .o files end up in ./? Currently they build in the directory the source file exists in and that's bad.
I'd do it this way:
SRCS = somefile.c someother.c more.c
%.o : %.c
[same as before]
-include $(SRCS:%.c=.deps/%.P)
vpath %.c util ../src

Resources