def projDir = "projects" def miopenDir = "${projDir}/miopen" def ckDir = "${projDir}/composablekernel" def rocmnode(name) { return '(rocmtest || miopen) && (' + name + ')' } // Returns the original PR branch tip SHA (pullHash) or branch HEAD SHA (hash) // from the Jenkins SCMRevisionAction, which is recorded before any local merge. // Returns null for non-SCM triggers; caller should handle fall back. @NonCPS String getGitHubCommitHash(def build) { def scmAction = build?.actions.find { action -> action instanceof jenkins.scm.api.SCMRevisionAction } if (scmAction?.revision instanceof org.jenkinsci.plugins.github_branch_source.PullRequestSCMRevision) { return scmAction.revision.pullHash } else if (scmAction?.revision instanceof jenkins.plugins.git.AbstractGitSCMSource$SCMRevisionImpl) { return scmAction.revision.hash } return null } def get_branch_name(){ def shared_library_branch = scm.branches[0].name if (shared_library_branch .contains("*/")) { shared_library_branch = shared_library_branch.split("\\*/")[1] } echo "${shared_library_branch}" return shared_library_branch } /// Stage name format: /// [DataType] Backend[/Compiler] BuildType [TestSet] [Target] /// /// The only mandatory elements are Backend and BuildType; others are optional. /// /// DataType := { Fp16 | Bf16 | Int8 | Fp32 } /// Backend := { Hip | HipNoGPU} /// Compiler := { Clang* | GCC* } /// * "Clang" is the default for the Hip backend, and implies hip-clang compiler. /// * The default compiler is usually not specified. /// BuildType := { Release* | Debug | Install } [ BuildTypeModifier ] /// * BuildTypeModifier := { NOCOMGR | Embedded | Static | Normal-Find | Fast-Find /// NOCK | NOMLIR | Tensile | Tensile-Latest | Package | ... } /// TestSet := { All | Smoke* | | Build-only } /// * "All" corresponds to "cmake -DMIOPEN_TEST_ALL=On". /// * "Smoke" (-DMIOPEN_TEST_ALL=Off) is the default and usually not specified. /// * "Performance Dataset" is a performance test with a specified dataset. /// Target := { gfx908 | gfx90a | gfx942 } [ Xnack+ ] utils = null def show_node_info() { sh """ echo "NODE_NAME = \$NODE_NAME" lsb_release -sd uname -r cat /sys/module/amdgpu/version ls /opt/ -la """ } def cloneUpdateRefRepo() { def refRepoPath = "/var/jenkins/ref-repo/rocm-libraries" def lockLabel = "git ref repo lock - ${env.NODE_NAME}" def folderExists = sh( script: "test -d ${refRepoPath}/refs", returnStatus: true ) == 0 if (!folderExists) { echo "rocm-libraries repo does not exist at ${refRepoPath}, creating mirror clone..." echo "locking on label: ${lockLabel}" lock(lockLabel) { def cloneCommand = """ set -ex rm -rf ${refRepoPath} && mkdir -p ${refRepoPath} git clone --mirror https://github.com/ROCm/rocm-libraries.git ${refRepoPath} """ sh(script: cloneCommand, label: "clone ref repo") } echo "Completed git clone, lock released" } echo "rocm-libraries repo exists at ${refRepoPath}, performing git remote update..." echo "locking on label: ${lockLabel}" lock(lockLabel) { def fetchCommand = """ set -ex cd ${refRepoPath} git remote prune origin git remote update """ sh(script: fetchCommand, label: "update ref repo") } echo "Completed git ref repo fetch, lock released" } def checkoutRepo() { //update ref repo cloneUpdateRefRepo() def scmVars = checkout scm // getGitHubCommitHash reads SCMRevisionAction recorded before any local merge, // giving the true PR branch tip (pullHash) or branch HEAD (hash). // Falls back to ORIG_HEAD (pre-merge HEAD set by git merge) when SCMRevisionAction // is unavailable, then to HEAD for branch builds where no merge occurred. env.GIT_COMMIT = getGitHubCommitHash(currentBuild.rawBuild) ?: sh(returnStdout: true, script: ''' git rev-parse ORIG_HEAD 2>/dev/null || git rev-parse HEAD ''').trim() } def withWorkingDir(Closure body) { show_node_info() checkoutRepo() dir("${env.WORKSPACE}/${env.MIOPEN_DIR}") { if (utils == null) { utils = load "vars/utils.groovy" } body() } } // Loads utils if a "Restart from Stage" skipped all earlier withWorkingDir calls. def ensureUtils() { if (utils == null) { node(rocmnode("nogpu")) { withWorkingDir {} } } } // Posts GitHub status from the pipeline post section, loading utils if needed. def postGithubStatus(String context, String state, String description, Closure extraActions = null) { if (utils == null) { node(rocmnode("nogpu")) { withWorkingDir { utils.setGithubStatus(context, state, description) if (extraActions) { extraActions() } } } } else { node { utils.setGithubStatus(context, state, description) if (extraActions) { extraActions() } } } } def runDbSyncJob(def flags, def gpu_family) { script { withWorkingDir { utils.buildHipClangJob(dvc_pull: true, setup_flags: "-DMIOPEN_TEST_DBSYNC=1" + flags, make_targets: 'test_db_sync', execute_cmd: './bin/test_db_sync', needs_gpu:false, build_install: true, gpu_family: gpu_family ) } } } def runBuildAndSingleGtestJob(Map conf=[:]) { def flags = conf.get('flags', '') def build_timeout_minutes = (conf.get('build_timeout_minutes', 420) as Integer) def gpu_family = conf.get('gpu_family', null) // catchError prevents exceptions from propagating so junit always runs. def buildReached = false catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') { withWorkingDir { def single_gtest_flags = " -DMIOPEN_TEST_DISCRETE=OFF -DGTEST_PARALLEL_LEVEL=4 " buildReached = true utils.buildHipClangJob( setup_flags: single_gtest_flags + flags, build_cmd: "LLVM_PATH=/opt/rocm/llvm CTEST_PARALLEL_LEVEL=4 ninja -j\$(nproc) install miopen_gtest check", build_install:true, build_timeout:build_timeout_minutes, gpu_family: gpu_family) } } // Post failure if catchError swallowed an exception before buildHipClangJob ran. if (!buildReached && utils != null) { utils.setGithubStatus(env.STAGE_NAME, 'failure', 'Stage failed before build started') } // Collect JUnit XML even on failure. def xmlGlob = "${env.MIOPEN_DIR}/build/test_results/miopen_gtest_shard*.xml" junit allowEmptyResults: true, testResults: xmlGlob } def runDockerBuild(String gpuFamily) { node(rocmnode("docker")) { try { withWorkingDir { def (dockerImage, imageName) = utils.getDockerImageWithStatus(gpu_family: gpuFamily, ensure_only: true) // Expose the built CI image name so Publish Dev Image can retag it directly. if (gpuFamily == "ci") { env.CI_DOCKER_IMAGE = imageName } } } finally { cleanWs() } } } def runTheRockDockerBuild() { node(rocmnode("docker")) { try { withWorkingDir { def result = utils.buildTheRockDockerImage() // Expose values to subsequent stages via pipeline-scoped env vars. env.THEROCK_CANDIDATE_IMAGE = result.image ?: "" env.THEROCK_FULL_HASH = result.fullHash ?: "" env.THEROCK_SHORT_HASH = result.shortHash ?: "" // "true" means the hash is already live on :therock - Promote stage is skipped. env.THEROCK_SKIP_UPDATE = result.skip ? "true" : "false" } } finally { cleanWs() } } } def runDevImagePublish() { node(rocmnode("docker")) { try { withWorkingDir { utils.publishDevDockerImage() } } finally { cleanWs() } } } def runTheRockDockerPromote() { node(rocmnode("docker")) { try { withWorkingDir { utils.promoteTheRockDockerImage( env.THEROCK_CANDIDATE_IMAGE, env.THEROCK_FULL_HASH ) } } finally { cleanWs() } } } //launch develop branch nightly/weekly jobs CRON_SETTINGS = BRANCH_NAME == "develop" ? ''' 0 0 * * * % RUN_NIGHTLY_TESTS=true;BUILD_PACKAGE_AND_CHECKS=false;BUILD_FULL_TESTS=false;TARGET_GFX908=false;TARGET_GFX90A=true;TARGET_GFX942=true 0 1 * * * % NONCRITICAL_HW_NIGHTLY=true;BUILD_PACKAGE_AND_CHECKS=false;BUILD_FULL_TESTS=false;TARGET_GFX908=true;TARGET_NAVI35=true;TARGET_GFX90A=false;TARGET_GFX942=false 0 22 * * * % BUILD_THEROCK_DOCKER=true ''' : "" pipeline { agent none options { skipDefaultCheckout() // Disabled: selective-rerun needs all parallel stages to finish. // parallelsAlwaysFailFast() } triggers{ parameterizedCron(CRON_SETTINGS) } parameters { booleanParam( name: "BUILD_DOCKER", defaultValue: true, description: "") booleanParam( name: "BUILD_THEROCK_DOCKER", defaultValue: false, description: "") booleanParam( name: "BUILD_FULL_TESTS", defaultValue: true, description: "") booleanParam( name: "RUN_HIP_TIDY", defaultValue: true, description: "Control the Hip Tidy (clang-analyze) stage in Full Tests") booleanParam( name: "BUILD_PACKAGE_AND_CHECKS", defaultValue: true, description: "Control the Package and Static Checks stage") booleanParam( name: "TARGET_NOGPU", defaultValue: true, description: "") booleanParam( name: "TARGET_GFX908", defaultValue: false, description: "") booleanParam( name: "TARGET_GFX90A", defaultValue: true, description: "") booleanParam( name: "TARGET_GFX942", defaultValue: true, description: "") booleanParam( name: "TARGET_NAVI32", defaultValue: false, description: "") booleanParam( name: "TARGET_NAVI35", defaultValue: false, description: "Navi3.5 Strix Halo") booleanParam( name: "TARGET_NAVI4", defaultValue: false, description: "") booleanParam( name: "DATATYPE_NA", defaultValue: true, description: "") booleanParam( name: "DATATYPE_FP32", defaultValue: true, description: "") booleanParam( name: "DATATYPE_TF32", defaultValue: true, description: "") booleanParam( name: "DATATYPE_FP16", defaultValue: true, description: "") booleanParam( name: "DATATYPE_BF16", defaultValue: true, description: "") booleanParam( name: "DBSYNC_TEST", defaultValue: true, description: "Control DB Sync test execution in Full Tests") string(name: "DOCKER_IMAGE_OVERRIDE", defaultValue: '', description: "Specify a custom Docker image to use for CI for debugging") booleanParam( name: "USE_SCCACHE_DOCKER", defaultValue: true, description: "Use the sccache for building CK in the Docker Image (default: ON)") booleanParam( name: "RUN_NIGHTLY_TESTS", defaultValue: false, description: "Run the nightly tests (default: OFF)") booleanParam( name: "NONCRITICAL_HW_NIGHTLY", defaultValue: false, description: "Nightly builds for non-critical hardware (gfx908, Navi 3.5) (default: OFF)") } environment{ extra_log_env = " MIOPEN_LOG_LEVEL=5 " Fp16_flags = " -DMIOPEN_TEST_HALF=On" Bf16_flags = " -DMIOPEN_TEST_BFLOAT16=On" Int8_flags = " -DMIOPEN_TEST_INT8=On" Full_test = " -DMIOPEN_TEST_ALL=On" Tf32_flags = " -DMIOPEN_TEST_TF32=On" gfx908_flags = " -DMIOPEN_INSTALL_GPU_DATABASES=gfx908" gfx90a_flags = " -DMIOPEN_INSTALL_GPU_DATABASES=gfx90a" gfx942_flags = " -DMIOPEN_INSTALL_GPU_DATABASES=gfx942" gfx1151_flags = " -DMIOPEN_INSTALL_GPU_DATABASES=gfx1151" gfx1101_flags = " -DMIOPEN_INSTALL_GPU_DATABASES=gfx1101" Smoke_targets = " check MIOpenDriver" NOCOMGR_flags = " -DMIOPEN_USE_COMGR=Off" NOMLIR_flags = " -DMIOPEN_USE_MLIR=Off" CK_DIR = "${ckDir}" MIOPEN_DIR = "${miopenDir}" PROJ_DIR = "${projDir}" Build_timeout_minutes = 420 } stages{ // Builds rocm/miopen:therock- if the pinned hash changed; sets THEROCK_CANDIDATE_IMAGE. stage('Build TheRock Docker: Build') { when { expression { params.BUILD_THEROCK_DOCKER } } steps { script { runTheRockDockerBuild() } } } // Always runs when BUILD_THEROCK_DOCKER=true, even if the TheRock hash is unchanged. // This is intentional: nightly CI catches CK regressions and produces the dev image // regardless of whether TheRock itself advanced. stage('Build Docker'){ when { expression { params.BUILD_THEROCK_DOCKER \ || (params.BUILD_DOCKER && params.TARGET_NOGPU && params.DATATYPE_NA) } } parallel { stage('Build Docker for CI') { steps { script { runDockerBuild('ci') } } } // Unified docker build for CI // stage('Build Docker gfx90X') { steps { script { runDockerBuild('gfx90X') } } } // stage('Build Docker gfx942, gfx950') { steps { script { runDockerBuild('gfx942_gfx950') } } } // stage('Build Docker Navi(gfx1101, gfx1151)') { steps { script { runDockerBuild('navi') } } } } } stage("Package and Static checks") { when { expression { params.BUILD_THEROCK_DOCKER \ || (params.BUILD_PACKAGE_AND_CHECKS && params.TARGET_NOGPU && params.DATATYPE_NA) } } steps { script { ensureUtils() parallel utils.packageAndStaticCheckStages(params, env, this.&rocmnode, this.&withWorkingDir) } } } stage("Full Tests") { when { expression { params.BUILD_FULL_TESTS || params.BUILD_THEROCK_DOCKER } } steps { script { ensureUtils() parallel utils.fullTestStages(params, env, this.&rocmnode, this.&withWorkingDir, this.&runDbSyncJob, this.&runBuildAndSingleGtestJob) } } } // Promotes the candidate to :therock only after all CI stages pass. stage('Build TheRock Docker: Promote') { when { expression { params.BUILD_THEROCK_DOCKER && env.THEROCK_SKIP_UPDATE != 'true' } } steps { script { runTheRockDockerPromote() } } } // Publishes rocm/miopen-dev:multiarch_dev_ and :latest after CI passes. stage('Publish Dev Image') { when { expression { params.BUILD_THEROCK_DOCKER } } steps { script { runDevImagePublish() } } } stage("Nightly Tests") { when { expression { params.RUN_NIGHTLY_TESTS } } steps { script { ensureUtils() parallel utils.nightlyTestStages(params, env, this.&rocmnode, this.&withWorkingDir) } } } stage("Non-Critical HW Nightly Tests") { when { expression { params.NONCRITICAL_HW_NIGHTLY } } steps { script { ensureUtils() parallel utils.nonCriticalHWNightlyStages(params, env, this.&rocmnode, this.&withWorkingDir, this.&runDbSyncJob, this.&runBuildAndSingleGtestJob) } } } } post { success { script { postGithubStatus('Math CI Summary', 'success', 'All checks have passed') } } failure { script { postGithubStatus('Math CI Summary', 'failure', 'Some checks have failed') { utils.sendTeamsFailureNotification(buildTheRock: params.BUILD_THEROCK_DOCKER) } } } } }