Newer
Older
mbed-os / platform / mbed-trace / Jenkinsfile
@Lingkai Dong Lingkai Dong on 2 Sep 2020 9 KB Move mbed-trace into platform
echo "Start to build"

properties ([
  buildDiscarder(
    logRotator(
      artifactNumToKeepStr: '10',
      numToKeepStr: '100'
    )
  )
])

// List of targets to compile
def morpheusTargets = [
  //"LPC1768",
  //"NUCLEO_F401RE",
  "K64F"
]
// Map morpheus toolchains to compiler labels on Jenkins
def toolchains = [
  ARM: "armcc",
  IAR: "iar_arm",
  GCC_ARM: "arm-none-eabi-gcc"
]
// yotta target includes toolchain
def yottaTargets = [
  "frdm-k64f-gcc": "gcc",
  "frdm-k64f-armcc": "armcc",
  "stm32f429i-disco-gcc": "gcc",
  "x86-linux-native": "linux && astyle"
]

// Initial maps for parallel build steps
def stepsForParallel = [:]
// Jenkins pipeline does not support map.each, we need to use oldschool for loop
for (int i = 0; i < morpheusTargets.size(); i++) {
  for(int j = 0; j < toolchains.size(); j++) {
    def target = morpheusTargets.get(i)
    def toolchain = toolchains.keySet().asList().get(j)
    def compilerLabel = toolchains.get(toolchain)
    def stepName = "mbed-os5-${target} ${toolchain}"
    stepsForParallel[stepName] = morpheusBuildStep(target, compilerLabel, toolchain)
  }
}
// map yotta steps
for (int i = 0; i < yottaTargets.size(); i++) {
  def target = yottaTargets.keySet().asList().get(i)
  def compilerLabel = yottaTargets.get(target)
  def stepName = "mbed-os3-${target}"
  stepsForParallel[stepName] = yottaBuildStep(target, compilerLabel)
}

/* Jenkins does not allow stages inside parallel execution,
 * https://issues.jenkins-ci.org/browse/JENKINS-26107 will solve this by adding labeled blocks
 */
// Actually run the steps in parallel - parallel takes a map as an argument, hence the above.
timestamps {
  timeout(time: 30, unit: "MINUTES") {
    parallel stepsForParallel
  }
}

def execute(cmd) {
  if(isUnix()) {
   sh "${cmd}"
  } else {
   bat "${cmd}"
  }
}

//Create morpheus build steps for parallel execution
def morpheusBuildStep(target, compilerLabel, toolchain) {
  return {
    node ("${compilerLabel}") {
      deleteDir()
      dir("mbed-trace") {
        String buildName = "mbed-os5-${target}-${toolchain}"
        def scmVars = checkout scm
        env.GIT_COMMIT_HASH = scmVars.GIT_COMMIT
        setBuildStatus('PENDING', "build ${buildName}", 'build starts')
        stage ("build:${buildName}") {
          try{
            execute("mbed --version")
            execute("echo https://github.com/armmbed/mbed-os/#6a0a86538c0b9b2bfcc4583b1e2b7fea8f4e71e9 > mbed-os.lib")
            execute("mbed deploy")
            execute("rm -rf ./mbed-os/platform/mbed-trace")
            execute("mbed compile -m ${target} -t ${toolchain} --library")
            setBuildStatus('SUCCESS', "build ${buildName}", "build done")
          } catch (err) {
            echo "Caught exception: ${err}"
            setBuildStatus('FAILURE', "build ${buildName}", "build failed")
            throw err
          }
        }
        stage("build:example:${buildName}") {
          execute("mkdir ../example-mbed-os-5 || true")
          execute("cp -R example/mbed-os-5 ../example-mbed-os-5")
          dir("../example-mbed-os-5") {
            def exampleName = "example-${buildName}"
            setBuildStatus('PENDING', "build ${exampleName}", 'build starts')
            try {
              execute("echo \"https://github.com/ARMmbed/mbed-os/#6a0a86538c0b9b2bfcc4583b1e2b7fea8f4e71e9\" > mbed-os.lib")
              execute("echo \"https://github.com/ARMmbed/mbed-trace#${env.GIT_COMMIT_HASH}\" > mbed-trace.lib")
              execute("mbed new .")
              execute("mbed deploy")
              execute("rm -rf ./mbed-os/platform/mbed-trace")
              execute("rm -rf ./mbed-trace/example")
              execute("rm -rf ./mbed-trace/test")
              execute("mbed compile -t ${toolchain} -m ${target}")
              setBuildStatus('SUCCESS', "build ${exampleName}", "build done")
            } catch(err) {
              echo "Caught exception: ${err}"
              setBuildStatus('FAILURE', "build ${exampleName}", "build failed")
              currentBuild.result = 'FAILURE'
            } finally {
              // clean up
              postBuild(buildName, false)
              step([$class: 'WsCleanup'])
            }
          }
        }
      }
    }
  }
}

//Create yotta build steps for parallel execution
def yottaBuildStep(target, compilerLabel) {
  return {
    String buildName = "mbed-os3-${target}"
    node ("${compilerLabel}") {
      deleteDir()
      dir("mbed-trace") {
        def scmVars = checkout scm
        env.GIT_COMMIT_HASH = scmVars.GIT_COMMIT
        def isTest = target == "x86-linux-native" // tests are valid only in linux target
        stage ("build:${buildName}") {
          setBuildStatus('PENDING', "build ${buildName}", 'build starts')
          try{
            execute("yotta --version")
            execute("yotta target $target")
            execute("yotta --plain build mbed-trace")
            setBuildStatus('SUCCESS', "build ${buildName}", "build done")
          } catch (err) {
            echo "Caught exception: ${err}"
            setBuildStatus('FAILURE', "build ${buildName}", "build failed")
            currentBuild.result = 'FAILURE'
          }
        } // stage
        if (isTest) {
          stage("test:${buildName}") {
            setBuildStatus('PENDING', "test ${buildName}", 'test starts')
            try {
              execute("yotta test mbed_trace_test")
              execute("lcov --base-directory . --directory . --capture --output-file coverage.info")
              execute("genhtml -o ./test_coverage coverage.info")
              execute("gcovr -x -o junit.xml")
              execute("cppcheck --enable=all --std=c99 --inline-suppr --template=\"{file},{line},{severity},{id},{message}\" source 2> cppcheck.txt")

              // check if astyle is correct
              execute("astyle --options=.astylerc source/*.c mbed-trace/*.h")
              // check differency
              execute("git diff-index -p --exit-code HEAD")

              setBuildStatus('SUCCESS', "test ${buildName}", "test done")
            } catch(err) {
              echo "Caught exception: ${err}"
              setBuildStatus('FAILURE', "test ${buildName}", "test failed")
              currentBuild.result = 'FAILURE'
            }
          } // stage
          stage("example:${buildName}") {
            dir("example/linux") {
              def exampleName = "example-linux"
              setBuildStatus('PENDING', "build ${exampleName}", 'build starts')
              try {
                execute("make")
                setBuildStatus('SUCCESS', "build ${exampleName}", "build done")
              } catch(err) {
                echo "Caught exception: ${err}"
                setBuildStatus('FAILURE', "build ${exampleName}", "build failed")
                currentBuild.result = 'FAILURE'
              }
            }
          } // stage

          stage("leak-check:${buildName}") {
            dir("example/linux") {
              def stageName = "leak-check"
              setBuildStatus('PENDING', "test ${stageName}", 'test starts')
              try {
                execute("./memtest.sh")
                setBuildStatus('SUCCESS', "test ${stageName}", "test done")
              } catch(err) {
                echo "Caught exception: ${err}"
                setBuildStatus('FAILURE', "test ${stageName}", "test failed")
                currentBuild.result = 'FAILURE'
              }
            }
          } // stage
        } // if linux
        postBuild(buildName, isTest)
        step([$class: 'WsCleanup'])
      } // dir
    }
  }
}

def postBuild(buildName, isTest) {
    // move files to target+toolchain specific folder
    execute("mkdir -p output/${buildName}")
    execute("find . -name 'libmbed-trace.a' -exec mv {} 'output/${buildName}' \\;")
    execute("find . -name 'mbed-trace.ar' -exec mv {} 'output/${buildName}' \\;")
    execute("find ../example-mbed-os-5 -name 'example-mbed-os-5.bin' -exec mv {} 'output/${buildName}/example-mbed-os-5.bin' \\; || true")
    // Archive artifacts
    step([
      $class: 'ArtifactArchiver',
      artifacts: "cppcheck.txt,output/**",
      fingerprint: true,
      allowEmptyArchive: true
    ])
    if (isTest) {
        // Publish cobertura
        step([
            $class: 'CoberturaPublisher',
            coberturaReportFile: 'junit.xml'
        ])
        // Publish compiler warnings
        step([
          $class: 'WarningsPublisher',
          parserConfigurations: [[
            parserName: 'GNU Make + GNU C Compiler (gcc)',
            pattern: 'mbed-trace/*.h,source/*.c,test/*.cpp'
          ]],
          unstableTotalAll: '0',
          useDeltaValues: true,
          usePreviousBuildAsReference: true
        ])
        // Publish HTML reports
        publishHTML(target: [
          alwayLinkToLastBuild: false,
          keepAll: true,
          reportDir: "test_coverage",
          reportFiles: "index.html",
          reportName: "Build HTML Report"
        ])
    }
}
// helper function to set build status to github PR
def setBuildStatus(String state, String context, String message) {
    step([
        $class: "GitHubCommitStatusSetter",
        reposSource: [
            $class: "ManuallyEnteredRepositorySource",
            url: "https://github.com/ARMmbed/mbed-trace.git"
        ],
        contextSource: [
            $class: "ManuallyEnteredCommitContextSource",
            context: context
        ],
        errorHandlers: [[
            $class: "ChangingBuildStatusErrorHandler",
            result: "UNSTABLE"
        ]],
        commitShaSource: [
            $class: "ManuallyEnteredShaSource",
            sha: env.GIT_COMMIT_HASH
        ],
        statusResultSource: [
            $class: 'ConditionalStatusResultSource',
            results: [
                [
                    $class: 'AnyBuildResult',
                    message: message,
                    state: state
                ]
            ]
        ]
    ])
}