using BinaryBuilder, Pkg
using Base.BinaryPlatforms

const YGGDRASIL_DIR = "../../.."
include(joinpath(YGGDRASIL_DIR, "fancy_toys.jl"))
include(joinpath(YGGDRASIL_DIR, "platforms", "llvm.jl"))
include("../common.jl")

name = "LLVM_utils_unified"
version = v"0.1.1"
llvm_full_versions = [
    v"15.0.7+12",               # Julia 1.10
    v"16.0.6+6",                # Julia 1.11
    # v"17.0.6+5",
    v"18.1.7+5",                # Julia 1.12
    # v"19.1.7+2",
    v"20.1.8+0",                # Julia 1.13
    v"21.1.8+0",
    v"22.1.8+0",                # Julia 1.15
]

augment_platform_block = """
    using Base.BinaryPlatforms

    $(LLVM.augment)

    augment_platform!(platform::Platform) = augment_llvm!(platform)
"""

function normalize_symbol(str)
    str = replace(str, "-"=>"_")
    if startswith(str,"ll") || str == "opt"
        return Symbol(str)
    else
        return Symbol("llvm_"*str)
    end
end

# The list of tools shipped by upstream LLVM evolves between releases, so we
# key a `tools_list` by LLVM major version. Derived by inspecting the `tools/`
# directory of the corresponding LLVM_jll release, filtering out the clang-side
# scripts/helpers, non-executable entries (e.g. `lit` is a directory), and the
# tools stripped by `llvm_utils_script` (`c-index-test`, `llvm-exegesis`).
const tools_lists = Dict(
  15 => [
    "bugpoint",
    "count",
    "diagtool",
    "FileCheck",
    "find-all-symbols",
    "llc",
    "lli",
    "lli-child-target",
    "llvm-addr2line",
    "llvm-ar",
    "llvm-as",
    "llvm-bcanalyzer",
    "llvm-bitcode-strip",
    "llvm-cat",
    "llvm-cfi-verify",
    "llvm-cov",
    "llvm-c-test",
    "llvm-cvtres",
    "llvm-cxxdump",
    "llvm-cxxfilt",
    "llvm-cxxmap",
    "llvm-debuginfod",
    "llvm-debuginfod-find",
    "llvm-diff",
    "llvm-dis",
    "llvm-dlltool",
    "llvm-dwarfdump",
    "llvm-dwarfutil",
    "llvm-dwp",
    "llvm-extract",
    "llvm-gsymutil",
    "llvm-ifs",
    "llvm-install-name-tool",
    "llvm-jitlink",
    "llvm-jitlink-executor",
    "llvm-lib",
    "llvm-libtool-darwin",
    "llvm-link",
    "llvm-lipo",
    "llvm-lto",
    "llvm-lto2",
    "llvm-mc",
    "llvm-mca",
    "llvm-ml",
    "llvm-modextract",
    "llvm-mt",
    "llvm-nm",
    "llvm-objcopy",
    "llvm-objdump",
    "llvm-opt-report",
    "llvm-otool",
    "llvm-pdbutil",
    "llvm-PerfectShuffle",
    "llvm-profdata",
    "llvm-profgen",
    "llvm-ranlib",
    "llvm-rc",
    "llvm-readelf",
    "llvm-readobj",
    "llvm-reduce",
    "llvm-remark-size-diff",
    "llvm-rtdyld",
    "llvm-sim",
    "llvm-size",
    "llvm-split",
    "llvm-stress",
    "llvm-strings",
    "llvm-strip",
    "llvm-symbolizer",
    "llvm-tapi-diff",
    "llvm-tblgen",
    "llvm-tli-checker",
    "llvm-undname",
    "llvm-windres",
    "llvm-xray",
    "modularize",
    "not",
    "obj2yaml",
    "opt",
    "pp-trace",
    "sancov",
    "sanstats",
    "split-file",
    "UnicodeNameMappingGenerator",
    "verify-uselistorder",
    "yaml2obj",
    "yaml-bench",
  ],
  16 => [
    "amdgpu-arch",
    "bugpoint",
    "count",
    "diagtool",
    "FileCheck",
    "find-all-symbols",
    "llc",
    "lli",
    "lli-child-target",
    "llvm-addr2line",
    "llvm-ar",
    "llvm-as",
    "llvm-bcanalyzer",
    "llvm-bitcode-strip",
    "llvm-cat",
    "llvm-cfi-verify",
    "llvm-cov",
    "llvm-c-test",
    "llvm-cvtres",
    "llvm-cxxdump",
    "llvm-cxxfilt",
    "llvm-cxxmap",
    "llvm-debuginfo-analyzer",
    "llvm-debuginfod",
    "llvm-debuginfod-find",
    "llvm-diff",
    "llvm-dis",
    "llvm-dlltool",
    "llvm-dwarfdump",
    "llvm-dwarfutil",
    "llvm-dwp",
    "llvm-extract",
    "llvm-gsymutil",
    "llvm-ifs",
    "llvm-install-name-tool",
    "llvm-jitlink",
    "llvm-jitlink-executor",
    "llvm-lib",
    "llvm-libtool-darwin",
    "llvm-link",
    "llvm-lipo",
    "llvm-lto",
    "llvm-lto2",
    "llvm-mc",
    "llvm-mca",
    "llvm-ml",
    "llvm-modextract",
    "llvm-mt",
    "llvm-nm",
    "llvm-objcopy",
    "llvm-objdump",
    "llvm-opt-report",
    "llvm-otool",
    "llvm-pdbutil",
    "llvm-PerfectShuffle",
    "llvm-profdata",
    "llvm-profgen",
    "llvm-ranlib",
    "llvm-rc",
    "llvm-readelf",
    "llvm-readobj",
    "llvm-reduce",
    "llvm-remark-size-diff",
    "llvm-remarkutil",
    "llvm-rtdyld",
    "llvm-sim",
    "llvm-size",
    "llvm-split",
    "llvm-stress",
    "llvm-strings",
    "llvm-strip",
    "llvm-symbolizer",
    "llvm-tapi-diff",
    "llvm-tblgen",
    "llvm-tli-checker",
    "llvm-undname",
    "llvm-windres",
    "llvm-xray",
    "modularize",
    "not",
    "nvptx-arch",
    "obj2yaml",
    "opt",
    "pp-trace",
    "sancov",
    "sanstats",
    "split-file",
    "UnicodeNameMappingGenerator",
    "verify-uselistorder",
    "yaml2obj",
    "yaml-bench",
  ],
  18 => [
    "amdgpu-arch",
    "bugpoint",
    "count",
    "diagtool",
    "FileCheck",
    "find-all-symbols",
    "llc",
    "lli",
    "lli-child-target",
    "llvm-addr2line",
    "llvm-ar",
    "llvm-as",
    "llvm-bcanalyzer",
    "llvm-bitcode-strip",
    "llvm-cat",
    "llvm-cfi-verify",
    "llvm-cov",
    "llvm-c-test",
    "llvm-cvtres",
    "llvm-cxxdump",
    "llvm-cxxfilt",
    "llvm-cxxmap",
    "llvm-debuginfo-analyzer",
    "llvm-debuginfod",
    "llvm-debuginfod-find",
    "llvm-diff",
    "llvm-dis",
    "llvm-dlltool",
    "llvm-dwarfdump",
    "llvm-dwarfutil",
    "llvm-dwp",
    "llvm-extract",
    "llvm-gsymutil",
    "llvm-ifs",
    "llvm-install-name-tool",
    "llvm-jitlink",
    "llvm-jitlink-executor",
    "llvm-lib",
    "llvm-libtool-darwin",
    "llvm-link",
    "llvm-lipo",
    "llvm-lto",
    "llvm-lto2",
    "llvm-mc",
    "llvm-mca",
    "llvm-ml",
    "llvm-modextract",
    "llvm-mt",
    "llvm-nm",
    "llvm-objcopy",
    "llvm-objdump",
    "llvm-opt-report",
    "llvm-otool",
    "llvm-pdbutil",
    "llvm-PerfectShuffle",
    "llvm-profdata",
    "llvm-profgen",
    "llvm-ranlib",
    "llvm-rc",
    "llvm-readelf",
    "llvm-readobj",
    "llvm-readtapi",
    "llvm-reduce",
    "llvm-remarkutil",
    "llvm-rtdyld",
    "llvm-sim",
    "llvm-size",
    "llvm-split",
    "llvm-stress",
    "llvm-strings",
    "llvm-strip",
    "llvm-symbolizer",
    "llvm-tblgen",
    "llvm-tli-checker",
    "llvm-undname",
    "llvm-windres",
    "llvm-xray",
    "modularize",
    "not",
    "nvptx-arch",
    "obj2yaml",
    "opt",
    "pp-trace",
    "sancov",
    "sanstats",
    "split-file",
    "UnicodeNameMappingGenerator",
    "verify-uselistorder",
    "yaml2obj",
    "yaml-bench",
  ],
  20 => [
    "amdgpu-arch",
    "bugpoint",
    "count",
    "diagtool",
    "FileCheck",
    "find-all-symbols",
    "llc",
    "lli",
    "lli-child-target",
    "llvm-addr2line",
    "llvm-ar",
    "llvm-as",
    "llvm-bcanalyzer",
    "llvm-bitcode-strip",
    "llvm-cat",
    "llvm-cfi-verify",
    "llvm-cgdata",
    "llvm-cov",
    "llvm-c-test",
    "llvm-ctxprof-util",
    "llvm-cvtres",
    "llvm-cxxdump",
    "llvm-cxxfilt",
    "llvm-cxxmap",
    "llvm-debuginfo-analyzer",
    "llvm-debuginfod",
    "llvm-debuginfod-find",
    "llvm-diff",
    "llvm-dis",
    "llvm-dlltool",
    "llvm-dwarfdump",
    "llvm-dwarfutil",
    "llvm-dwp",
    "llvm-extract",
    "llvm-gsymutil",
    "llvm-ifs",
    "llvm-install-name-tool",
    "llvm-jitlink",
    "llvm-jitlink-executor",
    "llvm-lib",
    "llvm-libtool-darwin",
    "llvm-link",
    "llvm-lipo",
    "llvm-lto",
    "llvm-lto2",
    "llvm-mc",
    "llvm-mca",
    "llvm-ml",
    "llvm-modextract",
    "llvm-mt",
    "llvm-nm",
    "llvm-objcopy",
    "llvm-objdump",
    "llvm-opt-report",
    "llvm-otool",
    "llvm-pdbutil",
    "llvm-PerfectShuffle",
    "llvm-profdata",
    "llvm-profgen",
    "llvm-ranlib",
    "llvm-rc",
    "llvm-readelf",
    "llvm-readobj",
    "llvm-readtapi",
    "llvm-reduce",
    "llvm-remarkutil",
    "llvm-rtdyld",
    "llvm-sim",
    "llvm-size",
    "llvm-split",
    "llvm-stress",
    "llvm-strings",
    "llvm-strip",
    "llvm-symbolizer",
    "llvm-tblgen",
    "llvm-tli-checker",
    "llvm-undname",
    "llvm-windres",
    "llvm-xray",
    "modularize",
    "not",
    "nvptx-arch",
    "obj2yaml",
    "opt",
    "pp-trace",
    "reduce-chunk-list",
    "sancov",
    "sanstats",
    "split-file",
    "UnicodeNameMappingGenerator",
    "verify-uselistorder",
    "yaml2obj",
    "yaml-bench",
  ],
  21 => [
    "amdgpu-arch",
    "bugpoint",
    "count",
    "diagtool",
    "FileCheck",
    "find-all-symbols",
    "llc",
    "lli",
    "lli-child-target",
    "llvm-addr2line",
    "llvm-ar",
    "llvm-as",
    "llvm-bcanalyzer",
    "llvm-bitcode-strip",
    "llvm-cat",
    "llvm-cfi-verify",
    "llvm-cgdata",
    "llvm-cov",
    "llvm-c-test",
    "llvm-ctxprof-util",
    "llvm-cvtres",
    "llvm-cxxdump",
    "llvm-cxxfilt",
    "llvm-cxxmap",
    "llvm-debuginfo-analyzer",
    "llvm-debuginfod",
    "llvm-debuginfod-find",
    "llvm-diff",
    "llvm-dis",
    "llvm-dlltool",
    "llvm-dwarfdump",
    "llvm-dwarfutil",
    "llvm-dwp",
    "llvm-extract",
    "llvm-gsymutil",
    "llvm-ifs",
    "llvm-install-name-tool",
    "llvm-jitlink",
    "llvm-jitlink-executor",
    "llvm-lib",
    "llvm-libtool-darwin",
    "llvm-link",
    "llvm-lipo",
    "llvm-lto",
    "llvm-lto2",
    "llvm-mc",
    "llvm-mca",
    "llvm-ml",
    "llvm-ml64",
    "llvm-modextract",
    "llvm-nm",
    "llvm-objcopy",
    "llvm-objdump",
    "llvm-opt-report",
    "llvm-otool",
    "llvm-pdbutil",
    "llvm-PerfectShuffle",
    "llvm-profdata",
    "llvm-profgen",
    "llvm-ranlib",
    "llvm-rc",
    "llvm-readelf",
    "llvm-readobj",
    "llvm-readtapi",
    "llvm-reduce",
    "llvm-remarkutil",
    "llvm-rtdyld",
    "llvm-sim",
    "llvm-size",
    "llvm-split",
    "llvm-stress",
    "llvm-strings",
    "llvm-strip",
    "llvm-symbolizer",
    "llvm-tblgen",
    "llvm-test-mustache-spec",
    "llvm-tli-checker",
    "llvm-undname",
    "llvm-windres",
    "llvm-xray",
    "modularize",
    "not",
    "nvptx-arch",
    "obj2yaml",
    "offload-arch",
    "opt",
    "pp-trace",
    "reduce-chunk-list",
    "sancov",
    "sanstats",
    "split-file",
    "UnicodeNameMappingGenerator",
    "verify-uselistorder",
    "yaml2obj",
    "yaml-bench",
  ],
  22 => [
    "amdgpu-arch",
    "bugpoint",
    "count",
    "diagtool",
    "FileCheck",
    "find-all-symbols",
    "llc",
    "lli",
    "lli-child-target",
    "llvm-addr2line",
    "llvm-ar",
    "llvm-as",
    "llvm-bcanalyzer",
    "llvm-bitcode-strip",
    "llvm-cas",
    "llvm-cat",
    "llvm-cfi-verify",
    "llvm-cgdata",
    "llvm-cov",
    "llvm-c-test",
    "llvm-ctxprof-util",
    "llvm-cvtres",
    "llvm-cxxdump",
    "llvm-cxxfilt",
    "llvm-cxxmap",
    "llvm-debuginfo-analyzer",
    "llvm-debuginfod",
    "llvm-debuginfod-find",
    "llvm-diff",
    "llvm-dis",
    "llvm-dlltool",
    "llvm-dwarfdump",
    "llvm-dwarfutil",
    "llvm-dwp",
    "llvm-extract",
    "llvm-gsymutil",
    "llvm-ifs",
    "llvm-install-name-tool",
    "llvm-ir2vec",
    "llvm-jitlink",
    "llvm-jitlink-executor",
    "llvm-lib",
    "llvm-libtool-darwin",
    "llvm-link",
    "llvm-lipo",
    "llvm-lto",
    "llvm-lto2",
    "llvm-mc",
    "llvm-mca",
    "llvm-ml",
    "llvm-ml64",
    "llvm-modextract",
    "llvm-nm",
    "llvm-objcopy",
    "llvm-objdump",
    "llvm-offload-binary",
    "llvm-offload-wrapper",
    "llvm-opt-report",
    "llvm-otool",
    "llvm-pdbutil",
    "llvm-PerfectShuffle",
    "llvm-profdata",
    "llvm-profgen",
    "llvm-ranlib",
    "llvm-rc",
    "llvm-readelf",
    "llvm-readobj",
    "llvm-readtapi",
    "llvm-reduce",
    "llvm-remarkutil",
    "llvm-rtdyld",
    "llvm-sim",
    "llvm-size",
    "llvm-split",
    "llvm-stress",
    "llvm-strings",
    "llvm-strip",
    "llvm-symbolizer",
    "llvm-tblgen",
    "llvm-test-mustache-spec",
    "llvm-tli-checker",
    "llvm-undname",
    "llvm-windres",
    "llvm-xray",
    "modularize",
    "not",
    "nvptx-arch",
    "obj2yaml",
    "offload-arch",
    "opt",
    "pp-trace",
    "reduce-chunk-list",
    "sancov",
    "sanstats",
    "split-file",
    "UnicodeNameMappingGenerator",
    "verify-uselistorder",
    "yaml2obj",
    "yaml-bench",
  ],
)

# `configure_extraction` reads `tools_list` from the enclosing scope; we rebind
# it before each call below based on the LLVM major version being built.
tools_list = String[]

# determine exactly which tarballs we should build
builds = []
for llvm_assertions in (false, true), llvm_full_version in llvm_full_versions
    llvm_full_version >= v"15" || continue
    libllvm_version = llvm_full_version

    # pick the tools list appropriate for this LLVM version
    global tools_list = tools_lists[llvm_full_version.major]

    _, _, sources, script, platforms, products, dependencies =
        configure_extraction(ARGS, llvm_full_version, "LLVM_utils", libllvm_version;
                             assert=llvm_assertions, augmentation=true,
                             dont_dlopen=false)
    isempty(platforms) && continue
    # ignore the output version, as we want a unified JLL

    dependencies = map(dependencies) do dep
        # ignore the version of any LLVM dependency, as we'll automatically load
        # an appropriate version of LLVM_utils via platform augmentations
        # TODO: make this an argument to `configure_extraction`?
        if isa(dep, Dependency) && contains(dep.pkg.name, "LLVM")
            Dependency(dep.pkg.name; dep.platforms)
        else
            dep
        end
    end
    push!(builds, [name, version, sources, script, platforms, products, dependencies])
end

# don't allow `build_tarballs` to override platform selection based on ARGS.
# we handle that ourselves by calling `should_build_platform`
non_platform_ARGS = filter(arg -> startswith(arg, "--"), ARGS)

# `--register` should only be passed to the latest `build_tarballs` invocation
non_reg_ARGS = filter(arg -> arg != "--register", non_platform_ARGS)

for (i, build) in enumerate(builds)
    build_tarballs(i == lastindex(builds) ? non_platform_ARGS : non_reg_ARGS,
                   build...;
                   skip_audit=true, dont_dlopen=true, julia_compat="1.10",
                   augment_platform_block)
end

# rebuild trigger: 1
