diff --git a/Jenkinsfile b/Jenkinsfile index 395a8d8c6d1..26ead393d15 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -10,12 +10,58 @@ * * Contributors: * Mickael Istria (Red Hat Inc.) - initial API and implementation + * Hannes Wellmann - Build Equinox native launchers and executables on demand as part of master- and verification-builds *******************************************************************************/ +def nativeBuildAgent(String platform, Closure body) { + def final nativeBuildStageName = 'Build Launcher native binaries' + if (platform == 'gtk.linux.x86_64') { + return podTemplate(yaml: ''' +apiVersion: v1 +kind: Pod +spec: + containers: + - name: "launcherbuild" + image: "eclipse/platformreleng-centos-swt-build:8" + imagePullPolicy: "Always" + command: + - cat + tty: true + volumeMounts: + - name: tools + mountPath: /opt/tools + volumes: + - name: tools + persistentVolumeClaim: + claimName: tools-claim-jiro-releng + - name: volume-known-hosts + configMap: + name: known-hosts +''') { node(POD_LABEL) { stage(nativeBuildStageName) { container('launcherbuild') { body() } } } } + } else { + if (platform == 'cocoa.macosx.x86_64') { + platform = 'cocoa.macosx.aarch64' + } + return node('swt.natives-' + platform) { stage(nativeBuildStageName) { body() } } //TODO: generalize labels + } +} + +/** Returns the download URL of the JDK against whoose C headers (in the 'include/' folder) and native libaries the natives are compiled.*/ +def getNativeJdkUrl(String os, String arch) { // To update the used JDK version update the URL template below + return "https://download.eclipse.org/justj/jres/17/downloads/20230428_1804/org.eclipse.justj.openjdk.hotspot.jre.minimal.stripped-17.0.7-${os}-${arch}.tar.gz" +} + +//TODO: adjust tag names to be based on the binaries version instead of a time-stamp? +def getLatestGitTag() { + return sh(script: 'git describe --abbrev=0 --tags --match v[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]', returnStdout: true).strip() +} + def isOnMainIshBranch() { return env.BRANCH_NAME == ('master') || env.BRANCH_NAME ==~ 'R[0-9]+_[0-9]+_maintenance' } +boolean BUILD_NATIVES = false + pipeline { options { skipDefaultCheckout() // Specialiced checkout is performed below @@ -34,11 +80,29 @@ pipeline { environment { EQUINOX_BINARIES_LOC = "$WORKSPACE/equinox.binaries" } + parameters { + booleanParam(name: 'forceNativeBuilds', defaultValue: false, description: 'Forces to run the native builds of Equinox\' binaries. Will push the built binaries to the master branch, unless \'skipCommit\' is set. Useful in debugging.') + booleanParam(name: 'skipCommit', defaultValue: false, description: 'Stops committing to equinox and equinox binaries repo at the end. Useful in debugging.') + } stages { stage('Checkout SCM') { steps{ dir('equinox') { checkout scm + script { + def authorMail = sh(script: 'git log -1 --pretty=format:"%ce" HEAD', returnStdout: true) + echo 'HEAD commit author: ' + authorMail + def buildBotMail = 'eclipse-releng-bot@eclipse.org' + if (buildBotMail.equals(authorMail) && !params.forceNativeBuilds) { + // Prevent endless build-loops due to self triggering because of a previous automated native-build and the associated updates. + currentBuild.result = 'ABORTED' + error('Abort build only triggered by automated natives update.') + } + sh """ + git config --global user.email '${buildBotMail}' + git config --global user.name 'Eclipse Releng Bot' + """ + } } dir('equinox.binaries') { checkout([$class: 'GitSCM', @@ -50,6 +114,161 @@ pipeline { } } } + stage('Check if native launchers and executables changed') { + steps{ + dir('equinox') { + script { + def nativeBuildTag = getLatestGitTag() + echo "Last tag: ${nativeBuildTag}" + def diff = sh(script: "git diff HEAD ${nativeBuildTag} -- 'features/org.eclipse.equinox.executable.feature/library'", returnStdout: true) + boolean nativesChanged = !diff.strip().isEmpty() + echo "Natives changed since ${nativeBuildTag}: ${nativesChanged}" + if (nativesChanged || params.forceNativeBuilds) { + BUILD_NATIVES = true + dir('features/org.eclipse.equinox.executable.feature/library') { + sh ''' + old_ver=$(grep "min_ver=" make_version.mak |cut -d= -f2) + new_ver=$(expr $old_ver + 1) + sed -i -e "s/$old_ver/$new_ver/" make_version.mak + ''' + stash(name:"equinox.binaries.sources.cocoa", includes: '*,cocoa/') + stash(name:"equinox.binaries.sources.gtk", includes: '*,gtk/') + stash(name:"equinox.binaries.sources.win32", includes: '*,win32/') + } + } + } + } + } + } + stage('Build launcher and executable binaries, if needed') { + when { + expression { BUILD_NATIVES } + } + matrix { + axes { + axis { + name 'PLATFORM' + values 'cocoa.macosx.aarch64' , 'cocoa.macosx.x86_64', 'gtk.linux.aarch64', 'gtk.linux.ppc64le', 'gtk.linux.x86_64', 'win32.win32.x86_64' + } + } + stages { + stage('Build Equinox natives') { + options { + timeout(time: 120, unit: 'MINUTES') // Some build agents are rare and it might take awhile until they are available. + } + steps { + script { + def (ws, os, arch) = env.PLATFORM.split('\\.') + dir("jdk-download-${os}.${arch}") { + // Fetch the JDK, which provides the C header-files and shared native libaries, against which the natives are build. + sh "curl ${getNativeJdkUrl(os, arch)} | tar -xzf - include/ lib/" + stash(name:"jdk.resources.${os}.${arch}", includes: "include/,lib/") + deleteDir() + } + nativeBuildAgent("${PLATFORM}") { + cleanWs() // Workspace not cleaned by default + echo "OS: ${os}, ARCH: ${arch}" + unstash("equinox.binaries.sources.${ws}") + dir('jdk.resources') { + unstash("jdk.resources.${os}.${arch}") + } + withEnv(["JAVA_HOME=${WORKSPACE}/jdk.resources", "EXE_OUTPUT_DIR=${WORKSPACE}/libs", "LIB_OUTPUT_DIR=${WORKSPACE}/libs"]) { + dir(ws) { + if (isUnix()) { + sh "sh build.sh -ws ${ws} -os ${os} -arch ${arch} install" + } else { + bat "cmd /c build.bat -ws ${ws} -os ${os} -arch ${arch} install" + } + } + } + dir('libs') { + stash("equinox.binaries.${PLATFORM}") + } + } + } + } + } + stage('Collect and sign binaries') { + steps { + dir("libs/${PLATFORM}") { + unstash("equinox.binaries.${PLATFORM}") + script { + def (ws, os, arch) = env.PLATFORM.split('\\.') + withEnv(["ws=${ws}", "os=${os}", "arch=${arch}", "isMainIshBranch=${isOnMainIshBranch()}"]) { + sh ''' + fn-sign-file () + { + filename=$1 + signerUrl=$2 + extraArguments=$3 + if [[ ${isMainIshBranch} == true ]]; then + mv ${filename} unsigned-${filename} + curl --fail --form "file=@unsigned-${filename}" --output "${filename}" ${extraArguments} "${signerUrl}" + rm unsigned-${filename} + fi + } + + binPath=${WORKSPACE}/equinox.binaries/org.eclipse.equinox.executable/bin/${ws}/${os}/${arch} + libPath=${WORKSPACE}/equinox.binaries/org.eclipse.equinox.launcher.${ws}.${os}.${arch} + + if [[ ${PLATFORM} == cocoa.macosx.* ]]; then + libFileExt='so' + signerUrl='https://cbi.eclipse.org/macos/codesign/sign' + binPath = ${binPath}/Eclipse.app/Contents/MacOS + # Sign executable for Mac + wget https://download.eclipse.org/eclipse/relengScripts/entitlement/sdk.entitlement + fn-sign-file eclipse "${signerUrl}" '-F entitlements=@sdk.entitlement' + + elif [[ ${PLATFORM} == gtk.linux.* ]]; then + libFileExt='so' + + elif [[ ${PLATFORM} == win32.win32.* ]]; then + exeFileExt='*.exe' + libFileExt='dll' + signerUrl='https://cbi.eclipse.org/authenticode/sign' + fi + + if [[ -n "${signerUrl}" ]]; then + echo "Sign ${PLATFORM} libraries" + for file in *.${libFileExt}; do + fn-sign-file "${file}" "${signerUrl}" + done + fi + + mkdir -p ${binPath} + mkdir -p ${libPath} + + echo 'Clean existing binaries' + rm -rf ${binPath}/eclipse${exeFileExt} + rm -rf ${libPath}/eclipse_*.${libFileExt} + + echo "Copy new binaries" + mv eclipse${exeFileExt} ${binPath} + mv eclipse_*.${libFileExt} ${libPath} + ''' + } + } + } + } + } + } + } + } + stage('Commit native binaries, if build') { + when { + expression { BUILD_NATIVES } + } + steps { + dir('equinox.binaries') { + withAnt(installation: 'apache-ant-latest', jdk: 'openjdk-jdk11-latest') { // nashorn javascript-engine required in ant-scripts + sh ''' + cd ${WORKSPACE}/equinox/releng/org.eclipse.equinox.launcher.releng/ + ant commitBinaries -DlocalGit=${WORKSPACE} + ''' + } + } + } + } stage('Build') { steps { dir('equinox') { @@ -69,5 +288,38 @@ pipeline { } } } + stage('Push Equinox-native binaries, if build') { + when { + expression { BUILD_NATIVES } + } + steps { + sshagent(['github-bot-ssh']) { + sh """ + # Check for the master-branch as late as possible to have as much of the same behaviour as possible + if [[ ${isOnMainIshBranch()} == true ]]; then + if [[ ${params.skipCommit} != true ]]; then + + # Don't rebase and just fail in case another commit has been pushed to the master/maintanance branch in the meantime + pushd equinox + git push origin HEAD:refs/heads/${BRANCH_NAME} + git push origin refs/tags/${getLatestGitTag()} + popd + + pushd equinox.binaries + git push origin HEAD:refs/heads/${BRANCH_NAME} + git push origin refs/tags/${getLatestGitTag()} + popd + + exit 0 + else + echo Committing is skipped + fi + else + echo Skip pushing changes of native-binaries for branch '${BRANCH_NAME}' + fi + """ + } + } + } } } diff --git a/releng/org.eclipse.equinox.launcher.releng/build.xml b/releng/org.eclipse.equinox.launcher.releng/build.xml index 071ebd1afee..534956c5aba 100644 --- a/releng/org.eclipse.equinox.launcher.releng/build.xml +++ b/releng/org.eclipse.equinox.launcher.releng/build.xml @@ -240,12 +240,13 @@ - - + + - + - + +