// -*- groovy -*-
//
// Copyright (c) 2022-2023 Amazon.com, Inc. or its affiliates.  All rights
//                         reserved.
// Copyright (c) 2022-2023 Joe Downs.  All rights reserved.
// Copyright (c) 2023      Cisco Systems, Inc.  All rights reserved.
// $COPYRIGHT$
//
// Additional copyrights may follow
//
// $HEADER$
// SPDX-License-Identifier: BSD-3-Clause-Open-MPI
//
// Build an Open MPI Pull Request
//
//
// WORKSPACE Layout:
//   ompi/                 Open MPI source tree

// We if we push changes to a PR, we don't need to keep old jobs running, so
// we'll use the milestone step in Jenkins. Using an example from
// https://stackoverflow.com/questions/40760716/jenkins-abort-running-build-if-new-one-is-started:
//
// - Build 1 runs and creates milestone 1.
// - While build 1 is running, build 2 fires. It has milestone 1 and milestone
//   2. It passes milestone 1, which causes build 1 to abort.
def buildNumber = env.BUILD_NUMBER as int
if (buildNumber > 1) {
    milestone(buildNumber - 1)
}
milestone(buildNumber)

// Add build description linking back to PR. This is redundant to the "GitHub"
// link on the Pull Request page, but the Build page does not have a direct link
// back to the PR. The "Details" link at the bottom of the GitHub PR page brings
// you to the Jenkins Build page, so we're adding the link back to the GitHub PR
// page.
if (env.CHANGE_URL) {
    currentBuild.description = "This is a build of <a href=\"${CHANGE_URL}\"}\">Open MPI PR #${CHANGE_ID}</a>"
} else {
    currentBuild.description = "Build of ${BRANCH_NAME}"
}

check_stages = prepare_check_stages()
println("Initialized Pipeline")

// Today, we only expect to have one stage (do everything), but allow that
// we may split build and test stages in the future.
for (check_stage in check_stages) {
    parallel(check_stage)
}

println('Tests Completed')

// Returns a list of build stages ("build Open MPI", "Build Tests", etc.),
// although currently we only support the one stage of "everything", where each
// build stage is a map of different configurations to test.
def prepare_check_stages() {
    def configure_options = [
	"--disable-dlopen",
	"--disable-oshmem",
	"--enable-builtin-atomic",
	"--enable-ipv6",
	"--enable-mca-dso"
    ]
    // explicitly list the newest and oldest versions of GCC that are included
    // in the distros listed below to try and give us good version coverage.
    def compilers = [
	"gcc9",
	"gcc15",
	"clang11",
	"clang21"
    ]
    def platforms = [
	"rhel8",
	"rhel9",
	"rhel10",
	"amazon_linux_2023-arm64",
	"amazon_linux_2023-x86_64",
	"freebsd_15",
	"ubuntu_24.04-arm64",
	"ubuntu_24.04-x86_64",
	"ubuntu_26.04-arm64",
	"ubuntu_26.04-x86_64"
    ]
    def check_stages_list = []

    // Build everything stage
    def build_parallel_map = [:]
    for (platform in platforms) {
        def name = "Platform: ${platform}".replaceAll("-", "")
        build_parallel_map.put(name, prepare_build(name, platform, ""))
    }

    for (compiler in compilers) {
        def name = "Compiler: ${compiler}".replaceAll("-", "")
        build_parallel_map.put(name, prepare_build(name, compiler, "--compiler \\\"${compiler}\\\""))
    }

      for (configure_option in configure_options) {
        def name = "Configure: ${configure_option}".replaceAll("-", "")
        build_parallel_map.put(name, prepare_build(name, "amazon_linux_2", "--configure-args \\\"${configure_option}\\\""))
    }

    build_parallel_map.put("distcheck", prepare_build("distcheck", "tarball_build", "--distcheck"))
    build_parallel_map.put("vpath", prepare_build("vpath", "", "--build-dir ompi-build"))

    check_stages_list.add(build_parallel_map)

    return check_stages_list
}

def prepare_build(build_name, label, build_arg) {
    return {
        stage("${build_name}") {
            node(label) {
                // Checkout into ompi-source instead of the top of the
                // workspace, so that we have room in the workspace to setup a
                // vpath build.
                dir ('ompi-source') {
                    checkout(changelog: false, poll: false, scm: scm)
                }

                // If pr-builder.sh fails, the sh step will throw an exception,
                // which we catch so that the job doesn't abort and continues on
                // to other steps - such as cleanup. Because we catch the
                // exception, we need to tell Jenkins the overall job has
                // failed.
                try {
                    sh "/bin/bash -x ompi-source/.ci/community-jenkins/pr-builder.sh ${build_arg} --source-dir ompi-source"
                } catch (Exception e) {
                    currentBuild.result = "FAILURE"
                }
                cleanWs(notFailBuild: true)
            }
        }
    }
}
