1 ################################################################################
2 # Desc: Mihrtec Standard Transpilation Makefile
5 ################################################################################
6 # This makefile manages a build environment targeting native platforms with gcc
7 # and web platforms with either emscripten or binaryen (js or wasm).
8 ################################################################################
9 # Each .c file is automatically compiled into an environment-dependent object
10 # file of compiler-specific format (.o for gcc, .bc for llvm/emcc/em++).
11 ################################################################################
13 # Source languages handled by this build system
15 # Language-specific compilers and flags passed in from environment
16 c_C
:= $(strip $(notdir $(CC
)))
17 c_FLAGS
:= $(strip $(CFLAGS
)) -I.
18 c_LIBS
:= SDL2_ttf SDL2_net SDL2_image SDL2 wolfssl
20 cpp_C
:= $(strip $(notdir $(CXX
)))
21 cpp_FLAGS
:= $(strip $(CXXFLAGS
)) $(c_FLAGS
)
24 go_FLAGS
:= $(c_FLAGS
)
25 # Source to source languages
31 # Compiler-specific associations. Each compiler has a binary object suffix
32 # (OBJ), an archiver (AR), and an archiver object suffix (AROBJ). Each compiler
33 # may optionally have defined a linker (LD), and a binary output suffix (OUT).
38 $(cpp_C
)_LD
:= $(cc_LD
)
42 gcc_AROBJ
:= $(cc_AROBJ
)
45 emcc_AROBJ
:= $(emcc_OBJ
) #emar is just a python script that reparses shit for emcc
48 em
++_OBJ
:= $(emcc_OBJ
)
50 em
++_AROBJ
:= $(emcc_AROBJ
)
51 em
++_OUT
:= $(emcc_OUT
)
55 gccgo_AROBJ
:= $(cc_AROBJ
)
56 # Shell functions to determine what libraries can be linked by the compiler
57 cc_LDLIBS
:= $(shell ls
/usr
/lib | grep
".so" | sed
-e
's@^.*lib\([_\+a-zA-Z0-9\-]*\)\..*@\1@g')
58 gcc_LDLIBS
:= $(cc_LDLIBS
)
60 g
++_LDLIBS
:= $(cc_LDLIBS
)
62 go_LDLIBS
:= $(cc_LDLIBS
)
64 # The directory in './' containing app drivers and compilation info
67 LIB_DIR
:= $(ROOT_DIR
)/lib
$(shell uname
-m
)/$($(c_C
)_OBJ
)
68 LIBDL_DIR
:= $(LIB_DIR
)/.cache
69 LIBINC_DIR
:= $(ROOT_DIR
)/include
70 # The makefile MUST be capable of generating these directories and all of their
71 # contents. These directories are removed during the 'scrub' rule
72 MAKE_DIRS
:= $(LIB_DIR
) $(LIBDL_DIR
) $(LCLLIB_DIR
)
73 # Set up lib inclusions, and scan for built libs
74 c_FLAGS
+= -I
$(LIBINC_DIR
)
75 c_OBJ
:= $($(c_C
)_OBJ
)
76 # Modules are any directories other than 'DRIVER_MODULE' in './' and produce a
77 # single object file in './' containing all of the module's symbols and
79 MODULES
:= $(filter-out $(DRIVER_DIR
),$(subst /,,$(shell ls
-d
*/)))
81 # determine the dependencies of a given source and language, but route link
82 # issues to /dev/null since we will generate them later
83 # can pass an optional third argument for additional opts
84 define SRC_LANG_DEPS
=
85 $(shell $($2_C) -MM
-MG
$(if
$3,-I
$3) $($2_FLAGS) $1 2> /dev
/null\
86 | sed
-e
's@^.*\.o: @@' -e
's@\\@@')
89 # create a list of module object archives associated with the given files,
90 # if any, filtering out any drivers or non-module objects
91 define FLIST_MODULEOBJS
=
92 $(sort $(shell echo
$(filter-out .
%,$(filter-out $(DRIVER_DIR
)/%,$1))\
93 | sed
-e
's@^\([a-Z\-\+]*\)/*@\1.$(c_OBJ)@g' ))
96 # establish a dependency rule given a source and language. if passed a
97 # directory name as the third argument, will prefix all header files in the
98 # dependency with the local lib
99 define SRC_LANG_DEPRULE
=
100 $(eval
$1_$2_DEPS := $(call SRC_LANG_DEPS
,$1,$2,$3))
101 $(foreach hdr
,$(filter %.h
,$($1_$2_DEPS)),\
102 $(if
$(word 2,$(subst /, ,$(hdr
))),,\
103 $(eval
$1_$2_DEPS := $($1_$2_DEPS:$(hdr
)=$3/$(hdr
)))\
105 $(basename $1).
$($2_OBJ): $($1_$2_DEPS)
108 # establish a build and link rule given a source driver and language ############
109 define SRC_LANG_LINKRULE
=
110 # turn the non-existent third argument into an identifier for our link data
111 $(eval
3 := $(notdir $(basename $1)))
112 # generate the dependecies list of the driver itself
113 $(eval
$3_DRV_DEPS := $(filter-out $1,$(call SRC_LANG_DEPS
,$1,$2)))
114 # Implicit and Manual LD Options to allow .ld files to specify manual linking to
115 # one of the built-in modules. Implicit LDOs are ones we can surmise from
116 # include rules generated with 'SRC_LANG_DEPS', but these are only caught if
117 # there was an explicitly included header, or some other file, within the source
118 # file of the driver to one of the internal modules. This practice is a
119 # reliable way to automatically link an internal module, but if desired an
120 # associated '.ld' file for the driver can contain a list of whitespace
121 # separated module names to ensure they link properly
122 $(eval MODULE_FILES
:= $(filter-out .
% $(DRIVER_DIR
)/%,$($3_DEP)))
123 $(eval MODULE_FILES
:= $(basename $(MODULE_FILES
)))
124 $(foreach mfile
,$(MODULE_FILES
),\
125 $(eval
$3_ILDO += $(firstword $(subst /, ,$(mfile
)))))
126 # Find the .ld files, if present, and include their 'links' to our internal libs
127 $(if
$(wildcard $(1:.
$2=.
ld)),\
128 $(eval
$3_MLDO := $(basename $(strip $(file
<$(1:.
$2=.
ld))))))
129 # Combine the link info into a single list '$3_LDO'
130 $(eval
$3_LDO := $(patsubst %,%.
$($($2_C)_AROBJ
),$(sort $($3_ILDO) $($3_MLDO))))
131 # If the compiler supports linking, distinguish static from dynamic linking
133 $(eval
$3_STLIBS := $(filter-out $($($2_C)_LDLIBS
),$($2_LIBS)))\
134 $(eval
$3_DLIBS := $(filter-out $($3_STLIBS),$($2_LIBS))),\
135 $(eval
$3_STLIBS := $(2_LIBS
)))
137 $(eval
$3_DIR := $(dir $(1:%.
$2=$(ROOT_DIR
)/%)))
138 $(eval MAKE_DIRS
+= $($3_DIR))
139 # Setup the sources for this object
140 $(eval
$3_SRC := $($3_LDO))
142 # Find the dependencies
143 $(eval
$3_DEP := $($3_DRV_DEPS))
144 $(eval
$3_DEP += $($3_SRC))
145 # Preserver ordering and build out the linking order (don't use -Wstatic/dynamic)
146 $(foreach lib
,$($2_LIBS),\
147 $(if
$(findstring $(lib
),$($3_STLIBS)),\
148 $(eval
$3_SRC := $(LIB_DIR
)/lib
$(lib
).
$($($2_C)_AROBJ
) $($3_SRC)),\
149 $(eval
$3_SRC := -l
$(lib
) $($3_SRC))))
150 $($3_DIR)$3$($2_OUT): $($3_DEP) |
$($3_DIR)
151 $($2_C) $($2_FLAGS) $($3_SRC) -o
$$@
152 #/SRC_LANG_LINKRULE##############################################################
155 # generate rule for turning an entire module's collection of binary objects into
156 # a single locally-linked (no external -L libs) object (for simplified linking
157 # modules as static libs).
158 define MODULE_ARCRULE
=
159 $(eval
$1_ARCDEPS := $(filter $1/%,$(foreach lang
,$(LANGS
),$($(lang
)_MOD_TRG
))))
160 $1.
$($(c_C
)_AROBJ
): $($1_ARCDEPS) |
$(LCLLIB_DIR
)
161 $($(c_C
)_AR
) cr
$$@
$$^
164 # define an object creation rule given a module and language
165 define MODULE_LANG_OBJRULE
=
166 $1/%.
$($2_OBJ): $1/%.
$2
167 $$($2_C) -I
$1 $$(filter-out -l
% %.a
,$$($2_FLAGS)) $$< -c
-o
$$@
170 # LANG_LIB_PARENT_BUILDRULE######################################################
171 # define rules for creating unknown libraries in the local build environment for
172 # either binary or bytecode representation given a library name and, optionally,
173 # a parent lib that included it for error reporting #############################
174 define LANG_LIB_PARENT_BUILDRULE
=
175 # Construct the download method, or error if there isn't a valid one
177 $(if
$($2_GITADDR),$(eval LIBDL
:= git clone
$($2_GITADDR) $2),\
178 $(if
$($2_HGADDR),$(eval LIBDL
:= hg clone
$($2_HGADDR) $2),\
179 $(if
$($2_CVSADDR),$(shell echo
$($2_CVSPASS) > ~
/.cvspass
)\
180 $(eval LIBDL
:= export CVSROOT
=$($2_CVSADDR) && cvs
$($2_CVSGET)),\
181 $(if
$($2_WEBADDR),$(eval LIBDL
:= wget
-O
$($2_WEBTARG) $($2_WEBADDR)),\
182 $(eval
$(error No way to download
$2 needed by
$3 for
$1 language
))))))
186 cd
$(LIBDL_DIR
) && $(LIBDL
) $(if
$($2_WEBINIT),&& $($2_WEBINIT))
187 # '$2' Make and install rule
188 $(LIB_DIR
)/lib
$2.so
: $(LIBDL_DIR
)/$2 $($2_LIBDEPS:%=$(LIB_DIR
)/lib
%.so
)
190 cd
$(LIBDL_DIR
)/$2 && $($2_MAKE)
191 cd
$(LIBDL_DIR
)/$2 && $($2_INSTALL)
193 # each library generated will be included in the libs available list, removed
194 # from the missing libs list, and passed by -l'libname' to the language flags
195 $(foreach lang
,$(LIBLANGS
),\
196 $(eval
$(lang
)_LIBS_AVAILABLE
+= $2)\
197 $(eval
$(lang
)_MISSING_LIBS
:= $(filter-out $2,$($(lang
)_MISSING_LIBS
)))\
198 $(eval
$(lang
)_SOBS
+= $(2:%=$(LIB_DIR
)/lib
%.so
))\
200 #/LANG_LIB_PARENT_BUILDRULE######################################################
204 #LANG_LIB_INIT###################################################################
205 # For every library in this language, we need to determine how, and if, to link
206 # its data. This could potentially be done recursively as libraries have
207 # dependencies upon each other. We call a recursive macro here to expand out
208 # all the libs for this language, and then flatten the tree by removing
209 # duplicates with shell commands. We can't use make's 'sort' function because
210 # we actually care about the ordering so that interdependent libraries can link
211 # back up the tree ##############################################################
212 define LANG_LIB_INIT
=
213 # Implicit defaults for lib.mk files
214 $(eval AUTOGEN
:= .
/autogen.sh
)
215 $(eval CONFIGURE
:= .
/configure
)
216 $(eval CONFIGURE
+= --prefix=$(abspath
$(LIB_DIR
)/.trash
))
217 $(eval CONFIGURE
+= --includedir=$(abspath
$(LIBINC_DIR
)))
218 $(eval CONFIGURE
+= --libdir=$(abspath
$(LIB_DIR
)))
219 $(eval MKCMD
:= make
-k
)
220 $(eval MKINSTALL
:= make
-k
install)
221 $(eval MKCLEAN
:= make
clean)
223 # One of these must be defined in the .mk file imported
224 $(foreach var
, GITADDR WEBADDR HGADDR CVSADDR
,\
225 $(eval undefine
$(var
)))
226 # Other, optional, variables to clear
227 $(eval undefine WEBTARG
) # File 'WEBADDR' is stored in
228 $(eval undefine WEBINIT
) # Method of extracting 'WEBTARG', if necessary
229 $(eval undefine CVSGET
) # checkout command
230 $(eval undefine CVSPASS
) # auto-put into ~/.cvspass to login automatically
231 # Sneaky expansion to call a file inclusion with unelevated (eval'ed) text
233 $(eval
include $2.mk
)
235 # If there is a .mk file for this lib, import it. Otherwise, we'll try to link
236 # it later, unless there is no linker for the language's compiler.
237 $(if
$(wildcard $2.mk
),$(eval
$(call EVALINCLUDE
)),\
239 $(warning No
$2.mk for lib
$2, linking from the system
),\
240 $(error No
$2.mk for lib
$2 and
'$($1_C)' is not configured for linking
)))
242 # Store lib specific build info
243 $(foreach var
,GITADDR WEBADDR HGADDR CVSADDR WEBTARG WEBINIT CVSGET CVSPASS
,\
244 $(eval
$2_$(var
) := $($(var
))))
245 $(eval
$2_MAKE := $(if
$(AUTOGEN
),$(AUTOGEN
) && ))
246 $(eval
$2_MAKE += $(if
$(CONFIGURE
),$(CONFIGURE
) && ))
248 $(eval
$2_MAKE += $(MKCMD
)),\
249 $(error
$2.mk requires a valid MKCMD definition
))
251 $(eval
$2_INSTALL := $(MKINSTALL
)),\
252 $(error
$2.mk requires a valid MKINSTALL definition
))
253 $(eval
$2_CLEAN := $(MKCLEAN
))
254 $(eval
$2_DEPS := $(LIBDEPS
))
256 # Add dependencies we found (potentially recursively, ignore repeats until later
257 # so we can preserve ordering information for interdependency build ordering)
258 $(if
$(LIBDEPS
),$(eval
$1_LIBS += $(LIBDEPS
)))
260 # languages that can link this library (typically any lang that shares the
261 # object format (ELF by default, but this is compiler-dependent (e.g. emcc uses
262 # llvm bitcode, which is just an intermediary representation of data that can
263 # link to anything else statically, or as an archive)))
264 $(eval LIBLANGS ?
= c
cpp go
)
265 $(if
$(findstring $1,$(LIBLANGS
)),,\
266 $(error
$2.mk must specifically set LIBLANGS
:= $1 if it is compatible with
$1))
268 # Mark this lib as available for all its other compatible languages. This list
269 # is assumed to be sorted later, so we will ignore repeats for speed
270 $(foreach lang
,$(LIBLANGS
),\
271 $(if
$(findstring $2,$($(lang
)_LIBS
)),,\
272 $(eval
$(lang
)_LIBS
+= $2)\
275 # Recurse this function for every libdep encountered
276 $(foreach libdep
,$(LIBDEPS
),\
277 $(eval
$(call LANG_LIB_INIT
,$1,$(libdep
)))\
279 #/LANG_LIB_INIT##################################################################
282 # Initialize data for supported lanaguages ######################################
284 # Initialize all relevant (although not necessarily used) language relative data
285 $(eval
$1_OBJ := $($($1_C)_OBJ
))
286 $(eval
$1_OUT := $($($1_C)_OUT
))
287 $(eval
$1_SOURCES := $(subst .
/,,$(shell find
-name
"*.$1")))
288 # For each source language that can compile to this language, add the expected
289 # result of such a transformation to the sources of this language. Then, if the
290 # source-to-source compiler also creates duplicate files of other formats
291 # (i.e. yacc/bison also produce accompanying .h files), add them to the list of
292 # source files that this makefile can generate ('$1_TARGETS'), to later be deleted
293 # during the clean rule.
294 $(foreach srcl
,$($1_SRCL),\
295 $(eval
$(srcl
)_SOURCES
:= $(shell find
-name
"*.$(srcl)" | \
296 sed
-e
's@^\(.*\).$(srcl)@\1$($(srcl)_STEM:%=.%).$1@g' -e
's@\./@@'))\
297 $(eval
$1_SOURCES += $($(srcl
)_SOURCES
))\
298 $(eval
$(srcl
)_TARGETS
+= $($(srcl
)_SOURCES
:%.
$(srcl
)=.
$1))\
299 $(foreach dup
,$($(srcl
)_DUP
),\
300 $(eval
$(srcl
)_TARGETS
+= $($(srcl
)_TARGETS
:%.
$1=%.
$(dup
)))\
303 $(eval
$1_DRV_SRC := $(filter $(DRIVER_DIR
)/%,$($1_SOURCES)))
304 $(eval DRV_SRC
+= $($1_DRV_SRC))
305 $(eval
$1_DRV_TRG := $(patsubst %.
$1,$(ROOT_DIR
)/%$($1_OUT),$($1_DRV_SRC)))
306 $(eval DRV_TRG
+= $($1_DRV_TRG))
307 $(eval
$1_MOD_SRC := $(filter-out $(DRIVER_DIR
)/%,$($1_SOURCES)))
308 $(eval MOD_SRC
+= $($1_MOD_SRC))
309 $(eval
$1_MOD_TRG := $(subst .
$1,.
$($1_OBJ),$($1_MOD_SRC)))
310 $(eval MOD_TRG
+= $($1_MOD_TRG))
311 # Generate a list of shared object files this language has access to
312 $(eval
$1_SOBS := $(wildcard $(LIB_DIR
)/lib
*.so
))
314 # First, initialize language-module, module, and language relative data. Then,
315 # filter out each source file from the module and generate Dependency Rules and
316 # Module Object Rules for each of them.
317 $(foreach module
,$(MODULES
),\
318 $(eval
$(module
)_
$1_SRC := $(filter $(module
)/%,$($1_MOD_SRC)))\
319 $(eval
$(module
)_
$1_TRG := $(filter $(module
)/%,$($1_MOD_TRG)))\
320 $(eval
$(module
)_SRC
+= $(module
)_
$1_SRC)\
321 $(eval
$(module
)_TRG
+= $($(module
)_
$1_TRG))\
322 $(eval
$1_TRG += $($(module
)_TRG
))\
324 $(foreach src
,$(filter $(module
)/%,$($1_MOD_SRC)),\
325 $(eval
$(call SRC_LANG_DEPRULE
,$(src
),$1,$(module
)))\
326 $(eval
$(call MODULE_LANG_OBJRULE
,$(module
),$1)))\
328 $(eval
$1_TARGETS += $($1_TRG))
330 # For all the listed libraries, initialize the library, which will set up all of
331 # its local ($1_LIBS, etc) data
332 $(foreach lib
,$($1_LIBS),\
333 $(call LANG_LIB_INIT
,$1,$(lib
)))
334 # The initializer produces an ordered list of interlinked dependencies in groups
335 # by depth, so when reading backwards and compressing repeats we are left with a
336 # reliable build order for library interlinking. this awk program just looks
337 # through a list of space separated words backwards failing to print when it
338 # encounters a repeat (which is suitable as a kind of ad-hoc make lisp function)
339 $(eval
$1_LIBS := $(shell echo
"$($1_LIBS)" | awk \
342 printf (!a[$$i]++) ? $$i FS : "";
348 #/LANG_INIT######################################################################
351 # The following awk program reverses the order of a list while also removing
353 define AWK_REVERSE_SQUASH
=
356 BEGIN { OFS = " "; ORS = " " }
357 { for (i=NF; i>1; i--)
363 # The recursive library dependecy traversal constructs a tree ordered by nested
364 # dependency depth. when linking, this order is reversed.
365 # Initialize each language and look for its files
366 $(foreach lang
,$(LANGS
),\
367 $(eval
$(call LANG_INIT
,$(lang
)))\
368 $(eval
$(lang
)_LIBS
:= $(shell echo
$($(lang
)_LIBS
) |
$(call AWK_REVERSE_SQUASH
))))
370 # Generate rules for making missing libs
371 $(foreach lang
,$(LANGS
),\
372 $(foreach mlib
,$($(lang
)_MISSING_LIBS
),\
373 $(eval
$(call LANG_LIB_PARENT_BUILDRULE
,$(lang
),$(mlib
),root
))\
376 # Create module object rules for each module and scan for interpreted code
377 # sources (yacc, etc)
378 $(foreach module
,$(MODULES
),\
379 $(eval
$(call MODULE_ARCRULE
,$(module
)))\
382 # Create lang-specific rules for producing final (linked) targets
383 $(foreach lang
,$(LANGS
),\
384 $(foreach drvsrc
,$($(lang
)_DRV_SRC
),\
385 $(eval
$(call SRC_LANG_DEPRULE
,$(drvsrc
),$(lang
)))\
386 $(eval
$(call SRC_LANG_LINKRULE
,$(drvsrc
),$(lang
)))\
389 # Make any dirs that we're in charge of
393 # Create driver rules for each driver we found, let users call make specifying
394 # the basename of the driver, and just route to the correct linkrule we made in
396 DRV_FNAMES
:= $(notdir $(DRV_SRC
))
397 .PHONY
: all $(basename $(DRV_NAMES
))
398 $(foreach fname
,$(DRV_FNAMES
),\
399 $(eval
$(basename $(fname
)): \
400 $(filter %/$(basename $(fname
))$($(lastword
$(subst .
, ,$(fname
))_OUT
)),$(DRV_TRG
)))\
402 all: $(basename $(DRV_FNAMES
))
405 # Source-to-source build rules
406 # Compilers that don't specify an output location can be made to change
407 # directories with 'cd' to the target file's directory, update the command line
408 # source files, and allow the compiler to output in its default current working
409 # directory 'cwd'. this is done by setting the $1_CHDIR flag to 't'
410 define SRCLANG_TRGLANG_BUILDRULE
=
411 %$($1_STEM:%=.
%).
$2$(if
$($1_DUP), %$($1_STEM:%=.
%).
$($1_DUP)): %.
$1
412 $(if
$($1_CHDIR),cd
$$(shell dirname
$$@
) && )$$($1_C) $$($1_FLAGS)$(if
$($1_CHDIR),\
413 $$(shell echo
$$@ | sed
-e
's@^.*/\([^\.]*\).*@\1@').
$1,\
416 $(foreach lang
,$(LANGS
),$(if
$($(lang
)_SRCL
),$(foreach srcl
,$($(lang
)_SRCL
),\
417 $(eval
$(call SRCLANG_TRGLANG_BUILDRULE
,$(srcl
),$(lang
))))))
420 CLEANABLES
:= $(foreach lang
,$(LANGS
),$(foreach srclang
,$($(lang
)_SRCL
), $($(srclang
)_TARGETS
)) $($(lang
)_TARGETS
))