makefile fixes, moved the linking subsystem to driver-rule-reckoning
[henge/webcc.git] / make / lib / rules.mk
1 ################################################################################
2 # Desc: Make functions for creating rules (Make making Make)
3 # Author: Ken Grimes
4 # Date: 2016
5 ################################################################################
6 # This file consists of library functions for make-making-make functionality
7 ################################################################################
8
9 # A long function definition to grab deps from the source's compiler and route
10 # errors to /dev/null if the rule generator expresses bogus errors
11 define SRC_LANG_FLAGS_OPT_DEPS =
12 $(filter-out \ %:,$(shell $($2_C) $3 $4 -MG $1 2> /dev/null))
13 endef
14
15 #SRC_LANG_RULE###################################################################
16 # Given a source and a language, generate a rule to make the object. The second
17 # invocation of gcc per file lists only its local includes, and filters out
18 # anything already caught. Anything remaining should be considered to be
19 # available in the current working directory of the source file(i.e. if you
20 # '#include "something.h"' in 'mymodule/horseradish.c', you would expect to
21 # include 'mymodule/something.h'. this check lets that happen), otherwise it is
22 # some kind of user error #######################################################
23 define SRC_LANG_RULE =
24 $(if $($1),,$(eval $1 := t)\
25 $(eval MOD := $(filter $(MODULES),$(firstword $(subst /, ,$(subst $(SRC_DIR),,$(dir $1))))))\
26 $(eval FLG := $(MOD:%=-I$(SRC_DIR)/% ))\
27 $(if $(wildcard $1),
28 $(eval DEPS := $(filter-out \ %:,$(shell $($2_C) $(FLG) -M -MG $1)))\
29 $(eval DDEPS := $(filter $(DRIVER_DIR:$(SRC_DIR)/%=%/%),$(DEPS)))\
30 $(eval MDEPS := $(filter $(MODULES:%=%/%),$(DEPS)))\
31 $(eval DEPS := $(filter-out $(DDEPS) $(MDEPS),$(DEPS)))\
32 $(eval MDEPS := $(MDEPS:%=$(SRC_DIR)/%))\
33 $(eval DDEPS := $(DDEPS:%=$(SRC_DIR)/%))\
34 $(eval LOST := $(filter-out \ %: $(DRIVER_DIR:./%=%)/% $(MODULES:%=$(SRC_DIR:./%=%)/%/%),$(shell $($2_C) $(FLG) -MM -MG $1)))\
35 $(eval MDEPS += $(LOST:%=$(dir $1)%))\
36 $(eval MDEPS := $(shell echo $(MDEPS) | sed -e 's@[a-zA-Z0-9\-\+/]*\.\./include@$(LIBINC_DIR)@g'))\
37 $(eval ALLDEPS := $(DDEPS) $(MDEPS) $(DEPS)),\
38 $(error Cannot generate deps for: $1, file not found))\
39 $(eval SRC_OBJ := $(basename $1).$($2_OBJ))\
40 $(eval MAKE_TARGETS += $(SRC_OBJ))
41 # Assume that lost dependencies are in the folder with the source
42 $(foreach lost,$(LOST),
43 $(eval ALLDEPS := $(subst $(lost), $(dir $1)$(lost),$(ALLDEPS))))
44
45 # Find relative deps in our local LIBINC_DIR, if available. Otherwise they
46 # should be expected in the src directory
47 $(eval RELDEPS := $(filter-out $(SRC_DIR:./%=%)/% ./% /%,$(ALLDEPS)))
48 $(foreach dep,$(RELDEPS),
49 $(eval ALLDEPS := $(subst $(dep),\
50 $(if $(wildcard $(LIBINC_DIR)/$(dep)),$(LIBINC_DIR),$(SRC_DIR))/$(dep),$(ALLDEPS))))
51
52 # Find any deps that aren't built yet, which the compiler has flagged
53 # as missing, but which we know the expected location of libs for
54 # this language should have their includes in 'LIBINC_DIR'
55 $(eval BUILDDEPS := $(filter $($2_LIBS:%=%/%),$(ALLDEPS)))
56 $(eval ALLDEPS := $(filter-out $(BUILDDEPS),$(ALLDEPS)) $(BUILDDEPS:%=$(LIBINC_DIR)/%))
57 # Object for $1
58 $(SRC_OBJ): $(ALLDEPS)
59 $($2_C) $$($2_FLAGS) $(FLG) -c -o $$@ $1
60 $(if $($2_DBG),\
61 $(eval DBG_OBJ := $(dir $1).$($2_DBG)/$(basename $(notdir $1)).$($2_OBJ))\
62 $(if $(findstring $(dir $(DBG_OBJ)),$(MAKE_DIRS)),,\
63 $(eval MAKE_DIRS += $(dir $(DBG_OBJ))))
64 $(eval MAKE_TARGETS += $(DBG_OBJ))
65 # Object for $1 with $($2_DBG) symbols
66 $(DBG_OBJ): $(ALLDEPS) | $(dir $(DBG_OBJ))
67 $($2_C) $$($2_FLAGS) $(FLG) -Og -g$($2_DBG) -c -o $$@ $1
68 ))
69 #/SRC_LANG_RULE##################################################################
70 endef
71
72 #SRC_LANG_DRVRULE################################################################
73 # establish a build and link rule given a source driver and language ############
74 define SRC_LANG_DRVRULE =
75 $(eval DRIVER_NAME := $(basename $(notdir $1)))
76 $(eval DRIVER_TARG := $(1:$(SRC_DIR)/%.$2=$(ROOT_DIR)/%$($2_OUT)))
77 $(eval DRIVER_TARG_D := $(DRIVER_TARG)-d)
78 $(eval DRIVER_SOB := $(basename $1).$($2_OBJ))
79 $(eval DRIVER_SOB_D := $(DRIVER_SOB:$(dir $(DRIVER_SOB))%=$(dir $(DRIVER_SOB)).$($2_DBG)/%))
80
81 # Generate a rule for the driver object
82 $(eval $(call SRC_LANG_RULE,$1,$2))
83
84 # Modules can be implicitly specified by a driver if its dependencies include
85 # ANY files from any module directory. The 'MDEPS' var is set by SRC_LANG_RULE
86 # and expresses the dependencies required to build the object file only.
87 $(foreach module_dep,$(MDEPS),
88 $(eval DRIVER_MODULES += $(firstword $(subst /, ,$(module_dep))))
89 )
90 $(eval DRIVER_MODULES := $(filter-out $(DRIVER_DIR) . ..,$(sort $(DRIVER_MODULES))))
91
92 # Read associated linking data for the driver. If no link data is available,
93 # all libraries are assumed to be statically linked by the build system. files
94 # prefixed with 'test' will first attempt to find a 'ld' file matching its
95 # basename with the 'test' prefix removed (i.e. ./src/bin/testscanner.c will
96 # first attempt to find ./src/bin/scanner.ld for its ld information. if not
97 # available, it will instead use ./src/bin/testscanner.ld. if still not
98 # available, all libraries will attempt static linking - and if those static
99 # libs are not available, the system will attempt to retrieve the source code
100 # and build the static lib
101 $(eval DRIVER_LDFILE := $(1:%.$2=%.ld))
102 $(if $(filter test%,$(DRIVER_NAME)),
103 $(eval DRV_LD := $(subst $(DRIVER_NAME).$2,$(DRIVER_NAME:test%=%).ld,$1))
104 $(if $(wildcard $(DRV_LD)),
105 $(eval DRIVER_LDFILE := $(DRV_LD))
106 ))
107 $(eval $1_LD := $(file <$(DRIVER_LDFILE)))
108 $(eval undefine DRIVER_LDINFO)
109 $(eval undefine DRIVER_LDINFO_D)
110 $(foreach ldobj,$($1_LD),
111 $(if $(filter -l%,$(ldobj)),
112 $(eval ldlibname := $(ldobj:-l%=%))
113 $(if $(filter $(ldlibname),$($($2_C)_LDLIBS)),
114 $(eval DRIVER_LDINFO += $(ldobj))
115 $(eval DRIVER_LDINFO_D += $(ldobj)),
116 $(info Statically linking '$(ldobj:-l%=lib%)', linker '$($($2_C)_LD)' for compiler '$($2_C)' cannot link this lib.)
117 $(if $($(ldlibname)_INIT),,$(call LANG_LIB_INIT,$2,$(ldlibname),$1))
118 $(foreach deplib,$($(ldlibname)_DEPS) $(ldlibname),
119 $(eval DRIVER_LDINFO += $(LIB_DIR)/$(deplib).$($2_AROBJ))
120 $(eval DRIVER_LDINFO_D += $(LIB_DIR)/.$($2_DBG)/$(deplib).$($2_AROBJ)))
121 ),
122 $(if $(findstring $(ldobj),$(MODULES)),
123 $(eval DRIVER_LDINFO += $(SRC_DIR)/$(ldobj).$($2_AROBJ))
124 $(eval DRIVER_LDINFO_D += $(SRC_DIR)/.$($2_DBG)/$(ldobj).$($2_AROBJ)),
125 $(info Resolving static dependencies for '$(ldobj:%=lib%)'.)
126 $(if $($(ldobj)_INIT),,$(call LANG_LIB_INIT,$2,$(ldobj),$1))
127 $(foreach deplib,$($(ldobj)_DEPS) $(ldobj),
128 $(eval DRIVER_LDINFO += $(LIB_DIR)/$(deplib).$($2_AROBJ))
129 $(eval DRIVER_LDINFO_D += $(LIB_DIR)/.$($2_DBG)/$(deplib).$($2_AROBJ)))
130 ))
131 )
132 $(eval DRIVER_LDINFO += $(1:%.$2=%.$($2_OBJ)))
133 $(eval DRIVER_LDINFO_D += $(1:$(dir $1)%.$2=$(dir $1).$($2_DBG)/%.$($2_OBJ)))
134 $(info DRIVER_INFO: $(DRIVER_LDINFO))
135 $(info DRIVER_INFO:: $(DRIVER_LDINFO_D))
136
137 # Directory setup
138 $(eval MAKE_DIRS += $(dir $(DRIVER_TARG)))
139 $(if $($2_DBG),
140 $(eval MAKE_DIRS += $(dir $(DRIVER_TARG_D))))
141
142 # Add the driver target to this language's targets for cleaning
143 $(eval SCRUB_TARGETS += $(DRIVER_TARG) $(DRIVER_TARG_D))
144 $(DRIVER_TARG): $(filter-out -l%,$(DRIVER_LDINFO)) | $(dir $(DRIVER_TARG))
145 $($2_C) $($2_FLAGS) $(DRIVER_LDINFO) -o $$@
146 # Output a rule for building with debug flags
147 $(eval MAKE_DIRS += $(DRIVER_TARG_DIR).$($2_DBG)/)
148 $(DRIVER_TARG_D): $(filter-out -l%,$(DRIVER_LDINFO_D)) | $(dir $(DRIVER_TARG_D))
149 $($2_C) $($2_FLAGS) $(DRIVER_LDINFO_D) -o $$@
150 # Make a rule to run this driver after building
151 $(DRIVER_NAME)-run: $(DRIVER_TARG)
152 $(DRIVER_TARG)
153 $(DRIVER_NAME)-d: $(DRIVER_TARG_D)
154
155 #/SRC_LANG_DRVRULE###############################################################
156 endef
157
158 #MODULE_ARCRULE##################################################################
159 # generate rule for turning an entire module's collection of binary objects into
160 # a single locally-linked (no external -L libs) object (for simplified linking
161 # modules as static libs).#######################################################
162 define MODULE_ARCRULE =
163 $(eval ARCDEPS := $(filter $(SRC_DIR)/$1/%.$(c_OBJ),$(foreach lang,$(LANGS),$($(lang)_MOD_TRG))))\
164 $(eval MAKE_TARGETS+= $(SRC_DIR)/$1.$(c_AROBJ))\
165
166 $(SRC_DIR)/$1.$(c_AROBJ): $(ARCDEPS)
167 $(c_AR) cr $$@ $$^
168 $(if $(c_DBG),
169 $(eval undefine DBGARCDEPS)
170 $(foreach arcdep,$(ARCDEPS),$(eval DBGARCDEPS += $(dir $(arcdep)).$(c_DBG)/$(notdir $(arcdep))))
171 $(eval MAKE_TARGETS+= .$(c_DBG)/$1.$(c_AROBJ))\
172
173 $(SRC_DIR)/.$(c_DBG)/$1.$(c_AROBJ): $(DBGARCDEPS) | .$(c_DBG)/
174 $(c_AR) cr $$@ $$^
175 )
176 #/MODULE_ARCRULE#################################################################
177 endef
178
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 $(eval CONF_FLAG := --prefix=$(abspath $(LIB_DIR)).trash)
185 $(eval CONF_FLAG += --includedir=$(abspath $(LIBINC_DIR)))
186 # Setup installation rule steps for this lib
187 $(eval
188 BUILDSTEPS := $(if $(AUTOGEN),$(AUTOGEN) && )
189 BUILDSTEPS += $(if $(CONFIGURE),$(CONFIGURE) $(CONF_FLAG) --libdir=$(abspath $(LIB_DIR)) && )
190 $(if $(MKCMD),\
191 BUILDSTEPS += $(MKCMD),
192 $(error $2.mk requires a valid MKCMD definition))
193 $(if $(MKINSTALL),\
194 INSTALLCMD := $(MKINSTALL),\
195 $(error $2.mk requires a valid MKINSTALL definition))
196 CLEANCMD := $(MKCLEAN)
197 )
198 # Construct the download method, or error if there isn't a valid one
199 $(if $(GITADDR),$(eval LIBDL := git clone $(GITADDR) $2),\
200 $(if $(HGADDR),$(eval LIBDL := hg clone $(HGADDR) $2),\
201 $(if $(CVSADDR),\
202 $(eval LIBDL := export CVSROOT=$(CVSADDR))\
203 $(eval LIBDL += && echo '$(CVSPASS)' > ~/.cvspass)\
204 $(eval LIBDL += && cvs $($2_CVSGET))
205 $(eval LIBDL += && $(if $(CVSPASS_BAK),echo $(CVSPASS_BAK) > ~/.cvspass,rm ~/.cvspass)),\
206 $(if $(WEBADDR),$(eval LIBDL := wget -O $(WEBTARG) $(WEBADDR)),\
207 $(eval $(error No way to download $2 needed by $3 for $1 language ))))))
208 # '$2' Download Rule
209 $(LIBDL_DIR)/$2:
210 mkdir -p $$@
211 cd $(LIBDL_DIR) && $(LIBDL) $(if $(WEBINIT),&& $(WEBINIT))
212 # '$2' Make and install rule, and ensure it isn't marked "clean"
213 $(LIB_DIR)/lib$2.$($1_AROBJ): $(LIBDEPS:%=$(LIB_DIR)/lib%.$($1_AROBJ)) | $(LIBDL_DIR)/$2
214 @mkdir -p $(LIB_DIR)
215 cd $(LIBDL_DIR)/$2 && export LD_RUN_PATH=$(LIBLDPATH) && $(BUILDSTEPS)
216 cd $(LIBDL_DIR)/$2 && $(INSTALLCMD)
217 @rm -f $(LIBDL_DIR)/$2.clean
218
219 # '$2' Rule for building under debug mode
220 $(if $($1_DBG),\
221 $(eval LIBDBG_DIR := $(LIB_DIR)/.$($1_DBG))\
222 $(eval $(if $(findstring $(LIBDBG_DIR),$(MAKE_DIRS)),,MAKE_DIRS += $(LIBDBG_DIR)))\
223 $(eval LIBDBG_TARG := $(LIBDBG_DIR)/lib$2.$($1_AROBJ))\
224 $(eval LIBDBG_DEPS := $(LIBDEPS:%=$(LIB_DIR)/.$($1_DBG)/lib%.$($1_AROBJ)))\
225 $(eval DBGBUILDSTEPS := $(if $(AUTOGEN),$(if $(AUTOGENDBG),$(AUTOGENDBG),$(AUTOGEN)) && ))\
226 $(eval DBGBUILDSTEPS += $(if $(CONFIGURE),$(if $(CONFIGUREDBG),$(CONFIGUREDBG),$(CONFIGURE)) $(CONF_FLAG) --libdir=$(abspath $(LIB_DIR))/.$($1_DBG)))\
227 $(eval DBGBUILDSTEPS += $(if $(MKDBGCMD),$(MKDBGCDM),$(MKCMD)))\
228
229 # '$2' Make and install rule for debugging
230 $(LIBDBG_TARG): $(LIBDBG_DEPS) | $(LIBDL_DIR)
231 @mkdir -p $(LIBDBG_DIR)
232 cd $(LIBDL_DIR)/$2 && export LD_RUN_PATH=$(LIBLDPATH:$(LIB_DIR)%=$(LIBDBG_DIR)) && $(DBGBUILDSTEPS)
233 cd $(LIBDL_DIR)/$2 && $(INSTALLCMD)
234 @rm -f $(LIBDL_DIR)/$2.clean
235 )
236
237 # '$2' Clean rule - drop a marker in the directory so we don't bother cleaning
238 # things that are clean... by default there is no real way of tracking this in a
239 # makefile
240 clean_$2:
241 $$(if $$(wildcard $(LIBDL_DIR)/$2),$$(if $$(wildcard $(LIBDL_DIR)/$2.clean),,\
242 cd $(LIBDL_DIR)/$2 && $(CLEANCMD) && touch $(abspath $(LIBDL_DIR))/$2.clean))
243
244 #/LANG_LIB_PARENT_BUILDRULE######################################################
245 endef
246
247
248 #LANG_LIB_INIT###################################################################
249 # For every library in this language, we need to determine how, and if, to link
250 # its data. This could potentially be done recursively as libraries have
251 # dependencies upon each other. We call a recursive macro here to expand out
252 # all the libs for this language, and then flatten the tree by removing
253 # duplicates with shell commands. We can't use make's 'sort' function because
254 # we actually care about the ordering so that interdependent libraries can link
255 # back up the tree ##############################################################
256 define LANG_LIB_INIT =
257 # Initialize per-library data, then import data from an accompanying file that
258 # can overwrite these vars
259 $(eval
260 # Implicit defaults for lib.mk files
261 AUTOGEN := ./autogen.sh
262 AUTOGENDBG := ./autogen.sh --enable-debug
263 CONFIGURE := ./configure
264 MKCMD := make -k
265 MKINSTALL := make -k install
266 MKCLEAN := make clean
267 MKDBGCMD := make -k
268 LIBDEPS :=
269 LIBLANGS := c cpp go
270 LIBLDPATH := $(if $($($1_C)_LD),$(if $(LD_LIBRARY_PATH),$(LD_LIBRARY_PATH):,/usr/lib:)$(abspath $(LIB_DIR)))
271 # One of these must be defined in the .mk file imported
272 $(foreach var, GITADDR WEBADDR HGADDR CVSADDR,\
273 $(eval undefine $(var)))
274 # Other, optional, variables to clear
275 $(foreach var, WEBTARG WEBINIT CVSGET CVSPASS CONFIGUREDBG,
276 $(eval undefine $(var)))
277 )
278 # If there is a .mk file for this lib, import it. Otherwise, we'll try to link
279 # it later, unless there is no linker for the language's compiler.
280 $(if $(wildcard $(CONF_DIR)/$2.mk),$(eval $(file <$(CONF_DIR)/$2.mk)),\
281 $(if $($($1_C)_LD),\
282 $(warning No $2.mk for lib$2, attempting to link from the system),\
283 $(error No $2.mk for lib$2 and '$($1_C)' is not configured for linking)))\
284
285 # Add dependencies we found (potentially recursively, ignore repeats until later
286 # so we can preserve ordering information for interdependency build ordering)
287 $(if $(LIBDEPS),\
288 $(eval $1_LIBS += $(LIBDEPS))\
289 $(if $3,$(eval $3_DEPS += $(LIBDEPS)))\
290 $(eval $2_DEPS += $(LIBDEPS))\
291 )
292 # languages that can link this library (typically any lang that shares the
293 # object format (ELF by default, but this is compiler-dependent (e.g. emcc uses
294 # llvm bitcode, which is just an intermediary representation of data that can
295 # link to anything else statically, or as an archive)))
296 $(if $(findstring $1,$(LIBLANGS)),,\
297 $(error $2.mk must specifically set LIBLANGS := $1 if it is compatible with $1))
298 # Mark this lib as available for all its other compatible languages. This list
299 # is assumed to be sorted later, so we will ignore repeats for speed
300 $(foreach lang,$(LIBLANGS),\
301 $(if $(findstring $2,$($(lang)_LIBS)),,\
302 $(eval $(lang)_LIBS += $2)))
303 # Generate a build rule for this lib, unless it's already built
304 $(if $(findstring $2,$(BUILTLIBS)),,\
305 $(eval $(call LANG_LIB_PARENT_BUILDRULE,$1,$2,$3))\
306 $(eval BUILTLIBS += $2))
307 # Recurse this function for every libdep encountered
308 $(foreach libdep,$(LIBDEPS),\
309 $(call LANG_LIB_INIT,$1,$(libdep),$2))
310 $(eval $2_DEPS := $(shell echo "$($2_DEPS)" | $(call AWK_REVERSE_SQUASH)))
311 $(eval $2_INIT := t)
312 #/LANG_LIB_INIT#################################################################
313 endef
314
315 # Initialize data for supported lanaguages ######################################
316 define LANG_INIT =
317 $(eval
318 $1_OBJ := $($($1_C)_OBJ)
319 $1_OUT := $($($1_C)_OUT)
320 $1_AROBJ := $($($1_C)_AROBJ)
321 $1_SOURCES := $(shell find $(SRC_DIR) -name "*.$1" -not -name ".*")
322 $1_DBG := $($($1_C)_DBG)
323 $1_AR := $($($1_C)_AR)
324 )
325 # Find save language-specific driver and module sources and targets
326 $(eval $1_DRV_SRC := $(filter $(DRIVER_DIR)/%,$($1_SOURCES)))
327 $(eval $1_DRV_TRG := $(patsubst $(SRC_DIR)/%.$1,$(ROOT_DIR)/%$($1_OUT),$($1_DRV_SRC)))
328 $(eval $1_MOD_SRC := $(filter-out $(DRIVER_DIR)/%,$($1_SOURCES)))
329 $(eval $1_MOD_TRG := $(addsuffix .$($1_OBJ),$(basename $($1_MOD_SRC))))
330 # Add those sources and targets to a language-irreverant var
331 $(eval
332 DRV_SRC += $($1_DRV_SRC)
333 DRV_TRG += $($1_DRV_TRG)
334 MOD_SRC += $($1_MOD_SRC)
335 MOD_TRG += $($1_MOD_TRG)
336 )\
337 # First, initialize language-module, module, and language relative data. Then,
338 # filter out each source file from the module and generate Dependency Rules and
339 # Module Object Rules for each of them.
340 $(foreach module,$(MODULES),
341 # Evaluate the module sources for this language
342 $(eval $(module)_$1_SRC := $(filter $(SRC_DIR)/$(module)/%,$($1_MOD_SRC)))
343 # Evaluate module targets for this language
344 $(eval $(module)_$1_TRG := $($(module)_$1_SRC:%.$1=%.$($1_OBJ)))
345 # Add these language-specific sources and targets to the module's src/targs
346 $(eval
347 $(module)_SRC += $($(module)_$1_SRC)
348 $(module)_TRG += $($(module)_$1_TRG)
349 )
350 # For every source file, create a build rule for it for our language
351 $(foreach src,$(filter $(SRC_DIR)/$(module)/%,$($1_MOD_SRC)),\
352 $(eval $(call SRC_LANG_RULE,$(src),$1))\
353 ))
354 #/LANG_INIT######################################################################
355 endef