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
113 $(eval
$3_DEP := $(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 $(eval
$3_LDO := $(patsubst %,%.
$($($2_C)_AROBJ
),$(sort $($3_ILDO) $($3_MLDO))))
130 # If we have any libs to generate, make sure their specific shared object file
131 # is available, and that the linker is linking to our expected link directory
132 # for our compiler and architecture (transpilation magic)
133 $(eval
$3_SRC_OBJS := $(filter-out $1 %.h
,$($3_DEP) $($3_LDO)))
134 # If the compiler supports linking, filter any shared objects out of the list
135 # that can be linked (the -l command option should already be in $2_FLAGS)
136 # otherwise, explicitly include all shared objects in the source objects, while
137 # also putting them on the dep list
139 $(eval
$3_SOB := $(filter-out $($2_LD_LIBS),$($2_LIBS))),\
140 $(eval
$3_SOB := $($2_LIBS:%=$(LIB_DIR
)/lib
%.so
))\
141 $(eval
$3_SRC_OBJS += $($3_SOB)))
142 $(eval
$3_SOB += $(filter $(LIB_DIR
)/lib
%.so
,$($2_FLAGS)))
144 $(eval
$3_DIR := $(dir $(1:%.
$2=$(ROOT_DIR
)/%)))
145 $(eval MAKE_DIRS
+= $($3_DIR))
146 # Commandline setup. The recursive library dependecy traversal constructs a
147 # tree ordered by nested dependency depth. when linking, this order is reversed
148 # Establish the link tree:
149 $(eval LINK_TREE
:= $(filter -l
% %.so
,$($2_FLAGS)) $($3_DEP) $($3_LDO) $1)
150 $(eval LINK_TREE
:= $(shell echo
$(filter-out %.h
,$(LINK_TREE
)) | awk \
152 BEGIN { OFS = " "; ORS = " " }
153 { for (i=NF; i>1; i--)
159 $($3_DIR)$3$($2_OUT): $($3_SOB) $($3_LDO) $($3_DEP) |
$($3_DIR)
160 $$($2_C) $$(filter-out $(LINK_TREE
),$($2_FLAGS)) $(LINK_TREE
) -o
$$@
161 #/SRC_LANG_LINKRULE##############################################################
164 # generate rule for turning an entire module's collection of binary objects into
165 # a single locally-linked (no external -L libs) object (for simplified linking
166 # modules as static libs).
167 define MODULE_ARCRULE
=
168 $(eval
$1_ARCDEPS := $(filter $1/%,$(foreach lang
,$(LANGS
),$($(lang
)_MOD_TRG
))))
169 $1.
$($(c_C
)_AROBJ
): $($1_ARCDEPS) |
$(LCLLIB_DIR
)
170 $($(c_C
)_AR
) cr
$$@
$$^
173 # define an object creation rule given a module and language
174 define MODULE_LANG_OBJRULE
=
175 $1/%.
$($2_OBJ): $1/%.
$2
176 $$($2_C) -I
$1 $$(filter-out -l
%,$$($2_FLAGS)) $$< -c
-o
$$@
179 # LANG_LIB_PARENT_BUILDRULE######################################################
180 # define rules for creating unknown libraries in the local build environment for
181 # either binary or bytecode representation given a library name and, optionally,
182 # a parent lib that included it for error reporting #############################
183 define LANG_LIB_PARENT_BUILDRULE
=
184 # Construct the download method, or error if there isn't a valid one
186 $(if
$($2_GITADDR),$(eval LIBDL
:= git clone
$($2_GITADDR) $2),\
187 $(if
$($2_HGADDR),$(eval LIBDL
:= hg clone
$($2_HGADDR) $2),\
188 $(if
$($2_CVSADDR),$(shell echo
$($2_CVSPASS) > ~
/.cvspass
)\
189 $(eval LIBDL
:= export CVSROOT
=$($2_CVSADDR) && cvs
$($2_CVSGET)),\
190 $(if
$($2_WEBADDR),$(eval LIBDL
:= wget
-O
$($2_WEBTARG) $($2_WEBADDR)),\
191 $(eval
$(error No way to download
$2 needed by
$3 for
$1 language
))))))
195 cd
$(LIBDL_DIR
) && $(LIBDL
) $(if
$($2_WEBINIT),&& $($2_WEBINIT))
196 # '$2' Make and install rule
197 $(LIB_DIR
)/lib
$2.so
: $(LIBDL_DIR
)/$2 $($2_LIBDEPS:%=$(LIB_DIR
)/lib
%.so
)
199 cd
$(LIBDL_DIR
)/$2 && $($2_MAKE)
200 cd
$(LIBDL_DIR
)/$2 && $($2_INSTALL)
202 # each library generated will be included in the libs available list, removed
203 # from the missing libs list, and passed by -l'libname' to the language flags
204 $(foreach lang
,$(LIBLANGS
),\
205 $(eval
$(lang
)_LIBS_AVAILABLE
+= $2)\
206 $(eval
$(lang
)_MISSING_LIBS
:= $(filter-out $2,$($(lang
)_MISSING_LIBS
)))\
207 $(eval
$(lang
)_SOBS
+= $(2:%=$(LIB_DIR
)/lib
%.so
))\
209 #/LANG_LIB_PARENT_BUILDRULE######################################################
213 #LANG_LIB_INIT###################################################################
214 # For every library in this language, we need to determine how, and if, to link
215 # its data. This could potentially be done recursively as libraries have
216 # dependencies upon each other. We call a recursive macro here to expand out
217 # all the libs for this language, and then flatten the tree by removing
218 # duplicates with shell commands. We can't use make's 'sort' function because
219 # we actually care about the ordering so that interdependent libraries can link
220 # back up the tree ##############################################################
221 define LANG_LIB_INIT
=
222 # Implicit defaults for lib.mk files
223 $(eval AUTOGEN
:= .
/autogen.sh
)
224 $(eval CONFIGURE
:= .
/configure
)
225 $(eval CONFIGURE
+= --prefix=$(abspath
$(LIB_DIR
)/.trash
))
226 $(eval CONFIGURE
+= --includedir=$(abspath
$(LIBINC_DIR
)))
227 $(eval CONFIGURE
+= --libdir=$(abspath
$(LIB_DIR
)))
228 $(eval MKCMD
:= make
-k
)
229 $(eval MKINSTALL
:= make
-k
install)
230 $(eval MKCLEAN
:= make
clean)
232 # One of these must be defined in the .mk file imported
233 $(foreach var
, GITADDR WEBADDR HGADDR CVSADDR
,\
234 $(eval undefine
$(var
)))
235 # Other, optional, variables to clear
236 $(eval undefine WEBTARG
) # File 'WEBADDR' is stored in
237 $(eval undefine WEBINIT
) # Method of extracting 'WEBTARG', if necessary
238 $(eval undefine CVSGET
) # checkout command
239 $(eval undefine CVSPASS
) # auto-put into ~/.cvspass to login automatically
240 # Sneaky expansion to call a file inclusion with unelevated (eval'ed) text
242 $(eval
include $2.mk
)
244 # If there is a .mk file for this lib, import it. Otherwise, we'll try to link
245 # it later, unless there is no linker for the language's compiler.
246 $(if
$(wildcard $2.mk
),$(eval
$(call EVALINCLUDE
)),\
248 $(warning No
$2.mk for lib
$2, linking from the system
),\
249 $(error No
$2.mk for lib
$2 and
'$($1_C)' is not configured for linking
)))
251 # Store lib specific build info
252 $(foreach var
,GITADDR WEBADDR HGADDR CVSADDR WEBTARG WEBINIT CVSGET CVSPASS
,\
253 $(eval
$2_$(var
) := $($(var
))))
254 $(eval
$2_MAKE := $(if
$(AUTOGEN
),$(AUTOGEN
) && ))
255 $(eval
$2_MAKE += $(if
$(CONFIGURE
),$(CONFIGURE
) && ))
257 $(eval
$2_MAKE += $(MKCMD
)),\
258 $(error
$2.mk requires a valid MKCMD definition
))
260 $(eval
$2_INSTALL := $(MKINSTALL
)),\
261 $(error
$2.mk requires a valid MKINSTALL definition
))
262 $(eval
$2_CLEAN := $(MKCLEAN
))
263 $(eval
$2_DEPS := $(LIBDEPS
))
265 # Add dependencies we found (potentially recursively, ignore repeats until later
266 # so we can preserve ordering information for interdependency build ordering)
267 $(if
$(LIBDEPS
),$(eval
$1_LIBS += $(LIBDEPS
)))
269 # languages that can link this library (typically any lang that shares the
270 # object format (ELF by default, but this is compiler-dependent (e.g. emcc uses
271 # llvm bitcode, which is just an intermediary representation of data that can
272 # link to anything else statically, or as an archive)))
273 $(eval LIBLANGS ?
= c
cpp go
)
274 $(if
$(findstring $1,$(LIBLANGS
)),,\
275 $(error
$2.mk must specifically set LIBLANGS
:= $1 if it is compatible with
$1))
277 # Mark this lib as available for all its other compatible languages. This list
278 # is assumed to be sorted later, so we will ignore repeats for speed
279 $(foreach lang
,$(LIBLANGS
),\
280 $(eval
$(lang
)_LD_LIBS
+= $2)\
281 $(if
$(findstring $2,$($(lang
)_LIBS
)),,\
282 $(eval
$(lang
)_LIBS
+= $2)\
285 # Recurse this function for every libdep encountered
286 $(foreach libdep
,$(LIBDEPS
),\
287 $(eval
$(call LANG_LIB_INIT
,$1,$(libdep
)))\
289 #/LIB_INIT#######################################################################
292 # Initialize data for supported lanaguages ######################################
294 # Initialize all relevant (although not necessarily used) language relative data
295 $(eval
$1_OBJ := $($($1_C)_OBJ
))
296 $(eval
$1_OUT := $($($1_C)_OUT
))
297 $(eval
$1_SOURCES := $(subst .
/,,$(shell find
-name
"*.$1")))
298 # For each source language that can compile to this language, add the expected
299 # result of such a transformation to the sources of this language. Then, if the
300 # source-to-source compiler also creates duplicate files of other formats
301 # (i.e. yacc/bison also produce accompanying .h files), add them to the list of
302 # source files that this makefile can generate ('$1_MKSRC'), to later be deleted
303 # during the clean rule.
304 $(foreach srcl
,$($1_SRCL),\
305 $(eval
$1_MKSRC := $(shell find
-name
"*.$(srcl)" \
306 | sed
-e
's@^\(.*\).$(srcl)@\1$($(srcl)_STEM:%=.%).$1@g' -e
's@\./@@'))\
307 $(eval
$1_SOURCES += $($1_MKSRC))\
308 $(eval
$(srcl
)_SOURCES
+= $($1_MKSRC))\
309 $(if
$($(srcl
)_DUP
),\
310 $(eval
$1_SOURCES += $($1_MKSRC:%.
$1=%.
$($(srcl
)_DUP
))))\
313 $(eval
$1_DRV_SRC := $(filter $(DRIVER_DIR
)/%,$($1_SOURCES)))
314 $(eval DRV_SRC
+= $($1_DRV_SRC))
315 $(eval
$1_DRV_TRG := $(patsubst %.
$1,$(ROOT_DIR
)/%$($1_OUT),$($1_DRV_SRC)))
316 $(eval DRV_TRG
+= $($1_DRV_TRG))
317 $(eval
$1_MOD_SRC := $(filter-out $(DRIVER_DIR
)/%,$($1_SOURCES)))
318 $(eval MOD_SRC
+= $($1_MOD_SRC))
319 $(eval
$1_MOD_TRG := $(subst .
$1,.
$($1_OBJ),$($1_MOD_SRC)))
320 $(eval MOD_TRG
+= $($1_MOD_TRG))
321 # Generate a list of shared object files this language has access to
322 $(eval
$1_SOBS := $(wildcard $(LIB_DIR
)/lib
*.so
))
324 # First, initialize language-module, module, and language relative data. Then,
325 # filter out each source file from the module and generate Dependency Rules and
326 # Module Object Rules for each of them.
327 $(foreach module
,$(MODULES
),\
328 $(eval
$(module
)_
$1_SRC := $(filter $(module
)/%,$($1_MOD_SRC)))\
329 $(eval
$(module
)_
$1_TRG := $(filter $(module
)/%,$($1_MOD_TRG)))\
330 $(eval
$(module
)_SRC
+= $(module
)_
$1_SRC)\
331 $(eval
$(module
)_TRG
+= $($(module
)_
$1_TRG))\
332 $(eval
$1_TRG += $($(module
)_TRG
))\
334 $(foreach src
,$(filter $(module
)/%,$($1_MOD_SRC)),\
335 $(eval
$(call SRC_LANG_DEPRULE
,$(src
),$1,$(module
)))\
336 $(eval
$(call MODULE_LANG_OBJRULE
,$(module
),$1)))\
338 $(eval
$1_TARGETS += $($1_TRG))
340 # For all the listed libraries, initialize the library, which will set up all of
341 # its local ($1_LIBS, etc) data
342 $(foreach lib
,$($1_LIBS),\
343 $(call LANG_LIB_INIT
,$1,$(lib
)))
344 # The initializer produces an ordered list of interlinked dependencies in groups
345 # by depth, so when reading backwards and compressing repeats we are left with a
346 # reliable build order for library interlinking. this awk program just looks
347 # through a list of space separated words backwards failing to print when it
348 # encounters a repeat (which is suitable as a kind of ad-hoc make lisp function)
349 $(eval
$1_LIBS := $(shell echo
"$($1_LIBS)" | awk \
352 printf (!a[$$i]++) ? $$i FS : "";
358 # If our compiler has a linker, find its list of available links and start
359 # adding '-lLIBNAME' options to the language flags if the linker has them, or to
360 # the list of missing libraries for this language if the linker cannot find them.
361 # If this compiler has no linker, mark all libs as missing
362 # $($1_SOBS:$(LIB_DIR)/lib%.so=%))
363 $(eval
$1_LD_LIBS := $(sort $1_LD_LIBS))
365 $(eval
$1_LD_LIBS := $($($1_C)_LDLIBS
))\
366 $(foreach lib
,$($1_LIBS),\
367 $(if
$(findstring $(lib
),$($1_LD_LIBS)),\
368 $(eval
$1_FLAGS += -l
$(lib
)),\
369 $(eval
$1_MISSING_LIBS += $(lib
))\
370 $(eval
$1_FLAGS += $(LIB_DIR
)/lib
$(lib
).a
)\
372 $(eval
$1_MISSING_LIBS := $(filter-out $($1_LD_LIBS),$($1_LIBS)))\
374 #/LANG_INIT######################################################################
377 # Initialize each language and look for its files
378 $(foreach lang
,$(LANGS
),\
379 $(eval
$(call LANG_INIT
,$(lang
))))
381 # Generate rules for making missing libs
382 $(foreach lang
,$(LANGS
),\
383 $(foreach mlib
,$($(lang
)_MISSING_LIBS
),\
384 $(eval
$(call LANG_LIB_PARENT_BUILDRULE
,$(lang
),$(mlib
),root
))\
387 # Create module object rules for each module and scan for interpreted code
388 # sources (yacc, etc)
389 $(foreach module
,$(MODULES
),\
390 $(eval
$(call MODULE_ARCRULE
,$(module
)))\
391 $(eval YS
+= $(wildcard $(module
)/*.y
))\
394 # Create lang-specific rules for producing final (linked) targets
395 $(foreach lang
,$(LANGS
),\
396 $(foreach drvsrc
,$($(lang
)_DRV_SRC
),\
397 $(eval
$(call SRC_LANG_DEPRULE
,$(drvsrc
),$(lang
)))\
398 $(eval
$(call SRC_LANG_LINKRULE
,$(drvsrc
),$(lang
)))\
401 # Make any dirs that we're in charge of
405 # Create driver rules for each driver we found, let users call make specifying
406 # the basename of the driver, and just route to the correct linkrule we made in
408 DRV_FNAMES
:= $(notdir $(DRV_SRC
))
409 .PHONY
: all $(basename $(DRV_NAMES
))
410 $(foreach fname
,$(DRV_FNAMES
),\
411 $(eval
$(basename $(fname
)): \
412 $(filter %/$(basename $(fname
))$($(lastword
$(subst .
, ,$(fname
))_OUT
)),$(DRV_TRG
)))\
414 all: $(basename $(DRV_FNAMES
))
417 # Source-to-source build rules
418 # Compilers that don't specify an output location can be made to change
419 # directories with 'cd' to the target file's directory, update the command line
420 # source files, and allow the compiler to output in its default current working
421 # directory 'cwd'. this is done by setting the $1_CHDIR flag to 't'
422 define SRCLANG_TRGLANG_BUILDRULE
=
423 %$($1_STEM:%=.
%).
$2$(if
$($1_DUP), %$($1_STEM:%=.
%).
$($1_DUP)): %.
$1
424 $(if
$($1_CHDIR),cd
$$(shell dirname
$$@
) && )$$($1_C) $$($1_FLAGS)$(if
$($1_CHDIR),\
425 $$(shell echo
$$@ | sed
-e
's@^.*/\([^\.]*\).*@\1@').
$1,\
428 $(foreach lang
,$(LANGS
),$(if
$($(lang
)_SRCL
),$(foreach srcl
,$($(lang
)_SRCL
),\
429 $(eval
$(call SRCLANG_TRGLANG_BUILDRULE
,$(srcl
),$(lang
))))))
432 rm -f
$(foreach lang
,$(LANGS
),$($(lang
)_TARGETS
) $($(lang
)_MKSRC
))