include base #omkbuild
+include prepare #omkbuild
+include test #omkbuild
include build #omkbuild
QUIET_CMD_ECHO = echo
endif
+# Convenient variables
+comma := ,
+squote := '
+#'
+empty :=
+space := $(empty) $(empty)
+
+###
+# Escape single quote for use in echo statements
+escsq = $(subst $(squote),'\$(squote)',$1)
+
+###
+# Strip/replace OUTPUT_DIR from/in the argument
+strip_out = $(patsubst $(OUTPUT_DIR)/%,%,$(1))
+repl_out = $(patsubst $(OUTPUT_DIR)/%,$$(OUTPUT_DIR)/%,$(1))
+
+.PHONY: FORCE
+
+
MAKEFILE_OMK=Makefile.omk
# All subdirectories (even linked ones) containing Makefile.omk
# Usage in Makefile.omk: SUBDIRS = $(ALL_OMK_SUBDIRS)
# Usage: $(call omk_pass_subdir_template,<pass name>,<build dir>,<subdir>)
define omk_pass_subdir_template
.PHONY: $(pass)-$(3)-subdir
-$(pass)-submakes: $(pass)-$(3)-subdir
+$(pass)-subdirs: $(pass)-$(3)-subdir
$(pass)-$(3)-subdir:
@$(call mkdir_def,$(2)/$(3))
+@$(MAKE) SOURCES_DIR=$(SOURCES_DIR)/$(3) $(NO_PRINT_DIRECTORY) \
RELATIVE_DIR=$(RELATIVE_PREFIX)$(3) -C $(2)/$(3) \
- -f $(SUBDIR_MAKEFILE) $(pass)-submakes
-# In subdirectories we can call submakes directly since passes are
-# already searialized on the toplevel make.
+ -f $(SUBDIR_MAKEFILE) $(pass)
endef
ifdef OMK_TESTSROOT
# Usage: $(call omk_pass_template,<pass name>,<build dir>,[<local make flags>],[<local enable condition>])
define omk_pass_template
-.PHONY: $(pass) $(pass)-local $(pass)-check $(pass)-submakes
-$(foreach subdir,$(SUBDIRS),$(eval $(call omk_pass_subdir_template,$(pass),$(2),$(subdir))))
-$(pass):
-# Submakes have to be called this way and not as dependecies for pass
-# serialization to work
- +@$(MAKE) SOURCES_DIR=$(SOURCES_DIR) $(NO_PRINT_DIRECTORY) \
- RELATIVE_DIR=$(RELATIVE_DIR) \
- -f $(SOURCESDIR_MAKEFILE) $(pass)-submakes
-$(pass)-submakes:
- @true # Do not emit "nothing to be done" messages
-
-ifneq ($(4)$($(pass)_HOOKS),)
-$(pass)-submakes: $(pass)-this-dir
-$(pass)-this-dir: $(foreach subdir,$(SUBDIRS),$(pass)-$(subdir)-subdir)
+.PHONY: $(pass) $(pass)-local $(pass)-msg $(pass)-subdirs
+$(pass): $(pass)-local
+$(pass)-local: $(pass)-subdirs $(pass)-msg
+$(foreach subdir,$(SUBDIRS),$(call omk_pass_subdir_template,$(pass),$(2),$(subdir)))
+
+$(pass)-msg: $(pass)-subdirs
+@echo "make[omk]: $(pass) in $(RELATIVE_DIR)"
- @$(call mkdir_def,$(2))
- +@$(MAKE) $(NO_PRINT_DIRECTORY) SOURCES_DIR=$(SOURCES_DIR) RELATIVE_DIR=$(RELATIVE_DIR) -C $(2) \
- -f $(SOURCESDIR_MAKEFILE) $(3) $(check-target) $(1:%=%-local)
-$(pass)-local: $($(pass)_HOOKS)
-endif
endef
# =======================
@echo "# This file should not be altered manually" >> "$(CONFIG_FILE)-default"
@echo "# Overrides should be stored in file $(notdir $(CONFIG_FILE))" >> "$(CONFIG_FILE)-default"
@echo >> "$(CONFIG_FILE)-default"
- @$(MAKE) $(NO_PRINT_DIRECTORY) -C $(OUTPUT_DIR) \
+ @$(MAKE) $(NO_PRINT_DIRECTORY) -C $(MAKERULES_DIR) \
RELATIVE_DIR="" SOURCES_DIR=$(OUTPUT_DIR) \
-f $(OUTPUT_DIR)/Makefile default-config-pass
-####
-# kbuild: Generic definitions
-# Convenient variables
-comma := ,
-squote := '
-#'
-empty :=
-space := $(empty) $(empty)
-
-BUILD_DIR_NAME = _build
-COMPILED_DIR_NAME = _compiled
-
-USER_INCLUDE_DIR := $(OUTPUT_DIR)/$(COMPILED_DIR_NAME)/include
-USER_LIB_DIR := $(OUTPUT_DIR)/$(COMPILED_DIR_NAME)/lib
-USER_UTILS_DIR := $(OUTPUT_DIR)/$(COMPILED_DIR_NAME)/bin-utils
-USER_TESTS_DIR := $(OUTPUT_DIR)/$(COMPILED_DIR_NAME)/bin-tests
-USER_BIN_DIR := $(OUTPUT_DIR)/$(COMPILED_DIR_NAME)/bin
-USER_BUILD_DIR := $(OUTPUT_DIR)/$(BUILD_DIR_NAME)/user
-LINK_BUILD_DIR := $(OUTPUT_DIR)/$(BUILD_DIR_NAME)/link
-
-USER_OBJS_DIR = $(USER_BUILD_DIR)/$(RELATIVE_DIR)
-KERN_OBJS_DIR = $(KERN_BUILD_DIR)/$(RELATIVE_DIR)
-OMK_WORK_DIR = $(USER_OBJS_DIR)
-
-strip_out = $(patsubst $(OUTPUT_DIR)/%,%,$(1))
-repl_out = $(patsubst $(OUTPUT_DIR)/%,$$(OUTPUT_DIR)/%,$(1))
-
-.PHONY: FORCE
-
-#####################
+##################### -*- makefile-gmake -*-
# Build pseudo-pass #
#####################
+ifneq (,$(filter build,$(MAKECMDGOALS)))
+all: build-pseudo-pass
-rule_targets = $($(1)_targets)
-rule_deps = $($(1)_deps)
-rule_msg = $($(1)_msg)
-rule_cmd = $($(1)_cmd)
+rule_targets = $(value $(1)_targets)
+rule_deps = $(value $(1)_deps)
+rule_msg = $(value $(1)_msg)
+rule_cmd = $(value $(1)_cmd)
-# Usage: $(call build_rule,<target>)
+# Usage: $(eval $(call build_rule,<target>))
define build_rule
-$(rule_target): $(rule_deps) $(omk_inc_file)
- @$(QUIET_CMD_ECHO) " "$(rule_msg) "$$(call strip_out,$$(@))"
+$(rule_targets): $(rule_deps) #$(omk_inc_file)
+ @$(QUIET_CMD_ECHO) " "$(rule_msg)"$$@" #"$$(call strip_out,$$@)"
$(Q)$(rule_cmd)
endef
-# TODO: Include target.omk only in the subtree
--include $(shell true; find $(USER_BUILD_DIR) -name '*omk.inc') # `true' is a hack for MinGW
+# TODO: Include __goals.omk.inc only in the subtree
+-include $(shell true; find $(USER_BUILD_DIR) -name '*.omk.inc') # `true' is a hack for MinGW
+# FIXME: Disable automatic remake of included files
$(foreach rule,$(prepared_rules),$(eval $(call build_rule,$(rule))))
-.PHONY: build-pseudo-pass
-build-pseudo-pass: $(foreach target,$(default_build_targets),$(target))
-
-################
-# Prepare pass #
-################
-
-$(eval $(call omk_pass_template,prepare-pass,$(USER_OBJS_DIR),,always))
-
-###
-# Name of the prepared rule. This name is used as prefix of variable
-# names in .omk.inc and is based on the first target
-omk_inc_rule = $(firstword $(1:$(OUTPUT_DIR)/%=%))
-
-# The name of .omk.inc file is derived from $(omk_inc_rule) and
-# depends whether the target is under _build or elsewhere.
-omk_inc_file = $(if $(filter $(BUILD_DIR_NAME)%,$(omk_inc_rule)),\
- $(OMK_WORK_DIR)/$(notdir $(omk_inc_rule)).omk.inc,\
- $(OMK_WORK_DIR)/$(subst /,_,$(omk_inc_rule)).omk.inc)
-
-target_omk_file = $(OMK_WORK_DIR)/$(notdir $(1)).target.omk
-
-###
-# prepare_rule is used to generate .omk.inc files during prepare-pass.
-# All paths in this file should be relative to $(OUTPUT_DIR)
-# Usage: $(call prepare_rule,<targets>,<deps>,<msg>,<cmd>)
-define prepare_rule
-prepare-pass-local: $(omk_inc_file)
-
-$(omk_inc_file): FORCE
- $(Q)echo 'prepared_rules += $(omk_inc_rule)' > $$@.tmp; \
- echo '$(omk_inc_rule)_targets = $$(call strip_out,$(1))' >> $$@.tmp; \
- echo '$(omk_inc_rule)_deps += $$(call strip_out,$(2))' >> $$@.tmp; \
- echo '$(omk_inc_rule)_msg = $(3)' >> $$@.tmp; \
- echo '$(omk_inc_rule)_cmd = $$(call repl_out,$(4))' >> $$@.tmp; \
- if cmp -s $$@.tmp $$@; then rm $$@.tmp; else mv $$@.tmp $$@; fi
-endef
-
-###
-# prepare_rule_default - same as prepare rules but the target is put
-# as dependency to the default build rule, which causes it to be built
-# upon calling make
-define prepare_rule_default
-$(call prepare_rule,$(1),$(2),$(3),$(4))
-prepared_default_build_targets += $(call strip_out,$(1))
-endef
-
-prepare-pass-local: $(OMK_WORK_DIR)/omk.inc
-$(OMK_WORK_DIR)/omk.inc: FORCE
- $(Q)echo 'default_build_targets += $(prepared_default_build_targets)' > $@.tmp; \
- if cmp -s $@.tmp $@; then rm $@.tmp; else mv $@.tmp $@; fi
-
-
-############
-# Programs #
-############
-
-# Usage: $(call program_template,<executable-name>,<bin|utils|test>)
-define program_template
-$(1)_OBJS += $$(patsubst %.c,%.o,$$(filter %.c,$$($(1)_SOURCES)))
-$(1)_OBJS := $$(addprefix $(USER_OBJS_DIR),$$(sort $$($(1)_OBJS:%/=%)))
-
-USER_OBJS += $$($(1)_OBJS)
-USER_SOURCES += $$($(1)_SOURCES)
-
-
-
-program_cmd = $$(if $$(filter %.cc,$$($(1)_SOURCES)),$$(CXX),$$(CC)) \
- $$($(1)_OBJS) $$($(1)_LIBS:%=-l%) $$(LOADLIBES) $$(LDFLAGS) -Wl,-rpath-link,$(USER_LIB_DIR) \
- -Wl,-Map,$(USER_OBJS_DIR)/$(1).exe.map -o $$$$@
-
-$(call prepare_rule_default,\
- $(USER_BIN_DIR)/$(1)$(EXE_SUFFIX),\
- $$($(1)_OBJS) $$($(1)_LIBS),\
- "LINK ",\
- $$(program_cmd))
-endef
-
-c_o_cmd := set -e; cd $$(dir $$@); \
- if $$(c_o_COMPILE) $$(CC_DEPFLAGS) -o $$@ -c $$< ; \
- then mv -f "$$@.d.tmp" "$$@.d" ; \
- else rm -f "$$@.d.tmp" ; \
- fi
-
-
-$(foreach prog,$(bin_PROGRAMS),$(eval $(call program_template,$(prog),bin)))
-$(foreach src,$(filter %.c,$(USER_SOURCES)),$(eval $(call prepare_rule,\
- $(USER_OBJS_DIR)/$(src:%.c=%.o),\
- $(SOURCES_DIR)/$(src),\
- "CC ",\
- $(c_o_cmd))))
-
-default: prepare-pass
- $(MAKE) -qp build-pseudo-pass > make.dp.build-pseudo-pass
- $(MAKE) build-pseudo-pass
+.PHONY: build
+build: $(foreach target,$(omk_goals),$(target))
+endif # (,$(filter build,$(MAKECMDGOALS)))
--- /dev/null
+
+################
+# Prepare pass #
+################
+
+$(eval $(call omk_pass_template,prepare-pass,$(USER_OBJS_DIR),,always))
+
+###
+# Name of the prepared rule. This name is used as prefix of variable
+# names in .omk.inc and is based on the first target
+omk_inc_rule = $(firstword $(1:$(OUTPUT_DIR)/%=%))
+
+# The name of .omk.inc file is derived from $(omk_inc_rule) and
+# depends whether the target is under _build or elsewhere.
+omk_inc_file = $(if $(filter $(BUILD_DIR_NAME)%,$(omk_inc_rule)),\
+ $(OMK_WORK_DIR)/$(notdir $(omk_inc_rule)).omk.inc,\
+ $(OMK_WORK_DIR)/$(subst /,_,$(omk_inc_rule)).omk.inc)
+
+target_omk_file = $(OMK_WORK_DIR)/$(notdir $(1)).target.omk
+
+# >'< substitution is for echo to work,
+# >$< substitution to preserve $ when reloading .omk.inc file
+subst-cmd = $(subst \#,\\\#,$(subst $$,$$$$,$(call escsq,$(1))))
+
+###
+# prepare_rule is used to generate .omk.inc files during prepare-pass.
+# All paths in this file should be relative to $(OUTPUT_DIR)
+# Usage: $(eval $(call prepare_rule,<targets>,<deps>,<msg>,<cmd>))
+define prepare_rule
+prepare-pass-local: $(omk_inc_file)
+
+$(omk_inc_file): FORCE
+#TODO remove the next line
+ @echo " PREP "$$(call strip_out,$$@); \
+ echo 'prepared_rules += $(omk_inc_rule)' > $$@.tmp; \
+ echo '$(omk_inc_rule)_targets = $$(call strip_out,$(1))' >> $$@.tmp; \
+ echo '$(omk_inc_rule)_deps += $$(call strip_out,$(2))' >> $$@.tmp; \
+ echo '$(omk_inc_rule)_msg = $(3)' >> $$@.tmp; \
+ echo '$(omk_inc_rule)_cmd = $(call subst-cmd,$(4))' >> $$@.tmp; \
+ if cmp -s $$@.tmp $$@; then rm $$@.tmp; else mv $$@.tmp $$@; fi
+endef
+
+###
+# prepare_rule_goal - same as prepare rules but the target is put
+# as dependency to the default goal, which causes it to be built
+# upon calling make
+define prepare_rule_goal
+$(call prepare_rule,$(1),$(2),$(3),$(4))
+prepared_goals += $(call strip_out,$(1))
+endef
+
+prepare-pass-local: $(OMK_WORK_DIR)/__goals.omk.inc
+$(OMK_WORK_DIR)/__goals.omk.inc: FORCE
+ $(Q)echo 'omk_goals += $(prepared_goals)' > $@.tmp; \
+ if cmp -s $@.tmp $@; then rm $@.tmp; else mv $@.tmp $@; fi
+
+
--- /dev/null
+BUILD_DIR_NAME = _build
+COMPILED_DIR_NAME = _compiled
+
+USER_INCLUDE_DIR := $(OUTPUT_DIR)/$(COMPILED_DIR_NAME)/include
+USER_LIB_DIR := $(OUTPUT_DIR)/$(COMPILED_DIR_NAME)/lib
+USER_UTILS_DIR := $(OUTPUT_DIR)/$(COMPILED_DIR_NAME)/bin-utils
+USER_TESTS_DIR := $(OUTPUT_DIR)/$(COMPILED_DIR_NAME)/bin-tests
+USER_BIN_DIR := $(OUTPUT_DIR)/$(COMPILED_DIR_NAME)/bin
+USER_BUILD_DIR := $(OUTPUT_DIR)/$(BUILD_DIR_NAME)/user
+LINK_BUILD_DIR := $(OUTPUT_DIR)/$(BUILD_DIR_NAME)/link
+
+# Avoid double slash at the end in the top-level directory
+USER_OBJS_DIR = $(USER_BUILD_DIR)$(RELATIVE_DIR:%=/%)
+KERN_OBJS_DIR = $(KERN_BUILD_DIR)$(RELATIVE_DIR:%=/%)
+OMK_WORK_DIR = $(USER_OBJS_DIR)
+
+############
+# Programs #
+############
+
+# Check GCC version for user build
+ifndef CC_MAJOR_VERSION
+CC_MAJOR_VERSION := $(shell $(CC) -dumpversion | sed -e 's/\([^.]\)\..*/\1/')
+endif
+# Prepare suitable define for dependency building
+ifeq ($(CC_MAJOR_VERSION),2)
+CC_DEPFLAGS = -Wp,-MD,"$$@.d.tmp"
+else
+CC_DEPFLAGS = -MT $$@ -MD -MP -MF "$$@.d.tmp"
+endif
+
+c_o_COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -DOMK_FOR_USER
+
+c_o_cmd = \
+ if $(c_o_COMPILE) $(CC_DEPFLAGS) -o $$@ -c $$< ; \
+ then mv -f "$$@.d.tmp" "$$@.d"; \
+ else rm -f "$$@.d.tmp"; exit 1; \
+ fi
+
+
+# Usage: $(call program_template,<executable-name>,<bin|utils|test>)
+define program_template
+$(1)_OBJS += $$(patsubst %.c,%.o,$$(filter %.c,$$($(1)_SOURCES)))
+$(1)_OBJS := $$(addprefix $(USER_OBJS_DIR)/,$$(sort $$($(1)_OBJS:%/=%)))
+
+USER_OBJS += $$($(1)_OBJS)
+USER_SOURCES += $$($(1)_SOURCES)
+
+$(call prepare_rule_goal,\
+ $(USER_BIN_DIR)/$(1)$(EXE_SUFFIX),\
+ $$($(1)_OBJS) $$($(1)_LIBS),\
+ "LINK ",\
+ $(program_cmd))
+endef
+
+program_cmd = $(if $(filter %.cc,$$($(1)_SOURCES)),$(CXX),$(CC)) \
+ $($(1)_OBJS) $($(1)_LIBS:%=-l%) $(LOADLIBES) $(LDFLAGS) -Wl,-rpath-link,$(USER_LIB_DIR) \
+ -Wl,-Map,$(USER_OBJS_DIR)/$(1).exe.map -o $$@
+
+$(foreach prog,$(bin_PROGRAMS),$(eval $(call program_template,$(prog),bin)))
+#$(foreach prog,$(bin_PROGRAMS),$(info >>>$(call program_template,$(prog),bin)<<<))
+$(foreach src,$(filter %.c,$(USER_SOURCES)),\
+ $(eval $(call prepare_rule,\
+ $(USER_OBJS_DIR)/$(src:%.c=%.o),\
+ $(SOURCES_DIR)/$(src),\
+ "CC ",\
+ $(c_o_cmd))))
+
+$(eval $(call prepare_rule_goal,\
+ $(USER_OBJS_DIR)/test.X.o,\
+ $(SOURCES_DIR)/test.c,\
+ "CC ",\
+ $(c_o_cmd)))
+
+# OUTPUT_DIR is not defined in Makefile.build so we has to pass it on
+# command-line. After this file will be merged with Makefile.rules, it
+# will not be necessary to pass it.
+default: check-dir prepare-pass
+ -$(MAKE) -qp -f $(MAKERULES_DIR)/Makefile.rules build > make.db.build-pseudo-pass;
+ +@echo "make[omk]: build"
+ @$(MAKE) $(NO_PRINT_DIRECTORY) -f $(MAKERULES_DIR)/Makefile.rules build
+
+check-dir:
+ @$(call mkdir_def,$(USER_BUILD_DIR))
+ @$(call mkdir_def,$(USER_INCLUDE_DIR))
+ @$(call mkdir_def,$(USER_LIB_DIR))
+ @$(call mkdir_def,$(USER_BIN_DIR))
+ @$(call mkdir_def,$(USER_UTILS_DIR))
+ @$(call mkdir_def,$(USER_TESTS_DIR))