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

Commit cbb5c4d

Browse files
committed
Added changes to support running MATLAB on remote agents.
1 parent e824674 commit cbb5c4d

File tree

5 files changed

+103
-43
lines changed

5 files changed

+103
-43
lines changed

src/main/java/com/mathworks/ci/MatlabBuilder.java

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ public class MatlabBuilder extends Builder implements SimpleBuildStep {
6060
private static final String MATLAB_RUNNER_RESOURCE =
6161
"com/mathworks/ci/MatlabBuilder/runMatlabTests.m";
6262
private static final String AUTOMATIC_OPTION = "RunTestsAutomaticallyOption";
63+
private String fileSeparator;
6364

6465

6566
@DataBoundConstructor
@@ -101,6 +102,14 @@ private String getCustomMatlabCommand() {
101102
private void setEnv(EnvVars env) {
102103
this.env = env;
103104
}
105+
106+
private void setFileSeparator(String fileSeparator) {
107+
this.fileSeparator = fileSeparator;
108+
}
109+
110+
private String getFileSeparator() {
111+
return this.fileSeparator;
112+
}
104113

105114
@Extension
106115
public static class MatlabDescriptor extends BuildStepDescriptor<Builder> {
@@ -197,7 +206,7 @@ public FormValidation getFirstErrorOrWarning(
197206
}
198207
} catch (MatlabVersionNotFoundException e) {
199208
return FormValidation
200-
.error(Message.getValue("Builder.invalid.matlab.root.error"));
209+
.warning(Message.getValue("Builder.invalid.matlab.root.warning"));
201210
}
202211
}
203212
return FormValidation.ok();
@@ -266,7 +275,7 @@ public FormValidation doCheckTaCoberturaChkBx(@QueryParameter boolean taCobertur
266275
.warning(Message.getValue("Builder.matlab.cobertura.support.warning"));
267276
}
268277
} catch (MatlabVersionNotFoundException e) {
269-
return FormValidation.error(Message.getValue("Builder.invalid.matlab.root.error"));
278+
return FormValidation.warning(Message.getValue("Builder.invalid.matlab.root.warning"));
270279
}
271280
}
272281

@@ -394,6 +403,8 @@ public void perform(@Nonnull Run<?, ?> build, @Nonnull FilePath workspace,
394403
@Nonnull Launcher launcher, @Nonnull TaskListener listener)
395404
throws InterruptedException, IOException {
396405
final boolean isLinuxLauncher = launcher.isUnix();
406+
String fileSeparator = getNodeSpecificFileSeperator(launcher);
407+
this.setFileSeparator(fileSeparator);
397408

398409
// Invoke MATLAB command and transfer output to standard
399410
// Output Console
@@ -414,11 +425,12 @@ private synchronized int execMatlabCommand(Run<?, ?> build, FilePath workspace,
414425
// Copy MATLAB scratch file into the workspace only if Automatic option is selected.
415426
if (testRunMode.contains(AUTOMATIC_OPTION)) {
416427
copyMatlabScratchFileInWorkspace(MATLAB_RUNNER_RESOURCE, MATLAB_RUNNER_TARGET_FILE,
417-
workspace, getClass().getClassLoader());
428+
workspace, getClass().getClassLoader(), launcher);
418429
}
419430
ProcStarter matlabLauncher;
420431
try {
421-
MatlabReleaseInfo rel = new MatlabReleaseInfo(getLocalMatlab());
432+
FilePath nodeSpecificMatlabRoot = new FilePath(launcher.getChannel(),getLocalMatlab());
433+
MatlabReleaseInfo rel = new MatlabReleaseInfo(nodeSpecificMatlabRoot);
422434
matlabLauncher = launcher.launch().pwd(workspace).envs(this.env);
423435
if (rel.verLessThan(BASE_MATLAB_VERSION_BATCH_SUPPORT)) {
424436
ListenerLogDecorator outStream = new ListenerLogDecorator(listener);
@@ -450,7 +462,7 @@ public List<String> constructMatlabCommandWithBatch() {
450462
}
451463

452464
matlabDefaultArgs =
453-
Arrays.asList(getLocalMatlab() + File.separator + "bin" + File.separator + "matlab",
465+
Arrays.asList(getLocalMatlab() + this.getFileSeparator() + "bin" + this.getFileSeparator() + "matlab",
454466
"-batch", runCommand);
455467

456468
return matlabDefaultArgs;
@@ -473,7 +485,7 @@ public List<String> constructDefaultMatlabCommand(boolean isLinuxLauncher) {
473485

474486
private String[] getPreRunnerSwitches() {
475487
String[] preRunnerSwitches =
476-
{getLocalMatlab() + File.separator + "bin" + File.separator + "matlab", "-nosplash",
488+
{getLocalMatlab() + this.getFileSeparator() + "bin" + this.getFileSeparator() + "matlab", "-nosplash",
477489
"-nodesktop", "-noAppIcon"};
478490
return preRunnerSwitches;
479491
}
@@ -504,13 +516,22 @@ private String[] getRunnerSwitch() {
504516
}
505517

506518
private void copyMatlabScratchFileInWorkspace(String matlabRunnerResourcePath,
507-
String matlabRunnerTarget, FilePath workspace, ClassLoader classLoader)
519+
String matlabRunnerTarget, FilePath workspace, ClassLoader classLoader, Launcher launcher)
508520
throws IOException, InterruptedException {
521+
FilePath targetWorkspace = new FilePath(launcher.getChannel(), workspace.getRemote());
522+
FilePath targetFile =
523+
new FilePath(targetWorkspace, Message.getValue(matlabRunnerTarget));
509524
InputStream in = classLoader.getResourceAsStream(matlabRunnerResourcePath);
510-
Path target =
511-
new File(workspace.getRemote(), Message.getValue(matlabRunnerTarget)).toPath();
512525

513-
Files.copy(in, target, StandardCopyOption.REPLACE_EXISTING);
526+
targetFile.copyFrom(in);
527+
}
528+
529+
private String getNodeSpecificFileSeperator(Launcher launcher) {
530+
if (launcher.isUnix()) {
531+
return "/";
532+
} else {
533+
return "\\";
534+
}
514535
}
515536

516537
}

src/main/java/com/mathworks/ci/MatlabReleaseInfo.java

Lines changed: 66 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,25 @@
66
*/
77

88
import java.io.File;
9+
import java.io.IOException;
910
import java.nio.file.NotDirectoryException;
1011
import java.util.HashMap;
1112
import java.util.Map;
1213
import javax.xml.parsers.DocumentBuilder;
1314
import javax.xml.parsers.DocumentBuilderFactory;
1415
import org.apache.commons.collections.MapUtils;
16+
import org.jenkinsci.remoting.RoleChecker;
1517
import org.w3c.dom.Document;
1618
import org.w3c.dom.Element;
1719
import org.w3c.dom.Node;
1820
import org.w3c.dom.NodeList;
1921
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
22+
import hudson.FilePath;
23+
import hudson.FilePath.FileCallable;
24+
import hudson.remoting.VirtualChannel;
2025

2126
public class MatlabReleaseInfo {
22-
private String matlabRoot;
27+
private FilePath matlabRoot;
2328
private static final String VERSION_INFO_FILE = "VersionInfo.xml";
2429
private static final String VERSION_INFO_ROOT_TAG = "MathWorks_version_info";
2530
private static final String RELEASE_TAG = "release";
@@ -36,6 +41,10 @@ public class MatlabReleaseInfo {
3641
private Map<String, String> versionInfoCache = new HashMap<String, String>();
3742

3843
public MatlabReleaseInfo(String matlabRoot) {
44+
this.matlabRoot = new FilePath(new File(matlabRoot));
45+
}
46+
47+
public MatlabReleaseInfo(FilePath matlabRoot) {
3948
this.matlabRoot = matlabRoot;
4049
}
4150

@@ -73,33 +82,11 @@ public boolean verLessThan(double version) throws MatlabVersionNotFoundException
7382
private Map<String, String> getVersionInfoFromFile() throws MatlabVersionNotFoundException {
7483
if (MapUtils.isEmpty(versionInfoCache)) {
7584
try {
76-
File versionFile = new File(this.matlabRoot + File.separator + VERSION_INFO_FILE);
77-
if(versionFile.isFile()) {
78-
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
79-
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
80-
Document doc = dBuilder.parse(versionFile);
81-
82-
doc.getDocumentElement().normalize();
83-
NodeList nList = doc.getElementsByTagName(VERSION_INFO_ROOT_TAG);
84-
85-
for (int temp = 0; temp < nList.getLength(); temp++) {
86-
Node nNode = nList.item(temp);
87-
if (nNode.getNodeType() == Node.ELEMENT_NODE) {
88-
89-
Element eElement = (Element) nNode;
90-
91-
versionInfoCache.put(RELEASE_TAG, eElement.getElementsByTagName(RELEASE_TAG)
92-
.item(0).getTextContent());
93-
versionInfoCache.put(VERSION_TAG, eElement.getElementsByTagName(VERSION_TAG)
94-
.item(0).getTextContent());
95-
versionInfoCache.put(DESCRIPTION_TAG, eElement
96-
.getElementsByTagName(DESCRIPTION_TAG).item(0).getTextContent());
97-
versionInfoCache.put(DATE_TAG,
98-
eElement.getElementsByTagName(DATE_TAG).item(0).getTextContent());
99-
}
100-
}
85+
FilePath versionFile = new FilePath(this.matlabRoot, VERSION_INFO_FILE);
86+
if(versionFile.exists()) {
87+
versionInfoCache.putAll(versionFile.act(new RemoteFileOperation()));
10188
}
102-
else if(!new File(this.matlabRoot).exists()){
89+
else if(!this.matlabRoot.exists()){
10390
throw new NotDirectoryException("Invalid matlabroot path");
10491
}else {
10592
versionInfoCache.putAll(VERSION_OLDER_THAN_17A);
@@ -112,4 +99,56 @@ else if(!new File(this.matlabRoot).exists()){
11299
}
113100
return versionInfoCache;
114101
}
102+
103+
/*
104+
* Static File Callable to perform File operations on specific remote nodes.
105+
*/
106+
107+
private static final class RemoteFileOperation implements FileCallable<Map<String, String>> {
108+
109+
private static final long serialVersionUID = 1L;
110+
111+
private Map<String, String> versionInfoCache = new HashMap<String, String>();
112+
113+
@Override
114+
public void checkRoles(RoleChecker checker) throws SecurityException {
115+
116+
}
117+
118+
@SuppressFBWarnings(value = "REC_CATCH_EXCEPTION",
119+
justification = "Irrespective of exception type, intention is to handle it in same way. Also, there is no intention to propagate any runtime exception up in the hierarchy.")
120+
@Override
121+
public Map<String, String> invoke(File versionFile, VirtualChannel channel)
122+
throws IOException, InterruptedException {
123+
124+
try {
125+
126+
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
127+
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
128+
Document doc = dBuilder.parse(versionFile);
129+
doc.getDocumentElement().normalize();
130+
NodeList nList = doc.getElementsByTagName(VERSION_INFO_ROOT_TAG);
131+
132+
for (int temp = 0; temp < nList.getLength(); temp++) {
133+
Node nNode = nList.item(temp);
134+
if (nNode.getNodeType() == Node.ELEMENT_NODE) {
135+
136+
Element eElement = (Element) nNode;
137+
138+
versionInfoCache.put(RELEASE_TAG, eElement.getElementsByTagName(RELEASE_TAG)
139+
.item(0).getTextContent());
140+
versionInfoCache.put(VERSION_TAG, eElement.getElementsByTagName(VERSION_TAG)
141+
.item(0).getTextContent());
142+
versionInfoCache.put(DESCRIPTION_TAG, eElement
143+
.getElementsByTagName(DESCRIPTION_TAG).item(0).getTextContent());
144+
versionInfoCache.put(DATE_TAG,
145+
eElement.getElementsByTagName(DATE_TAG).item(0).getTextContent());
146+
}
147+
}
148+
} catch (Exception e) {
149+
throw new IOException("Error in reading MATLAB VersionInfo file",e);
150+
}
151+
return versionInfoCache;
152+
}
153+
}
115154
}

src/main/resources/config.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
Builder.display.name = Run MATLAB Tests
55
Builder.matlab.runner.target.file.name = runMatlabTests.m
66
Builder.matlab.cobertura.support.warning = To generate a Cobertura report, use MATLAB R2017b or a newer version.
7-
Builder.invalid.matlab.root.error = Unable to launch MATLAB from the specified location. Verify the MATLAB root folder path.
7+
Builder.invalid.matlab.root.warning = Unable to find MATLAB from the specified location on this system(but perhaps it exists on some agents)
88
Builder.matlab.root.empty.error = Full path to the MATLAB root folder is required.
99
Builder.matlab.test.support.error = To run tests with the Jenkins plugin, use MATLAB R2013a or a newer version.
1010
builder.matlab.automatictestoption.display.name = Automatic

src/test/java/com/mathworks/ci/MatlabBuilderTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,7 @@ public void verifyInvalidMatlabRootDisplaysError() throws Exception {
383383
project.getBuildersList().add(this.matlabBuilder);
384384
this.matlabBuilder.setMatlabRoot("/fake/matlab/path");
385385
HtmlPage page = jenkins.createWebClient().goTo("job/test0/configure");
386-
WebAssert.assertTextPresent(page, TestMessage.getValue("Builder.invalid.matlab.root.error"));
386+
WebAssert.assertTextPresent(page, TestMessage.getValue("Builder.invalid.matlab.root.warning"));
387387
}
388388

389389
/*
@@ -396,7 +396,7 @@ public void verifyValidMatlabRootDoesntDisplayError() throws Exception {
396396
project.getBuildersList().add(this.matlabBuilder);
397397
this.matlabBuilder.setMatlabRoot(getMatlabroot("R2018b"));
398398
HtmlPage page = jenkins.createWebClient().goTo("job/test0/configure");
399-
WebAssert.assertTextNotPresent(page, TestMessage.getValue("Builder.invalid.matlab.root.error"));
399+
WebAssert.assertTextNotPresent(page, TestMessage.getValue("Builder.invalid.matlab.root.warning"));
400400
}
401401

402402
/*
@@ -429,8 +429,8 @@ public void verifyCoberturaError() throws Exception {
429429
coberturaChkBx.setChecked(true);
430430
Thread.sleep(2000);
431431
String pageText = page.asText();
432-
String filteredPageText = pageText.replaceFirst(TestMessage.getValue("Builder.invalid.matlab.root.error"), "");
433-
Assert.assertTrue(filteredPageText.contains(TestMessage.getValue("Builder.invalid.matlab.root.error")));
432+
String filteredPageText = pageText.replaceFirst(TestMessage.getValue("Builder.invalid.matlab.root.warning"), "");
433+
Assert.assertTrue(filteredPageText.contains(TestMessage.getValue("Builder.invalid.matlab.root.warning")));
434434
}
435435

436436
/*

src/test/resources/testconfig.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@
44
Verify.matlab.invokes.positive = MATLAB is invoking positive tests
55
Verify.build.ignore.test.failure = Build Ignored test failure
66
Builder.matlab.cobertura.support.warning = To generate a Cobertura report, use MATLAB R2017b or a newer version.
7-
Builder.invalid.matlab.root.error = Unable to launch MATLAB from the specified location. Verify the MATLAB root folder path.
7+
Builder.invalid.matlab.root.warning = Unable to find MATLAB from the specified location on this system(but perhaps it exists on some agents)
88
Builder.matlab.root.empty.error = Full path to the MATLAB root folder is required.

0 commit comments

Comments
 (0)