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 ldconfig
-p | head
-n
-2 | grep
-v Broken | sed
-e
's@^.*lib\([_\+a-Z0-9\-]*\)\..*@\1@g' -e
'1d')
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 # If we have a linker, include 'LIB_DIR' in the paths it looks in
77 $(foreach lang
,$(LANGS
),$(if
$($($(lang
)_C
)_LD
),$(eval\
78 $(lang
)_FLAGS
+= -L
$(LIB_DIR
)\
80 # Modules are any directories other than 'DRIVER_MODULE' in './' and produce a
81 # single object file in './' containing all of the module's symbols and
83 MODULES
:= $(filter-out $(DRIVER_DIR
),$(subst /,,$(shell ls
-d
*/)))
85 # determine the dependencies of a given source and language, but route link
86 # issues to /dev/null since we will generate them later
87 # can pass an optional third argument for additional opts
88 define SRC_LANG_DEPS
=
89 $(shell $($2_C) -MM
-MG
$(if
$3,-I
$3) $($2_FLAGS) $1 2> /dev
/null\
90 | sed
-e
's@^.*\.o: @@' -e
's@\\@@')
93 # create a list of module object archives associated with the given files,
94 # if any, filtering out any drivers or non-module objects
95 define FLIST_MODULEOBJS
=
96 $(sort $(shell echo
$(filter-out .
%,$(filter-out $(DRIVER_DIR
)/%,$1))\
97 | sed
-e
's@^\([a-Z\-\+]*\)/*@\1.$(c_OBJ)@g' ))
100 # establish a dependency rule given a source and language. if passed a
101 # directory name as the third argument, will prefix all header files in the
102 # dependency with the local lib
103 define SRC_LANG_DEPRULE
=
104 $(eval
$1_$2_DEPS := $(call SRC_LANG_DEPS
,$1,$2,$3))
105 $(foreach hdr
,$(filter %.h
,$($1_$2_DEPS)),\
106 $(if
$(word 2,$(subst /, ,$(hdr
))),,\
107 $(eval
$1_$2_DEPS := $($1_$2_DEPS:$(hdr
)=$3/$(hdr
)))\
109 $(basename $1).
$($2_OBJ): $($1_$2_DEPS)
112 # establish a build and link rule given a source driver and language ############
113 define SRC_LANG_LINKRULE
=
114 # turn the non-existent third argument into an identifier for our link data
115 $(eval
3 := $(notdir $(basename $1)))
116 # generate the dependecies list
117 $(eval
$3_DEP := $(filter-out $1,$(call SRC_LANG_DEPS
,$1,$2)))
118 # Implicit and Manual LD Options to allow .ld files to specify manual linking to
119 # one of the built-in modules. Implicit LDOs are ones we can surmise from
120 # include rules generated with 'SRC_LANG_DEPS', but these are only caught if
121 # there was an explicitly included header, or some other file, within the source
122 # file of the driver to one of the internal modules. This practice is a
123 # reliable way to automatically link an internal module, but if desired an
124 # associated '.ld' file for the driver can contain a list of whitespace
125 # separated module names to ensure they link properly
126 $(eval MODULE_FILES
:= $(filter-out .
% $(DRIVER_DIR
)/%,$($3_DEP)))
127 $(eval MODULE_FILES
:= $(basename $(MODULE_FILES
)))
128 $(foreach mfile
,$(MODULE_FILES
),\
129 $(eval
$3_ILDO += $(firstword $(subst /, ,$(mfile
)))))
130 # Find the .ld files, if present, and include their 'links' to our internal libs
131 $(if
$(wildcard $(1:.
$2=.
ld)),\
132 $(eval
$3_MLDO := $(basename $(strip $(file
<$(1:.
$2=.
ld))))))
133 $(eval
$3_LDO := $(patsubst %,%.
$($($2_C)_AROBJ
),$(sort $($3_ILDO) $($3_MLDO))))
134 # If we have any libs to generate, make sure their specific shared object file
135 # is available, and that the linker is linking to our expected link directory
136 # for our compiler and architecture (transpilation magic)
137 $(eval
$3_SRC_OBJS := $(filter-out $1 %.h
,$($3_DEP) $($3_LDO)))
138 # If the compiler supports linking, filter any shared objects out of the list
139 # that can be linked (the -l command option should already be in $2_FLAGS)
140 # otherwise, explicitly include all shared objects in the source objects, while
141 # also putting them on the dep list
143 $(eval
$3_SOB := $(filter-out $($2_LD_LIBS),$($2_LIBS))),\
144 $(eval
$3_SOB := $($2_LIBS:%=$(LIB_DIR
)/lib
%.so
))\
145 $(eval
$3_SRC_OBJS += $($3_SOB)))
146 $(eval
$3_SOB += $(filter $(LIB_DIR
)/lib
%.so
,$($2_FLAGS)))
148 $(eval
$3_DIR := $(dir $(1:%.
$2=$(ROOT_DIR
)/%)))
149 $(eval MAKE_DIRS
+= $($3_DIR))
150 # Commandline setup. The recursive library dependecy traversal constructs a
151 # tree ordered by nested dependency depth. when linking, this order is reversed
152 # Establish the link tree:
153 $(eval LINK_TREE
:= $(filter -l
% %.so
,$($2_FLAGS)) $($3_DEP) $($3_LDO) $1)
154 $(eval LINK_TREE
:= $(shell echo
$(filter-out %.h
,$(LINK_TREE
)) | awk \
156 BEGIN { OFS = " "; ORS = " " }
157 { for (i=NF; i>1; i--)
163 $($3_DIR)$3$($2_OUT): $($3_SOB) $($3_LDO) $($3_DEP) |
$($3_DIR)
164 $$($2_C) $$(filter-out $(LINK_TREE
),$($2_FLAGS)) $(LINK_TREE
) -o
$$@
165 #/SRC_LANG_LINKRULE##############################################################
168 # generate rule for turning an entire module's collection of binary objects into
169 # a single locally-linked (no external -L libs) object (for simplified linking
170 # modules as static libs).
171 define MODULE_ARCRULE
=
172 $(eval
$1_ARCDEPS := $(filter $1/%,$(foreach lang
,$(LANGS
),$($(lang
)_MOD_TRG
))))
173 $1.
$($(c_C
)_AROBJ
): $($1_ARCDEPS) |
$(LCLLIB_DIR
)
174 $($(c_C
)_AR
) cr
$$@
$$^
177 # define an object creation rule given a module and language
178 define MODULE_LANG_OBJRULE
=
179 $1/%.
$($2_OBJ): $1/%.
$2
180 $$($2_C) -I
$1 $$(filter-out -l
%,$$($2_FLAGS)) $$< -c
-o
$$@
183 # LANG_LIB_PARENT_BUILDRULE######################################################
184 # define rules for creating unknown libraries in the local build environment for
185 # either binary or bytecode representation given a library name and, optionally,
186 # a parent lib that included it for error reporting #############################
187 define LANG_LIB_PARENT_BUILDRULE
=
188 # Construct the download method, or error if there isn't a valid one
190 $(if
$($2_GITADDR),$(eval LIBDL
:= git clone
$($2_GITADDR) $2),\
191 $(if
$($2_HGADDR),$(eval LIBDL
:= hg clone
$($2_HGADDR) $2),\
192 $(if
$($2_CVSADDR),$(shell echo
$($2_CVSPASS) > ~
/.cvspass
)\
193 $(eval LIBDL
:= export CVSROOT
=$($2_CVSADDR) && cvs
$($2_CVSGET)),\
194 $(if
$($2_WEBADDR),$(eval LIBDL
:= wget
-O
$($2_WEBTARG) $($2_WEBADDR)),\
195 $(eval
$(error No way to download
$2 needed by
$3 for
$1 language
))))))
199 cd
$(LIBDL_DIR
) && $(LIBDL
) $(if
$($2_WEBINIT),&& $($2_WEBINIT))
200 # '$2' Make and install rule
201 $(LIB_DIR
)/lib
$2.so
: $(LIBDL_DIR
)/$2 $($2_LIBDEPS:%=$(LIB_DIR
)/lib
%.so
)
203 cd
$(LIBDL_DIR
)/$2 && $($2_MAKE)
204 cd
$(LIBDL_DIR
)/$2 && $($2_INSTALL)
206 # each library generated will be included in the libs available list, removed
207 # from the missing libs list, and passed by -l'libname' to the language flags
208 $(foreach lang
,$(LIBLANGS
),\
209 $(eval
$(lang
)_LIBS_AVAILABLE
+= $2)\
210 $(eval
$(lang
)_MISSING_LIBS
:= $(filter-out $2,$($(lang
)_MISSING_LIBS
)))\
211 $(eval
$(lang
)_SOBS
+= $(2:%=$(LIB_DIR
)/lib
%.so
))\
213 #/LANG_LIB_PARENT_BUILDRULE######################################################
217 #LANG_LIB_INIT###################################################################
218 # For every library in this language, we need to determine how, and if, to link
219 # its data. This could potentially be done recursively as libraries have
220 # dependencies upon each other. We call a recursive macro here to expand out
221 # all the libs for this language, and then flatten the tree by removing
222 # duplicates with shell commands. We can't use make's 'sort' function because
223 # we actually care about the ordering so that interdependent libraries can link
224 # back up the tree ##############################################################
225 define LANG_LIB_INIT
=
226 # Implicit defaults for lib.mk files
227 $(eval AUTOGEN
:= .
/autogen.sh
)
228 $(eval CONFIGURE
:= .
/configure
)
229 $(eval CONFIGURE
+= --prefix=$(abspath
$(LIB_DIR
)/.trash
))
230 $(eval CONFIGURE
+= --includedir=$(abspath
$(LIBINC_DIR
)))
231 $(eval CONFIGURE
+= --libdir=$(abspath
$(LIB_DIR
)))
232 $(eval MKCMD
:= make
-k
)
233 $(eval MKINSTALL
:= make
-k
install)
234 $(eval MKCLEAN
:= make
clean)
236 # One of these must be defined in the .mk file imported
237 $(foreach var
, GITADDR WEBADDR HGADDR CVSADDR
,\
238 $(eval undefine
$(var
)))
239 # Other, optional, variables to clear
240 $(eval undefine WEBTARG
) # File 'WEBADDR' is stored in
241 $(eval undefine WEBINIT
) # Method of extracting 'WEBTARG', if necessary
242 $(eval undefine CVSGET
) # checkout command
243 $(eval undefine CVSPASS
) # auto-put into ~/.cvspass to login automatically
244 # Sneaky expansion to call a file inclusion with unelevated (eval'ed) text
246 $(eval
include $2.mk
)
248 # If there is a .mk file for this lib, import it. Otherwise, we'll try to link
249 # it later, unless there is no linker for the language's compiler.
250 $(if
$(wildcard $2.mk
),$(eval
$(call EVALINCLUDE
)),\
252 $(warning No
$2.mk for lib
$2, linking from the system
),\
253 $(error No
$2.mk for lib
$2 and
'$($1_C)' is not configured for linking
)))
255 # Store lib specific build info
256 $(foreach var
,GITADDR WEBADDR HGADDR CVSADDR WEBTARG WEBINIT CVSGET CVSPASS
,\
257 $(eval
$2_$(var
) := $($(var
))))
258 $(eval
$2_MAKE := $(if
$(AUTOGEN
),$(AUTOGEN
) && ))
259 $(eval
$2_MAKE += $(if
$(CONFIGURE
),$(CONFIGURE
) && ))
261 $(eval
$2_MAKE += $(MKCMD
)),\
262 $(error
$2.mk requires a valid MKCMD definition
))
264 $(eval
$2_INSTALL := $(MKINSTALL
)),\
265 $(error
$2.mk requires a valid MKINSTALL definition
))
266 $(eval
$2_CLEAN := $(MKCLEAN
))
267 $(eval
$2_DEPS := $(LIBDEPS
))
269 # Add dependencies we found (potentially recursively, ignore repeats until later
270 # so we can preserve ordering information for interdependency build ordering)
271 $(if
$(LIBDEPS
),$(eval
$1_LIBS += $(LIBDEPS
)))
273 # languages that can link this library (typically any lang that shares the
274 # object format (ELF by default, but this is compiler-dependent (e.g. emcc uses
275 # llvm bitcode, which is just an intermediary representation of data that can
276 # link to anything else statically, or as an archive)))
277 $(eval LIBLANGS ?
= c
cpp go
)
278 $(if
$(findstring $1,$(LIBLANGS
)),,\
279 $(error
$2.mk must specifically set LIBLANGS
:= $1 if it is compatible with
$1))
281 # Mark this lib as available for all its other compatible languages. This list
282 # is assumed to be sorted later, so we will ignore repeats for speed
283 $(foreach lang
,$(LIBLANGS
),\
284 $(eval
$(lang
)_LD_LIBS
+= $2)\
285 $(if
$(findstring $2,$($(lang
)_LIBS
)),,\
286 $(eval
$(lang
)_LIBS
+= $2)\
289 # Recurse this function for every libdep encountered
290 $(foreach libdep
,$(LIBDEPS
),\
291 $(eval
$(call LANG_LIB_INIT
,$1,$(libdep
)))\
293 #/LIB_INIT#######################################################################
296 # Initialize data for supported lanaguages ######################################
298 # Initialize all relevant (although not necessarily used) language relative data
299 $(eval
$1_OBJ := $($($1_C)_OBJ
))
300 $(eval
$1_OUT := $($($1_C)_OUT
))
301 $(eval
$1_SOURCES := $(subst .
/,,$(shell find
-name
"*.$1")))
302 # For each source language that can compile to this language, add the expected
303 # result of such a transformation to the sources of this language. Then, if the
304 # source-to-source compiler also creates duplicate files of other formats
305 # (i.e. yacc/bison also produce accompanying .h files), add them to the list of
306 # source files that this makefile can generate ('$1_MKSRC'), to later be deleted
307 # during the clean rule.
308 $(info $1_SOURCES: $($1_SOURCES))
309 $(foreach srcl
,$($1_SRCL),\
310 $(eval
$1_MKSRC := $(shell find
-name
"*.$(srcl)" \
311 | sed
-e
's@^\(.*\).$(srcl)@\1$($(srcl)_STEM:%=.%).$1@g' -e
's@\./@@'))\
312 $(eval
$1_SOURCES += $($1_MKSRC))\
313 $(eval
$(srcl
)_SOURCES
+= $($1_MKSRC))\
314 $(if
$($(srcl
)_DUP
),\
315 $(eval
$1_SOURCES += $($1_MKSRC:%.
$1=%.
$($(srcl
)_DUP
))))\
317 $(info $1_SOURCES: $($1_SOURCES))
319 $(eval
$1_DRV_SRC := $(filter $(DRIVER_DIR
)/%,$($1_SOURCES)))
320 $(eval DRV_SRC
+= $($1_DRV_SRC))
321 $(eval
$1_DRV_TRG := $(patsubst %.
$1,$(ROOT_DIR
)/%$($1_OUT),$($1_DRV_SRC)))
322 $(eval DRV_TRG
+= $($1_DRV_TRG))
323 $(eval
$1_MOD_SRC := $(filter-out $(DRIVER_DIR
)/%,$($1_SOURCES)))
324 $(eval MOD_SRC
+= $($1_MOD_SRC))
325 $(eval
$1_MOD_TRG := $(subst .
$1,.
$($1_OBJ),$($1_MOD_SRC)))
326 $(eval MOD_TRG
+= $($1_MOD_TRG))
327 # Generate a list of shared object files this language has access to
328 $(eval
$1_SOBS := $(wildcard $(LIB_DIR
)/lib
*.so
))
330 # First, initialize language-module, module, and language relative data. Then,
331 # filter out each source file from the module and generate Dependency Rules and
332 # Module Object Rules for each of them.
333 $(foreach module
,$(MODULES
),\
334 $(eval
$(module
)_
$1_SRC := $(filter $(module
)/%,$($1_MOD_SRC)))\
335 $(eval
$(module
)_
$1_TRG := $(filter $(module
)/%,$($1_MOD_TRG)))\
336 $(eval
$(module
)_SRC
+= $(module
)_
$1_SRC)\
337 $(eval
$(module
)_TRG
+= $($(module
)_
$1_TRG))\
338 $(eval
$1_TRG += $($(module
)_TRG
))\
340 $(foreach src
,$(filter $(module
)/%,$($1_MOD_SRC)),\
341 $(eval
$(call SRC_LANG_DEPRULE
,$(src
),$1,$(module
)))\
342 $(eval
$(call MODULE_LANG_OBJRULE
,$(module
),$1)))\
344 $(eval
$1_TARGETS += $($1_TRG))
346 # For all the listed libraries, initialize the library, which will set up all of
347 # its local ($1_LIBS, etc) data
348 $(foreach lib
,$($1_LIBS),\
349 $(call LANG_LIB_INIT
,$1,$(lib
)))
350 # The initializer produces an ordered list of interlinked dependencies in groups
351 # by depth, so when reading backwards and compressing repeats we are left with a
352 # reliable build order for library interlinking. this awk program just looks
353 # through a list of space separated words backwards failing to print when it
354 # encounters a repeat (which is suitable as a kind of ad-hoc make lisp function)
355 $(eval
$1_LIBS := $(shell echo
"$($1_LIBS)" | awk \
358 printf (!a[$$i]++) ? $$i FS : "";
364 # If our compiler has a linker, find its list of available links and start
365 # adding '-lLIBNAME' options to the language flags if the linker has them, or to
366 # the list of missing libraries for this language if the linker cannot find them.
367 # If this compiler has no linker, mark all libs as missing
368 $(eval
$1_LD_LIBS := $(sort $1_LD_LIBS))
370 $(eval
$1_LD_LIBS := $($($1_C)_LDLIBS
) $($1_SOBS:$(LIB_DIR
)/lib
%.so
=%))\
371 $(foreach lib
,$($1_LIBS),\
372 $(if
$(findstring $(lib
),$($1_LD_LIBS)),\
373 $(eval
$1_FLAGS += -l
$(lib
)),\
374 $(eval
$1_MISSING_LIBS += $(lib
))\
375 $(eval
$1_FLAGS += $(LIB_DIR
)/lib
$(lib
).so
)\
377 $(eval
$1_MISSING_LIBS := $(filter-out $($1_LD_LIBS),$($1_LIBS)))\
379 #/LANG_INIT######################################################################
382 # Initialize each language and look for its files
383 $(foreach lang
,$(LANGS
),\
384 $(eval
$(call LANG_INIT
,$(lang
))))
386 # Generate rules for making missing libs
387 $(foreach lang
,$(LANGS
),\
388 $(foreach mlib
,$($(lang
)_MISSING_LIBS
),\
389 $(eval
$(call LANG_LIB_PARENT_BUILDRULE
,$(lang
),$(mlib
),root
))\
392 # Create module object rules for each module and scan for interpreted code
393 # sources (yacc, etc)
394 $(foreach module
,$(MODULES
),\
395 $(eval
$(call MODULE_ARCRULE
,$(module
)))\
396 $(eval YS
+= $(wildcard $(module
)/*.y
))\
399 # Create lang-specific rules for producing final (linked) targets
400 $(foreach lang
,$(LANGS
),\
401 $(foreach drvsrc
,$($(lang
)_DRV_SRC
),\
402 $(eval
$(call SRC_LANG_DEPRULE
,$(drvsrc
),$(lang
)))\
403 $(eval
$(call SRC_LANG_LINKRULE
,$(drvsrc
),$(lang
)))\
406 # Make any dirs that we're in charge of
410 # Create driver rules for each driver we found, let users call make specifying
411 # the basename of the driver, and just route to the correct linkrule we made in
413 DRV_FNAMES
:= $(notdir $(DRV_SRC
))
414 .PHONY
: all $(basename $(DRV_NAMES
))
415 $(foreach fname
,$(DRV_FNAMES
),\
416 $(eval
$(basename $(fname
)): \
417 $(filter %/$(basename $(fname
))$($(lastword
$(subst .
, ,$(fname
))_OUT
)),$(DRV_TRG
)))\
419 all: $(basename $(DRV_FNAMES
))
422 # Source-to-source build rules
423 # Compilers that don't specify an output location can be made to change
424 # directories with 'cd' to the target file's directory, update the command line
425 # source files, and allow the compiler to output in its default current working
426 # directory 'cwd'. this is done by setting the $1_CHDIR flag to 't'
427 define SRCLANG_TRGLANG_BUILDRULE
=
428 $(info SRCLANG_TRGLANG_BUILDRULE
$1 $2)
429 %$($1_STEM:%=.
%).
$2$(if
$($1_DUP), %$($1_STEM:%=.
%).
$($1_DUP)): %.
$1
430 $(if
$($1_CHDIR),cd
$$(shell dirname
$$@
) && )$$($1_C) $$($1_FLAGS)$(if
$($1_CHDIR),\
431 $$(shell echo
$$@ | sed
-e
's@^.*/\([^\.]*\).*@\1@').
$1,\
434 $(foreach lang
,$(LANGS
),$(if
$($(lang
)_SRCL
),$(foreach srcl
,$($(lang
)_SRCL
),\
435 $(eval
$(call SRCLANG_TRGLANG_BUILDRULE
,$(srcl
),$(lang
))))))
438 rm -f
$(foreach lang
,$(LANGS
),$($(lang
)_TARGETS
) $($(lang
)_MKSRC
))