# Make rules for FSL projects # # Automatic GNU make rules are used for compiling C and C++ files. all: help: @echo " make Rebuild project targets"; @echo " make clean Remove executables, libraries and object files"; @echo " make install Install into your local FSLDEVDIR"; clean: ${RM} -f *.o *.a *.so *.exe \ ${XFILES} ${FXFILES} ${SOFILES} ${AFILES} ${TESTXFILES} ${RM} -rf ${DEPDIR} insertcopyright: ${FSLDIR}/share/fsl/sbin/insertcopyright * */* # Automatically generate Make rules and dependencies for # all source files in the project. Based on the technique # described at: # # https://make.mad-scientist.net/papers/advanced-auto-dependency-generation/ # # This mechanism supports C (.c), C++ (.cc, .cxx, .cpp) # and CUDA (.cu) source files. # # For each source file, a dependency file is created in a # subdirectory .deps/, which defines a rule specifying the # dependencies for that source file. The dependency file # is created the first time that the source file is compiled # (as part of a single compiler invocation). DEPDIR = .deps # The default directory for object files is the project # directory (i.e. "."), but this can be overridden via # the BUILDDIR and CUDABUILDDIR variables. Note that these # variables must be set *before* this file is included. # # Make sure we have a single trailing slash to use in the # compilation rules below. ifdef SRCDIR SRCDIR_ := ${SRCDIR:/=}/ endif ifdef BUILDDIR BUILDDIR_ := ${BUILDDIR:/=}/ endif ifdef CUDABUILDDIR CUDABUILDDIR_ := ${CUDABUILDDIR:/=}/ else ifdef BUILDDIR_ CUDABUILDDIR_ := ${BUILDDIR_} endif endif # The SRCFILES variable defaults to all source files within # the project directory, however note that SRCFILES can be # overridden in the project Makefile. Note that SRCFILES # must be set *before* this file is included. SRCFILES ?= $(shell find ${SRCDIR_}* -name "*.c" \ -or -name "*.cc" \ -or -name "*.cxx" \ -or -name "*.cpp" \ -or -name "*.cu") # We need to define separate build rules for files in each # source directory, so we pull out each source directory # from the SRCFILES list. If $SRCDIR has been set, we # exclude any directories not contained within it. SRCDIRS := $(sort $(dir ${SRCFILES})) SRCDIRS := $(filter ${SRCDIR_}%,${SRCDIRS}) # Now we get into the nitty gritty. We use a different # naming convention for C/C++ and CUDA dependency # files, to support projects which may have C++/CUDA # source files with the same name, or which may compile # the same file with different parameters for CPU / GPU # execution. # # C/C++ dependency files end in .c.d, and CUDA dep files # end in .cu.d. # # If a source file is contained within a sub-directory, # the sub-directory is baked into the dependency file name # (with slashes replaced by underscores). # # For example, a file "srcs/code.cpp" being compiled with # g++ will have a dependency file named # ".deps/srcs_code.cpp.c.d" CDEPFILES := $(addsuffix .c.d, $(subst /,_,${SRCFILES})) CUDEPFILES := $(addsuffix .cu.d, $(subst /,_,${SRCFILES})) CDEPFILES := $(addprefix ${DEPDIR}/, ${CDEPFILES}) CUDEPFILES := $(addprefix ${DEPDIR}/, ${CUDEPFILES}) DEPFILES := ${CDEPFILES} ${CUDEPFILES} # Rule to make deps directory ${DEPDIR}: @mkdir -p ${DEPDIR} # Define an empty rule for each dep file. In the compilation # rules below, we list each dependency file as a prerequisite # of the corresponding object file. But because the object # and dependency files are created in a single step, Make would # fail if the dependency file does not exist. # # However, by listing dependency files as targets with no # prerequisites or commands causes Make to basically assume # that they exist. ${DEPFILES}: # This is where the auto dependency rules are actually applied - # we include all dependency files that exist. include $(wildcard ${DEPFILES}) # Rules for compilation. We define our own compilation rules # which are equivalent to the implicit Make rules, but with # some modifications to allow automatic dependency generation. # Compiler flags for generating dependency files. These are # added in the %.o compilation rules below. CDEPFLAGS = -MMD -MT $$@ -MF ${DEPDIR}/$$(subst /,_,$$<).c.d CUDEPFLAGS = -MMD -MT $$@ -MF ${DEPDIR}/$$(subst /,_,$$<).cu.d # Override implicit rules %: %.c %: %.cc %: %.cxx %: %.cpp %: %.c++ %.o: %.c %.o: %.cc %.o: %.cxx %.o: %.cpp %.o: %.c++ %.o: %.cu # This canned recipe defines a rule for compiling a specific # type of source file and saving the object file into a # specific location. It has been carefully constructed to # ensure that the compiler flags are not expanded until the # rule is executed. # # The following arguments are passed: # $1 source dir (or empty string) # $2 full build dir (or empty string) # $3 source suffix ("cpp", "cxx", etc) # $4 dependency file suffix ("c" or "cu") # $5 Name of compiler variable # $6 Name of compiler flags variable # $7 dependency flags # This rule ensures the deps directory exists before anything # is compiled, and lists the dependency file as a prerequisite # of the object file so that if the dep file is missing, the # object file will be re-built. define gen_build_rule = ${2}%.o: ${1}%.${3} | ${DEPDIR}/$(subst /,_,${1})%.${3}.${4}.d ${DEPDIR} ${2} $$(eval COMPILER := $$(value ${5})) $$(eval COMPILER_FLAGS := $$(value ${6})) $${COMPILER} $${COMPILER_FLAGS} ${7} -c -o $$@ $$< endef # This canned recipe defines rules for compiling source files # located in a specific directory. Separate rules are required, # because the build directory structure replicates the source # directory structure. For example, the rule for files in the # project root would be: # # build/%.o: %.cpp # # Whereas the rule for files in a sub-directory (let's say # cuda/*.cu) would be: # # build/cuda/%.o: cuda/%.cu # # The following arguments are passed: # $1 The source directory (or an empty string for pwd) # $2 The source directory, with $SRCDIR stripped, for # projects which store sources in a subdirectory define gen_build_rules = # Define rules to create build directories # (unless objects are being saved to pwd) ifneq (${BUILDDIR_}${2},) ${BUILDDIR_}${2}: @mkdir -p $$@ endif ifneq (${CUDABUILDDIR_},${BUILDDIR_}) ifneq (${CUDABUILDDIR_}${2},) ${CUDABUILDDIR_}${1}: @mkdir -p $$@ endif endif # Define rules to compile different types of source file # Eddy uses g++ to compile .cpp files that are linked into # a CUDA executble, so we define an additional rule here. ifneq ($(wildcard ${1}*.cpp),) $$(eval $$(call gen_build_rule,${1},${BUILDDIR_}${2},cpp,c,CXX,CXXFLAGS,$${CDEPFLAGS})) ifneq (${CUDABUILDDIR}, ${BUILDDIR}) $$(eval $$(call gen_build_rule,${1},${CUDABUILDDIR_}${2},cpp,cu,CXX,CUDACXXFLAGS,$${CUDEPFLAGS})) endif endif ifneq ($(wildcard ${1}*.cc),) $$(eval $$(call gen_build_rule,${1},${BUILDDIR_}${2},cc,c,CXX,CXXFLAGS,$${CDEPFLAGS})) endif ifneq ($(wildcard ${1}*.cxx),) $$(eval $$(call gen_build_rule,${1},${BUILDDIR_}${2},cxx,c,CXX,CXXFLAGS,$${CDEPFLAGS})) endif ifneq ($(wildcard ${1}*.c++),) $$(eval $$(call gen_build_rule,${1},${BUILDDIR_}${2},c++,c,CXX,CXXFLAGS,$${CDEPFLAGS})) endif ifneq ($(wildcard ${1}*.c),) $$(eval $$(call gen_build_rule,${1},${BUILDDIR_}${2},c,c,CC,CFLAGS,$${CDEPFLAGS})) endif ifneq ($(wildcard ${1}*.cu),) $$(eval $$(call gen_build_rule,${1},${CUDABUILDDIR_}${2},cu,cu,NVCC,NVCCFLAGS,$${CUDEPFLAGS})) endif endef # Generate build rules for each source directory $(foreach SD_,${SRCDIRS},$(eval $(call gen_build_rules,${SD_:./%=%},$(patsubst ./%,%,${SD_:${SRCDIR_}%=%})))) # Make sure that environment compiler flags are passed through # to sub-makes. The _ENV* variables are defined in vars.mk install: @mkdir -p ${FSLDEVDIR} @CXXFLAGS="${_ENVCXXFLAGS}" \ CFLAGS="${_ENVCFLAGS}" \ CPPFLAGS="${_ENVCPPFLAGS}" \ LDFLAGS="${_ENVLDFLAGS}" \ ${MAKE} \ "DESTDIR=${FSLDEVDIR}" \ master-install-script # The custominstall rule can be optionally # defined in project Makefiles. .PHONY: custominstall master-install-script: @if [ "X${PROJNAME}X" = XX ] ; then \ echo " " ; \ echo "No PROJNAME defined in the Makefile" ; \ echo " ... aborting install" ; \ echo " " ; \ exit 4 ; \ fi; @CXXFLAGS="${_ENVCXXFLAGS}" \ CFLAGS="${_ENVCFLAGS}" \ CPPFLAGS="${_ENVCPPFLAGS}" \ LDFLAGS="${_ENVLDFLAGS}" \ ${MAKE} all @${MAKE} exeinstall @${MAKE} hdrinstall @${MAKE} libinstall @${MAKE} tclinstall @${MAKE} pyinstall @${MAKE} datainstall @${MAKE} custominstall # Generic routine used to install # XFILES, PYFILES, SCRIPTS, etc etc # Expects four arguments: # - $1: Label, just used to print a descriptive message # - $2: Destination directory for installation # - $3: Permission mask to apply to all installed files (e.g. 0755) # - $4: Space-separated list of target files/directories to install define _x_install = if ! echo $(wildcard $(4)) | grep -q -e "^ *$$"; then \ echo Installing $(1) ; \ ${MKDIR} -p -m 0755 $(2) ; \ fi for target in $(4) verylongdummyname ; do \ if [ -f $$target ] ; then \ echo ${INSTALL} -m $(3) $$target $(2)/ ; \ ${INSTALL} -m $(3) $$target $(2)/ ; \ elif [ -d $$target ]; then \ echo ${CP} -r $$target $(2)/ ; \ ${CP} -r $$target $(2)/ ; \ find $(2)/`basename $$target` -type d -exec chmod 0755 {} \; ; \ find $(2)/`basename $$target` -type f -exec chmod $(3) {} \; ; \ fi; \ done; endef # Installs PYFILES into $FSLDIR/etc/fsl/python/$PROJNAME/ pyinstall: @${MKDIR} -p -m 0755 $(dest_PYDIR) @$(call _x_install,"python scripts",${dest_PYDIR}/${PROJNAME}/,0644,${PYFILES}) # Installs AFILES and SOFILES into $FSLDIR/lib/ libinstall: @${MKDIR} -p -m 0755 $(dest_LIBDIR) @$(call _x_install,"library files",${dest_LIBDIR},0644,${AFILES} ${SOFILES}) # Installs: # - HFILES into $FSLDIR/include/$PROJNAME/ # - GLOBALHFILES into $FSLDIR/include/ hdrinstall: @${MKDIR} -p -m 0755 $(dest_INCDIR) @$(call _x_install,"header files",${dest_INCDIR}/${PROJNAME}/,0644,${HFILES}) @$(call _x_install,"global header files",${dest_INCDIR}/,0644,${GLOBALHFILES}) # Installs DATAFILES into $FSLDIR/data/$PROJNAME/ datainstall: @${MKDIR} -p -m 0755 $(dest_DATADIR) @$(call _x_install,"data files",${dest_DATADIR}/${PROJNAME}/,0644,${DATAFILES}) # Installs: # - XFILES into $FSLDIR/bin/ # - SCRIPTS into $FSLDIR/bin/ exeinstall: @${MKDIR} -p -m 0755 $(dest_BINDIR) @$(call _x_install,"binaries",${dest_BINDIR}/,0755,${XFILES}) @$(call _x_install,"scripts",${dest_BINDIR}/,0755,${SCRIPTS}) # Installs: # - TCLFILES into $FSLDIR/tcl/ # - RUNTCLS into $FSLDIR/bin/ tclinstall: @${MKDIR} -p -m 0755 $(dest_TCLDIR) @${MKDIR} -p -m 0755 $(dest_BINDIR) @$(call _x_install,"tcl scripts",${dest_TCLDIR}/,0755,${TCLFILES}) @# create RUNTCLS links @for lntarget in ${RUNTCLS} verylongdummyname ; do \ if [ $${lntarget} != verylongdummyname ] ; then \ cd ${dest_BINDIR} ; \ if [ ${SYSTYPE} != Darwin ] ; then \ ${RM} -f $${lntarget} ; \ echo ln -s Runtcl $${lntarget} ; \ ln -s Runtcl $${lntarget} ; \ fi ; \ ${RM} -f $${lntarget}_gui ; \ echo ln -s Runtcl $${lntarget}_gui ; \ ln -s Runtcl $${lntarget}_gui ; \ fi \ done