rules.mk 11 KB
Newer Older
wangkx1's avatar
change  
wangkx1 committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
# 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