/* * 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 repository = 'https://github.com/86Box/86Box.git' def branch = 'master' 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': '-t --preset=regular', 'Debug': '--preset=debug', 'Dev': '-t --preset=experimental -D VNC=OFF' ] def anyFailure = false def gitClone(repository, branch) { /* Clean workspace. */ cleanWs() /* Use stashes to avoid performing multiple clones. */ if (env.GIT_STASHED != 'true') { /* Perform clone/checkout. */ def scmVars = checkout poll: true, changelog: true, scm: [$class: 'GitSCM', branches: [[name: branch]], userRemoteConfigs: [[url: repository]]] if (env.GIT_COMMIT == null) { /* Save the current HEAD commit. */ env.GIT_COMMIT = scmVars.GIT_COMMIT } else if (env.GIT_COMMIT != scmVars.GIT_COMMIT) { /* Checkout the commit read from the polling log. */ if (isUnix()) sh "git checkout ${env.GIT_COMMIT} || exit 0" else bat "git checkout ${env.GIT_COMMIT} || exit /b 0" } println "[-] Using git commit [${env.GIT_COMMIT}]" /* Stash data and mark it as stashed. */ stash name: 'git', useDefaultExcludes: false 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 { /* Extract the polled commit from the polling log, so that git checkout can be used to avoid JENKINS-20518 race conditions caused by two pushes too close together. */ node('master') { /* must run on master node to read polling log */ /* 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 commit [${env.GIT_COMMIT}] from polling log" } } } catch (e) {} } /* Adding to the above, run a git clone as soon as possible on any node to further avoid race conditions caused by busy node executor delays. */ node { /* Run git clone. */ gitClone(repository, branch) /* Clean workspace, in case this is running in a non-build node. */ cleanWs() } /* Create source tarball. */ node('Linux') { try { /* Run git clone. */ gitClone(repository, branch) /* Switch to temp directory. */ dir("${env.WORKSPACE_TMP}/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(repository, branch) /* Switch to output directory. */ dir("${env.WORKSPACE_TMP}/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; } } } } } } }