├── .github ├── dependabot.yml └── workflows │ └── cd.yaml ├── .gitignore ├── .mvn ├── extensions.xml └── maven.config ├── CHANGELOG.md ├── CONFIGDOC.md ├── Jenkinsfile ├── LICENSE ├── README.md ├── SECURITY.md ├── azure-pipelines.yml ├── devel └── contributing.md ├── examples └── Run-MATLAB-Tests.md ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── mathworks │ │ └── ci │ │ ├── BuildArtifactAction.java │ │ ├── BuildArtifactData.java │ │ ├── BuildConsoleAnnotator.java │ │ ├── BuildTargetNote.java │ │ ├── FormValidationUtil.java │ │ ├── ListenerLogDecorator.java │ │ ├── MatlabBuildWrapperContent.java │ │ ├── MatlabBuilderConstants.java │ │ ├── MatlabExecutionException.java │ │ ├── MatlabInstallation.java │ │ ├── MatlabInstallationAxis.java │ │ ├── MatlabItemListener.java │ │ ├── MatlabNotFoundError.java │ │ ├── MatlabReleaseInfo.java │ │ ├── MatlabVersionNotFoundException.java │ │ ├── MatrixPatternResolver.java │ │ ├── Message.java │ │ ├── UseMatlabVersionBuildWrapper.java │ │ ├── Utilities.java │ │ ├── actions │ │ ├── MatlabAction.java │ │ ├── MatlabActionFactory.java │ │ ├── RunMatlabBuildAction.java │ │ ├── RunMatlabCommandAction.java │ │ └── RunMatlabTestsAction.java │ │ ├── freestyle │ │ ├── RunMatlabBuildBuilder.java │ │ ├── RunMatlabCommandBuilder.java │ │ ├── RunMatlabTestsBuilder.java │ │ └── options │ │ │ ├── BuildOptions.java │ │ │ ├── SelectByFolder.java │ │ │ ├── SourceFolder.java │ │ │ ├── SourceFolderPaths.java │ │ │ ├── StartupOptions.java │ │ │ └── TestFolders.java │ │ ├── parameters │ │ ├── BuildActionParameters.java │ │ ├── CommandActionParameters.java │ │ ├── MatlabActionParameters.java │ │ └── TestActionParameters.java │ │ ├── pipeline │ │ ├── MatlabBuildStepExecution.java │ │ ├── MatlabCommandStepExecution.java │ │ ├── MatlabRunTestsStepExecution.java │ │ ├── RunMatlabBuildStep.java │ │ ├── RunMatlabCommandStep.java │ │ └── RunMatlabTestsStep.java │ │ ├── tools │ │ ├── InstallationFailedException.java │ │ ├── MatlabInstaller.java │ │ └── MatlabRelease.java │ │ └── utilities │ │ ├── GetSystemProperties.java │ │ └── MatlabCommandRunner.java ├── resources │ ├── +ciplugins │ │ └── +jenkins │ │ │ ├── BuildReportPlugin.m │ │ │ ├── TaskRunProgressPlugin.m │ │ │ └── getDefaultPlugins.m │ ├── com │ │ └── mathworks │ │ │ └── ci │ │ │ ├── BuildArtifactAction │ │ │ ├── index.jelly │ │ │ └── summary.jelly │ │ │ ├── MatlabBuilder │ │ │ ├── RunTestsAutomaticallyOption │ │ │ │ ├── config.jelly │ │ │ │ ├── help-taCoberturaChkBx.html │ │ │ │ ├── help-taJunitChkBx.html │ │ │ │ ├── help-taModelCoverageChkBx.html │ │ │ │ ├── help-taPDFReportChkBx.html │ │ │ │ ├── help-taSTMResultsChkBx.html │ │ │ │ └── help-tatapChkBx.html │ │ │ ├── RunTestsWithCustomCommandOption │ │ │ │ ├── config.jelly │ │ │ │ └── help-customMatlabCommand.html │ │ │ ├── config.jelly │ │ │ ├── help-matlabRoot.html │ │ │ ├── help-testRunTypeList.html │ │ │ └── runMatlabTests.m │ │ │ ├── MatlabInstallation │ │ │ ├── config.jelly │ │ │ └── help-home.html │ │ │ ├── MatlabInstallationAxis │ │ │ ├── config.jelly │ │ │ └── help.html │ │ │ ├── SelectByTag │ │ │ └── config.jelly │ │ │ ├── UseMatlabVersionBuildWrapper │ │ │ ├── config.jelly │ │ │ ├── help-matlabRootFolder.html │ │ │ └── help.html │ │ │ ├── freestyle │ │ │ ├── RunMatlabBuildBuilder │ │ │ │ ├── config.jelly │ │ │ │ ├── help-buildOptions.html │ │ │ │ ├── help-startupOptions.html │ │ │ │ └── help-tasks.html │ │ │ ├── RunMatlabCommandBuilder │ │ │ │ ├── config.jelly │ │ │ │ ├── help-matlabCommand.html │ │ │ │ └── help-startupOptions.html │ │ │ ├── RunMatlabTestsBuilder │ │ │ │ ├── SelectByTag │ │ │ │ │ └── config.jelly │ │ │ │ ├── config.jelly │ │ │ │ ├── help-coberturaArtifact.html │ │ │ │ ├── help-junitArtifact.html │ │ │ │ ├── help-loggingLevel.html │ │ │ │ ├── help-modelCoverageArtifact.html │ │ │ │ ├── help-outputDetail.html │ │ │ │ ├── help-pdfReportArtifact.html │ │ │ │ ├── help-selectByFolder.html │ │ │ │ ├── help-selectByTag.html │ │ │ │ ├── help-sourceFolder.html │ │ │ │ ├── help-startupOptions.html │ │ │ │ ├── help-stmResultsArtifact.html │ │ │ │ ├── help-strict.html │ │ │ │ ├── help-tapArtifact.html │ │ │ │ └── help-useParallel.html │ │ │ └── options │ │ │ │ ├── BuildOptions │ │ │ │ └── config.jelly │ │ │ │ ├── SelectByFolder │ │ │ │ └── config.jelly │ │ │ │ ├── SourceFolder │ │ │ │ └── config.jelly │ │ │ │ └── StartupOptions │ │ │ │ └── config.jelly │ │ │ ├── pipeline │ │ │ ├── RunMatlabBuildStep │ │ │ │ ├── config.jelly │ │ │ │ ├── help-buildOptions.html │ │ │ │ ├── help-startupOptions.html │ │ │ │ └── help-tasks.html │ │ │ ├── RunMatlabCommandStep │ │ │ │ ├── config.jelly │ │ │ │ ├── help-command.html │ │ │ │ └── help-startupOptions.html │ │ │ └── RunMatlabTestsStep │ │ │ │ ├── config.jelly │ │ │ │ ├── help-codeCoverageCobertura.html │ │ │ │ ├── help-loggingLevel.html │ │ │ │ ├── help-modelCoverageCobertura.html │ │ │ │ ├── help-outputDetail.html │ │ │ │ ├── help-selectByFolder.html │ │ │ │ ├── help-selectByTag.html │ │ │ │ ├── help-sourceFolder.html │ │ │ │ ├── help-startupOptions.html │ │ │ │ ├── help-strict.html │ │ │ │ ├── help-testResultsJUnit.html │ │ │ │ ├── help-testResultsPDF.html │ │ │ │ ├── help-testResultsSimulinkTest.html │ │ │ │ ├── help-testResultsTAP.html │ │ │ │ └── help-useParallel.html │ │ │ └── tools │ │ │ └── MatlabInstaller │ │ │ ├── config.jelly │ │ │ ├── help-products.html │ │ │ └── help-release.html │ ├── config.properties │ └── index.jelly └── webapp │ └── disable.js └── test ├── java ├── integ │ └── com │ │ └── mathworks │ │ └── ci │ │ ├── BuildArtifactActionTest.java │ │ ├── MatlabInstallationTest.java │ │ ├── RunMatlabBuildBuilderTest.java │ │ ├── RunMatlabBuildBuilderTester.java │ │ ├── RunMatlabBuildStepTest.java │ │ ├── RunMatlabCommandBuilderTest.java │ │ ├── RunMatlabCommandBuilderTester.java │ │ ├── RunMatlabCommandStepTest.java │ │ ├── RunMatlabTestBuilderPersistenceTest.java │ │ ├── RunMatlabTestsBuilderTest.java │ │ ├── RunMatlabTestsBuilderTester.java │ │ ├── RunMatlabTestsStepTest.java │ │ ├── TestMessage.java │ │ └── UseMatlabVersionBuildWrapperTest.java └── unit │ └── com │ └── mathworks │ └── ci │ ├── actions │ ├── MatlabActionTest.java │ ├── RunMatlabBuildActionTest.java │ ├── RunMatlabCommandActionTest.java │ └── RunMatlabTestsActionTest.java │ ├── freestyle │ ├── RunMatlabBuildBuilderUnitTest.java │ ├── RunMatlabCommandBuilderUnitTest.java │ └── RunMatlabTestsBuilderUnitTest.java │ ├── pipeline │ ├── MatlabBuildStepExecutionUnitTest.java │ ├── MatlabCommandStepExecutionUnitTest.java │ └── MatlabRunTestsStepExecutionUnitTest.java │ ├── tools │ └── MatlabInstallerUnitTest.java │ └── utilities │ ├── GetSystemPropertiesUnitTest.java │ ├── MatlabCommandRunnerTest.java │ └── MatlabCommandRunnerTester.java └── resources ├── buildArtifacts ├── t1 │ └── buildArtifact.json └── t2 │ └── buildArtifact.json ├── com └── mathworks │ └── ci │ ├── linux │ └── bin │ │ └── matlab.sh │ └── win │ └── bin │ └── matlab.bat ├── mockito-extensions └── org.mockito.plugins.MockMaker ├── run_matlab_command_test.bat ├── run_matlab_command_test.sh ├── testconfig.properties ├── testcontent.txt └── versioninfo ├── R2015a └── toolbox │ └── matlab │ └── general │ └── Contents.m ├── R2015b └── toolbox │ └── matlab │ └── general │ └── Contents.m ├── R2016b ├── READ.txt └── toolbox │ └── matlab │ └── general │ └── Contents.m ├── R2017a ├── VersionInfo.xml └── bin │ ├── matlab │ └── matlab.exe ├── R2018a ├── VersionInfo.xml └── bin │ ├── matlab │ └── matlab.exe └── R2018b ├── VersionInfo.xml └── bin ├── matlab └── matlab.exe /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuring-dependabot-version-updates 2 | --- 3 | version: 2 4 | updates: 5 | - package-ecosystem: maven 6 | directory: / 7 | schedule: 8 | interval: monthly 9 | - package-ecosystem: github-actions 10 | directory: / 11 | schedule: 12 | interval: monthly 13 | -------------------------------------------------------------------------------- /.github/workflows/cd.yaml: -------------------------------------------------------------------------------- 1 | # Note: additional setup is required, see https://www.jenkins.io/redirect/continuous-delivery-of-plugins 2 | # 3 | # Please find additional hints for individual trigger use case 4 | # configuration options inline this script below. 5 | # 6 | --- 7 | name: cd 8 | on: 9 | workflow_dispatch: 10 | inputs: 11 | validate_only: 12 | required: false 13 | type: boolean 14 | description: | 15 | Run validation with release drafter only 16 | → Skip the release job 17 | # Note: Change this default to true, 18 | # if the checkbox should be checked by default. 19 | default: false 20 | ###### Opting for manual release trigger so commented check_run trigger 21 | # If you don't want any automatic trigger in general, then 22 | # the following check_run trigger lines should all be commented. 23 | # Note: Consider the use case #2 config for 'validate_only' below 24 | # as an alternative option! 25 | # check_run: 26 | # types: 27 | # - completed 28 | 29 | permissions: 30 | checks: read 31 | contents: write 32 | 33 | jobs: 34 | maven-cd: 35 | uses: jenkins-infra/github-reusable-workflows/.github/workflows/maven-cd.yml@v1 36 | with: 37 | ###### Opting for a manual release trigger so updated validate_only accordingly 38 | # Comment / uncomment the validate_only config appropriate to your preference: 39 | # 40 | # Use case #1 (automatic release): 41 | # - Let any successful Jenkins build trigger another release, 42 | # if there are merged pull requests of interest 43 | # - Perform a validation only run with drafting a release note, 44 | # if manually triggered AND inputs.validate_only has been checked. 45 | # 46 | # validate_only: ${{ inputs.validate_only == true }} 47 | # 48 | # Alternative use case #2 (no automatic release): 49 | # - Same as use case #1 - but: 50 | # - Let any check_run trigger a validate_only run. 51 | # => enforce the release job to be skipped. 52 | # 53 | validate_only: ${{ inputs.validate_only == true || github.event_name == 'check_run' }} 54 | secrets: 55 | MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} 56 | MAVEN_TOKEN: ${{ secrets.MAVEN_TOKEN }} 57 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Jenkins development server folders 2 | target 3 | work 4 | 5 | # Binaries and downloaded resources 6 | src/main/resources/matlab-script-generator.zip 7 | src/main/resources/**/run-matlab-command* 8 | src/main/resources/license.txt 9 | 10 | .idea/ 11 | 12 | **/.DS_Store -------------------------------------------------------------------------------- /.mvn/extensions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | io.jenkins.tools.incrementals 4 | git-changelist-maven-extension 5 | 1.8 6 | 7 | 8 | -------------------------------------------------------------------------------- /.mvn/maven.config: -------------------------------------------------------------------------------- 1 | -Pconsume-incrementals 2 | -Pmight-produce-incrementals 3 | -Dchangelist.format=%d.v%s 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Change log 2 | === 3 | 4 | ### Newer Versions 5 | 6 | For newer versions of the plugin, see [GitHub releases.](https://github.com/jenkinsci/matlab-plugin/releases) 7 | 8 | ### 1.0.3 9 | 10 | Release date: _Nov 05, 2019_ 11 | 12 | * Bug Fix : Scratch file copy is restricted to "Automatic" option. 13 | * README doc updates. 14 | 15 | 16 | ### 1.0.2 17 | 18 | Release date: _Aug 21, 2019_ 19 | 20 | * MATRIX build support. 21 | * Plugin support for older versions of MATLAB until R2013a. 22 | 23 | ### 1.0.1 24 | 25 | Release date: _May 10, 2019_ 26 | 27 | * Jenkins run-time environment variables support in MATLAB script. 28 | * Enhancement of README with SCM configuration step for "Automatic" Test mode. 29 | 30 | ### 1.0.0 31 | 32 | Release date: _Apr 08, 2019_ 33 | 34 | * Initial release. 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | buildPlugin(jdkVersions: [11]) 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 MathWorks 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Continuous Integration with MATLAB on Jenkins 2 | This plugin enables you to build and test your MATLAB® project as part of your Jenkins™ build. For example, you can automatically identify any code issues in your project, run tests and generate test and coverage artifacts, and package your files into a toolbox. 3 | 4 | ## Releases 5 | For a detailed list of releases, see [Change Logs](/CHANGELOG.md). 6 | 7 | ## Configuration Steps 8 | To configure the plugin, see [Plugin Configuration Guide](/CONFIGDOC.md). 9 | 10 | ## Examples 11 | To learn how to use the plugin in testing workflows, see [Examples](/examples/). 12 | 13 | ## Contact Us 14 | If you have any questions or suggestions, please contact MathWorks® at [continuous-integration@mathworks.com](mailto:continuous-integration@mathworks.com). 15 | 16 | ## License 17 | MIT © 2019 The MathWorks, Inc. 18 | 19 | 20 | ## Build Results 21 | 22 | 23 | | Overall | 24 | |---| 25 | | [![Build Status](https://dev.azure.com/iat-ci/jenkins-matlab-plugin/_apis/build/status/mathworks.jenkins-matlab-plugin?branchName=master)](https://dev.azure.com/iat-ci/jenkins-matlab-plugin/_build/latest?definitionId=6&branchName=master) | 26 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Reporting Security Vulnerabilities 2 | 3 | If you believe you have discovered a security vulnerability, please report it to 4 | [security@mathworks.com](mailto:security@mathworks.com). Please see 5 | [MathWorks Vulnerability Disclosure Policy for Security Researchers](https://www.mathworks.com/company/aboutus/policies_statements/vulnerability-disclosure-policy.html) 6 | for additional information. 7 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # Maven 2 | # Build your Java project and run tests with Apache Maven. 3 | # Add steps that analyze code, save build artifacts, deploy, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/java 5 | 6 | strategy: 7 | matrix: 8 | linux: 9 | imageName: 'ubuntu-latest' 10 | mac: 11 | imageName: 'macOS-latest' 12 | windows: 13 | imageName: 'windows-latest' 14 | 15 | pool: 16 | vmImage: $(imageName) 17 | 18 | trigger: 19 | - master 20 | 21 | steps: 22 | - task: JavaToolInstaller@0 23 | inputs: 24 | versionSpec: '11' 25 | jdkArchitectureOption: 'x64' 26 | jdkSourceOption: 'PreInstalled' 27 | 28 | - task: Maven@4 29 | inputs: 30 | mavenPomFile: 'pom.xml' 31 | mavenOptions: '-Xmx3072m -Dmaven.javadoc.skip=true' 32 | publishJUnitResults: true 33 | testResultsFiles: '**/surefire-reports/TEST-*.xml' 34 | codeCoverageToolOption: 'JaCoCo' 35 | goals: 'verify' 36 | displayName: 'Build $(imageName)' 37 | 38 | -------------------------------------------------------------------------------- /devel/contributing.md: -------------------------------------------------------------------------------- 1 | ## Contributing 2 | 3 | Verify changes by running tests and building locally with the following command: 4 | 5 | ``` 6 | mvn verify 7 | ``` 8 | 9 | ## Creating a New Release 10 | 11 | Familiarize yourself with [Jenkins Plugin Release Using GitHub Actions](https://www.jenkins.io/doc/developer/publishing/releasing-cd/). 12 | 13 | Changes should be made on a new branch. The new branch should be merged to the main branch via a pull request. Ensure that all of the CI pipeline checks and tests have passed for your changes. 14 | 15 | After the pull request has been approved and merged to main, run the [cd.yaml](https://github.com/jenkinsci/matlab-plugin/actions/workflows/cd.yaml) file to create a new release. This workflow will create a release under [Releases](https://github.com/jenkinsci/matlab-plugin/releases). Review the autogenerated release notes and update them as needed. 16 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/BuildArtifactData.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci; 2 | 3 | /** 4 | * Copyright 2024 The MathWorks, Inc. 5 | */ 6 | 7 | public class BuildArtifactData { 8 | 9 | private String taskName; 10 | private String taskDuration; 11 | private boolean taskFailed; 12 | 13 | private String taskDescription; 14 | private boolean taskSkipped; 15 | private String skipReason; 16 | 17 | public BuildArtifactData() { 18 | } 19 | 20 | public String getTaskDuration() { 21 | return this.taskDuration; 22 | } 23 | 24 | public void setTaskDuration(String taskDuration) { 25 | this.taskDuration = taskDuration; 26 | } 27 | 28 | public String getTaskName() { 29 | return this.taskName; 30 | } 31 | 32 | public void setTaskName(String taskName) { 33 | this.taskName = taskName; 34 | } 35 | 36 | public boolean getTaskSkipped() { 37 | return this.taskSkipped; 38 | } 39 | 40 | public void setTaskSkipped(boolean taskSkipped) { 41 | this.taskSkipped = taskSkipped; 42 | } 43 | 44 | public String getSkipReason() { 45 | return (this.skipReason == null) ? "" : this.skipReason; 46 | } 47 | 48 | public void setSkipReason(String skipReason) { 49 | this.skipReason = skipReason; 50 | } 51 | 52 | public boolean getTaskFailed() { 53 | return this.taskFailed; 54 | } 55 | 56 | public void setTaskFailed(boolean taskFailed) { 57 | this.taskFailed = taskFailed; 58 | } 59 | 60 | public String getTaskDescription() { 61 | return this.taskDescription; 62 | } 63 | 64 | public void setTaskDescription(String taskDescription) { 65 | this.taskDescription = taskDescription; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/BuildConsoleAnnotator.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci; 2 | 3 | /** 4 | * Copyright 2024 The MathWorks, Inc. 5 | */ 6 | 7 | import com.google.common.base.Charsets; 8 | import hudson.console.ConsoleLogFilter; 9 | import hudson.console.LineTransformationOutputStream; 10 | import hudson.model.Run; 11 | import java.io.ByteArrayOutputStream; 12 | import java.io.IOException; 13 | import java.io.OutputStream; 14 | import java.io.Serializable; 15 | import java.nio.ByteBuffer; 16 | import java.nio.charset.Charset; 17 | import jenkins.util.JenkinsJVM; 18 | 19 | public class BuildConsoleAnnotator extends LineTransformationOutputStream { 20 | private final OutputStream out; 21 | private final Charset charset; 22 | 23 | private final byte[][] antNotes; 24 | 25 | public BuildConsoleAnnotator(OutputStream out, Charset charset) { 26 | this(out, charset, createBuildNotes()); 27 | } 28 | 29 | private BuildConsoleAnnotator(OutputStream out, Charset charset, byte[][] antNotes) { 30 | this.out = out; 31 | this.charset = charset; 32 | this.antNotes = antNotes; 33 | } 34 | 35 | private static byte[][] createBuildNotes() { 36 | JenkinsJVM.checkJenkinsJVM(); 37 | try { 38 | ByteArrayOutputStream targetNote = new ByteArrayOutputStream(); 39 | new BuildTargetNote().encodeTo(targetNote); 40 | ByteArrayOutputStream outcomeNote = new ByteArrayOutputStream(); 41 | return new byte[][] { targetNote.toByteArray(), outcomeNote.toByteArray() }; 42 | } catch (IOException e) { 43 | throw new RuntimeException(e); 44 | } 45 | } 46 | 47 | @Override 48 | protected void eol(byte[] b, int len) throws IOException { 49 | String line = charset.decode(ByteBuffer.wrap(b, 0, len)).toString(); 50 | // trim off CR/LF from the end 51 | line = trimEOL(line); 52 | if (line.contains("[MATLAB-Build-")) 53 | out.write(antNotes[0]); 54 | 55 | out.write(b, 0, len); 56 | } 57 | 58 | @Override 59 | public void flush() throws IOException { 60 | out.flush(); 61 | } 62 | 63 | @Override 64 | public void close() throws IOException { 65 | super.close(); 66 | out.close(); 67 | } 68 | 69 | private static class ConsoleLogFilterImpl extends ConsoleLogFilter implements Serializable { 70 | private static final long serialVersionUID = 1; 71 | private byte[][] buildNotes = createBuildNotes(); 72 | 73 | // Taking care of old MATLAB build actions. 74 | private Object readResolve() { 75 | if (buildNotes == null) { 76 | buildNotes = createBuildNotes(); 77 | } 78 | return this; 79 | } 80 | 81 | @Override 82 | public OutputStream decorateLogger(Run build, OutputStream logger) throws IOException, InterruptedException { 83 | return new BuildConsoleAnnotator(logger, Charsets.UTF_8, buildNotes); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/BuildTargetNote.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci; 2 | 3 | /** 4 | * Copyright 2024 The MathWorks, Inc. 5 | */ 6 | 7 | import com.google.common.annotations.VisibleForTesting; 8 | import hudson.Extension; 9 | import hudson.MarkupText; 10 | import hudson.console.ConsoleAnnotationDescriptor; 11 | import hudson.console.ConsoleAnnotator; 12 | import hudson.console.ConsoleNote; 13 | import java.util.regex.Pattern; 14 | 15 | public class BuildTargetNote extends ConsoleNote { 16 | @VisibleForTesting 17 | public static boolean ENABLED = !Boolean.getBoolean(BuildTargetNote.class.getName() + ".disabled"); 18 | 19 | public BuildTargetNote() { 20 | } 21 | 22 | @Override 23 | public ConsoleAnnotator annotate(Object context, MarkupText text, int charPos) { 24 | MarkupText.SubText t = text.findToken(Pattern.compile("MATLAB-Build-")); 25 | String taskName = text.subText(13, text.length() - 2).getText(); 26 | taskName = taskName.replace("]", "").trim(); 27 | if (t != null) 28 | t.addMarkup(0, t.length() - 1, "", ""); 29 | return null; 30 | } 31 | 32 | @Extension 33 | public static final class DescriptorImpl extends ConsoleAnnotationDescriptor { 34 | public String getDisplayName() { 35 | return "Build targets"; 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/FormValidationUtil.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci; 2 | 3 | /** 4 | * Copyright 2019-2024 The MathWorks, Inc. 5 | * 6 | * This is Utility class which provides commonly used methods for form validations across builders 7 | */ 8 | 9 | import java.util.List; 10 | import java.util.function.Function; 11 | import hudson.util.FormValidation; 12 | import hudson.util.FormValidation.Kind; 13 | 14 | public class FormValidationUtil { 15 | 16 | public static FormValidation getFirstErrorOrWarning( 17 | List> validations, String validationArg) { 18 | if (validations == null || validations.isEmpty()) 19 | return FormValidation.ok(); 20 | try { 21 | for (Function val : validations) { 22 | FormValidation validationResult = val.apply(validationArg); 23 | if (validationResult.kind.compareTo(Kind.ERROR) == 0 24 | || validationResult.kind.compareTo(Kind.WARNING) == 0) { 25 | return validationResult; 26 | } 27 | } 28 | } catch (Exception e) { 29 | return FormValidation.warning(Message.getValue("Builder.invalid.matlab.root.warning")); 30 | } 31 | return FormValidation.ok(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/ListenerLogDecorator.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci; 2 | 3 | /** 4 | * Copyright 2018-2024 The MathWorks, Inc. 5 | */ 6 | 7 | import java.io.IOException; 8 | import java.io.OutputStream; 9 | import java.nio.charset.Charset; 10 | import hudson.console.LineTransformationOutputStream; 11 | import hudson.model.TaskListener; 12 | 13 | public class ListenerLogDecorator extends LineTransformationOutputStream { 14 | private OutputStream listener; 15 | private final Charset charsetUtf8 = Charset.forName("UTF-8"); 16 | 17 | public ListenerLogDecorator(TaskListener listner) throws IOException { 18 | this.listener = listner != null ? listner.getLogger() : null; 19 | } 20 | 21 | @Override 22 | protected void eol(byte[] bytes, int length) throws IOException { 23 | if (this.listener == null) { 24 | return; 25 | } 26 | 27 | String line = new String(bytes, 0, length, charsetUtf8); 28 | this.listener.write(line.getBytes(charsetUtf8)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/MatlabBuildWrapperContent.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci; 2 | 3 | /** 4 | * Copyright 2020-2024 The MathWorks, Inc. 5 | * 6 | * Class to parse Stapler request for Use MATLAB Version build wrapper. 7 | */ 8 | 9 | import org.kohsuke.stapler.DataBoundConstructor; 10 | 11 | public class MatlabBuildWrapperContent { 12 | 13 | private final String matlabInstallationName; 14 | private final String matlabRootFolder; 15 | 16 | @DataBoundConstructor 17 | public MatlabBuildWrapperContent(String matlabInstallationName, String matlabRootFolder) { 18 | this.matlabInstallationName = matlabInstallationName; 19 | this.matlabRootFolder = matlabRootFolder; 20 | } 21 | 22 | public String getMatlabInstallationName() { 23 | return matlabInstallationName; 24 | } 25 | 26 | public String getMatlabRootFolder() { 27 | return matlabRootFolder; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/MatlabBuilderConstants.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci; 2 | 3 | /* 4 | * Copyright 2019-2024 The MathWorks, Inc. 5 | */ 6 | 7 | public class MatlabBuilderConstants { 8 | public static final double BASE_MATLAB_VERSION_RUNTESTS_SUPPORT = 8.1; 9 | public static final double BASE_MATLAB_VERSION_NO_APP_ICON_SUPPORT = 8.6; 10 | public static final double BASE_MATLAB_VERSION_BATCH_SUPPORT = 9.5; 11 | public static final double BASE_MATLAB_VERSION_COBERTURA_SUPPORT = 9.3; 12 | public static final double BASE_MATLAB_VERSION_MODELCOVERAGE_SUPPORT = 9.5; 13 | public static final double BASE_MATLAB_VERSION_EXPORTSTMRESULTS_SUPPORT = 9.6; 14 | 15 | public static final String MATLAB_RUNNER_TARGET_FILE = "Builder.matlab.runner.target.file.name"; 16 | public static final String MATLAB_TESTS_RUNNER_TARGET_FILE = "runMatlabTests.m"; 17 | public static final String MATLAB_RUNNER_RESOURCE = "com/mathworks/ci/MatlabBuilder/runMatlabTests.m"; 18 | public static final String AUTOMATIC_OPTION = "RunTestsAutomaticallyOption"; 19 | 20 | // Input parameter names (Passed to runMatlabTests.m as name-value pair 21 | // arguments) 22 | public static final String PDF_REPORT = "'PDFReport'"; 23 | public static final String TAP_RESULTS = "'TAPResults'"; 24 | public static final String JUNIT_RESULTS = "'JUnitResults'"; 25 | public static final String STM_RESULTS = "'SimulinkTestResults'"; 26 | public static final String COBERTURA_CODE_COVERAGE = "'CoberturaCodeCoverage'"; 27 | public static final String COBERTURA_MODEL_COVERAGE = "'CoberturaModelCoverage'"; 28 | 29 | // Matlab Script generator package 30 | public static final String MATLAB_SCRIPT_GENERATOR = "matlab-script-generator.zip"; 31 | 32 | // Test runner file prefix 33 | public static final String MATLAB_TEST_RUNNER_FILE_PREFIX = "runner_"; 34 | 35 | // Temporary MATLAB folder name in workspace 36 | public static final String TEMP_MATLAB_FOLDER_NAME = ".matlab"; 37 | 38 | // MATLAB default function/plugin paths 39 | public static final String DEFAULT_PLUGIN = "+ciplugins/+jenkins/getDefaultPlugins.m"; 40 | public static final String BUILD_REPORT_PLUGIN = "+ciplugins/+jenkins/BuildReportPlugin.m"; 41 | public static final String TASK_RUN_PROGRESS_PLUGIN = "+ciplugins/+jenkins/TaskRunProgressPlugin.m"; 42 | public static final String BUILD_ARTIFACT = "buildArtifact"; 43 | 44 | public static final String NEW_LINE = System.getProperty("line.separator"); 45 | 46 | // MATLAB Runner Script 47 | public static final String TEST_RUNNER_SCRIPT = String.join(NEW_LINE, 48 | "addpath('${TEMP_FOLDER}');", 49 | "testScript = genscript(${PARAMS});", 50 | "disp('Running MATLAB script with content:');", 51 | "disp(testScript.Contents);", 52 | "fprintf('___________________________________\\n\\n');", 53 | "run(testScript);"); 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/MatlabExecutionException.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci; 2 | 3 | /** 4 | * Copyright 2021-2024 The MathWorks, Inc. 5 | */ 6 | 7 | import java.lang.Exception; 8 | 9 | public class MatlabExecutionException extends Exception { 10 | 11 | private final int exitCode; 12 | 13 | public MatlabExecutionException(int exitCode) { 14 | super(String.format(Message.getValue("matlab.execution.exception.prefix"), exitCode)); 15 | this.exitCode = exitCode; 16 | } 17 | 18 | /* 19 | * Function to retrieve MATLAB process's exit code. 20 | * This may require Jenkins In-process script approval. 21 | */ 22 | public int getExitCode() { 23 | return exitCode; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/MatlabInstallation.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci; 2 | 3 | /** 4 | * Copyright 2020-2024 The MathWorks, Inc. 5 | * 6 | * Describable class for adding MATLAB installations in Jenkins Global Tool configuration. 7 | */ 8 | 9 | import hudson.CopyOnWrite; 10 | import hudson.EnvVars; 11 | import hudson.Extension; 12 | import hudson.FilePath; 13 | import hudson.Util; 14 | import hudson.model.EnvironmentSpecific; 15 | import hudson.model.Node; 16 | import hudson.model.TaskListener; 17 | import hudson.slaves.NodeSpecific; 18 | import hudson.tools.ToolDescriptor; 19 | import hudson.tools.ToolInstallation; 20 | import hudson.tools.ToolProperty; 21 | import java.io.File; 22 | import java.io.IOException; 23 | import java.util.Arrays; 24 | import java.util.List; 25 | import javax.annotation.CheckForNull; 26 | import javax.annotation.Nonnull; 27 | 28 | import jenkins.model.Jenkins; 29 | import net.sf.json.JSONObject; 30 | import org.jenkinsci.Symbol; 31 | import org.kohsuke.stapler.DataBoundConstructor; 32 | import org.kohsuke.stapler.StaplerRequest; 33 | 34 | public class MatlabInstallation extends ToolInstallation 35 | implements EnvironmentSpecific, NodeSpecific { 36 | private static final long serialVersionUID = 1L; 37 | 38 | @DataBoundConstructor 39 | public MatlabInstallation(String name, @CheckForNull String home, List> properties) { 40 | super(Util.fixEmptyAndTrim(name), Util.fixEmptyAndTrim(home), properties); 41 | } 42 | 43 | /* 44 | * Constructor for Custom object 45 | */ 46 | 47 | public MatlabInstallation(String name) { 48 | super(name, null, null); 49 | } 50 | 51 | @Override 52 | public MatlabInstallation forEnvironment(EnvVars envVars) { 53 | return new MatlabInstallation(getName(), envVars.expand(getHome()), getProperties().toList()); 54 | } 55 | 56 | @Override 57 | public MatlabInstallation forNode(@Nonnull Node node, TaskListener log) throws IOException, InterruptedException { 58 | return new MatlabInstallation(getName(), translateFor(node, log), getProperties().toList()); 59 | } 60 | 61 | @Override 62 | public void buildEnvVars(EnvVars env) { 63 | String home = getHome(); 64 | if (home == null) { 65 | return; 66 | } 67 | env.put("PATH+matlabroot", home + "/bin"); 68 | } 69 | 70 | public static MatlabInstallation[] getAll() { 71 | return Jenkins.get().getDescriptorByType(DescriptorImpl.class).getInstallations(); 72 | } 73 | 74 | public static boolean isEmpty() { 75 | return getAll().length == 0; 76 | } 77 | 78 | public static MatlabInstallation getInstallation(String name) { 79 | for (MatlabInstallation inst : getAll()) { 80 | if (name.equals(inst.getName())) { 81 | return inst; 82 | } 83 | } 84 | return null; 85 | } 86 | 87 | @Extension 88 | @Symbol("matlab") 89 | public static class DescriptorImpl extends ToolDescriptor { 90 | @CopyOnWrite 91 | private volatile MatlabInstallation[] installations = new MatlabInstallation[0]; 92 | 93 | public DescriptorImpl() { 94 | load(); 95 | } 96 | 97 | @Override 98 | public String getDisplayName() { 99 | return "MATLAB"; 100 | } 101 | 102 | @Override 103 | public MatlabInstallation[] getInstallations() { 104 | return Arrays.copyOf(installations, installations.length); 105 | } 106 | 107 | @Override 108 | public MatlabInstallation newInstance(StaplerRequest req, JSONObject formData) { 109 | return (MatlabInstallation) req.bindJSON(clazz, formData); 110 | } 111 | 112 | public void setInstallations(MatlabInstallation... matlabInstallations) { 113 | this.installations = matlabInstallations; 114 | save(); 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/MatlabInstallationAxis.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci; 2 | 3 | /** 4 | * Copyright 2020-2024 The MathWorks, Inc. 5 | * 6 | * Describable class for MATLAB Axis that provides a list of configured MATLAB installation for 7 | * generating matrix configurations. 8 | */ 9 | 10 | import hudson.Extension; 11 | import hudson.matrix.Axis; 12 | import hudson.matrix.AxisDescriptor; 13 | import hudson.matrix.MatrixProject; 14 | import org.kohsuke.stapler.DataBoundConstructor; 15 | 16 | import java.util.ArrayList; 17 | import java.util.Arrays; 18 | import java.util.List; 19 | 20 | public class MatlabInstallationAxis extends Axis { 21 | 22 | @DataBoundConstructor 23 | public MatlabInstallationAxis(List values) { 24 | super(Message.getValue("Axis.matlab.key"), evaluateValues(values)); 25 | } 26 | 27 | static private List evaluateValues(List values) { 28 | // Add default configuration is values are null or not selected. 29 | if (values == null || values.isEmpty()) { 30 | values = new ArrayList<>(Arrays.asList("default")); 31 | } 32 | return values; 33 | } 34 | 35 | @Extension 36 | public static class DescriptorImpl extends AxisDescriptor { 37 | 38 | @Override 39 | public String getDisplayName() { 40 | return Message.getValue("Axis.matlab.key"); 41 | } 42 | 43 | @Override 44 | public boolean isInstantiable() { 45 | return !isMatlabInstallationEmpty(); 46 | } 47 | 48 | public boolean checkUseMatlabVersion(Object it) { 49 | return MatlabItemListener.getMatlabBuildWrapperCheckForPrj(((MatrixProject) it).getFullName()) 50 | && !isMatlabInstallationEmpty(); 51 | } 52 | 53 | public MatlabInstallation[] getInstallations() { 54 | return MatlabInstallation.getAll(); 55 | } 56 | 57 | public String getUseMatlabWarning() { 58 | return Message.getValue("Axis.use.matlab.warning"); 59 | } 60 | 61 | public boolean isMatlabInstallationEmpty() { 62 | return MatlabInstallation.isEmpty(); 63 | } 64 | 65 | public String getNoInstallationError() { 66 | return Message.getValue("Axis.no.installed.matlab.error"); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/MatlabItemListener.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci; 2 | 3 | /** 4 | * Copyright 2020-2024 The MathWorks, Inc. 5 | * 6 | * Item listener class to provide functionality to check UI element states for a 7 | * Multi-configuration project. 8 | */ 9 | 10 | import hudson.Extension; 11 | import hudson.matrix.MatrixConfiguration; 12 | import hudson.matrix.MatrixProject; 13 | import hudson.model.Item; 14 | import hudson.model.TopLevelItem; 15 | import hudson.model.listeners.ItemListener; 16 | import jenkins.model.Jenkins; 17 | 18 | import java.util.Collection; 19 | import java.util.HashMap; 20 | import java.util.List; 21 | import java.util.Map; 22 | 23 | @Extension 24 | public final class MatlabItemListener extends ItemListener { 25 | private static final Map prjCheckMatlabAxis = new HashMap<>(); 26 | private static final Map prjCheckMatlabBuildWrapper = new HashMap<>(); 27 | 28 | @Override 29 | public void onLoaded() { 30 | checkItems(Jenkins.get().getItems()); 31 | } 32 | 33 | @Override 34 | public void onUpdated(Item item) { 35 | if (!(item instanceof MatrixProject)) { 36 | return; 37 | } 38 | checkSingleItem(item); 39 | } 40 | 41 | private void checkItems(List items) { 42 | for (TopLevelItem item : items) { 43 | if (item instanceof MatrixProject) { 44 | check((MatrixProject) item); 45 | } 46 | } 47 | } 48 | 49 | private void checkSingleItem(Item item) { 50 | check((MatrixProject) item); 51 | } 52 | 53 | private void check(MatrixProject prj) { 54 | checkForAxis(prj); 55 | checkForBuildWrapper(prj); 56 | } 57 | 58 | private void checkForAxis(MatrixProject prj) { 59 | boolean checkForAxis = false; 60 | Collection configurations = prj.getActiveConfigurations(); 61 | for (MatrixConfiguration conf : configurations) { 62 | String matlabAxisValue = conf.getCombination().get(Message.getValue("Axis.matlab.key")); 63 | if (matlabAxisValue != null) { 64 | checkForAxis = true; 65 | break; 66 | } 67 | } 68 | prjCheckMatlabAxis.put(prj.getFullName(), checkForAxis); 69 | } 70 | 71 | private void checkForBuildWrapper(MatrixProject prj) { 72 | boolean checkForBuildWrapper = false; 73 | for (Object bWrapper : prj.getBuildWrappersList().toArray()) { 74 | if (bWrapper instanceof UseMatlabVersionBuildWrapper) { 75 | checkForBuildWrapper = ((UseMatlabVersionBuildWrapper) bWrapper).getMatlabInstallationName() != null; 76 | break; 77 | } 78 | } 79 | prjCheckMatlabBuildWrapper.put(prj.getFullName(), checkForBuildWrapper); 80 | } 81 | 82 | public static boolean getMatlabAxisCheckForPrj(String prjName) { 83 | return prjCheckMatlabAxis.get(prjName) != null && prjCheckMatlabAxis.get(prjName); 84 | } 85 | 86 | public static boolean getMatlabBuildWrapperCheckForPrj(String prjName) { 87 | return prjCheckMatlabBuildWrapper.get(prjName) != null && prjCheckMatlabBuildWrapper.get(prjName); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/MatlabNotFoundError.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci; 2 | 3 | /** 4 | * Copyright 2020-2024 The MathWorks, Inc. 5 | */ 6 | 7 | public class MatlabNotFoundError extends Error { 8 | 9 | private static final long serialVersionUID = 7918595075502022644L; 10 | 11 | MatlabNotFoundError(String errorMessage) { 12 | super(errorMessage); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/MatlabVersionNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci; 2 | 3 | /* 4 | * Copyright 2018-2024 The MathWorks, Inc. 5 | * 6 | * This Exception class provides a business exception for all Classes/methods which tries to get 7 | * version information of MATLAB. 8 | */ 9 | 10 | public class MatlabVersionNotFoundException extends Exception { 11 | MatlabVersionNotFoundException(String errorMessage, Throwable err) { 12 | super(errorMessage, err); 13 | } 14 | 15 | MatlabVersionNotFoundException(String errorMessage) { 16 | super(errorMessage); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/MatrixPatternResolver.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci; 2 | 3 | /* 4 | * Copyright 2019-2024 The MathWorks, Inc. 5 | * 6 | * This is Matrix pattern resolver class which is a utility for identifying variables. Either $xyz, 7 | * ${xyz} or ${a.b} but not $a.b, while ignoring "$$" 8 | */ 9 | 10 | import java.util.regex.Matcher; 11 | import java.util.regex.Pattern; 12 | 13 | public class MatrixPatternResolver { 14 | private String inputString; 15 | private static Pattern VARIBLE = Pattern.compile("\\$([A-Za-z0-9_]+|\\{[A-Za-z0-9_.]+\\}|\\$)"); 16 | 17 | public MatrixPatternResolver(String inputString) { 18 | this.inputString = inputString; 19 | } 20 | 21 | public String getInputString() { 22 | return this.inputString; 23 | } 24 | 25 | public boolean hasVariablePattern() { 26 | Matcher m = VARIBLE.matcher(getInputString()); 27 | return m.find(0); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/Message.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci; 2 | 3 | /* Copyright 2018-2024 The MathWorks, Inc. 4 | * 5 | * This Class is wrapper to access the static configuration values across project. Acts as 6 | * Utility class to access key & value pairs from config.properties 7 | */ 8 | 9 | import java.util.ResourceBundle; 10 | 11 | public class Message { 12 | 13 | private static String MATLAB_BUILDER_DISPLAY_NAME = "Builder.display.name"; 14 | private static String CONFIG_FILE = "config"; 15 | 16 | private static ResourceBundle rb = ResourceBundle.getBundle(CONFIG_FILE); 17 | 18 | public static String getBuilderDisplayName() { 19 | 20 | return rb.getString(MATLAB_BUILDER_DISPLAY_NAME); 21 | 22 | } 23 | 24 | public static String getValue(String key) { 25 | 26 | return rb.getString(key); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/Utilities.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci; 2 | 3 | /** 4 | * Copyright 2020-2024 The MathWorks, Inc. 5 | * 6 | * Utility class for common methods. 7 | */ 8 | 9 | import hudson.EnvVars; 10 | import hudson.FilePath; 11 | import hudson.model.Computer; 12 | import hudson.model.Node; 13 | import hudson.model.TaskListener; 14 | 15 | import java.io.IOException; 16 | import java.util.List; 17 | import java.util.Objects; 18 | import java.util.function.Predicate; 19 | import java.util.stream.Collectors; 20 | 21 | public class Utilities { 22 | 23 | public static String getCellArrayFromList(List listOfStr) { 24 | // Ignore empty string values in the list 25 | Predicate isEmpty = String::isEmpty; 26 | Predicate isNotEmpty = isEmpty.negate(); 27 | List filteredListOfStr = listOfStr.stream().filter(isNotEmpty).collect(Collectors.toList()); 28 | 29 | // Escape apostrophe for MATLAB 30 | filteredListOfStr.replaceAll(val -> "'" + val.replaceAll("'", "''") + "'"); 31 | return "{" + String.join(",", filteredListOfStr) + "}"; 32 | } 33 | 34 | public static void addMatlabToEnvPathFromAxis(Computer cmp, TaskListener listener, EnvVars env) 35 | throws IOException, InterruptedException { 36 | String name = env.get(Message.getValue("Axis.matlab.key")); 37 | 38 | // If no MATLAB axis is set or if 'Use MATLAB version' is selected, return 39 | if (name == null || name.isEmpty() || env.get("matlabroot") != null) { 40 | return; 41 | } 42 | 43 | FilePath matlabRoot = getNodeSpecificHome(name, cmp.getNode(), listener, env); 44 | 45 | FilePath matlabBin = new FilePath(matlabRoot, "bin"); 46 | env.put("PATH+matlabroot", matlabBin.getRemote()); 47 | 48 | // Specify which MATLAB was added to path. 49 | listener.getLogger().println( 50 | "\n" + String.format(Message.getValue("matlab.added.to.path.from"), matlabBin.getRemote()) + "\n"); 51 | } 52 | 53 | public static FilePath getNodeSpecificHome(String instName, Node node, TaskListener listener, EnvVars env) 54 | throws IOException, InterruptedException { 55 | MatlabInstallation inst = MatlabInstallation.getInstallation(instName); 56 | if (inst == null || node == null) { 57 | // Following will error out in BuildWrapper 58 | throw new MatlabNotFoundError("MATLAB installations could not be found"); 59 | } 60 | 61 | // get installation for node and environment. 62 | inst = inst.forNode(node, listener).forEnvironment(env); 63 | 64 | FilePath matlabExecutablePath = node.createPath(inst.getHome()); 65 | // If no MATLAB version is configured for current node, throw error. 66 | if (matlabExecutablePath == null || !matlabExecutablePath.exists()) { 67 | throw new MatlabNotFoundError( 68 | String.format(Message.getValue("matlab.not.found.error.for.node"), instName, Objects 69 | .requireNonNull(node).getDisplayName())); 70 | } 71 | return matlabExecutablePath; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/actions/MatlabAction.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci.actions; 2 | 3 | /** 4 | * Copyright 2024, The MathWorks Inc. 5 | */ 6 | 7 | import com.mathworks.ci.BuildArtifactAction; 8 | import com.mathworks.ci.BuildConsoleAnnotator; 9 | import com.mathworks.ci.MatlabBuilderConstants; 10 | import com.mathworks.ci.utilities.MatlabCommandRunner; 11 | import hudson.FilePath; 12 | import hudson.model.Run; 13 | import org.apache.commons.lang.RandomStringUtils; 14 | 15 | import java.io.File; 16 | import java.io.IOException; 17 | 18 | public class MatlabAction { 19 | MatlabCommandRunner runner; 20 | BuildConsoleAnnotator annotator; 21 | String actionID; 22 | 23 | public String getActionID() { 24 | return (this.actionID == null) ? "" : this.actionID; 25 | } 26 | 27 | public MatlabAction(MatlabCommandRunner runner) { 28 | this.runner = runner; 29 | } 30 | 31 | public MatlabAction(MatlabCommandRunner runner, BuildConsoleAnnotator annotator) { 32 | this.runner = runner; 33 | this.actionID = RandomStringUtils.randomAlphanumeric(8); 34 | this.annotator = annotator; 35 | } 36 | 37 | public void copyBuildPluginsToTemp() throws IOException, InterruptedException { 38 | // Copy plugins and override default plugins function 39 | runner.copyFileToTempFolder(MatlabBuilderConstants.DEFAULT_PLUGIN, MatlabBuilderConstants.DEFAULT_PLUGIN); 40 | runner.copyFileToTempFolder(MatlabBuilderConstants.BUILD_REPORT_PLUGIN, 41 | MatlabBuilderConstants.BUILD_REPORT_PLUGIN); 42 | runner.copyFileToTempFolder(MatlabBuilderConstants.TASK_RUN_PROGRESS_PLUGIN, 43 | MatlabBuilderConstants.TASK_RUN_PROGRESS_PLUGIN); 44 | } 45 | 46 | public void setBuildEnvVars() throws IOException, InterruptedException { 47 | // Set environment variable 48 | runner.addEnvironmentVariable( 49 | "MW_MATLAB_BUILDTOOL_DEFAULT_PLUGINS_FCN_OVERRIDE", 50 | "ciplugins.jenkins.getDefaultPlugins"); 51 | runner.addEnvironmentVariable("MW_BUILD_PLUGIN_ACTION_ID", this.getActionID()); 52 | runner.addEnvironmentVariable( 53 | "MW_MATLAB_TEMP_FOLDER", 54 | runner.getTempFolder().toString()); 55 | } 56 | 57 | public void teardownAction(Run build) { 58 | // Handle build result 59 | if (this.annotator != null) { 60 | moveJsonArtifactToBuildRoot(build, MatlabBuilderConstants.BUILD_ARTIFACT); 61 | } 62 | 63 | try { 64 | this.runner.removeTempFolder(); 65 | } catch (Exception e) { 66 | System.err.println(e.toString()); 67 | } 68 | } 69 | 70 | private void moveJsonArtifactToBuildRoot(Run build, String artifactBaseName) { 71 | try { 72 | FilePath file = new FilePath(this.runner.getTempFolder(), artifactBaseName + ".json"); 73 | if (file.exists()) { 74 | FilePath rootLocation = new FilePath( 75 | new File( 76 | build.getRootDir().getAbsolutePath(), 77 | artifactBaseName + this.getActionID() + ".json")); 78 | file.copyTo(rootLocation); 79 | file.delete(); 80 | build.addAction(new BuildArtifactAction(build, this.getActionID())); 81 | } 82 | } catch (Exception e) { 83 | // Don't want to override more important error 84 | // thrown in catch block 85 | System.err.println(e.toString()); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/actions/MatlabActionFactory.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci.actions; 2 | 3 | /** 4 | * Copyright 2024, The MathWorks Inc. 5 | */ 6 | 7 | import java.io.Serializable; 8 | import java.io.IOException; 9 | import com.mathworks.ci.parameters.*; 10 | 11 | public class MatlabActionFactory implements Serializable { 12 | public RunMatlabCommandAction createAction(CommandActionParameters params) 13 | throws IOException, InterruptedException { 14 | return new RunMatlabCommandAction(params); 15 | } 16 | 17 | public RunMatlabBuildAction createAction(BuildActionParameters params) throws IOException, InterruptedException { 18 | return new RunMatlabBuildAction(params); 19 | } 20 | 21 | public RunMatlabTestsAction createAction(TestActionParameters params) throws IOException, InterruptedException { 22 | return new RunMatlabTestsAction(params); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/actions/RunMatlabBuildAction.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci.actions; 2 | 3 | /** 4 | * Copyright 2024, The MathWorks Inc. 5 | */ 6 | 7 | import java.io.IOException; 8 | 9 | import com.mathworks.ci.BuildConsoleAnnotator; 10 | import com.mathworks.ci.MatlabExecutionException; 11 | import com.mathworks.ci.parameters.BuildActionParameters; 12 | import com.mathworks.ci.utilities.MatlabCommandRunner; 13 | 14 | import hudson.model.Run; 15 | 16 | public class RunMatlabBuildAction extends MatlabAction { 17 | private BuildActionParameters params; 18 | 19 | public RunMatlabBuildAction(MatlabCommandRunner runner, BuildConsoleAnnotator annotator, 20 | BuildActionParameters params) { 21 | super(runner, annotator); 22 | this.params = params; 23 | } 24 | 25 | public RunMatlabBuildAction(BuildActionParameters params) throws IOException, InterruptedException { 26 | this(new MatlabCommandRunner(params), 27 | new BuildConsoleAnnotator( 28 | params.getTaskListener().getLogger(), 29 | params.getBuild().getCharset()), 30 | params); 31 | } 32 | 33 | public void run() throws IOException, InterruptedException, MatlabExecutionException { 34 | super.copyBuildPluginsToTemp(); 35 | super.setBuildEnvVars(); 36 | 37 | // Redirect output to the build annotator 38 | runner.redirectStdOut(annotator); 39 | 40 | // Prepare the build tool command 41 | // TODO: Devise better solution then prepending the command 42 | // here. 43 | String command = "addpath('" 44 | + runner.getTempFolder().getRemote() 45 | + "'); buildtool"; 46 | 47 | if (params.getTasks() != null) { 48 | command += " " + params.getTasks(); 49 | } 50 | 51 | if (params.getBuildOptions() != null) { 52 | command += " " + params.getBuildOptions(); 53 | } 54 | 55 | try { 56 | runner.runMatlabCommand(command); 57 | } catch (Exception e) { 58 | this.params.getTaskListener().getLogger() 59 | .println(e.getMessage()); 60 | throw (e); 61 | } finally { 62 | annotator.forceEol(); 63 | 64 | Run build = this.params.getBuild(); 65 | super.teardownAction(build); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/actions/RunMatlabCommandAction.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci.actions; 2 | 3 | /** 4 | * Copyright 2024, The MathWorks Inc. 5 | */ 6 | 7 | import java.io.IOException; 8 | 9 | import com.mathworks.ci.BuildConsoleAnnotator; 10 | import com.mathworks.ci.MatlabExecutionException; 11 | import com.mathworks.ci.parameters.CommandActionParameters; 12 | import com.mathworks.ci.utilities.MatlabCommandRunner; 13 | 14 | import hudson.model.Run; 15 | 16 | public class RunMatlabCommandAction extends MatlabAction { 17 | private CommandActionParameters params; 18 | 19 | public RunMatlabCommandAction(MatlabCommandRunner runner, BuildConsoleAnnotator annotator, 20 | CommandActionParameters params) { 21 | super(runner, annotator); 22 | this.params = params; 23 | } 24 | 25 | public RunMatlabCommandAction(CommandActionParameters params) throws IOException, InterruptedException { 26 | this(new MatlabCommandRunner(params), 27 | new BuildConsoleAnnotator( 28 | params.getTaskListener().getLogger(), 29 | params.getBuild().getCharset()), 30 | params); 31 | } 32 | 33 | public void run() throws IOException, InterruptedException, MatlabExecutionException { 34 | super.copyBuildPluginsToTemp(); 35 | super.setBuildEnvVars(); 36 | 37 | // Redirect output to the build annotator 38 | runner.redirectStdOut(annotator); 39 | 40 | // Prepare MATLAB command 41 | String command = "addpath('" 42 | + runner.getTempFolder().getRemote() 43 | + "'); " + this.params.getCommand(); 44 | 45 | try { 46 | runner.runMatlabCommand(command); 47 | } catch (Exception e) { 48 | this.params.getTaskListener().getLogger() 49 | .println(e.getMessage()); 50 | throw (e); 51 | } finally { 52 | annotator.forceEol(); 53 | 54 | Run build = this.params.getBuild(); 55 | super.teardownAction(build); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/actions/RunMatlabTestsAction.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci.actions; 2 | 3 | /** 4 | * Copyright 2024, The MathWorks Inc. 5 | */ 6 | 7 | import java.io.IOException; 8 | import java.util.List; 9 | import java.util.ArrayList; 10 | 11 | import hudson.FilePath; 12 | import hudson.model.Run; 13 | 14 | import com.mathworks.ci.Utilities; 15 | import com.mathworks.ci.MatlabBuilderConstants; 16 | import com.mathworks.ci.MatlabExecutionException; 17 | import com.mathworks.ci.parameters.TestActionParameters; 18 | import com.mathworks.ci.utilities.MatlabCommandRunner; 19 | 20 | public class RunMatlabTestsAction extends MatlabAction { 21 | private TestActionParameters params; 22 | 23 | public RunMatlabTestsAction(MatlabCommandRunner runner, TestActionParameters params) { 24 | super(runner); 25 | this.params = params; 26 | } 27 | 28 | public RunMatlabTestsAction(TestActionParameters params) throws IOException, InterruptedException { 29 | this(new MatlabCommandRunner(params), params); 30 | } 31 | 32 | public void run() throws IOException, InterruptedException, MatlabExecutionException { 33 | // Copy in genscript 34 | FilePath genScriptZip = runner.copyFileToTempFolder( 35 | MatlabBuilderConstants.MATLAB_SCRIPT_GENERATOR, 36 | "genscript.zip"); 37 | genScriptZip.unzip(runner.getTempFolder()); 38 | 39 | // Prepare the command 40 | String command = MatlabBuilderConstants.TEST_RUNNER_SCRIPT; 41 | command = command.replace("${TEMP_FOLDER}", runner.getTempFolder().getRemote()); 42 | command = command.replace("${PARAMS}", getParameterString()); 43 | 44 | // Run the command 45 | try { 46 | runner.runMatlabCommand(command); 47 | } catch (Exception e) { 48 | this.params.getTaskListener() 49 | .getLogger() 50 | .println(e.getMessage()); 51 | throw (e); 52 | } finally { 53 | Run build = this.params.getBuild(); 54 | super.teardownAction(build); 55 | } 56 | } 57 | 58 | private String singleQuotify(String in) { 59 | return "'" + in.replace("'", "''") + "'"; 60 | } 61 | 62 | // Concatenate the input arguments, try to keep this function as 63 | // readable as possible because it can get hairy. 64 | private String getParameterString() { 65 | // The final list to be concatted and returned 66 | final List inputArgsList = new ArrayList(); 67 | 68 | inputArgsList.add("'Test'"); 69 | 70 | // Prepare source and test folder lists 71 | String sourceFolders = null; 72 | if (this.params.getSourceFolder() != null) { 73 | sourceFolders = this.params.getSourceFolder().size() == 0 74 | ? null 75 | : Utilities.getCellArrayFromList(this.params.getSourceFolder()); 76 | } 77 | 78 | String selectFolders = null; 79 | if (this.params.getSelectByFolder() != null) { 80 | selectFolders = this.params.getSelectByFolder().size() == 0 81 | ? null 82 | : Utilities.getCellArrayFromList(this.params.getSelectByFolder()); 83 | } 84 | 85 | // All string-based fields 86 | final String[] names = { 87 | "'PDFTestReport'", 88 | "'TAPTestResults'", 89 | "'JUnitTestResults'", 90 | "'CoberturaCodeCoverage'", 91 | MatlabBuilderConstants.STM_RESULTS, 92 | "'CoberturaModelCoverage'", 93 | "'SelectByTag'", 94 | "'UseParallel'", 95 | "'Strict'", 96 | "'LoggingLevel'", 97 | "'OutputDetail'", 98 | "'SourceFolder'", 99 | "'SelectByFolder'" 100 | }; 101 | final String[] values = { 102 | this.params.getTestResultsPDF(), 103 | this.params.getTestResultsTAP(), 104 | this.params.getTestResultsJUnit(), 105 | this.params.getCodeCoverageCobertura(), 106 | this.params.getTestResultsSimulinkTest(), 107 | this.params.getModelCoverageCobertura(), 108 | this.params.getSelectByTag(), 109 | this.params.getUseParallel(), 110 | this.params.getStrict(), 111 | this.params.getLoggingLevel(), 112 | this.params.getOutputDetail(), 113 | sourceFolders, 114 | selectFolders 115 | }; 116 | 117 | for (int i = 0; i < names.length; i++) { 118 | if (values[i] != null && !values[i].equals("false")) { 119 | inputArgsList.add(names[i]); 120 | String arg = values[i].equals("true") || values[i].startsWith("{") 121 | ? values[i] 122 | : singleQuotify(values[i]); 123 | inputArgsList.add(arg); 124 | } 125 | } 126 | 127 | return String.join(",", inputArgsList); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/freestyle/RunMatlabBuildBuilder.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci.freestyle; 2 | 3 | /** 4 | * Copyright 2022-2024 The MathWorks, Inc. 5 | */ 6 | 7 | import java.io.IOException; 8 | import javax.annotation.Nonnull; 9 | import org.kohsuke.stapler.DataBoundConstructor; 10 | import org.kohsuke.stapler.DataBoundSetter; 11 | import org.kohsuke.stapler.StaplerRequest; 12 | import hudson.EnvVars; 13 | import hudson.Extension; 14 | import hudson.FilePath; 15 | import hudson.Launcher; 16 | import hudson.init.Initializer; 17 | import hudson.init.InitMilestone; 18 | import hudson.model.Items; 19 | import hudson.model.AbstractProject; 20 | import hudson.model.Result; 21 | import hudson.model.Run; 22 | import hudson.model.TaskListener; 23 | import hudson.tasks.BuildStepDescriptor; 24 | import hudson.tasks.Builder; 25 | import jenkins.tasks.SimpleBuildStep; 26 | import net.sf.json.JSONObject; 27 | 28 | import com.mathworks.ci.Message; 29 | import com.mathworks.ci.actions.MatlabActionFactory; 30 | import com.mathworks.ci.actions.RunMatlabBuildAction; 31 | import com.mathworks.ci.parameters.BuildActionParameters; 32 | import com.mathworks.ci.freestyle.options.*; 33 | 34 | public class RunMatlabBuildBuilder extends Builder implements SimpleBuildStep { 35 | // Deprecated 36 | private transient int buildResult; 37 | 38 | // In use 39 | private String tasks; 40 | private StartupOptions startupOptions; 41 | private BuildOptions buildOptions; 42 | 43 | private MatlabActionFactory factory; 44 | 45 | public RunMatlabBuildBuilder(MatlabActionFactory factory) { 46 | this.factory = factory; 47 | } 48 | 49 | @DataBoundConstructor 50 | public RunMatlabBuildBuilder() { 51 | this(new MatlabActionFactory()); 52 | } 53 | 54 | // Getter and Setters to access local members 55 | @DataBoundSetter 56 | public void setTasks(String tasks) { 57 | this.tasks = tasks; 58 | } 59 | 60 | @DataBoundSetter 61 | public void setStartupOptions(StartupOptions startupOptions) { 62 | this.startupOptions = startupOptions; 63 | } 64 | 65 | @DataBoundSetter 66 | public void setBuildOptions(BuildOptions buildOptions) { 67 | this.buildOptions = buildOptions; 68 | } 69 | 70 | public String getTasks() { 71 | return this.tasks; 72 | } 73 | 74 | public StartupOptions getStartupOptions() { 75 | return this.startupOptions; 76 | } 77 | 78 | public String getStartupOptionsAsString() { 79 | return this.startupOptions == null 80 | ? "" 81 | : this.startupOptions.getOptions(); 82 | } 83 | 84 | public BuildOptions getBuildOptions() { 85 | return this.buildOptions; 86 | } 87 | 88 | public String getBuildOptionsAsString() { 89 | return this.buildOptions == null 90 | ? null 91 | : this.buildOptions.getOptions(); 92 | } 93 | 94 | @Extension 95 | public static class RunMatlabBuildDescriptor extends BuildStepDescriptor { 96 | 97 | @Initializer(before = InitMilestone.PLUGINS_STARTED) 98 | public static void addAliases() { 99 | Items.XSTREAM2.addCompatibilityAlias("com.mathworks.ci.RunMatlabBuildBuilder", RunMatlabBuildBuilder.class); 100 | } 101 | 102 | // Overridden Method used to show the text under build dropdown 103 | @Override 104 | public String getDisplayName() { 105 | return Message.getValue("Builder.build.builder.display.name"); 106 | } 107 | 108 | @Override 109 | public boolean configure(StaplerRequest req, JSONObject formData) throws FormException { 110 | save(); 111 | return super.configure(req, formData); 112 | } 113 | 114 | /* 115 | * This is to identify which project type in jenkins this should be 116 | * applicable.(non-Javadoc) 117 | * 118 | * @see hudson.tasks.BuildStepDescriptor#isApplicable(java.lang.Class) 119 | * 120 | * if it returns true then this build step will be applicable for all project 121 | * type. 122 | */ 123 | @Override 124 | public boolean isApplicable( 125 | @SuppressWarnings("rawtypes") Class jobtype) { 126 | return true; 127 | } 128 | } 129 | 130 | @Override 131 | public void perform(@Nonnull Run build, @Nonnull FilePath workspace, 132 | @Nonnull Launcher launcher, @Nonnull TaskListener listener) 133 | throws InterruptedException, IOException { 134 | 135 | // Get the environment variable specific to the this build 136 | final EnvVars env = build.getEnvironment(listener); 137 | 138 | BuildActionParameters params = new BuildActionParameters( 139 | build, workspace, env, launcher, listener, 140 | this.getStartupOptionsAsString(), 141 | this.getTasks(), 142 | this.getBuildOptionsAsString()); 143 | RunMatlabBuildAction action = factory.createAction(params); 144 | 145 | try { 146 | action.run(); 147 | } catch (Exception e) { 148 | build.setResult(Result.FAILURE); 149 | } 150 | } 151 | 152 | // Added for backwards compatibility: 153 | // Called when object is loaded from persistent data. 154 | protected Object readResolve() { 155 | if (factory == null) { 156 | factory = new MatlabActionFactory(); 157 | } 158 | 159 | return this; 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/freestyle/options/BuildOptions.java: -------------------------------------------------------------------------------- 1 | 2 | package com.mathworks.ci.freestyle.options; 3 | 4 | /** 5 | * Copyright 2024 The MathWorks, Inc. 6 | * 7 | * Describable class for Build Options. 8 | */ 9 | 10 | import hudson.Extension; 11 | import hudson.Util; 12 | import hudson.model.AbstractDescribableImpl; 13 | import hudson.model.Descriptor; 14 | import org.kohsuke.stapler.DataBoundConstructor; 15 | 16 | public class BuildOptions extends AbstractDescribableImpl { 17 | 18 | private String options; 19 | 20 | @DataBoundConstructor 21 | public BuildOptions(String options) { 22 | this.options = Util.fixNull(options); 23 | } 24 | 25 | public String getOptions() { 26 | return this.options; 27 | } 28 | 29 | @Extension 30 | public static class DescriptorImpl extends Descriptor { 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/freestyle/options/SelectByFolder.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci.freestyle.options; 2 | 3 | /** 4 | * Copyright 2020-2024 The MathWorks, Inc. 5 | */ 6 | 7 | import java.util.List; 8 | import java.util.stream.Collectors; 9 | 10 | import org.kohsuke.stapler.DataBoundConstructor; 11 | 12 | import hudson.Extension; 13 | import hudson.Util; 14 | import hudson.model.AbstractDescribableImpl; 15 | import hudson.model.Descriptor; 16 | 17 | public class SelectByFolder extends AbstractDescribableImpl { 18 | private List testFolderPaths; 19 | private static final String SELECT_BY_FOLDER = "SelectByFolder"; 20 | 21 | @DataBoundConstructor 22 | public SelectByFolder(List testFolderPaths) { 23 | this.testFolderPaths = Util.fixNull(testFolderPaths); 24 | } 25 | 26 | public List getTestFolderPaths() { 27 | return this.testFolderPaths; 28 | } 29 | 30 | public List getTestFolderStringPaths() { 31 | return this.testFolderPaths.stream().map( 32 | p -> p.getTestFolders()).collect(Collectors.toList()); 33 | } 34 | 35 | public void addSourceToInputArgs(List inputArgsList, String cellArraySourceVal) { 36 | // Concatenate all source folders to MATLAB cell array string. 37 | inputArgsList.add("'" + SELECT_BY_FOLDER + "'" + "," + cellArraySourceVal); 38 | } 39 | 40 | @Extension 41 | public static class DescriptorImpl extends Descriptor { 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/freestyle/options/SourceFolder.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci.freestyle.options; 2 | 3 | /** 4 | * Copyright 2020-2024 The MathWorks, Inc. 5 | * 6 | * Describable class for Source Folder Option in RunMATLABTest Build step. 7 | */ 8 | 9 | import hudson.Extension; 10 | import hudson.Util; 11 | import hudson.model.AbstractDescribableImpl; 12 | import hudson.model.Descriptor; 13 | import org.kohsuke.stapler.DataBoundConstructor; 14 | import java.util.List; 15 | import java.util.stream.Collectors; 16 | 17 | public class SourceFolder extends AbstractDescribableImpl { 18 | 19 | private List sourceFolderPaths; 20 | private static final String SOURCE_FOLDER = "SourceFolder"; 21 | 22 | @DataBoundConstructor 23 | public SourceFolder(List sourceFolderPaths) { 24 | this.sourceFolderPaths = Util.fixNull(sourceFolderPaths); 25 | } 26 | 27 | public List getSourceFolderPaths() { 28 | return this.sourceFolderPaths; 29 | } 30 | 31 | public List getSourceFolderStringPaths() { 32 | return this.sourceFolderPaths.stream().map( 33 | (SourceFolderPaths p) -> p.getSrcFolderPath()) 34 | .collect(Collectors.toList()); 35 | } 36 | 37 | public void addSourceToInputArgs(List inputArgsList, String cellArraySourceVal) { 38 | // Concatenate all source folders to MATLAB cell array string. 39 | inputArgsList.add("'" + SOURCE_FOLDER + "'" + "," + cellArraySourceVal); 40 | } 41 | 42 | @Extension 43 | public static class DescriptorImpl extends Descriptor { 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/freestyle/options/SourceFolderPaths.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci.freestyle.options; 2 | 3 | /** 4 | * Copyright 2020-2024 The MathWorks, Inc. 5 | * 6 | * Describable class for Repeatable Source Folder text boxes in Source Folder option 7 | * in RunMATLABTest Build step. 8 | */ 9 | 10 | import hudson.Extension; 11 | import hudson.model.AbstractDescribableImpl; 12 | import hudson.model.Descriptor; 13 | import org.kohsuke.stapler.DataBoundConstructor; 14 | 15 | public class SourceFolderPaths extends AbstractDescribableImpl { 16 | 17 | private String srcFolderPath; 18 | 19 | @DataBoundConstructor 20 | public SourceFolderPaths(String srcFolderPath) { 21 | this.srcFolderPath = srcFolderPath; 22 | } 23 | 24 | public String getSrcFolderPath() { 25 | return this.srcFolderPath; 26 | } 27 | 28 | @Extension 29 | public static final class DescriptorImpl extends Descriptor { 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/freestyle/options/StartupOptions.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci.freestyle.options; 2 | 3 | /** 4 | * Copyright 2023-2024 The MathWorks, Inc. 5 | * 6 | * Describable class for Startup Options. 7 | */ 8 | 9 | import hudson.Extension; 10 | import hudson.Util; 11 | import hudson.model.AbstractDescribableImpl; 12 | import hudson.model.Descriptor; 13 | import org.kohsuke.stapler.DataBoundConstructor; 14 | 15 | public class StartupOptions extends AbstractDescribableImpl { 16 | 17 | private String options; 18 | 19 | @DataBoundConstructor 20 | public StartupOptions(String options) { 21 | this.options = Util.fixNull(options); 22 | } 23 | 24 | public String getOptions() { 25 | return this.options; 26 | } 27 | 28 | @Extension 29 | public static class DescriptorImpl extends Descriptor { 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/freestyle/options/TestFolders.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci.freestyle.options; 2 | 3 | /** 4 | * Copyright 2020-2024 The MathWorks, Inc. 5 | */ 6 | 7 | import org.kohsuke.stapler.DataBoundConstructor; 8 | 9 | import hudson.Extension; 10 | import hudson.model.AbstractDescribableImpl; 11 | import hudson.model.Descriptor; 12 | 13 | public class TestFolders extends AbstractDescribableImpl { 14 | 15 | private String testFolders; 16 | 17 | @DataBoundConstructor 18 | public TestFolders(String testFolders) { 19 | this.testFolders = testFolders; 20 | } 21 | 22 | public String getTestFolders() { 23 | return this.testFolders; 24 | } 25 | 26 | @Extension 27 | public static final class DescriptorImpl extends Descriptor { 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/parameters/BuildActionParameters.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci.parameters; 2 | 3 | /** 4 | * Copyright 2024 The MathWorks, Inc. 5 | */ 6 | 7 | import java.io.IOException; 8 | import hudson.FilePath; 9 | import hudson.EnvVars; 10 | import hudson.Launcher; 11 | import hudson.model.Run; 12 | import hudson.model.TaskListener; 13 | import org.jenkinsci.plugins.workflow.steps.StepContext; 14 | 15 | public class BuildActionParameters extends MatlabActionParameters { 16 | private String tasks; 17 | private String buildOptions; 18 | 19 | public BuildActionParameters(StepContext context, String startupOpts, String tasks, String buildOpts) 20 | throws IOException, InterruptedException { 21 | super(context, startupOpts); 22 | this.tasks = tasks; 23 | this.buildOptions = buildOpts; 24 | } 25 | 26 | public BuildActionParameters(Run build, FilePath workspace, EnvVars env, Launcher launcher, 27 | TaskListener listener, String startupOpts, String tasks, String buildOptions) { 28 | super(build, workspace, env, launcher, listener, startupOpts); 29 | this.tasks = tasks; 30 | this.buildOptions = buildOptions; 31 | } 32 | 33 | public String getTasks() { 34 | return tasks; 35 | } 36 | 37 | public String getBuildOptions() { 38 | return buildOptions; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/parameters/CommandActionParameters.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci.parameters; 2 | 3 | /** 4 | * Copyright 2024 The MathWorks, Inc. 5 | */ 6 | 7 | import java.io.IOException; 8 | import hudson.FilePath; 9 | import hudson.EnvVars; 10 | import hudson.Launcher; 11 | import hudson.model.Run; 12 | import hudson.model.TaskListener; 13 | import org.jenkinsci.plugins.workflow.steps.StepContext; 14 | 15 | public class CommandActionParameters extends MatlabActionParameters { 16 | private String command; 17 | 18 | public CommandActionParameters(StepContext context, String startupOpts, String command) 19 | throws IOException, InterruptedException { 20 | super(context, startupOpts); 21 | this.command = command; 22 | } 23 | 24 | public CommandActionParameters(Run build, FilePath workspace, EnvVars env, Launcher launcher, 25 | TaskListener listener, String startupOpts, String command) { 26 | super(build, workspace, env, launcher, listener, startupOpts); 27 | this.command = command; 28 | } 29 | 30 | public String getCommand() { 31 | return command; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/parameters/MatlabActionParameters.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci.parameters; 2 | 3 | /** 4 | * Copyright 2024 The MathWorks, Inc. 5 | */ 6 | 7 | import java.io.IOException; 8 | import hudson.FilePath; 9 | import hudson.EnvVars; 10 | import hudson.Launcher; 11 | import hudson.model.Run; 12 | import hudson.model.TaskListener; 13 | import org.jenkinsci.plugins.workflow.steps.StepContext; 14 | 15 | public class MatlabActionParameters { 16 | private Run build; 17 | private FilePath workspace; 18 | private EnvVars env; 19 | private Launcher launcher; 20 | private TaskListener listener; 21 | 22 | private String startupOptions; 23 | 24 | public MatlabActionParameters(StepContext context, String startupOpts) throws IOException, InterruptedException { 25 | this.build = context.get(Run.class); 26 | this.workspace = context.get(FilePath.class); 27 | this.env = context.get(EnvVars.class); 28 | this.launcher = context.get(Launcher.class); 29 | this.listener = context.get(TaskListener.class); 30 | this.startupOptions = startupOpts; 31 | } 32 | 33 | public MatlabActionParameters(Run build, FilePath workspace, EnvVars env, Launcher launcher, TaskListener listener, 34 | String startupOpts) { 35 | this.build = build; 36 | this.workspace = workspace; 37 | this.env = env; 38 | this.launcher = launcher; 39 | this.listener = listener; 40 | this.startupOptions = startupOpts; 41 | } 42 | 43 | public Run getBuild() { 44 | return build; 45 | } 46 | 47 | public FilePath getWorkspace() { 48 | return workspace; 49 | } 50 | 51 | public EnvVars getEnvVars() { 52 | return env; 53 | } 54 | 55 | public Launcher getLauncher() { 56 | return launcher; 57 | } 58 | 59 | public TaskListener getTaskListener() { 60 | return listener; 61 | } 62 | 63 | public String getStartupOptions() { 64 | return startupOptions; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/parameters/TestActionParameters.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci.parameters; 2 | 3 | /** 4 | * Copyright 2024 The MathWorks, Inc. 5 | */ 6 | 7 | import java.util.List; 8 | import java.util.ArrayList; 9 | import java.io.IOException; 10 | import hudson.FilePath; 11 | import hudson.EnvVars; 12 | import hudson.Launcher; 13 | import hudson.model.Run; 14 | import hudson.model.TaskListener; 15 | import org.jenkinsci.plugins.workflow.steps.StepContext; 16 | 17 | public class TestActionParameters extends MatlabActionParameters { 18 | private String testResultsPDF; 19 | private String testResultsTAP; 20 | private String testResultsJUnit; 21 | private String codeCoverageCobertura; 22 | private String testResultsSimulinkTest; 23 | private String modelCoverageCobertura; 24 | private String selectByTag; 25 | private String loggingLevel; 26 | private String outputDetail; 27 | private boolean useParallel; 28 | private boolean strict; 29 | private List sourceFolder = new ArrayList<>(); 30 | private List selectByFolder = new ArrayList<>(); 31 | 32 | public TestActionParameters(StepContext context, String startupOpts, 33 | String testResultsPDF, String testResultsTAP, String testResultsJUnit, 34 | String codeCoverageCobertura, String testResultsSimulinkTest, String modelCoverageCobertura, 35 | String selectByTag, String loggingLevel, String outputDetail, 36 | boolean useParallel, boolean strict, List sourceFolder, 37 | List selectByFolder) 38 | throws IOException, InterruptedException { 39 | super(context, startupOpts); 40 | this.testResultsPDF = testResultsPDF; 41 | this.testResultsTAP = testResultsTAP; 42 | this.testResultsJUnit = testResultsJUnit; 43 | this.codeCoverageCobertura = codeCoverageCobertura; 44 | this.testResultsSimulinkTest = testResultsSimulinkTest; 45 | this.modelCoverageCobertura = modelCoverageCobertura; 46 | this.selectByTag = selectByTag; 47 | this.loggingLevel = loggingLevel; 48 | this.outputDetail = outputDetail; 49 | this.useParallel = useParallel; 50 | this.strict = strict; 51 | this.sourceFolder = sourceFolder; 52 | this.selectByFolder = selectByFolder; 53 | } 54 | 55 | public TestActionParameters(Run build, FilePath workspace, EnvVars env, Launcher launcher, 56 | TaskListener listener, String startupOpts, 57 | String testResultsPDF, String testResultsTAP, String testResultsJUnit, 58 | String codeCoverageCobertura, String testResultsSimulinkTest, String modelCoverageCobertura, 59 | String selectByTag, String loggingLevel, String outputDetail, 60 | boolean useParallel, boolean strict, List sourceFolder, 61 | List selectByFolder) { 62 | super(build, workspace, env, launcher, listener, startupOpts); 63 | this.testResultsPDF = testResultsPDF; 64 | this.testResultsTAP = testResultsTAP; 65 | this.testResultsJUnit = testResultsJUnit; 66 | this.codeCoverageCobertura = codeCoverageCobertura; 67 | this.testResultsSimulinkTest = testResultsSimulinkTest; 68 | this.modelCoverageCobertura = modelCoverageCobertura; 69 | this.selectByTag = selectByTag; 70 | this.loggingLevel = loggingLevel; 71 | this.outputDetail = outputDetail; 72 | this.useParallel = useParallel; 73 | this.strict = strict; 74 | this.sourceFolder = sourceFolder; 75 | this.selectByFolder = selectByFolder; 76 | } 77 | 78 | public String getTestResultsPDF() { 79 | return testResultsPDF; 80 | } 81 | 82 | public String getTestResultsTAP() { 83 | return testResultsTAP; 84 | } 85 | 86 | public String getTestResultsJUnit() { 87 | return testResultsJUnit; 88 | } 89 | 90 | public String getCodeCoverageCobertura() { 91 | return codeCoverageCobertura; 92 | } 93 | 94 | public String getTestResultsSimulinkTest() { 95 | return testResultsSimulinkTest; 96 | } 97 | 98 | public String getModelCoverageCobertura() { 99 | return modelCoverageCobertura; 100 | } 101 | 102 | public String getSelectByTag() { 103 | return selectByTag; 104 | } 105 | 106 | public String getLoggingLevel() { 107 | if (loggingLevel == null) { 108 | return null; 109 | } 110 | 111 | return loggingLevel.equalsIgnoreCase("default") ? null : loggingLevel; 112 | } 113 | 114 | public String getOutputDetail() { 115 | if (outputDetail == null) { 116 | return null; 117 | } 118 | 119 | return outputDetail.equalsIgnoreCase("default") ? null : outputDetail; 120 | } 121 | 122 | public String getUseParallel() { 123 | return String.valueOf(useParallel); 124 | } 125 | 126 | public String getStrict() { 127 | return String.valueOf(strict); 128 | } 129 | 130 | public List getSourceFolder() { 131 | return sourceFolder; 132 | } 133 | 134 | public List getSelectByFolder() { 135 | return selectByFolder; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/pipeline/MatlabBuildStepExecution.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci.pipeline; 2 | 3 | /** 4 | * Copyright 2022-2024 The MathWorks, Inc. 5 | */ 6 | 7 | import java.io.IOException; 8 | 9 | import org.jenkinsci.plugins.workflow.steps.StepContext; 10 | import org.jenkinsci.plugins.workflow.steps.SynchronousNonBlockingStepExecution; 11 | import hudson.model.Result; 12 | 13 | import com.mathworks.ci.actions.MatlabActionFactory; 14 | import com.mathworks.ci.actions.RunMatlabBuildAction; 15 | import com.mathworks.ci.parameters.BuildActionParameters; 16 | 17 | public class MatlabBuildStepExecution extends SynchronousNonBlockingStepExecution { 18 | 19 | private static final long serialVersionUID = 4771831219402275744L; 20 | 21 | private MatlabActionFactory factory; 22 | private RunMatlabBuildStep step; 23 | 24 | public MatlabBuildStepExecution(MatlabActionFactory factory, StepContext ctx, RunMatlabBuildStep step) 25 | throws IOException, InterruptedException { 26 | super(ctx); 27 | 28 | this.factory = factory; 29 | this.step = step; 30 | } 31 | 32 | public MatlabBuildStepExecution(StepContext ctx, RunMatlabBuildStep step) throws IOException, InterruptedException { 33 | this(new MatlabActionFactory(), ctx, step); 34 | } 35 | 36 | @Override 37 | public Void run() throws Exception { 38 | BuildActionParameters params = new BuildActionParameters( 39 | getContext(), 40 | step.getStartupOptions(), 41 | step.getTasks(), 42 | step.getBuildOptions()); 43 | 44 | RunMatlabBuildAction action = factory.createAction(params); 45 | try { 46 | action.run(); 47 | } catch (Exception e) { 48 | // throw an exception if return code is non-zero 49 | stop(e); 50 | } 51 | 52 | getContext().setResult(Result.SUCCESS); 53 | return null; 54 | } 55 | 56 | @Override 57 | public void stop(Throwable cause) throws Exception { 58 | getContext().onFailure(cause); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/pipeline/MatlabCommandStepExecution.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci.pipeline; 2 | 3 | /** 4 | * Copyright 2023-2024 The MathWorks, Inc. 5 | */ 6 | 7 | import java.io.IOException; 8 | import org.jenkinsci.plugins.workflow.steps.StepContext; 9 | import org.jenkinsci.plugins.workflow.steps.SynchronousNonBlockingStepExecution; 10 | import hudson.model.Result; 11 | 12 | import com.mathworks.ci.actions.MatlabActionFactory; 13 | import com.mathworks.ci.parameters.CommandActionParameters; 14 | import com.mathworks.ci.actions.RunMatlabCommandAction; 15 | 16 | public class MatlabCommandStepExecution extends SynchronousNonBlockingStepExecution { 17 | 18 | private static final long serialVersionUID = 1957239693658914450L; 19 | 20 | private MatlabActionFactory factory; 21 | private RunMatlabCommandStep step; 22 | 23 | public MatlabCommandStepExecution(MatlabActionFactory factory, StepContext context, RunMatlabCommandStep step) 24 | throws IOException, InterruptedException { 25 | super(context); 26 | 27 | this.factory = factory; 28 | this.step = step; 29 | } 30 | 31 | public MatlabCommandStepExecution(StepContext context, RunMatlabCommandStep step) 32 | throws IOException, InterruptedException { 33 | this(new MatlabActionFactory(), context, step); 34 | } 35 | 36 | @Override 37 | public Void run() throws Exception { 38 | CommandActionParameters params = new CommandActionParameters( 39 | getContext(), 40 | step.getStartupOptions(), 41 | step.getCommand()); 42 | RunMatlabCommandAction action = factory.createAction(params); 43 | 44 | try { 45 | action.run(); 46 | } catch (Exception e) { 47 | stop(e); 48 | } 49 | 50 | getContext().setResult(Result.SUCCESS); 51 | return null; 52 | } 53 | 54 | @Override 55 | public void stop(Throwable cause) throws Exception { 56 | getContext().onFailure(cause); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/pipeline/MatlabRunTestsStepExecution.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci.pipeline; 2 | 3 | /** 4 | * Copyright 2020-2024 The MathWorks, Inc. 5 | */ 6 | 7 | import java.io.IOException; 8 | 9 | import org.jenkinsci.plugins.workflow.steps.StepContext; 10 | import org.jenkinsci.plugins.workflow.steps.SynchronousNonBlockingStepExecution; 11 | 12 | import hudson.model.Result; 13 | 14 | import com.mathworks.ci.actions.MatlabActionFactory; 15 | import com.mathworks.ci.actions.RunMatlabTestsAction; 16 | import com.mathworks.ci.parameters.TestActionParameters; 17 | 18 | public class MatlabRunTestsStepExecution extends SynchronousNonBlockingStepExecution { 19 | 20 | private static final long serialVersionUID = 6704588180717665100L; 21 | 22 | private MatlabActionFactory factory; 23 | private RunMatlabTestsStep step; 24 | 25 | public MatlabRunTestsStepExecution(MatlabActionFactory factory, StepContext context, RunMatlabTestsStep step) 26 | throws IOException, InterruptedException { 27 | super(context); 28 | 29 | this.factory = factory; 30 | this.step = step; 31 | } 32 | 33 | public MatlabRunTestsStepExecution(StepContext context, RunMatlabTestsStep step) 34 | throws IOException, InterruptedException { 35 | this(new MatlabActionFactory(), context, step); 36 | } 37 | 38 | @Override 39 | public Void run() throws Exception { 40 | TestActionParameters params = new TestActionParameters( 41 | getContext(), 42 | step.getStartupOptions(), 43 | step.getTestResultsPDF(), 44 | step.getTestResultsTAP(), 45 | step.getTestResultsJUnit(), 46 | step.getCodeCoverageCobertura(), 47 | step.getTestResultsSimulinkTest(), 48 | step.getModelCoverageCobertura(), 49 | step.getSelectByTag(), 50 | step.getLoggingLevel(), 51 | step.getOutputDetail(), 52 | step.getUseParallel(), 53 | step.getStrict(), 54 | step.getSourceFolder(), 55 | step.getSelectByFolder()); 56 | RunMatlabTestsAction action = factory.createAction(params); 57 | try { 58 | action.run(); 59 | } catch (Exception e) { 60 | stop(e); 61 | } 62 | 63 | getContext().setResult(Result.SUCCESS); 64 | return null; 65 | } 66 | 67 | @Override 68 | public void stop(Throwable cause) throws Exception { 69 | getContext().onFailure(cause); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/pipeline/RunMatlabBuildStep.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci.pipeline; 2 | 3 | /** 4 | * Copyright 2022-2024 The MathWorks, Inc. 5 | */ 6 | 7 | import java.io.Serializable; 8 | import java.util.Set; 9 | import org.jenkinsci.plugins.workflow.steps.Step; 10 | import org.jenkinsci.plugins.workflow.steps.StepContext; 11 | import org.jenkinsci.plugins.workflow.steps.StepDescriptor; 12 | import org.jenkinsci.plugins.workflow.steps.StepExecution; 13 | import org.kohsuke.stapler.DataBoundConstructor; 14 | import org.kohsuke.stapler.DataBoundSetter; 15 | import com.google.common.collect.ImmutableSet; 16 | import hudson.EnvVars; 17 | import hudson.Extension; 18 | import hudson.FilePath; 19 | import hudson.Launcher; 20 | import hudson.model.Run; 21 | import hudson.model.TaskListener; 22 | import hudson.Util; 23 | 24 | import com.mathworks.ci.Message; 25 | 26 | public class RunMatlabBuildStep extends Step implements Serializable { 27 | 28 | private static final long serialVersionUID = 1L; 29 | 30 | private String tasks; 31 | private String startupOptions; 32 | private String buildOptions; 33 | 34 | @DataBoundConstructor 35 | public RunMatlabBuildStep() { 36 | 37 | } 38 | 39 | public String getTasks() { 40 | return Util.fixNull(tasks); 41 | } 42 | 43 | public String getStartupOptions() { 44 | return Util.fixNull(startupOptions); 45 | } 46 | 47 | public String getBuildOptions() { 48 | return Util.fixNull(buildOptions); 49 | } 50 | 51 | @DataBoundSetter 52 | public void setTasks(String tasks) { 53 | this.tasks = tasks; 54 | } 55 | 56 | @DataBoundSetter 57 | public void setStartupOptions(String startupOptions) { 58 | this.startupOptions = startupOptions; 59 | } 60 | 61 | @DataBoundSetter 62 | public void setBuildOptions(String buildOptions) { 63 | this.buildOptions = buildOptions; 64 | } 65 | 66 | @Override 67 | public StepExecution start(StepContext context) throws Exception { 68 | return new MatlabBuildStepExecution(context, this); 69 | } 70 | 71 | @Extension 72 | public static class CommandStepDescriptor extends StepDescriptor { 73 | 74 | @Override 75 | public Set> getRequiredContext() { 76 | return ImmutableSet.of(TaskListener.class, FilePath.class, Launcher.class, 77 | EnvVars.class, Run.class); 78 | } 79 | 80 | @Override 81 | public String getFunctionName() { 82 | return Message.getValue("matlab.build.build.step.name"); 83 | } 84 | 85 | @Override 86 | public String getDisplayName() { 87 | return Message.getValue("matlab.build.step.display.name"); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/pipeline/RunMatlabCommandStep.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci.pipeline; 2 | 3 | /** 4 | * Copyright 2020-2024 The MathWorks, Inc. 5 | */ 6 | 7 | import java.io.Serializable; 8 | import java.util.Set; 9 | import org.jenkinsci.plugins.workflow.steps.Step; 10 | import org.jenkinsci.plugins.workflow.steps.StepContext; 11 | import org.jenkinsci.plugins.workflow.steps.StepDescriptor; 12 | import org.jenkinsci.plugins.workflow.steps.StepExecution; 13 | import org.kohsuke.stapler.DataBoundConstructor; 14 | import org.kohsuke.stapler.DataBoundSetter; 15 | import com.google.common.collect.ImmutableSet; 16 | import hudson.EnvVars; 17 | import hudson.Extension; 18 | import hudson.FilePath; 19 | import hudson.Launcher; 20 | import hudson.model.Run; 21 | import hudson.model.TaskListener; 22 | import hudson.Util; 23 | 24 | import com.mathworks.ci.Message; 25 | 26 | public class RunMatlabCommandStep extends Step implements Serializable { 27 | 28 | private static final long serialVersionUID = 1L; 29 | 30 | private String command; 31 | private String startupOptions = ""; 32 | 33 | @DataBoundConstructor 34 | public RunMatlabCommandStep(String command) { 35 | this.command = command; 36 | } 37 | 38 | public String getCommand() { 39 | return this.command; 40 | } 41 | 42 | @DataBoundSetter 43 | public void setStartupOptions(String startupOptions) { 44 | this.startupOptions = startupOptions; 45 | } 46 | 47 | public String getStartupOptions() { 48 | return Util.fixNull(this.startupOptions); 49 | } 50 | 51 | @Override 52 | public StepExecution start(StepContext context) throws Exception { 53 | return new MatlabCommandStepExecution(context, this); 54 | } 55 | 56 | @Extension 57 | public static class CommandStepDescriptor extends StepDescriptor { 58 | 59 | @Override 60 | public Set> getRequiredContext() { 61 | return ImmutableSet.of(TaskListener.class, FilePath.class, Launcher.class, 62 | EnvVars.class, Run.class); 63 | } 64 | 65 | @Override 66 | public String getFunctionName() { 67 | return Message.getValue("matlab.command.build.step.name"); 68 | } 69 | 70 | @Override 71 | public String getDisplayName() { 72 | return Message.getValue("matlab.command.step.display.name"); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/tools/InstallationFailedException.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci.tools; 2 | 3 | /** 4 | * Copyright 2024, The MathWorks, Inc. 5 | */ 6 | 7 | import java.io.IOException; 8 | 9 | // Extend IOException so we can throw and stop the build if installation fails 10 | 11 | public class InstallationFailedException extends IOException { 12 | 13 | InstallationFailedException(String message) { 14 | super(message); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/tools/MatlabRelease.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci.tools; 2 | 3 | /** 4 | * Copyright 2025, The MathWorks, Inc. 5 | */ 6 | 7 | public class MatlabRelease { 8 | public String name; 9 | public boolean isPrerelease; 10 | 11 | public MatlabRelease(String name, boolean isPrerelease) { 12 | this.name = name; 13 | this.isPrerelease = isPrerelease; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/mathworks/ci/utilities/GetSystemProperties.java: -------------------------------------------------------------------------------- 1 | package com.mathworks.ci.utilities; 2 | 3 | /** 4 | * Copyright 2024, The MathWorks, Inc. 5 | */ 6 | 7 | import jenkins.security.MasterToSlaveCallable; 8 | 9 | public class GetSystemProperties extends MasterToSlaveCallable { 10 | 11 | private static final long serialVersionUID = 1L; 12 | 13 | private final String[] properties; 14 | 15 | public GetSystemProperties(String... properties) { 16 | this.properties = properties; 17 | } 18 | 19 | public String[] call() { 20 | String[] values = new String[properties.length]; 21 | for (int i = 0; i < properties.length; i++) { 22 | values[i] = System.getProperty(properties[i]); 23 | } 24 | return values; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/resources/+ciplugins/+jenkins/BuildReportPlugin.m: -------------------------------------------------------------------------------- 1 | classdef BuildReportPlugin < matlab.buildtool.plugins.BuildRunnerPlugin 2 | 3 | % Copyright 2024 The MathWorks, Inc. 4 | 5 | properties 6 | TaskDetails = {}; 7 | end 8 | 9 | methods (Access=protected) 10 | function runTaskGraph(plugin, pluginData) 11 | runTaskGraph@matlab.buildtool.plugins.BuildRunnerPlugin(plugin, pluginData); 12 | 13 | [fID, msg] = fopen(fullfile(getenv("MW_MATLAB_TEMP_FOLDER"),"buildArtifact.json"), "w"); 14 | if fID == -1 15 | warning("ciplugins:jenkins:BuildReportPlugin:UnableToOpenFile","Could not open a file for Jenkins build result table due to: %s", msg); 16 | else 17 | closeFile = onCleanup(@()fclose(fID)); 18 | a = struct(); 19 | a.taskDetails = plugin.TaskDetails; 20 | s = jsonencode(a, PrettyPrint=true); 21 | fprintf(fID, "%s", s); 22 | end 23 | end 24 | 25 | function runTask(plugin, pluginData) 26 | runTask@matlab.buildtool.plugins.BuildRunnerPlugin(plugin, pluginData); 27 | 28 | taskDetail = getCommonTaskDetail(pluginData); 29 | plugin.TaskDetails = [plugin.TaskDetails, taskDetail]; 30 | end 31 | 32 | function skipTask(plugin, pluginData) 33 | skipTask@matlab.buildtool.plugins.BuildRunnerPlugin(plugin, pluginData); 34 | 35 | taskDetail = getCommonTaskDetail(pluginData); 36 | taskDetail.skipReason = pluginData.SkipReason; 37 | plugin.TaskDetails = [plugin.TaskDetails, taskDetail]; 38 | end 39 | end 40 | end 41 | 42 | function taskDetail = getCommonTaskDetail(pluginData) 43 | taskDetail = struct(); 44 | taskDetail.name = pluginData.TaskResults.Name; 45 | taskDetail.description = pluginData.TaskGraph.Tasks.Description; 46 | taskDetail.failed = pluginData.TaskResults.Failed; 47 | taskDetail.skipped = pluginData.TaskResults.Skipped; 48 | taskDetail.duration = string(pluginData.TaskResults.Duration); 49 | end -------------------------------------------------------------------------------- /src/main/resources/+ciplugins/+jenkins/TaskRunProgressPlugin.m: -------------------------------------------------------------------------------- 1 | classdef TaskRunProgressPlugin < matlab.buildtool.plugins.BuildRunnerPlugin 2 | % 3 | 4 | % Copyright 2023 The MathWorks, Inc. 5 | 6 | methods (Access=protected) 7 | 8 | function runTask(plugin, pluginData) 9 | disp("[MATLAB-Build-" + pluginData.TaskResults.Name + "-" + getenv('MW_BUILD_PLUGIN_ACTION_ID') +"]"); 10 | runTask@matlab.buildtool.plugins.BuildRunnerPlugin(plugin, pluginData); 11 | end 12 | 13 | function skipTask(plugin, pluginData) 14 | disp("[MATLAB-Build-" + pluginData.TaskResults.Name + "-" + getenv('MW_BUILD_PLUGIN_ACTION_ID') +"]"); 15 | skipTask@matlab.buildtool.plugins.BuildRunnerPlugin(plugin, pluginData); 16 | end 17 | end 18 | end -------------------------------------------------------------------------------- /src/main/resources/+ciplugins/+jenkins/getDefaultPlugins.m: -------------------------------------------------------------------------------- 1 | function plugins = getDefaultPlugins(pluginProviderData) 2 | % 3 | 4 | % Copyright 2024 The MathWorks, Inc. 5 | arguments 6 | pluginProviderData (1,1) struct = struct(); 7 | end 8 | 9 | plugins = [ ... 10 | matlab.buildtool.internal.getFactoryDefaultPlugins(pluginProviderData) ... 11 | ciplugins.jenkins.BuildReportPlugin() ... 12 | ciplugins.jenkins.TaskRunProgressPlugin() ... 13 | ]; 14 | end -------------------------------------------------------------------------------- /src/main/resources/com/mathworks/ci/BuildArtifactAction/index.jelly: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |

11 | 12 |

13 |
14 | 15 | 16 | No tasks 17 | 18 | 19 |
20 | ${(it.totalCount)} Tasks 21 | 22 | (${h.getDiffString(it.failCount-prev.failCount)}) 23 | 24 | 25 | , ${(it.skipCount)} Skipped 26 | 27 | (${h.getDiffString(it.skipCount-prev.skipCount)}) 28 | 29 | 30 |
31 |
32 | 33 |
34 | 35 |
36 |
37 |
38 | ${(it.failCount)} Failed 39 | 40 | (${h.getDiffString(it.totalCount-prev.totalCount)}) 41 | 42 |
43 |
44 |
45 |
46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 68 | 90 | 95 | 96 | 97 | 98 | 99 |
TaskStatus DescriptionDuration (HH:mm:ss)
61 | 62 | ${p.taskName} 63 | 64 | 65 | ${p.taskName} 66 | 67 | 69 | 70 | 71 | 72 | Failed 73 | 74 | 75 | 76 | 77 | Succeeded 78 | 79 | 80 | 81 | 82 | Skipped 83 | 84 | (${p.skipReason}) 85 | 86 | 87 | 88 | 89 | 91 | 92 | ${p.taskDescription} 93 | 94 | ${p.taskDuration}
100 |
101 |
102 |
-------------------------------------------------------------------------------- /src/main/resources/com/mathworks/ci/BuildArtifactAction/summary.jelly: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 |

MATLAB Build Results

10 | 11 | 12 |
Unable to generate a build artifact.
13 |
14 |
15 |

Tasks run: ${it.totalCount}

16 |
17 | Failed: ${it.failCount} 18 |
19 |
20 | Skipped: ${it.skipCount} 21 |
22 |
23 |
-------------------------------------------------------------------------------- /src/main/resources/com/mathworks/ci/MatlabBuilder/RunTestsAutomaticallyOption/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/main/resources/com/mathworks/ci/MatlabBuilder/RunTestsAutomaticallyOption/help-taCoberturaChkBx.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | Generate a Cobertura code coverage report, named cobertura.xml, to be stored in the matlabTestArtifacts folder of the current Jenkins workspace. 4 |
5 |
-------------------------------------------------------------------------------- /src/main/resources/com/mathworks/ci/MatlabBuilder/RunTestsAutomaticallyOption/help-taJunitChkBx.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | Generate a JUnit style test report, named junittestresults.xml, to be stored in the matlabTestArtifacts folder of the current Jenkins workspace. 4 |
5 |
-------------------------------------------------------------------------------- /src/main/resources/com/mathworks/ci/MatlabBuilder/RunTestsAutomaticallyOption/help-taModelCoverageChkBx.html: -------------------------------------------------------------------------------- 1 |
2 |
Generate a Cobertura model coverage report, coberturamodelcoverage.xml, and save it in the matlabTestArtifacts folder of the Jenkins workspace. 3 | This report includes coverage results for Simulink® models that are tested using Simulink Test™.
4 |
Note: This feature requires a Simulink Coverage™ license and is supported only in MATLAB R2018b or a newer release.
5 |
6 |
-------------------------------------------------------------------------------- /src/main/resources/com/mathworks/ci/MatlabBuilder/RunTestsAutomaticallyOption/help-taPDFReportChkBx.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | Generate a test report in PDF format, testreport.pdf, and save it in the matlabTestArtifacts folder of the Jenkins workspace. 4 |

5 | Due to Jenkins Content Security Policy rules, the generated report might not open properly from within the Jenkins workspace. 6 | Consider copying the report to a location outside the workspace and opening it from there. For more information, 7 | see Configuring Jenkins Content Security Policy. 8 |

9 | Note:This feature is not currently supported on MacOS platforms. 10 |
-------------------------------------------------------------------------------- /src/main/resources/com/mathworks/ci/MatlabBuilder/RunTestsAutomaticallyOption/help-taSTMResultsChkBx.html: -------------------------------------------------------------------------------- 1 |
2 |
Export Simulink Test™ Manager results in MLDATX format, simulinktestresults.mldatx, and save them in the matlabTestArtifacts folder of the Jenkins workspace.
3 |
Note: This feature requires a Simulink Test license and is supported only in MATLAB R2019a or a newer release. 4 |
-------------------------------------------------------------------------------- /src/main/resources/com/mathworks/ci/MatlabBuilder/RunTestsAutomaticallyOption/help-tatapChkBx.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | Generate a TAP style test report, named taptestresults.tap, to be stored in the matlabTestArtifacts folder of the current Jenkins workspace. 4 |
5 |
-------------------------------------------------------------------------------- /src/main/resources/com/mathworks/ci/MatlabBuilder/RunTestsWithCustomCommandOption/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/main/resources/com/mathworks/ci/MatlabBuilder/RunTestsWithCustomCommandOption/help-customMatlabCommand.html: -------------------------------------------------------------------------------- 1 |
2 | To run your tests, enter MATLAB commands separated by semicolons.
3 | Example: cd c:\MATLAB\tests; runtests('IncludingSubfolder','true');
4 | 5 |
 
6 | Recommendation:If you require a number of MATLAB commands to execute your build, consider writing a MATLAB script and executing the script file instead.
7 | Note: The build will fail if the execution of any MATLAB command causes an error. 8 |
9 |
-------------------------------------------------------------------------------- /src/main/resources/com/mathworks/ci/MatlabBuilder/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Using this build step is not recommended and will be removed in a feature release. Use “Run MATLAB Tests” or “Run MATLAB Command” instead. 5 | 6 | 7 | 8 | 9 | 10 | 11 |