Subversion Repositories shark

Rev

Rev 1683 | Blame | Compare with Previous | Last modification | View Log | RSS feed

# Copyright (c) 2007 Fabio Checconi <fabio@gandalf.sssup.it>
#
# This is a non-recursive makefile that supports a Kbuild-like syntax
# for input/output specification.

# Handle the V=0|1 option.  We use both a long and a short form.  In
# the short one we use the $(Q) variable prepended to shell commands
# (e.g., $(Q)mkdir) that in quiet mode expands to @, and in verbose
# mode expands to nothing.  In the long one we have a couple of strings
# per command, one named quiet_cmd, the other cmd, and we always echo
# $(QS)cmd, so if $(QS) expands to quiet_ the first string is printed.
# The first string is the quiet version of the command, the second one
# holds the whole command.
ifneq ($(V),1)
        Q:=@
        QS:=quiet_
endif

# Handle the make -s flag: produce no output at all...
ifneq ($(findstring s,$(MAKEFLAGS)),)
        Q:=@
        QS:=nothing_
endif

# The variables used to generate the rules to build targets.  Use
# CROSS_COMPILE to specify the prefix to be used for the toolchain.
ar:= $(CROSS_COMPILE)$(AR)
as:= $(CROSS_COMPILE)$(CC)
cc:= $(CROSS_COMPILE)$(CC)
ld:= $(CROSS_COMPILE)$(LD)
rm:= $(if $(RM),$(RM),rm)
install:= $(if $(INSTALL),$(INSTALL),install)

# As in the rules we cannot use the variables we use in the makefile,
# because we reassign them for every directory we visit, we just try
# to recover the information from the targets being built.  Another
# way could have been to expand the variables when generating the
# rules, but it would have made rule generation unreadable.
in-local:= $$(subst $$(srctree)/,,$$<)
out-local:= $$(subst install-,,$$(subst $$(objtree)/,,$$@))
in-objs:= $$(filter %.o,$$^)

# The rule patterns, in quiet and in verbose form.  The verbose form is
# used also for the actual command.
quiet_ar_cmd    := '  AR\\t$(out-local)'
ar_cmd          := $(ar) cr $$@ $(in-objs)

quiet_cc_cmd    := '  CC\\t$(in-local)'
cc_cmd          := $(cc) $$(cflags-$$(<:.c=.o)) $$(cppflags-$$(<:.c=.o))\
                        -c $$< -o $$@

quiet_as_cmd    := '  AS\\t$(in-local)'
as_cmd          := $(as) $$(aflags-$$(<:.s=.o)) -c $$< -o $$@

quiet_dep_cmd   := '  DEP\\t$(in-local)'
dep_cmd         := $(cc) $$(cflags-$$(<:.c=.o)) $$(cppflags-$$(<:.c=.o))\
                        -M -MT \
                        "$$(subst $$(srctree),$$(objtree),$$(<:.c=.o))\
                        $$@" $$< > $$@ 2>/dev/null

quiet_ld-r_cmd  := '  LD [R]\\t$(out-local)'
ld-r_cmd        := $(ld) $$(ldflags-$$(dir $$@)) $$(ldflags-$$@)\
                        -r $(in-objs) -o $$@

quiet_ld_cmd    := '  LD\\t$(out-local)'
ld_cmd          := $(ld) $$(ldflags-$$(dir $$@)) $$(startup-$$@) $(in-objs)\
                        $$(ldflags-$$@) -o $$@

quiet_rm_cmd    := '  RM\\t$$(subst clean-,,$$(subst distclean-,,$(out-local)))'
rm_cmd          := -$(rm) $$(subst clean-,,$$(subst distclean-,,$$@))

quiet_install_cmd := '  INSTALL $(out-local)'
install_cmd     := -$(install) $$(subst install-,,$$@) $$(installdir)

# Generate the requested rule for the requested directory.  First echo
# the command according to the requested verbosity (suppress all the
# output if $(QS)$(1)_cmd is not defined, i.e., in the silent make case,)
# and then emit the actual command.
define cmd
        $(if $($(QS)$(1)_cmd),@echo -e '$($(QS)$(1)_cmd)')
        @$($(1)_cmd)
endef

# Remove duplicate slashes, ../'s, trailing slashes and so on from paths.
clean-path=$(dir $(abspath $(1)))$(notdir $(abspath $(1)))

# Canonicalize a path, converting .. and . $(1) is the current directory,
# and $(2) are the paths to be converted. 
to-local-path=$(subst $(1)/,,$(abspath $(addprefix $(1)/,$(2))))

# Keep only paths in $(1) that start with ./ (i.e., that are relative to the
# current directory.)
keep-local=$(foreach p, $(1),$(if $(subst ./,,$(dir $(p))),,$(p)))

# Adjust the paths for all the prerequisites of a target.  Given a list
# of objects, with relative or absolute path, figure out the right paths
# in the build directory.  Leave the output in $(target-objs).
define all-prerequisites-templ
target-obj-list:=$(1)

# Filter absolute paths.
target-objs:=$$(filter $(srctree)/%,$$(target-obj-list))
target-obj-list:=$$(filter-out $$(target-objs),$$(target-obj-list))

# Add all the other objects, and convert the path to the $(objtree)
# root and with a reasonable path.
target-objs+=$$(addprefix $$(curdir)/,$$(target-obj-list))
target-objs:=$$(foreach dir,$$(subst $(srctree),$(objtree),$$(target-objs)),\
        $$(call clean-path,$$(dir)))
endef

# Produce targets for a given library/application.
define libapp-templ
libapp:=$(1)
is-app:=$(strip $(2))
libapp-target:=$$(objdir)/$$(libapp)

# Get a list of all the prerequisites needed to build the library/application.
$$(eval $$(call all-prerequisites-templ,\
        $$(patsubst %/,%/obj.o,$$($$(libapp)-objs))))

$$(foreach flag,$(flags),$$(eval $$(call flags-per-target,$$(flag),$$(libapp))))
$$(foreach obj,$$(target-objs),\
        $$(eval $$(call flags-per-objects,$$(obj),$$(libapp))))

# Produce the real target, in $(objtree), and the user-friendly target,
# with a relative path to $(srctree).  Note that even when the two trees
# are equal we define two targets; a possible optimization could be to
# use relative paths for the object tree in this case, but the code can
# become even less readable than it is now...
ifeq ($$(is-app),true)
startup-$$(libapp-target):= $$($$(libapp)-startup)
$$(libapp-target): $$(target-objs)
        $(call cmd,ld)
else
$$(libapp-target): $$(target-objs)
        $(call cmd,ar)
endif
short-libapp-taget:=$$(subst $$(srctree)/,,$$(curdir)/$$(libapp))
.PHONY: $$(short-libapp-target)
$$(short-libapp-target): $$(libapp-target)

# Update the $(subdirs) for $(curdir) and its target list.
subdirs+=$$(filter %/,$$($$(libapp)-objs))
curdir-targets+=$$(libapp-target)

.PHONY: clean-$$(libapp-target)
clean-$$(libapp-target):
        $(call cmd,rm)
curdir-clean-targets+= clean-$$(libapp-target)

ifneq ($$(is-app),true)
.PHONY: install-$$(libapp-target)
install-$$(libapp-target): $$(libapp-target)
        $(call cmd,install)
curdir-install-targets+= install-$$(libapp-target)
endif
endef

# Produce the needed code for a given object.
define obj-templ
obj:=$(1)
obj-target:=$$(objdir)/$$(obj)
obj-dep:=$$(objdir)/.$$(obj:.o=.c).deps

# Handle dependencies.
ifeq ($(goals-have-clean),)
$$(obj-dep): $$(objdir-prereq)
-include $$(obj-dep)
endif

# Produce the user shortcut for the real target (that does not need to
# be produced, as it is handled by implicit rules.)
short-obj-target:= $$(subst $$(srctree)/,,$$(curdir)/$$(obj))
.PHONY: $$(short-obj-target)
$$(short-obj-target): $$(obj-target)

curdir-targets+=$$(obj-target)

.PHONY: clean-$$(obj-target) distclean-$$(obj-dep)
clean-$$(obj-target):
        $(call cmd,rm)
distclean-$$(obj-dep):
        $(call cmd,rm)
curdir-clean-targets+= clean-$$(obj-target)
curdir-distclean-targets+= distclean-$$(obj-dep)

ifneq ($$(filter $$(obj),$$(install-objs)),)
.PHONY: install-$$(obj-target)
install-$$(obj-target): $$(obj-target)
        $(call cmd,install)
curdir-install-targets+= install-$$(obj-target)
endif
endef

# Handle the flags for the current directory.  for each *FLAGS variable
# used by the implicit rules there are two variants:
#    o the local one, e.g., cflags:=-I. or cflags+=-I. changing the
#      flags used only by the current directory.
#    o The global one, e.g., exported-cflags:=-I$(srctree) changing the
#      flags for the current directory and all its subdirs.
# The flag handling is split in three parts, one to be inserted before
# including the current directory makefile, one before recurring into
# subdirs, the other one after the recursion.
define flags-save-templ
flag:=$(1)
# Flags on input to this dir; they must be preserved for our sibling
# directories.
saved-$$(flag)-$$(curdir):= $$($$(flag))
endef

define flags-pre-templ
flag:=$(1)

# For the current directory, use the user-specified flags (that may
# come from our parent directory.)  Extra flags are used to specify
# library-specific flags.
$$(flag)-$$(curdir):=$$($$(flag)) $$(exported-$$(flag))

#to-print+= [ $$(flag)-$$(curdir) = $$($$(flag)-$$(curdir)) ] RET

# Export to our children also the user exported flags.
$$(flag):= $$(saved-$$(flag)-$$(curdir)) $$(exported-$$(flag))
exported-$$(flag):=
endef

define flags-post-templ
flag:=$(1)

# Restore the flags that our parent specified.
$$(flag):= $$(saved-$$(flag)-$$(curdir))
endef

# Allow redefinition of flags on the basis of a single target.  Each
# target gets its own version of the flags, based on the ones of the
# current directory and its own ones.
define flags-per-target
flag:=$(1)
target:=$(2)

$$(flag)-$$(curdir)/$$(target):=$$($$(flag)-$$(curdir)) \
        $$($$(target)-$$(flag)) $$(extra-$$(flag)-$$(curdir)/$$(target))

ifneq ($$(target),install-objs)
# Those are the flags coming from the upper directories, they are set for
# obj.o targets, we must export them to our children, so we leave them here.
exported-$$(flag)-$$(curdir)/$$(target):= $$($$(target)-$$(flag)) \
        $$(extra-$$(flag)-$$(curdir)/$$(target))
endif
$$(target)-$$(flag):=
endef

define flags-per-object
# Generate flags only for objects in the current directory, as prerequisites
# that come from ousdide are not compiled here.
flag:=$(1)
target:=$(3)
ifeq ($(dir $(subst $(objtree),$(srctree),$(2))),$$(curdir)/)
this-obj:=$(notdir $(2))

# The flags for a given object are the ones specified for its target
# plus any specific one.
$$(flag)-$$(curdir)/$$(this-obj):= $$($$(flag)-$$(curdir)/$$(target))\
        $$($$(this-obj)-$$(flag))
$$(this-obj)-$$(flag):=

ifeq ($(3),install-objs)
to-print+= [ $(2) $(3) ] RET
endif
endif
# Pass the right flags to the subdirs we use to build $(target).
ifeq ($$(notdir $(2)),obj.o)
extra-$$(flag)-$(2):= $$(exported-$$(flag)-$$(curdir)/$$(target))
endif
endef

# Allow the user to specify per-object flags
define flags-per-objects
$$(foreach flag,$$(flags),\
        $$(eval $$(call flags-per-object,$$(flags),$(1),$(2))))
endef

# Helper to define per-library flags.
define flags-per-libs
$$(foreach flag,$$(flags),\
        $$(eval $$(call flags-per-lib,$$(flags),$(1),$(2))))
endef

# Disable default implicit rules.
.SUFFIXES:

# The roots of our source and object trees.  If an O=dir option is
# passed to the toplevel makefile all the output of the building
# process is in dir.
srctree:=$(call clean-path,$(CURDIR))
objtree:=$(if $(O),$(call clean-path,$(O)),$(srctree))

ifeq ($(wildcard $(objtree)),)
$(error Output directory $(objtree) does not exist)
endif

clean-targets:=
distclean-targets:=
install-targets:=

goals-have-clean:=$(strip $(filter clean distclean,$(MAKECMDGOALS)))

aflags:= $(AFLAGS)
cflags:= $(CFLAGS)
cppflags:= $(CPPFLAGS)
ldflags:= $(LDFLAGS)
flags:= aflags cflags cppflags ldflags

# XXX debug only
to-print:=

# The following macro is used to do a standard set of operations
# for every subdirectory we have to work on.  It is instantiated
# every time we change subdirectory, and generates a set of targets
# specific to that directory.
define recurse-templ

# Remember where we are in the source and object tree.  The clean-path
# macro is used for user output and for generating targets independently
# from directories being specified as dir or dir/ or with multiple slashes
# and so on.
curdir:=$$(call clean-path,$(1))
objdir:=$(objtree)$$(subst $(srctree),,$$(curdir))

# Save the flags our parent passed us.
$$(foreach flag,$(flags),$$(eval $$(call flags-save-templ,$$(flag))))

# A list of all the targets defined for the current directory.  It is
# used to produce an user-reachable target for submakes into source tree
# subdirectories, e.g., if src/utils is a source directory, make src/utils
# will build all the targets defined inside it.  Start with the short path
# to all subdirs.
curdir-targets:=

curdir-clean-targets:=
curdir-distclean-targets:=
curdir-install-targets:=

install-objs:=

# If the output directory does not exist every target that actually
# needs it will have to create it.
ifneq ($(objtree),$(srctree))
ifeq ($$(wildcard $$(objdir)),)
objdir-prereq:=$$(objdir)/.build
$$(objdir)/.build:
        $(Q)if [ ! -d $$(dir $$@) ] ; then \
                mkdir -p $$(dir $$@) ; touch $$@ ; fi
endif
endif

# Include the next subdirectory.  Don't include the root makefile,
# if we are at the first step of the recursion.
include $$(subst $(srctree)/Makefile,,$$(curdir)/Makefile)

# Figure out all the targets.  We support three kind of targets:
#   - subdirectories
#   - libraries
#   - objects
#
# Subdirectories are recursively processed, libraries are built using
# the objects specified in their *.a-objs target (if it contains a
# subdirectory the latter is processed and the resulting obj.o object
# is included,) and objects are compiled and incrementally linked
# together in a obj.o file.
subdirs:=$$(filter %/, $$(targets))
objs:=$$(filter %.o, $$(targets))
libs:=$$(filter %.a, $$(targets))
apps:=$$(filter-out $$(subdirs) $$(objs) $$(libs),$$(targets))

$$(eval $$(call all-prerequisites-templ, $$(subdirs)))
subdir-targets:= $$(target-objs)

# Recurse into all the subdirectories.
curdir-targets+=$$(subst $(objtree)/,,$$(subdir-targets))

# Look for prerequisites to build obj.o for the current directory.
$$(eval $$(call all-prerequisites-templ,\
        $$(filter-out $$(exclude),$$(objs))))
target-objs+=$$(addsuffix /obj.o,$$(subdir-targets))

$$(foreach flag,$(flags),$$(eval $$(call flags-pre-templ,$$(flag))))

# Produce a target for obj.o, even if not needed (it will be empty in
# this case.)
ifneq ($$(strip $$(target-objs)),)
$$(foreach flag,$(flags),$$(eval $$(call flags-per-target,$$(flag),obj.o)))
$$(foreach obj,$$(target-objs),\
        $$(eval $$(call flags-per-objects,$$(obj),obj.o)))
$$(objdir)/obj.o: $$(target-objs)
        $(call cmd,ld-r)
else
$$(objdir)/obj.o:
        $(Q)touch $$@
endif
curdir-targets+= $$(objdir)/obj.o

# to-print+= [ [ $$(objdir)/obj.o ] $$(target-objs) ]

# Produce the needed targets for each library.
ifneq ($$(strip $$(libs)),)
$$(foreach lib,$$(libs),$$(eval $$(call libapp-templ,$$(lib),false)))
endif

ifneq ($$(strip $$(apps)),)
$$(foreach app,$$(apps),$$(eval $$(call libapp-templ,$$(app),true)))
endif

# Produce a target for each local object.  Build the list of all the
# targets specified by the current subdir.  It is given by all the local
# objects in $(targets) or in a library contained in it.  Non-local
# objects can be specified as dependencies but must be built from the
# directory containing them (if needed their directory can specify that
# they are not to be included in the local obj.o putting them into
# $(exclude); this may not seem straightforward but is to support a
# few corner cases.)
#
# First build a relative path for all the .o files in the targets (the
# default one and the library ones,) taking care of not being confused
# by relative directory specifications.
#all-local-objs-paths:=$$(subst $$(curdir)/,,\
#       $$(abspath $$(addprefix $$(curdir)/,\
#       $$(objs) $$(install-objs)\
#       $$(foreach lib,$$(libs),$$(filter %.o,$$($$(lib)-objs)))\
#       $$(foreach app,$$(apps),$$(filter %.o,$$($$(app)-objs))))))
all-local-objs-paths:=$$(call to-local-path,$$(curdir),\
        $$(objs)\
        $$(foreach lib,$$(libs),$$(filter %.o,$$($$(lib)-objs)))\
        $$(foreach app,$$(apps),$$(filter %.o,$$($$(app)-objs))))

all-local-install-objs-paths:=$$(call to-local-path,$$(curdir),\
        $$(install-objs))

# Then keep only those with a dir equal to ./  A local object is not
# allowed to be specified with a path specifying a directory different
# from the current one.  Objects to be installed are only taken into
# account if they're local to the current directory.
#all-local-objs:=$$(foreach obj, $$(all-local-objs-paths),\
#       $$(if $$(subst ./,,$$(dir $$(obj))),,$$(obj)))
all-local-install-objs:=$$(call keep-local,$$(all-local-install-objs-paths))
all-local-objs:=$$(call keep-local,$$(all-local-objs-paths))\
        $$(all-local-install-objs)

$$(eval $$(call all-prerequisites-templ,$$(all-local-install-objs)))
$$(foreach flag,$(flags),\
        $$(eval $$(call flags-per-target,$$(flag),install-objs)))
$$(foreach obj,$$(target-objs),\
        $$(eval $$(call flags-per-objects,$$(obj),install-objs)))

# Produce one target per object, and the rules to build them along with
# their dependencies.
ifneq ($$(strip $$(all-local-objs)),)
$$(foreach obj,$$(all-local-objs),$$(eval $$(call obj-templ,$$(obj))))

$$(objdir)/%.o: $$(curdir)/%.c
        $(call cmd,cc)

$$(objdir)/%.o: $$(curdir)/%.s
        $(call cmd,as)

$$(objdir)/.%.c.deps: $$(curdir)/%.c
        $(call cmd,dep)

endif

# Produce the user-friendly target for the source dir, depending
# on its obj.o, libs and local objects (only if they can be built.)
curdir-target:= $$(subst $$(srctree)/,,$$(curdir))
.PHONY: $$(curdir-target)
$$(curdir-target): $$(curdir-targets)

# Produce the clean and distclean targets for the current directory.
.PHONY: clean-$$(curdir-target) distclean-$$(curdir-target)
clean-$$(curdir-target): $$(curdir-clean-targets)

distclean-$$(curdir-target): clean-$$(curdir-target) \
        $$(curdir-distclean-targets)

# to-print+=[ distclean-$$(curdir-target) ]

clean-targets+= clean-$$(curdir-target)
distclean-targets+= distclean-$$(curdir-target)

# Produce the install target.  This is very Shark specific...
.PHONY: install-$$(curdir-target)
$$(curdir-install-targets): $(installdir)/.build
install-$$(curdir-target): $$(curdir-install-targets)
install-targets+= install-$$(curdir-target)

#to-print+=[ install-$$(curdir-target) = $$(curdir-install-targets) ]

# Actually do the recursion, generating the code specified by
# recurse-tmpl for every subdirectory needed from the one we are
# leaving.
ifneq ($$(strip $$(subdirs)),)
$$(foreach dir,$$(subdirs),$$(eval $$(call recurse-templ,$(1)/$$(dir))))
curdir:=$$(call clean-path,$(1))
endif

$$(foreach flag,$(flags),$$(eval $$(call flags-post-templ,$$(flag))))
endef

.DEFAULT_GOAL:= all

installdir:=$(objtree)/.lib

# Include all the Shark specific configuration...
include $(if $(S),$(S)/)Rules.mk

# Let the good times roll...
$(eval $(call recurse-templ,$(srctree)))

all: $(srctree)

.PHONY: clean distclean
clean: $(clean-targets)
distclean: $(distclean-targets)

debug:
        @echo $(to-print)

.PHONY: install $(install-targets)
$(installdir)/.build:
        $(Q)if [ ! -d $(dir $@) ] ; then \
                mkdir -p $(dir $@) ; touch $@ ; fi

install: $(install-targets)