set                     ports_ignore_different "yes"

PortSystem              1.0
PortGroup               muniversal 1.0

categories              math
license                 BSD
name                    atlas
version                 3.10.2
revision                2
use_bzip2               yes

# additional versions
set lapackversion       3.6.0
set lapackname          lapack
set atlasdist           ${name}${version}${extract.suffix}
set lapackdist          ${lapackname}-${lapackversion}.tgz

maintainers             {vince @Veence}
platforms               {darwin < 20}

description             Portable optimal linear algebra software
long_description        Provides a complete BLAS and LAPACK API.\nATLAS\
                        achieves performance on par\
                        with machine-specific tuned libraries.

# No precompiled binaries
# archive_sites

homepage            http://math-atlas.sourceforge.net/

master_sites        sourceforge:project/math-atlas/Stable/${version}:atlas \
                    http://www.netlib.org/lapack:lapack

distfiles           ${atlasdist}:atlas \
                    ${lapackdist}:lapack

extract.only        ${atlasdist}

patchfiles          patch-archinfo_x86_c.diff \
                    patch-emit_mm_c.diff \
                    patch-makes-Make.lib.diff

checksums           atlas${version}.tar.bz2 \
                    rmd160  af4099e8bb65f5d771618ca1580f5bc8210f83fe \
                    sha256  3aab139b118bf3fcdb4956fbd71676158d713ab0d3bccb2ae1dc3769db22102f \
                    lapack-${lapackversion}.tgz \
                    rmd160  a92d0d869e42cf5835039b043e613e323b069f04 \
                    sha256  a9a0082c918fe14e377bbd570057616768dca76cbdc713457d8199aaa233ffc3

worksrcdir          ATLAS

variant gcc5        conflicts   gcc49 mpclang37 perf\
                    description {build using macports-gcc-5} {

    configure.compiler      macports-gcc-5
}

if {[variant_isset gcc5]} {

    set gcc_version 5
    set use_clang   ""

}

variant gcc49       conflicts   gcc5 mpclang37 perf\
                    description {build using macports-gcc-4.9} {

    configure.compiler      macports-gcc-4.9
}

if {[variant_isset gcc49]} {

    set gcc_version 49
    set use_clang   ""

}

variant mpclang37   conflicts   gcc49 gcc5 perf\
                    description {use mp-clang-3.7 and gfortran} { 

    configure.compiler      macports-clang-3.7
}

if {[variant_isset mpclang37]} {

    set use_clang   "37"
    set gcc_version 0
}

# Disabled until further notice
#variant perf conflicts gcc47 gcc48 gcc49 gcc5 mpclang34 clang \
#             description {Uses clang-mp-3.4 for C and dragonegg-3.4 for Fortran} {
#
#    if {[vercmp ${xcodeversion} 5.0] < 0} {
#        configure.compiler      macports-clang-3.4
#    } else {
#        set use_clang           "Xcode"
#        set gcc_version         0
#    }
#
#    depends_build-append    port:dragonegg-3.4-gcc-4.8
#    configure.f77           ${prefix}/bin/dragonegg-3.4-gfortran-mp-4.8
#
#    set clang_flags         "-O3 -fPIC -fomit-frame-pointer" 
#    set gcc_flags           "${clang_flags} \
#                            -specs=${prefix}/libexec/dragonegg/llvm34gcc48-integrated-as.specs"
#}
#
#if {[variant_isset perf]} {
#
#    set use_clang   "34"
#    set gcc_version 48
#}

variant nofortran       description {Forgo use of fortran compiler} {
}

# AVX or higher?

if {(${os.major} > 11 && \
        (![catch {sysctl hw.optional.fma} result1] && $result1 == 1)) ||
        (![catch {sysctl hw.cpufamily} result2] && $result2 == 280134364)} {

    set has_fma "yes"
} else {

    set has_fma "no"
}

if {(${os.major} > 11 && \
        (![catch {sysctl hw.optional.avx2_0} result1] && $result1 == 1)) ||
        (![catch {sysctl hw.cpufamily} result2] && $result2 == 280134364)} {

    set has_avx2 "yes"
} else {

    set has_avx2 "no"
}

if {(${os.major} > 10 && \
        (![catch {sysctl hw.optional.avx1_0} result1] && $result1 == 1)) ||
        (![catch {sysctl hw.cpufamily} result2] && $result2 == 1418770316)} {

    set has_avx "yes"
} else {

    set has_avx "no"
}

# If none of the above variants is chosen, make gcc47 the default for
# up to SSE4.2 (Core i1) capable processors, and Xcode-clang for newer

if {![variant_isset gcc49] && ![variant_isset gcc5] && ![variant_isset mpclang37]} {

    if {${has_avx} == "no"} {
    
        # Defaults to gcc47
        default_variants    +gcc5
        set gcc_version     5
        set use_clang       ""
    } else {

        # Use an appropriate version of Clang
        set gcc_version     0    
        if {[vercmp ${xcodeversion} 6.0] >= 0} {
        # With Xcode 5.0+, defaults to Xcode-clang
            set use_clang       "Xcode"
        } else {
            # Otherwise use Macports clang 3.7
            default_variants    +mpclang37
            set use_clang       "37"
        }
    }
}

if {![info exists universal_possible]} {
    set universal_possible [expr {${os.universal_supported} && [llength ${configure.universal_archs}] >= 2}]
}
set universal       [expr {${universal_possible} && [variant_isset universal]}]

# Finds the type of CPU we are running on
set native_arch ${build_arch}

if {${os.arch} eq "i386"} {

    set universal_archs_supported {i386 x86_64}
    if {![catch {exec sysctl -n hw.machine} result]} {
        set native_arch ${result}
    }
} elseif {${os.arch} eq "powerpc"} {

    set universal_archs_supported {ppc  ppc64}
    if {![catch {exec sysctl -n hw.cpusubtype} result] && $result == 100} {
        set native_arch "ppc64"
    } else {
    
        set native_arch "ppc"
    }
}

configure.args \
    --with-netlib-lapack-tarfile=${distpath}/${lapackdist} \
    -O 12 \
    -v 5 \
    --dylibs

# Chose the right flags for GCC and Clang compilers

if {${build_arch} eq "i386" || ${build_arch} eq "x86_64"} {

    # General flags
    # GCC 
    set gcc_flags   "-fomit-frame-pointer -mfpmath=sse -O3\
                     -fno-schedule-insns2 -fPIC"

    # Clang 
    set clang_flags "-O3 -fomit-frame-pointer -fPIC"

    # Vectorizing unit flags
    if {${has_fma} == "yes"} {
    
        # Only Clang handles AVX+ correctly
        set gvct_flag   "-msse4.2"
        # downgrade FMA to AVX for the time being
        set cvct_flag   "-mfma"
        if {${use_clang} != ""} {

            configure.args-append   -A 28 -V 456
        } else {

            # No AVX when using GCC 4.x
            configure.args-append   -A 26 -V 448
        }
    } elseif  {${has_avx2} == "yes"} {
    
        set gvct_flag   "-msse4.2"
        set cvct_flag   "-mavx"
    } elseif {${has_avx} == "yes"} {

        set gvct_flag   "-msse4.2"
        set cvct_flag   "-mavx"
    } elseif {${os.major} > 9 && \
            ![catch {sysctl hw.optional.sse4_2} result1] && $result1 == 1} {

        set gvct_flag   "-msse4.2"
        set cvct_flag   "-msse4.2"
    } elseif {${os.major} > 9 && \
            ![catch {sysctl hw.optional.sse4_1} result1] && $result1 == 1} {

        set gvct_flag   "-msse4.1"
        set cvct_flag   "-msse4.1"
    } elseif {${os.major} > 8 && \
    ![catch {sysctl hw.optional.supplementalsse3} result1] && $result1 == 1} {

        set gvct_flag   "-mssse3"
        set cvct_flag   "-mssse3"
    } else {

        # Default vectorization flag is sse3 (CoreSolo / CoreDuo)
        set gvct_flag   "-msse3"
        set cvct_flag   "-msse3"
    }
} elseif {${build_arch} eq "ppc"} {

    # PPC
    set gcc_flags           "-O3 -mcpu=7400 -mtune=7400\
                            -fno-schedule-insns -fschedule-insns2 -fPIC \
                            -force_cpusubtype_ALL"
    set clang_flags         "-O3 -arch ppc -fPIC"

    set gvct_flag           "-maltivec -mabi=altivec"
    set cvct_flag           "-maltivec"
    
    configure.args-append   -A 4

    # On Leopard, we may warn the user that ppc 
    # is not as efficient as ppc64
    pre-fetch {
        if {${native_arch} eq "ppc64"} {

                ui_msg "Compiling for ppc on a G5 machine.\
                        This could lead to reduced performance."
        }
    }

} else {

    # PPC64
    set gcc_flags           "-mcpu=970 -mtune=970 -mvrsave -mpowerpc64\
                            -fno-schedule-insns -fschedule-insns2 -fPIC \
                            -force_cpusubtype_ALL"
    # Clang has no PPC64 target so compile for PPC
    set clang_flags         "-O3 -arch ppc -fPIC"

    set gvct_flag           "-maltivec -mabi=altivec"
    set cvct_flag           "-maltivec"
}

# Basic universal config
# Advanced config (flags) is done during pre-configure phase

if {${universal} == 1} {

    configure.universal_args-delete         --disable-dependency-tracking

    lappend merger_configure_args(ppc)      --cflags=-m32 \
                                            -b 32 \
                                            -A 4 \
                                            -V 4 \
                                            -s gas_ppc \
                                            -Fa alg -force_cpusubtype_ALL

    lappend merger_configure_args(ppc64)    --cflags=-m64 \
                                            -b 64 \
                                            -A 5 \
                                            -V 4 \
                                            -s gas_ppc \
                                            -Fa alg -force_cpusubtype_ALL

    lappend merger_configure_args(i386)     --cflags=-m32 \
                                            -b 32 \
                                            -A 22 \
                                            -V 448 \
                                            -t 2 \
                                            -s gas_x86_32

    lappend merger_configure_args(x86_64)   --cflags=-m64 \
                                            -b 64 \
                                            -s gas_x86_64
}

# Threading
# If we run on a mono-processor machine (PPC), then disable threading

if {![catch {sysctl hw.logicalcpu} result] && $result == 1} {

    configure.args-append    -t 0
    set no_threads  1
} else {

    set no_threads  0

    # Threading relies on OpenMP if gcc compilers are selected
    # Does not seem to work reliably. Disabled.
    #if {${gcc_version} != 0} {

        configure.args-append   -Si omp 0
    #}
}

# Phases
# Work around some bugs in the 3.10.1 tarball:
# https://code.google.com/p/libarchive/issues/detail?id=299
# https://sourceforge.net/p/math-atlas/support-requests/884

extract.post_args-replace   -xf -xkf
extract.post_args-append    {|| true}

post-patch {
    if {${build_arch} eq "ppc" || ${build_arch} eq "ppc64"} {
    
        reinplace \
        "s|cMVFLAGS=\\\\\"%s\\\\\"|cMVFLAGS=\\\\\"-force_cpusubtype_ALL\ %s\\\\\"|" \
         ${worksrcpath}/include/atlas_mvtesttime.h
    }

    if {${no_threads} == 1} {
        exec sed -i bak -E "/.*threads.*/d" ${worksrcpath}/Make.top
    }
}

# Change the default compilers
pre-configure {

    # Inform user of selected C compiler
    ui_msg "Selected C compiler: ${configure.cc}"

    # Fortran stuff
    set fortran [expr ! [variant_isset nofortran]]

    # If we use clang, we must grasp a gfortran compiler to build
    # Fortran sources (do not impact overall Atlas performance)

    if {${use_clang} != "" && ${fortran} == 1} {

#        if {![variant_isset perf]} {
            set gflist  [glob -noc ${prefix}/bin/gfortran-mp-*]
            if {[llength ${gflist}] == 0} {

                # No fortran compiler found, bail out
                return -code error "Atlas could not detect any fortran compiler.\
                                    If you really don’t need the fortran interface\
                                    to be built, please use the +nofortran option,\
                                    else install a fortran compiler (e.g. gcc4X)\
                                    before building Atlas."
            } else {

                # Select most recent gfortran
                global gcc_version

                configure.f77   [lindex [lsort -dec ${gflist}] 0]
                set gcc_version   \
                [string replace ${configure.f77} 0 [string length ${prefix}]+[string length bin/gfortran-mp-]]
                set gcc_version [string map {. ""} $gcc_version]
            }
#        } else {
#            set libgccpath                  ${prefix}/lib/gcc48
#        }
    }

    if {${fortran} == 1} {

        # Output the F77 selected compiler
        if {[variant_isset perf]} {
            ui_msg "Using dragonegg-3.4-gfortran-mp-4.8 as Fortran compiler."
        } else {
            ui_msg "Selected F77 compiler: gfortran${gcc_version}"
        }
    } else {

        # No Fortran
        configure.args-append   --nof77
    }
    
    # Now set the flags according to the compiler family chosen
    # Also set “width” of the processor (32 or 64-bit)

    if {${gcc_version} != 0} {
        set libgccpath                  ${prefix}/lib/gcc${gcc_version}
    }

    # Some tweaks on the original source before universal build 
    # duplicates it
    # Overrides goodgcc
    reinplace -E "s|GOODGCC =.*|GOODGCC = ${configure.cc}\");|" \
        ${worksrcpath}/CONFIG/src/SpewMakeInc.c

    # Some further tweaks
    reinplace    "s|cc=gcc|cc=${configure.cc}|g" \
                    ${worksrcpath}/configure
    reinplace    "s|-no-cpp-precomp||g" \
                    ${worksrcpath}/CONFIG/src/atlcomp.txt
                
    # Non-universal builds
    if {${universal} == 0} {

        configure.args-append   -C acg ${configure.cc}
        
        # If we build for the CPU of the machine, use
        # machine arch tuning (except on ppc)
        # -march=natives supersedes -msse4.2 so disable it on AVX
        # capable machines
        if {${has_avx} == "no" && ${build_arch} ne "ppc"} {

            append gcc_flags    " -march=native"
        }
        append clang_flags  " -march=native"
        
        # AVX stuff. GCC compilers cannot use AVX instruction set because the
        # system as(1) is outdated
        # Clang 3.7 is fine

        if {${has_avx} == "yes" && ${use_clang} == ""} {

            # Force Core i1 arch for ASM kernels
            ui_warn "GCC compilers on MacOS do not support AVX:\
                    downgrading."
            configure.args-append   -A 25
        }
        
        if {${build_arch} eq "ppc"} {
        
            # ppc uses altivec vectorizing unit
            configure.args-append       -V 4
        }

        if {${use_clang} == ""} {

            # GCC
            configure.args-append       -F acg "'${gcc_flags} ${gvct_flag}'"

            if {${fortran} == 1} {

                configure.args-append   -C if ${configure.f77}
                configure.args-append   -F if "'${gcc_flags} ${gvct_flag}'"
                configure.args-append   -Ss f77lib \
                                            ${libgccpath}/libgfortran.dylib
            }
            if {${build_arch} eq "x86_64" || ${build_arch} eq "ppc64"} {

                configure.args-append   -b 64
            } else {

                configure.args-append   -b 32
            }
        } else {

            # clang
            if {[variant_isset perf]} {

                append cvct_flag       " -march=native"
                set gvct_flag           ${cvct_flag}
            }
            configure.args-append   -F acg  "'${clang_flags} ${cvct_flag}'"

            if {${fortran} == 1} {

                configure.args-append   -C if ${configure.f77}            
                configure.args-append   -F if "'${gcc_flags} ${gvct_flag}'"
                configure.args-append   -Ss f77lib \
                                            ${libgccpath}/libgfortran.dylib
            }

            if {${build_arch} eq "x86_64"} {

                configure.args-append   -b 64
            } else {

                # Clang has no ppc64 target so ppc64 ➸ regular ppc (32 bit)
                configure.args-append   -b 32
            }
        }
        
        # Create build directory
        file mkdir ${worksrcpath}/build
    } else {
    
        # Universal builds
        foreach arch ${universal_archs_to_use} {
            
            # Init ‘universal’ flags 
            set gcc_univ_flags          ${gcc_flags}
            set gvct_univ_flag          ${gvct_flag}
            set clang_univ_flags        ${clang_flags}
            set cvct_univ_flag          ${cvct_flag}
            set gcc_supp_flags          ""
            set clang_supp_flags        ""
            
            # We build for the native architecture
            if {${arch} eq ${native_arch} && ${build_arch} ne "ppc"} {
                
                # Don’t add -march=native to a GCC build on an AVX
                # machine because this flag overrides -msse4.2
                if {${has_avx} == "no"} {
                
                    append gcc_supp_flags   "-march=native "
                }
                append clang_supp_flags     "-march=native "
            }
            
            # Non-native targets. Apply a priori flags.

            # Clamp i386 optimization to -sse3 in universal builds and
            # forces clang/gcc to use -m32 flag
            if {${arch} eq "i386" && ${native_arch} ne "i386"} {

                set gvct_univ_flag      "-msse3"
                set cvct_univ_flag      "-msse3"
        
                append gcc_supp_flags   "-m32"
                append clang_supp_flags "-m32"
            }
            
            # For x86_64 builds, be conservative and use only -ssse3
            # Core2 CPU and SSE3 ASM kernels
            if {${arch} eq "x86_64" && ${native_arch} ne "x86_64"} {
            
                set gvct_univ_flag      "-mssse3"
                set cvct_univ_flag      "-mssse3"
                
                append gcc_supp_flags   "-m64"
                append clang_supp_flags "-m64"
                
                lappend merger_configure_args("x86_64") -A 25 -V 448
            }
            
            # ppc (on Intel or ppc64 machines)
            if {${arch} eq "ppc" && ${native_arch} ne "ppc"} {
            
                set gcc_univ_flags      "-O3 -mcpu=7400 -mtune=7400\
                                         -fno-schedule-insns -fschedule-insns2\
                                         -fPIC"
                set clang_univ_flags    "-O3 -arch ppc -fPIC"

                set gvct_univ_flag      "-maltivec -mabi=altivec"
                set cvct_univ_flag      "-maltivec"

                append gcc_supp_flags   "-m32"
                append clang_supp_flags "-m32"
            }
            
            # ppc64 (only for gcc, for clang this is taken to be the
            # same target as ppc)
            if {${arch} eq "ppc64" && ${native_arch} ne "ppc64"} {
            
                set gcc_univ_flags      "-mcpu=970 -mtune=970 -mvrsave \
                                         -mpowerpc64 -fno-schedule-insns\
                                         -fschedule-insns2 -fPIC"
                set clang_univ_flags    "-O3 -arch ppc -fPIC"

                set gvct_univ_flag      "-maltivec -mabi=altivec"
                set cvct_univ_flag      "-maltivec"

                append gcc_supp_flags   "-m64"
                append clang_supp_flags "-m64"
            }
            
            # Now add the flags to merger_configure_args(${arch})
            # Same code as the non-universal case
            lappend merger_configure_args(${arch})  -C acg ${configure.cc} 
            if {${use_clang} == ""} {
        
                # GCC
                lappend merger_configure_args(${arch}) \
                    -F acg "'${gcc_univ_flags} ${gcc_supp_flags}\
                             ${gvct_univ_flag}'"
            
                if {${fortran} == 1} {
            
                    lappend merger_configure_args(${arch}) \
                        -C if ${configure.f77} \
                        -F if "'${gcc_univ_flags} ${gcc_supp_flags}\
                                ${gvct_univ_flag}'"
                
                    set libgccarchpath    ${libgccpath}/${arch}
                    if {[file exists ${libgccarchpath}/libgfortran.dylib]} {

                        lappend merger_configure_args(${arch}) \
                            -Ss f77lib ${libgccarchpath}/libgfortran.dylib
                    } else {

                        lappend merger_configure_args(${arch}) \
                            -Ss f77lib ${libgccpath}/libgfortran.dylib
                    }
                }
            } else {

                # clang
                lappend merger_configure_args(${arch}) \
                    -F acg "'${clang_univ_flags} ${clang_supp_flags}\
                             ${cvct_univ_flag}'"
            
                if {${fortran} == 1} {
            
                    lappend merger_configure_args(${arch}) \
                        -C if ${configure.f77} \
                        -F if "'${gcc_univ_flags} ${gcc_supp_flags}\
                                ${gvct_univ_flag}'"
                
                    set libgccarchpath    ${libgccpath}/${arch}
                    if {[file exists ${libgccarchpath}/libgfortran.dylib]} {

                        lappend merger_configure_args(${arch}) \
                            -Ss f77lib ${libgccarchpath}/libgfortran.dylib
                    } else {

                        lappend merger_configure_args(${arch}) \
                            -Ss f77lib ${libgccpath}/libgfortran.dylib
                    }
                }
            }
            
            # Remove spurious { } from arguments list
            set merger_configure_args(${arch}) \
                [join [split $merger_configure_args(${arch}) "{}"]]
                
            # Create ${arch} build tree
            copy ${worksrcpath} ${worksrcpath}-${arch}
            file mkdir ${worksrcpath}-${arch}/build
        }
    }
    
    # On PPC64 we need extra flags for configuring
    if {${native_arch} eq "ppc64"} {
        configure.args-append   --cc="\"${configure.cc} -m32 \
                                 -force_cpusubtype_ALL\""
    }

    # Get some system specific variables for maximum optimization
    set cpufreq [expr {[sysctl hw.cpufrequency] / 1000000}]

    if {${os.arch} eq "powerpc"} {

        configure.args-append    -D c -DWALL
    } else {

        configure.args-append    -D c -DPentiumCPS=${cpufreq}
    }    
}

# We configure from the build directory
configure.dir            ${worksrcpath}/build
configure.cmd            ../configure

post-configure {

    # Store in ${atlas_paths} the ${worksrcpath}s corresponding to the
    # archs Atlas is being built for.
    set     atlas_paths {}
    if {${universal} == 1} {

        foreach arch ${universal_archs_to_use} {

            lappend atlas_paths ${worksrcpath}-${arch}
        }
    } else {

        lappend atlas_paths ${worksrcpath}
    }

    foreach path ${atlas_paths} {

        # Recursively remove directories
        ui_debug    "Patch to recursively remove directories on clean."
        reinplace   "s|rm -f|rm -rf|g" ${path}/build/Makefile

        # Threading — ensure libgomp is correctly pulled in during link
        #if {${gcc_version} != 0 && ${no_threads} == 0} {

        #   set gmpfx    ${prefix}/lib/gcc${gcc_version}
        #   set arch     [lindex [split [lindex [split $path "/"] end] "-"] 2]
        #   if {${arch} ne "" && \
        #       [file exists ${gmpfx}/${arch}/libgomp.dylib]} {

        #      reinplace -E \
        #          "s|(LIBS =.*)|\\1 ${gmpfx}/${arch}/libgomp.dylib|" \
        #          ${path}/build/Make.inc
        #  } else {

        #     reinplace -E \
        #         "s|(LIBS =.*)|\\1 ${gmpfx}/libgomp.dylib|" \
        #         ${path}/build/Make.inc
        # }
        #}
    }
}

build.env-append        CC=${configure.cc} CXX=${configure.cxx}
use_parallel_build      no
build.target            build
build.dir               ${worksrcpath}/build

post-build {
    if {${universal} == 0} {

        set libdir ${build.dir}/lib
        reinplace "s|^LIBTOOL=.*$|LIBTOOL=\$\(GCC\) \$\(GCCFLAGS\)|" \
            ${libdir}/Makefile

        # On mono-processors machines, build libsatlas.dylib only
        if {${no_threads} == 1} {

            system -W ${libdir} "${build.cmd} dylib"
        } else {

            system -W ${libdir} "${build.cmd} shared_all"
        }
    } else {

        foreach arch ${universal_archs_to_use} {

            set libdir ${worksrcpath}-${arch}/build/lib
            reinplace "s|^LIBTOOL=.*$|LIBTOOL=\$\(GCC\) \$\(GCCFLAGS\)|" \
                ${libdir}/Makefile

            # Same as above
           if {${no_threads} == 1} {

                system -W ${libdir} "${build.cmd} dylib"
            } else {

                system -W ${libdir} "${build.cmd} shared_all"
            }
        }
    }
}

destroot.dir          ${build.dir}
destroot.destdir      DESTDIR=${destroot}${prefix}

post-destroot {
    # On monoprocessor machines, make tatlas and satlas synonymous
    if {${no_threads} == 1} {

        set libdir ${destroot}${prefix}/lib
        system -W ${libdir} "ln -s libsatlas.dylib libtatlas.dylib"
    }
}

livecheck.name        math-atlas
livecheck.distname    Stable
