# -*- 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               compiler_blacklist_versions 1.0

name                    ld64
epoch                   2
version                 3
revision                2
set dyld_version        655.1.1
categories              devel
platforms               darwin
maintainers             {jeremyhu @jeremyhu} {kencu @kencu}
homepage                http://opensource.apple.com/source/${name}/
master_sites            http://opensource.apple.com/tarballs/${name} \
                        http://opensource.apple.com/tarballs/dyld
license                 APSL-2
installs_libs           no
description             ld64 is the new mach-o linker
long_description        ld64 combines several object files and libraries, \
                        resolves references, and produces an ouput file.

checksums           dyld-655.1.1.tar.gz \
                    rmd160  eef3ce30381e5fa23152c637e3bde2d8cf4cde0b \
                    sha256  8ca6e3cf0263d3f69dfa65e0846e2bed051b0cff92e796352ad178e7e4c92f1d \
                    ld64-97-standalone-libunwind-headers.patch \
                    rmd160  f6da71e097aa61b1055b3fdc12cd39aafed5f492 \
                    sha256  370d02757ea628b5dd145c099e42fc4eb88cc09cf459a59e32d14bbc9b4a105e \
                    ld64-97.17.tar.gz \
                    rmd160  d52df7d7f741c8bedd29cbac73dbb9db992b4795 \
                    sha256  02bd46af0809eaa415d096d7d41c3e8e7d80f7d8d181840866fb87f036b4e089 \
                    ld64-127.2.tar.gz \
                    rmd160  8ee709341549a1944732daef6ebab7ef1acfcc6e \
                    sha256  97b75547b2bd761306ab3e15ae297f01e7ab9760b922bc657f4ef72e4e052142 \
                    ld64-236.3.tar.gz \
                    rmd160  6a3f44aa9ae57a60d2cff5b3d47be7972ad83029 \
                    sha256  8ef36729b643201081ab45ebd8586ede8f9968bc17614b679a940faa82875ca6 \
                    ld64-274.2.tar.gz \
                    rmd160  16e57faf2b1d3f3808fb8e4b167cf23a14f106b0 \
                    sha256  175d89c419e99d49a7a5f7e4196d3cef4c9e19cc17a425c332e86df6b516f7d7 \
                    ld64-450.3.tar.gz \
                    rmd160  4d263cff143228e021c438d3c2d1800ff367c895 \
                    sha256  140619e676e099581771dbad98277850ff731cd23938bed95b4d7171616acca1 \
                    size    729639

subport ld64-97 {
    # Xcode 3.2.6
    version             97.17
    revision            9

    set makefile        "Makefile-97"
    set ld64_ver        97

    # the fallbacks on all current systems (10.6+ at least) are not set up to build this
    compiler.blacklist-append *clang*

    patchfiles-append \
        ld64-version.patch \
        ld64-97-no-LTO.patch \
        ld64-97-ppc-branch-island.patch \
        ld64-97-arm_types_PR38931.patch \
        ld64-97-long-branch-warn.patch \
        ld64-97-standalone-libunwind-headers.patch \
        ld64-97-no-Availability.h.patch \
        ld64-97-BaseAtomImplicitDecl.patch \
        ld64-97-no-ppc-thread_status.patch \
        ld64-97-configured_architectures.patch

    if {${os.major} >= 14} {
        pre-fetch {
            ui_error "$subport is not supported on Yosemite or later."
            error "unsupported platform"
        }
    }
}

subport ld64-127 {
    # Xcode 4.2
    # This was the last ld64 release that supported linking ppc executables.
    version             127.2
    revision            14

    # multiple errors with macports clang versions newer than 3.7
    compiler.blacklist-append {macports-clang-[5-9].0} macports-clang-devel

    set makefile        "Makefile-127"
    set ld64_ver        127

    patchfiles-append \
        ld64-version.patch \
        ld64-134-missing-include.patch \
        ld64-127-any-cctools.patch \
        ld64-127-long-branch-warn.patch \
        ld64-127-cxx_initializer_order_and_sim.patch \
        ld64-127-ppc.patch \
        ld64-127-configured_architectures.patch \
        ld64-127-align_file_offset_with_address_layout.patch \
        ld64-127-alignment_padding.patch \
        ld64-127-filter_out_zero_length_functions.patch \
        ld64-127-i386-reloc.patch \
        ld64-127-malformed_object_crash.patch \
        ld64-127-no_ld_classic.patch \
        ld64-127-static_slidable.patch \
        ld64-97-no-Availability.h.patch \
        PR-12249048.patch \
        PR-12057707.patch \
        PR-13282463.patch \
        PR-29117886.patch \
        ld64-ppc-9610466.patch
}

subport ld64-236 {
    # Xcode 5.1
    version             236.3
    revision            8

    # http://trac.macports.org/ticket/43737
    # < 100 is a guess.  Xcode 4.1 might work, so I'm leaving it as an option unless someone reports a failure.
    compiler.blacklist-append *gcc* {clang < 100}

    # multiple errors with macports clang versions newer than 5.0
    compiler.blacklist-append {macports-clang-[6-9].0} macports-clang-devel

    set makefile        "Makefile-133"
    set ld64_ver        236

    patchfiles-append \
        ld64-version.patch \
        ld64-133-no-CrashReporterClient.h.patch \
        ld64-134-missing-include.patch \
        ld64-136-i386-badAddress.patch \
        PR-16935960.patch \
        PR-16936488.patch \
        PR-28988396.patch \
        PR-29117886.patch \
        PR-33746767.patch \
        ld64-ppc-9610466.patch

    if {${configure.cxx_stdlib} eq "libstdc++"} {
        patchfiles-append   ld64-236-hash_set.patch
    }

    if {${os.major} <= 9} {
        pre-fetch {
            ui_error "$subport is not supported on Leopard or earlier.  It requires the blocks runtime."
            error "unsupported platform"
        }
    }
}

subport ld64-274 {
    # Xcode 8.2.1
    version             274.2
    revision            0

    # https://trac.macports.org/ticket/43737
    # https://trac.macports.org/ticket/50130
    compiler.blacklist-append *gcc* {clang < 300}

    set makefile        "Makefile-274"
    set ld64_ver        274

    patchfiles-append \
        ld64-version.patch \
        ld64-133-no-CrashReporterClient.h.patch \
        ld64-134-missing-include.patch \
        ld64-136-i386-badAddress.patch \
        ld64-ppc-9610466.patch \
        PR-49393.patch \
        PR-28988396.patch \
        PR-29117886.patch \
        PR-29679726.patch \
        PR-29723276.patch \
        PR-29723629.patch \
        PR-33746767.patch

    depends_lib-append port:libcxx
    configure.cxx_stdlib libc++
    configure.cxxflags-append -std=c++11

    supported_archs i386 x86_64
}

subport ld64-latest {
    # Xcode 10.2
    version 450.3
    set makefile        "Makefile-450"

    revision            0

    # https://trac.macports.org/ticket/43737
    # https://trac.macports.org/ticket/50130
    # clang 700.1.81 (10.10) fails with error: use of undeclared identifier '__builtin_mul_overflow'
    compiler.blacklist-append *gcc* {clang < 701}

    set ld64_ver        latest

    patchfiles-append \
        ld64-version.patch \
        ld64-133-no-CrashReporterClient.h.patch \
        ld64-136-i386-badAddress.patch \
        ld64-ppc-9610466.patch \
        ld64-409-snapshot-no-mkpath-np.diff \
        ld64-409-lto-file-llvm-3.4-fix.diff \
        ld64-409-Options-strndup.diff \
        ld64-409-add-missing-machine-defs.diff \
        ld64-409-options-disable-i386-warning.diff \
        ld64-450-move-baseplatform-def-to-header.diff \

    depends_lib-append port:libcxx port:libtapi
    configure.cxx_stdlib libc++
    configure.cxxflags-append -std=c++11

    # silence some warnings
    configure.cxxflags-append -Wno-deprecated-declarations
    configure.cxxflags-append -Wno-parentheses-equality

    supported_archs i386 x86_64
}

subport ld64-xcode {
    version             2
}

variant universal {}

if {${subport} eq ${name}} {
    distfiles
    build {}
    use_configure no
    # avoid cyclic macports-clang dep on 10.6/libc++
    configure.cxx_stdlib

    variant ld64_97 conflicts ld64_127 ld64_236 ld64_274 ld64_xcode description {Use ld64-97 as the default linker (last version that works on Tiger)} {}
    variant ld64_127 conflicts ld64_97 ld64_236 ld64_274 ld64_xcode description {Use ld64-127 as the default linker (last version that supports ppc and works on Leopard)} {}
    variant ld64_236 conflicts ld64_97 ld64_127 ld64_274 ld64_xcode description {Use ld64-236 as the default linker (last version that builds against OS X's libstdc++)} {}
    variant ld64_274 conflicts ld64_97 ld64_127 ld64_236 ld64_xcode description {Use ld64-274 as the default linker (last version that builds without libtapi support)} {}
    variant ld64_xcode conflicts ld64_97 ld64_127 ld64_236 ld64_274 description {Use ld64-xcode as the default linker (version provided by the selected Xcode.app toolchain)} {}

    if {![variant_isset ld64_97] && ![variant_isset ld64_127] && ![variant_isset ld64_236] && ![variant_isset ld64_274] && ![variant_isset ld64_xcode]} {
        if {${os.major} < 9} {
            default_variants +ld64_97
        } elseif {${os.major} < 11} {
            # os versions < 11 can't build newer ld64 versions with their stock xcode compilers
            # so need to call in clang-9.0 and that has a runtime dep on ld64 (circular)
            default_variants +ld64_127
         } elseif {${os.major} < 16} {
            # os versions < 16 can't build libtapi or newer ld64 versions with their stock xcode compilers
            # so need to call in clang-9.0 and that has a runtime dep on ld64 (circular)
            default_variants +ld64_274
        }
    }

    if {[vercmp $xcodeversion 11.0] >= 0} {
        default_variants +ld64_xcode
    }

    if {[variant_isset ld64_97]} {
        set ld64_ver 97
    } elseif {[variant_isset ld64_127]} {
        set ld64_ver 127
    } elseif {[variant_isset ld64_236]} {
        set ld64_ver 236
    } elseif {[variant_isset ld64_274]} {
        set ld64_ver 274
    } elseif {[variant_isset ld64_xcode]} {
        set ld64_ver xcode
    } else {
        set ld64_ver latest
    }

    depends_run         port:ld64-${ld64_ver}

    destroot {
        ln -s ld-${ld64_ver} ${destroot}${prefix}/bin/ld

        file mkdir ${destroot}${prefix}/libexec/ld64
        ln -s ld-${ld64_ver} ${destroot}${prefix}/libexec/ld64/ld

        if {![variant_isset ld64_xcode]} {
            ln -s dyldinfo-${ld64_ver} ${destroot}${prefix}/bin/dyldinfo
            ln -s machocheck-${ld64_ver} ${destroot}${prefix}/bin/machocheck
            ln -s ObjectDump-${ld64_ver} ${destroot}${prefix}/bin/ObjectDump
            ln -s rebase-${ld64_ver} ${destroot}${prefix}/bin/rebase
            ln -s unwinddump-${ld64_ver} ${destroot}${prefix}/bin/unwinddump
        }
    }
} elseif {${subport} eq "${name}-xcode"} {
    distfiles
    use_configure no
    build {}
    destroot {
        file mkdir ${destroot}${prefix}/libexec/ld64
        xinstall -m 755 ${filespath}/ld-xcode ${destroot}${prefix}/libexec/ld64/ld-xcode
        ln -s ../libexec/ld64/ld-xcode ${destroot}${prefix}/bin/ld-xcode
    }
} else {
    distfiles           dyld-${dyld_version}.tar.gz ${name}-${version}.tar.gz

    depends_build       path:include/mach-o/arm/reloc.h:libmacho-headers

    if {[string match macports-clang-* ${configure.compiler}]} {
        # For a new enough install_name_tool
        depends_build-append port:cctools
    }

    set all_llvm_variants {llvm50 llvm60 llvm70 llvm80 llvm90 llvmdev}
    if {${os.major} < 10} {
        lappend all_llvm_variants llvm33
    }
    if {${os.major} < 12} {
        lappend all_llvm_variants llvm34
    }
    if {${os.major} < 14} {
        lappend all_llvm_variants llvm37
    }
    array set llvm_variant_version {llvm33 3.3 llvm34 3.4 llvm37 3.7 llvm50 5.0 llvm60 6.0 llvm70 7.0 llvm80 8.0 llvm90 9.0 llvmdev devel}
    set llvm_version {}

    foreach variantname $all_llvm_variants {
        set this_llvm_version $llvm_variant_version($variantname)
        variant $variantname conflicts {*}[ldelete $all_llvm_variants $variantname] description "Use llvm-${this_llvm_version} for libLTO" "
            set llvm_version        $this_llvm_version
            depends_lib-append      port:llvm-${this_llvm_version}
        "
    }

    proc some_llvm_variant_set {} {
        global all_llvm_variants
        foreach variantname $all_llvm_variants {
            if {[variant_isset $variantname]} {
                return yes
            }
        }
        return no
    }

    # We don't set llvmXX as the default variant on Tiger because it would introduce a
    # dependency cycle as llvm requires apple-gcc42 and ld64 to build correctly.  Users
    # wanting LTO support in ld64 on Tiger can install the +llvm variant after llvm
    # has been installed.

    if {![some_llvm_variant_set]} {
        if {${os.major} == 12 || ${os.major} == 13} {
            # Using llvm-3.7 to break a dependency cycle (https://trac.macports.org/ticket/53138)
            default_variants +llvm37
        } elseif {${os.major} == 10 || ${os.major} == 11} {
            default_variants +llvm34
        } elseif {${os.major} == 9} {
            # Using llvm-3.3 to break dependency cycle (https://trac.macports.org/ticket/52091)
            default_variants +llvm33
        }

        if {![some_llvm_variant_set] && ${os.major} >= 9} {
            default_variants +llvm90
        }

        if {![some_llvm_variant_set] && ${os.major} >= 9} {
            pre-fetch {
                ui_error "Your platform cannot be configured without LTO support in ld64.  Please enable one of the llvmXX variants, and try again."
                return -code error "Your platform cannot be configured without LTO support in ld64.  Please enable one of the llvmXX variants, and try again."
            }
        }
    }

    if {${os.platform} eq "darwin" && ${os.major} < 11 && ${subport} eq "ld64-127" && [variant_isset llvm34]} {
        # This port is used by clang-3.4 to bootstrap libcxx.
        configure.cxx_stdlib    libstdc++
    }

    if {${os.major} < 10} {
        # Leopard and earlier didn't have libunwind.h in the SDK
        depends_build-append    path:include/libunwind.h:libunwind-headers
    }

    compiler.blacklist-append gcc-4.0

    pre-fetch {
        if {${os.major} < 9} {
            if {${llvm_version} != ""} {
                if {![file exists ${prefix}/bin/llvm-config-mp-${llvm_version}]} {
                    ui_error "You must first install ld64 without llvm support to build llvm.  After llvm is installed, you can reinstall ld64 with the llvm variant."
                    return -code error "You must first install ld64 without llvm support to build llvm.  After llvm is installed, you can reinstall ld64 with the llvm variant."
                }
            }
        }
    }

    post-extract {
        file copy ${filespath}/${makefile} ${worksrcpath}/Makefile
    }

    post-patch {
        reinplace "s|@@VERSION@@|${version}|g" \
            ${worksrcpath}/src/ld/Options.cpp

        if {${os.major} < 9} {
            # No CommonCrypto, use openssl
            reinplace "s:<CommonCrypto/CommonDigest.h>:<openssl/md5.h>:" \
                ${worksrcpath}/src/ld/MachOWriterExecutable.hpp
            reinplace "s:CC_MD5:MD5:" \
                ${worksrcpath}/src/ld/MachOWriterExecutable.hpp

            reinplace "s:-Wl,-exported_symbol,__mh_execute_header::g" \
                ${worksrcpath}/Makefile

            foreach reg {cr ctr eax ebp ebx ecx edi edx eip esi esp lr mq r0 r1 r10 r11 r12 r13 r14 r15 r16 r17 r18 r19 r2 r20 r21 r22 r23 r24 r25 r26 r27 r28 r29 r3 r30 r31 r4 r5 r6 r7 r8 r9 rax rbp rbx rcx rdi rdx rip rsi rsp srr0 srr1 vrsave xer} {
                reinplace "s|__${reg}|${reg}|g" ${worksrcpath}/src/ld/parsers/libunwind/Registers.hpp
            }
        }

        foreach header [glob ${workpath}/dyld-${dyld_version}/include/{,*/}*.h] {
            if {${os.major} < 9} {
                reinplace "s|__MAC_10_5|__MAC_NA|g" ${header}
                reinplace "s|AVAILABLE_MAC_OS_X_VERSION_10_\\(.\\)_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_5|AVAILABLE_MAC_OS_X_VERSION_10_\\1_AND_LATER|" ${header}
                reinplace "s|AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER|__attribute__((unavailable))|g" ${header}

                reinplace "s|Availability.h|AvailabilityMacros.h|g" ${header}
                reinplace "s|__OSX_AVAILABLE_STARTING(__MAC_NA,.*)|__attribute__((unavailable))|g" ${header}
                reinplace "s|__OSX_AVAILABLE_BUT_DEPRECATED(__MAC_NA,.*)|__attribute__((unavailable))|g" ${header}
                reinplace "s|__OSX_AVAILABLE_STARTING(__MAC_10_\\(\[^,\]*\\),.*)|AVAILABLE_MAC_OS_X_VERSION_10_\\1_AND_LATER|g" ${header}
                reinplace "s|__OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_\\(\[^,\]*\\),__MAC_NA,.*)|AVAILABLE_MAC_OS_X_VERSION_10_\\1_AND_LATER|g" ${header}
                reinplace "s|__OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_\\(\[^,\]*\\),__MAC_10_\\(\[^,\]*\\),.*)|AVAILABLE_MAC_OS_X_VERSION_10_\\1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_\\2|g" ${header}
            }
        }

        system "cd ${workpath}/dyld-${dyld_version} && patch -p0 < ${filespath}/dyld-655-no-blocks.patch"

        # what is in this patch might be done as reinplaces, as above.
        system "cd ${workpath}/dyld-${dyld_version} && patch -p0 < ${filespath}/dyld-655-availability.diff"
    }

    configure {
        system "cd ${worksrcpath} && ${build.cmd} src/ld/configure.h"
    }

    set cxx_stdlibflags {}
    if {[string match *clang* ${configure.cxx}]} {
        set cxx_stdlibflags -stdlib=${configure.cxx_stdlib}

        # On Leopard, we want to make sure we pick up our newer libunwind (in libc++abi.dylib) instead of the one in libgcc_s.10.5.dylib
        if {${configure.cxx_stdlib} eq "libc++"} {
            configure.ldflags-append -lc++abi
        }
    }

    configure.cppflags-append -I${workpath}/dyld-${dyld_version}/include

    build.args \
        CC="${configure.cc}" \
        CXX="${configure.cxx}" \
        OTHER_CPPFLAGS="${configure.cppflags}" \
        OTHER_CFLAGS="${configure.cflags} [get_canonical_archflags cc]" \
        OTHER_CXXFLAGS="${configure.cxxflags} ${cxx_stdlibflags} [get_canonical_archflags cxx]" \
        OTHER_LDFLAGS="${configure.ldflags} ${cxx_stdlibflags} [get_canonical_archflags ld]"

    destroot.args \
        PREFIX=${prefix}

    pre-build {
        if {${llvm_version} != ""} {
            build.args-append LLVM_CONFIG=${prefix}/bin/llvm-config-mp-${llvm_version}
        }

        if {${os.major} < 9} {
            # use full library path to prevent opportunistic linking to MP openssl
            # see https://trac.macports.org/ticket/57016
            build.args-append OTHER_LDFLAGS_LD64=/usr/lib/libcrypto.dylib
        }
    }

    post-destroot {
        # ${prefix}/bin/ld will always use the llvm we built against
        # ${prefix}/libexec/ld64/ld uses relative linking for use with the llvm ports
        file mkdir ${destroot}${prefix}/libexec/ld64
        file copy ${destroot}${prefix}/bin/ld ${destroot}${prefix}/libexec/ld64/ld-${ld64_ver}

        file rename ${destroot}${prefix}/bin/dyldinfo ${destroot}${prefix}/bin/dyldinfo-${ld64_ver}
        file rename ${destroot}${prefix}/bin/ld ${destroot}${prefix}/bin/ld-${ld64_ver}
        file rename ${destroot}${prefix}/bin/machocheck ${destroot}${prefix}/bin/machocheck-${ld64_ver}
        file rename ${destroot}${prefix}/bin/ObjectDump ${destroot}${prefix}/bin/ObjectDump-${ld64_ver}
        file rename ${destroot}${prefix}/bin/rebase ${destroot}${prefix}/bin/rebase-${ld64_ver}
        file rename ${destroot}${prefix}/bin/unwinddump ${destroot}${prefix}/bin/unwinddump-${ld64_ver}

        # we can only install one set of man pages from one subport without file collisions
        # so install only the latest man pages, good for most installations
        if {![string match ${subport} "${name}-latest"]} {
            delete {*}[glob ${destroot}${prefix}/share/man/man1/*]
        }

        # this is not presently working as we need to account for the name.1 -> name-${ld64_ver}.1 syntax
        #       fs-traverse path ${destroot}${prefix}/share/man/man1 {
        #           if {[file isfile ${path}]} {
        #               file rename ${path} ${path}-${ld64_ver}
        #           }
        #       }

        if {${llvm_version} != ""} {
            system "install_name_tool -change ${prefix}/libexec/llvm-${llvm_version}/lib/libLTO.dylib \
                    @executable_path/../lib/libLTO.dylib ${destroot}${prefix}/libexec/ld64/ld-${ld64_ver}"
            system "install_name_tool -change @rpath/libLTO.dylib \
                    @executable_path/../lib/libLTO.dylib ${destroot}${prefix}/libexec/ld64/ld-${ld64_ver}"
        }
    }
}

# this should be in base
if {"${configure.sdkroot}" eq ""} {
    configure.sdkroot "/"
}

# for a universal build, we need to run the tests for each arch and clean between
if {![variant_isset universal]} {
    test.run     yes
    test.dir     ${workpath}/ld64-${version}/unit-tests/test-cases
    test.cmd     perl ../bin/make-recursive.pl \
                 CXX="${configure.cxx} -arch ${configure.build_arch}"  \
                 CC="${configure.cc} -arch ${configure.build_arch}" \
                 BUILT_PRODUCTS_DIR="${workpath}/ld64-${version}" \
                 LD_SYSROOT="${configure.sdkroot}" \
                 LD="${workpath}/ld64-${version}/ld" \
                 OSX_SDK="${configure.sdkroot}" \
                 ARCH="${configure.build_arch}" \
                 | perl ../bin/result-filter.pl
    test.target
}

livecheck.type          none
