Jenkinsfile 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638
  1. #!/usr/bin/env groovy
  2. /*
  3. * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
  4. * its licensors.
  5. *
  6. * For complete copyright and license terms please see the LICENSE at the root of this
  7. * distribution (the "License"). All use of this software is governed by the License,
  8. * or, if provided, by the license below or the license accompanying this file. Do not
  9. * remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
  10. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. *
  12. */
  13. PIPELINE_CONFIG_FILE = 'scripts/build/Jenkins/lumberyard.json'
  14. INCREMENTAL_BUILD_SCRIPT_PATH = 'scripts/build/bootstrap/incremental_build_util.py'
  15. EMPTY_JSON = readJSON text: '{}'
  16. PROJECT_REPOSITORY_NAME = 'o3de-atom-sampleviewer'
  17. PROJECT_ORGANIZATION_NAME = 'aws-lumberyard'
  18. ENGINE_REPOSITORY_NAME = 'o3de'
  19. ENGINE_ORGANIZATION_NAME = 'aws-lumberyard'
  20. def pipelineProperties = []
  21. def pipelineParameters = [
  22. // Build/clean Parameters
  23. // The CLEAN_OUTPUT_DIRECTORY is used by ci_build scripts. Creating the parameter here passes it as an environment variable to jobs and is consumed that way
  24. booleanParam(defaultValue: false, description: 'Deletes the contents of the output directory before building. This will cause a \"clean\" build. NOTE: does not imply CLEAN_ASSETS', name: 'CLEAN_OUTPUT_DIRECTORY'),
  25. booleanParam(defaultValue: false, description: 'Deletes the contents of the output directories of the AssetProcessor before building.', name: 'CLEAN_ASSETS'),
  26. booleanParam(defaultValue: false, description: 'Deletes the contents of the workspace and forces a complete pull.', name: 'CLEAN_WORKSPACE'),
  27. booleanParam(defaultValue: false, description: 'Recreates the volume used for the workspace. The volume will be created out of a snapshot taken from main.', name: 'RECREATE_VOLUME'),
  28. stringParam(defaultValue: 'main', description: 'Sets a different branch from o3de engine repo to use or use commit id. Default is mainline', trim: true, name: 'ENGINE_BRANCH'),
  29. stringParam(defaultValue: 'origin/main', description: 'Sets a refspec for the mainline branch. Default is head of main', trim: true, name: 'ENGINE_REFSPEC')
  30. ]
  31. def palSh(cmd, lbl = '', winSlashReplacement = true) {
  32. if (env.IS_UNIX) {
  33. sh label: lbl,
  34. script: cmd
  35. } else if (winSlashReplacement) {
  36. bat label: lbl,
  37. script: cmd.replace('/','\\')
  38. } else {
  39. bat label: lbl,
  40. script: cmd
  41. }
  42. }
  43. def palMkdir(path) {
  44. if (env.IS_UNIX) {
  45. sh label: "Making directories ${path}",
  46. script: "mkdir -p ${path}"
  47. } else {
  48. def win_path = path.replace('/','\\')
  49. bat label: "Making directories ${win_path}",
  50. script: "mkdir ${win_path}."
  51. }
  52. }
  53. def palRm(path) {
  54. if (env.IS_UNIX) {
  55. sh label: "Removing ${path}",
  56. script: "rm ${path}"
  57. } else {
  58. def win_path = path.replace('/','\\')
  59. bat label: "Removing ${win_path}",
  60. script: "del ${win_path}"
  61. }
  62. }
  63. def palRmDir(path) {
  64. if (env.IS_UNIX) {
  65. sh label: "Removing ${path}",
  66. script: "rm -rf ${path}"
  67. } else {
  68. def win_path = path.replace('/','\\')
  69. bat label: "Removing ${win_path}",
  70. script: "rd /s /q ${win_path}"
  71. }
  72. }
  73. def IsPullRequest(branchName) {
  74. // temporarily using the name to detect if we are in a PR
  75. // In the future we will check with github
  76. return branchName.startsWith('PR-')
  77. }
  78. def IsJobEnabled(branchName, buildTypeMap, pipelineName, platformName) {
  79. if (IsPullRequest(branchName)) {
  80. return buildTypeMap.value.TAGS && buildTypeMap.value.TAGS.contains(pipelineName)
  81. }
  82. def job_list_override = params.JOB_LIST_OVERRIDE ? params.JOB_LIST_OVERRIDE.tokenize(',') : ''
  83. if (!job_list_override.isEmpty()) {
  84. return params[platformName] && job_list_override.contains(buildTypeMap.key);
  85. } else {
  86. return params[platformName] && buildTypeMap.value.TAGS && buildTypeMap.value.TAGS.contains(pipelineName)
  87. }
  88. }
  89. def GetRunningPipelineName(JENKINS_JOB_NAME) {
  90. // If the job name has an underscore
  91. def job_parts = JENKINS_JOB_NAME.tokenize('/')[0].tokenize('_')
  92. if (job_parts.size() > 1) {
  93. return [job_parts.take(job_parts.size() - 1).join('_'), job_parts[job_parts.size()-1]]
  94. }
  95. return [job_parts[0], 'default']
  96. }
  97. @NonCPS
  98. def RegexMatcher(str, regex) {
  99. def matcher = (str =~ regex)
  100. return matcher ? matcher.group(1) : null
  101. }
  102. def LoadPipelineConfig(String pipelineName, String branchName) {
  103. echo 'Loading pipeline config'
  104. def pipelineConfig = {}
  105. pipelineConfig = readJSON file: PIPELINE_CONFIG_FILE
  106. palRm(PIPELINE_CONFIG_FILE)
  107. pipelineConfig.platforms = EMPTY_JSON
  108. // Load the pipeline configs per platform
  109. pipelineConfig.PIPELINE_CONFIGS.each { pipeline_config ->
  110. def platform_regex = pipeline_config.replace('.','\\.').replace('*', '(.*)')
  111. if (!env.IS_UNIX) {
  112. platform_regex = platform_regex.replace('/','\\\\')
  113. }
  114. echo "Searching platform pipeline configs in ${pipeline_config} using ${platform_regex}"
  115. for (pipeline_config_path in findFiles(glob: pipeline_config)) {
  116. echo "\tFound platform pipeline config ${pipeline_config_path}"
  117. def platform = RegexMatcher(pipeline_config_path, platform_regex)
  118. if(platform) {
  119. pipelineConfig.platforms[platform] = EMPTY_JSON
  120. pipelineConfig.platforms[platform].PIPELINE_ENV = readJSON file: pipeline_config_path.toString()
  121. }
  122. palRm(pipeline_config_path.toString())
  123. }
  124. }
  125. // Load the build configs
  126. pipelineConfig.BUILD_CONFIGS.each { build_config ->
  127. def platform_regex = build_config.replace('.','\\.').replace('*', '(.*)')
  128. if (!env.IS_UNIX) {
  129. platform_regex = platform_regex.replace('/','\\\\')
  130. }
  131. echo "Searching configs in ${build_config} using ${platform_regex}"
  132. for (build_config_path in findFiles(glob: build_config)) {
  133. echo "\tFound config ${build_config_path}"
  134. def platform = RegexMatcher(build_config_path, platform_regex)
  135. if(platform) {
  136. pipelineConfig.platforms[platform].build_types = readJSON file: build_config_path.toString()
  137. }
  138. }
  139. }
  140. return pipelineConfig
  141. }
  142. def GetBuildEnvVars(Map platformEnv, Map buildTypeEnv, String pipelineName) {
  143. def envVarMap = [:]
  144. platformPipelineEnv = platformEnv['ENV'] ?: [:]
  145. platformPipelineEnv.each { var ->
  146. envVarMap[var.key] = var.value
  147. }
  148. platformEnvOverride = platformEnv['PIPELINE_ENV_OVERRIDE'] ?: [:]
  149. platformPipelineEnvOverride = platformEnvOverride[pipelineName] ?: [:]
  150. platformPipelineEnvOverride.each { var ->
  151. envVarMap[var.key] = var.value
  152. }
  153. buildTypeEnv.each { var ->
  154. // This may override the above one if there is an entry defined by the job
  155. envVarMap[var.key] = var.value
  156. }
  157. // Environment that only applies to to Jenkins tweaks.
  158. // For 3rdParty downloads, we store them in the EBS volume so we can reuse them across node
  159. // instances. This allow us to scale up and down without having to re-download 3rdParty
  160. envVarMap['LY_PACKAGE_DOWNLOAD_CACHE_LOCATION'] = "${envVarMap['WORKSPACE']}/3rdParty/downloaded_packages"
  161. envVarMap['LY_PACKAGE_UNPACK_LOCATION'] = "${envVarMap['WORKSPACE']}/3rdParty/packages"
  162. return envVarMap
  163. }
  164. def GetEnvStringList(Map envVarMap) {
  165. def strList = []
  166. envVarMap.each { var ->
  167. strList.add("${var.key}=${var.value}")
  168. }
  169. return strList
  170. }
  171. def GetLfsConfig(cmd, lbl = '') {
  172. if (env.IS_UNIX) {
  173. sh label: lbl,
  174. script: cmd,
  175. returnStdout: true
  176. } else {
  177. powershell label: lbl, // Powershell is used due to bat output returning the prompt as well
  178. script: cmd,
  179. returnStdout: true
  180. }
  181. }
  182. def getEngineRemoteConfig(remoteConfigs) {
  183. def engineRemoteConfigs = [name: "${ENGINE_REPOSITORY_NAME}",
  184. url: remoteConfigs.url[0]
  185. .replace("${PROJECT_REPOSITORY_NAME}", "${ENGINE_REPOSITORY_NAME}")
  186. .replace("/${PROJECT_ORGANIZATION_NAME}/", "/${ENGINE_ORGANIZATION_NAME}/"),
  187. refspec: "+refs/heads/main:refs/remotes/${params.ENGINE_REFSPEC}",
  188. credentialsId: remoteConfigs.credentialsId[0]
  189. ]
  190. return engineRemoteConfigs
  191. }
  192. def CheckoutBootstrapScripts(String branchName) {
  193. checkout([$class: "GitSCM",
  194. branches: [[name: "*/${branchName}"]],
  195. doGenerateSubmoduleConfigurations: false,
  196. extensions: [
  197. [$class: "PruneStaleBranch"],
  198. [$class: "SparseCheckoutPaths",
  199. sparseCheckoutPaths: [
  200. [ $class: "SparseCheckoutPath", path: "scripts/build/Jenkins/" ],
  201. [ $class: "SparseCheckoutPath", path: "scripts/build/bootstrap/" ],
  202. [ $class: "SparseCheckoutPath", path: "scripts/build/Platform" ]
  203. ]
  204. ],
  205. [$class: "CloneOption", depth: 1, noTags: false, reference: "", shallow: true]
  206. ],
  207. submoduleCfg: [],
  208. userRemoteConfigs: [getEngineRemoteConfig(scm.userRemoteConfigs)]
  209. ])
  210. }
  211. def CheckoutRepo(boolean disableSubmodules = false) {
  212. def projectsAndUrl = [
  213. "${ENGINE_REPOSITORY_NAME}": getEngineRemoteConfig(scm.userRemoteConfigs),
  214. "${PROJECT_REPOSITORY_NAME}": scm.userRemoteConfigs[0]
  215. ]
  216. projectsAndUrl.each { projectAndUrl ->
  217. if(!fileExists(projectAndUrl.key)) {
  218. palMkdir(projectAndUrl.key)
  219. }
  220. dir(projectAndUrl.key) {
  221. if(fileExists('.git')) {
  222. // If the repository after checkout is locked, likely we took a snapshot while git was running,
  223. // to leave the repo in a usable state, garbagecollect. This also helps in situations where
  224. def indexLockFile = '.git/index.lock'
  225. if(fileExists(indexLockFile)) {
  226. palSh('git gc', 'Git GarbageCollect')
  227. }
  228. if(fileExists(indexLockFile)) { // if it is still there, remove it
  229. palRm(indexLockFile)
  230. }
  231. }
  232. }
  233. }
  234. def random = new Random()
  235. def retryAttempt = 0
  236. retry(5) {
  237. if (retryAttempt > 0) {
  238. sleep random.nextInt(60 * retryAttempt) // Stagger checkouts to prevent HTTP 429 (Too Many Requests) response from CodeCommit
  239. }
  240. retryAttempt = retryAttempt + 1
  241. projectsAndUrl.each { projectAndUrl ->
  242. dir(projectAndUrl.key) {
  243. def branchName = scm.branches
  244. palSh('git lfs uninstall', 'Git LFS Uninstall') // Prevent git from pulling lfs objects during checkout
  245. if(projectAndUrl.key == "${ENGINE_REPOSITORY_NAME}") {
  246. branchName = [[name: params.ENGINE_BRANCH]]
  247. }
  248. checkout scm: [
  249. $class: 'GitSCM',
  250. branches: branchName,
  251. extensions: [
  252. [$class: 'PruneStaleBranch'],
  253. [$class: 'SubmoduleOption', disableSubmodules: disableSubmodules, recursiveSubmodules: true],
  254. [$class: 'CheckoutOption', timeout: 60]
  255. ],
  256. userRemoteConfigs: [projectAndUrl.value]
  257. ]
  258. if(fileExists(".lfsconfig")) {
  259. def localLfsUrl = GetLfsConfig("git config -f .lfsconfig --get lfs.url", "Getting LFS URL").replace("https://","").trim() // Read the lfs file instead of relying on env var
  260. withCredentials([usernamePassword(credentialsId: "${env.GITHUB_USER}", passwordVariable: 'accesstoken', usernameVariable: 'username')]) {
  261. palSh("git config -f .lfsconfig lfs.url https://${username}:${accesstoken}@${localLfsUrl}", 'Set credentials', false)
  262. }
  263. }
  264. palSh('git lfs install', 'Git LFS Install')
  265. palSh('git lfs pull', 'Git LFS Pull')
  266. }
  267. }
  268. }
  269. // CHANGE_ID is used by some scripts to identify uniquely the current change (usually metric jobs)
  270. dir(PROJECT_REPOSITORY_NAME) {
  271. palSh('git rev-parse HEAD > commitid', 'Getting commit id')
  272. env.CHANGE_ID = readFile file: 'commitid'
  273. env.CHANGE_ID = env.CHANGE_ID.trim()
  274. palRm('commitid')
  275. }
  276. }
  277. def PreBuildCommonSteps(Map pipelineConfig, String repositoryName, String projectName, String pipeline, String branchName, String platform, String buildType, String workspace, boolean mount = true, boolean disableSubmodules = false) {
  278. echo 'Starting pre-build common steps...'
  279. if (mount) {
  280. unstash name: 'incremental_build_script'
  281. def pythonCmd = ''
  282. if(env.IS_UNIX) pythonCmd = 'sudo -E python -u '
  283. else pythonCmd = 'python -u '
  284. if(env.RECREATE_VOLUME?.toBoolean()) {
  285. palSh("${pythonCmd} ${INCREMENTAL_BUILD_SCRIPT_PATH} --action delete --repository_name ${repositoryName} --project ${projectName} --pipeline ${pipeline} --branch ${branchName} --platform ${platform} --build_type ${buildType}", 'Deleting volume', winSlashReplacement=false)
  286. }
  287. timeout(5) {
  288. palSh("${pythonCmd} ${INCREMENTAL_BUILD_SCRIPT_PATH} --action mount --repository_name ${repositoryName} --project ${projectName} --pipeline ${pipeline} --branch ${branchName} --platform ${platform} --build_type ${buildType}", 'Mounting volume', winSlashReplacement=false)
  289. }
  290. if(env.IS_UNIX) {
  291. sh label: 'Setting volume\'s ownership',
  292. script: """
  293. if sudo test ! -d "${workspace}"; then
  294. sudo mkdir -p ${workspace}
  295. cd ${workspace}/..
  296. sudo chown -R lybuilder:root .
  297. fi
  298. """
  299. }
  300. }
  301. // Cleanup previous repo location, we are currently at the root of the workspace, if we have a .git folder
  302. // we need to cleanup. Once all branches take this relocation, we can remove this
  303. if(env.CLEAN_WORKSPACE?.toBoolean() || fileExists("${workspace}/.git")) {
  304. if(fileExists(workspace)) {
  305. palRmDir(workspace)
  306. }
  307. }
  308. dir(workspace) {
  309. // Add folder where we will store the 3rdParty downloads and packages
  310. if(!fileExists('3rdParty')) {
  311. palMkdir('3rdParty')
  312. }
  313. CheckoutRepo(disableSubmodules)
  314. }
  315. dir("${workspace}/${ENGINE_REPOSITORY_NAME}") {
  316. // Get python
  317. if(env.IS_UNIX) {
  318. sh label: 'Getting python',
  319. script: 'python/get_python.sh'
  320. } else {
  321. bat label: 'Getting python',
  322. script: 'python/get_python.bat'
  323. }
  324. if(env.CLEAN_OUTPUT_DIRECTORY?.toBoolean() || env.CLEAN_ASSETS?.toBoolean()) {
  325. def command = "${pipelineConfig.BUILD_ENTRY_POINT} --platform ${platform} --type clean"
  326. if (env.IS_UNIX) {
  327. sh label: "Running ${platform} clean",
  328. script: "${pipelineConfig.PYTHON_DIR}/python.sh -u ${command}"
  329. } else {
  330. bat label: "Running ${platform} clean",
  331. script: "${pipelineConfig.PYTHON_DIR}/python.cmd -u ${command}".replace('/','\\')
  332. }
  333. }
  334. }
  335. }
  336. def Build(Map options, String platform, String type, String workspace) {
  337. // If EXECUTE_FROM_PROJECT is defined, we execute the script from the project instead of from the engine
  338. // In both cases, the scripts are in the engine, is just what the current dir is and how we get to the scripts
  339. def currentDir = "${workspace}/${ENGINE_REPOSITORY_NAME}"
  340. def pathToEngine = ""
  341. if (env.EXECUTE_FROM_PROJECT?.toBoolean()) {
  342. currentDir = "${workspace}/${PROJECT_REPOSITORY_NAME}"
  343. pathToEngine = "../${ENGINE_REPOSITORY_NAME}/"
  344. }
  345. def command = "${pathToEngine}${options.BUILD_ENTRY_POINT} --platform ${platform} --type ${type}"
  346. dir("${currentDir}") {
  347. if (env.IS_UNIX) {
  348. sh label: "Running ${platform} ${type}",
  349. script: "${pathToEngine}${options.PYTHON_DIR}/python.sh -u ${command}"
  350. } else {
  351. bat label: "Running ${platform} ${type}",
  352. script: "${pathToEngine}${options.PYTHON_DIR}/python.cmd -u ${command}".replace('/','\\')
  353. }
  354. }
  355. }
  356. def TestMetrics(Map options, String workspace, String branchName, String repoName, String buildJobName, String outputDirectory, String configuration) {
  357. catchError(buildResult: null, stageResult: null) {
  358. def cmakeBuildDir = [workspace, ENGINE_REPOSITORY_NAME, outputDirectory].join('/')
  359. dir("${workspace}/${ENGINE_REPOSITORY_NAME}") {
  360. checkout scm: [
  361. $class: 'GitSCM',
  362. branches: [[name: '*/main']],
  363. extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'mars']],
  364. userRemoteConfigs: [[url: "${env.MARS_REPO}", name: 'mars', credentialsId: "${env.GITHUB_USER}"]]
  365. ]
  366. withCredentials([usernamePassword(credentialsId: "${env.SERVICE_USER}", passwordVariable: 'apitoken', usernameVariable: 'username')]) {
  367. def command = "${options.PYTHON_DIR}/python.cmd -u mars/scripts/python/ctest_test_metric_scraper.py " +
  368. '-e jenkins.creds.user %username% -e jenkins.creds.pass %apitoken% ' +
  369. "-e jenkins.base_url ${env.JENKINS_URL} " +
  370. "${cmakeBuildDir} ${branchName} %BUILD_NUMBER% AR ${configuration} ${repoName} "
  371. bat label: "Publishing ${buildJobName} Test Metrics",
  372. script: command
  373. }
  374. }
  375. }
  376. }
  377. def PostBuildCommonSteps(String workspace, boolean mount = true) {
  378. echo 'Starting post-build common steps...'
  379. if (mount) {
  380. def pythonCmd = ''
  381. if(env.IS_UNIX) pythonCmd = 'sudo -E python -u '
  382. else pythonCmd = 'python -u '
  383. try {
  384. timeout(5) {
  385. palSh("${pythonCmd} ${INCREMENTAL_BUILD_SCRIPT_PATH} --action unmount", 'Unmounting volume')
  386. }
  387. } catch (Exception e) {
  388. echo "Unmount script error ${e}"
  389. }
  390. }
  391. }
  392. def CreateSetupStage(Map pipelineConfig, String repositoryName, String projectName, String pipelineName, String branchName, String platformName, String jobName, Map environmentVars) {
  393. return {
  394. stage("Setup") {
  395. PreBuildCommonSteps(pipelineConfig, repositoryName, projectName, pipelineName, branchName, platformName, jobName, environmentVars['WORKSPACE'], environmentVars['MOUNT_VOLUME'])
  396. }
  397. }
  398. }
  399. def CreateBuildStage(Map pipelineConfig, String platformName, String jobName, Map environmentVars) {
  400. return {
  401. stage("${jobName}") {
  402. Build(pipelineConfig, platformName, jobName, environmentVars['WORKSPACE'])
  403. }
  404. }
  405. }
  406. def CreateTestMetricsStage(Map pipelineConfig, String branchName, Map environmentVars, String buildJobName, String outputDirectory, String configuration) {
  407. return {
  408. stage("${buildJobName}_metrics") {
  409. TestMetrics(pipelineConfig, environmentVars['WORKSPACE'], branchName, env.DEFAULT_REPOSITORY_NAME, buildJobName, outputDirectory, configuration)
  410. }
  411. }
  412. }
  413. def CreateTeardownStage(Map environmentVars) {
  414. return {
  415. stage("Teardown") {
  416. PostBuildCommonSteps(environmentVars['WORKSPACE'], environmentVars['MOUNT_VOLUME'])
  417. }
  418. }
  419. }
  420. def projectName = ''
  421. def pipelineName = ''
  422. def branchName = ''
  423. def pipelineConfig = {}
  424. // Start Pipeline
  425. try {
  426. stage('Setup Pipeline') {
  427. node('controller') {
  428. def envVarList = []
  429. if(isUnix()) {
  430. envVarList.add('IS_UNIX=1')
  431. }
  432. withEnv(envVarList) {
  433. timestamps {
  434. repositoryUrl = scm.getUserRemoteConfigs()[0].getUrl()
  435. // repositoryName is the full repository name
  436. repositoryName = (repositoryUrl =~ /https:\/\/github.com\/(.*)\.git/)[0][1]
  437. (projectName, pipelineName) = GetRunningPipelineName(env.JOB_NAME) // env.JOB_NAME is the name of the job given by Jenkins
  438. if(env.BRANCH_NAME) {
  439. branchName = env.BRANCH_NAME
  440. } else {
  441. branchName = scm.branches[0].name // for non-multibranch pipelines
  442. env.BRANCH_NAME = branchName // so scripts that read this environment have it (e.g. incremental_build_util.py)
  443. }
  444. pipelineProperties.add(disableConcurrentBuilds())
  445. echo "Running \"${pipelineName}\" for \"${branchName}\" on engine branch \"${params.ENGINE_BRANCH}\"..."
  446. CheckoutBootstrapScripts(params.ENGINE_BRANCH)
  447. // Load configs
  448. pipelineConfig = LoadPipelineConfig(pipelineName, branchName)
  449. // Add each platform as a parameter that the user can disable if needed
  450. if (!IsPullRequest(branchName)) {
  451. pipelineParameters.add(stringParam(defaultValue: '', description: 'Filters and overrides the list of jobs to run for each of the below platforms (comma-separated). Can\'t be used during a pull request.', name: 'JOB_LIST_OVERRIDE'))
  452. pipelineConfig.platforms.each { platform ->
  453. pipelineParameters.add(booleanParam(defaultValue: true, description: '', name: platform.key))
  454. }
  455. }
  456. pipelineProperties.add(parameters(pipelineParameters))
  457. properties(pipelineProperties)
  458. // Stash the INCREMENTAL_BUILD_SCRIPT_PATH since all nodes will use it
  459. stash name: 'incremental_build_script',
  460. includes: INCREMENTAL_BUILD_SCRIPT_PATH
  461. }
  462. }
  463. }
  464. }
  465. if(env.BUILD_NUMBER == '1' && !IsPullRequest(branchName)) {
  466. // Exit pipeline early on the intial build. This allows Jenkins to load the pipeline for the branch and enables users
  467. // to select build parameters on their first actual build. See https://issues.jenkins.io/browse/JENKINS-41929
  468. currentBuild.result = 'SUCCESS'
  469. return
  470. }
  471. def someBuildHappened = false
  472. // Build and Post-Build Testing Stage
  473. def buildConfigs = [:]
  474. // Platform Builds run on EC2
  475. pipelineConfig.platforms.each { platform ->
  476. platform.value.build_types.each { build_job ->
  477. if (IsJobEnabled(branchName, build_job, pipelineName, platform.key)) { // User can filter jobs, jobs are tagged by pipeline
  478. def envVars = GetBuildEnvVars(platform.value.PIPELINE_ENV ?: EMPTY_JSON, build_job.value.PIPELINE_ENV ?: EMPTY_JSON, pipelineName)
  479. envVars['JOB_NAME'] = "${branchName}_${platform.key}_${build_job.key}" // backwards compatibility, some scripts rely on this
  480. envVars['CMAKE_LY_PROJECTS'] = "../${PROJECT_REPOSITORY_NAME}"
  481. def nodeLabel = envVars['NODE_LABEL']
  482. someBuildHappened = true
  483. buildConfigs["${platform.key} [${build_job.key}]"] = {
  484. node("${nodeLabel}") {
  485. if(isUnix()) { // Has to happen inside a node
  486. envVars['IS_UNIX'] = 1
  487. }
  488. withEnv(GetEnvStringList(envVars)) {
  489. try {
  490. def build_job_name = build_job.key
  491. CreateSetupStage(pipelineConfig, repositoryName, projectName, pipelineName, branchName, platform.key, build_job.key, envVars).call()
  492. if(build_job.value.steps) { //this is a pipe with many steps so create all the build stages
  493. build_job.value.steps.each { build_step ->
  494. build_job_name = build_step
  495. def buildTypeJson = platform.value.build_types["${build_job_name}"]
  496. def buildTypePipelineEnv = buildTypeJson ? buildTypeJson.PIPELINE_ENV : EMPTY_JSON
  497. def jobEnvVars = envVars
  498. buildTypePipelineEnv.each { var ->
  499. jobEnvVars[var.key] = var.value
  500. }
  501. withEnv(GetEnvStringList(jobEnvVars)) {
  502. CreateBuildStage(pipelineConfig, platform.key, build_step, envVars).call()
  503. }
  504. }
  505. } else {
  506. CreateBuildStage(pipelineConfig, platform.key, build_job.key, envVars).call()
  507. }
  508. if (env.MARS_REPO && platform.key == 'Windows' && build_job_name.startsWith('test')) {
  509. def output_directory = platform.value.build_types[build_job_name].PARAMETERS.OUTPUT_DIRECTORY
  510. def configuration = platform.value.build_types[build_job_name].PARAMETERS.CONFIGURATION
  511. CreateTestMetricsStage(pipelineConfig, branchName, envVars, build_job_name, output_directory, configuration).call()
  512. }
  513. }
  514. catch(Exception e) {
  515. // https://github.com/jenkinsci/jenkins/blob/master/core/src/main/java/hudson/model/Result.java
  516. // {SUCCESS,UNSTABLE,FAILURE,NOT_BUILT,ABORTED}
  517. def currentResult = envVars['ON_FAILURE_MARK'] ?: 'FAILURE'
  518. if (currentResult == 'FAILURE') {
  519. currentBuild.result = 'FAILURE'
  520. error "FAILURE: ${e}"
  521. } else if (currentResult == 'UNSTABLE') {
  522. currentBuild.result = 'UNSTABLE'
  523. unstable(message: "UNSTABLE: ${e}")
  524. }
  525. }
  526. finally {
  527. CreateTeardownStage(envVars).call()
  528. }
  529. }
  530. }
  531. }
  532. }
  533. }
  534. }
  535. timestamps {
  536. stage('Build') {
  537. parallel buildConfigs // Run parallel builds
  538. }
  539. echo 'All builds successful'
  540. }
  541. if (!someBuildHappened) {
  542. currentBuild.result = 'NOT_BUILT'
  543. }
  544. }
  545. catch(Exception e) {
  546. error "Exception: ${e}"
  547. }
  548. finally {
  549. try {
  550. if(env.SNS_TOPIC) {
  551. snsPublish(
  552. topicArn: env.SNS_TOPIC,
  553. subject:'Build Result',
  554. message:"${currentBuild.currentResult}:${BUILD_URL}:${env.RECREATE_VOLUME}:${env.CLEAN_OUTPUT_DIRECTORY}:${env.CLEAN_ASSETS}"
  555. )
  556. }
  557. step([
  558. $class: 'Mailer',
  559. notifyEveryUnstableBuild: true,
  560. sendToIndividuals: true,
  561. recipients: emailextrecipients([
  562. [$class: 'CulpritsRecipientProvider'],
  563. [$class: 'RequesterRecipientProvider']
  564. ])
  565. ])
  566. } catch(Exception e) {
  567. }
  568. }