X-Git-Url: https://www.kengrimes.com/gitweb/?p=henge%2Fwebcc.git;a=blobdiff_plain;f=make%2Flib%2Frules.mk;h=2f5944b6bbf745a08f781747e29bb30fd6a0f49c;hp=50ca62bf30200457ae21a23b808e9eba3d02fadd;hb=3d03d8941abeb2a78f3334098a7bd09285023da8;hpb=72c750dfa72953b34e383380ef436127b3cd7d86 diff --git a/make/lib/rules.mk b/make/lib/rules.mk index 50ca62b..2f5944b 100644 --- a/make/lib/rules.mk +++ b/make/lib/rules.mk @@ -1,6 +1,6 @@ ################################################################################ # Desc: Make functions for creating rules (Make making Make) -# Author: Ken Grimes +# Author: ksg # Date: 2016 ################################################################################ # This file consists of library functions for make-making-make functionality @@ -19,7 +19,7 @@ endef # available in the current working directory of the source file(i.e. if you # '#include "something.h"' in 'mymodule/horseradish.c', you would expect to # include 'mymodule/something.h'. this check lets that happen), otherwise it is -# some kind of user error ####################################################### +# some kind of user error ###################################################### define SRC_LANG_RULE = $(if $($1),,$(eval $1 := t)\ $(eval MOD := $(filter $(MODULES),$(firstword $(subst /, ,$(subst $(SRC_DIR),,$(dir $1))))))\ @@ -42,9 +42,16 @@ $(eval MAKE_TARGETS += $(SRC_OBJ)) $(foreach lost,$(LOST), $(eval ALLDEPS := $(subst $(lost), $(dir $1)$(lost),$(ALLDEPS)))) -# Find any deps that aren't built yet, which the compiler has flagged -# as missing, but which we know the expected location of libs for -# this language should have their includes in 'LIBINC_DIR' +# Find relative deps in our local LIBINC_DIR, if available. Otherwise they +# should be expected in the src directory +$(eval RELDEPS := $(filter-out $(SRC_DIR:./%=%)/% ./% /%,$(ALLDEPS))) +$(foreach dep,$(RELDEPS), +$(eval ALLDEPS := $(subst $(dep),\ +$(if $(wildcard $(LIBINC_DIR)/$(dep)),$(LIBINC_DIR),$(SRC_DIR))/$(dep),$(ALLDEPS)))) + +# Find any deps that aren't built yet, which the compiler has flagged as +# missing, but which we know the expected location of libs for this language +# should have their includes in 'LIBINC_DIR' $(eval BUILDDEPS := $(filter $($2_LIBS:%=%/%),$(ALLDEPS))) $(eval ALLDEPS := $(filter-out $(BUILDDEPS),$(ALLDEPS)) $(BUILDDEPS:%=$(LIBINC_DIR)/%)) # Object for $1 @@ -59,15 +66,41 @@ $(eval MAKE_TARGETS += $(DBG_OBJ)) $(DBG_OBJ): $(ALLDEPS) | $(dir $(DBG_OBJ)) $($2_C) $$($2_FLAGS) $(FLG) -Og -g$($2_DBG) -c -o $$@ $1 )) -#/SRC_LANG_RULE################################################################## +#/SRC_LANG_RULE################################################################# +endef + +#LANG_LIB_DEPLIST############################################################### +# Crawl the tree of dependencies for static libraries, terminating at root +# dependencies or any dynamic link ############################################# +define LANG_LIB_DEPLIST = +$(if $($2_INIT),,$(call LANG_LIB_INIT,$1,$2)) +$(foreach dep,$($2_DEPS) +endef + + +define LIB_PARSELINKS = +$( +$(eval DLIBS := $(patsubst +%,%,$(filter +%,$(LINK_ORDER)))) +$(eval SLIBS := $(patsubst -%,%,$(filter -%,$(LINK_ORDER)))) +$(foreach ldobj,$(patsubst +%,%,$(LINK_ORDER:-%=%)), +$(if $(or $(filter $(ldobj),$(DLIBS)),$(and $(filter-out $(SLIBS),$(ldobj)), $(filter $(ldobj),$($($2_C)_LDLIBS)))), +$(eval DRIVER_LDINFO += -l$(ldobj)), +$(if $(filter $(ldobj),$(MODULES)), +$(eval DRIVER_LDINFO += $(SRC_DIR)/$(ldobj).$($2_AROBJ)) +$(eval DRIVER_LDINFO_D += $(SRC_DIR)/.$($2_DBG)/$(ldobj).$($2_AROBJ)), +$(if $($(ldobj)_INIT),,$(call LANG_LIB_INIT,$2,$(ldobj),$1)) +$(foreach dep,$($(ldobj)_DEPS) + endef -#SRC_LANG_DRVRULE################################################################ -# establish a build and link rule given a source driver and language ############ +#SRC_LANG_DRVRULE############################################################### +# establish a build and link rule given a source driver and language ########### define SRC_LANG_DRVRULE = -$(eval DRIVER_NAME := $(basename $(notdir $1))) -$(eval DRIVER_TARG := $(1:$(SRC_DIR)/%.$2=$(ROOT_DIR)/%$($2_OUT))) -$(eval DRIVER_SOB := $(basename $1).$($2_OBJ)) +$(eval DRIVER_NAME := $(basename $(notdir $1))) +$(eval DRIVER_TARG := $(1:$(SRC_DIR)/%.$2=$(ROOT_DIR)/%$($2_OUT))) +$(eval DRIVER_TARG_D := $(DRIVER_TARG)-d) +$(eval DRIVER_SOB := $(basename $1).$($2_OBJ)) +$(eval DRIVER_SOB_D := $(DRIVER_SOB:$(dir $(DRIVER_SOB))%=$(dir $(DRIVER_SOB)).$($2_DBG)/%)) # Generate a rule for the driver object $(eval $(call SRC_LANG_RULE,$1,$2)) @@ -80,75 +113,74 @@ $(eval DRIVER_MODULES += $(firstword $(subst /, ,$(module_dep)))) ) $(eval DRIVER_MODULES := $(filter-out $(DRIVER_DIR) . ..,$(sort $(DRIVER_MODULES)))) -# In the event that the driver developer does not wish to include a header to -# any internal modules, the developer may instead create a file with the same -# basename as the driver, but with the '.ld' suffix, which contains a space -# separated list of internal modules to link together during compilation -$(eval DRIVER_LDVALS := $(file <$(1:%.$2=%.ld))) -$(eval DRIVER_LFLAGS := $(filter -l%,$(DRIVER_LDVALS))) -$(eval DRIVER_MODULES += $(filter-out -l%,$(DRIVER_LDVALS))) -$(eval DRIVER_MODULES := $(sort $(DRIVER_MODULES))) -# List of module archives to link together during compilation -$(eval DRIVER_ARCHIVES := $(DRIVER_MODULES:%=$(SRC_DIR)/%.$($2_AROBJ))) -$(eval DRIVER_DEPS := $(DRIVER_SOB) $(DRIVER_ARCHIVES)) -$(eval DRIVER_DBGARCHIVES := $(DRIVER_MODULES:%=$(SRC_DIR)/.$($2_DBG)/%.$($2_AROBJ))) -$(eval DRIVER_DBGDEPS := $(dir $(DRIVER_SOB)).$($2_DBG)/$(notdir $(DRIVER_SOB))) -$(eval DRIVER_DBGDEPS += $(DRIVER_DBGARCHIVES)) +# Read associated linking data for the driver. If no link data is available, +# all libraries are assumed to be statically linked by the build system. files +# prefixed with 'test' will first attempt to find a 'ld' file matching its +# basename with the 'test' prefix removed (i.e. ./src/bin/testscanner.c will +# first attempt to find ./src/bin/scanner.ld for its ld information. if not +# available, it will instead use ./src/bin/testscanner.ld. if still not +# available, all libraries will attempt static linking - and if those static +# libs are not available, the system will attempt to retrieve the source code +# and build the static lib +$(eval DRIVER_LDFILE := $(1:%.$2=%.mk)) +$(if $(filter test%,$(DRIVER_NAME)), +$(eval DRV_LD := $(subst $(DRIVER_NAME).$2,$(DRIVER_NAME:test%=%).mk,$1)) +$(if $(wildcard $(DRV_LD)), +$(eval DRIVER_LDFILE := $(DRV_LD)) +)) +$(eval undefine LINK_ORDER) +$(eval undefine LINK_ORDER_D) +$(eval $(file <$(DRIVER_LDFILE))) +$(eval undefine DRIVER_LDINFO) +$(eval undefine DRIVER_LDINFO_D) +$(eval DRIVER_LDINFO += $(1:%.$2=%.$($2_OBJ))) +$(eval DRIVER_LDINFO_D += $(1:$(dir $1)%.$2=$(dir $1).$($2_DBG)/%.$($2_OBJ))) +$(eval DLIBS := $(patsubst +%,%,$(filter +%,$(LINK_ORDER)))) +$(eval SLIBS := $(patsubst -%,%,$(filter -%,$(LINK_ORDER)))) +$(foreach ldobj,$(patsubst +%,%,$(LINK_ORDER:-%=%)), +$(if $(or $(filter $(ldobj),$(DLIBS)),$(and $(filter-out $(SLIBS),$(ldobj)), $(filter $(ldobj),$($($2_C)_LDLIBS)))), +$(eval DRIVER_LDINFO += -l$(ldobj)), +$(if $(filter $(ldobj),$(MODULES)), +$(eval DRIVER_LDINFO += $(SRC_DIR)/$(ldobj).$($2_AROBJ)) +$(eval DRIVER_LDINFO_D += $(SRC_DIR)/.$($2_DBG)/$(ldobj).$($2_AROBJ)), +$(if $($(ldobj)_INIT),,$(call LANG_LIB_INIT,$2,$(ldobj),$1)) +$(foreach dep,$($(ldobj)_DEPS) -# If the compiler supports linking, distinguish static from dynamic links, -# otherwise set all libraries to static -$(if $($($2_C)_LD),\ -$(eval STLIBS := $(filter-out $($($2_C)_LDLIBS),$($2_LIBS)))\ -$(eval DLIBS := $(filter-out $(STLIBS),$($2_LIBS))),\ -$(eval STLIBS := $($2_LIBS))) +$(if $(filter -l%,$(ldobj)), +$(eval ldlibname := $(ldobj:-l%=%)) +$(if $(filter $(ldlibname),$($($2_C)_LDLIBS)), +$(eval DRIVER_LDINFO += $(ldobj)) +$(eval DRIVER_LDINFO_D += $(ldobj)), +$(info Statically linking '$(ldobj:-l%=lib%)', linker '$($($2_C)_LD)' for compiler '$($2_C)' cannot link this lib.) +$(if $($(ldlibname)_INIT),,$(call LANG_LIB_INIT,$2,$(ldlibname),$1)) +$(foreach deplib,$($(ldlibname)_DEPS) $(ldlibname), +$(eval DRIVER_LDINFO += $(LIB_DIR)/$(deplib).$($2_AROBJ)) +$(eval DRIVER_LDINFO_D += $(LIB_DIR)/.$($2_DBG)/$(deplib).$($2_AROBJ))) +), +$(if $(findstring $(ldobj),$(MODULES)), +$(eval DRIVER_LDINFO += $(SRC_DIR)/$(ldobj).$($2_AROBJ)) +$(eval DRIVER_LDINFO_D += $(SRC_DIR)/.$($2_DBG)/$(ldobj).$($2_AROBJ)), +$(info Resolving static dependencies for '$(ldobj:%=lib%)'.) +$(if $($(ldobj)_INIT),,$(call LANG_LIB_INIT,$2,$(ldobj),$1)) +$(foreach deplib,$($(ldobj)_DEPS) $(ldobj), +$(eval DRIVER_LDINFO += $(LIB_DIR)/$(deplib).$($2_AROBJ)) +$(eval DRIVER_LDINFO_D += $(LIB_DIR)/.$($2_DBG)/$(deplib).$($2_AROBJ))) +)) +) # Directory setup -$(eval DRIVER_TARG_DIR := $(dir $(1:$(SRC_DIR)/%.$2=$(ROOT_DIR)/%))) -$(eval MAKE_DIRS += $(DRIVER_TARG_DIR)) -$(if $($2_DBG),$(eval MAKE_DIRS += $(DRIVER_TARG_DIR).$($2_DBG))) - -# Setup the sources for this object. A driver build rule does not include any -# compile steps at all, and so should not contain any sources that are, -# themselves, traditional "source files" like a language-compiled 'c' file. The -# driver depends on the existence of its equivalent object file (created through -# compilation), which is expected to contain a 'main' function (or entry for -# '_start'). This driver object is then simply linked to its libraries and -# module archives to create an executable binary in the output folder. -# ORDER MATTERS HERE, this is for the linker: -$(eval DRIVER_SRC := $(DRIVER_LFLAGS) $(DRIVER_DEPS)) -$(eval DRIVER_DBGSRC := $(DRIVER_LFLAGS) $(DRIVER_DBGDEPS)) -# Iterate through the list of libraries in our language and stack commands from -# left to right in the "sources" section of the LD command (or LD section of the -# commandline for the compiler), to preserve ordering and also ensure that it is -# linked correctly IN THAT ORDER (static vs dynamic). -$(foreach lib,$($2_LIBS),\ -$(if $(findstring $(lib),$(STLIBS)), -# Evaluate static lib $(lib) and add it to the dependencies and sources of the rules we built -$(eval STATIC_SRC := $(LIB_DIR)/lib$(lib).$($2_AROBJ))\ -$(eval STATIC_DBGSRC := $(LIB_DIR)/.$($2_DBG)/lib$(lib).$($2_AROBJ))\ -$(eval DRIVER_SRC := $(STATIC_SRC) $(DRIVER_SRC))\ -$(eval DRIVER_DBGSRC := $(STATIC_DBGSRC) $(DRIVER_DBGSRC))\ -$(eval DRIVER_DEPS += $(STATIC_SRC))\ -$(eval DRIVER_DBGDEPS += $(STATIC_DBGSRC)), -# Add dynamic lib to the sources with a link flag, in its correct ordering -$(eval DRIVER_SRC := -l$(lib) $(DRIVER_SRC))\ -$(eval DRIVER_DBGSRC := -l$(lib) $(DRIVER_DBGSRC))\ -)) +$(eval MAKE_DIRS += $(dir $(DRIVER_TARG))) +$(if $($2_DBG), +$(eval MAKE_DIRS += $(dir $(DRIVER_TARG_D)))) # Add the driver target to this language's targets for cleaning -$(eval SCRUB_TARGETS += $(DRIVER_TARG)) -$(DRIVER_TARG): $(DRIVER_DEPS) | $(DRIVER_TARG_DIR) - $($2_C) $($2_FLAGS) $(DRIVER_SRC) -o $$@ +$(eval SCRUB_TARGETS += $(DRIVER_TARG) $(DRIVER_TARG_D)) +$(DRIVER_TARG): $(filter-out -l%,$(DRIVER_LDINFO)) | $(dir $(DRIVER_TARG)) + $($2_C) $($2_FLAGS) $(DRIVER_LDINFO) -o $$@ # Output a rule for building with debug flags -$(eval DRIVER_DBGTARG := $(DRIVER_TARG_DIR)$(basename $(notdir $(DRIVER_TARG)))-d$($2_OUT)) -$(eval SCRUB_TARGETS += $(DRIVER_DBGTARG)) $(eval MAKE_DIRS += $(DRIVER_TARG_DIR).$($2_DBG)/) -$(DRIVER_DBGTARG): $(DRIVER_DBGDEPS) | $(DRIVER_TARG_DIR) - $($2_C) $($2_FLAGS) $(DRIVER_DBGSRC) -o $$@ -# Make a rule to run this driver after building -$(DRIVER_NAME)-run: $(DRIVER_TARG) - $(DRIVER_TARG) -$(DRIVER_NAME)-d: $(DRIVER_DBGTARG) +$(DRIVER_TARG_D): $(filter-out -l%,$(DRIVER_LDINFO_D)) | $(dir $(DRIVER_TARG_D)) + $($2_C) $($2_FLAGS) $(DRIVER_LDINFO_D) -o $$@ #/SRC_LANG_DRVRULE############################################################### endef @@ -263,7 +295,7 @@ MKCMD := make -k MKINSTALL := make -k install MKCLEAN := make clean MKDBGCMD := make -k -LIBDEPS := +LINK_ORDER := LIBLANGS := c cpp go LIBLDPATH := $(if $($($1_C)_LD),$(if $(LD_LIBRARY_PATH),$(LD_LIBRARY_PATH):,/usr/lib:)$(abspath $(LIB_DIR))) # One of these must be defined in the .mk file imported @@ -273,17 +305,22 @@ $(eval undefine $(var))) $(foreach var, WEBTARG WEBINIT CVSGET CVSPASS CONFIGUREDBG, $(eval undefine $(var))) ) -# If there is a .mk file for this lib, import it. Otherwise, we'll try to link -# it later, unless there is no linker for the language's compiler. +# If there is a .mk file for this lib, import it. Otherwise, inform user of +# error $(if $(wildcard $(CONF_DIR)/$2.mk),$(eval $(file <$(CONF_DIR)/$2.mk)),\ -$(if $($($1_C)_LD),\ -$(warning No $2.mk for lib$2, attempting to link from the system),\ -$(error No $2.mk for lib$2 and '$($1_C)' is not configured for linking)))\ +$(error Expected $(CONF_DIR)/$2.mk for lib$2)) # Add dependencies we found (potentially recursively, ignore repeats until later # so we can preserve ordering information for interdependency build ordering) -$(if $(LIBDEPS),\ -$(eval $1_LIBS += $(LIBDEPS))) +$(eval undefine $2_LDINFO) +$(foreach ldobj,$(LINK_ORDER),\ +$(eval $2_LDINFO += +) +$(if $(LINK_ORDER),\ +$(eval $1_LIBS += $(LIBDEPS))\ +$(if $3,$(eval $3_DEPS += $(LIBDEPS)))\ +$(eval $2_DEPS += $(LIBDEPS))\ +) # languages that can link this library (typically any lang that shares the # object format (ELF by default, but this is compiler-dependent (e.g. emcc uses # llvm bitcode, which is just an intermediary representation of data that can @@ -302,10 +339,12 @@ $(eval BUILTLIBS += $2)) # Recurse this function for every libdep encountered $(foreach libdep,$(LIBDEPS),\ $(call LANG_LIB_INIT,$1,$(libdep),$2)) +$(eval $2_DEPS := $(shell echo "$($2_DEPS)" | $(call AWK_REVERSE_SQUASH))) +$(eval $2_INIT := t) #/LANG_LIB_INIT################################################################# endef -# Initialize data for supported lanaguages ###################################### +# Initialize data for supported lanaguages ##################################### define LANG_INIT = $(eval $1_OBJ := $($($1_C)_OBJ) @@ -344,15 +383,19 @@ $(module)_TRG += $($(module)_$1_TRG) $(foreach src,$(filter $(SRC_DIR)/$(module)/%,$($1_MOD_SRC)),\ $(eval $(call SRC_LANG_RULE,$(src),$1))\ )) -# For all the listed libraries, initialize the library, which will set up all of -# its local ($1_LIBS, etc) data -$(foreach lib,$($1_LIBS),\ -$(eval $(call LANG_LIB_INIT,$1,$(lib),$1))) -# The initializer produces an ordered list of interlinked dependencies in groups -# by depth, so when reading backwards and compressing repeats we are left with a -# reliable build order for library interlinking. this awk program just looks -# through a list of space separated words backwards failing to print when it -# encounters a repeat -$1_LIBS := $(shell echo "$($1_LIBS)" | $(call AWK_REVERSE_SQUASH)) -#/LANG_INIT###################################################################### +#/LANG_INIT##################################################################### +endef + +# Create a phony target and rule for the specified source driver +define DRVSRC_DRIVERPHONY = +$(eval PH_TARG := $(basename $(notdir $1))) +$(eval PH_TARGS += $(PH_TARG)) +$(eval PH_LANG := $(lastword $(subst ., ,$1))) +$(eval RL_TARG := $(basename $(subst $(SRC_DIR),$(ROOT_DIR),$1))$($(PH_LANG)_OUT)) +.PHONY: $(PH_TARG) $(PH_TARG:%=%-d) +$(PH_TARG): $(RL_TARG) + @echo Built [$$@] at ./$$< +$(PH_TARG:%=%-d): $(RL_TARG:%=%-d) + @echo Build [$$@] with debugging information at ./$$< +#/SRC_SIMLIST_PHONYRULE ######################################################## endef