# -*- coding: utf-8; mode: tcl; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:ft=tcl:et:sw=4:ts=4:sts=4

PortSystem          1.0
PortGroup           compilers 1.0
PortGroup           mpiutil 1.0

#===============================================================================
#
# *** IMPORTANT NOTE ***
#
# When making logic changes to this port, PLEASE review port 'openmpi' to see
# if the same changes should be applied. While the subports and variants aren't
# exactly the same between the two - and things like configure arguments
# certainly differ, as they're different code bases - much of the core logic
# is very similar. (And often identical.)
#
# Please help us avoid divergent MPI ports, which cause serious migraines.
#
#===============================================================================

# make sure to keep in sync with mpi-doc
name                mpich
version             4.3.0
revision            0

license             BSD
categories          science parallel net
maintainers         {eborisch @eborisch} \
                    {mascguy @mascguy}

description         Message Passing Interface (MPI) Library

long_description    MPICH is a high-performance and widely portable\
                    implementation of the Message Passing Interface (MPI) \
                    standard (MPI-1, MPI-2 and MPI-3). The goals of MPICH are:\
                    (1) to provide an MPI implementation that efficiently\
                    supports different computation and communication platforms\
                    including commodity clusters (desktop systems,\
                    shared-memory systems, multicore architectures), high-speed\
                    networks (10 Gigabit Ethernet, InfiniBand, Myrinet,\
                    Quadrics) and proprietary high-end computing systems (Blue\
                    Gene, Cray) and (2) to enable cutting-edge research in MPI\
                    through an easy-to-extend modular framework for other\
                    derived implementations.

homepage            https://www.mpich.org/
master_sites        ${homepage}static/downloads/${version}

checksums           rmd160  bbe67d97908feca27392f118b554f1993d8765f8 \
                    sha256  5e04132984ad83cab9cc53f76072d2b5ef5a6d24b0a9ff9047a8ff96121bcc63 \
                    size    37472513

# Disable livecheck for all subports; only enabled for main port, at end of portfile
livecheck.type      none

#-------------------------------------------------------------------------------
# Target Compiler Logic
#-------------------------------------------------------------------------------

# As MPICH creates compiler wrappers, there are lots of
# variants for what compiler the user would like to wrap.

# Please update the mpi portgroup whenever clist is changed.

# Subport names and corresponding configure.compiler value
set clist [dict create]
set clist_unsupported [list]
set clist_obsolete [list]

# Compilers supported across-the-board
dict set clist gcc7    {macports-gcc-7}
dict set clist gcc10   {macports-gcc-10}
dict set clist gcc11   {macports-gcc-11}
dict set clist gcc12   {macports-gcc-12}
dict set clist gcc13   {macports-gcc-13}
dict set clist gcc14   {macports-gcc-14}
dict set clist clang11 {macports-clang-11}
dict set clist clang12 {macports-clang-12}
dict set clist clang13 {macports-clang-13}
dict set clist clang14 {macports-clang-14}
dict set clist clang15 {macports-clang-15}
dict set clist clang16 {macports-clang-16}
dict set clist clang17 {macports-clang-17}
dict set clist clang18 {macports-clang-18}
dict set clist clang19 {macports-clang-19}
dict set clist clang20 {macports-clang-20}

# Only enable default (gcc), and Xcode clang, for MacOS 10.7 and later
if { ${os.major} >= 11 } {
    dict set clist default {}
    dict set clist clang   {clang}
} else {
    lappend clist_unsupported \
        default clang
}

# Clang 9 and 10 not supported on ARM
if { ${os.arch} eq "arm" } {
    lappend clist_unsupported \
        clang90 clang10
} else {
    dict set clist clang90 {macports-clang-9.0}
    dict set clist clang10 {macports-clang-10}
}

# GCC 9 only supported for macOS 10.6 through 10.10
if { ${os.major} >= 10 && ${os.major} <= 14 } {
    dict set clist gcc9  {macports-gcc-9}
} else {
    lappend clist_unsupported \
        gcc9
}

#-------------------------------------------------------------------------------
# Subport Creation/Validation
#-------------------------------------------------------------------------------

set cname \
    [lindex [split ${subport} -] end]

mpiutil_add_subports \
    ${name} ${subport} \
    ${clist} ${clist_unsupported} ${clist_obsolete}

set subport_enabled \
    [mpiutil_validate_subport \
        ${name} ${subport} ${cname} \
        ${clist} ${clist_unsupported} ${clist_obsolete} \
    ]

#-------------------------------------------------------------------------------
# General Subport Logic
#-------------------------------------------------------------------------------

# TODO: simplify via regex capture group, for clang version
if {[string match {*-clang1[3-9]} ${subport}]
    || [string match {*-clang2[0-9]} ${subport}]} {
    platforms   {darwin >= 11}
}

if {${subport_enabled}} {
    PortGroup select 1.0
    PortGroup muniversal 1.0
    PortGroup legacysupport 1.1

    if {${cname} eq "default"} {
        set cname "mp"
    }

    # Add various dependencies: build, lib, and run
    mpiutil_add_depends \
        ${subport} ${cname}

    # Determine whether buildbot binaries should be used, and disable if necessary
    mpiutil_set_binary_eligibility \
        ${subport} ${cname}

    depends_build-append        port:pkgconfig
    depends_lib-append          port:libxml2

    if {${cname} eq "mp"} {
        if {${os.major} == 10} {
            # Make every attempt to avoid MacPorts compilers on 10.6
            configure.cxx_stdlib libstdc++
        }
    }

    # ch4 is hanging in finalize https://github.com/pmodels/mpich/issues/6584
    # always use ch3 for now.

    configure.args  --disable-dependency-tracking    \
                    --disable-fortran                \
                    --disable-silent-rules           \
                    --enable-base-cache              \
                    --enable-cache                   \
                    --enable-cxx                     \
                    --enable-fast=O2                 \
                    --enable-shared                  \
                    --enable-versioning              \
                    --enable-timer-type=mach_absolute_time \
                    --with-pm=hydra                  \
                    --with-thread-package=posix      \
                    --with-hwloc-prefix=${prefix}    \
                    --disable-collalgo-tests         \
                    --with-device=ch4:ofi:tcp

    # Hopefully avoid double-rpath issues.
    compilers.add_gcc_rpath_support no

    if {${os.major} < 12} {
        # MPICH requires OpenCL version 1.2, which was not introduced until OS X Mountain Lion
        configure.args-append \
                    --enable-opencl=no
        # https://trac.macports.org/ticket/70418 (change in scandir prototype)
        patchfiles-append   patch-scandir-lion.diff
    }

    patchfiles-append \
                    patch-no_qmkshrobj.diff \
                    patch-mpich-darwin-powerpc.diff

    post-patch {
        reinplace \
            "s/commons_use_dylibs_works=yes/commons_use_dylibs_works=no/" \
            configure
    }

    platform darwin powerpc {
        # libfabric calls for atomicops, on PPC at least
        configure.ldflags-append \
                    -latomic
    }

    # We're making compiler wrappers here... don't default to -O2 for wrappers.
    # Actual library code is compiled with -O2 via --enable-fast=O2 configure arg
    configure.optflags-delete   -O2 -Os
    configure.cppflags-delete   -I${prefix}/include
    configure.ldflags-delete    -L${prefix}/lib

    select.group                mpi
    select.file                 ${filespath}/portselect/${name}-${cname}

    # Prevent "ccache" and "distcc" from being baked into the wrapper scripts.
    configure.ccache            no
    configure.distcc            no

    if {${cname} ne "mp"} {
        set compiler            [dict get ${clist} ${cname}]
        configure.compiler      [lindex ${compiler} 0]
        unset compiler

        long_description-append \
            "\n\nTHIS SUBPORT WRAPS ${cname}'s C/C++"
    } else {
        long_description-append \
            "\n\nTHIS SUBPORT WRAPS MACPORTS' DEFAULT COMPILER FOR C/C++"
    }

    configure.args-append   \
        --libdir=${prefix}/lib/${name}-${cname} \
        --sysconfdir=${prefix}/etc/${name}-${cname} \
        --program-suffix=-${name}-${cname} \
        --includedir=${prefix}/include/${name}-${cname}

    # avoid dependency on port:bash
    configure.env-append \
        BASH_SHELL=/bin/bash

    # setting xFLAGS resulting in mpicc, etc. always using these flags
    # setting MPICHLIB_xFLAGS only uses them for building the library
    foreach env_var {CFLAGS CPPFLAGS CXXFLAGS FFLAGS FCFLAGS} {
        configure.args-append \
            MPICHLIB_${env_var}=\"\$${env_var}\" \
            ${env_var}=""
    }

    # remove LDFLAGS mpicc, etc. and pkgconfig files
    post-build {
        foreach arch [get_canonical_archs] {
            if {[variant_exists universal] && [variant_isset universal]} {
                set dir ${worksrcpath}-${arch}
            } else {
                set dir ${worksrcpath}
            }
            foreach f [glob \
                           ${dir}/src/env/*.bash \
                           ${dir}/src/env/*.sh \
                           ${dir}/src/packaging/pkgconfig/*.pc] {
                reinplace \
                    "s| ${configure.ldflags}||g" \
                    ${f}
                reinplace \
                    "s| -arch ${arch}||g" \
                    ${f}
            }
        }
    }

    post-destroot {
        # This version doesn't supply doc/manpages
        if {[file isdirectory ${destroot}${prefix}/share/man]} {
            delete ${destroot}${prefix}/share/man
        }
        if {[file isdirectory ${destroot}${prefix}/share/doc]} {
            delete ${destroot}${prefix}/share/doc
        }
    }

    #---------------------------------------------------------------------------
    # Fortran Support
    #---------------------------------------------------------------------------

    set fortran_enabled no

    if { [string first gcc $cname] == 0 } {
        set fortran_enabled yes

        long_description-append "AND FORTRAN COMPILERS"

        variant fortran description {Stub : fortran always enabled for gcc-based version} {}
        default_variants-append     +fortran

        if {${cname} eq "gcc10" ||
            ${cname} eq "gcc11" ||
            ${cname} eq "gcc12" ||
            ${cname} eq "gcc13"} {
            # see https://lists.mpich.org/pipermail/discuss/2020-January/005862.html
            # see https://github.com/pmodels/mpich/issues/4300
            # see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91556
            configure.fflags-append -fallow-argument-mismatch
            configure.fcflags-append -fallow-argument-mismatch
        }

        # GCC C++ always uses libstdc++
        # see https://trac.macports.org/ticket/59185
        configure.cxx_stdlib libstdc++
    } else {
        long_description-append "(AND THE FORTRAN COMPILER SELECTED BY THE VARIANT, IF ANY)"

        compilers.allow_arguments_mismatch yes
        compilers.choose   fc f77 f90
        compilers.setup    default_fortran

        if {[fortran_variant_isset]} {
            set fortran_enabled yes

            # Ask the portgroup for the correct dependency - the Fortran
            # variants gccX and g95 match the name of the ports but
            # gccdevel does not.
            depends_lib-append      [fortran_variant_depends]

            if {[variant_isset g95]} {
                configure.args-append lt_cv_ld_force_load=no
            }
        }
    }

    if {${fortran_enabled}} {
        configure.args-replace  --disable-fortran \
                                --enable-fortran

        # avoid
        #     Python 3 is required to generate F90 bindings but not found!
        set python_branch       3.13
        set python_version      [string map {. {}} ${python_branch}]
        depends_build-append    port:python${python_version}
        depends_skip_archcheck-append   port:python${python_version}
        configure.python        ${prefix}/bin/python${python_branch}

        select.file             ${filespath}/portselect/${name}-${cname}-fortran
    }

    unset fortran_enabled

    #---------------------------------------------------------------------------
    # Universal Build Support
    #---------------------------------------------------------------------------

    if {![info exists universal_possible]} {
        set universal_possible [expr {${os.universal_supported} && [llength ${configure.universal_archs}] >= 2}]
    }
    # Fortran headers do not understand preprocessors commands like __LP64__
    # Fortran mod files can not be merged for different architectures
    # create a architecture specific folder for the non-build_arch architecture
    if {${universal_possible} && [variant_isset universal]} {
        patchfiles-append patch-universal_fortran.diff
        post-patch {
            if {${build_arch} eq "ppc" || ${build_arch} eq "ppc64"} {
                set arch32 "ppc"
                set arch64 "ppc64"
            } else {
                set arch32 "i386"
                set arch64 "x86_64"
            }
            reinplace \
                "s|__MACPORTS_32_BIT_ARCH__|${arch32}|g" \
                ${worksrcpath}/src/env/mpifort.bash.in
            reinplace \
                "s|__MACPORTS_64_BIT_ARCH__|${arch64}|g" \
                ${worksrcpath}/src/env/mpifort.bash.in
            reinplace \
                "s|__MACPORTS_BUILD_ARCH__|${build_arch}|g" \
                ${worksrcpath}/src/env/mpifort.bash.in
        }
        merger-post-destroot {
            foreach arch ${configure.universal_archs} {
                set incdir  ${destroot}-${arch}${prefix}/include/${name}-${cname}
                if {${arch} ne ${build_arch}} {
                    set archinc ${incdir}/${arch}
                    xinstall -d -m 0755 ${archinc}
                    foreach f [glob -nocomplain -directory ${incdir} *.mod mpif.h] {
                        move ${f} ${archinc}
                    }
                }
            }
        }
    }

    #---------------------------------------------------------------------------
    # Variants
    #---------------------------------------------------------------------------

    variant threads description {Build with full thread support} {
        configure.args-append   --enable-threads=multiple
    }

    variant gforker description {Use gforker process manager instead of the default hydra} {
        configure.args-replace  --with-pm=hydra \
                                --with-pm=gforker
    }

    variant native description {Build for local machine} {
        configure.args-replace      --enable-fast=O2 \
                                    --enable-fast=all
        # Note these need to be above the following foreach
        if {${build_arch} in [list ppc ppc64]} {
            configure.cflags-append     -mtune=native
            configure.cxxflags-append   -mtune=native
        } else {
            configure.cflags-append     -march=native
            configure.cxxflags-append   -march=native
        }
    }

    #---------------------------------------------------------------------------
    # Notes
    #---------------------------------------------------------------------------

    mpiutil_add_notes \
        ${name} ${subport} ${cname} ${select.file}

} elseif {${subport} eq ${name}} {
    PortGroup               stub 1.0

    depends_lib-append      port:${name}-default

    livecheck.type          regex
    livecheck.regex         {href=.([0-9.p]+)/}
    livecheck.url           ${homepage}/static/downloads/
}
