From 75aba75ff8a09de8dc48f0331b78b4f5f3e0a028 Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Wed, 28 Mar 2012 18:49:38 +0200 Subject: [PATCH] Support proper shadowing of target.mk files The build system overlays multiple source trees (repositories) such that they can shadow libraries and include search paths. This patch extends the shadowing concept to build targets. Furthermore, it streamlines the build stage for generating library depenencies, reducing the processing time of this stage by 10-20 percent. Fixes #165. --- tool/builddir/build.mk | 119 ++++++++++++++++++++++++----------------- 1 file changed, 71 insertions(+), 48 deletions(-) diff --git a/tool/builddir/build.mk b/tool/builddir/build.mk index 7c66837dc..601167170 100644 --- a/tool/builddir/build.mk +++ b/tool/builddir/build.mk @@ -90,10 +90,10 @@ export LIBGCC_INC_DIR = $(shell dirname `$(CUSTOM_CXX_LIB) -print-libgcc-file-na # # Find out about the target directories to build # -DST_DIRS = $(filter-out all clean bin cleanall again run/%,$(MAKECMDGOALS)) +DST_DIRS := $(filter-out all clean bin cleanall again run/%,$(MAKECMDGOALS)) ifeq ($(MAKECMDGOALS),) -DST_DIRS = . +DST_DIRS := * endif # @@ -102,13 +102,6 @@ endif all $(DST_DIRS): gen_deps_and_build_targets @true -# -# Helper to find targets in repositories -# The sed command is there to replace /./ by /. This happens when DST_DIRS = . -# -find_src_target_mk = $(GNU_FIND) $$i/src/$(@:.visit=) -name target.mk 2>/dev/null | sed "s/\/\.\//\//g" -find_all_src_target_mk = for i in $(REPOSITORIES); do $(find_src_target_mk); done - -include $(call select_from_repositories,etc/specs.conf) -include $(BUILD_BASE_DIR)/etc/specs.conf export SPEC_FILES := $(foreach SPEC,$(SPECS),$(call select_from_repositories,mk/spec-$(SPEC).mk)) @@ -135,56 +128,86 @@ init_progress_log: .PHONY: init_libdep_file init_libdep_file: - @echo "#" > $(LIB_DEP_FILE) - @echo "# Library dependencies for build '$(DST_DIRS)'" >> $(LIB_DEP_FILE) - @echo "#" >> $(LIB_DEP_FILE) - @echo "" >> $(LIB_DEP_FILE) - @echo "export SPEC_FILES := \\" >> $(LIB_DEP_FILE) - @for i in $(SPEC_FILES); do \ - echo " $$i \\" >> $(LIB_DEP_FILE); done - @echo "" >> $(LIB_DEP_FILE) - @echo "LIB_CACHE_DIR = $(LIB_CACHE_DIR)" >> $(LIB_DEP_FILE) - @echo "BASE_DIR = $(realpath $(BASE_DIR))" >> $(LIB_DEP_FILE) - @echo "VERBOSE ?= $(VERBOSE)" >> $(LIB_DEP_FILE) - @echo "VERBOSE_MK ?= $(VERBOSE_MK)" >> $(LIB_DEP_FILE) - @echo "VERBOSE_DIR ?= $(VERBOSE_DIR)" >> $(LIB_DEP_FILE) - @echo "INSTALL_DIR ?= $(INSTALL_DIR)" >> $(LIB_DEP_FILE) - @echo "SHELL ?= $(SHELL)" >> $(LIB_DEP_FILE) - @echo "MKDIR ?= mkdir" >> $(LIB_DEP_FILE) - @echo "" >> $(LIB_DEP_FILE) - @echo "all:" >> $(LIB_DEP_FILE) - @echo " @true # prevent nothing-to-be-done message" >> $(LIB_DEP_FILE) - @echo "" >> $(LIB_DEP_FILE) + @echo "checking library dependencies..." + @(echo "#"; \ + echo "# Library dependencies for build '$(DST_DIRS)'"; \ + echo "#"; \ + echo ""; \ + echo "export SPEC_FILES := \\"; \ + for i in $(SPEC_FILES); do \ + echo " $$i \\"; done; \ + echo ""; \ + echo "LIB_CACHE_DIR = $(LIB_CACHE_DIR)"; \ + echo "BASE_DIR = $(realpath $(BASE_DIR))"; \ + echo "VERBOSE ?= $(VERBOSE)"; \ + echo "VERBOSE_MK ?= $(VERBOSE_MK)"; \ + echo "VERBOSE_DIR ?= $(VERBOSE_DIR)"; \ + echo "INSTALL_DIR ?= $(INSTALL_DIR)"; \ + echo "SHELL ?= $(SHELL)"; \ + echo "MKDIR ?= mkdir"; \ + echo ""; \ + echo "all:"; \ + echo " @true # prevent nothing-to-be-done message"; \ + echo "") > $(LIB_DEP_FILE) # -# We check if any target.mk files exist in the specified src directory. If -# there exist any target.mk files, we revisit each repository and create -# corresponding rules in the library-dependency file. -# -# This stage is executed serially. +# We check if any target.mk files exist in the specified directories within +# any of the repositories listed in the 'REPOSITORIES' variable. # $(dir $(LIB_DEP_FILE)): @mkdir -p $@ -VISIT_DST_DIRS = $(addsuffix .visit,$(DST_DIRS)) +# +# Find all 'target.mk' files located within any of the specified subdirectories +# ('DST_DIRS') and any repository. The 'sort' is used to remove duplicates. +# +TARGETS_TO_VISIT := $(shell find $(REPOSITORIES:=/src) -false \ + $(foreach DST,$(DST_DIRS), \ + -or -path "*/src/$(DST)/**target.mk" \ + -printf " %P ")) +TARGETS_TO_VISIT := $(sort $(TARGETS_TO_VISIT)) -.PHONY: $(VISIT_DST_DIRS) -.NOTPARALLEL: $(VISIT_DST_DIRS) -$(VISIT_DST_DIRS): $(dir $(LIB_DEP_FILE)) init_libdep_file init_progress_log - @echo "checking library dependencies for $(@:.visit=)..." - @test "`$(find_all_src_target_mk)`" != "" ||\ - (echo Error: non-existing target $(@:.visit=); false) - $(VERBOSE_MK)set -e; for i in $(REPOSITORIES); do \ - for j in `$(find_src_target_mk)`; do \ +# +# Perform sanity check for non-existing targets being specified at the command +# line. +# +MISSING_TARGETS := $(strip \ + $(foreach DST,$(DST_DIRS),\ + $(if $(filter $(DST)/%,$(TARGETS_TO_VISIT)),,$(DST)))) + +ifneq ($(MAKECMDGOALS),) +ifneq ($(MISSING_TARGETS),) +init_libdep_file: error_missing_targets +error_missing_targets: + @for target in $(MISSING_TARGETS); do \ + echo "Error: target '$$target' does not exist"; done + @false; +endif +endif + +# +# The procedure of collecting library dependencies is realized as a single +# rule to gain maximum performance. If we invoked a rule for each target, +# we would need to spawn one additional shell per target, which would take +# 10-20 percent more time. +# +traverse_dependencies: $(dir $(LIB_DEP_FILE)) init_libdep_file init_progress_log + $(VERBOSE_MK) \ + for target in $(TARGETS_TO_VISIT); do \ + for rep in $(REPOSITORIES); do \ + test -f $$rep/src/$$target || continue; \ $(MAKE) $(VERBOSE_DIR) -f $(BASE_DIR)/mk/dep_prg.mk \ - REP_DIR=$$i TARGET_MK=$$j \ - BUILD_BASE_DIR=$(BUILD_BASE_DIR) \ - SHELL=$(SHELL) \ - DARK_COL="$(DARK_COL)" DEFAULT_COL="$(DEFAULT_COL)"; done; done + REP_DIR=$$rep TARGET_MK=$$rep/src/$$target \ + BUILD_BASE_DIR=$(BUILD_BASE_DIR) \ + SHELL=$(SHELL) \ + DARK_COL="$(DARK_COL)" DEFAULT_COL="$(DEFAULT_COL)"; \ + break; \ + done; \ + done .PHONY: $(LIB_DEP_FILE) -$(LIB_DEP_FILE): $(VISIT_DST_DIRS) +$(LIB_DEP_FILE): traverse_dependencies ## ## Second stage: build targets based on the result of the first stage