import hudson.triggers.TimerTrigger;

// ============================================================================
// Global properties
// ============================================================================

// Start build every night between 1:00-4:59
// Keep only last 10 builds
properties([
    pipelineTriggers(
        [cron(env.BRANCH_NAME == 'master' ? 'H H(1-4) * * *' : '')]
    ),
    buildDiscarder(
        logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10')
    )
])

// Checks whether the current build was triggered by cron job.
// We need this to check, whether we want to trigger a deployment
// step for a stable nightly build.
def is_triggered_by_cron()
{
    for (cause in currentBuild.rawBuild.getCauses())
    {
        if (cause instanceof TimerTrigger.TimerTriggerCause)
            return true
        return false
    }
}

// Check if this build is a nightly build (only master supports building periodically)
Boolean is_nightly = is_triggered_by_cron()

// ============================================================================
// Define test functions
// ============================================================================

/* Executes unit tests
 * \param slave_name   The name of the slave this test is executed on; String
 * \param compiler     The name of the compiler to use; String
 * \param build_type   The build type to use [Debug, Release]; String
 * \param has_cereal   Whether to use cereal; Boolean
 */
def run_unit_tests(slave_name, compiler, build_type, has_cereal)
{
    def build_name
    def cxx
    def workspace
    def platform
    def checkout_dir

    stage('initialise')
    {
        workspace = pwd()
        echo workspace
        // Clean the workspace before building.
        deleteDir()

        build_name = "$slave_name $compiler $build_type $env.BRANCH_NAME Build-$env.BUILD_NUMBER"
        platform = sh(script: 'uname -s', returnStdout: true).trim()

        // sh 'git clone https://github.com/seqan/seqan3-infrastructure.git seqan3-infra'
        cxx = sh(script: "which $compiler", returnStdout: true).trim()

        // Perform checkout in specific location.
        // The relative folder structure is important for the ctest script.
        dir('checkout')
        {
            checkout scm
            checkout_dir = pwd()
        }
    }

    stage('configure')
    {
        dir("$workspace/build")
        {
            sh "cmake $checkout_dir -DCMAKE_BUILD_TYPE=$build_type -DCMAKE_CXX_COMPILER=$compiler -DCMAKE_CXX_FLAGS=\"-Werror -Wno-missing-field-initializers -DFULL_TEST_SUITE\" -DSDSL_CEREAL=$has_cereal"
        }
    }

    stage('build tests')
    {
        dir("$workspace/build")
        {
            sh "make sdsl_test_targets -j 4 -k"
        }
    }

    stage('build examples')
    {
        dir("$workspace/build")
        {
            sh "make sdsl_examples -j 4 -k"
        }
    }

    stage('build tutorials')
    {
        dir("$workspace/build")
        {
            sh "make sdsl_tutorials -j 4 -k"
        }
    }

    stage('run tests')
    {
        dir("$workspace/build")
        {
            def ctest_args = ""
            if (build_type == "Debug")
                ctest_args = "-E \"faust|moby\"" // turn off slow tests using faust.txt and moby.int
            sh "ctest --output-on-failure -j 4 $ctest_args"
        }
    }
}

/* Defines unit tests
 * \param tasks        The task map to add the job to; Map
 * \param compiler     The name of the compiler to use; String
 * \param build_type   The build type to use [Debug, Release]; String
 * \param has_cereal   Whether to use cereal; Boolean
 */
def define_unit_test(tasks, compiler, build_type, has_cereal)
{
    def label = "ubuntu_${compiler}_${build_type}_$has_cereal"
    label = label.replace("++", "cc") // currently CMake of SDSL fails since it uses the path in a regex string and ++ characters are not escaped
    tasks["ubuntu-${compiler}-${build_type}-${has_cereal}"] =
    {
        node("ubuntu && $compiler")
        {
            // For docker nodes we need to define the workspace explicitly, as they use a mounted
            // volume on the filesystem which is shared by all docker instances. Since the docker executor
            // doesn't know the other running instances it cannot locking the workspace.
            ws("$env.WORKSPACE/ws/$label")
            {
                stage('install')
                {
                    sh """
                        sudo apt-get update
                        sudo apt-get install -y $compiler cmake git clang-format
                    """

                }
                run_unit_tests(env.DOCKER_SLAVE_NAME,
                               compiler,
                               build_type,
                               has_cereal)
            }
        }
    }
}

// ============================================================================
// Define unit test matrix
// ============================================================================

// Configure unit tests. Add platforms here but adapt the inner loop of the matrix setup to call instantiate and
// setup the right agent.
def axis_agent = ["ubuntu"]
def axis_compiler = ["g++-4.9", "g++-5", "g++-6", "g++-7", "g++-8"] // "clang++"
def tasks = [:]

// The following for loop defines the compiler matrix for the unit tests.
// First iterate over all supported compiler versions
// If the compilers do not match on the respective platform either use a different scope to
// iterate over them or define some kind of map to point to the right compiler name per platform.
for(int i = 0; i < axis_compiler.size(); ++i)
{
    define_unit_test(tasks,
                     axis_compiler[i],
                     "Release",
                     "0")
}

// Debug build
define_unit_test(tasks,
                 "g++-8",
                 "Debug",
                 "0")

// Cereal build
define_unit_test(tasks,
                 "g++-7",
                 "Release",
                 "1")

/* TODO sanitizer builds
 * g++-7 sanitizer options
 */

/* TODO doc builds
 * user develop
 */

/* TODO coverage build
 * g++-8 debug mode
 */

/* TODO valgrind build
 *
 */

/* TODO performance build
 * g++-7, g++-8 [, g++-9] [, clang-concpets]
 */

// ============================================================================
// Execute build matrix
// ============================================================================

// This is the main stage that executes all jobs registered in the tasks map
// All steps are executed in parallel. If the contention on the agents are to high
// consider moving some tasks out into another stage that runs after this one.
stage ("matrix")
{
    parallel tasks
}
