434 lines
9.8 KiB
Makefile
434 lines
9.8 KiB
Makefile
|
# mduem - Little utility for little software development
|
||
|
#
|
||
|
# This is a library Makefile to maintain software development, especially for
|
||
|
# Vim script. Note that this Makefile requires GNU make to use.
|
||
|
# Coding Rules #{{{1
|
||
|
#
|
||
|
# - Use non-empty string as true and empty string as false.
|
||
|
#
|
||
|
#
|
||
|
# Naming Rules:
|
||
|
#
|
||
|
# - Use UPPER_CASE variables to be configured by user.
|
||
|
#
|
||
|
# - Use lower_case variables for internal use of mduem.
|
||
|
#
|
||
|
# - Use suffix "_p" to indicate that a boolean value is resulted from
|
||
|
# a variable.
|
||
|
# Example: SHOULD_INSTALL_ASIS_P
|
||
|
#
|
||
|
# - Use noun for ordinary variables.
|
||
|
# Example: repos_name, TARGETS_GENERATED
|
||
|
#
|
||
|
# - Use verb for variables as functions.
|
||
|
# Example: resolve_dep_uri, RENAME_TARGET
|
||
|
#
|
||
|
# - Use prefix "generate_rule_" for variables to generate make rules.
|
||
|
# Example: generate_rule_to_install_a_target
|
||
|
#
|
||
|
# - Use abbreviations for words which names are too long to code.
|
||
|
# Example: dependency => dep, directory => dir, repository => repos
|
||
|
#
|
||
|
# - Use lower-case names for phony targets.
|
||
|
#
|
||
|
# - Use verb for phony targets.
|
||
|
# Example: clean, install, pack, ...
|
||
|
#
|
||
|
# - Use hyphens to join words in names of phony targets.
|
||
|
# Example: clean-junks, fetch-deps
|
||
|
#
|
||
|
# - Use prefix "," for names of files which are automatically generated by
|
||
|
# mduem and they are temporary ones.
|
||
|
# Example: test/,good-case.output
|
||
|
#
|
||
|
# - Use directory ".mduem" to contain stuffs for internal use.
|
||
|
# Example: .mduem/cache/
|
||
|
#
|
||
|
# - All rules may be violated if there is a strong custom from old times.
|
||
|
# Example: all (phony target)
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
# Common #{{{1
|
||
|
|
||
|
# Use "all" as the default target if no targets were specified on the command
|
||
|
# line or any other rules were defined before including this Makefile.
|
||
|
.DEFAULT_GOAL := all
|
||
|
all:
|
||
|
|
||
|
SHELL := /bin/bash
|
||
|
this_makefile := $(lastword $(MAKEFILE_LIST))
|
||
|
cache_makefile := .mduem/cache/Makefile.variables
|
||
|
user_makefiles := $(filter-out \
|
||
|
$(this_makefile) $(cache_makefile), \
|
||
|
$(MAKEFILE_LIST))
|
||
|
|
||
|
not = $(if $(1),,t)
|
||
|
toplevel_dir := $(shell git rev-parse --show-toplevel 2>/dev/null)
|
||
|
inner_dir := $(shell git rev-parse --show-prefix 2>/dev/null)
|
||
|
git_controlled_p := $(toplevel_dir)
|
||
|
toplevel_dir_p := $(and $(git_controlled_p),$(call not,$(inner_dir)))
|
||
|
|
||
|
ifneq '$(git_controlled_p)' ''
|
||
|
$(cache_makefile): \
|
||
|
$(toplevel_dir)/.git/config \
|
||
|
$(toplevel_dir)/.git/index \
|
||
|
$(this_makefile)
|
||
|
@echo 'GENERATE $@'
|
||
|
@mkdir -p '$(dir $@)'
|
||
|
@{ \
|
||
|
current_branch="$$(git symbolic-ref -q HEAD \
|
||
|
| sed -e 's|^refs/heads/||')"; \
|
||
|
_origin_name="$$(git config "branch.$$current_branch.remote")"; \
|
||
|
origin_name="$${_origin_name:-origin}"; \
|
||
|
_origin_uri="$$(git config "remote.$$origin_name.url")"; \
|
||
|
origin_uri="$${_origin_uri:-../.}"; \
|
||
|
\
|
||
|
echo "all_files_in_repos := \
|
||
|
$(filter-out .gitmodules \
|
||
|
$(shell cd $(toplevel_dir) && \
|
||
|
git submodule foreach 'echo "$$path"'),\
|
||
|
$(shell git ls-files))"; \
|
||
|
echo "current_branch := $${current_branch}"; \
|
||
|
echo "origin_name := $${origin_name}"; \
|
||
|
echo "origin_uri := $${origin_uri}"; \
|
||
|
echo 'repos_name := $(notdir $(shell pwd))'; \
|
||
|
echo 'version := $(shell git describe --tags --always --dirty)'; \
|
||
|
} >'$@'
|
||
|
endif
|
||
|
include $(cache_makefile)
|
||
|
|
||
|
# The type of a repository. It must be one of the following values:
|
||
|
#
|
||
|
# generic For any software.
|
||
|
# vim-script For Vim plugins, etc.
|
||
|
REPOS_TYPE ?= $(if $(filter vim-%,$(repos_name)),vim-script,generic)
|
||
|
vim_script_repos_p := $(filter vim-script,$(REPOS_TYPE))
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
# all #{{{1
|
||
|
|
||
|
.PHONY: all
|
||
|
all: build
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
# build #{{{1
|
||
|
|
||
|
TARGETS_ARCHIVED ?= $(all_files_in_repos)
|
||
|
TARGETS_GENERATED ?=# Empty
|
||
|
TARGETS_STATIC ?=# Empty
|
||
|
|
||
|
targets_all_installed := $(TARGETS_GENERATED) $(TARGETS_STATIC)
|
||
|
targets_all_archived := $(sort \
|
||
|
$(TARGETS_ARCHIVED) \
|
||
|
$(targets_all_installed) \
|
||
|
$(cache_makefile) \
|
||
|
)
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
.PHONY: build
|
||
|
build: $(targets_all_installed)
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
# clean #{{{1
|
||
|
|
||
|
.PHONY: clean
|
||
|
clean: clean-generated clean-junks
|
||
|
|
||
|
.PHONY: clean-generated
|
||
|
clean-generated:
|
||
|
@echo 'CLEAN-GENERATED'
|
||
|
@rm -rf $(TARGETS_GENERATED)
|
||
|
@find -name '.mduem' | xargs rm -rf
|
||
|
|
||
|
.PHONY: clean-junks
|
||
|
clean-junks:
|
||
|
@echo 'CLEAN-JUNKS'
|
||
|
@find -name '*~' -or -name ',*' | xargs rm -rf
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
# fetch-deps #{{{1
|
||
|
|
||
|
DEPS ?=# Empty
|
||
|
vim_script_deps := $(if $(vim_script_repos_p),vim-vspec vimup,)
|
||
|
all_deps := $(vim_script_deps) $(DEPS)
|
||
|
|
||
|
DEP_vim_vspec_URI ?= ../vim-vspec
|
||
|
DEP_vim_vspec_VERSION ?= 0.0.3
|
||
|
|
||
|
DEP_vimup_URI ?= ../vimup
|
||
|
DEP_vimup_VERSION ?= 0.0.1
|
||
|
|
||
|
|
||
|
# BUGS: This resolves "../" just once, but it's enough for usual cases.
|
||
|
resolve_dep_uri = $(strip $(if $(filter ../%,$(1)), \
|
||
|
$(dir $(origin_uri))$(1:../%=%), \
|
||
|
$(1)))
|
||
|
normalize_dep_name = $(subst -,_,$(1))
|
||
|
get_dep_raw_uri = $(DEP_$(call normalize_dep_name,$(1))_URI)
|
||
|
get_dep_dir_name = $(patsubst %.git,%,$(notdir $(call get_dep_uri,$(1))))
|
||
|
|
||
|
get_dep_uri = $(call resolve_dep_uri,$(call get_dep_raw_uri,$(1)))
|
||
|
get_dep_version = $(DEP_$(call normalize_dep_name,$(1))_VERSION)
|
||
|
get_dep_dir = .mduem/deps/$(call get_dep_dir_name,$(1))
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
.PHONY: fetch-deps
|
||
|
fetch-deps: $(all_deps:%=.mduem/deps/,%)
|
||
|
|
||
|
# FIXME: Update for changes on only DEPS and other values.
|
||
|
.mduem/deps/,%: $(user_makefiles)
|
||
|
@echo 'FETCH-DEP $*'
|
||
|
@mkdir -p '$(dir $@)'
|
||
|
@ ( \
|
||
|
if [ -d '$(call get_dep_dir,$*)' ] \
|
||
|
; then \
|
||
|
cd './$(call get_dep_dir,$*)' \
|
||
|
&& git fetch \
|
||
|
&& git checkout -f mduem-master \
|
||
|
; else \
|
||
|
git clone '$(call get_dep_uri,$*)' '$(call get_dep_dir,$*)'\
|
||
|
&& cd './$(call get_dep_dir,$*)' \
|
||
|
&& git checkout -b mduem-master \
|
||
|
; fi \
|
||
|
&& git reset --hard '$(call get_dep_version,$*)' \
|
||
|
; ) &>'$@.log' \
|
||
|
|| { cat '$@.log'; false; }
|
||
|
@touch '$@'
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
# install #{{{1
|
||
|
# Core #{{{2
|
||
|
|
||
|
INSTALLATION_DIR ?= $(error Please set INSTALLATION_DIR)
|
||
|
|
||
|
RENAME_TARGET ?= $(patsubst %,$(INSTALLATION_DIR)/%,$(1))
|
||
|
SHOULD_INSTALL_ASIS_P ?=# All files are version-filtered by default.
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
.PHONY: install
|
||
|
install: build
|
||
|
|
||
|
|
||
|
define generate_rule_to_install_a_target # (build_target, install_target)
|
||
|
install: $(2)
|
||
|
$(2): $(1)
|
||
|
@echo 'INSTALL $(1)'
|
||
|
@mkdir -p '$(dir $(2))'
|
||
|
@cp '--preserve=mode,ownership' '$(1)' '$(2)'
|
||
|
ifeq '$(call SHOULD_INSTALL_ASIS_P,$(1))' ''
|
||
|
@sed -i -e 's/0.2.10/$(version)/' '$(2)'
|
||
|
endif
|
||
|
|
||
|
endef
|
||
|
$(eval \
|
||
|
$(foreach t, \
|
||
|
$(targets_all_installed), \
|
||
|
$(call generate_rule_to_install_a_target,$(t),$(call RENAME_TARGET,$(t)))))
|
||
|
|
||
|
|
||
|
# This should be placed at the last to ensure that post-install is executed
|
||
|
# after any other rules to install.
|
||
|
install: post-install
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
# post-install #{{{2
|
||
|
|
||
|
TARGETS_POST_INSTALL ?=# Empty
|
||
|
targets_post_install_builtin :=# Empty
|
||
|
|
||
|
|
||
|
ifneq '$(vim_script_repos_p)' ''
|
||
|
target_vim_helptags := $(call RENAME_TARGET,doc/tags)
|
||
|
$(target_vim_helptags): $(filter doc/%.txt,$(targets_all_installed))
|
||
|
@echo 'POST-INSTALL vim helptags'
|
||
|
@vim -n -N -u NONE -U NONE -e -c 'helptags $(dir $@) | qall!'
|
||
|
|
||
|
targets_post_install_builtin += $(target_vim_helptags)
|
||
|
endif
|
||
|
|
||
|
|
||
|
.PHONY: post-install
|
||
|
post-install: $(targets_post_install_builtin) $(TARGETS_POST_INSTALL)
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
# pack #{{{1
|
||
|
|
||
|
archive_basename = $(repos_name)-$(version)
|
||
|
archive_name = $(archive_basename).zip
|
||
|
|
||
|
|
||
|
.PHONY: pack
|
||
|
pack: $(archive_name)
|
||
|
|
||
|
$(archive_name): $(cache_makefile)
|
||
|
rm -rf '$(archive_basename)' '$(archive_name)'
|
||
|
$(MAKE) \
|
||
|
'INSTALLATION_DIR=$(archive_basename)' \
|
||
|
'targets_all_installed=$(targets_all_archived)' \
|
||
|
install
|
||
|
zip -r $(archive_name) $(archive_basename)/
|
||
|
rm -rf '$(archive_basename)'
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
# release #{{{1
|
||
|
|
||
|
.PHONY: release
|
||
|
release: $(if $(vim_script_repos_p),release-vim-script,release-default)
|
||
|
|
||
|
|
||
|
.PHONY: release-default
|
||
|
release-default:
|
||
|
@echo 'Rules to release are not defined.'
|
||
|
|
||
|
|
||
|
.PHONY: release-vim-script
|
||
|
release-vim-script: fetch-deps $(repos_name).vimup pack
|
||
|
./.mduem/deps/vimup/vimup update-script $(repos_name)
|
||
|
rm $(repos_name).vimup
|
||
|
|
||
|
.PHONY: release-new-vim-script
|
||
|
release-new-vim-script: fetch-deps $(repos_name).vimup pack
|
||
|
./.mduem/deps/vimup/vimup new-script $(repos_name)
|
||
|
rm $(repos_name).vimup
|
||
|
|
||
|
$(repos_name).vimup: $(firstword $(sort $(filter doc/%.txt, \
|
||
|
$(all_files_in_repos))))
|
||
|
./.mduem/deps/vimup/vimup-info-generator \
|
||
|
<$< \
|
||
|
>$(repos_name).vimup
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
# test #{{{1
|
||
|
|
||
|
test_cases := $(patsubst test/%.expected,%, \
|
||
|
$(filter test/%.expected,$(all_files_in_repos)))
|
||
|
|
||
|
|
||
|
default_test_rule_deps := $(MAKEFILE_LIST)
|
||
|
define default_test_rule
|
||
|
source './$<' &>'$@' || { cat '$@'; false; }
|
||
|
endef
|
||
|
|
||
|
ifneq '$(vim_script_repos_p)' ''
|
||
|
all_vim_scripts := $(filter %.vim,$(all_files_in_repos))
|
||
|
vim_script_test_rule_deps := .mduem/deps/vim-vspec/bin/vspec $(all_vim_scripts)
|
||
|
define vim_script_test_rule
|
||
|
./$(call get_dep_dir,vim-vspec)/bin/vspec \
|
||
|
$< \
|
||
|
"$$PWD" \
|
||
|
$(foreach d,$(all_deps),$(call get_dep_dir,$(d))) \
|
||
|
&>$@
|
||
|
endef
|
||
|
endif
|
||
|
|
||
|
TEST_RULE ?= $(if $(vim_script_repos_p), \
|
||
|
$(vim_script_test_rule), \
|
||
|
$(default_test_rule))
|
||
|
TEST_RULE_DEPS ?=# Empty
|
||
|
|
||
|
builtin_test_rule_deps := $(if $(vim_script_repos_p), \
|
||
|
$(vim_script_test_rule_deps), \
|
||
|
$(default_test_rule_deps))
|
||
|
all_test_rule_deps := $(builtin_test_rule_deps) $(TEST_RULE_DEPS)
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
.PHONY: test
|
||
|
test: fetch-deps test/,ok
|
||
|
|
||
|
test/,ok: $(test_cases:%=test/,%.ok)
|
||
|
@echo 'ALL TESTS ARE PASSED.'
|
||
|
@touch $@
|
||
|
|
||
|
test/,%.ok: test/%.input $(all_test_rule_deps)
|
||
|
@echo -n 'TEST $* ... '
|
||
|
@$(MAKE) --silent '$(@:.ok=.diff)'
|
||
|
@if ! [ -s $(@:.ok=.diff) ]; then \
|
||
|
echo 'OK'; \
|
||
|
else \
|
||
|
echo 'FAILED'; \
|
||
|
cat $(@:.ok=.diff); \
|
||
|
echo 'END'; \
|
||
|
false; \
|
||
|
fi
|
||
|
@touch $@
|
||
|
|
||
|
test/,%.diff: test/%.expected test/,%.output
|
||
|
@diff -u $^ >$@; true
|
||
|
|
||
|
test/,%.output: test/%.input $(all_test_rule_deps)
|
||
|
@$(TEST_RULE)
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
# __END__ #{{{1
|
||
|
# vim: foldmethod=marker
|