/* * 86Box A hypervisor and IBM PC system emulator that specializes in * running old operating systems and software designed for IBM * PC systems and compatibles from 1981 through fairly recent * system designs based on the PCI bus. * * This file is part of the 86Box distribution. * * Jenkins build pipeline definition. * * * * Authors: RichardG, * * Copyright 2021 RichardG. */ def osArchs = [ 'Windows': ['32', '64'], 'Linux': ['x86', 'x86_64', 'arm32', 'arm64'] ] def archNames = [ '32': 'x86 (32-bit)', 'x86': 'x86 (32-bit)', '64': 'x64 (64-bit)', 'x86_64': 'x64 (64-bit)', 'arm32': 'ARM (32-bit)', 'arm64': 'ARM (64-bit)' ] def dynarecNames = [ 'ODR': 'Old Recompiler (recommended)', 'NDR': 'New Recompiler (beta)', 'NoDR': 'No Dynamic Recompiler' ] def dynarecArchs = [ '32': ['ODR', 'NDR'], 'x86': ['ODR', 'NDR'], '64': ['ODR', 'NDR'], 'x86_64': ['ODR', 'NDR'], 'arm32': ['NDR'], 'ARM32': ['NDR'], 'arm64': ['NDR'], 'ARM64': ['NDR'] ] def dynarecFlags = [ 'ODR': '-D NEW_DYNAREC=OFF', 'NDR': '-D NEW_DYNAREC=ON', 'NoDR': '-D DYNAREC=OFF' ] def dynarecSlugs = [ 'ODR': '', 'NDR': '-NDR', 'NoDR': '' ] def presets = [ 'Regular', 'Debug' ] def presetSlugs = [ 'Regular': '', 'Debug': '-Debug', 'Dev': '-Dev' ] def presetFlags = [ 'Regular': '--preset=regular', 'Debug': '--preset=debug', 'Dev': '--preset=experimental -D VNC=OFF' ] def anyFailure = false def gitClone() { /* Read git tag from environment or set the default one. */ if (env.GIT_COMMIT == null) env.GIT_COMMIT = 'master' println "[-] Using git tag [${env.GIT_COMMIT}]" /* Use stashes to avoid performing multiple clones. */ if (env.GIT_STASHED != 'true') { /* Perform clone/checkout. */ def scmVars = checkout scm: [$class: 'GitSCM', branches: [[name: env.GIT_COMMIT]], userRemoteConfigs: [[url: 'https://github.com/86Box/86Box.git']]] env.GIT_COMMIT = scmVars.GIT_COMMIT /* Stash data and mark it as stashed. */ stash name: 'git' env.GIT_STASHED = 'true' } else { /* Unstash data. */ unstash name: 'git' } } def removeDir(dir) { if (isUnix()) sh "rm -rf \"$dir\" || exit 0" else bat "if exist \"$dir\" rd /s /q \"$dir\" & exit /b 0" } def runBuild(args) { if (isUnix()) sh "chmod u+x \"$WORKSPACE/.ci/build.sh\" && exec \"$WORKSPACE/.ci/build.sh\" $args" else bat "C:\\msys64\\msys2_shell.cmd -msys2 -defterm -here -no-start -c 'exec \"\$(cygpath -u \\'%WORKSPACE%\\')/.ci/build.sh\" $args'" } pipeline { agent none environment { DISCORD_WEBHOOK_URL = credentials('discord-webhook-url') } options { disableConcurrentBuilds() quietPeriod(0) } parameters { string(name: 'BUILD_TYPE', defaultValue: 'beta', /* !!! CHANGE HERE !!! for build type */ description: "Build type to pass on to CMake. Don't change this, you should instead change the default value on .ci/Jenkinsfile") } stages { stage('Source Tarball') { agent none steps { script { /* Hack to extract the current HEAD commit from this build's git polling log. This avoids a race condition where HEAD changes in the time period between Jenkins polling the git repository and the first build node performing the first git clone once ready. (See issue JENKINS-20518) */ if (env.GIT_COMMIT == null) { /* This must run on the master node to read the polling log. */ node('master') { /* Ignore exceptions as this is not really critical. */ try { /* Switch to this build's directory. */ dir("${env.JENKINS_HOME}/jobs/${env.JOB_NAME}/builds/${env.BUILD_NUMBER}") { /* Parse polling log. */ def pollingLog = readFile file: 'polling.log' def match = pollingLog =~ /Latest remote head revision on [^ ]+ is: ([a-zA-Z0-9]+)/ if (match && match[0]) { env.GIT_COMMIT = match[0][1] println "[-] Read git tag [${env.GIT_COMMIT}] from polling log" } } } catch (e) {} } /* If the polling log parsing fails, perform a dummy git clone on any node. Not quite as fast as reading the polling log, but it works as a backup. */ if (env.GIT_COMMIT == null) { node { /* Ignore exceptions again as this is not really critical. */ try { gitClone() } catch (e) {} try { cleanWs() } catch (e) {} } } } /* Create source tarball. */ node('Linux') { try { /* Run git clone. */ gitClone() /* Switch to temp directory. */ dir(WORKSPACE_TMP) { /* Clean output directory of potential stale old builds. */ removeDir('output') /* Switch to output directory. */ dir('output') { /* Run source tarball creation process. */ def packageName = "${env.JOB_BASE_NAME}-Source-b${env.BUILD_NUMBER}" runBuild("-s \"$packageName\"") /* Archive resulting artifacts. */ archiveArtifacts artifacts: "$packageName*" } } } catch (e) { /* Mark that a failure occurred. */ anyFailure = true /* Force this stage to fail. */ catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') { throw e; } } } /* Build here to avoid creating a redundant parent stage on the stage view. */ osArchs.each { os, thisOsArchs -> thisOsArchs.each { arch -> def thisArchDynarecs = dynarecArchs[arch] if (!thisArchDynarecs) thisArchDynarecs = ['NoDR'] thisArchDynarecs.each { dynarec -> presets.each { preset -> node(os) { stage("$os $arch $dynarec $preset") { try { /* Run git clone. */ gitClone() /* Switch to temp directory. */ dir(WORKSPACE_TMP) { /* Clean output directory of potential stale old builds. */ removeDir('output') /* Switch to output directory. */ dir('output') { /* Run build process. */ def packageName = "${env.JOB_BASE_NAME}${dynarecSlugs[dynarec]}${presetSlugs[preset]}-$os-$arch-b${env.BUILD_NUMBER}" dir("${dynarecNames[dynarec]}/$os - ${archNames[arch]}") { runBuild("-b \"$packageName\" \"$arch\" ${presetFlags[preset]} ${dynarecFlags[dynarec]} -D \"BUILD_TYPE=$BUILD_TYPE\" -D \"EMU_BUILD=build ${env.BUILD_NUMBER}\" -D \"EMU_BUILD_NUM=${env.BUILD_NUMBER}\"") } /* Archive resulting artifacts. */ archiveArtifacts artifacts: "**/**/$packageName*" } } } catch (e) { /* Mark that a failure occurred. */ anyFailure = true /* Force this stage to fail. */ catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') { throw e; } } } } } } } } } } } } post { always { script { /* Mark build as failed if any step has failed. */ if (anyFailure) { println "[!] Failing build because a build stage failed" currentBuild.result = 'FAILURE' } /* Send out build notifications. */ if (!env.JOB_BASE_NAME.contains('TestBuildPleaseIgnore')) { try { /* Notify Discord. */ def result = currentBuild.currentResult.toLowerCase() discordSend webhookURL: DISCORD_WEBHOOK_URL, title: "${env.JOB_BASE_NAME} #${env.BUILD_NUMBER}", link: env.BUILD_URL, result: currentBuild.currentResult, description: "**Status:** ${result}\n\u2060", /* word joiner character forces a blank line */ enableArtifactsList: false, showChangeset: true /* Notify IRC, which needs a node for whatever reason. */ node { ircNotify() } } catch (e) { /* Force this stage to fail. */ catchError(buildResult: currentBuild.result, stageResult: 'FAILURE') { throw e; } } } } } } }