Skip to content
This repository was archived by the owner on Mar 27, 2025. It is now read-only.

Commit 2d0b1be

Browse files
authored
Merge pull request #238 from mathworks/2.9.1-SNAPSHOT-Qualification
2.9.1 snapshot qualification (2.10.0)
2 parents 7d5d6a2 + 20980e3 commit 2d0b1be

File tree

18 files changed

+1249
-151
lines changed

18 files changed

+1249
-151
lines changed

.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
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+
**/.DS_Store

CONFIGDOC.md

Lines changed: 184 additions & 134 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Continuous Integration with MATLAB on Jenkins
2-
This plugin enables you to run MATLAB® scripts, functions, and statements as part of your build. You also can run MATLAB and Simulink® tests, and generate test and coverage artifacts such as JUnit-style test results and Cobertura code and model coverage reports.
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.
33

44
## Releases
55
For a detailed list of releases, see [Change Logs](/CHANGELOG.md).

azure-pipelines.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
strategy:
77
matrix:
88
linux:
9-
imageName: 'ubuntu-18.04'
9+
imageName: 'ubuntu-latest'
1010
mac:
1111
imageName: 'macOS-10.15'
1212
windows:

examples/Run-MATLAB-Tests.md

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,37 +24,37 @@ To follow the steps in this example:
2424
Create a new project and configure it by following these steps:
2525
1. In your Jenkins interface, select **New Item** on the left. A new page opens where you can choose the type of your project. Enter a project name, and then click **Freestyle project**. To confirm your choices, click **OK**.
2626

27-
![create_project](https://user-images.githubusercontent.com/48831250/193948023-65f038c2-a81e-416b-8dea-0cf91ae5f105.png)
27+
![create_project](https://user-images.githubusercontent.com/48831250/217659170-43474c58-d6a1-44fa-bf72-eafe2aca49bb.png)
2828

2929
2. On the project configuration page, in the **Source Code Management** section, specify the repository that hosts your tests.
3030

31-
![source_control](https://user-images.githubusercontent.com/48831250/94478391-37a73700-01a1-11eb-9f89-a5a71413baf0.png)
31+
![source_control](https://user-images.githubusercontent.com/48831250/217660122-eddabcd5-cab1-4c41-a175-3641457b6d2c.png)
3232

33-
3. In the **Build Environment** section, select **Use MATLAB version** and specify the MATLAB version you want to use in the build. If your desired MATLAB version is not listed under **Use MATLAB version**, enter the full path to its root folder in the **MATLAB root** box.
33+
3. In the **Build Environment** section, select **Use MATLAB version** and specify the MATLAB version you want to use in the build. If your preferred MATLAB version is not listed under **Use MATLAB version**, enter the full path to its root folder in the **MATLAB root** box.
3434

35-
![build_environment](https://user-images.githubusercontent.com/48831250/193948480-564ac249-0ef2-407b-b18c-ee52b5129ac0.png)
35+
![build_environment](https://user-images.githubusercontent.com/48831250/217660546-65dc1045-2e4b-4e4b-a1cb-c4b0fedbdbc3.png)
3636

37-
4. In the **Build** section, select **Add build step > Run MATLAB Tests**. Then, specify the artifacts to be generated in the project workspace. In this example, the plugin generates Cobertura code coverage and JUnit-style test results reports. Furthermore, to generate the coverage report, the plugin uses only the code in the `source` folder located in the root of the repository. For more information about the build steps provided by the plugin, see [Plugin Configuration Guide](../CONFIGDOC.md).
37+
4. In the **Build Steps** section, select **Add build step > Run MATLAB Tests**. Then, specify the artifacts to be generated in the project workspace. In this example, the plugin generates test results in JUnit XML format and code coverage results in Cobertura XML format. Furthermore, to generate the coverage results, the plugin uses only the code in the `source` folder located in the root of the repository. For more information about the build steps provided by the plugin, see [Plugin Configuration Guide](../CONFIGDOC.md).
3838

39-
![run_,matlab_tests](https://user-images.githubusercontent.com/48831250/193948891-aa09960a-04ba-4a13-9eea-e4bebab9371d.png)
39+
![run_matlab_tests](https://user-images.githubusercontent.com/48831250/217660935-7b6fbcda-5149-4863-983c-64360b8edd07.png)
4040

41-
5. In the **Post-build Actions** section, add two post-build actions to publish the Cobertura code coverage and JUnit-style test results reports. For each artifact, provide the path to the report.
41+
5. In the **Post-build Actions** section, add two post-build actions to publish the JUnit-style test results and the Cobertura code coverage results. For each artifact, provide the path to the report.
4242

43-
![post_build](https://user-images.githubusercontent.com/48831250/193949176-aced5dfb-f7b9-4978-8726-7d01bae4bc97.png)
43+
![post_build](https://user-images.githubusercontent.com/48831250/217661749-c0ed8340-9fe8-4f88-82f9-f91c2737259d.png)
4444

45-
6. Click **Save** to save the project configuration settings. You can access and modify your settings at a later stage by selecting **Configure** in the project interface, which displays the project name at the top-left of the page.
45+
6. Click **Save** to save the project configuration settings. You can access and modify your settings at a later stage by selecting **Configure** in the project interface, which displays the project name at the upper-left corner of the page.
4646

4747
## Run Tests and Inspect Artifacts
48-
To build your freestyle project, click **Build Now** in the project interface. Jenkins triggers a build, assigns it a number under **Build History**, and runs the build. If the build is successful, a blue circle icon appears next to the build number. If the build fails, Jenkins adds a red circle icon. In this example, the build succeeds because all the tests in the Times Table App project pass.
48+
To build your freestyle project, click **Build Now** in the project interface. Jenkins triggers a build, assigns it a number under **Build History**, and runs the build. If the build succeeds, a green icon appears next to the build number. If the build fails, Jenkins adds a red icon. In this example, the build succeeds because all the tests in the Times Table App project pass.
4949

5050
Navigate to the project workspace by clicking the **Workspace** icon in the project interface. The generated artifacts are in the `matlabTestArtifacts` folder of the workspace.
5151

52-
![workspace](https://user-images.githubusercontent.com/48831250/193951807-a09ef1f8-cf7e-49c8-8615-af5fbd97acd0.png)
52+
![workspace](https://user-images.githubusercontent.com/48831250/217663519-d5a4c5bb-43e5-4ff4-ae32-c4b5e1181da7.png)
5353

54-
Click the **Status** icon in the project interface. You can access the published artifacts by opening the **Latest Test Result** and **Coverage Report** links. For example, open the **Latest Test Result** link to view the published JUnit-style test results. In the new page, open the link in the **All Tests** table. The table expands and lists information for each of the test classes within the Times Table App project.
54+
Click the **Status** icon in the project interface. You can access the published artifacts by clicking the **Latest Test Result** and **Coverage Report** links. For example, click the **Latest Test Result** link to view the published JUnit-style test results. On the test results page, click the **(root)** link in the **All Tests** table. The table expands and lists information for each of the test classes within the Times Table App project.
5555

56-
![test_results](https://user-images.githubusercontent.com/48831250/194086287-35f7c677-4c18-4316-b676-f2491ffa20a7.png)
56+
![test_results](https://user-images.githubusercontent.com/48831250/217663985-14b433e2-7546-40b1-a2e3-34d56f1f11ff.png)
5757

5858
## See Also
5959
* [Plugin Configuration Guide](../CONFIGDOC.md)<br/>
60-
* [Explore an Example Project (MATLAB)](https://www.mathworks.com/help/matlab/matlab_prog/explore-an-example-project.html)
60+
* [Explore an Example Project (MATLAB)](https://www.mathworks.com/help/matlab/matlab_prog/explore-an-example-project.html)
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package com.mathworks.ci;
2+
3+
/**
4+
* Copyright 2022 The MathWorks, Inc.
5+
*
6+
*/
7+
8+
import java.io.IOException;
9+
import org.jenkinsci.plugins.workflow.steps.StepContext;
10+
import org.jenkinsci.plugins.workflow.steps.SynchronousNonBlockingStepExecution;
11+
import hudson.EnvVars;
12+
import hudson.FilePath;
13+
import hudson.Launcher;
14+
import hudson.Launcher.ProcStarter;
15+
import hudson.model.Result;
16+
import hudson.model.TaskListener;
17+
18+
public class MatlabBuildStepExecution extends SynchronousNonBlockingStepExecution<Void> implements MatlabBuild {
19+
20+
private static final long serialVersionUID = 4771831219402275744L;
21+
22+
private String tasks;
23+
24+
public MatlabBuildStepExecution(StepContext context, String tasks) {
25+
super(context);
26+
this.tasks = tasks;
27+
}
28+
29+
private String getTasks() {
30+
return this.tasks;
31+
}
32+
33+
@Override
34+
public Void run() throws Exception {
35+
final Launcher launcher = getContext().get(Launcher.class);
36+
final FilePath workspace = getContext().get(FilePath.class);
37+
final TaskListener listener = getContext().get(TaskListener.class);
38+
final EnvVars env = getContext().get(EnvVars.class);
39+
40+
//Make sure the Workspace exists before run
41+
42+
workspace.mkdirs();
43+
44+
int exitCode = execMatlabCommand(workspace, launcher, listener, env);
45+
46+
if(exitCode != 0){
47+
// throw an exception if return code is non-zero
48+
stop(new MatlabExecutionException(exitCode));
49+
}
50+
51+
getContext().setResult(Result.SUCCESS);
52+
return null;
53+
}
54+
55+
@Override
56+
public void stop(Throwable cause) throws Exception {
57+
getContext().onFailure(cause);
58+
}
59+
60+
private int execMatlabCommand(FilePath workspace, Launcher launcher,
61+
TaskListener listener, EnvVars envVars) throws IOException, InterruptedException {
62+
final String uniqueTmpFldrName = getUniqueNameForRunnerFile();
63+
final String uniqueBuildFile =
64+
"build_" + getUniqueNameForRunnerFile().replaceAll("-", "_");
65+
final FilePath uniqueTmpFolderPath =
66+
getFilePathForUniqueFolder(launcher, uniqueTmpFldrName, workspace);
67+
68+
// Create MATLAB script
69+
createMatlabScriptByName(uniqueTmpFolderPath, uniqueBuildFile, workspace, listener);
70+
ProcStarter matlabLauncher;
71+
72+
try {
73+
matlabLauncher = getProcessToRunMatlabCommand(workspace, launcher, listener, envVars,
74+
"cd('"+ uniqueTmpFolderPath.getRemote().replaceAll("'", "''") +"'); "+ uniqueBuildFile, uniqueTmpFldrName);
75+
listener.getLogger()
76+
.println("#################### Starting command output ####################");
77+
return matlabLauncher.pwd(workspace).join();
78+
79+
} finally {
80+
// Cleanup the tmp directory
81+
if (uniqueTmpFolderPath.exists()) {
82+
uniqueTmpFolderPath.deleteRecursive();
83+
}
84+
}
85+
}
86+
87+
private void createMatlabScriptByName(FilePath uniqeTmpFolderPath, String uniqueScriptName, FilePath workspace, TaskListener listener) throws IOException, InterruptedException {
88+
89+
// Create a new command runner script in the temp folder.
90+
final FilePath matlabBuildFile =
91+
new FilePath(uniqeTmpFolderPath, uniqueScriptName + ".m");
92+
final String tasks = getContext().get(EnvVars.class).expand(getTasks());
93+
String cmd = "buildtool";
94+
95+
if (!tasks.trim().isEmpty()) {
96+
cmd += " " + tasks;
97+
}
98+
final String matlabBuildFileContent =
99+
"cd '" + workspace.getRemote().replaceAll("'", "''") + "';\n" + cmd;
100+
101+
// Display the commands on console output for users reference
102+
listener.getLogger()
103+
.println("Generating MATLAB script with content:\n" + cmd + "\n");
104+
105+
matlabBuildFile.write(matlabBuildFileContent, "UTF-8");
106+
}
107+
}
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
package com.mathworks.ci;
2+
3+
/**
4+
* Copyright 2022 The MathWorks, Inc.
5+
*
6+
*/
7+
8+
import java.io.IOException;
9+
import javax.annotation.Nonnull;
10+
import org.kohsuke.stapler.DataBoundConstructor;
11+
import org.kohsuke.stapler.DataBoundSetter;
12+
import org.kohsuke.stapler.StaplerRequest;
13+
import hudson.EnvVars;
14+
import hudson.Extension;
15+
import hudson.FilePath;
16+
import hudson.Launcher;
17+
import hudson.Launcher.ProcStarter;
18+
import hudson.model.AbstractProject;
19+
import hudson.model.Result;
20+
import hudson.model.Run;
21+
import hudson.model.TaskListener;
22+
import hudson.model.Computer;
23+
import hudson.tasks.BuildStepDescriptor;
24+
import hudson.tasks.Builder;
25+
import jenkins.tasks.SimpleBuildStep;
26+
import net.sf.json.JSONObject;
27+
28+
public class RunMatlabBuildBuilder extends Builder implements SimpleBuildStep, MatlabBuild {
29+
private int buildResult;
30+
private String tasks;
31+
32+
@DataBoundConstructor
33+
public RunMatlabBuildBuilder() {}
34+
35+
// Getter and Setters to access local members
36+
@DataBoundSetter
37+
public void setTasks(String tasks) {
38+
this.tasks = tasks;
39+
}
40+
41+
public String getTasks() {
42+
return this.tasks;
43+
}
44+
45+
@Extension
46+
public static class RunMatlabBuildDescriptor extends BuildStepDescriptor<Builder> {
47+
48+
// Overridden Method used to show the text under build dropdown
49+
@Override
50+
public String getDisplayName() {
51+
return Message.getValue("Builder.build.builder.display.name");
52+
}
53+
54+
@Override
55+
public boolean configure(StaplerRequest req, JSONObject formData) throws FormException {
56+
save();
57+
return super.configure(req, formData);
58+
}
59+
60+
/*
61+
* This is to identify which project type in jenkins this should be applicable.(non-Javadoc)
62+
*
63+
* @see hudson.tasks.BuildStepDescriptor#isApplicable(java.lang.Class)
64+
*
65+
* if it returns true then this build step will be applicable for all project type.
66+
*/
67+
@Override
68+
public boolean isApplicable(
69+
@SuppressWarnings("rawtypes") Class<? extends AbstractProject> jobtype) {
70+
return true;
71+
}
72+
}
73+
74+
@Override
75+
public void perform(@Nonnull Run<?, ?> build, @Nonnull FilePath workspace,
76+
@Nonnull Launcher launcher, @Nonnull TaskListener listener)
77+
throws InterruptedException, IOException {
78+
79+
// Get the environment variable specific to the this build
80+
final EnvVars env = build.getEnvironment(listener);
81+
82+
// Invoke MATLAB build and transfer output to standard
83+
// Output Console
84+
85+
86+
buildResult = execMatlabCommand(workspace, launcher, listener, env);
87+
88+
if (buildResult != 0) {
89+
build.setResult(Result.FAILURE);
90+
}
91+
}
92+
93+
private int execMatlabCommand(FilePath workspace, Launcher launcher,
94+
TaskListener listener, EnvVars envVars) throws IOException, InterruptedException {
95+
96+
/*
97+
* Handle the case for using MATLAB Axis for multi conf projects by adding appropriate
98+
* matlabroot to env PATH
99+
* */
100+
Utilities.addMatlabToEnvPathFrmAxis(Computer.currentComputer(), listener, envVars);
101+
102+
final String uniqueTmpFldrName = getUniqueNameForRunnerFile();
103+
final String uniqueBuildFile =
104+
"build_" + getUniqueNameForRunnerFile().replaceAll("-", "_");
105+
final FilePath uniqeTmpFolderPath =
106+
getFilePathForUniqueFolder(launcher, uniqueTmpFldrName, workspace);
107+
108+
// Create MATLAB script
109+
createMatlabScriptByName(uniqeTmpFolderPath, uniqueBuildFile, workspace, listener, envVars);
110+
ProcStarter matlabLauncher;
111+
112+
try {
113+
matlabLauncher = getProcessToRunMatlabCommand(workspace, launcher, listener, envVars,
114+
"cd('"+ uniqeTmpFolderPath.getRemote().replaceAll("'", "''") +"');"+ uniqueBuildFile, uniqueTmpFldrName);
115+
116+
listener.getLogger()
117+
.println("#################### Starting command output ####################");
118+
return matlabLauncher.pwd(workspace).join();
119+
120+
} catch (Exception e) {
121+
listener.getLogger().println(e.getMessage());
122+
return 1;
123+
} finally {
124+
// Cleanup the tmp directory
125+
if (uniqeTmpFolderPath.exists()) {
126+
uniqeTmpFolderPath.deleteRecursive();
127+
}
128+
}
129+
}
130+
131+
private void createMatlabScriptByName(FilePath uniqeTmpFolderPath, String uniqueScriptName, FilePath workspace, TaskListener listener, EnvVars envVars) throws IOException, InterruptedException {
132+
133+
// Create a new command runner script in the temp folder.
134+
final FilePath matlabCommandFile =
135+
new FilePath(uniqeTmpFolderPath, uniqueScriptName + ".m");
136+
final String tasks = envVars.expand(getTasks());
137+
String cmd = "buildtool";
138+
139+
if (!tasks.trim().isEmpty()) {
140+
cmd += " " + tasks;
141+
}
142+
143+
final String matlabCommandFileContent =
144+
"cd '" + workspace.getRemote().replaceAll("'", "''") + "';\n" + cmd;
145+
146+
// Display the commands on console output for users reference
147+
listener.getLogger()
148+
.println("Generating MATLAB script with content:\n" + cmd + "\n");
149+
150+
matlabCommandFile.write(matlabCommandFileContent, "UTF-8");
151+
}
152+
}

0 commit comments

Comments
 (0)