# -*- Makefile -*-
#
# $Id: GNUmakefile.cfg 629815 2008-02-21 14:54:57Z faridz $
#
# makefile to configure the C++ Standard library
#
########################################################################
#
# Licensed to the Apache Software  Foundation (ASF) under one or more
# contributor  license agreements.  See  the NOTICE  file distributed
# with  this  work  for  additional information  regarding  copyright
# ownership.   The ASF  licenses this  file to  you under  the Apache
# License, Version  2.0 (the  "License"); you may  not use  this file
# except in  compliance with the License.   You may obtain  a copy of
# the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the  License is distributed on an  "AS IS" BASIS,
# WITHOUT  WARRANTIES OR CONDITIONS  OF ANY  KIND, either  express or
# implied.   See  the License  for  the  specific language  governing
# permissions and limitations under the License.
#
# Copyright 1999-2007 Rogue Wave Software, Inc.
#
########################################################################
#
# Creates a C++ header file, config.h, containing a list of (possibly
# commented out) preprocessor #defines in the form _RWSTD_NO_XXX,
# where the XXX is the file name part (w/o the extension) of XXX.cpp
# (some C or C++ program)
#
# Files named NO_XXX.cpp are assumed to be negative tests, which are
# considered successful unless they exit with zero status, in which
# case they are considered failed and the corresponding macro,
# _RWSTD_NO_XXX, is #defined.
#
# Each test named  XXX.lib.cpp is translated into a  static or dynamic
# library (depending  on the presence of shared  in BUILDMODE variable),
# and may  be linked into other  executables. To link a  library into an
# executable  the  name of  the  library  must  be mentioned  in  LDOPTS
# somewhere in the executable's source.
#
# XXX.cpp can but doesn't need to contain main(). If it doesn't, the
# macro _RWSTD_NO_XXX will be commented out in config.h iff XXX.cpp
# successfully compiles.
#
# If XXX.cpp contains main(), _RWSTD_NO_XXX will be commented out iff
# XXX.cpp not only successfully compiles, but also links and runs.
#
# Any output produced by the executable XXX obtained by compiling and
# linking XXX.cpp is appended to the end of config.h.
#
########################################################################

include ../makefile.in

SRCDIR       = $(TOPDIR)/etc/config/src
VPATH        = $(SRCDIR)

CPPFLAGS    += -I.
CPPFLAGS    := $(filter-out -I$(TOPDIR)/include/ansi,$(CPPFLAGS))

CXXFLAGS    += $(WARNFLAGS)

# get a sorted list of config tests, starting with any shell scripts
# with the .sh suffix, followed by C++ (.cpp) programs
SRCS        := $(sort $(notdir $(wildcard $(SRCDIR)/*.sh)))
SRCS        += $(sort $(notdir $(wildcard $(SRCDIR)/*.cpp)))
OBJS        := $(SRCS:.cpp=.o)
TARGET      := $(SRCS:.cpp=)
PWD         := $(shell pwd)
DASH_H       = -H

CCNAME       = $(CXX)-$(CCVER)
LOGFILE      = config.log

# can't use LDOPTS when working with HP aCC, it's used by the linker
ifneq ($(CXX),aCC)
    LOPT     = LDOPTS
    LDFLAGS += $(LDOPTS)
else
    LOPT     = _LDOPTS
    LDFLAGS += $(_LDOPTS)
endif

# append $(LDLIBS) last, after $(LDOPTS), since the latter may depend
# on the former being after it on the link line
LDFLAGS     += -lm $(LDLIBS)

# CXXPRELINK - command for compilers that use template
#              instantiation models that require a prelink stage
ifneq ($(PRELINKFLAGS),)
CXXPRELINK   = $(CXX) $(CPPFLAGS) $(LDFLAGS) $(PRELINKFLAGS)
endif   # ($(PRELINKFLAGS),)


# helper function to compile a file and log results
# arguments:
#   $(1): source file name
#   $(2): object file name
#   $(3): additional compiler flags (optional)
define compile
    command="$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(3) -c $(1) -o $(2)";    \
    echo "$$command" >>$(LOGFILE);                                    \
    $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(3) -c $(1) -o $(2) >>$(LOGFILE) 2>&1
endef

# helper function to link a file and log results
# arguments:
#   $(1): object file name
#   $(2): executable file name
#   $(3): additional linker flags (optional)
define link
    command="$(LD) $(1) $(LDFLAGS) $(3) -o $(2)";                     \
    echo "$$command" >>$(LOGFILE);                                    \
    $(LD) $(1) $(LDFLAGS) $(3) -o $(2) >>$(LOGFILE) 2>&1
endef

# helper function to compile and link a file and log results
# arguments:
#   $(1): source file name
#   $(2): object file name
#   $(3): executable file name
#   $(4): additional compiler flags (optional)
#   $(5): additional linker flags (optional)
define compile_then_link
    command="$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(4) -c $(1) -o $(2)"             \
            "&& $(LD) $(2) $(LDFLAGS) $(5) -o $(3)";                          \
    echo "$$command" >>$(LOGFILE);                                            \
       $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(4) -c $(1) -o $(2) >>$(LOGFILE) 2>&1  \
    && $(LD) $(2) $(LDFLAGS) $(5) -o $(3) >>$(LOGFILE) 2>&1
endef

# helper function to prelink a file and log results
# arguments:
#   $(1): source file name
#   $(2): object file name
define prelink
    command="$(CXXPRELINK) $(1) -o $(2)";      \
    echo "$$command" >>$(LOGFILE);             \
    $(CXXPRELINK) $(1) -o $(2)  >>$(LOGFILE)
endef

# helper function to create an archive out of an object file
define archive
    command="$(AR) $(ARFLAGS) $(1) $(2)";     \
    echo "$$command" >>$(LOGFILE);            \
    $(AR) $(ARFLAGS) $(1) $(2) >>$(LOGFILE)
endef

########################################################################
#  TARGETS
########################################################################

all: config.h


# recreate config.h and update its timestamp
config.h: $(SRCS)
	$(MAKE) config
	touch config.h

# (re)create config.h; do not change timestamp if the new file is the same
# make .o first to make sure the %.o: %.cpp rule gets invoked (make bug?)
# run a configure script as the last step (e.g., to remove unwanted files)

# creates a file named vars.sh, containing shell assignments corresponding
# to makefile variables defined in $(BUILDDIR)/makefile.in (variables with
# invalid names (those containing periods) are commented out); vars.sh is
# used in libc_decl.sh to invoke the compiler and linker
config: clean sane 
	@([ -f config.h ] && mv config.h config.h.~ ;                       \
          echo "// configured for $(CCNAME) on `uname -sr`" >config.h ;     \
          for file in $(TARGET) ; do                                        \
              src=$(SRCDIR)/$$file.cpp;                                     \
              desc=`head -n 1 2>/dev/null $$src | sed "s:// *::"`;          \
              [ "$$desc" != "" ] && printf "%-50.50s " "$$desc";            \
              grep "[^a-zA-Z0-9_]*main *(" $$src >/dev/null 2>&1 ;          \
              if [ $$? -eq 0 ] ; then                                       \
                  opts=`sed -n "s/^[^A-Z_a-z0-9]*LDOPTS *= *\(.*\)/\1/p"    \
                        $$src`;                                             \
                  targets="$$file.o $$file run RUN=$$file $(LOPT)=$$opts";  \
              elif [ `echo $$file | grep "\.lib"` ] ; then                  \
                  targets="$$file$(LIBSUFFIX)" ;                            \
              elif [ `echo $$file | grep "\.sh"` ] ; then                   \
                  if [ ! -f vars.sh ] ; then                                \
                      cat ../makefile.in                                    \
                      | sed -e "s/= *\([^ ][^ ]* .*\)/=\"\1\"/"             \
                            -e "s/^\( *[^=]*\.[^=]*=.*\)/# \1/"             \
                            -e "s/^\([^ ]*\) *= *\(.*\)/\1=\2 ; export \1/" \
                            -e 's/$$(\([^)]*\))/${\1}/g' >vars.sh ;         \
                  fi ;                                                      \
                  $(SRCDIR)/$$file config.h $(LOGFILE) ;                    \
                  echo ;                                                    \
                  continue ;                                                \
              else                                                          \
                  targets="$$file.o run RUN=$$file.o" ;                     \
              fi;                                                           \
              $(MAKE) $$targets OUTPUT=config.h -k >/dev/null 2>&1 ;        \
              if [ "$$desc" != "" ] ; then                                  \
                  sym="_RWSTD_" ;                                           \
                  echo $$file | grep "^NO_" >/dev/null ;                    \
                  [ $$? -ne 0 ] && sym="$${sym}NO_" ;                       \
                  str=`sed -n "s/\(.*\)\(#\)define $$sym$$file$$/\1\2/p"    \
                       config.h`;                                           \
                  if [ "$$str" = "// #" ] ; then                            \
                       echo "ok";                                           \
                  elif [ "$$str" = "#" ] ; then                             \
                      echo "no ($$sym$$file)";                              \
                  else                                                      \
                      echo "--" ;                                           \
                  fi;                                                       \
              fi;                                                           \
          done;                                                             \
          diff config.h config.h.~ >/dev/null 2>&1 ;                        \
          if [ $$? -eq 0 ] ; then                                           \
              mv config.h.~ config.h ;                                      \
              echo "config.h unchanged";                                    \
          elif [ -f config.h~ ] ; then                                      \
              echo "previous config.h saved in config.h~";                  \
          fi;                                                               \
          [ -x ./configure ] && ./configure -f ./config.h -d $(TOPDIR) ;    \
          exit 0)

# run one or more (perhaps all) executables in $(RUN) or $(TARGET) if
# the former is not defined; if an executable doesn't exist and the .o
# doesn't contain main it is still considered success (this is needed
# when the run target is invoked from within the %.o: %.cpp pattern rule
# .o's are not run (obviously) and are just assumed to be the result of
# a successful compilation
run:
	@(output=$(OUTPUT) ;                                             \
          [ "$$output" = "" ] && output=/dev/tty ;                       \
          target=$(RUN) ;                                                \
          [ "$$target" = "" ] && target=$(TARGET) ;                      \
          for file in $$target ; do                                      \
              symbol="#define _RWSTD_" ;                                 \
              basename $$file | grep "^NO_" >/dev/null 2>&1 ;            \
              neg=$$? ;                                                  \
              [ $$neg -ne 0 ] && symbol="$${symbol}NO_" ;                \
              symbol="$${symbol}`echo $$file                             \
                      | sed -e 's:.*/::' -e 's/\.o//'`";                 \
              if [ `echo $$file | grep "\.o"` ] ; then                   \
                  test -r $$file ;                                       \
              elif [ ! -x $$file ] ; then                                \
                  nm -gp $$file.o 2>&1 | grep "T *main *$$" >/dev/null ; \
                  test -f $$file.o -a ! $$? -eq 0 ;                      \
              else                                                       \
                  echo "./$$file" >>$(LOGFILE) ;                         \
                  LD_LIBRARY_PATH=$$LD_LIBRARY_PATH:. ;                  \
                  LIBPATH=$$LIBPATH:. ;                                  \
                  export LIBPATH LD_LIBRARY_PATH ;                       \
                  text=`./$$file` ;                                      \
              fi;                                                        \
              res=$$? ;                                                  \
                 [ $$res -eq 0 -a $$neg -ne 0 ]                          \
              || [ $$res -ne 0 -a $$neg -eq 0 ] && symbol="// $$symbol" ;\
              grep "$$symbol$$" config.h ;                               \
              if [ $$? -ne 0 ] ; then                                    \
                  echo "$$symbol" >>$$output;                            \
                  if [ "$$text" != "" ]; then                            \
                      printf "%s\n" "$$text" >>$$output;                 \
                  fi;                                                    \
              fi;                                                        \
          done;                                                          \
          exit 0)

# determine the type of architecture (LP32, ILP32, ILP64, LLP64, or LP64)
arch:
	@(output=$(OUTPUT);                                               \
          [ "$$output" = "" ] && output=/dev/tty ;                        \
	  for type in int long "long long" "void*" ; do                   \
              echo "int main () { return sizeof ($$type); }"              \
                   | tee a.cpp >>$(LOGFILE) ;                             \
              $(call compile_then_link,a.cpp,a.o,a.out);                  \
              if [ $$? -eq 0 ] ; then                                     \
                  size="$$size`./a.out ; echo $$?`" ;                     \
              else                                                        \
                  size="$${size}-";                                       \
              fi;                                                         \
          done;                                                           \
          case "$$size" in                                                \
              24?4 ) arch="LP32" ;;  44?4 ) arch="ILP32" ;;               \
              88?8 ) arch="ILP64" ;; 4488 ) arch="LLP64" ;;               \
              48?8 ) arch="LP64" ;;  *    ) arch="$$size" ;;              \
          esac ;                                                          \
          echo "int main () { int i = 1; return *(char*)&i; }"            \
               | tee a.cpp >>$(LOGFILE) ;                                 \
          $(call compile_then_link,a.cpp,a.o,a.out);                      \
          if [ $$? -eq 0 ] ; then                                         \
              endian=" little endian" ;                                   \
              ./a.out ;                                                   \
              [ $$? -eq 0 ] && endian=" big endian" ;                     \
          else                                                            \
              echo "error";                                               \
              cat $(LOGFILE);                                             \
              exit 1;                                                     \
          fi;                                                             \
          echo "$$arch$$endian" >$$output ;                               \
         )

# check compiler, linker, and run environment's sanity, determine
# system (or compiler) architecture (word size, address size, etc.)
sane:
	@(echo;                                                             \
          echo "configuring stdcxx $(LIBVER) for $(CCNAME) on $(PLATFORM)"; \
          echo;                                                             \
          rm -f a.out ;                                                     \
          echo "int main () { return 0; }" | tee a.cpp >>$(LOGFILE) ;       \
          printf "%-50.50s " "checking if the compiler is sane";            \
          $(call compile,a.cpp,a.o);                                        \
          if [ $$? -eq 0 ] ; then                                           \
              echo "ok (invoked with $(CXX))";                              \
          else                                                              \
              echo "no"; echo;                                              \
              cat $(LOGFILE) ;                                              \
              exit 1;                                                       \
          fi;                                                               \
          printf "%-50.50s " "checking if the linker is sane";              \
          $(call link,a.o,a.out);                                           \
          if [ $$? -eq 0 ] ; then                                           \
              echo "ok (invoked with $(LD))";                               \
          else                                                              \
              echo "no"; echo;                                              \
              cat $(LOGFILE) ;                                              \
              exit 1;                                                       \
          fi;                                                               \
          printf "%-50.50s " "checking system architecture";                \
          `$(MAKE) arch OUTPUT=a.cpp>/dev/null 2>&1` ;                      \
          cat a.cpp ;                                                       \
          rm a.cpp )

clean:
	@rm -f a.out core *.o *.i *.ii *.ti vars.sh \
               *$(LIBSUFFIX) $(TARGET)

realclean: clean
	rm -f *.d *.o *.a *.so a.cpp

listtarget:
	@echo $(TARGET)


.PHONY: all clean config configure c_headers listtarget realclean run


########################################################################
#  COMMON RULES
########################################################################

#empty rule for .cc files so they won't be separately compiled
%: %.cc ;

# compile .cpp so that any macros used by the translation unit are configured
# checks config.h to make sure macro isn't yet defined before going recursive
# *.lib.cpp files a compiled (and linked) into libraries (shared or static)
# with LDOPTS reset to the empty string
%.o: %.cpp
	@(dep=`egrep "^ *# *if[n]*def  *_RWSTD_" $<                    \
               | sed -e "s/.*# *if[n]*def *\([^ ]*\) */\1/g"           \
                     -e "s/_RWSTD_NO_//g" -e "s/_RWSTD_//g"` ;         \
          for sym in $$dep ; do                                        \
              fname=$$sym ;                                            \
              src=$(SRCDIR)/$$fname.cpp;                               \
              [ ! -r $$src ] && fname="NO_$$fname" ;                   \
              grep "_RWSTD_NO_$$sym$$" config.h >/dev/null ;           \
              if [ $$? -ne 0 ] ; then                                  \
                  grep "[^a-zA-Z0-9_]*main *(" $$src >/dev/null 2>&1 ; \
                  if [ $$? -eq 0 ] ; then                              \
                      opts=`sed -n                                     \
                            "s/^[^A-Z_a-z0-9]*LDOPTS *= *\(.*\)/\1/p"  \
                            $$src`;                                    \
                      targets="$$fname.o $$fname run RUN=$$fname       \
                              $(LOPT)=$$opts" ;                        \
                  elif [ `echo $$fname | grep "\.lib"` ] ; then        \
                      targets="$$fname$(LIBSUFFIX) $(LOPT)=" ;         \
                  else                                                 \
                      targets="$$fname.o run RUN=$$fname.o" ;          \
                  fi;                                                  \
                  $(MAKE) $$targets OUTPUT=config.h -k ;               \
              fi;                                                      \
          done;                                                        \
          true)
	$(call compile,$<,$@)

# remove .o to prevent the run target from getting confused
%: %.o
	$(call link,$^,$@); rm $<

# build a library from any source file named *.lib.cpp
%.lib$(LIBSUFFIX): %.lib.cpp
ifeq ($(findstring shared,$(BUILDMODE)),shared)
	$(call compile,$<,$@.o,$(PICFLAGS))
  ifeq ($(findstring xlC,$(CXX)),xlC)
#       IBM xlC first "preprocesses" .o's with -qmkshrobj
#       and then uses `ar' to create a shared library...
	$(call prelink,$@.o,@.lib.o)
	$(call archive,$@,$@.lib.o)
  else
	$(call link,$@.o,$@,$(LDSOFLAGS))
  endif
else
	$(call compile,$<,$@.o)
	$(call archive,$@,$@.o)
endif


# parallelization of the configuration infrastructure not supported
.NOTPARALLEL:
