Appendix I. The ccxx.mk File

Here we include a complete copy of rules/object-code/ccxx.mk.

# This makefile rules fragment supports compilation of C and C++ code
# into static libraries and dynamically linked executables.  Shared
# libraries are not presently supported but will be in the future.
# Please see the file make/README.shared-libraries for details.
#
# Please see ccxx-help.txt for details on how to use these rules.

# ---------------

# Notes to implementors

# CCXX_TOOLCHAIN contains the name of a makefile fragment (without the
# .mk; loaded from abuild-specified search path) that defines the
# functions that these rules use to perform actual compiles.  The best
# place to learn about how these work, in addition to carefully
# reading these notes, is to look at the built-in compiler support
# files included with abuild.  gcc.mk is a good UNIX example, and
# msvc.mk is a good Windows example.  The compiler support file
# provide the following:

#   .LIBPATTERNS: gnu make variable which must contain patterns to
#   match library names so that dependencies on -llib will work.

#   OBJ: the suffix of non-library object files

#   LOBJ: the suffix of library object files; may be the same as OBJ

#   PREPROCESS_c: a command used to invoke the C preprocessor

#   PREPROCESS_cxx: a command used to invoke the C++ preprocessor

#   COMPILE_c: a command used to invoke the C compiler

#   COMPILE_cxx: a command used to invoke the C++ compiler

#   LINK_c: a command used to invoke the linker for a C program that
#   uses no C++ libraries

#   LINK_cxx: a command used to invoke the linker for a C++ program

#   DFLAGS: default debugging flags

#   OFLAGS: default optimization flags

#   WFLAGS: default warning flags

#   CCXX_GEN_DEPS: if it is possible to make COMPILE_c and COMPILE_cxx
#   generate correct dependency information as a side effect of
#   compilation, add the appropriate flags to COMPILE_c and
#   COMPILE_cxx and set CCXX_GEN_DEPS to @: to suppress the running of
#   gen_deps.  Otherwise, don't set this variable, in which case it
#   will default to $(GEN_DEPS).  Abuild requires dependency files
#   that contain an empty rule with each object file depending on all
#   of its dependencies as well as an empty rule for each dependency
#   that depends on nothing.  This way, missing header files will
#   cause the target to rebuild instead of fail.  Our own gen_deps
#   does this, as does gcc's -MP option.

#   IGNORE_TARGETS: optional: a list of targets (object files,
#   libraries, etc.)  that should be ignored when determining whether
#   there are orphan targets.

#   $(call libname,libbase): a function that returns the full name of
#   a library from its base.  For example, $(call libname,moo) would
#   typically return libmoo.a on a UNIX system and moo.lib on a
#   Windows system.

#   $(call shlibname,libbase,major,minor,revision): a function that
#   returns the full name of a shared library from its base, major
#   version, minor version, and revision number.  For example, $(call
#   shlibname,moo,1,2,3) might return libmoo.so.1.2.3 on a UNIX system
#   and moo1.dll on a Windows system.  The version arguments are
#   optional.  Each one must be ignored if the ones before it are
#   omitted.

#   $(call binname,binbase): a function that returns the full name of
#   an executable from its base.  For example, $(call binname,moo)
#   would typically return moo on a UNIX system and moo.exe on a
#   Windows system.

#   $(call include_flags,include-dirs): a function that returns
#   include flags forthe given include directories.  This result
#   should be suitable to passing as flags to the preprocessor and C
#   or C++ compiler.

#   $(call make_obj,compiler,pic,flags,src,obj): a function that uses
#   the given compiler to convert src to obj.  The first argument will
#   always be either $(COMPILE_c) or $(COMPILE_cxx).  The second
#   argument will be 1 if we need position-independent code for shared
#   libraries (or static libraries that might be linked into shared
#   libraries) and empty otherwise.

#   $(call make_lib,objects,libbase): a function that creates a
#   library with the given base name.  Note that libbase has not been
#   passed to $(libname).

#   $(call make_bin,linker,compiler-flags,link-flags,objects,lib-dirs,li\
\bs,binbase):
#   a function that generates an executable file with the given base
#   name from the given objects linking from libs that are found from
#   the given libdirs.  The first argument will always be either
#   $(LINK_c) or $(LINK_cxx).  Note that that binbase has not been
#   passed to $(binname).  Compiler support implementors are
#   encouraged to prepend the variable $(LINKWRAPPER) to link
#   statements.  This makes it possible for the user to set
#   LINKWRAPPER to some program that wraps the link step.  Examples of
#   programs that do this include Purify and Quantify.  NOTE: Your
#   make_bin function should do something with the
#   WHOLE_lib_$(libname) variables: either it should link in the whole
#   library or issue an error that it is not supported.  See
#   toolchains/unix_compiler.mk and toolchains/msvc.mk for examples of
#   each case.
#
#   $(call make_shlib,linker,compiler-flags,link-flags,objects,lib-dirs,\
\libs,shlibbase,major,minor,revision):
#   function that creates a library with the given base name.  This
#   function must take the same arguments as make_bin plus the shared
#   library version information.  Compiler support authors are
#   encouraged to prepend the link statement with $(LINKWRAPPER) as
#   with make_bin.

# When preparing to use a specific toolchain, please see comments in
# that toolchain's makefile fragment for any requirements that it may
# have.

# ---------------

TARGETS_lib ?=
TARGETS_bin ?=

# Make sure the user has asked for some targets.
ifeq ($(words $(TARGETS_lib) $(TARGETS_bin)), 0)
_qtx_dummy := $(call QTC.TC,abuild,ccxx.mk no targets,0)
$(error No ccxx targets are defined)
endif

# Separate TARGETS_lib into _TARGETS_static_lib and
# _TARGETS_shared_lib
_TARGETS_shared_lib := $(filter-out %:static,$(foreach L,$(TARGETS_lib),\
\$(L)$(if $(filter undefined,$(origin SHLIB_$(L))),:static)))
_TARGETS_static_lib := $(filter-out $(_TARGETS_shared_lib),$(TARGETS_lib))

# Define ccxx_shlibname to call shlibname with the right arguments
define ccxx_shlibname
$(call shlibname,$(1),$(word 1,$(SHLIB_$(1))),$(word 2,$(SHLIB_$(1))),$(\
\word 3,$(SHLIB_$(1))))
endef

# Define ccxx_all_shlibnames to get all variants of the shared library name
define ccxx_all_shlibnames
$(sort $(call shlibname,$(1),$(word 1,$(SHLIB_$(1))),$(word 2,$(SHLIB_$(\
\1))),$(word 3,$(SHLIB_$(1)))) \
       $(call shlibname,$(1),$(word 1,$(SHLIB_$(1))),$(word 2,$(SHLIB_$(\
\1))),) \
       $(call shlibname,$(1),$(word 1,$(SHLIB_$(1))),,) \
       $(call shlibname,$(1),,,))
endef

# Add each target to the "all" and "clean" rules
_static_lib_TARGETS := $(foreach T,$(_TARGETS_static_lib),$(call libname,$(T)))
_shared_lib_TARGETS := $(foreach T,$(_TARGETS_shared_lib),$(call ccxx_sh\
\libname,$(T)))
_lib_TARGETS := $(_static_lib_TARGETS) $(_shared_lib_TARGETS)
_bin_TARGETS := $(foreach T,$(TARGETS_bin),$(call binname,$(T)))

all:: $(_lib_TARGETS) $(_bin_TARGETS)

# Add all local libraries to LIBS and all local library directories to
# LIBDIRS.
ifneq ($(words $(TARGETS_lib)),0)
LIBS := $(filter-out $(LIBS),$(_TARGETS_static_lib) $(_TARGETS_shared_li\
\b)) $(LIBS)
LIBDIRS := $(filter-out $(LIBDIRS),.) $(LIBDIRS)
endif

# Make sure that the user has provided sources for each target.
_UNDEFINED := $(call undefined_vars,\
                $(foreach T,$(TARGETS_lib),SRCS_lib_$(T)) \
                $(foreach T,$(TARGETS_bin),SRCS_bin_$(T)))
ifneq ($(words $(_UNDEFINED)),0)
_qtx_dummy := $(call QTC.TC,abuild,ccxx.mk undefined variables,0)
$(error The following variables are undefined: $(_UNDEFINED))
endif

# Basic compilation functions

DFLAGS ?=
OFLAGS ?=
WFLAGS ?=
XCPPFLAGS ?=
XCFLAGS ?=
XCXXFLAGS ?=
XLINKFLAGS ?=
LINKWRAPPER ?=
LINK_AS_C ?=

ifeq ($(ABUILD_SUPPORT_1_0),1)
 ifneq ($(origin LINK_SHLIBS), undefined)
  ifeq (-$(strip $(LINK_SHLIBS))-,--)
   $(error setting LINK_SHLIBS to an empty value no longer works; overri\
\de LIBS instead)
  else
   $(call deprecate,1.1,LINK_SHLIBS is deprecated; as of version 1.0.3$(\
\_comma) abuild always links shared libraries)
  endif
 endif
endif

# These functions expand to the complete list of debug, optimization
# and warning flags that apply to a specific file.  In this case,
# file-specific values override general values.

# Usage: $(call file_dflags,src)
define file_dflags
$(call value_if_defined,DFLAGS_$(call strip_srcdir,$(1)),$(DFLAGS))
endef

# Usage: $(call file_oflags,src)
define file_oflags
$(call value_if_defined,OFLAGS_$(call strip_srcdir,$(1)),$(OFLAGS))
endef

# Usage: $(call file_wflags,src)
define file_wflags
$(call value_if_defined,WFLAGS_$(call strip_srcdir,$(1)),$(WFLAGS))
endef

# Usage: $(call file_dowflags,src)
define file_dowflags
$(call file_dflags,$(1)) $(call file_oflags,$(1)) $(call file_wflags,$(1))
endef

# These functions expand to the complete list of "extra" flags that
# apply to a specific file.  They are, from general to specific:
# XCPPFLAGS, then XCPPFLAGS_file (and similar for CFLAGS and
# CXXFLAGS).  We use $(call value_if_defined ...) to access the
# file-specific variables to avoid the undefined variable warning for
# each undefined variable since not defining these is the usual case.

# Usage: $(call file_cppflags,src)
define file_cppflags
$(call include_flags,$(INCLUDES) $(SRCDIR) .) $(call file_dowflags,$(1))\
\ $(XCPPFLAGS) $(call value_if_defined,XCPPFLAGS_$(call strip_srcdir,$(1)),)
endef

# Usage: $(call file_cflags,src)
define file_cflags
$(call file_cppflags,$(1)) $(XCFLAGS) $(call value_if_defined,XCFLAGS_$(\
\call strip_srcdir,$(1)),)
endef

# Usage: $(call file_cxxflags,src)
define file_cxxflags
$(call file_cflags,$(1)) $(XCXXFLAGS) $(call value_if_defined,XCXXFLAGS_\
\$(call strip_srcdir,$(1)),)
endef

# Usage: $(call use_pic,src): determines the value of pic to pass to make_obj
define use_pic
$(and $(filter $(call strip_srcdir,$(<)), $(_lib_SRCS)), $(if $(call val\
\ue_if_defined,NOPIC_$(call strip_srcdir,$(<)),),,1))
endef

LANGNAME_c := C
LANGNAME_cxx := C++
CCXX_GEN_DEPS ?= $(GEN_DEPS)
CCCXX_LINKER = $(if $(LINK_AS_C),$(LINK_c),$(LINK_cxx))
# Usage: $(call ccxx_compile,language): language = { c | cxx }
define ccxx_compile
        @: $(call QTC.TC,abuild,ccxx.mk ccxx_compile,0)
        @mkdir -p $(dir $@)
        $(CCXX_GEN_DEPS) \
                "$(PREPROCESS_$(1)) $(call file_cppflags,$<)" \
                "$<" "$@" "$*.$(DEP)"
        -$(RM) $@
        @$(PRINT) "Compiling $< as $(LANGNAME_$(1))"
        $(call make_obj,$(COMPILE_$(1)),$(call use_pic,$<), \
                        $(call file_$(1)flags,$<),$<,$@)
endef

# Usage: $(call ccxx_preprocess,language): language = { c | cxx }
define ccxx_preprocess
        @mkdir -p $(dir $@)
        @$(PRINT) "Preprocessing $< as $(LANGNAME_$(1)) to $@"
        -$(RM) $@
        $(PREPROCESS_$(1)) $(call file_cppflags,$<) $< > $@
endef

# Usage: $(call ccxx_make_static_lib,library-base)
define ccxx_make_static_lib
        @: $(call QTC.TC,abuild,ccxx.mk ccxx_make_static_lib,0)
        -$(RM) $(call libname,$(1))
        @$(PRINT) "Creating $(1) library"
        $(call make_lib,$(OBJS_lib_$(1)),$(1))
endef

# Usage: $(call ccxx_make_shared_lib,library-base)
define ccxx_make_shared_lib
        @: $(call QTC.TC,abuild,ccxx.mk ccxx_make_shared_lib,0)
        @$(PRINT) "Creating $(1) shared library"
er-out $(_TARGETS_shared_lib),$(LIBS)),$(1),$(word 1,$(SHLIB_$(1))),$(wo\
\rd 2,$(SHLIB_$(1))),$(word 3,$(SHLIB_$(1))))
endef

# Usage: $(call ccxx_make_bin,executable-base)
define ccxx_make_bin
        @: $(call QTC.TC,abuild,ccxx.mk ccxx_make_bin,0)
        -$(RM) $(call binname,$(1))
        @$(PRINT) "Creating $(1) executable"
        $(call make_bin,$(CCCXX_LINKER),$(XCFLAGS) $(XCXXFLAGS) $(DFLAGS\
\) $(OFLAGS) $(WFLAGS),$(XLINKFLAGS),$(OBJS_bin_$(1)),$(LIBDIRS),$(LIBS),$(1))
endef

c_to_o = $(call ccxx_compile,c)
cxx_to_o = $(call ccxx_compile,cxx)
lib_c_to_o = $(c_to_o)
bin_c_to_o = $(c_to_o)
lib_cxx_to_o = $(cxx_to_o)
bin_cxx_to_o = $(cxx_to_o)
c_to_i = $(call ccxx_preprocess,c)
cxx_to_i = $(call ccxx_preprocess,cxx)

# For each SRCS_lib_x and SRCS_bin_x, create corresponding OBJS_lib_x
# and OBJS_bin_x by transforming all .c, .cc, and .cpp file names to
# object file names.

$(foreach T,$(TARGETS_lib),\
   $(eval OBJS_lib_$(T) := \
       $(call x_to_y,c,$(LOBJ),SRCS_lib_$(T)) \
       $(call x_to_y,cc,$(LOBJ),SRCS_lib_$(T)) \
       $(call x_to_y,cpp,$(LOBJ),SRCS_lib_$(T))))
$(foreach T,$(TARGETS_bin),\
   $(eval OBJS_bin_$(T) := \
       $(call x_to_y,c,$(OBJ),SRCS_bin_$(T)) \
       $(call x_to_y,cc,$(OBJ),SRCS_bin_$(T)) \
       $(call x_to_y,cpp,$(OBJ),SRCS_bin_$(T))))

# Combine all sources from various bases into types (lib and bin) and
# then separate by suffix.  These variables are used for static pattern
# rules to invoke the correct compilation steps for files based on
# suffix and target type.

_lib_SRCS := $(sort $(foreach T,$(TARGETS_lib),$(SRCS_lib_$(T))))
_bin_SRCS := $(sort $(foreach T,$(TARGETS_bin),$(SRCS_bin_$(T))))
_all_SRCS := $(sort $(_lib_SRCS) $(_bin_SRCS))
_lib_COBJS := $(call x_to_y,c,$(LOBJ),_lib_SRCS)
_lib_CCOBJS := $(call x_to_y,cc,$(LOBJ),_lib_SRCS)
_lib_CPPOBJS := $(call x_to_y,cpp,$(LOBJ),_lib_SRCS)
_bin_COBJS := $(call x_to_y,c,$(OBJ),_bin_SRCS)
_bin_CCOBJS := $(call x_to_y,cc,$(OBJ),_bin_SRCS)
_bin_CPPOBJS := $(call x_to_y,cpp,$(OBJ),_bin_SRCS)
_Cpproc := $(call x_to_y,c,i,_lib_SRCS) $(call x_to_y,c,i,_bin_SRCS)
_CCpproc := $(call x_to_y,cc,i,_lib_SRCS) $(call x_to_y,cc,i,_bin_SRCS)
_CPPpproc :=    $(call x_to_y,cpp,i,_lib_SRCS) $(call x_to_y,cpp,i,_bin_SRCS)

# Make sure ".." doesn't appear in any source file names.
ifneq ($(words $(findstring /../,$(_all_SRCS)) $(filter ../%,$(_all_SRCS))), 0)
_qtx_dummy := $(call QTC.TC,abuild,ccxx.mk ERR .. in srcs,0)
$(error The path component ".." may not appear in any source file names)
endif

# Include dependency files for each source file
_lib_OBJS := $(_lib_COBJS) $(_lib_CCOBJS) $(_lib_CPPOBJS)
_bin_OBJS := $(_bin_COBJS) $(_bin_CCOBJS) $(_bin_CPPOBJS)
_all_DEPS := $(call x_to_y,$(LOBJ),$(DEP),_lib_OBJS) \
             $(call x_to_y,$(OBJ),$(DEP),_bin_OBJS)

# Remove any extraneous dep files
_extra_deps := $(filter-out $(_all_DEPS),$(wildcard *.$(DEP)))
ifneq ($(words $(_extra_deps)),0)
_qtx_dummy := $(call QTC.TC,abuild,ccxx.mk remove extra deps,0)
DUMMY := $(shell $(PRINT) 1>&2 Removing extraneous $(DEP) files)
DUMMY := $(shell $(RM) $(_extra_deps))
endif

-include $(_all_DEPS)


# Define static pattern rules that invoke the proper compilation
# function for each object file.

$(_lib_COBJS): %.$(LOBJ): %.c
        $(lib_c_to_o)

$(_lib_CCOBJS): %.$(LOBJ): %.cc
        $(lib_cxx_to_o)

$(_lib_CPPOBJS): %.$(LOBJ): %.cpp
        $(lib_cxx_to_o)

$(_bin_COBJS): %.$(OBJ): %.c
        $(bin_c_to_o)

$(_bin_CCOBJS): %.$(OBJ): %.cc
        $(bin_cxx_to_o)

$(_bin_CPPOBJS): %.$(OBJ): %.cpp
        $(bin_cxx_to_o)

$(_Cpproc): %.i: %.c FORCE
        $(c_to_i)

$(_CCpproc): %.i: %.cc FORCE
        $(cxx_to_i)

$(_CPPpproc): %.i: %.cpp FORCE
        $(cxx_to_i)

# Ensure that we can use -llib dependencies properly.
.LIBPATTERNS ?=
$(foreach PAT,$(.LIBPATTERNS),$(eval vpath $(PAT) $(LIBDIRS)))

# For each library and executable target, create a rule that makes the
# target dependent on its objects.  Also make executable targets
# depend on the libraries in LIBS, which includes local libraries,
# and shared libary targets depend on the static libraries in LIBS.
# In addition, we make local executable targets explicitly depend on
# local library targets.  The reason for doing this as well as adding
# the -llib target for local libraries is that make will not try to
# build the -llib target if it doesn't exist.
l_LIBS = $(foreach L,$(LIBS),-l$(L))
l_not_local_shared = $(foreach L,$(filter-out $(_TARGETS_shared_lib),$(L\
\IBS)),-l$(L))
$(foreach T,$(_TARGETS_static_lib),\
   $(eval $(call libname,$(T)): $(OBJS_lib_$(T)) ; \
        $(call ccxx_make_static_lib,$(T))))
$(foreach T,$(_TARGETS_shared_lib),\
   $(eval $(call ccxx_shlibname,$(T)): $(OBJS_lib_$(T)) $(l_not_local_sh\
\ared) $(_static_lib_TARGETS); \
        $(call ccxx_make_shared_lib,$(T))))
$(foreach T,$(TARGETS_bin),\
   $(eval $(call binname,$(T)): $(OBJS_bin_$(T)) $(l_LIBS) $(_lib_TARGETS); \
        $(call ccxx_make_bin,$(T))))


# For each local library target x that does not exist, make -lx depend
# on $(call libname,x).  This prevents errors about -lx not existing
# when a binary target is built explicitly from clean.  We avoid
# creating this dependency if the library already exists because
# otherwise make will translate this into a circular dependency when
# it replaces -lx with the actual library file in the rule.
$(foreach T,$(_TARGETS_static_lib),\
   $(eval -l$(T): $(if $(wildcard $(call libname,$(T))),,$(call libname,\
\$(T)))))
$(foreach T,$(_TARGETS_shared_lib),\
   $(eval -l$(T): $(if $(wildcard $(call ccxx_shlibname,$(T))),,$(call c\
\cxx_shlibname,$(T)))))

_all_obj := $(sort $(_lib_OBJS) $(_bin_OBJS))
# The list of all libraries includes static versions of the shared
# libraries as well since on some platforms (Windows), creating a
# shared library also creates a static library of the same name.
_all_lib := $(sort \
   $(foreach T,$(TARGETS_lib),$(call libname,$(T))) \
   $(foreach T,$(_TARGETS_shared_lib),$(call ccxx_all_shlibnames,$(T))))
_all_bin := $(foreach T,$(TARGETS_bin),$(call binname,$(T)))

# Check for and remove orphan targets
IGNORE_TARGETS ?=
_existing_obj := $(sort $(wildcard *.$(OBJ) *.$(LOBJ)))
_extra_obj := $(filter-out $(_all_obj) $(IGNORE_TARGETS),$(_existing_obj))
ifeq ($(words $(_extra_obj)),0)
 # No extra objects found; check for other extra targets.  Check for
 # libraries and shared libraries, and if we can recognize executables
 # as such (they have some recognizable suffix), check for them as
 # well.
 _all_other := $(_all_lib)
 _existing_other := $(sort $(wildcard $(call libname,*) $(call shlibname\
\,*,,,)))
 ifneq ($(call binname,*),*)
  # If executables are recognizable as such
  _all_other += $(_all_bin)
  _existing_other += $(sort $(wildcard $(call binname,*)))
 endif
 _extra_other := $(filter-out $(_all_other) $(IGNORE_TARGETS),$(_existin\
\g_other))
 ifneq ($(words $(_extra_other)),0)
  _qtx_dummy := $(call QTC.TC,abuild,ccxx.mk found extra other,0)
  # For all binary and library targets to relink
  DUMMY := $(shell $(PRINT) 1>&2 Extra targets found: removing libraries\
\ and binaries)
  DUMMY := $(shell $(RM) $(_extra_other) $(_all_lib) $(_all_bin))
 endif
else
 # Extra object files found; remove all extra objects as well as any
 # library or binary targets which we want to force to be recreated.
 _qtx_dummy := $(call QTC.TC,abuild,ccxx.mk found extra objs,0)
 DUMMY := $(shell $(PRINT) 1>&2 Extra object files found: removing libra\
\ries and binaries)
 DUMMY := $(shell $(RM) $(_extra_obj) $(_all_lib) $(_all_bin))
endif

# Create a debugging target that shows values of some critical
# variables.
.PHONY: ccxx_debug
ccxx_debug::
        @: $(call QTC.TC,abuild,ccxx.mk ccxx_debug,0)
        @$(PRINT) INCLUDES = $(INCLUDES)
        @$(PRINT) LIBDIRS = $(LIBDIRS)
        @$(PRINT) LIBS = $(LIBS)

# Include built-in support for certain code generators.  These should
# have been plugins, but they were added before plugins were
# supported.
include $(abMK)/standard-code-generators.mk