From ea43d054e007aa35b063f78866f9aee52959b067 Mon Sep 17 00:00:00 2001 From: Marko Kreen Date: Tue, 29 Nov 2011 20:55:49 +0200 Subject: [PATCH] antimake: New build system. Emulates Automake syntax on plain GNU Make. --- m4/antimake.m4 | 14 + mk/antimake.mk | 1443 +++++++++++++++++++++++++++++++++++++++++++++++ mk/antimake.txt | 458 +++++++++++++++ 3 files changed, 1915 insertions(+) create mode 100644 m4/antimake.m4 create mode 100755 mk/antimake.mk create mode 100644 mk/antimake.txt diff --git a/m4/antimake.m4 b/m4/antimake.m4 new file mode 100644 index 0000000..792b5c8 --- /dev/null +++ b/m4/antimake.m4 @@ -0,0 +1,14 @@ + +dnl +dnl AMK_INIT: Generate initial makefile +dnl + +AC_DEFUN([AMK_INIT], [ + +# if building separately from srcdir, write top-level makefile +if test "$srcdir" != "."; then + echo "include $srcdir/Makefile" > Makefile +fi + +]) + diff --git a/mk/antimake.mk b/mk/antimake.mk new file mode 100755 index 0000000..7fefa86 --- /dev/null +++ b/mk/antimake.mk @@ -0,0 +1,1443 @@ +#! /usr/bin/make -f + +# +# antimake.mk - automake syntax with GNU Make +# +# Copyright (c) 2011 Marko Kreen +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# + +# Goals: +# - Clean user Makefiles, by using automake syntax +# - Clean output during build +# - Optional ties with `autoconf` and `libtool` +# - Automatic dependency tracking +# - Avoid separate build step for Makefiles +# - No extra tools needed except GNU Make + +# Usage without autoconf: +# - copy antimake.mk into source dir, then: include antimake.mk +# - copy/link antimake.mk into PATH, then: include $(shell antimake.mk) +# +# Usage with autoconf: +# - Copy to antimake.mk.in at top dir, then process with autoconf +# to antimake.mk and include that one in Makefiles. +# +# - Have config.mak.in that also includes antimake.mk. +# Suggestion: the separate file should include antimake.mk +# using $(abs_top_srcdir) to support separate build dir. +# +# - Include config and antimake.mk separately in user Makefiles + +## +## Startup hacks +## + +# detect GNU make version, confuse others +$(eval GNUMAKE380=1) +GNUMAKE381=$(or ,1) +define GNUMAKE382 = +1 +endef + +# give error of too old +ifeq ($(GNUMAKE381),) +$(error GNU Make 3.81+ required) +endif + + +# extra targets if this file is executed directly +ifeq ($(words $(MAKEFILE_LIST)), 1) + +.PHONY: show-location show-config + +# default: print location. For "include $(shell antimake.mk)"-style usage. +show-location: + @echo $(MAKEFILE_LIST) + +# show autoconfigurable variables +show-config: + @grep '@[^ ]*@$$' $(MAKEFILE_LIST) + +endif + + +## +## Allow this file to be processed through autoconf +## + +# +# to extract autoconfigurable values: +# $ grep '@[^ ]*@$' antimake.mk > config.mk.in +# $ antimake.mk show-config > config.mk.in +# +ifneq ($(filter-out @%,@PACKAGE_NAME@),) + +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ + +PORTNAME = @PORTNAME@ +EXEEXT = @EXEEXT@ +HAVE_CC_DEPFLAG = @HAVE_CC_DEPFLAG@ + +# C language +CC = @CC@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CFLAGS = @CFLAGS@ +DEFS = @DEFS@ +WFLAGS = @WFLAGS@ + +# C++ language +CXX = @CXX@ +CXXFLAGS = @CXXFLAGS@ + +# linking +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ + +# static and shared libs +AR = @AR@ +ARFLAGS = @ARFLAGS@ +RANLIB = @RANLIB@ +LIBTOOL = @LIBTOOL@ + +# other tools +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_DATA = @INSTALL_DATA@ +MKDIR_P = @MKDIR_P@ +SED = @SED@ +AWK = @AWK@ +GREP = @GREP@ +EGREP = @EGREP@ +STRIP = @STRIP@ + +# install locations +prefix = @prefix@ +exec_prefix = @exec_prefix@ +bindir = @bindir@ +includedir = @includedir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datarootdir = @datarootdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +docdir = @docdir@ +mandir = @mandir@ +libdir = @libdir@ +localedir = @localedir@ +pkgdatadir = @pkgdatadir@ +pkgconfigdir = @pkgconfigdir@ + +# autoconf values for top dir +abs_top_srcdir ?= @abs_top_srcdir@ +abs_top_builddir ?= @abs_top_builddir@ +nosub_top_srcdir ?= @top_srcdir@ +nosub_top_builddir ?= @top_builddir@ + +endif # end of @xx@ values + +## +## In case of missing autoconf values, provide sane defaults +## + +PACKAGE_NAME ?= package +PACKAGE_TARNAME ?= $(PACKAGE_NAME) +PACKAGE_VERSION ?= 0.0 +PACKAGE_STRING ?= $(PACKAGE_NAME) $(PACKAGE_VERSION) +PACKAGE_URL ?= +PACKAGE_BUGREPORT ?= + +PORTNAME ?= unix +EXEEXT ?= +HAVE_CC_DEPFLAG ?= yes + +# C language +CC ?= cc +CPP ?= cpp +CPPFLAGS ?= +CFLAGS ?= -O -g +DEFS ?= +WFLAGS ?= -Wall + +# C++ language +CXX ?= c++ +CXXFLAGS ?= -O -g + +# linking +LD ?= ld +LDFLAGS ?= +LIBS ?= + +# static and shared libs +LIBTOOL ?= libtool +AR ?= ar +ARFLAGS ?= rcs +ifeq ($(ARFLAGS),rv) +ARFLAGS = rcs +endif +RANLIB ?= ranlib + +# other tools +INSTALL ?= install +INSTALL_PROGRAM ?= $(INSTALL) +INSTALL_SCRIPT ?= $(INSTALL) +INSTALL_DATA ?= $(INSTALL) +MKDIR_P ?= mkdir -p +SED ?= sed +AWK ?= awk +GREP ?= grep +EGREP ?= grep -E +STRIP ?= strip + +# install locations +prefix ?= /usr/local +exec_prefix ?= ${prefix} +bindir ?= ${exec_prefix}/bin +includedir ?= ${prefix}/include +sbindir ?= ${exec_prefix}/sbin +libexecdir ?= ${exec_prefix}/libexec +datarootdir ?= ${prefix}/share +datadir ?= ${datarootdir} +sysconfdir ?= ${prefix}/etc +docdir ?= ${datarootdir}/doc/${PACKAGE_TARNAME} +mandir ?= ${datarootdir}/man +libdir ?= ${exec_prefix}/lib +localedir ?= ${datarootdir}/locale +pkgdatadir ?= ${datarootdir}/${PACKAGE_TARNAME} +pkgconfigdir ?= ${libdir}/pkgconfig + +# autoconf values for top dir +abs_top_srcdir ?= $(CURDIR) +abs_top_builddir ?= $(CURDIR) +nosub_top_builddir ?= . +nosub_top_srcdir ?= . + +## +## Variables for user makefiles +## + +# current subdirectory location from top dir (foo/bar) +SUBLOC ?= . + +# subdirectories in current directory +SUBDIRS ?= + +# extra files for clean targets +CLEANFILES ?= +DISTCLEANFILES ?= +MAINTAINERCLEANFILES ?= + +# Additional flags for Makefile use, to avoid need +# to touch flags coming from autoconf/cmdline +AM_DEFS ?= +AM_CPPFLAGS ?= +AM_CFLAGS ?= +AM_LDFLAGS ?= +AM_LIBTOOLFLAGS ?= + +AM_MAKEFLAGS ?= +AM_LIBS ?= + +# libusual sources, for embedded usage +USUAL_DIR ?= . + +# V=1 -> verbose build +V ?= 0 + +# turn on function tracing +AM_TRACE ?= + +# default formats for 'dist' +AM_DIST_DEFAULT ?= gzip + +## +## Non-user-serviceable area +## + +# Hacking: +# +# - Uppercase names are simple (late) variables, lowercase names - targets, +# mixedcase - functions that need to be $(call)-ed. +# +# - Minimal amount of shell should be used here. +# +# - Minimal amount of := and $(eval) +# +# - It's useful to indent the expressions for easier understanding. +# Later the indendation needs to be removed, as whitespace is significant for Make. +# Several functions must not add any extra whitespace. +# +# GNU Make features in new versions: +# +# 3.80 - 2002-10-03: base version. $(eval) $(value) $(MAKEFILE_LIST) $(.VARIABLES) $(call fixes) +# 3.81 - 2006-04-01: $(or), $(and), $(lastword), $(abspath), $(realpath), $(info), $(flavor) +# 3.82 - 2010-07-28: private, undefine, define var := +# +# This file should use only features from 3.80 + +## +## command helpers +## + +CCLD ?= $(CC) +COMPILE ?= $(CC) $(AM_DEFS) $(DEFS) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LINK ?= $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ + +AM_AR ?= $(AR) $(ARFLAGS) + +LIBTOOLCMD ?= $(LIBTOOL) $(LIBTOOLQ) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) + +RM = rm -f + + +## +## Internals +## + +# varables that can be set per-target with target_VAR +# they appear as AM_foo. [Not supported: COMPILE] +AM_TARGET_VARIABLES += CFLAGS CPPFLAGS LDFLAGS LIBTOOLFLAGS DEFS LIBS +AM_TARGET_RELOC_VARIABLES += CFLAGS CPPFLAGS LDFLAGS + +# list of language (rather compiler) names +AM_LANGUAGES += C CXX + +AM_BIG_PRIMARIES += LIBRARIES LTLIBRARIES PROGRAMS +AM_SMALL_PRIMARIES += HEADERS SCRIPTS DATA MANS + +# list of destinations per primary +AM_DESTINATIONS += bin lib libexec sbin \ + data doc include locale man sysconf \ + pkgdata pkgconfig \ + noinst EXTRA + +# primaries where 'dist' is default +AM_DIST_PRIMARIES += HEADERS + +AM_PRIMARIES = $(AM_BIG_PRIMARIES) $(AM_SMALL_PRIMARIES) + +# distclean does rm -rf on that +OBJDIR = .objs + +# non-configurable +OBJEXT = o + +# files that need to be converted to objects +AM_SRCEXTS = $(foreach lang,$(AM_LANGUAGES),$(AM_LANG_$(lang)_SRCEXTS)) + +# target types - big/small: with/without objects +# list of flags, 'noinst' is taken as dest, 'base' is always default +AM_FLAGS = base nobase dist nodist + +## configure non-defult target params +AM_PROGRAMS_InstFunc = ProgInstall +AM_LTLIBRARIES_InstFunc = LTLibInstall +AM_LTLIBRARIES_OBJEXT = .lo +AM_SCRIPTS_InstFunc = ScriptInstall +AM_MANS_InstFunc = ManInstall + +# files to distribute +am_DISTFILES := +am_FINAL_DISTFILES = $(sort $(am_DISTFILES)) +AM_DIST_BASE = $(PACKAGE_TARNAME)-$(PACKAGE_VERSION) + +AM_ALL_TARGETS = + +## +## Make dependencies work +## + +HAVE_CC_DEPFLAG ?= yes +ifeq ($(HAVE_CC_DEPFLAG),yes) +OBJDEPS = -MD -MP -MT $@ -MF $@.d +endif + + +## +## Quiet by default, 'make V=1' shows commands +## + +CTX ?= +ifeq ($(V), 0) +E = @printf "%-4s %-8s %s\n" "$(CTX)" +Q = @ +LIBTOOLQ = --silent +MAKEFLAGS += --no-print-directory +else +E = @true +Q = +LIBTOOLQ = --silent +endif + + +## +## libtool activation +## + +# libtool activates when detects %.lo / %.la pattern +LTCOMPILE = $(if $(filter %.lo,$@),$(LIBTOOLCMD) --mode=compile) +LTLINK = $(if $(filter %.la %.lo,$^),$(LIBTOOLCMD) --mode=link) +LTCLEAN = $(LIBTOOLCMD) --mode=clean + + +## +## Default setup for C +## + +AM_LANG_C_SRCEXTS = .c +define AM_LANG_C_COMPILE + $(E) "CC" $< + $(Q) $(LTCOMPILE) $(COMPILE) $(OBJDEPS) -c -o $@ $< +endef +define AM_LANG_C_LINK + $(E) "CCLD" $@ + $(Q) $(LTLINK) $(LINK) $^ $(AM_LIBS) $(LIBS) $(AM_LT_RPATH) +endef + + +## +## Default setup for C++ +## + +AM_TARGET_VARIABLES += CXXFLAGS + +CXXLD ?= $(CXX) +CXXCOMPILE ?= $(CXX) $(AM_DEFS) $(DEFS) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLINK ?= $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ + +AM_LANG_CXX_SRCEXTS = .cc .cpp cxx +define AM_LANG_CXX_COMPILE + $(E) "CXX" $< + $(Q) $(LTCOMPILE) $(CXXCOMPILE) $(OBJDEPS) -c -o $@ $< +endef +define AM_LANG_CXX_LINK + $(E) "CXXLD" $@ + $(Q) $(LTLINK) $(CXXLINK) $^ $(AM_LIBS) $(LIBS) $(AM_LT_RPATH) +endef + + +## +## Various other shortcuts +## + +define ar_lib + @$(MKDIR_P) $(dir $@) + $(E) "AR" $@ + $(Q) $(AM_AR) $@ $^ + $(E) "RANLIB" $@ + $(Q) $(RANLIB) $@ +endef + +# 1 - dir +define ProgInstall + $(E) "INSTALL" "$< $(1)" + $(Q) $(MKDIR_P) $(1) + $(Q) $(INSTALL_PROGRAM) $< $(1) +endef + +# 1 - dir +define ScriptInstall + $(E) "INSTALL" "$< $(1)" + $(Q) $(MKDIR_P) $(1) + $(Q) $(INSTALL_SCRIPT) $< $(1) +endef + +# 1 - dir +define DataInstall + $(E) "INSTALL" "$< $(1)" + $(Q) $(MKDIR_P) $(1) + $(Q) $(INSTALL_DATA) $< $(1) +endef + +# 1 - dir, add manX subdir +ManInstall = $(call DataInstall,$(1)/man$(call LastWord,$(subst ., ,$<))) + +# 1 - dir +define LTLibInstall + $(E) "INSTALL" "$< $(1)" + $(Q) $(MKDIR_P) $(1) + $(Q) $(LIBTOOLCMD) --mode=install $(INSTALL) $< $(1) +endef + + +## +## Create .srcext -> .obj mapping for a language +## + +# 1-tgt, 2-name, 3-srcext +define LangObjTarget +$(trace3) +$$(OBJDIR)/$(1)/%.o $$(OBJDIR)/$(1)/%.lo: %$(3) + @$$(MKDIR_P) $$(dir $$@) + $$(AM_LANG_$(2)_COMPILE) +endef + +# 1=tgt, 2=name +define LangSetup +$(trace2) +$(foreach ext,$(AM_LANG_$(2)_SRCEXTS),$(call LangObjTarget,$(1),$(2),$(ext))$(NewLine)) +endef + + +## +## Utility functions +## + +# for function debugging, put them at the start of body +ifdef AM_TRACE +trace1=$(warning $0('$1')) +trace2=$(warning $0('$1','$2')) +trace3=$(warning $0('$1','$2','$3')) +trace4=$(warning $0('$1','$2','$3','$4')) +trace5=$(warning $0('$1','$2','$3','$4','$5')) +endif + +# for use inside $(eval) +IFDEF = ifdef +IFEQ = ifeq +IFNEQ = ifneq +ELSE = else +ENDIF = endif + +# returns 'true' if $1==$2 +Eq = $(if $(1)$(2),$(if $(findstring $(1),$(2)),$(if $(findstring $(2),$(1)),true)),true) + +Not = $(if $(1),,true) +Neq = $(call Not,$(call Eq,$(1),$(2))) + +# replace [-./] with '_' +CleanName = $(subst /,_,$(subst -,_,$(subst .,_,$(1)))) + +# return last word from word list +LastWord = $(if $(1),$(word $(words $(1)),$(1))) + +Empty = +Space = $(Empty) $(Empty) +# twice to unconfuse syntax hiliters +SQuote = ' +SQuote = ' +define NewLine + + +endef + +# quote str for shell +ShellQuote = '$(subst $(SQuote),'\$(SQuote)',$(1))' + +# replace extensions +# 1-src ext list +# 2-target ext +# 3-source list +ReplaceExts = $(foreach ext,$(1),$(patsubst %$(ext),%$(2),$(filter %$(ext),$(3)))) + +# objs with objdir from source list (1-cleantgt, 2-src list) +SourceObjs = $(trace1)$(call SourceObjsExt,$(1),.$(OBJEXT),$(2)) + +# objs with objdir from source list +# 1-cleantgt, 2-objext, 3-srcs list +SourceObjsExt = $(addprefix $(call JoinPath,$(OBJDIR),$(1))/, $(call ReplaceExts,$(AM_SRCEXTS),$(2),$(3))) + +# dependency files from object files, must match OBJDEPS +DepFiles = $(wildcard $(addsuffix .d,$(1))) + +# per-target var override, 1=target, 2=varname +# if foo_VAR exists, expand to: +# build_foo install_foo clean_foo: AM_VAR = $(foo_VAR) +#TgtVar = $(if $($(1)_$(2)),build_$(1): AM_$(2) = $($(1)_$(2))) +TgtVar = $(if $($(1)_$(2)),$$($(1)_FINAL): AM_$(2) = $($(1)_$(2))) + +# loop TgtVar over AM_TARGET_VARIABLES, 1=target +VarOverride = $(foreach var,$(AM_TARGET_VARIABLES),$(eval $(call TgtVar,$(1),$(var)))) + +# check if actual target (.h, .exe) is nodist based on primary and flags +# 1-prim 2-flags +TargetNoDist = $(strip $(if $(filter nodist,$(2)), \ + true, \ + $(if $(filter dist,$(2)), \ + , \ + $(filter-out $(AM_DIST_PRIMARIES),$(1))))) + +# return sources that match language +# 1-lang +# 2-sources +LangFiles = $(filter $(addprefix %,$(AM_LANG_$(1)_SRCEXTS)),$(2)) + +# return list of langs that match sources. +# 1-sources +LangList = $(strip $(foreach lang,$(AM_LANGUAGES),$(if $(call LangFiles,$(lang),$(1)),$(lang)))) + +# 1-sources +LinkLangList = $(foreach lang,$(call LangList,$(1)),$(if $(AM_LANG_$(lang)_LINK),$(lang))) + +# pick linker variable based on sources, fallback to C +# 1-sources +DetectLinkVar = AM_LANG_$(call LastWord,C $(call LinkLangList,$(1)))_LINK + +# convert 'foo/bar' -> '../..' +UpDirStep1 = $(subst /, ,$(1)) +UpDirStep2 = $(foreach dir,$(call UpDirStep1,$(1)),../) +UpDirStep3 = $(subst / ,/,$(call UpDirStep2,$(1))) +UpDirStep4 = $(patsubst %/,%,$(call UpDirStep3,$(1))) +UpDir = $(if $(filter-out .,$(1)),$(call UpDirStep4,$(1)),.) + +# a,b -> a/b, skips component if '.' +JoinPath2 = $(if $(filter-out .,$(1)),$(if $(filter-out .,$(2)),$(1)/$(2),$(1)),$(2)) + +# a,b -> a/b, unless b is absolute +JoinPath = $(if $(filter /%,$(2)),$(2),$(call JoinPath2,$(1),$(2))) + + + +## +## Parse target list variables +## + +## pick out components from name, call function +# 1-varname, 2-words, 3-func, 4-func arg +# func args: 1-var, 2-prim, 3-dest, 4-flags, 5-arg +ParseName = $(call $(3),$(1),$(filter $(AM_PRIMARIES),$(2)),$(filter $(AM_DESTINATIONS),$(2)),$(filter $(AM_FLAGS),$(2)),$(4)) + +ForEachList = $(foreach var,$(2),$(call ParseName,$(var),$(subst _, ,$(var)),$(1),$(3))) + +## try reconstruct name, if fails, its a random variable +# 1-var, 2-prim,3-dest,4-flags +CheckName = $(if $(call Eq,$(subst _, ,$(1)),$(strip $(4) $(call LastWord,$(3)) $(call LastWord,$(2)))),$(1)) + +## also check if variable is filled +# 1-var, 2-prim,3-dest,4-flags +CheckNameFull = $(if $(call CheckName,$(1),$(2),$(3),$(4)),$(if $($(1)),$(1))) + +## +## Loop over targets in list variables +## + +## call function on parsed target +# 1-var, 2-prim, 3-dest, 4-flags, 5-func +# func args: 1-cleantgt, 2-tgt, 3-prim, 4-dest, 5-flags +ForEachTarget2 = $(foreach tgt,$($(1)),$(call $(5),$(call CleanName,$(tgt)),$(tgt),$(2),$(3),$(4))) + +## ForEachTarget: call function on all targets in lists +# 1-func, 2- var list +# func args: 1-cleantgt, 2-tgt, 3-prim, 4-dest, 5-flags +ForEachTarget = $(call ForEachList,ForEachTarget2,$(2),$(1)) + + +## +## Utility functions for libusual link +## + +# module names from sources (plus headers) +UsualMods = $(trace1)$(shell $(USUAL_DIR)/find_modules.sh $(USUAL_DIR) $(wildcard $(addprefix $(srcdir)/,$(1)))) + +# full-path sources based on module list +UsualSrcsFull = $(wildcard $(addprefix $(USUAL_DIR)/usual/,$(addsuffix *.[ch],$(1)))) + +# remove USUAL_DIR +UsualStrip = $(subst $(USUAL_DIR)/,,$(1)) + +# simple-path sources based on module list +UsualSrcs = $(call UsualStrip,$(call UsualSrcsFull,$(1))) + +# objs with objdir from source file list (1-cleantgt, 2-src list) +UsualObjs = $(call SourceObjs,$(1),$(call UsualSrcs,$(call UsualMods,$(2)))) + +# usual sources from user source file list +UsualSources = $(call UsualSrcsFull,$(call UsualMods,$(1))) + + +## EMBED_SUBDIRS relocations + +## add subdir to files +# 1-subdir, 2-file list +RelocFiles = $(foreach f,$(2),$(call JoinPath,$(1),$(f))) + + +# 1-dir, 2-pfx, 3-full +RelocOneFlag2 = $(2)$(call JoinPath,$(1),$(patsubst $(2)%,%,$(3))) + +# 1-dir, 2-flag +RelocOneFlag = $(if $(filter -L%,$(2)), \ + $(call RelocOneFlag2,$(1),-L,$(2)), \ + $(if $(filter -I%,$(2)), \ + $(call RelocOneFlag2,$(1),-I,$(2)), \ + $(if $(filter -%,$(2)), \ + $(2), \ + $(call JoinPath,$(1),$(2))))) + +## Relocate relative files, relative -I/-L, ignore -* +# 1-dir, 2- flaglist +RelocFlags = $(strip $(if $(filter-out .,$(1)), \ + $(foreach flg,$(2),$(call RelocOneFlag,$(1),$(flg))),$(2))) + + +## Separate build dir relocation + +## non-local source dir: -Isrc/include -> -Isrc/include -I$(srcdir)/src/include +# 1-srcdir, 2-flag list +FixIncludes = $(strip $(if $(filter-out .,$(1)), \ + $(foreach flg,$(2),$(call FixIncludes2,$(1),$(flg))), \ + $(2))) +# 1-dir, 2-flg +FixIncludes2 = $(if $(filter -I%,$(2)), \ + $(call FixIncludes3,$(1),$(patsubst -I%,%,$(2))), \ + $(2)) +# 1-dir, 2-orig dir +FixIncludes3 = -I$(2) -I$(call JoinPath,$(srcdir),$(2)) + + +## +## Makefile fragments +## + +### fill values +# abs_top_srcdir, abs_top_builddir +# nosub_top_builddir, nosub_top_srcdir +# 1 - subdir +define SetDirs +abs_builddir := $$(call JoinPath,$$(abs_top_builddir),$(1)) +abs_srcdir := $$(call JoinPath,$$(abs_top_srcdir),$(1)) +top_builddir := $$(call UpDir,$(1)) +top_srcdir := $$(call JoinPath,$$(top_builddir),$$(nosub_top_srcdir)) +builddir := . +$(IFEQ) ($$(nosub_top_srcdir),$$(nosub_top_builddir)) +srcdir := . +$(ELSE) +srcdir := $$(call JoinPath,$$(top_srcdir),$(1)) +$(ENDIF) +endef + + +## +## Embedded subdirs +## + +# func args: 1-cleantgt, 2-tgt, 3-prim, 4-dest, 5-flags +define RelocBigTarget +$(trace5) +# move vars: +$(foreach var,$(AM_TARGET_VARIABLES),$(NewLine)$$(am_PFX)_$(1)_$(var) := $$($(1)_$(var))) + +# move and relocate +$$(am_PFX)_$(1)_SOURCES := $$(call RelocFiles,$$(am_DIR),$$($(1)_SOURCES)) +$$(am_PFX)_$(1)_LDADD := $$(call RelocFlags,$$(am_DIR),$$($(1)_LDADD)) +$$(am_PFX)_$(1)_LIBADD := $$(call RelocFlags,$$(am_DIR),$$($(1)_LIBADD)) +$$(am_PFX)_$(1)_CFLAGS := $$(call RelocFlags,$$(am_DIR),$$($(1)_CFLAGS)) +$$(am_PFX)_$(1)_CPPFLAGS := $$(call RelocFlags,$$(am_DIR),$$($(1)_CPPFLAGS)) +$$(am_PFX)_$(1)_LDFLAGS := $$(call RelocFlags,$$(am_DIR),$$($(1)_LDFLAGS)) + +# clean vars +$(1)_SOURCES = +$(1)_LDADD = +$(1)_LIBADD = +$(foreach var,$(AM_TARGET_VARIABLES),$(NewLine)$(1)_$(var) = ) +endef + + +## pick actual func +# func args: 1-cleantgt, 2-tgt, 3-prim, 4-dest, 5-flags +define RelocTarget +$(trace5) +$(if $(filter $(AM_BIG_PRIMARIES),$(3)),$(call RelocBigTarget,$(1),$(2),$(3),$(4),$(5))) +endef + + +## relocate target list +# func args: 1-var, 2-prim, 3-dest, 4-flags, 5-arg +define RelocTList +$(trace5) + +# detect top and subdir target conflict - it's easier to detect +# and error out than to work around the rare case +$(IFNEQ) (,$$(filter $(2),$$(AM_BIG_PRIMARIES))) +$(IFEQ) (.,$$(am_DIR)) +am_TOP_NAMES += $$(foreach tgt,$$($(1)),$$(call CleanName,$$(tgt))) +$(ELSE) +$(IFNEQ) (,$$(filter $$(am_TOP_NAMES),$$(foreach tgt,$$($(1)),$$(call CleanName,$$(tgt))))) +$$(error $$(NewLine)$$(NewLine)\ +*** Target names used in top Makefile cannot be re-used in embedded Makefiles. $$(NewLine)\ +*** The target variables (eg. _SOURCES) conflict is not handled yet) +$(ENDIF) +$(ENDIF) +$(ENDIF) + +# move value under real_% +$(IFEQ) ($(real_$(1)),) +real_$(1) := +$(ENDIF) +real_$(1) += $$(call RelocFiles,$$(am_DIR),$$($(1))) +$(1) = + +# remember in proper list +$(IFEQ) ($(3),EXTRA) +am_EXTRA_TARGETLISTS += real_$(1) +$(ELSE) +am_TARGETLISTS += real_$(1) +$(ENDIF) +endef + + +## process included values +# 1-dir, 2-pfx, 3-tlist +define EmbedProcess +$(trace3) + +$(IFNEQ) ($$(filter $(1),$$(am_EMBED_DONE)),) +$$(error Double entry in EMBED_SUBDIRS: $(1)) +$(ENDIF) + +# init local vars +am_DIR := $(1) +am_LOC := $$(call JoinPath,$$(SUBLOC),$(1)) +am_PFX := $(2) +am_EMBED_DONE += $(1) + +# reloc & save vars +am_DISTFILES += $$(call RelocFiles,$$(am_DIR),$$(EXTRA_DIST)) +am_CLEANFILES += $$(call RelocFiles,$$(am_DIR),$$(CLEANFILES)) +am_DISTCLEANFILES += $$(call RelocFiles,$$(am_DIR),$$(DISTCLEANFILES)) +am_MAINTAINERCLEANFILES += $$(call RelocFiles,$$(am_DIR),$$(MAINTAINERCLEANFILES)) +am_EMBED_TODO += $$(call RelocFiles,$$(am_DIR),$$(EMBED_SUBDIRS)) +am_SUBDIRS += $$(call RelocFiles,$$(am_DIR),$$(SUBDIRS)) +am_DIST_SUBDIRS += $$(call RelocFiles,$$(am_DIR),$$(DIST_SUBDIRS)) +# clean vars for new dir +EXTRA_DIST = +CLEANFILES = +DISTCLEANFILES = +MAINTAINERCLEANFILES = +EMBED_SUBDIRS = +SUBDIRS = +DIST_SUBDIRS = + +$(call SetDirs,$(call JoinPath,$(SUBLOC),$(1))) +$(call ForEachTarget,RelocTarget,$(3)) +$(call ForEachList,RelocTList,$(3)) +endef + + +## read Makefile.am, process it +# 1 - dir +DoEmbed = $(trace1)$(strip \ + $(if $(wildcard $(am_srcdir)/$(1)/Makefile.am), \ + $(eval include $(am_srcdir)/$(1)/Makefile.am $(NewLine)) \ + $(eval $(call EmbedProcess,$(1),$(call CleanName,$(1)),$(AM_ALL_TLISTS))), \ + $(error $(SUBLOC)/Makefile failure: $(call JoinPath,$(SUBLOC),$(1)/Makefile.am) not found.))) + +## +## Fragments that build targets +## + +# Note that variable initialization order is important here +# as some of them will be used immediately. + +## +## Install target object +## + +# 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags +define InstallTarget +$(trace5) + +$(1)_DEST := $$(if $$($(4)dir),$$($(4)dir),$$(error '$(4)dir' empty))$(if $(filter nobase,$(5)),/$(dir $(2))) + +$(1)_InstFunc := $$(if $$(AM_$(3)_InstFunc),$$(AM_$(3)_InstFunc),DataInstall) + +# actual installation +.PHONY: install_$(1) +install: install_$(1) +install_$(1): $(2) + $$(call $$($(1)_InstFunc),$$(DESTDIR)$$($(1)_DEST)) + +# hack to pass -rpath to LTLIBRARIES on build time (1) +$(2): AM_DEST = $$($(1)_DEST) + +endef + +# hack to pass -rpath to LTLIBRARIES on build time (2) +%.la: AM_LT_RPATH = $(if $(AM_DEST),-rpath $(AM_DEST)) + + +## +## Rules for big target +## + +# 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags +define BigTargetBuild +$(trace5) +AM_ALL_TARGETS += $(1) + +$(1)_ALLSRCS := $$($(1)_SOURCES) $$(EXTRA_$(1)_SOURCES) $$(nodist_$(1)_SOURCES) $$(nodist_EXTRA_$(1)_SOURCES) + +# calculate OBJS from SOURCES +$(1)_OBJEXT := $$(if $$(AM_$(3)_OBJEXT),$$(AM_$(3)_OBJEXT),.$$(OBJEXT)) +$(1)_OBJS := $$(call SourceObjsExt,$(1),$$($(1)_OBJEXT), \ + $$($(1)_SOURCES) $$(nodist_$(1)_SOURCES)) + +# include additional objects, move flags to _LIBS +$(IFEQ) ($(3),PROGRAMS) +$(1)_OBJS += $$(filter-out -%,$$($(1)_LDADD)) +$(1)_LIBS += $$(filter -%,$$($(1)_LDADD)) +$(ELSE) +$(1)_OBJS += $$(filter-out -%,$$($(1)_LIBADD)) +$(1)_LIBS += $$(filter -%,$$($(1)_LIBADD)) +$(ENDIF) + +# embed libusual objects directly +$(IFEQ) ($$($(1)_EMBED_LIBUSUAL),1) +$(1)_USUAL_SRCS = $$(call UsualSources,$$($(1)_ALLSRCS)) +$(1)_OBJS += $$(call UsualObjs,$(1),$$($(1)_SOURCES) $$(nodist_$(1)_SOURCES)) +$(1)_CPPFLAGS += -I$$(USUAL_DIR) +$(IFEQ) ($$(filter $$(USUAL_DIR),$(VPATH)),) +VPATH += $$(USUAL_DIR) +$(ENDIF) +$(ENDIF) + +# autodetect linker, unless given +$(IFEQ) ($($(1)_LINK),) +$(1)_LINKVAR := $$(call DetectLinkVar,$$($(1)_ALLSRCS)) +$(ELSE) +$(1)_LINKVAR := $(1)_LINK +$(ENDIF) + +# calculate target file name +$(IFEQ) ($(3)$($(1)_EXT),PROGRAMS) +$(1)_FINAL = $(2)$$(EXEEXT) +$(ELSE) +$(1)_FINAL = $(2)$$($(1)_EXT) +$(ENDIF) + +# hook libtool into LTLIBRARIES cleanup +$(IFEQ) ($(3),LTLIBRARIES) +$(1)_RM = $$(LTCLEAN) $$(RM) +$(ELSE) +$(1)_RM = $$(RM) +$(ENDIF) + +# fix includes in case of separate build dir +$(1)_CPPFLAGS := $$(call FixIncludes,$$(srcdir),$$($(1)_CPPFLAGS)) +$(1)_CFLAGS := $$(call FixIncludes,$$(srcdir),$$($(1)_CFLAGS)) + +# load dependencies +-include .dummy. $$(call DepFiles, $$($(1)_OBJS)) + +# actual build, clean & install targets +.PHONY: build_$(1) clean_$(1) + +# allow target-specific variables +$$(call VarOverride,$(1)) + +# build by default, unless EXTRA_foo +$(IFNEQ) ($(4),EXTRA) +all: build_$(1) +$(ENDIF) + +build_$(1): $$($(1)_SOURCES) $$(nodist_$(1)_SOURCES) +build_$(1): $$($(1)_DEPENDENCIES) +build_$(1): $$($(1)_FINAL) +$$($(1)_FINAL): $$($(1)_OBJS) + @$$(MKDIR_P) $$(dir $$@) + $$($(if $(filter LIBRARIES,$(3)),ar_lib,$$($(1)_LINKVAR))) + +clean: clean_$(1) +clean_$(1): + $$(E) "CLEAN" "$$($(1)_FINAL)" + $$(Q) $$($(1)_RM) -- $$($(1)_OBJS) $(if $(call TargetNoDist,$(3),$(5)),$$($(1)_FINAL)) + +DISTCLEANFILES += $$(nodist_$(1)_SOURCES) $$(nodist_EXTRA_$(1)_SOURCES) + +$(foreach lang,$(AM_LANGUAGES),$(call LangSetup,$(1),$(lang))) + +endef + +# 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags +define BigTargetDist +am_DISTFILES += $$(filter-out $$(nodist_EXTRA_$(1)_SOURCES) $$(nodist_$(1)_SOURCES),$$($(1)_SOURCES) \ + $$(EXTRA_$(1)_SOURCES)) $(if $(call TargetNoDist,$(3),$(5)),,$$($(1)_FINAL)) +endef + +# 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags +define MakeBigTarget +$(trace5) + +# build if first time +$(IFEQ) ($(filter $(1),$(AM_ALL_TARGETS)),) +$(call BigTargetBuild,$(1),$(2),$(3),$(4),$(5)) +$(call BigTargetDist,$(1),$(2),$(3),$(4),$(5)) +$(ELSE) +# allow only EXTRA be double +$(IFNEQ) ($(4),EXTRA) +$$(error Target '$2' described listed several times) +$(ENDIF) +$(ENDIF) + +# call InstallTarget, for dest != (EXTRA, noinst) +$(IFEQ) ($(filter EXTRA noinst,$(4)),) +$(call InstallTarget,$(1),$$($(1)_FINAL),$(3),$(4),$(5)) +$(ENDIF) + +endef + + +## +## Rules for small target +## + +# 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags +define MakeSmallTarget +$(trace5) +AM_ALL_TARGETS += $(1) + +# should the target file be distributed or cleaned? +$(IFEQ) ($(call TargetNoDist,$(3),$(5)),) +am_DISTFILES += $(2) +$(ELSE) +CLEANFILES += $(2) +$(ENDIF) + +# build if not EXTRA +$(IFNEQ) ($(4),EXTRA) +all: $(2) +# install if not EXTRA or noinst +$(IFNEQ) ($(4),noinst) +$(call InstallTarget,$(1),$(2),$(3),$(4),$(5)) +$(ENDIF) +$(ENDIF) + +endef + + +## +## Fill GNU-style vars for subdir +## + +# preferred to top_srcdir/top_builddir +topdir = $(top_builddir) + +ifneq ($(nosub_top_builddir),.) +$(error Non-local builddir not supported) +endif + +# initial locaton vars +$(eval $(call SetDirs,$(SUBLOC))) + +ifneq ($(nosub_top_srcdir),$(nosub_top_builddir)) +# use VPATH to find non-local sources +VPATH += $(srcdir) +# fix includes +AM_CPPFLAGS := $(call FixIncludes,$(srcdir),$(AM_CPPFLAGS)) +AM_CFLAGS := $(call FixIncludes,$(srcdir),$(AM_CFLAGS)) +endif + + +## +## Actual rules start +## + +# if output is redirected, prepare target dir and launch submake + +ifneq ($(O),) + +ABS_DST := $(call JoinPath,$(CURDIR),$(O)) +.PHONY: $(MAKECMDGOALS) + +all $(filter-out all,$(MAKECMDGOALS)): + @test -d $(O) || { echo "Directory $(O) does not exist"; exit 1; } + @for mk in $(filter-out /%,$(MAKEFILE_LIST)); do \ + if ! test -f $(O)/$${mk}; then \ + printf '%s\n%s\n%s\n%s\n%s\n' \ + 'abs_top_srcdir = $(CURDIR)' \ + 'abs_top_builddir = $(ABS_DST)' \ + 'nosub_top_srcdir = $(call UpDir,$(O))' \ + 'nosub_top_builddir = .' \ + 'include $$(abs_top_srcdir)/'"$${mk}" \ + > $(O)/$${mk}; \ + fi; \ + done + $(Q) $(MAKE) O= -C $(O) $(MAKECMDGOALS) + +# O=empty, this is main makefile +else + +## +## main targets, tie them with subdir and local targets +## + +# disable random rules +.SUFFIXES: + +all: sub-all all-local +clean: sub-clean clean-local +install: sub-install install-local +distclean: sub-distclean distclean-local +maintainer-clean: sub-maintainer-clean maintainer-clean-local +.PHONY: all clean install dist distclean maintainer-clean + +# -local are empty targets by default +.PHONY: all-local clean-local install-local distclean-local maintainer-clean-local +all-local clean-local install-local distclean-local maintainer-clean-local: + +## +## Actual embedding starts +## + +AM_ALL_TLISTS2 = $(filter $(addprefix %,$(AM_PRIMARIES)),$(.VARIABLES)) +AM_ALL_TLISTS = $(call ForEachList,CheckName,$(AM_ALL_TLISTS2)) + +am_srcdir := $(srcdir) +am_DIR := . +am_PFX := +am_TARGETLISTS := +am_EXTRA_TARGETLISTS := +am_TOP_NAMES := + +# move top-level targets away +$(eval $(call ForEachList,RelocTList,$(AM_ALL_TLISTS))) + +am_SUBDIRS := $(SUBDIRS) +am_DIST_SUBDIRS := $(DIST_SUBDIRS) +am_DISTFILES := $(EXTRA_DIST) +am_CLEANFILES := $(CLEANFILES) +am_DISTCLEANFILES := $(DISTCLEANFILES) +am_MAINTAINERCLEANFILES := $(MAINTAINERCLEANFILES) +am_EMBED_NOW := $(EMBED_SUBDIRS) +am_EMBED_DONE := +am_EMBED_TODO := +EXTRA_DIST = +CLEANFILES = +DISTCLEANFILES = +MAINTAINERCLEANFILES = +SUBDIRS = +DIST_SUBDIRS = +EMBED_SUBDIRS = + +$(foreach dir,$(am_EMBED_NOW),$(call DoEmbed,$(dir))) +am_EMBED_NOW := $(am_EMBED_TODO) +am_EMBED_TODO := + +$(foreach dir,$(am_EMBED_NOW),$(call DoEmbed,$(dir))) +am_EMBED_NOW := $(am_EMBED_TODO) +am_EMBED_TODO := + +$(foreach dir,$(am_EMBED_NOW),$(call DoEmbed,$(dir))) +am_EMBED_NOW := $(am_EMBED_TODO) +am_EMBED_TODO := +$(if $(am_EMBED_NOW),$(error EMBED_SUBDIRS recursion limit reached...)) + +# embedding done, move variables back + +$(eval $(call SetDirs,$(SUBLOC))) +CLEANFILES := $(am_CLEANFILES) +DISTCLEANFILES := $(am_DISTCLEANFILES) +MAINTAINERCLEANFILES := $(am_MAINTAINERCLEANFILES) +SUBDIRS := $(am_SUBDIRS) +DIST_SUBDIRS := $(am_DIST_SUBDIRS) +EMBED_SUBDIRS := $(am_EMBED_DONE) +am_CLEANFILES = +am_DISTCLEANFILES = +am_MAINTAINERCLEANFILES = +am_DIST_SUBDIRS = +am_SUBDIRS = +am_EMBED_DONE = + +am_TARGETLISTS := $(sort $(am_TARGETLISTS)) +am_EXTRA_TARGETLISTS := $(sort $(am_EXTRA_TARGETLISTS)) + +# allow seeing moved lists +AM_FLAGS += real + +## EMBED_SUBDIRS end + +## +## clean targets +## + + +clean: +ifdef CLEANFILES + $(E) "CLEAN" $@ + $(Q) $(RM) -- $(CLEANFILES) +endif + +distclean: clean + $(E) "DISTCLEAN" $@ + $(Q) $(RM) -r -- $(OBJDIR) +ifdef DISTCLEANFILES + $(Q) $(RM) -- $(DISTCLEANFILES) +endif + +maintainer-clean: clean + $(E) "MAINTAINERCLEAN" $@ + $(Q) $(RM) -r -- $(OBJDIR) +ifdef DISTCLEANFILES + $(Q) $(RM) -- $(DISTCLEANFILES) +endif +ifdef MAINTAINERCLEANFILES + $(Q) $(RM) -- $(MAINTAINERCLEANFILES) +endif + + +## +## actual subdir targets +## + +# 1-dir +define MakeSubDir + $(trace1) + $(E) "MKDIR" "Create $(call JoinPath,$(SUBLOC),$(1))" + $(Q) @$(MKDIR_P) $(1) + $(Q) @echo "include $(call UpDir,$(1))/$(srcdir)/$(1)/Makefile" > $(1)/Makefile +endef + +# 1-dir, 2-tgt +define SubTarget + $(trace2) + $(if $(wildcard $(1)/Makefile),,$(call MakeSubDir,$(1))) + $(E) "-->" "$(call JoinPath,$(SUBLOC),$(1))" + $(Q) $(MAKE) -C $(1) $(2) + $(E) "<--" "$(call JoinPath,$(SUBLOC),$(1))" +endef + +sub-all sub-install sub-clean: + $(foreach dir,$(SUBDIRS),$(call SubTarget,$(dir),$(subst sub-,,$@))$(NewLine)) + +# Avoid double dirs in DIST_SUBDIRS, without changing order +am_DISTDIRS = $(SUBDIRS) $(foreach dir,$(DIST_SUBDIRS),$(if $(filter $(dir),$(SUBDIRS)),,$(dir))) + +sub-dist sub-distclean sub-maintainer-clean: + $(foreach dir,$(am_DISTDIRS),$(call SubTarget,$(dir),$(subst sub-,,$@))$(NewLine)) +.PHONY: sub-all sub-clean sub-install sub-dist sub-distclean sub-maintainer-clean + + +## +## actual dist targets +## + +DistTarget = $(foreach fmt,$(1),dist-$(fmt)) + +AM_DIST_ALL ?= gzip bzip2 xz zip + +AM_DIST_ALL_TGTS = $(call DistTarget,$(AM_DIST_ALL)) +AM_DIST_DEF_TGTS = $(call DistTarget,$(AM_DIST_DEFAULT)) + +AM_FORMAT_gzip_EXT = tar.gz +AM_FORMAT_gzip_CMD = tar chof - $(AM_DIST_BASE) | gzip > $(AM_DIST_BASE).$(AM_FORMAT_gzip_EXT) +AM_FORMAT_bzip2_EXT = tar.bz2 +AM_FORMAT_bzip2_CMD = tar chof - $(AM_DIST_BASE) | bzip2 > $(AM_DIST_BASE).$(AM_FORMAT_bzip2_EXT) +AM_FORMAT_xz_EXT = tar.xz +AM_FORMAT_xz_CMD = tar chof - $(AM_DIST_BASE) | xz > $(AM_DIST_BASE).$(AM_FORMAT_xz_EXT) +AM_FORMAT_zip_EXT = zip +AM_FORMAT_zip_CMD = zip -rq $(AM_DIST_BASE).$(AM_FORMAT_zip_EXT) $(AM_DIST_BASE) + +# 1-name +define MakeDist + $(E) "CHECK" $@ + $(Q) $(MAKE) -s am-check-distfiles + $(E) "MKDIR" $(AM_DIST_BASE) + $(Q) $(RM) -r -- $(AM_DIST_BASE) $(AM_DIST_BASE).$(AM_DIST_$(1)_EXT) + $(Q) $(MKDIR_P) $(AM_DIST_BASE) + $(E) "COPY" $(AM_DIST_BASE) + $(Q) $(MAKE) -s am-show-distfiles | cpio -pmdL --quiet $(AM_DIST_BASE) + $(E) "PACK" $(AM_DIST_BASE).$(AM_FORMAT_$(1)_EXT) + $(Q) $(AM_FORMAT_$(1)_CMD) + $(Q) $(RM) -r -- $(AM_DIST_BASE) +endef + +.PHONY: dist $(AM_DIST_ALL_TGTS) +dist: $(AM_DIST_DEF_TGTS) +dist-all: $(AM_DIST_ALL_TGTS) +$(AM_DIST_ALL_TGTS): + $(call MakeDist,$(subst dist-,,$@)) + +# show list of files that need to be in final archive +.PHONY: am-show-distfiles +am-show-distfiles: + $(foreach dir,$(am_DISTDIRS),@$(MAKE) $(AM_MAKEFLAGS) --no-print-directory -C $(dir) $@ $(NewLine)) + $(foreach file,$(am_FINAL_DISTFILES),@echo "$(call JoinPath,$(SUBLOC),$(file))" $(NewLine)) + +# do dependencies as separate step, in case building outputs anything +.PHONY: am-check-distfiles +am-make-distfiles: $(am_FINAL_DISTFILES) + $(foreach dir,$(am_DISTDIRS),@$(MAKE) $(AM_MAKEFLAGS) -C $(dir) $@ $(NewLine)) + +## +## Now generate the rules +## + +## check which target func to call +# 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags +MakeTarget = $(call $(if $(filter $(AM_BIG_PRIMARIES),$(3)),MakeBigTarget,MakeSmallTarget),$(1),$(2),$(3),$(4),$(5)) + +## process all targets in one list +# 1-list, 2-prim,3-dest,4-flags +MakeTargetList = $(foreach tgt,$($(1)),$(call MakeTarget,$(call CleanName,$(tgt)),$(tgt),$(2),$(3),$(4))) + +## process all target lists +# 1=list names +ProcessTargets = $(call ForEachTarget,MakeTarget,$(1)) + +# process non-EXTRA targets +$(eval $(call ProcessTargets,$(am_TARGETLISTS))) + +# process EXTRA_* last, they may already have been processed +$(eval $(call ProcessTargets,$(am_EXTRA_TARGETLISTS))) + +## +## debug target +## + +# 1=var +define AmDebugShow +$(if $($(1)),@echo '$(1) = $($(1))') +$(NewLine) +endef + +# 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags +define AmDebugTarget +$(trace5) +$(foreach var,$(AM_DEBUG_TARGET_VARS),$(call AmDebugShow,$(1)_$(var))) +@echo "" +endef + +# func args: 1-var, 2-prim, 3-dest, 4-flags +CollectDests = $(filter-out noinst EXTRA,$(3)) +AM_USED_DESTS = $(sort $(call ForEachList,CollectDests,$(am_TARGETLISTS))) + +AM_DEBUG_VARS = GNUMAKE380 GNUMAKE381 GNUMAKE382 MAKEFILE_LIST \ + AM_LANGUAGES AM_FLAGS AM_DESTINATIONS \ + AM_ALL_TARGETS EXEEXT am_FINAL_DISTFILES \ + nosub_top_builddir nosub_top_srcdir \ + abs_top_srcdir abs_top_builddir \ + srcdir builddir top_srcdir top_builddir \ + SUBDIRS EMBED_SUBDIRS DIST_SUBDIRS \ + DISTFILES CLEANFILES DISTCLEANFILES MAINTAINERCLEANFILES +AM_DEBUG_TARGET_VARS = SOURCES OBJS LINKVAR DEST USUAL_OBJS USUAL_SRCS EXT FINAL \ + $(AM_TARGET_VARIABLES) +AM_DEBUG_LANG_VARS = SRCEXTS +am-debug: + @echo ""; echo "==== Global Variables ====" + $(foreach var,$(AM_DEBUG_VARS),$(call AmDebugShow,$(var))) + @echo ""; echo "==== Per-language Variables ====" + $(foreach lg,$(AM_LANGUAGES),$(foreach var,$(AM_DEBUG_LANG_VARS),$(call AmDebugShow,AM_LANG_$(lg)_$(var)))) + @echo ""; echo "==== Per-target Variables ====" + $(call ForEachTarget,AmDebugTarget,$(am_TARGETLISTS) $(am_EXTRA_TARGETLISTS)) + @echo ""; echo "==== Active install directories ====" + $(foreach dst,$(AM_USED_DESTS),@echo ' $(dst)dir = $($(dst)dir)'$(NewLine)) + + +## +## regtests for basic tools +## + +AM_TESTS = 1 2 3 4 5 6 7 8 9 10 11 12 13 14 +AM_TEST_1 = $(call Eq,a b c,a b c),$(call Eq,,),$(call Eq,a,aa),$(call Eq,a,a a) +AM_TEST_1_RES = true,true,, +AM_TEST_2 = $(call Neq,a,aa),$(call Neq,a,a) +AM_TEST_2_RES = true, +AM_TEST_3 = $(call CleanName,obj/foo-baz.x) +AM_TEST_3_RES = obj_foo_baz_x +AM_TEST_4 = $(call LastWord,a),$(call LastWord,a b c),$(call LastWord,) +AM_TEST_4_RES = a,c, +AM_TEST_5 = $(call ReplaceExts,.c .cpp X.foo,.o,s1.c s2.cpp s3X.foo s4.h) +AM_TEST_5_RES = s1.o s2.o s3.o +AM_TEST_5 = $(call LangList,foo.c c.foo),$(call LangList,foo.c c.foo f.cpp) +AM_TEST_5_RES = C,C CXX +AM_TEST_6 = $(call DetectLinkVar,foo.c c.foo),$(call DetectLinkVar,foo.c c.foo x.cpp),$(call DetectLinkVar,foo),$(call DetectLinkVar,) +AM_TEST_6_RES = AM_LANG_C_LINK,AM_LANG_CXX_LINK,AM_LANG_C_LINK,AM_LANG_C_LINK +AM_TEST_7 = $(call UpDir,foo)|$(call UpDir,)|$(call UpDir,.)|$(call UpDir,foo/bar)|$(call UpDir,a/b/c)| +AM_TEST_7_RES = ..|.|.|../..|../../..| +AM_TEST_8 = $(call JoinPath,.,.)|$(call JoinPath,,)|$(call JoinPath,a,.)|$(call JoinPath,.,b)|$(call JoinPath,a,b) +AM_TEST_8_RES = .||a|b|a/b +define AM_TEST_9_EVAL +$(IFEQ) ($$(AM_TEST_9_RES),OK) +AM_TEST_9 = OK +$(ELSE) +AM_TEST_9 = fail +$(ENDIF) +endef +AM_TEST_9_RES = OK +$(eval $(AM_TEST_9_EVAL)) +AM_TEST_10 = $(call CheckName,nobase_bin_PROGRAMS,PROGRAMS,bin,nobase)|$(call CheckName,a,a,,)|$(call CheckName,bin_bin_DATA,,bin bin,DATA) +AM_TEST_10_RES = nobase_bin_PROGRAMS|a| +AM_TEST_11_Show = $(4)-$(3)-$(2) +AM_TEST_11 = $(call ForEachList,AM_TEST_11_Show,bin_PROGRAMS foo_DATA baz_foo base_nobase_dist_nodist_DATA_PROGRAMS) +AM_TEST_11_RES = -bin-PROGRAMS --DATA -- base nobase dist nodist--DATA PROGRAMS +AM_TEST_12 = $(call RelocFlags,sub/dir,-I. -I./foo -Lfoo/bar -I/inc -L/lib -lfoo obj.o ../mod.a /lib/c.a) +AM_TEST_12_RES = -Isub/dir -Isub/dir/./foo -Lsub/dir/foo/bar -I/inc -L/lib -lfoo sub/dir/obj.o sub/dir/../mod.a /lib/c.a +AM_TEST_13 = $(call TargetNoDist,HEADERS,)|$(call TargetNoDist,HEADERS,nodist)|$(call TargetNoDist,PROGRAMS,)|$(call TargetNoDist,PROGRAMS,dist) +AM_TEST_13_RES = |true|PROGRAMS| +AM_TEST_14 = $(call ShellQuote,foo'bar\')|$(call ShellQuote,as!d' \\ $$foo) +AM_TEST_14_RES = 'foo'\''bar\'\'''|'as!d'\'' \\ $$foo' + +AmTest = $(if $(call Eq,$($(1)),$($(2))),@echo '$(1): OK',@echo '$(1): FAIL: $($(1)) != $($(2))')$(NewLine) +am-test: + $(Q) test "$(call Eq,a b c,a b c),$(call Eq,,),$(call Eq,a,aa),$(call Eq,a,a a)" = "true,true,," + $(foreach nr,$(AM_TESTS),$(call AmTest,AM_TEST_$(nr),AM_TEST_$(nr)_RES)) + +## +## help target +## + +AmHelpNames = targets standalone internal config dests +.PHONY: help $(foreach n,$(AmHelpNames),help-$(n) help-$(n)-local) +$(foreach n,$(AmHelpNames),help-$(n)-local): +help: $(foreach n,$(AmHelpNames),help-$(n) help-$(n)-local) + +# 1-var, 2-desc +AmConf = @printf ' %-27s %s=%s\n' $(call ShellQuote,$(2)) $(call ShellQuote,$(1)) $(call ShellQuote,$($(1))) + +help-targets: + @echo "" + @echo "Main targets:" + @echo " all Build all targets (default)" + @echo " install Install files" + @echo " dist Create source archive" + @echo " clean Clean built files" + @echo " distclean Clean configured files" + @echo " maintainer-clean Delete anything that can be generated" + +help-standalone: + @echo "" + @echo "Standalone targets: (make -f antimake.mk)" + @echo " show-location Prints full path to antimake.mk (default)" + @echo " show-config Prints template config.mak.in" + +help-internal: + @echo "" + @echo "Internal targets:" + @echo " am-show-distfiles Shows files that go into source archive" + @echo " am-debug Shows variables that affect the build" + @echo " am-test Regtest for internal functions" + +help-config: + @echo "" + @echo "Config variables and their current values:" + $(call AmConf,CC,C compiler) + $(call AmConf,CFLAGS,C compiler flags) + $(call AmConf,CPPFLAGS,C pre-processor flags) + $(call AmConf,LDFLAGS,Linker flags) + +help-dests: + @echo "" + @echo "Destinations for install [ prefix=$(prefix) ]:" + $(foreach dst,$(AM_USED_DESTS),@echo ' $(dst)dir = $($(dst)dir)'$(NewLine)) + +endif # O=empty + diff --git a/mk/antimake.txt b/mk/antimake.txt new file mode 100644 index 0000000..d161975 --- /dev/null +++ b/mk/antimake.txt @@ -0,0 +1,458 @@ += antimake.mk(5) = + +== NAME == + +antimake - Minimal Automake syntax on plain GNU Make + +== DESCRIPTION == + +Antimake makes possible to use GNU Automake conventions +to describe builds in ordinary Makefiles for GNU Make. + +It's main abstractions are target lists and target variables. +Target list describes target type and where to install. +Target variables give source files and additional flags for build. + +== EXAMPLE == + +------------------- +# target list +bin_PROGRAMS = prog + +# target variables for 'prog' +prog_SOURCES = prog.c prog.h +prog_LDADD = libutil.a + +# target list +noinst_LIBRARIES = libutil.a + +# target variables for 'libutil.a' +libutil_a_SOURCES = util.c util.h + +# load Antimake +include antimake.mk +------------------- + +== Terminology == + +Primary:: + target type, describes how to build and install particular type of targets. + +Target:: + a file that needs to be built and/or installed. + +Distribute:: + Include file in source .tar.gz. Non-distributed files are skipped + when building .tar.gz and are cleaned during `make distclean`. + +Source:: + Source files are files that appear in `..._SOURCES` per-target variable. + They are distributed by default. They may or may not result in object files. + It's fine to put both `.h` and `.c` files into _SOURCES. + +== TARGET LISTS == + +Target lists are variables that contain file names that need to be +built and installed. They are specially named so that the name also describes how they +are built, how and where they will be installed. + +The target list name contains 3 parts, separated with underscore, in following order: + +1. Optional flags. Flags are: `nodist`, `dist`, `nobase`, `base`. (Default: `base`, `nodist`) +2. Destination directory name. Destination directory called *bin* actual + location is stored in Make variable `$(bindir)`. Some common values: + `bin`, `lib`, `include`. There are more and the list can be extended. + Special name `noinst` means the target file should not be installed. +3. Target type, also called "primary". This will describe how the target + needs to be built. Common values: `PROGRAMS`, `LIBRARIES`, `DATA` + +For details, what the various values mean, see next sections. + +.Examples: +---------------- +bin_PROGRAMS = prog1 prog2 +# flags: base, nodist +# dest: $(bindir) +# type: PROGRAMS + +noinst_LIBRARIES = lib1.a lib2.a +# flags: base, nodist +# dest: noinst +# type: LIBRARIES + +nobase_dist_doc_DATA = docs/README +# flags: dist, nobase +# dest: $(docdir)/docs +# type: DATA +---------------- + +=== Primaries === + +`PROGRAMS`:: + executable programs, linked together from objects built from source files + +`LIBARIES`:: + static libraries, linked together from objects built from source files + +`LTLIBRARIES`:: + dynamic or static libraries, linked together from objects built from source files + +`HEADERS`:: + header files, no default build method, the target files have `dist` flag by default. + +`MANS`:: + man pages, no default build method, installed into manX subdir. + +`SCRIPTS`:: + scripts, executable file, no default build method + +`DATA`:: + data, non-executable file, no default build method + +=== Target list flags === + +`dist`:: + The target should be distributed with other sources. + Default for `HEADERS` type, others have `nodist` by default. + +`nodist`:: + Target is not distributed and should be cleaned with distclean. + Default for all primaries, except `HEADERS`. + +`base`:: + On install relative path is ignored, all files + end up in destination directory. Always default. + +`nobase`:: + On install relative path is kept. Eg: if `includedir=/usr/include` + then `nobase_include_HEADERS=mylib/common.h` + is installed to `/usr/include/mylib/common.h`. + +`noinst`:: + Target is built as part of build process, but is not installed. + +`EXTRA`:: + Targets in such list are not built, nor installed. + Useful to make sure that sources for dynamically configured targets + will end up in source tarball. Unlike other target list ariables, + `EXTRA_` may contain targets already defined in other + target lists, they will be filtered out from this list then. + +== Target variables == + +Only big targets take additional variables: `PROGRAMS`/`LIBRARIES`/`LTLIBRARIES`. + +`_SOURCES`:: + All source files, *.c *.h *.cpp *.hpp. + +`nodist__SOURCES`:: + Source files that should not be distributed. + +`EXTRA__SOURCES`:: + In case tgt_SOURCES is dynamic, here is non-dynamic + list of sources for distribution. Only dynamic sources + need to be listed here. + +`_DEPENDENCIES`:: + Add dependencies that need to be build before target build will start. + +`_CFLAGS`, `_CPPFLAGS`, `_LDFLAGS`, `_LIBTOOLFLAGS`:: + Override corresponging AM_xx variable + +`_LDADD`:: + Add dependencies that are used during linking. + For PROGRAMS only. + They will be added to linker command line. + +`_LIBADD`:: + Add dependencies that are used during linking. + For LIBRARIES/LTLIBRARIES only. + They will be added to linker command line. + +`_AR`:: + Overrides $(AR) $(ARFLAGS). + For LIBRARIES only. + +.Example: +------------------- +bin_PROGRAMS = prog +prog_SOURCE = main.c util.c util.h +prog_CFLAGS = $(GTK_CFLAGS) +prog_LDADD = $(GTK_LIBS) +------------------- + + +== Global variables == + +They can be set before `antimake.mk` inclusion to change +build behaviour. + +EXTRA_DIST:: + Additional files to include in source archive. + +CLEANFILES:: + Additional files to `make clean`. + +DISTCLEANFILES:: + Additional files to `make distclean`. + +MAINTAINERCLEANFILES:: + Additional files to `make maintainer-clean`. + +SUBDIRS:: + Subdirectories of current directory where Make + needs to be recursively launched. If subdirectory + `Makefile` is Antimake-base, it should set `SUBLOC`. + +SUBLOC:: + Current diretory location in overall source tree. + This can stay unset in top directory. Needed for + subdirectiories entered with `SUBDIRS` to find + its position in source tree. + +DIST_SUBDIRS:: + Subdirs that only `make dist`, `make distclean` and `make maintainer-clean` + will enter. + +EMBED_SUBDIRS:: + Subdirectories that are built non-recursively: they need to contain `Makefile.am` + that contains makefile-fragment with Antimake syntax + that describes local targets using relative filenames. + The fragment is included in main makefile and file + and variable names are converted and merged with top-level targets. + +USUAL_DIR:: + Location of libusual tree, used for embedding libusual + sources. + +=== More details on EMBED_SUBDIRS === + +It acts like `include $(dir)/Makefile.am` for each directory, except it converts +file and variable names. Example: + +--------------------- +Makefile: + EMBED_SUBDIRS = src + +src/Makefile.am: + bin_PROGRAMS = hello + hello_SOURCES = main.c + hello_CPPFLAGS = -I./include +--------------------- + +Conversion results as if top-level `Makefile` had contained following rows: + +---------------------- +bin_PROGRAMS += src/hello +src_hello_SOURCES = src/main.c +src_hello_CPPFLAGS = -I./src/include +---------------------- + + +=== Global variables for current location === + +* srcdir, builddir - relative path to source dir and build dir. +* top_srcdir, top_builddir - relative path to top-level source and build dir. +* abs_srcdir, abs_builddir - absolute path to source and build dir +* abs_top_srcdir, abs_top_builddir - absolute path to top-level source and build dir +* nosub_top_srcdir, nosub_top_builddir - relative path from top of builddir to srcdir and builddir. + +=== Global variables that target can override === + +- AM_CPPFLAGS +- AM_CFLAGS +- AM_LDFLAGS +- AM_LIBTOOLFLAGS +- AM_DEFS +- AM_MAKEFLAGS + +=== Global variables from autoconf === + +These variables come usually from autoconf, +but also have reasonable defaults: + + CC, DEFS, CPPFLAGS, CFLAGS, LDFLAGS, LIBS, + LIBTOOL, LIBTOOLFLAGS, AR, ARFLAGS, RANLIB, + CXX, CXXFLAGS, INSTALL, MKDIR_P, LN_S + +=== Global variables for extending Antimake === + +AM_DIST_DEFAULT:: + Default format(s) for `make dist` target. One or more of: + `gzip`, `bzip2`, `xz`, `zip`. Default: `gzip`. + + +AM_DESTINATIONS:: + Additional directory names to consider as valid + destinations. Expects corresponding `dir`-variable to be set. + +AM_SMALL_PRIMARIES:: + Additional single-file primaries. (Builtin: HEADERS, SCRIPTS, DATA, MANS) + +AM_BIG_PRIMARIES:: + Additional primaries built from objects. (Builtin: PROGRAMS, LIBRARIES, LTLIBRARIES) + +AM_LANGUAGES:: + Additional language names. Antimake expects variables + `AM_LANG_$(name)_SRCEXTS`, `AM_LANG_$(name)_COMPILE` and + `AM_LANG_$(name)_LINK` to be set. + + +=== Variables for command-line usage === + +DESTDIR:: + Relocate installation root. + +AM_TRACE:: + Turns on function-call debug info. Can be set from command-line. + +=== Hacking variables === + +GNUMAKE380, GNUMAKE381, GNUMAKE382:: + If we have at least that version of GNU Make. GNUMAKE380 is always + set, others may not be. If Makefile uses features from newer GNU Make + it would be good idea to use those flags and error out with clear + error message, instead having mysterious failures. + +=== Libtool flags === + +Useful http://www.gnu.org/software/libtool/manual/html_node/Link-mode.html[Libtool] +flags that can be put int tgt_LDFLAGS for a LTLIBRARY: + +* -export-dynamic +* -export-symbols symfile +* -export-symbols-regex regex +* -module + +See libtool http://www.gnu.org/software/libtool/manual/html_node/Versioning.html["Versioning"] chapter about those: + +* -avoid-version +* -version-info current[:revision[:age]] +* -version-number major[:minor[:revision]] +* -release major[:minor[:revision]] + + +== Top-level pseudo-targets == + +=== all === + +The default target when no other target is given on command-line. Builds all target files. + +==== Simple targets ==== + +These are simple - either the file already exists, +or the user needs to give build command. + +==== Object-based targets ==== + +The targets in primaries PROGRAMS, LIBRARIES and LTLIBRARIES consist of multiple source +files that need to be compiled into objects. Then the objects need to be +linked into final target. The process is roughly following: + +. Dependencies are built (_LDADD, _LIBADD, _DEPENDENCIES). +. Source list is filtered for extensions that can be compiled into object files, + object file list is created based on them. The rest of files are used and + dependencies for target, but otherwise ignored. +. Object files are built. +. Linker is picked based on source files - as there can be files in multiple languages, + the most advanced language wins (the one that appears later in `AM_LANGUAGES`) +. Final executable is linked. + +=== install === + +Install all targets to their destination directories, which is mentioned +in their target list variable name. Eg. `bin_PROGRAMS` will be installed +to `$(bindir)`. If destination is named `noinst`, it will not be installed. + +If the flag `nobase` is given, the relative filename is kept, otherwise +basename is taken and it will appear directly under destination directory. + +.Example: +------ +include_HEADERS = func1.h lib/func2.h +# Files will end up in: +# $(includedir)/func1.h +# $(includedir)/func2.h + +nobase_include_HEADERS = func1.h lib/func2.h +# Files will end up in: +# $(includedir)/func1.h +# $(includedir)/lib/func2.h +------ + +=== clean === + +- Remove files in `$(CLEANFILES)` +- Remove built objects. +- Remove target files, unless they are marked as `dist`. + (Note: `HEADERS` primary is `dist` by default, all other are `nodist`) + +=== distclean === + +- Remove files in `$(DISTCLEANFILES)` +- Remove sources tagged with `nodist`. All sources as `dist` by default. + +=== maintainer-clean === + +- Remove files in `$(MAINTAINERCLEANFILES)` + +=== help === + +Describe top-level targets. + +=== am-test === + +Regression test for low-level Antimake functions. + +=== am-debug === + +Show Antimake internal state. + +== FEATURES == + +Done: + +- Big primaries: PROGRAMS, LIBRARIES, LTLIBRARIES +- Small primaries: DATA, SCRIPTS, MANS, HEADERS +- Flags: base nobase dist nodist noinst EXTRA +- Target vars: SOURCES, CPPFLAGS, CFLAGS, LDFLAGS, LDADD/LIBADD +- Separate build dir +- Per-target objects +- Languages: C, CXX +- SUBDIRS, DIST_SUBDIRS +- EMBED_SUBDIRS + +Todo: + +- Improve docs +- Standardize and document how to extend +- Deps with non-gcc? +- Long if-s to support `O=` seems to break GNU Make 3.80. + Drop `O=` or drop 3.80? + +Probably out of scope: + +- `make uninstall` +- `make distcheck` +- `make dist` from separate build dir +- `install-(exec|data)-hook` - based on dir not primary +- Default file list for `EXTRA_DIST`. (Problem: distclean / maintainer-clean) + +Definitely out of scope: + +- automake conditionals +- automake extras (autoconf macros, ltdl) +- automake nanny mode (gnu/gnits) + +== SEE ALSO == + +GNU Make Reference: http://www.gnu.org/software/make/manual/make.html#Quick-Reference[] + +Recursive Make Considered Harmful: http://miller.emu.id.au/pmiller/books/rmch/[] + +Paul's Rules of Makefiles: http://make.mad-scientist.us/rules.html[] + +Small BSD-ish build system: https://webkeks.org/hg/buildsys/[] + +GNU Make Standard Library: http://sourceforge.net/projects/gmsl/[] + -- 2.39.5