86Box/.ci/Jenkinsfile
2022-02-13 18:23:56 -03:00

303 lines
8.1 KiB
Groovy

/*
* 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, <richardg867@gmail.com>
*
* Copyright 2021 RichardG.
*/
def repository = 'https://github.com/86Box/86Box.git'
def commitBrowser = 'https://github.com/86Box/86Box/commit/%s'
def branch = 'master'
def osArchs = [
'Windows': ['32', '64'],
'Linux': ['x86', 'x86_64', 'arm32', 'arm64']
]
def osFlags = [
'Windows': '',
'Linux': '-D QT=ON'
]
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 -D CMAKE_BUILD_TYPE=Release',
'Debug': '--preset=debug -D CMAKE_BUILD_TYPE=Debug',
'Dev': '--preset=experimental -D CMAKE_BUILD_TYPE=Debug -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. Should only be changed for one-off builds, otherwise change the default 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*"
}
/* Clean up. */
removeDir("${env.WORKSPACE_TMP}/output")
} 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]} ${osFlags[os]} -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*"
}
/* Clean up. */
removeDir("${env.WORKSPACE_TMP}/output")
} 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,
scmWebUrl: commitBrowser
/* 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;
}
}
}
}
}
}
}