# mcf - top-level Makefile

.SECONDEXPANSION:

######
# User Tools

PAGERS := less more cat
EDITORS := vim emacs nano
PDFVIEWERS := okular docviewer evince
WEBVIEWERS := google-chrome firefox

######
# Tools

I7 := $(shell which -a install/bin/i7 obj/gen/bin/dev/i7.sh | head -n 1)
P7 := $(shell which -a install/bin/p7 obj/gen/bin/dev/p7.sh | head -n 1)
D12 := $(shell which -a install/bin/d12 obj/gen/bin/dev/d12.sh | head -n 1)

C5 := $(shell which -a install/bin/c5 obj/gen/bin/sys/c5.sh | head -n 1)

ENV := env

MKDIR := mkdir
MKDIR_P = $(MKDIR) -p

CP := cp
CP_A = $(CP) -a

RM_R = $(RM) -r
RMDIR := rmdir
RMDIR_I = $(RMDIR) --ignore-fail-on-non-empty

INSTALL := install
INSTALL_D = $(INSTALL) -d

# development tools
TIME := time -p

######
# Local configuration

MAKEFILE_I7 = Makefile.i7
PACKAGE_I7 = package.i7
SYSTEM_I7 = system.i7
SITE_I7 = site.i7

LICENSE_LIB = core/license-parts

include $(MAKEFILE_I7)

ALIBS += $(LICENSE_LIB)

BUILD_DATE=$(shell date +%0F)
BUILD_TIME=$(shell date +%0T)

######
# Default rule

default: apps

######
# Default User Tools
# These definitions are used if they were not defined by the user

PAGER ?= $(shell which $(PAGERS) | head -n 1)
EDITOR ?= $(shell which $(EDITORS | head -n 1))
PDFVIEWR ?= $(shell which $(PDFVIEWERS) | head -n 1)
WEBVIEWER ?= $(shell which $(WEBVIEWERS) | head -n 1)

######
# Paths

SRCDIR =
LIBSRCDIR = $(SRCDIR)libs
BINSRCDIR = $(SRCDIR)src
CONFSRCDIR = $(SRCDIR)conf

EXEC_PREFIX ?= $(PREFIX)
DATA_PREFIX ?= $(PREFIX)

BINDIR = $(EXEC_PREFIX)/bin

SHAREDIR = $(EXEC_PREFIX)/share
LIBDIR = $(SHAREDIR)/$(PKG)
DOCDIR = $(SHAREDIR)/doc/$(PKG)

DATADIR = $(DATA_PREFIX)/cache/$(PKG)
LOGDIR = $(DATA_PREFIX)/log/$(PKG)
RUNDIR = $(DATA_PREFIX)/run/$(PKG)

ETCDIR = $(PREFIX)/etc
TMPDIR = $(PREFIX)/tmp
CONFDIR = $(PREFIX)/etc/$(PKG)

OBJDIR = obj
GENDIR = $(OBJDIR)/gen
BINOBJDIR = $(GENDIR)/bin
LIBOBJDIR = $(GENDIR)/libs
CONFOBJDIR = $(GENDIR)/conf
DISTOBJDIR = $(GENDIR)/dist

######
# Generated lists

CONF += $(addsuffix .conf,$(APPS) $(ALIBS))
CONF_SOURCES = $(wildcard $(addprefix $(CONFSRCDIR)/,$(addsuffix .in,$(CONF))))

TOOL_SOURCES = $(addprefix $(BINSRCDIR)/,$(addsuffix .in,$(APPS)))
LIBS_SOURCES = $(addprefix $(LIBSRCDIR)/,$(addsuffix .in,$(ALIBS)))
LIBS_SOURCES += $(LIBSRCDIR)/$(RUNTIME).in

SCRIPTS = $(addprefix $(BINOBJDIR)/,$(addsuffix .sh,$(APPS)))
DATA = $(patsubst $(CONFSRCDIR)/%.in,$(CONFOBJDIR)/%,$(CONF_SOURCES))
LIBS = $(addprefix $(LIBOBJDIR)/,$(addsuffix .sh,$(RUNTIME) $(ALIBS)))

DISTDIR = dist/$(PKG)-$(VERSION)
DISTFILES := $(shell find $(DIST) -type f)

CORE_ARCHIVE = $(DISTDIR)/$(PKG)-$(VERSION)

ARCHIVE_SUFFIXES = .tar.xz .tar.gz .tar.bz2 .tar.Z
ARCHIVES = $(addprefix $(CORE_ARCHIVE),$(ARCHIVE_SUFFIXES))

######
# Utility rules

tools::
	@echo '# mcsh tools'
	@echo 'I7="$(I7)"'
	@echo 'P7="$(P7)"'
	@echo 'D12="$(D12)"'
	@echo 'C5="$(C5)"'
	@echo '# system tools'
	@echo 'MAKE="$(MAKE)"'
	@echo 'TIME="$(TIME)"'
	@echo 'ENV="$(ENV)"'
	@echo 'MKDIR="$(MKDIR)"'
	@echo 'MKDIR_P="$(MKDIR_P)"'
	@echo '# installation'
	@echo 'CP="$(CP)"'
	@echo 'CP_A="$(CP_A)"'
	@echo 'INSTALL="$(INSTALL)"'
	@echo 'INSTALL_D="$(INSTALL_D)"'
	@echo '# cleanup'
	@echo 'RM="$(RM)"'
	@echo 'RM_R="$(RM_R)"'
	@echo 'RMDIR="$(RMDIR)"'
	@echo 'RMDIR_I="$(RMDIR_I)"'

env: $(MAKEFILE_I7)
	@$(I7) env bash

%-print:
	$(info $*=$($*))

%-rebuild:
	$(MAKE) $*-clean
	$(MAKE) $*

UNIMPLEMENTED := $$@-unimplemented

%-unimplemented:
	: $*: not implemented


######
# Build

all: apps extensions

apps: $(SCRIPTS) $(LIBS) $(DATA)

$(MAKEFILE_I7): $(MAKEFILE_I7).in $(PACKAGE_I7) $(SYSTEM_I7) $(SITE_I7)
	@if [ -z "$(I7)" ]; then \
		echo "$@ is missing and i7 was not found"; \
		echo "You must run ./bootstrap.sh to build this project."; \
		false; \
	fi
	$(I7) filter <"$<" >"$@" || ($(RM) "$@" && false)

$(SYSTEM_I7):
	touch "$@"

$(SITE_I7):
	touch "$@"

######
# Extensions

EXTS_ARCHIVE = $(DISTDIR)/$(PKG)-%-$(VERSION)
EXTS_ARCHIVES = $(addprefix $(EXTS_ARCHIVE),$(ARCHIVE_SUFFIXES))

EXTS_MAKEFILES := $(shell ls */Makefile)
EXTS := $(patsubst %/,%,$(dir $(EXTS_MAKEFILES)))

include $(EXTS_MAKEFILES)

EXTS_RULE_DEPS = $(addsuffix -$(subst extensions-,,$@),$(EXTS))

extensions: extensions-build

extensions-build: $$(EXTS_RULE_DEPS)
extensions-clean: $$(EXTS_RULE_DEPS)
extensions-install: $$(EXTS_RULE_DEPS)
extensions-uninstall: $$(EXTS_RULE_DEPS)
extensions-dist: $$(EXTS_RULE_DEPS)
extensions-dist-clean: $$(EXTS_RULE_DEPS)
extensions-check: $$(EXTS_RULE_DEPS)

######
# Script Generation

GEN_TMPL = $(GENDIR)/package.conf.in
GEN_CONF = $(GENDIR)/$(PKG).conf

GENVARS = pkg version url support_name support_email \
	bindir etcdir sharedir libdir docdir datadir tmpdir
GENLISTS = apps groups group_names

GEN = sed \
		-e 's,__pkg__,$(PKG),g' \
		-e 's,__pkg_name__,$(PKG_NAME),g' \
		-e 's,__version__,$(VERSION),g' \
		-e 's,__build_date__,$(BUILD_DATE),g' \
		-e 's,__build_time__,$(BUILD_TIME),g' \
		-e 's,__author__,$(AUTHOR),g' \
		-e 's,__url__,$(URL),g' \
		-e 's,__support_name__,$(SUPPORT_NAME),g' \
		-e 's,__support_email__,$(SUPPORT_EMAIL),g' \
		-e 's,__apps__,$(APPS),g' \
		-e 's,__groups__,$(GROUPS),g' \
		-e 's,__group_names__,$(GROUP_NAMES),g' \
		-e 's,__libs__,$(ALIBS),g' \
		-e 's,__extensions__,$(EXTS),g' \
		-e 's,__bindir__,$(BINDIR),g' \
		-e 's,__etcdir__,$(ETCDIR),g' \
		-e 's,__sharedir__,$(SHAREDIR),g' \
		-e 's,__libdir__,$(LIBDIR),g' \
		-e 's,__docdir__,$(DOCDIR),g' \
		-e 's,__logdir__,$(LOGDIR),g' \
		-e 's,__rundir__,$(RUNDIR),g' \
		-e 's,__datadir__,$(DATADIR),g' \
		-e 's,__tmpdir__,$(TMPDIR),g' \
		-e 's,__runtime__,$(LIBDIR)/$(RUNTIME).sh,g'

$(GEN_TMPL): Makefile
	$(MKDIR_P) "$(dir $@)"
	for gv in $(GENVARS); do \
		a=""; \
		case "$${gv}" in \
		pkg|version) a="# " ;; \
		esac; \
		echo "$${a}$$(echo $${gv} | tr a-z A-Z)='__$${gv}__'"; \
	done >"$@" || ($(RM) "$@" && false)
	for gv in $(GENLISTS); do \
		a=""; \
		case "$${gv}" in \
		apps) a="# " ;; \
		esac; \
		echo "$${a}$$(echo $${gv} | tr a-z A-Z)=( __$${gv}__ ) "; \
	done >>"$@" || ($(RM) "$@" && false)

$(GEN_CONF): $(GEN_TMPL)
	$(MKDIR_P) "$(dir $@)"
	$(GEN) "$<" > "$@"
	@touch .

gen-clean:
	$(RM) $(GEN_CONF) $(GEN_TMPL)

######
# Information

info: build-$(PKG)-conf
	@for f in $(MAKEFILE_I7) $(GEN_CONF); do \
		echo "======\n$$f:\n------"; \
		cat "$$f"; \
	done

build-$(PKG)-conf: $(GEN_CONF) $(SYSTEM_I7)

######
# Package Libraries

$(CONFOBJDIR)/%: $(CONFSRCDIR)/%.in $(MAKEFILE_I7)
	$(MKDIR_P) "$(dir $@)"
	$(GEN) "$<" > "$@"
	@touch .

install-conf:
	$(INSTALL) -d --mode 0755 $(CONFDIR)
	for a in $(ALIBS); do \
		src="$(CONFOBJDIR)/$$a.conf"; \
		dst="$(CONFDIR)/$$a.conf"; \
		if [ -k "$$dst" ]; then \
			echo "$$dst: skipping (sticky)"; \
		elif [ -f "$$src" ]; then \
			$(INSTALL) -d "$$(dirname "$$dst")"; \
			$(INSTALL) --mode 0644 "$$src" "$$dst"; \
		fi; \
	done
	for a in $(APPS); do \
		$(INSTALL) -d $(CONFDIR)/$$a; \
		for c in $(CONF); do \
			[ "$${c#$$a}" != "$$c" ] || continue; \
			src="$(CONFOBJDIR)/$$c"; \
			f="$(CONFDIR)/$$a/$${c##*/}"; \
			if [ -k "$$f" ]; then \
				echo "$$f: skipping (sticky)"; \
			elif [ -f "$$src" ]; then \
				$(INSTALL) -d "$$(dirname "$$f")"; \
				$(INSTALL) --mode 0644 "$$src" "$$f"; \
			fi; \
		done; \
	done

uninstall-conf:
	echo "The $(PKG) package configuration files must be removed manually."

######
# License Library

license: $(LIBSRCDIR)/$(LICENSE_LIB).in

$(LIBSRCDIR)/$(LICENSE_LIB).in: LICENSE WARRANTY COPYING
	$(D12) gen bash license $(PKG) LICENSE WARRANTY COPYING >"$@" \
		|| ($(RM) "$@" && false)

######
# Package Libraries

$(LIBOBJDIR)/%.sh: $(LIBSRCDIR)/%.in $(MAKEFILE_I7)
	$(MKDIR_P) "$(dir $@)"
	$(GEN) "$<" > "$@"
	@touch .

install-libs:
	$(INSTALL) -d --mode 0755 $(LIBDIR)
	for l in $(LIBS); do \
		lf="$(LIBDIR)/$${l#$(LIBOBJDIR)/}"; \
		$(MKDIR_P) "$$(dirname "$$lf")"; \
		$(INSTALL) --mode 0644 "$$l" "$$lf"; \
	done

uninstall-libs:
	for l in $(LIBS); do \
		lf="$(LIBDIR)/$${l#$(LIBOBJDIR)/}"; \
		$(RM) "$(LIBDIR)/$$lf"; \
		$(RMDIR_I) "$$(dirname "$$lf")"; \
	done
	-$(RMDIR_I) $(LIBDIR)

######
# Package Scripts

$(BINOBJDIR)/%.sh: $(BINSRCDIR)/%.in $(MAKEFILE_I7)
	$(MKDIR_P) "$(dir $@)"
	$(GEN) "$<" > "$@"
	@touch .

install-scripts:
	$(INSTALL) -d --mode 0755 $(BINDIR)
	for a in $(APPS); do \
		src="$(BINOBJDIR)/$$a.sh"; \
		dst="$(BINDIR)/$${a##*/}"; \
		$(INSTALL) --mode 0755 "$$src" "$$dst"; \
	done

uninstall-scripts:
	for a in $(APPS); do \
		$(RM) "$(BINDIR)/$${a##*/}"; \
	done
	-$(RMDIR_I) $(BINDIR)

######
# ChangeLog

CHANGELOG_DIST = ChangeLog ChangeLog-$(VERSION)

ChangeLog: .
	$(I7) changelog all HEAD >"$@"

ChangeLog-$(VERSION): .
	$(I7) changelog release HEAD >"$@"

$(GENDIR)/%/ChangeLog: %
	$(I7) changelog all "$*" >"$@"

$(GENDIR)/%/ChangeLog-$(VERSION): %
	$(I7) changelog release HEAD "$*" >"$@"

changelog: $(CHANGELOG_DIST)
changelog-clean:
	$(RM) $(CHANGELOG_DIST)

######
# Install

install: install-main

install-all: install-main extensions-install

install-main: apps
	$(MAKE) install-libs install-scripts install-conf

uninstall: uninstall-main extensions-uninstall

uninstall-main: apps
	$(MAKE) uninstall-libs uninstall-scripts uninstall-conf

######
# Local/Remote/Could Installation

install-local: dist
	$(P7) install local $(VERSION)

install-host-%: dist
	$(P7) install remote $(VERSION) $*

install-hosts:
ifeq ($(HOSTS),)
	$(error HOSTS not set)
endif
	$(MAKE) $(addprefix install-host-,$(HOSTS))

install-cloud:
ifeq ($(CLOUD),)
	$(error CLOUD not set)
endif
	$(MAKE) HOSTS="$$($(C5) host list "$(CLOUD)")"

######
# Development

shell:
	$(PKG)

package-config:
	$(P7) config >$(PACKAGE_I7).new
	mv $(PACKAGE_I7).new $(PACKAGE_I7)

dev-config:
	@echo "Generating $(SYSTEM_I7):" >/dev/stderr
	@echo "PREFIX=$(PWD)/install" | tee $(SYSTEM_I7) >/dev/stderr
	@echo "RELEASE_TYPE=debug" | tee -a $(SYSTEM_I7) >/dev/stderr

dev-env: dev-config
	$(MAKE) env

dev-clean: uninstall changelog-clean
	$(MAKE) dist-clean
	$(RM) $(SYSTEM_I7)
	if [ "$(PREFIX)" = "$(PWD)/install" ]; then $(RM_R) install; fi

######
# Cleanup

clean: gen-clean extensions-clean
	$(RM_R) $(SCRIPTS) $(LIBS) $(DATA)
	[ ! -d "$(GENDIR)" ] || $(RMDIR_I) "$(GENDIR)"

dist-clean: clean extensions-dist-clean
	$(RM_R) $(OBJDIR)
	$(RM) $(MAKEFILE_I7)

all-clean: dev-clean
	$(RM_R) $(DISTDIR)

######
# Distribution

dist: dist-main

dist-all: dist-main extensions-dist

dist-main: apps
	$(MAKE) $(ARCHIVES)

$(ARCHIVES): $(DISTOBJDIR)/core.stamp

$(DISTOBJDIR)/core.stamp: $(DISTFILES)
	$(P7) dist core
	$(MKDIR_P) $(DISTOBJDIR)
	touch $@

$(EXTS_ARCHIVES): %-build
	$(P7) dist extensions $*

######
# Package Testing

TESTDIR = $(OBJDIR)/test

check: check-tools
	$(MAKE) extensions-check

check-%:
ifeq ($(I7),)
	$(MAKE) check-$*
else
	+$(I7) check $* "$(TESTDIR)"
endif

check-clone: install
check-build: check-clone
check-tools: check-build
	$(MAKE) -C "$(TESTDIR)" $(addprefix tool-check-,$(notdir $(APPS)))

tool-check-%:
	mcui_backend=cli install/bin/$* check

######
# Distribution Testing

distcheck: check-build
	$(MAKE) -C "$(TESTDIR)" check-dist
	$(I7) check passed

check-dist: check-build
check-archive: dist

######
# Releases

release-%: dist-clean
	+$(P7) release wizard $*
