# -*- 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           gpg_verify 1.0
PortGroup           legacysupport 1.1

name                ghc
version             9.12.2
revision            2
categories          lang haskell
maintainers         {ieee.org:s.t.smith @essandess} openmaintainer
license             BSD

description         The Glorious Glasgow Haskell Compilation System
long_description    The Glasgow Haskell Compiler is a robust, \
                    fully-featured, optimising compiler and \
                    interactive environment for Haskell 98, GHC \
                    compiles Haskell to either native code or C.  It \
                    implements numerous experimental language \
                    extensions to Haskell 98, for example: \
                    concurrency, a foreign language interface, \
                    multi-parameter type classes, scoped type \
                    variables, existential and universal \
                    quantification, unboxed types, exceptions, weak \
                    pointers, and so on.  GHC comes with a \
                    generational garbage collector, and a space and \
                    time profiler.

homepage            https://haskell.org/${name}

# https://gitlab.haskell.org/ghc/ghc/-/wikis/building/hadrian
# https://www.haskell.org/ghc/blog/20220805-make-to-hadrian.html

use_xz              yes
master_sites        https://downloads.haskell.org/~${name}/${version}

platform arm {
    set ghc_build_arch  aarch64
}
platform i386 {
    set ghc_build_arch  x86_64
}
platform powerpc {
    set ghc_build_arch  ppc
}

if {${os.arch} ni [list arm i386]} {
    known_fail      yes
    pre-fetch {
        ui_error "GHC is only available on macOS \
            with [join ${supported_archs} " or "] architectures."
        error {unsupported platform}
    }
}

set ghc_version     ${version}
set ghc_distname    ${distname}-${ghc_build_arch}-apple-darwin

set exedir          ${prefix}/lib/${distname}
set docdir          ${prefix}/share/doc/${distname}
set mandir          ${prefix}/share/man/man1

set livecheck_url   https://gitlab.haskell.org/ghc/ghc/-/wikis/GHC%20Status#all-released-ghc-versions
set livecheck_regex {\[download]\[dl([0-9.]+)]}

# set build_arch by hand on arm64/x86_64 systems to get x86_64/arm64 checksums
# sudo port -d checksum ghc-prebuilt os.arch=arm build_arch=arm64
# sudo port -d checksum ghc-prebuilt os.arch=i386 build_arch=x86_64
# run `port clean --all ghc-prebuilt` afterwards
if {${build_arch} eq {arm64}} {
    checksums       ${ghc_distname}${extract.suffix} \
                    rmd160  7ca5c3377749cda9ea431ce03ad794dfb25ed574 \
                    sha256  4b61b933028c63ace950236ea3382d02e51a3d9cbd1ca3f6cf4fe14c71ff436c \
                    size    306760740
} elseif {${build_arch} eq {x86_64}} {
    checksums       ${ghc_distname}${extract.suffix} \
                    rmd160  e840d1a1a97b096459ce5f50ee9687fadcf1f7cf \
                    sha256  e7a40e39059dd3619d7884b7382f357e79a0f4e430181b805bdd57b3be9a7300 \
                    size    310907744
}
set ghc_source_checksums [list \
                    ${distname}-src${extract.suffix} \
                    rmd160  11d8a5f76773d355c868c6292167d2ae38279e57 \
                    sha256  0e49cd5dde43f348c5716e5de9a5d7a0f8d68d945dc41cf75dfdefe65084f933 \
                    size    33394536 \
                    ]
set ghc_testsuite_checksums [list \
                    ${distname}-testsuite${extract.suffix} \
                    rmd160  bd9105298f8e13ba977af4dd45b558229ac331e6 \
                    sha256  cf142eadb4ff9b84ba49c01f52813acbeb742d57b21e64c46424563e0bf410aa \
                    size    7785840 \
                    ]

# Has issues if enabled
configure.ccache    no

# gpg --list-packets ${distfiles}/${distname}-src${extract.suffix}.sig
# https://pgp.mit.edu/pks/lookup?op=get&search=0x2DE04D4E97DB64AD
set gpg_keyid       2DE04D4E97DB64AD

# relative paths to ${prefix}
set ghc_libdir ${prefix}/lib/${name}-${version}

# LlvmMaxVersion=20 from ./configure.ac; must be less than actual llvm version
set ghc_llvm_version    19

# common commands for subport `ghc-prebuilt` and port `ghc +prebuilt` variant
# Note: subport `ghc-prebuilt` is necessary to bootstrap Haskell build tools,
# including port `ghc`, and (nearly equivalent) port `ghc +prebuilt` is
# simply a fallback option when `ghc` cannot be compiled
proc ghc_prebuilt_commands {} {
    global          supported_archs \
                    distfiles ghc_distname extract.suffix \
                    gpg_verify.use_gpg_verification \
                    ghc_distname extract.suffix \
                    checksums ghc_distname extract.suffix \
                    gpg_verify.verify_gpg_signature \
                    filespath gpg_keyid \
                    post-checksum \
                    distpath \
                    depends_build \
                    depends_lib \
                    ghc_llvm_version \
                    extract.only extract.rename \
                    compiler.c_standard \
                    compiler.blacklist \
                    distname name ghc_version \
                    configure.env \
                    prefix \
                    build \
                    post-destroot \
                    exedir docdir mandir \
                    destroot \
                    fs-traverse \
                    ghc_build_arch

    supported_archs arm64 x86_64

    distfiles       ${ghc_distname}${extract.suffix}

    gpg_verify.use_gpg_verification \
                    yes

    if {[option gpg_verify.use_gpg_verification]} {
        distfiles-append \
                    ${ghc_distname}${extract.suffix}.sig
        checksums-append \
                    ${ghc_distname}${extract.suffix}.sig \
                    size    586

        post-checksum {
            gpg_verify.verify_gpg_signature \
                    ${filespath}/keyid-${gpg_keyid}.txt \
                    ${distpath}/${ghc_distname}${extract.suffix}.sig \
                    ${distpath}/${ghc_distname}${extract.suffix}
        }
    }

    depends_build-append \
                    port:file

    depends_lib-append \
                    port:cctools \
                    port:ld64 \
                    port:llvm-${ghc_llvm_version}

    extract.only    ${ghc_distname}${extract.suffix}
    extract.rename  yes

    # Build requires C11
    compiler.c_standard 2011
    # On OSX 10.{8,9} with clang 503, 600
    #   :info:build dtrace: failed to open td=gnu11/usr/bin/clang -std=gnu11 -E ...
    compiler.blacklist-append {clang < 700}

    distname        ${name}-${ghc_version}

    configure.env-append \
                    "LD=${prefix}/bin/ld" \
                    "LLC=${prefix}/bin/llc-mp-${ghc_llvm_version}" \
                    "OPT=${prefix}/bin/opt-mp-${ghc_llvm_version}"

    build           {}

    post-destroot {
        set exedir  ${prefix}/lib/${distname}
        set docdir  ${prefix}/share/doc/${distname}
        set mandir  ${prefix}/share/man/man1

        delete -force \
            ${destroot}${docdir}/archives

        fs-traverse d ${destroot}${prefix}/share {
            if { [file isdirectory ${d}]
                && [string match ${ghc_build_arch}-*-${distname}-* \
                    [file tail ${d}]] } {
                if { [file exists ${d}] } {
                    file delete -force {*}[glob -directory ${d} *]
                }
            }
        }
    }
}

if {${name} eq ${subport}} {
    variant prebuilt \
        description {Install prebuilt binaries. Note: variant `ghc +prebuilt`
            is used as a fallback when `ghc` cannot be compiled, whereas
            port `ghc-prebuilt` is necessary to bootstrap Haskell build tools,
            including port `ghc`.} {}

    # REMOVE after https://gitlab.haskell.org/ghc/ghc/-/issues/26166
    set ghc_compile_broken_major_version    25
    if { ${os.major} >= ${ghc_compile_broken_major_version} } {
        default_variants-append \
                    +prebuilt
    }

    if { ![variant_isset "prebuilt"] } {
        PortGroup   haskell_cabal 1.0

        haskell_cabal.use_prebuilt  yes

        haskell_cabal.env \
                    "CABAL_CONFIG=${haskell_cabal.cabal_root}/config" \
                    "GHC=${haskell_cabal.cabal_root}/bin/ghc" \
                    "LD=${prefix}/bin/ld" \
                    "LLC=${prefix}/bin/llc-mp-${ghc_llvm_version}" \
                    "OPT=${prefix}/bin/opt-mp-${ghc_llvm_version}" \
                    "PATH=${workpath}/bin:${haskell_cabal.cabal_root}/bin:$env(PATH)"

        # /usr/bin/clang++ -std=gnu++11: No such file or directory
        compiler.cxx_standard \
                    2011
        configure.cxxflags-append \
                    -std=gnu++11

        # https://trac.macports.org/ticket/65899
        # https://trac.macports.org/wiki/UsingTheRightCompiler#testing
        foreach phase {build destroot test} {
            ${phase}.env-append \
                    CC=${configure.cc} \
                    "CFLAGS=${configure.cflags}" \
                    CPP=${configure.cpp} \
                    "CPPFLAGS=${configure.cppflags}" \
                    CXX=${configure.cxx} \
                    "CXXFLAGS=${configure.cxxflags}" \
                    "LDFLAGS=${configure.ldflags}" \
                    "LD=${prefix}/bin/ld" \
                    "LLC=${prefix}/bin/llc-mp-${ghc_llvm_version}" \
                    "OPT=${prefix}/bin/opt-mp-${ghc_llvm_version}"
        }

        use_configure   yes

        configure.cmd   ./configure
        configure.pre_args \
                        --prefix=${prefix}
        configure.args

        build.cmd       hadrian
        build.target    binary-dist-dir
        build.pre_args
        build.post_args -VVVVV [haskell_cabal.build_getjobsarg]

        destroot.cmd    hadrian
        destroot.target install
        destroot.pre_args \
                        --prefix=${destroot}${prefix}
        destroot.post_args \
                        -VVVVV

        test.cmd        hadrian
        test.target     test
        test.pre_args
        test.post_args  [haskell_cabal.build_getjobsarg]

        distfiles       [lindex ${ghc_source_checksums} 0] \
                        [lindex ${ghc_testsuite_checksums} 0]
        checksums       {*}${ghc_source_checksums} \
                        {*}${ghc_testsuite_checksums}
        extract.only    [lindex ${ghc_source_checksums} 0] \
                        [lindex ${ghc_testsuite_checksums} 0]

        gpg_verify.use_gpg_verification \
                        yes

        if {[option gpg_verify.use_gpg_verification]} {
            distfiles-append \
                        ${distname}-testsuite${extract.suffix}.sig
            checksums-append \
                        ${distname}-testsuite${extract.suffix}.sig \
                size    586

            post-checksum {
                gpg_verify.verify_gpg_signature \
                    ${filespath}/keyid-${gpg_keyid}.txt \
                    ${distpath}/${distname}-testsuite${extract.suffix}.sig \
                    ${distpath}/${distname}-testsuite${extract.suffix}
            }
        }

        # Build requires C11
        compiler.c_standard 2011
        # On OSX 10.{8,9} with clang 503, 600
        #   :info:build dtrace: failed to open td=gnu11/usr/bin/clang -std=gnu11 -E ...
        compiler.blacklist-append {clang < 700}
        # * Missing (or bad) C libraries: m, dl, ffi
        compiler.blacklist-append {clang >= 1700.3}

        # use these to specify python versions, python3 required
        # use ${prefix}/var/macports/sources/rsync.macports.org/macports/release/tarballs/ports/_resources/port1.0/group/python-1.0.tcl
        set python3_version 313
        set python3_branch  [string index ${python3_version} 0].[string range ${python3_version} 1 end]
        set python3_prefix  ${frameworks_dir}/Python.framework/Versions/${python3_branch}
        set python3_bin     ${python3_prefix}/bin/python${python3_branch}

        depends_build-append \
                    port:alex \
                    port:autoconf \
                    port:automake \
                    port:bash \
                    port:bzip2 \
                    port:file \
                    port:gzip \
                    port:hadrian \
                    port:happy \
                    port:hscolour \
                    port:python${python3_version} \
                    port:py${python3_version}-sphinx \
                    port:texlive-fonts-extra \
                    port:texlive-fonts-recommended \
                    port:texlive-latex-extra \
                    port:texlive-xetex \
                    port:xz

        depends_lib-append  \
                    port:cctools \
                    port:gmp \
                    port:ld64 \
                    port:libffi \
                    port:libiconv \
                    port:llvm-${ghc_llvm_version} \
                    port:ncurses
        
        # build depends upon these x86_64 binaries
        depends_skip_archcheck-append \
                    alex \
                    hadrian \
                    happy \
                    hscolour

        post-extract {
            xinstall -d ${workpath}/bin
            ln -s   ${python3_bin} \
                    ${workpath}/bin/python3
            ln -s   ${prefix}/bin/sphinx-build-${python3_branch} \
                    ${workpath}/bin/sphinx-build
            # avoid this cabal/sed error in macOS 10 and earlier:
            # checking for version of sphinx-build... sed: illegal option -- r
            ln -s   ${prefix}/bin/gsed \
                    ${workpath}/bin/sed
        }

        post-patch {
            if { ![file exists ${worksrcpath}/boot] } {
                xinstall \
                    ${worksrcpath}/boot.source \
                    ${worksrcpath}/boot
            }

            # fix DejaVu texlive fonts names, e.g. "\setmonofont{DejaVu Sans Mono}"
            foreach f [list \
                        ${worksrcpath}/libraries/Cabal/doc/conf.py \
                        ${worksrcpath}/docs/users_guide/conf.py \
                    ] {
                reinplace -E \
                    "s|(\{DejaVu\[\[:alnum:]_]*)\[\[:space:]]+|\\1|g" ${f}
                reinplace -E \
                    "s|(\{DejaVu\[\[:alnum:]_]*)\[\[:space:]]+|\\1|g" ${f}
                reinplace -E \
                    "s|\{(DejaVu\[\[:alnum:]_]*)\}|\{\\1.ttf\}|g" ${f}
            }
        }

        pre-configure {
            system -W ${worksrcpath} \
                    "env ${haskell_cabal.env} ./boot"
        }

        use_autoconf    yes

        # https://gitlab.haskell.org/ghc/ghc/-/issues/22118
        configure.pre_args-append \
                    --with-ffi-includes=${prefix}/include \
                    --with-ffi-libraries=${prefix}/lib \
                    --with-gmp-includes=${prefix}/include \
                    --with-gmp-libraries=${prefix}/lib \
                    --with-iconv-includes=${prefix}/include \
                    --with-iconv-libraries=${prefix}/lib \
                    --with-system-libffi

        post-build {
            system -W ${build.dir} \
                    "env ${haskell_cabal.env} ${build.cmd} docs [haskell_cabal.build_getjobsarg]"
        }

        pre-destroot {
            # mk/install_script.sh isn't automatically created by hadrian
            set bindist_mkdir [glob ${worksrcpath}/_build/bindist/${distname}-*/mk]
            if {![file exists ${bindist_mkdir}/install_script.sh]} {
                xinstall -W ${worksrcpath} mk/install_script.sh ${bindist_mkdir}
            }
        }

        haskell_cabal.bindirs-append \
                    ${destroot}${prefix}/lib/${subport}-${version}/bin \
                    ${destroot}${prefix}/lib/${subport}-${version}/lib/bin
        
        # documentation
        post-destroot {
            xinstall -W ${filespath} -m 0644 ./ghci.conf ${destroot}${prefix}/etc

            delete  ${destroot}${prefix}/share/share/doc/${distname}/archives

            xinstall -d ${destroot}${prefix}/share/man/man1
            xinstall -m 0644 \
                    ${worksrcpath}/_build/manpage/${name}.1 \
                    ${destroot}${prefix}/share/man/man1
        }

        post-destroot {
            # delete any destroot path appearing in text files
            fs-traverse f ${destroot}${prefix} {
                if {[file isfile ${f}] && [file type ${f}] eq {file}} {
                    if {[string match "text/*" [lindex [exec file --mime-type ${f}] end]]} {
                        reinplace -q "s|${destroot}||g" ${f}
                    }
                }
            }
        }

        test.run    yes

        post-activate {
            system  "${prefix}/bin/ghc-pkg recache"
        }
    } else { # variant_isset "prebuilt"
        ghc_prebuilt_commands
    }

    notes "The GHC User Manual is available at:

    file://${prefix}/share/doc/${distname}/html/index.html
    ${prefix}/share/doc/${distname}/users_guide.pdf

Copy/edit ${prefix}/etc/ghci.conf to your directory ~/.ghc
for a user-specific startup configuration.

Note: variant `ghc +prebuilt` is used as a fallback when `ghc` 
cannot be compiled, whereas port `ghc-prebuilt` is necessary to bootstrap 
Haskell build tools, including port `ghc`."

    livecheck.url   ${livecheck_url}
    livecheck.type  regex
    livecheck.regex ${livecheck_regex}
}

subport ghc-prebuilt {
    revision        1

    ghc_prebuilt_commands

    # rename installed files to *-prebuilt
    post-destroot {
        foreach f [glob ${destroot}${prefix}/bin/*] {
            if {[file isfile ${f}] && [file type ${f}] eq {file}
                && [string match "text/*" [lindex [exec file --mime-type ${f}] end]]} {
                reinplace -E -q \
                    "s|^(exeprog=\".+)(\")\$|\\1-prebuilt\\2|" \
                    ${f}
                foreach d [list ${exedir} ${docdir}] {
                    reinplace -E -q "s|(${d})|\\1-prebuilt|g" ${f}
                }
                move ${f} ${f}-prebuilt
            } elseif {[file type ${f}] eq {link}} {
                set flink [file readlink ${f}]
                ln -s ${flink}-prebuilt ${f}-prebuilt
                delete ${f}
            }
        }
        foreach d [list ${destroot}${exedir} ${destroot}${docdir}] {
            move    ${d} ${d}-prebuilt
        }
        move ${destroot}${mandir}/${name}.1 ${destroot}${mandir}/${name}-prebuilt.1
    }

    notes "Note: port `ghc-prebuilt` is necessary to bootstrap Haskell
build tools, including port `ghc`, whereas port variant `ghc +prebuilt`
is used as a fallback when `ghc` cannot be compiled."

    livecheck.url   ${livecheck_url}
    livecheck.type  regex
    livecheck.regex ${livecheck_regex}
}

subport hadrian {
    # `hadrian --version` uses the shake version, not the hadrian.cabal version
    # See: https://hackage.haskell.org/package/shake
    version         0.19.8
    # please revbump when updating the ghc version
    revision        2

    description     Hadrian is the build system for the Glasgow Haskell Compiler
    long_description \
                    {*}${description}. Hadrian is more scalable, faster, \
                    and spectacularly more maintainable than its Make-based \
                    predecessor.

    homepage        https://gitlab.haskell.org/${name}/${name}/-/tree/master/${subport}

    distname        ${name}-${ghc_version}
    distfiles       [lindex ${ghc_source_checksums} 0]
    checksums       {*}${ghc_source_checksums}
    extract.only    [lindex ${ghc_source_checksums} 0]
if {[exists extract.rename]} {
    extract.rename  yes
}
    set worksrcpath ${workpath}/${worksrcdir}/${subport}

    variant stack \
        description {Use stack to build.} {}

    if { [variant_isset "stack"] } {
        PortGroup   haskell_stack 1.0

        post-extract {
            # Fix for cabal data-files hardcoded path in binary
            # See:
            # https://github.com/commercialhaskell/stack/issues/848
            # https://github.com/commercialhaskell/stack/issues/4857
            # https://github.com/haskell/cabal/issues/462
            # https://github.com/haskell/cabal/issues/3586
            xinstall -m 0644 -W ${worksrcpath} \
                    ${filespath}/Paths_${subport}.hs ./src/Paths_${subport}.hs

            reinplace "s|@PREFIX@|${prefix}|g" \
                    ${worksrcpath}/src/Paths_${subport}.hs
            reinplace "s|@NAME@|${subport}|g" \
                    ${worksrcpath}/src/Paths_${subport}.hs
            reinplace -E \
                    "s|(Version\[\[:space:\]\]+)\\\[\[\[:digit:\]\]+(,\[\[:digit:\]\]+){1,4}\\\]|\\1\[[join [split ${version} .] ,]\]|" \
                    ${worksrcpath}/src/Paths_${subport}.hs
        }
    } else {
        PortGroup   haskell_cabal 1.0

        variant haskell_cabal_use_prebuilt \
            description {Use prebuilt cabal and ghc. This is a necessary\
                default variant because hadrian is used to bootstrap ghc\
                and ghc is used to bootstrap cabal.} {
            haskell_cabal.use_prebuilt  yes
        }
        default_variants-append \
                    +haskell_cabal_use_prebuilt

        build.target-append \
                    --project-file=cabal.project \
                    --allow-newer
        build.post_args-append \
                    --bindir=${prefix}/bin \
                    --datadir=${prefix}/share/${subport}
    }

    # `hadrian --version` uses the shake version, not the hadrian.cabal version
    livecheck.url   https://hackage.haskell.org/package/shake
    livecheck.type  regex
    livecheck.regex {</a>, <strong>([0-9.]+)</strong>}
}
