#
#
#  BLIS    
#  An object-based framework for developing high-performance BLAS-like
#  libraries.
#
#  Copyright (C) 2014, The University of Texas at Austin
#  Copyright (C) 2020, Advanced Micro Devices, Inc.
#
#  Redistribution and use in source and binary forms, with or without
#  modification, are permitted provided that the following conditions are
#  met:
#   - Redistributions of source code must retain the above copyright
#     notice, this list of conditions and the following disclaimer.
#   - Redistributions in binary form must reproduce the above copyright
#     notice, this list of conditions and the following disclaimer in the
#     documentation and/or other materials provided with the distribution.
#   - Neither the name(s) of the copyright holder(s) nor the names of its
#     contributors may be used to endorse or promote products derived
#     from this software without specific prior written permission.
#
#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
#  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
#  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
#  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
#  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
#  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
#  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
#  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
#  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
#  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
#  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
#

# Only include this block of code once
ifndef COMMON_MK_INCLUDED
COMMON_MK_INCLUDED := yes



#
# --- CFLAGS storage functions -------------------------------------------------
#

# Define a function that stores the value of a variable to a different
# variable containing a specified suffix (corresponding to a configuration).
define store-var-for
$(strip $(1)).$(strip $(2)) := $($(strip $(1)))
endef

# Define a function similar to store-var-for, except that appends instead
# of overwriting.
define append-var-for
$(strip $(1)).$(strip $(2)) += $($(strip $(1)))
endef

# Define a function that stores the value of all of the variables in a
# make_defs.mk file to other variables with the configuration (the
# argument $(1)) added as a suffix. This function is called once from
# each make_defs.mk. Also, add the configuration to CONFIGS_INCL.
define store-make-defs
$(eval $(call store-var-for,CC,         $(1)))
$(eval $(call store-var-for,CC_VENDOR,  $(1)))
$(eval $(call store-var-for,CPPROCFLAGS,$(1)))
$(eval $(call store-var-for,CLANGFLAGS, $(1)))
$(eval $(call store-var-for,CXXLANGFLAGS,$(1)))
$(eval $(call store-var-for,CMISCFLAGS, $(1)))
$(eval $(call store-var-for,CPICFLAGS,  $(1)))
$(eval $(call store-var-for,CWARNFLAGS, $(1)))
$(eval $(call store-var-for,CDBGFLAGS,  $(1)))
$(eval $(call store-var-for,COPTFLAGS,  $(1)))
$(eval $(call store-var-for,CKOPTFLAGS, $(1)))
$(eval $(call store-var-for,CKVECFLAGS, $(1)))
$(eval $(call store-var-for,CROPTFLAGS, $(1)))
$(eval $(call store-var-for,CRVECFLAGS, $(1)))
CONFIGS_INCL += $(1)
endef

# Define a function that retreives the value of a variable for a
# given configuration.
define load-var-for
$($(strip $(1)).$(strip $(2)))
endef



#
# --- CFLAGS query functions ---------------------------------------------------
#

# Define some functions that return the appropriate CFLAGS for a given
# configuration. This assumes that the make_defs.mk files have already been
# included, which results in those values having been stored to
# configuration-qualified variables.

get-noopt-cflags-for     = $(strip $(CFLAGS_PRESET) \
                                   $(call load-var-for,CDBGFLAGS,$(1)) \
                                   $(call load-var-for,CWARNFLAGS,$(1)) \
                                   $(call load-var-for,CPICFLAGS,$(1)) \
                                   $(call load-var-for,CMISCFLAGS,$(1)) \
                                   $(call load-var-for,CLANGFLAGS,$(1)) \
                                   $(call load-var-for,CPPROCFLAGS,$(1)) \
                                   $(CTHREADFLAGS) \
                                   $(CINCFLAGS) $(VERS_DEF) \
                            )

get-noopt-cxxflags-for   = $(strip $(CFLAGS_PRESET) \
                                   $(call load-var-for,CDBGFLAGS,$(1)) \
                                   $(call load-var-for,CWARNFLAGS,$(1)) \
                                   $(call load-var-for,CPICFLAGS,$(1)) \
                                   $(call load-var-for,CMISCFLAGS,$(1)) \
                                   $(call load-var-for,CXXLANGFLAGS,$(1)) \
                                   $(call load-var-for,CPPROCFLAGS,$(1)) \
                                   $(CTHREADFLAGS) \
                                   $(CINCFLAGS) $(VERS_DEF) \
                            )

get-refinit-cflags-for   = $(strip $(call load-var-for,COPTFLAGS,$(1)) \
                                   $(call get-noopt-cflags-for,$(1)) \
                                   -DBLIS_CNAME=$(1) \
                                   $(BUILD_CPPFLAGS) \
                                   $(BUILD_SYMFLAGS) \
                            )

get-refkern-cflags-for   = $(strip $(call load-var-for,CROPTFLAGS,$(1)) \
                                   $(call load-var-for,CRVECFLAGS,$(1)) \
                                   $(call get-noopt-cflags-for,$(1)) \
                                   $(COMPSIMDFLAGS) \
                                   -DBLIS_CNAME=$(1) \
                                   $(BUILD_CPPFLAGS) \
                                   $(BUILD_SYMFLAGS) \
                            )

get-config-cflags-for    = $(strip $(call load-var-for,COPTFLAGS,$(1)) \
                                   $(call get-noopt-cflags-for,$(1)) \
                                   $(BUILD_CPPFLAGS) \
                                   $(BUILD_SYMFLAGS) \
                            )

get-frame-cflags-for     = $(strip $(call load-var-for,COPTFLAGS,$(1)) \
                                   $(call get-noopt-cflags-for,$(1)) \
                                   $(BUILD_CPPFLAGS) \
                                   $(BUILD_SYMFLAGS) \
                            )

get-aocldtl-cflags-for     = $(strip $(call load-var-for,COPTFLAGS,$(1)) \
                                   $(call get-noopt-cflags-for,$(1)) \
                                   $(BUILD_CPPFLAGS) \
                                   $(BUILD_SYMFLAGS) \
                            )


get-kernel-cflags-for    = $(strip $(call load-var-for,CKOPTFLAGS,$(1)) \
                                   $(call load-var-for,CKVECFLAGS,$(1)) \
                                   $(call get-noopt-cflags-for,$(1)) \
                                   $(COMPSIMDFLAGS) \
                                   $(BUILD_CPPFLAGS) \
                                   $(BUILD_SYMFLAGS) \
                            )

# When compiling sandboxes, we use flags similar to those of general framework
# source. This ensures that the same code can be linked and run across various
# sub-configurations. (If we switch to using refkern/kernel flags, we should
# prevent enabling sandboxes for umbrella families by verifying that
# config_list == config_name if --enable-sandbox is given.)
get-sandbox-c99flags-for = $(strip $(call load-var-for,COPTFLAGS,$(1)) \
                                   $(call get-noopt-cflags-for,$(1)) \
                                   $(CSBOXINCFLAGS) \
                                   $(BUILD_CPPFLAGS) \
                                   $(BUILD_SYMFLAGS) \
                            )
get-sandbox-cxxflags-for = $(strip $(call load-var-for,COPTFLAGS,$(1)) \
                                   $(call get-noopt-cxxflags-for,$(1)) \
                                   $(CSBOXINCFLAGS) \
                                   $(BUILD_CPPFLAGS) \
                                   $(BUILD_SYMFLAGS) \
                            )

# Define a separate function that will return appropriate flags for use by
# applications that want to use the same basic flags as those used when BLIS
# was compiled. (NOTE: This is the same as the $(get-frame-cflags-for ...)
# function, except that it omits two variables that contain flags exclusively
# for use when BLIS is being compiled/built: BUILD_CPPFLAGS, which contains a
# cpp macro that confirms that BLIS is being built; and BUILD_SYMFLAGS, which
# contains symbol export flags that are only needed when a shared library is
# being compiled/linked.)
get-user-cflags-for      = $(strip $(call load-var-for,COPTFLAGS,$(1)) \
                                   $(call get-noopt-cflags-for,$(1)) \
                            )

# Define functions that return messages appropriate for each non-verbose line
# of compilation output.
get-noopt-text          = "(CFLAGS for no optimization)"
get-refinit-text-for    = "('$(1)' CFLAGS for ref. kernel init)"
get-refkern-text-for    = "('$(1)' CFLAGS for ref. kernels)"
get-config-text-for     = "('$(1)' CFLAGS for config code)"
get-frame-text-for      = "('$(1)' CFLAGS for framework code)"
get-aocldtl-text-for    = "('$(1)' CFLAGS for AOCL debug and trace code)"
get-kernel-text-for     = "('$(1)' CFLAGS for kernels)"
get-sandbox-c99text-for = "('$(1)' CFLAGS for sandboxes)"
get-sandbox-cxxtext-for = "('$(1)' CXXFLAGS for sandboxes)"



#
# --- Miscellaneous helper functions -------------------------------------------
#

# Define functions that filters a list of filepaths $(1) that contain (or
# omit) an arbitrary substring $(2).
files-that-contain      = $(strip $(foreach f, $(1), $(if $(findstring $(2),$(f)),$(f),)))
files-that-dont-contain = $(strip $(foreach f, $(1), $(if $(findstring $(2),$(f)),,$(f))))

# Define a function that removes duplicate words from a list.
# NOTE: This function was obtained via [1]; thanks bobbogo for this
# concise definition.
# [1] https://stackoverflow.com/questions/16144115/makefile-remove-duplicate-words-without-sorting
rm-dupls = $(if $1,$(firstword $1) $(call rm-dupls,$(filter-out $(firstword $1),$1)))


#
# --- Include makefile configuration file --------------------------------------
#

# Use the current directory as the default path to the root directory for
# makefile fragments (and the configuration family's make_defs.mk), but
# allow the includer to override this value if it needs to point to an
# installation directory.
ifeq ($(strip $(SHARE_PATH)),)
SHARE_PATH        := .
endif

# Define the name of the configuration file.
CONFIG_MK_FILE     := config.mk

# Identify the base path for the root directory for makefile fragments (and
# the configuration family's make_defs.mk). We define this path in terms of
# SHARE_PATH, which gets a default value above (which is what happens for the
# top-level Makefile). If SHARE_PATH is specified by the Makefile prior to
# including common.mk, that path is used instead. This allows Makefiles for
# example code and test drivers to reference an installed prefix directory
# for situations when the build directory no longer exists.
BASE_SHARE_PATH    := $(SHARE_PATH)

# Include the configuration file.
-include $(BASE_SHARE_PATH)/$(CONFIG_MK_FILE)



#
# --- Handle 'make clean' and friends without config.mk ------------------------
#

# Detect whether we actually got the configuration file. If we didn't, then
# it is likely that the user has not yet generated it (via configure).
ifeq ($(strip $(CONFIG_MK_INCLUDED)),yes)
CONFIG_MK_PRESENT := yes
IS_CONFIGURED     := yes
else
CONFIG_MK_PRESENT := no
IS_CONFIGURED     := no
endif

# If we didn't get config.mk, then we need to set some basic variables so
# that make will function without error for things like 'make clean'.
ifeq ($(IS_CONFIGURED),no)

# If this makefile fragment is being run and there is no config.mk present,
# then it's probably safe to assume that the user is currently located in the
# source distribution.
DIST_PATH := .

# Even though they won't be used explicitly, it appears that setting these
# INSTALL_* variables to something sane (that is, not allowing them default
# to the empty string) is necessary to prevent make from hanging, likely
# because the statements that define UNINSTALL_LIBS and UNINSTALL_HEADERS,
# when evaluated, result in running 'find' on the root directory--definitely
# something we would like to avoid.
INSTALL_LIBDIR   := $(HOME)/blis/lib
INSTALL_INCDIR   := $(HOME)/blis/include
INSTALL_SHAREDIR := $(HOME)/blis/share

endif



#
# --- Primary makefile variable definitions ------------------------------------
#

# Construct the architecture-version string, which will be used to name the
# library upon installation.
VERS_CONF          := $(VERSION)-$(CONFIG_NAME)

# All makefile fragments in the tree will have this name.
FRAGMENT_MK        := .fragment.mk

# Locations of important files.
BUILD_DIR          := build
CONFIG_DIR         := config
FRAME_DIR          := frame
AOCLDTL_DIR        := aocl_dtl
REFKERN_DIR        := ref_kernels
KERNELS_DIR        := kernels
SANDBOX_DIR        := sandbox
OBJ_DIR            := obj
LIB_DIR            := lib
INCLUDE_DIR        := include
BLASTEST_DIR       := blastest
TESTSUITE_DIR      := testsuite
CPP_HEADER_DIR     := cpp
CPP_TEST_DIR       := testcpp

# The filename suffix for reference kernels.
REFNM              := ref

# Source suffixes.
CONFIG_SRC_SUFS    := c

KERNELS_SRC_SUFS   := c s S

FRAME_SRC_SUFS     := c

AOCLDTL_SRC_SUFS   := c

SANDBOX_C99_SUFS   := c
SANDBOX_CXX_SUFS   := cc cpp cxx
SANDBOX_SRC_SUFS   := $(SANDBOX_C99_SUFS) $(SANDBOX_CXX_SUFS)

# Header suffixes.
FRAME_HDR_SUFS     := h

AOCLDTL_HDR_SUFS   := h

SANDBOX_H99_SUFS   := h
SANDBOX_HXX_SUFS   := hh hpp hxx
SANDBOX_HDR_SUFS   := $(SANDBOX_H99_SUFS) $(SANDBOX_HXX_SUFS)

# Combine all header suffixes and remove duplicates via sort().
ALL_HDR_SUFS       := $(sort $(FRAME_HDR_SUFS)   \
                             $(SANDBOX_HDR_SUFS) \
                             $(AOCLDTL_HDR_SUFS))

ALL_H99_SUFS       := $(sort $(FRAME_HDR_SUFS)   \
                             $(SANDBOX_H99_SUFS) \
                             $(AOCLDTL_HDR_SUFS))

# The names of scripts that check output from the BLAS test drivers and
# BLIS test suite.
BLASTEST_CHECK     := check-blastest.sh
TESTSUITE_CHECK    := check-blistest.sh

# The names of the testsuite input/configuration files.
TESTSUITE_CONF_GEN := input.general
TESTSUITE_CONF_OPS := input.operations
TESTSUITE_FAST_GEN := input.general.fast
TESTSUITE_FAST_OPS := input.operations.fast
TESTSUITE_MIXD_GEN := input.general.mixed
TESTSUITE_MIXD_OPS := input.operations.mixed
TESTSUITE_SALT_GEN := input.general.salt
TESTSUITE_SALT_OPS := input.operations.salt
TESTSUITE_OUT_FILE := output.testsuite

# CHANGELOG file.
CHANGELOG          := CHANGELOG

# Something for OS X so that echo -n works as expected.
SHELL              := bash

# Construct paths to the four primary directories of source code:
# the config directory, general framework code, reference kernel code,
# and optimized kernel code.
CONFIG_PATH        := $(DIST_PATH)/$(CONFIG_DIR)
FRAME_PATH         := $(DIST_PATH)/$(FRAME_DIR)
AOCLDTL_PATH       := $(DIST_PATH)/$(AOCLDTL_DIR)
REFKERN_PATH       := $(DIST_PATH)/$(REFKERN_DIR)
KERNELS_PATH       := $(DIST_PATH)/$(KERNELS_DIR)
SANDBOX_PATH       := $(DIST_PATH)/$(SANDBOX_DIR)

# Construct paths to the makefile fragments for the four primary directories
# of source code: the config directory, general framework code, reference
# kernel code, and optimized kernel code.
CONFIG_FRAG_PATH   := ./obj/$(CONFIG_NAME)/$(CONFIG_DIR)
FRAME_FRAG_PATH    := ./obj/$(CONFIG_NAME)/$(FRAME_DIR)
AOCLDTL_FRAG_PATH  := ./obj/$(CONFIG_NAME)/$(AOCLDTL_DIR)
REFKERN_FRAG_PATH  := ./obj/$(CONFIG_NAME)/$(REFKERN_DIR)
KERNELS_FRAG_PATH  := ./obj/$(CONFIG_NAME)/$(KERNELS_DIR)
SANDBOX_FRAG_PATH  := ./obj/$(CONFIG_NAME)/$(SANDBOX_DIR)



#
# --- Library name and local paths ---------------------------------------------
#

# Use lib/CONFIG_NAME as the default path to the local header files, but
# allow the includer to override this value if it needs to point to an
# installation directory.
ifeq ($(strip $(LIB_PATH)),)
LIB_PATH           := $(LIB_DIR)/$(CONFIG_NAME)
endif

# Identify the base path for the intermediate library directory. We define
# this path in terms of LIB_PATH, which gets a default value above (which is
# what happens for the top-level Makefile). If LIB_PATH is specified by the
# Makefile prior to including common.mk, that path is used instead. This
# allows Makefiles for example code and test drivers to reference an installed
# prefix directory for situations when the build directory no longer exists.
BASE_LIB_PATH      := $(LIB_PATH)

# The base name of the BLIS library that we will build.
ifeq ($(THREADING_MODEL),off)
LIBBLIS            := libblis
else
LIBBLIS            := libblis-mt
endif

# The shared (dynamic) library file suffix is different for Linux and OS X.
ifeq ($(OS_NAME),Darwin)
SHLIB_EXT          := dylib
else ifeq ($(IS_WIN),yes)
ifeq ($(CC_VENDOR),gcc)
SHLIB_EXT          := dll.a
else
SHLIB_EXT          := lib
endif
else
SHLIB_EXT          := so
endif

# Note: These names will be modified later to include the configuration and
# version strings.
LIBBLIS_A          := $(LIBBLIS).a
LIBBLIS_SO         := $(LIBBLIS).$(SHLIB_EXT)

# Append the base library path to the library names.
LIBBLIS_A_PATH     := $(BASE_LIB_PATH)/$(LIBBLIS_A)
LIBBLIS_SO_PATH    := $(BASE_LIB_PATH)/$(LIBBLIS_SO)

# Create a filepath to a local symlink to the soname--that is, the same as
# LIBBLIS_SO_PATH except with the .so major version number. Since the shared
# library lists its soname as 'libblis.so.n', where n is the .so major version
# number, a symlink in BASE_LIB_PATH is needed so that ld can find the local
# shared library when the testsuite is run via 'make test' or 'make check'.

ifeq ($(OS_NAME),Darwin)
# OS X shared library extensions.
LIBBLIS_SO_MAJ_EXT := $(SO_MAJOR).$(SHLIB_EXT)
LIBBLIS_SO_MMB_EXT := $(SO_MMB).$(SHLIB_EXT)
else ifeq ($(IS_WIN),yes)
# Windows shared library extension.
LIBBLIS_SO_MAJ_EXT := $(SO_MAJOR).dll
LIBBLIS_SO_MMB_EXT :=
else
# Linux shared library extensions.
LIBBLIS_SO_MAJ_EXT := $(SHLIB_EXT).$(SO_MAJOR)
LIBBLIS_SO_MMB_EXT := $(SHLIB_EXT).$(SO_MMB)
endif
LIBBLIS_SONAME         := $(LIBBLIS).$(LIBBLIS_SO_MAJ_EXT)
LIBBLIS_SO_MAJ_PATH    := $(BASE_LIB_PATH)/$(LIBBLIS_SONAME)

# Construct the output path when building a shared library.
# NOTE: This code and the code immediately above is a little curious and
# perhaps could be refactored (carefully).
ifeq ($(IS_WIN),yes)
LIBBLIS_SO_OUTPUT_NAME := $(LIBBLIS_SO_MAJ_PATH)
else
LIBBLIS_SO_OUTPUT_NAME := $(LIBBLIS_SO_PATH)
endif

#
# --- Utility program definitions ----------------------------------------------
#

SH         := /bin/sh
MV         := mv
MKDIR      := mkdir -p
RM_F       := rm -f
RM_RF      := rm -rf
SYMLINK    := ln -sf
FIND       := find
GREP       := grep
EGREP      := grep -E
XARGS      := xargs
INSTALL    := install -c

# Script for creating a monolithic header file.
#FLATTEN_H  := $(DIST_PATH)/build/flatten-headers.sh
FLATTEN_H  := $(PYTHON) $(DIST_PATH)/build/flatten-headers.py

# Default archiver flags.
ARFLAGS    := cr

# Used to refresh CHANGELOG.
GIT        := git
GIT_LOG    := $(GIT) log --decorate



#
# --- Default linker definitions -----------------------------------------------
#

# NOTE: This section needs to reside before the inclusion of make_defs.mk
# files (just below), as most configurations' make_defs.mk don't tinker
# with things like LDFLAGS, but some do (or may), in which case they can
# manually override whatever they need.

# Define the external libraries we may potentially need at link-time.
ifeq ($(IS_WIN),yes)
LIBM       :=
else
LIBM       := -lm
endif
LIBMEMKIND := -lmemkind

# Default linker flags.
# NOTE: -lpthread is needed unconditionally because BLIS uses pthread_once()
# to initialize itself in a thread-safe manner.
LDFLAGS    := $(LDFLAGS_PRESET) $(LIBM) $(LIBPTHREAD)

# Add libmemkind to the link-time flags, if it was enabled at configure-time.
ifeq ($(MK_ENABLE_MEMKIND),yes)
LDFLAGS    += $(LIBMEMKIND)
endif

# Never use libm with Intel compilers.
ifeq ($(CC_VENDOR),icc)
LDFLAGS    := $(filter-out $(LIBM),$(LDFLAGS))
endif

# Never use libmemkind with Intel SDE.
ifeq ($(DEBUG_TYPE),sde)
LDFLAGS    := $(filter-out $(LIBMEMKIND),$(LDFLAGS))
endif

# Specify the shared library's 'soname' field.
# NOTE: The flag for creating shared objects is different for Linux and OS X.
ifeq ($(OS_NAME),Darwin)
# OS X shared library link flags.
SOFLAGS    := -dynamiclib
SOFLAGS    += -Wl,-install_name,$(libdir)/$(LIBBLIS_SONAME)
else
SOFLAGS    := -shared
ifeq ($(IS_WIN),yes)
# Windows shared library link flags.
ifeq ($(CC_VENDOR),clang)
SOFLAGS    += -Wl,-soname,$(LIBBLIS_SONAME)
else
SOFLAGS    += -Wl,--out-implib,$(BASE_LIB_PATH)/$(LIBBLIS).dll.a
endif
else
# Linux shared library link flags.
SOFLAGS    += -Wl,-soname,$(LIBBLIS_SONAME)
endif
endif

# Decide which library to link to for things like the testsuite and BLIS test
# drivers. We default to the static library, unless only the shared library was
# enabled, in which case we use the shared library.
LIBBLIS_L      := $(LIBBLIS_A)
LIBBLIS_LINK   := $(LIBBLIS_A_PATH)
ifeq ($(MK_ENABLE_SHARED),yes)
ifeq ($(MK_ENABLE_STATIC),no)
LIBBLIS_L      := $(LIBBLIS_SO)
LIBBLIS_LINK   := $(LIBBLIS_SO_PATH)
ifeq ($(IS_WIN),no)
# For Linux and OS X: set rpath property of shared object.
LDFLAGS        += -Wl,-rpath,$(BASE_LIB_PATH)
endif
endif
# On windows, use the shared library even if static is created.
ifeq ($(IS_WIN),yes)
LIBBLIS_L      := $(LIBBLIS_SO)
LIBBLIS_LINK   := $(LIBBLIS_SO_PATH)
endif
endif


#
# --- Include makefile definitions file ----------------------------------------
#

# Define the name of the file containing build and architecture-specific
# makefile definitions.
MAKE_DEFS_FILE     := make_defs.mk

# Assemble a list of all configuration family members, including the
# configuration family name itself. Note that sort() will remove duplicates
# for situations where CONFIG_NAME is present in CONFIG_LIST, such as would
# be the case for singleton families.
CONFIG_LIST_FAM    := $(sort $(strip $(CONFIG_LIST) $(CONFIG_NAME)))

# Construct the paths to the makefile definitions files, each of which
# resides in a separate configuration sub-directory. We use CONFIG_LIST_FAM
# since we might need the makefile definitions associated with the
# configuration family (if it is an umbrella family).
# NOTE: We use the prefix $(BASE_SHARE_PATH)/$(CONFIG_DIR)/ instead of
# $(CONFIG_PATH) so that make_defs.mk can be found when it is installed,
# provided the caller defined SHARE_PATH to that install directory.
CONFIG_PATHS       := $(addprefix $(BASE_SHARE_PATH)/$(CONFIG_DIR)/, \
                                  $(CONFIG_LIST_FAM))
MAKE_DEFS_MK_PATHS := $(addsuffix /$(MAKE_DEFS_FILE), $(CONFIG_PATHS))

# Initialize the list of included (found) configurations to empty.
CONFIGS_INCL       :=

# Include the makefile definitions files implied by the list of configurations.
-include $(MAKE_DEFS_MK_PATHS)

# Detect whether we actually got all of the make definitions files. If
# we didn't, then maybe a configuration is mislabeled or missing. The
# check-env-make-defs target checks ALL_MAKE_DEFS_MK_PRESENT and outputs
# an error message if it is set to 'no'.
# NOTE: We use CONFIG_LIST_FAM as the expected list of configurations.
# This combines CONFIG_NAME with CONFIG_LIST. The inclusion of CONFIG_NAME
# is needed for situations where the configuration family is an umbrella
# family (e.g. 'intel64'), since families have separate make_def.mk files.
CONFIGS_EXPECTED := $(CONFIG_LIST_FAM)
ifeq ($(sort $(strip $(CONFIGS_INCL))), \
      $(sort $(strip $(CONFIGS_EXPECTED))))
ALL_MAKE_DEFS_MK_PRESENT := yes
else
ALL_MAKE_DEFS_MK_PRESENT := no
endif



#
# --- Configuration-agnostic flags ---------------------------------------------
#

# --- Linker program ---

# Use whatever compiler was chosen.
LINKER     := $(CC)

# --- Warning flags ---

CWARNFLAGS :=

# Disable unused function warnings and stop compiling on first error for
# all compilers that accept such options: gcc, clang, and icc.
ifneq ($(CC_VENDOR),ibm)
CWARNFLAGS += -Wall -Wno-unused-function -Wfatal-errors
endif

# Disable tautological comparision warnings in clang.
ifeq ($(CC_VENDOR),clang)
CWARNFLAGS += -Wno-tautological-compare
endif

$(foreach c, $(CONFIG_LIST_FAM), $(eval $(call append-var-for,CWARNFLAGS,$(c))))

# --- Position-independent code flags (shared libraries only) ---

# Emit position-independent code for dynamic linking.
ifeq ($(IS_WIN),yes)
# Note: Don't use any fPIC flags for Windows builds since all code is position-
# independent.
CPICFLAGS :=
else
CPICFLAGS := -fPIC
endif
$(foreach c, $(CONFIG_LIST_FAM), $(eval $(call append-var-for,CPICFLAGS,$(c))))

# --- Symbol exporting flags (shared libraries only) ---

# NOTE: These flags are only applied when building BLIS and not used by
# applications that import BLIS compilation flags via the
# $(get-user-cflags-for ...) function.

# Determine default export behavior / visibility of symbols for gcc.
ifeq ($(CC_VENDOR),gcc)
ifeq ($(IS_WIN),yes)
ifeq ($(EXPORT_SHARED),all)
BUILD_SYMFLAGS := -Wl,--export-all-symbols, -Wl,--enable-auto-import
else # ifeq ($(EXPORT_SHARED),public)
BUILD_SYMFLAGS := -Wl,--exclude-all-symbols
endif
else # ifeq ($(IS_WIN),no)
ifeq ($(EXPORT_SHARED),all)
# Export all symbols by default.
BUILD_SYMFLAGS := -fvisibility=default
else # ifeq ($(EXPORT_SHARED),public)
# Hide all symbols by default and export only those that have been annotated
# as needing to be exported.
BUILD_SYMFLAGS := -fvisibility=hidden
endif
endif
endif

# Determine default export behavior / visibility of symbols for icc.
# NOTE: The Windows branches have been omitted since we currently make no
# effort to support Windows builds via icc (only gcc/clang via AppVeyor).
ifeq ($(CC_VENDOR),icc)
ifeq ($(EXPORT_SHARED),all)
# Export all symbols by default.
BUILD_SYMFLAGS := -fvisibility=default
else # ifeq ($(EXPORT_SHARED),public)
# Hide all symbols by default and export only those that have been annotated
# as needing to be exported.
BUILD_SYMFLAGS := -fvisibility=hidden
endif
endif

# Determine default export behavior / visibility of symbols for clang.
ifeq ($(CC_VENDOR),clang)
ifeq ($(IS_WIN),yes)
ifeq ($(EXPORT_SHARED),all)
# NOTE: clang on Windows does not appear to support exporting all symbols
# by default, and therefore we ignore the value of EXPORT_SHARED.
BUILD_SYMFLAGS :=
else # ifeq ($(EXPORT_SHARED),public)
# NOTE: The default behavior of clang on Windows is to hide all symbols
# and only export functions and other declarations that have beenannotated
# as needing to be exported.
BUILD_SYMFLAGS :=
endif
else # ifeq ($(IS_WIN),no)
ifeq ($(EXPORT_SHARED),all)
# Export all symbols by default.
BUILD_SYMFLAGS := -fvisibility=default
else # ifeq ($(EXPORT_SHARED),public)
# Hide all symbols by default and export only those that have been annotated
# as needing to be exported.
BUILD_SYMFLAGS := -fvisibility=hidden
endif
endif
endif

# --- Language flags ---

# Enable C99.
CLANGFLAGS := -std=c99
$(foreach c, $(CONFIG_LIST_FAM), $(eval $(call append-var-for,CLANGFLAGS,$(c))))

# Enable C++11.
CXXLANGFLAGS := -std=c++11
$(foreach c, $(CONFIG_LIST_FAM), $(eval $(call append-var-for,CXXLANGFLAGS,$(c))))

# --- C Preprocessor flags ---

# Enable clock_gettime() in time.h.
CPPROCFLAGS := -D_POSIX_C_SOURCE=200112L
$(foreach c, $(CONFIG_LIST_FAM), $(eval $(call append-var-for,CPPROCFLAGS,$(c))))

# --- Threading flags ---

ifeq ($(CC_VENDOR),gcc)
ifeq ($(THREADING_MODEL),auto)
THREADING_MODEL := openmp
endif
ifeq ($(THREADING_MODEL),openmp)
CTHREADFLAGS := -fopenmp
LDFLAGS      += -fopenmp
endif
ifeq ($(THREADING_MODEL),pthreads)
CTHREADFLAGS := -pthread
LDFLAGS      += $(LIBPTHREAD)
endif
endif

ifeq ($(CC_VENDOR),icc)
ifeq ($(THREADING_MODEL),auto)
THREADING_MODEL := openmp
endif
ifeq ($(THREADING_MODEL),openmp)
CTHREADFLAGS := -fopenmp
LDFLAGS      += -fopenmp
endif
ifeq ($(THREADING_MODEL),pthreads)
CTHREADFLAGS := -pthread
LDFLAGS      += $(LIBPTHREAD)
endif
endif

ifeq ($(CC_VENDOR),clang)
ifeq ($(THREADING_MODEL),auto)
THREADING_MODEL := pthreads
endif
ifeq ($(THREADING_MODEL),openmp)
CTHREADFLAGS := -fopenmp
LDFLAGS      += -fopenmp
endif
ifeq ($(THREADING_MODEL),pthreads)
CTHREADFLAGS := -pthread
LDFLAGS      += $(LIBPTHREAD)
endif
endif

# --- #pragma omp simd flags (used for reference kernels only) ---

ifeq ($(PRAGMA_OMP_SIMD),yes)
ifeq ($(CC_VENDOR),gcc)
COMPSIMDFLAGS := -fopenmp-simd
else
ifeq ($(CC_VENDOR),clang)
COMPSIMDFLAGS := -fopenmp-simd
else
ifeq ($(CC_VENDOR),icc)
COMPSIMDFLAGS := -qopenmp-simd
endif
endif
endif
else # ifeq ($(PRAGMA_OMP_SIMD),no)
COMPSIMDFLAGS :=
endif



#
# --- Adjust verbosity level manually using make V=[0,1] -----------------------
#

ifeq ($(V),1)
ENABLE_VERBOSE := yes
BLIS_ENABLE_TEST_OUTPUT := yes
endif

ifeq ($(V),0)
ENABLE_VERBOSE := no
BLIS_ENABLE_TEST_OUTPUT := no
endif

#
# --- Append OS-specific libraries to LDFLAGS ----------------------------------
#

ifeq ($(OS_NAME),Linux)
LDFLAGS += -lrt
endif



#
# --- LDFLAGS cleanup ----------------------------------------------------------
#



#
# --- Include makefile fragments -----------------------------------------------
#

# Initialize our list of directory paths to makefile fragments with the empty
# list. This variable will accumulate all of the directory paths in which
# makefile fragments reside.
FRAGMENT_DIR_PATHS :=

# Initialize our makefile variables that source code files will be accumulated
# into by the makefile fragments. This initialization is very important! These
# variables will end up with weird contents if we don't initialize them to
# empty prior to recursively including the makefile fragments.
MK_CONFIG_SRC      :=
MK_KERNELS_SRC     :=
MK_REFKERN_SRC     :=
MK_FRAME_SRC       :=
MK_AOCLDTL_SRC     :=
MK_SANDBOX_SRC     :=

# -- config --

# Construct paths to each of the sub-configurations specified in the
# configuration list. Note that we use CONFIG_LIST_FAM, which already
# has CONFIG_NAME included (with duplicates removed).
CONFIG_PATHS       := $(addprefix $(CONFIG_FRAG_PATH)/, $(CONFIG_LIST_FAM))

# This variable is used by the include statements as they recursively include
# one another. For the 'config' directory, we initialize it to that directory
# in preparation to include the fragments in the configuration sub-directory.
PARENT_SRC_PATH    := $(CONFIG_PATH)
PARENT_PATH        := $(CONFIG_FRAG_PATH)

# Recursively include the makefile fragments in each of the sub-configuration
# directories.
-include $(addsuffix /$(FRAGMENT_MK), $(CONFIG_PATHS))

# -- kernels --

# Construct paths to each of the kernel sets required by the sub-configurations
# in the configuration list.
KERNEL_PATHS       := $(addprefix $(KERNELS_FRAG_PATH)/, $(KERNEL_LIST))

# This variable is used by the include statements as they recursively include
# one another. For the 'kernels' directory, we initialize it to that directory
# in preparation to include the fragments in the configuration sub-directory.
PARENT_SRC_PATH    := $(KERNELS_PATH)
PARENT_PATH        := $(KERNELS_FRAG_PATH)

# Recursively include the makefile fragments in each of the kernels sub-
# directories.
-include $(addsuffix /$(FRAGMENT_MK), $(KERNEL_PATHS))

# -- ref_kernels --
# -- frame --

# This variable is used by the include statements as they recursively include
# one another. For the framework and reference kernel source trees (ie: the
# 'frame' and 'ref_kernels' directories), we initialize it to the top-level
# directory since that is its parent.
PARENT_SRC_PATH    := $(DIST_PATH)
PARENT_PATH        := $(OBJ_DIR)/$(CONFIG_NAME)

# Recursively include all the makefile fragments in the directories for the
# reference kernels and portable framework.
-include $(addsuffix /$(FRAGMENT_MK), $(REFKERN_FRAG_PATH))
-include $(addsuffix /$(FRAGMENT_MK), $(FRAME_FRAG_PATH))
-include $(addsuffix /$(FRAGMENT_MK), $(AOCLDTL_FRAG_PATH))

# -- sandbox --

# Construct paths to each sandbox. (At present, there can be only one.)
# NOTE: If $(SANDBOX) is empty (because no sandbox was enabled at configure-
# time) then $(SANDBOX_PATHS) will also be empty, which will cause no
# fragments to be included.
SANDBOX_PATHS      := $(addprefix $(SANDBOX_FRAG_PATH)/, $(SANDBOX))

# This variable is used by the include statements as they recursively include
# one another. For the 'sandbox' directory, we initialize it to that directory
# in preparation to include the fragments in the configuration sub-directory.
PARENT_SRC_PATH    := $(SANDBOX_PATH)
PARENT_PATH        := $(SANDBOX_FRAG_PATH)

# Recursively include the makefile fragments in the sandbox sub-directory.
-include $(addsuffix /$(FRAGMENT_MK), $(SANDBOX_PATHS))

# Create a list of the makefile fragments using the variable into which each
# of the above include statements accumulated their directory paths.
MAKEFILE_FRAGMENTS := $(addsuffix /$(FRAGMENT_MK), $(FRAGMENT_DIR_PATHS))

# Detect whether we actually got any makefile fragments. If we didn't, then it
# is likely that the user has not yet generated them (via configure).
ifeq ($(strip $(MAKEFILE_FRAGMENTS)),)
MAKEFILE_FRAGMENTS_PRESENT := no
else
MAKEFILE_FRAGMENTS_PRESENT := yes
endif


#
# --- Important sets of header files and paths ---------------------------------
#

# Define a function that will expand all of the directory paths given in $(1)
# to actual filepaths using the list of suffixes provided $(2).
get-filepaths = $(strip $(foreach path, $(1), \
                            $(foreach suf, $(2), \
                                $(wildcard $(path)/*.$(suf)) \
                 )       )   )

# Define a function that will expand all of the directory paths given in $(1)
# to actual filepaths using the list of suffixes provided $(2), taking only
# the first expansion from each directory with at least one file matching
# the current suffix. Finally, strip the filenames from all resulting files,
# returning only the directory paths.
get-dirpaths  = $(dir $(foreach path, $(1), \
                          $(firstword \
                              $(foreach suf, $(2), \
                                  $(wildcard $(path)/*.$(suf)) \
                 )     )   )   )

# We'll use two directory lists. The first is a list of all of the directories
# in which makefile fragments were generated (plus the current directory). The
# second is the subset of the first that begins with the sandbox root path.
ALLFRAG_DIR_PATHS := . $(FRAGMENT_DIR_PATHS)
SANDBOX_DIR_PATHS := $(filter $(SANDBOX_PATH)/%,$(ALLFRAG_DIR_PATHS))

ALL_H99_FILES     := $(call get-filepaths,$(ALLFRAG_DIR_PATHS),$(ALL_H99_SUFS))
FRAME_H99_FILES   := $(filter-out $(SANDBOX_PATH)/%,$(ALL_H99_FILES))

ALL_H99_DIRPATHS  := $(call get-dirpaths,$(ALLFRAG_DIR_PATHS),$(ALL_H99_SUFS))

SANDBOX_H99_FILES := $(call get-filepaths,$(SANDBOX_DIR_PATHS),$(SANDBOX_H99_SUFS))
SANDBOX_HXX_FILES := $(call get-filepaths,$(SANDBOX_DIR_PATHS),$(SANDBOX_HXX_SUFS))

SANDBOX_HDR_DIRPATHS := $(call get-dirpaths,$(SANDBOX_DIR_PATHS),$(ALL_HDR_SUFS))



#
# --- blis.h header definitions ------------------------------------------------
#

# Use include/CONFIG_NAME as the default path to the local header files, but
# allow the includer to override this value if it needs to point to an
# installation directory.
ifeq ($(strip $(INC_PATH)),)
INC_PATH        := $(INCLUDE_DIR)/$(CONFIG_NAME)
endif

# Identify the base path for the intermediate include directory. We define
# this path in terms of INC_PATH, which gets a default value above (which is
# what happens for the top-level Makefile). If INC_PATH is specified by the
# Makefile prior to including common.mk, that path is used instead. This
# allows Makefiles for example code and test drivers to reference an installed
# prefix directory for situations when the build directory no longer exists.
BASE_INC_PATH   := $(INC_PATH)

# Isolate the path to blis.h by filtering the file from the list of framework
# header files.
BLIS_H          := blis.h
BLIS_H_SRC_PATH := $(filter %/$(BLIS_H), $(FRAME_H99_FILES))

# Construct the path to what will be the intermediate flattened/monolithic
# blis.h file.
BLIS_H_FLAT     := $(BASE_INC_PATH)/$(BLIS_H)


#
# --- cblas.h header definitions -----------------------------------------------
#

# Isolate the path to cblas.h by filtering the file from the list of framework
# header files.
CBLAS_H          := cblas.h
CBLAS_H_SRC_PATH := $(filter %/$(CBLAS_H), $(FRAME_H99_FILES))

# Construct the path to what will be the intermediate flattened/monolithic
# cblas.h file.
CBLAS_H_FLAT    := $(BASE_INC_PATH)/$(CBLAS_H)


#
# --- Compiler include path definitions ----------------------------------------
#

# Obtain a list of header files #included inside of the bli_cntx_ref.c file.
# Paths to these files will be needed when compiling with the monolithic
# header.
ifeq ($(strip $(SHARE_PATH)),.)
REF_KER_SRC     := $(DIST_PATH)/$(REFKERN_DIR)/bli_cntx_ref.c
REF_KER_HEADERS := $(shell $(GREP) "\#include" $(REF_KER_SRC) | sed -e "s/\#include [\"<]\([a-zA-Z0-9\_\.\/\-]*\)[\">].*/\1/g" | $(GREP) -v $(BLIS_H))
endif

# Match each header found above with the path to that header, and then strip
# leading, trailing, and internal whitespace.
REF_KER_H_PATHS := $(strip $(foreach header, $(REF_KER_HEADERS), \
                               $(dir $(filter %/$(header), \
                                              $(FRAME_H99_FILES)))))

# Add -I to each header path so we can specify our include search paths to the
# C compiler. Then add frame/include since it's needed for bli_oapi_w[o]_cntx.h.
REF_KER_I_PATHS := $(strip $(patsubst %, -I%, $(REF_KER_H_PATHS)))
REF_KER_I_PATHS += -I$(DIST_PATH)/frame/include

# Prefix the paths above with the base include path.
# NOTE: We no longer need every header path in the source tree since we
# now #include the monolithic/flattened blis.h instead.
CINCFLAGS       := -I$(BASE_INC_PATH) $(REF_KER_I_PATHS)

# Obtain a list of header paths in the configured sandbox. Then add -I to each
# header path.
CSBOXINCFLAGS   := $(strip $(patsubst %, -I%, $(SANDBOX_HDR_DIRPATHS)))


#
# --- BLIS configuration header definitions ------------------------------------
#

# This file was created by configure, but we need to define it here so we can
# remove it as part of the clean targets.
BLIS_CONFIG_H   := ./bli_config.h


#
# --- Special preprocessor macro definitions -----------------------------------
#

# Define a C preprocessor macro to communicate the current version so that it
# can be embedded into the library and queried later.
VERS_DEF       := -DBLIS_VERSION_STRING=\"$(VERSION)\"

# Define a C preprocessor flag that is *only* defined when BLIS is being
# compiled. (In other words, an application that #includes blis.h will not
# get this cpp macro.)
BUILD_CPPFLAGS := -DBLIS_IS_BUILDING_LIBRARY



# end of ifndef COMMON_MK_INCLUDED conditional block
endif

