324 lines
13 KiB
Plaintext
324 lines
13 KiB
Plaintext
//=============================================
|
|
// NetModule Wireless Linux CI commons
|
|
//=============================================
|
|
echo "loading NWL CI common module..."
|
|
|
|
|
|
// URLs
|
|
//----------------------------
|
|
env.BITBUCKET_LOCAL = "bitbucket.gad.local"
|
|
env.BITBUCKET_URL = "https://${env.BITBUCKET_LOCAL}"
|
|
|
|
env.YOCTO_REPO_URL = "ssh://git@${env.BITBUCKET_LOCAL}:7999/nm-nsp/netmodule-wireless-linux.git"
|
|
|
|
env.STORAGE_URL = "http://nmrepo.netmodule.intranet"
|
|
env.SSTATE_STORAGE_URL = "${env.STORAGE_URL}/core-os-sstate"
|
|
|
|
env.HASHSERVER = "172.16.70.254:8686"
|
|
|
|
// Yocto build definitions
|
|
//----------------------------
|
|
env.YOCTO_REPO_DIR = "nwl"
|
|
env.YOCTO_RELEASE = 'kirkstone'
|
|
|
|
env.CI_IMAGE = "nwl-image"
|
|
|
|
|
|
// Methods declared in external code are accessible
|
|
// directly from other code in the external file
|
|
// indirectly via the object created by the load operation
|
|
// eg. extcode.build(...)
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
def isCurrentJobSuccess() {
|
|
return (currentBuild.currentResult == 'SUCCESS')
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
def cleaningClonedRepoDir() {
|
|
println "cleaning the entire repository..."
|
|
sh("git clean -ffdx")
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
def getGitCredentialID() {
|
|
return 'admin_credentials'
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
def setupGlobalEnvironmentVariables(repoDir, machine) {
|
|
env.MACHINE = "${machine}"
|
|
env.WORK_DIR = "${WORKSPACE}/${repoDir}"
|
|
env.SHARED_BUILD = "${env.WORK_DIR}/build"
|
|
env.BUILD_DEPLOY_DIR = "${env.SHARED_BUILD}/tmp/deploy"
|
|
env.IMG_DEPLOY_DIR = "${env.BUILD_DEPLOY_DIR}/images"
|
|
env.LICENSE_DEPLOY_DIR = "${env.BUILD_DEPLOY_DIR}/licenses"
|
|
env.SDK_DEPLOY_DIR = "${env.BUILD_DEPLOY_DIR}/sdk"
|
|
env.BUILD_HISTORY_DIR = "${env.SHARED_BUILD}/buildhistory"
|
|
env.SSTATE_CACHE = "${env.SHARED_BUILD}/sstate-cache"
|
|
env.PKG_CONTENT_DIR = "${env.WORK_DIR}/tmp/build-output"
|
|
env.DEPLOY_CONTENT_DIR = "${env.WORK_DIR}/toDeploy"
|
|
env.DOWNLOAD_DIR = "${JENKINS_HOME}/downloads"
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
def getBitbakePackage(machine) {
|
|
// ToDo: handle here bitbake packages if they differ and depend on the machine
|
|
return "${env.CI_IMAGE}"
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
def removePreExistingYoctoConfigs(confPath) {
|
|
if(fileExists("${env.YOCTO_REPO_DIR}/${confPath}")) {
|
|
println "Removing the bitbake config to integrate new meta layers..."
|
|
sh(script: "rm -rf ${env.YOCTO_REPO_DIR}/${confPath}")
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
def gitCheckout(gitUrl, branchTag, repoDir, hasSubmodules) {
|
|
println "checking out ${gitUrl} to ${repoDir}..."
|
|
def gitCredentials = getGitCredentialID()
|
|
if(!fileExists("./${repoDir}")) {
|
|
sshagent (credentials: [gitCredentials]) {
|
|
def inclSubmodulesOpt = hasSubmodules ? "--recurse-submodules" : ""
|
|
sh(script: "git clone ${inclSubmodulesOpt} ${gitUrl} ${repoDir}")
|
|
}
|
|
}
|
|
dir("${repoDir}") {
|
|
def updateSubmodulesCmd = hasSubmodules ? " && git submodule update --init --recursive" : ""
|
|
sshagent (credentials: [gitCredentials]) {
|
|
sh(script: "git fetch -ap && git fetch -t")
|
|
sh(script: "git checkout ${branchTag} && git pull --rebase ${updateSubmodulesCmd}")
|
|
}
|
|
if(hasSubmodules) {
|
|
submoduleStatus = sh(script: "git submodule status", returnStdout: true)
|
|
println "${submoduleStatus}"
|
|
}
|
|
gitHistory = sh(returnStdout: true, script: "git log --pretty=oneline -3")
|
|
println "Last 3 git commits:\n-----------------------------\n${gitHistory}"
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
def getMachineNameConfig() {
|
|
return "MACHINE ?= \"${env.MACHINE}\""
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
def getDownloadDirConfig() {
|
|
return "DL_DIR = \"${env.DOWNLOAD_DIR}\""
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
def getSstateMirrorConfig() {
|
|
def mirrorCfg = "SSTATE_MIRRORS = \"file://.* ${env.SSTATE_STORAGE_URL}/PATH\""
|
|
def signatureHdl = "BB_SIGNATURE_HANDLER = \"OEEquivHash\""
|
|
def hashSrv = "BB_HASHSERVE = \"${env.HASHSERVER}\""
|
|
return "${signatureHdl}\n${hashSrv}\n${mirrorCfg}"
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
def getArtifactConfig() {
|
|
return "NWL_IMAGE_EXTRACLASSES += \"nwl-image-ci\""
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
def setupConfigFile(confPath, confFile) {
|
|
// Keep in mind: order of configurations: site.conf, auto.conf, local.conf
|
|
dir("${env.YOCTO_REPO_DIR}") {
|
|
def machineCfg = getMachineNameConfig()
|
|
def downloadCfg = getDownloadDirConfig()
|
|
def sstateCfg = getSstateMirrorConfig()
|
|
def artifactCfg = getArtifactConfig()
|
|
def autoCfg = "${machineCfg}\n${downloadCfg}\n${sstateCfg}\n${parallelCfg}\n${artifactCfg}\n"
|
|
|
|
if(!fileExists("./${confPath}")) {
|
|
def sourceCmd = "source ${env.YOCTO_ENV}"
|
|
println "Initial build detected, sourcing environment to create structures and files..."
|
|
def srcEnvStatus = sh(returnStatus: true, script: "bash -c '${sourceCmd} > /dev/null 2>&1'")
|
|
println " -> status sourcing the yocto env = ${srcEnvStatus}"
|
|
}
|
|
writeFile(file: "${confFile}", text: "${autoCfg}")
|
|
}
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
def setupEnvironmentForArtifacts(machine) {
|
|
// NOTE: this part depends on
|
|
// - the path defined in env.YOCTO_DEPLOYS
|
|
// - the target as defined in env.BITBAKE_PKG
|
|
// - the yocto config preparation as done in setupConfigFile()
|
|
// - the specific configuration as stated in getArtifactConfig()
|
|
// and affects the function getPackageArtifacts()
|
|
env.YOCTO_ARTIFACTS = "${env.YOCTO_DEPLOYS}/${env.BITBAKE_PKG}-${machine}.ci-artifacts"
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
def setupBuildEnvironment(machine, branchTag, cloneDir, isDebug) {
|
|
// with the machine parameter it will be possible to set up different
|
|
// environment variables in here. Currently we use the SolidRun board
|
|
|
|
setupGlobalEnvironmentVariables(cloneDir, machine)
|
|
|
|
def confPath = "build/conf"
|
|
env.RELATIVE_AUTOCONF_FILE = "${confPath}/auto.conf"
|
|
|
|
env.YOCTO_DEPLOYS = "${env.IMG_DEPLOY_DIR}/${machine}"
|
|
|
|
env.YOCTO_ENV = "nwl-init-build-env"
|
|
env.BITBAKE_PKG = getBitbakePackage(machine)
|
|
env.ISQUIET = isDebug.toBoolean() ? "" : "-q"
|
|
env.BITBAKE_CMD = "${env.ISQUIET} ${env.BITBAKE_PKG}"
|
|
|
|
removePreExistingYoctoConfigs(confPath)
|
|
gitCheckout("${env.YOCTO_REPO_URL}", branchTag, cloneDir, true)
|
|
|
|
env.PKG_NAME = "${env.BITBAKE_PKG}-${machine}"
|
|
sh("mkdir -p ${env.DEPLOY_CONTENT_DIR}")
|
|
|
|
setupConfigFile(confPath, "${env.RELATIVE_AUTOCONF_FILE}")
|
|
setupEnvironmentForArtifacts(machine)
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
def printEnvironmentParameters() {
|
|
println "----------------------------------\n\
|
|
Environment Parameters:\n\
|
|
\n\
|
|
--> machine = ${env.MACHINE}\n\
|
|
--> git URL = ${env.YOCTO_REPO_URL}\n\
|
|
--> yocto dir = ${env.YOCTO_REPO_DIR}\n\
|
|
--> shared build dir = ${env.SHARED_BUILD}\n\
|
|
--> autoconf file = ${env.RELATIVE_AUTOCONF_FILE}\n\
|
|
--> yocto deploys = ${env.YOCTO_DEPLOYS}\n\
|
|
--> yocto environment = ${env.YOCTO_ENV}\n\
|
|
--> bitbake pagkage = ${env.BITBAKE_PKG}\n\
|
|
--> pagkage name = ${env.PKG_NAME}\n\
|
|
--> download dir = ${env.DOWNLOAD_DIR }\n\
|
|
--> artifacts file = ${env.YOCTO_ARTIFACTS}\n\
|
|
----------------------------------\n"
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// check Yocto output file for warnings and print them
|
|
def checkAndHintOnWarnings(yoctoOutputFile) {
|
|
def warnFindCmd = "cat \"${yoctoOutputFile}\" | grep \"WARNING:\" || true"
|
|
def foundWarnings = sh(returnStdout: true, script: "${warnFindCmd}")
|
|
if("${foundWarnings}" != "") {
|
|
println "----------=< WARNINGS FOUND >=-----------\n${foundWarnings}\n-----------------------------------------\n"
|
|
}
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
// retruns true if there is a fetch error
|
|
def hasFetchError(yoctoOutputFile) {
|
|
def hasFetchErrorCmd = "cat \"${yoctoOutputFile}\" | grep \"FetchError\" > /dev/null 2>&1 && exit 1 || exit 0"
|
|
return (sh(returnStatus: true, script: "bash -c '${hasFetchErrorCmd}'") != 0).toBoolean()
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
// retruns true if bitbake is unable to connect
|
|
def isBitbakeUnableToConnect(yoctoOutputFile) {
|
|
def errMsg= "ERROR: Unable to connect to bitbake server"
|
|
def isUnableCmd = "cat \"${yoctoOutputFile}\" | grep \"${errMsg}\" > /dev/null 2>&1 && exit 1 || exit 0"
|
|
return (sh(returnStatus: true, script: "bash -c '${isUnableCmd}'") != 0).toBoolean()
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
// kill any residing bitbake processes on errors
|
|
def killResidingBitbakeProcessesAtError(yoctoOutputFile) {
|
|
if(hasFetchError(yoctoOutputFile) || isBitbakeUnableToConnect(yoctoOutputFile)) {
|
|
println "Fetch- or connection error detected, killing residing bitbake processes..."
|
|
def getBbPidCmd = "ps -ax | grep bitbake | grep -v grep | head -n 1 | sed -e 's/^[ \t]*//' | cut -d' ' -f1"
|
|
def bitbakePid = sh(returnStdout: true, script: "${getBbPidCmd}")
|
|
if("${bitbakePid}" != "") {
|
|
println "Residing process found: ${bitbakePid}"
|
|
sh("kill -9 ${bitbakePid}")
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
def buildTheYoctoPackage() {
|
|
def yoctoOutFile = "yocto.out"
|
|
def sourceCall = "source ${env.YOCTO_ENV} > ${env.SHARED_BUILD}/${yoctoOutFile} 2>&1"
|
|
def buildCall = "bitbake ${env.BITBAKE_CMD} >> ${env.SHARED_BUILD}/${yoctoOutFile} 2>&1"
|
|
def buildCmd = "${sourceCall}; ${buildCall}"
|
|
def bitbakeStatus = 0;
|
|
def gitCredentials = getGitCredentialID()
|
|
sshagent (credentials: [gitCredentials]) {
|
|
bitbakeStatus = sh(returnStatus: true, script: "bash -c '${buildCmd}'")
|
|
}
|
|
println "bitbakeStatus=${bitbakeStatus}"
|
|
|
|
if(fileExists("${env.SHARED_BUILD}/${yoctoOutFile}")) {
|
|
if((bitbakeStatus != 0) || ("${env.ISQUIET}" == "")) {
|
|
println "Yoco Build Output: ----------------"
|
|
sh "cat ${env.SHARED_BUILD}/${yoctoOutFile}"
|
|
println "-----------------------------------"
|
|
}
|
|
checkAndHintOnWarnings("${env.SHARED_BUILD}/${yoctoOutFile}")
|
|
killResidingBitbakeProcessesAtError("${env.SHARED_BUILD}/${yoctoOutFile}")
|
|
}
|
|
|
|
// Do clean-up
|
|
sh "rm -f ${env.SHARED_BUILD}/${yoctoOutFile}"
|
|
sh(script: "git clean -f ${env.RELATIVE_AUTOCONF_FILE}")
|
|
|
|
if(bitbakeStatus != 0) {
|
|
error("Build error, check yocto build output")
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// copy the package- and license manifest into the current directory
|
|
def getManifests(machine, artifactPath, targetBaseName) {
|
|
def pkgArtifactName = "${env.BITBAKE_PKG}-${machine}"
|
|
def pkgManifestFile = "${artifactPath}/${pkgArtifactName}.manifest"
|
|
println "Copying Manifests...\n\
|
|
--> artifactPath = ${artifactPath}\n\
|
|
--> pkgArtifactName = ${pkgArtifactName}\n\
|
|
--> pkgManifestFile = ${pkgManifestFile}\n\
|
|
--> targetBaseName = ${targetBaseName}"
|
|
sh(label: "Copy Package Manifest", script: "cp ${pkgManifestFile} ${targetBaseName}.manifest")
|
|
sh(label: "Copy License Manifest", script: """
|
|
LATEST_LICENSE_DIR=\$(ls -Artd ${env.LICENSE_DEPLOY_DIR}/${pkgArtifactName}* | tail -n 1)
|
|
cp \$LATEST_LICENSE_DIR/license.manifest ${targetBaseName}_license.manifest
|
|
""")
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
// copy the yocto artifacts into the current directory
|
|
def getPackageArtifacts(machine, artifactPath, artifactListFile) {
|
|
println "Getting package artifacts and copy them to current directory...\n\
|
|
--> artifactPath = ${artifactPath}\n\
|
|
--> artifactListFile = ${artifactListFile}"
|
|
|
|
sh(label: "Copy ${machine} Package Artifacts", script: """
|
|
cat ${artifactListFile}
|
|
cat ${artifactListFile} | xargs -I % sh -c 'cp ${artifactPath}/% .'
|
|
""")
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
// copy the package artifacts to destination directory for packing
|
|
def collectingPackageArtifacts(machine) {
|
|
dir("${env.PKG_CONTENT_DIR}") {
|
|
println "Collecting yocto package artifacts (machine = ${machine})..."
|
|
|
|
def artifactPath = "${env.IMG_DEPLOY_DIR}/${machine}"
|
|
getManifests(machine, "${artifactPath}", "./${machine}")
|
|
getPackageArtifacts(machine, artifactPath, "${env.YOCTO_ARTIFACTS}")
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// pack and archive the artifacts
|
|
def packAndArchiveArtifacts(machine, pkgArchiveName) {
|
|
println "archiving the yocto package artifacts (machine = ${machine})..."
|
|
dir ('tmp/artifacts') {
|
|
zip archive: true, dir: "${env.PKG_CONTENT_DIR}", glob: "*", zipFile: "${pkgArchiveName}"
|
|
sh("cp ${pkgArchiveName} ${env.DEPLOY_CONTENT_DIR}/")
|
|
}
|
|
sh("rm -rf ${env.PKG_CONTENT_DIR}/*")
|
|
sh("rm -rf tmp/artifacts")
|
|
}
|
|
|
|
|
|
// !!Important Boilerplate!!
|
|
// The external code must return it's contents as an object
|
|
return this;
|