#
# This python script is called by configure to generate the makefile
# for zn_poly.
#
# Patched for Sage's 'spkg-install' to
# - respect the environment settings of CC, CPP, CXX, AR and RANLIB, and use
#   these as well as CFLAGS, CPPFLAGS, CXXFLAGS and LDFLAGS with their usual
#   meaning (i.e., CXX and not CPP for the C++ compiler, likewise CXXFLAGS
#   instead of CPPFLAGS, CPPFLAGS for C preprocessor flags, and LDFLAGS in
#   every link command using the compiler driver);
# - support passing CPPFLAGS and CXXFLAGS (via '--cppflags=...' and
#   '--cxxflags=...');
# - build a 64-bit shared library (.dylib) on Darwin / MacOS X by adding the
#   target 'libzn_poly.dylib64'.  (This is meanwhile superfluous, since
#   LDFLAGS are used in the receipt for libzn_poly.dylib as well.)
# - support a variable SONAME_FLAG (defaulting to '-soname', otherwise taken
#   from the environment (e.g. '-h' for the Sun linker);
# - support a variable SHARED_FLAG (defaulting to '-shared'), which could
#   later be used to unify the .so and .dylib targets.  (An SO_EXTENSION
#   variable isn't supported / used yet.)

with open('VERSION') as fobj:
    version = fobj.read().strip()

with open('ABI_VERSION') as fobj:
    abi_version = fobj.read().strip()


# --------------------------------------------------------------------------
# various lists of modules


# These are the modules that go into the actual zn_poly library. They get
# compiled in both optimised and debug modes.
lib_modules = ["array", "invert", "ks_support", "mulmid", "mulmid_ks", "misc",
               "mpn_mulmid", "mul", "mul_fft", "mul_fft_dft", "mul_ks",
               "nuss", "pack", "pmf", "pmfvec_fft", "tuning", "zn_mod"]
lib_modules = ["src/" + x for x in lib_modules]

# These are modules containing various test routines. They get compiled
# in debug mode only.
test_modules = ["test", "ref_mul", "invert-test", "pmfvec_fft-test",
                "mulmid_ks-test", "mpn_mulmid-test", "mul_fft-test",
                "mul_ks-test", "nuss-test", "pack-test"]
test_modules = ["test/" + x for x in test_modules]

# These are modules containing various profiling routines. They get compiled
# in optimised mode only.
prof_modules = ["prof_main", "profiler", "array-profile", "invert-profile",
                "mulmid-profile", "mpn_mulmid-profile", "mul-profile",
                "negamul-profile"]
prof_modules = ["profile/" + x for x in prof_modules]

# These are modules containing profiling routines for NTL. They get compiled
# in optimised mode only, with the C++ compiler.
cpp_prof_modules = ["profile/ntl-profile"]

# These are modules containing dummy routines replacing the NTL ones, if
# we're not compiling with NTL support.
noncpp_prof_modules = ["profile/ntl-profile-dummy"]

# These are modules shared by the test and profiling code. They get compiled
# in both debug and optimised mode.
testprof_modules = ["test/support"]

# Profiling targets. Each X has a main file "X-main.c" which is linked
# against prof_main.c. They are compiled once with PROFILE_NTL defined
# and once without.
prof_progs = ["array-profile", "invert-profile", "mulmid-profile",
              "mpn_mulmid-profile", "mul-profile", "negamul-profile"]
prof_progs = ["profile/" + x for x in prof_progs]

# These are modules used in the tuning program; they get compiled only in
# optimised mode.
tune_modules = ["tune", "mulmid-tune", "mpn_mulmid-tune", "mul-tune",
                "mul_ks-tune", "mulmid_ks-tune", "nuss-tune", "tuning"]
tune_modules = ["tune/" + x for x in tune_modules]

# Demo programs.
demo_progs = ["demo/bernoulli/bernoulli"]

# These are the headers that need to be copied to the install location.
install_headers = ["include/zn_poly.h", "include/wide_arith.h"]

# These are the other headers.
other_headers = ["include/support.h", "include/profiler.h",
                 "include/zn_poly_internal.h"]


# --------------------------------------------------------------------------
# read command line options

from optparse import OptionParser

parser = OptionParser()
parser.add_option("--prefix", dest="prefix", default="/usr/local")
parser.add_option("--cflags", dest="cflags", default="-O2")
parser.add_option("--cppflags", dest="cppflags", default="")
parser.add_option("--cxxflags", dest="cxxflags", default="-O2")
parser.add_option("--ldflags", dest="ldflags", default="")
parser.add_option("--gmp-prefix", dest="gmp_prefix", default="/usr/local")
parser.add_option("--ntl-prefix", dest="ntl_prefix", default="/usr/local")
parser.add_option("--use-flint", dest="use_flint", action="store_true",
                  default=False)
parser.add_option("--flint-prefix", dest="flint_prefix", default="/usr/local")
parser.add_option("--enable-tuning", dest="tuning", action="store_true",
                  default=True)
parser.add_option("--disable-tuning", dest="tuning", action="store_false")

options, args = parser.parse_args()


gmp_include_dir = options.gmp_prefix + "/include"
gmp_lib_dir = options.gmp_prefix + "/lib"

ntl_include_dir = options.ntl_prefix + "/include"
ntl_lib_dir = options.ntl_prefix + "/lib"

if options.use_flint:
   flint_include_dir = options.flint_prefix + "/include"
   flint_lib_dir = options.flint_prefix + "/lib"

cflags = options.cflags
cppflags = options.cppflags # C preprocessor flags
cxxflags = options.cxxflags # C++ compiler flags
ldflags = options.ldflags
prefix = options.prefix
zn_poly_tuning = options.tuning

# Note: This should be put into / added to cppflags:
includes = "-I" + gmp_include_dir + " -I./include"
# Note: This should be put into / added to ldflags:
libs = "-L" + gmp_lib_dir + " -lgmp -lm"

if options.use_flint:
   includes = includes + " -I" + flint_include_dir
   libs = libs + " -L" + flint_lib_dir + " -lflint"
   cflags = cflags + " -std=c99 -DZNP_USE_FLINT"

# Note: These also belong to CPPFLAGS and LDFLAGS, respectively:
# (But we currently don't use NTL in Sage's zn_poly installation anyway.)
cpp_includes = includes + " -I" + ntl_include_dir
cpp_libs = libs + " -L" + ntl_lib_dir + " -lntl"

# --------------------------------------------------------------------------
# generate the makefile

import time

now = time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime())
print(
"""#
# Do not edit directly -- this file was auto-generated
# by {0} on {1}
#
# (makemakefile.py patched for Sage, 04/2012)
""".format(__file__, now))


print(
"""
CC ?= gcc
CPP ?= cpp
CFLAGS = {0}
CPPFLAGS = {1}
LDFLAGS = {2}
INCLUDES = {3}  # These are options to the C preprocessor.
LIBS = {4}  # These are linker options passed to the compiler.

AR ?= ar
RANLIB ?= ranlib

SHARED_FLAG ?= -shared
SONAME_FLAG ?= -soname  # '-h' for the Sun/Solaris linker
""".format(cflags, cppflags, ldflags, includes, libs))


print(
"""CXX ?= g++  # The C++ compiler.
CXXFLAGS = {0}  # Options passed to the C++ compiler.
CPP_INCLUDES = {1}
CPP_LIBS = {2}
""".format(cxxflags, cpp_includes, cpp_libs))


print(
"""HEADERS = {0}
LIBOBJS = {1}
TESTOBJS = {2}
PROFOBJS = {3}
CPP_PROFOBJS = {4}
TUNEOBJS = {5}""".format(
    " ".join(install_headers + other_headers),
    " ".join([x + ".o" for x in lib_modules]),
    " ".join([x + "-DEBUG.o" for x in
              lib_modules + test_modules + testprof_modules]),
    " ".join([x + ".o" for x in
              lib_modules + prof_modules + noncpp_prof_modules + testprof_modules]),
    " ".join([x + ".o" for x in
              lib_modules + prof_modules + cpp_prof_modules + testprof_modules]),
    " ".join([x + ".o" for x in
              lib_modules + tune_modules + testprof_modules + prof_modules +
              noncpp_prof_modules if x not in ("src/tuning", "profile/prof_main")])
))


print(
"""ZN_POLY_TUNING = {0}
ZN_POLY_VERSION = {1}
ZN_POLY_ABI_VERSION = {2}
""".format(int(zn_poly_tuning), version, abi_version))


print(
"""all: libzn_poly.a

test: test/test
tune: tune/tune

check: test
\ttest/test -quick all

install:
\tmkdir -p {prefix}/include/zn_poly
\tmkdir -p {prefix}/lib
\tcp libzn_poly.a {prefix}/lib
\tcp include/zn_poly.h {prefix}/include/zn_poly
\tcp include/wide_arith.h {prefix}/include/zn_poly
""".format(prefix=prefix))


print(
"""clean:
\trm -f *.o
\trm -f test/*.o
\trm -f profile/*.o
\trm -f tune/*.o
\trm -f src/tuning.c
\trm -f src/*.o
\trm -f demo/bernoulli/*.o
\trm -f libzn_poly.a
\trm -f libzn_poly.dylib
\trm -f libzn_poly*.so*
\trm -f libzn_poly*.dll.a
\trm -f cygzn_poly.dll
\trm -f test/test
\trm -f tune/tune""")
for x in prof_progs:
   print("\trm -f " + x)
   print("\trm -f " + x + "-ntl")
for x in demo_progs:
   print("\trm -f " + x)
print(
"""
distclean: clean
\trm -f makefile

dist: distclean
\ttar --exclude-vcs --exclude=.gitignore -czf zn_poly-$(ZN_POLY_VERSION).tar.gz *


##### library targets

ifeq ($(ZN_POLY_TUNING), 1)
src/tuning.c: tune/tune
\ttune/tune > src/tuning.c
else
src/tuning.c: tune/tuning.c
\tcp tune/tuning.c src/tuning.c
endif

libzn_poly.a: $(LIBOBJS)
\t$(AR) -r libzn_poly.a $(LIBOBJS)
\t$(RANLIB) libzn_poly.a

# TODO: Put '-single_module -fPIC -dynamiclib' into $(SHARED_FLAG)
#       and use that; also support $(SO_EXTENSION)...
libzn_poly.dylib: $(LIBOBJS)
\t$(CC) $(LDFLAGS) -single_module -fPIC -dynamiclib -o libzn_poly.dylib $(LIBOBJS) $(LIBS)

# Left for compatibility with previous versions of Sage's 'spkg-install':
libzn_poly.dylib64: $(LIBOBJS)
\t$(CC) -m64 -single_module -fPIC -dynamiclib -o libzn_poly.dylib $(LIBOBJS) $(LIBS)

cygzn_poly.dll: $(LIBOBJS)
\t$(CC) $(SHARED_FLAG) $(LDFLAGS) -Wl,--out-implib,libzn_poly-$(ZN_POLY_VERSION).dll.a -o cygzn_poly.dll $(LIBOBJS) $(LIBS)

libzn_poly-$(ZN_POLY_VERSION).dll.a: cygzn_poly.dll

libzn_poly.dll.a: libzn_poly-$(ZN_POLY_VERSION).dll.a
\tln -sf libzn_poly-$(ZN_POLY_VERSION).dll.a libzn_poly.dll.a
\tln -sf libzn_poly-$(ZN_POLY_VERSION).dll.a libzn_poly-$(ZN_POLY_ABI_VERSION).dll.a

libzn_poly.so: libzn_poly-$(ZN_POLY_VERSION).so
\tln -sf libzn_poly-$(ZN_POLY_VERSION).so libzn_poly.so
\tln -sf libzn_poly-$(ZN_POLY_VERSION).so libzn_poly-$(ZN_POLY_ABI_VERSION).so

libzn_poly-$(ZN_POLY_VERSION).so: $(LIBOBJS)
\t$(CC) $(SHARED_FLAG) $(LDFLAGS) -Wl,-soname,libzn_poly-$(ZN_POLY_ABI_VERSION).so -o libzn_poly-$(ZN_POLY_VERSION).so $(LIBOBJS) $(LIBS)


##### test program

test/test: $(TESTOBJS) $(HEADERS)
\t$(CC) -g $(LDFLAGS) -o test/test $(TESTOBJS) $(LIBS)


##### profiling programs
""")

for x in prof_progs:
    print(
"""{0}-main.o: {0}-main.c $(HEADERS)
\t$(CC) $(CFLAGS) $(CPPFLAGS) $(INCLUDES) -DNDEBUG -o {0}-main.o -c {0}-main.c

{0}: {0}-main.o $(PROFOBJS)
\t$(CC) $(CFLAGS) $(LDFLAGS) -o {0} {0}-main.o $(PROFOBJS) $(LIBS)

{0}-main-ntl.o: {0}-main.c $(HEADERS)
\t$(CC) $(CFLAGS) $(CPPFLAGS) $(INCLUDES) -DPROFILE_NTL -DNDEBUG -o {0}-main-ntl.o -c {0}-main.c

{0}-ntl: {0}-main-ntl.o $(CPP_PROFOBJS)
\t$(CXX) $(CXXFLAGS) $(LDFLAGS) -o {0}-ntl {0}-main-ntl.o $(CPP_PROFOBJS) $(CPP_LIBS)
""".format(x))


print(
"""

##### tuning utility

tune/tune: $(TUNEOBJS)
\t$(CC) $(CFLAGS) $(LDFLAGS) -o tune/tune $(TUNEOBJS) $(LIBS)


##### demo programs
""")
for x in demo_progs:
    print(
"""{0}: {0}.o $(LIBOBJS)
\t$(CC) $(CFLAGS) $(LDFLAGS) -o {0} {0}.o $(LIBOBJS) $(LIBS)
""".format(x))


print("\n##### object files (with debug code)\n")
for x in lib_modules + test_modules + testprof_modules + demo_progs:
   print(
"""{0}-DEBUG.o: {0}.c $(HEADERS)
\t$(CC) -g $(CFLAGS) $(CPPFLAGS) $(INCLUDES) -DDEBUG -o {0}-DEBUG.o -c {0}.c
""".format(x))


print("\n##### object files (no debug code)\n")
for x in (lib_modules + prof_modules + testprof_modules +
          tune_modules + demo_progs):
   print(
"""{0}.o: {0}.c $(HEADERS)
\t$(CC) $(CFLAGS) $(CPPFLAGS) $(INCLUDES) -DNDEBUG -o {0}.o -c {0}.c
""".format(x))


print("\n##### object files (C++, no debug code)\n")
for x in cpp_prof_modules:
   print(
"""{0}.o: {0}.c $(HEADERS)
\t$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(CPP_INCLUDES) -DNDEBUG -o {0}.o -c {0}.c
""".format(x))
