();
+ }
+
+ for (String line; null != (line = reader.readLine());) {
+ int idx = 0;
+ // skip leading whitespaces...
+ for (; idx < line.length(); idx++) {
+ final char c = line.charAt(idx);
+ if (!Character.isWhitespace(c))
+ break;
+ }
+ if (!(idx < line.length()))
+ continue; // skip blank lines
+
+ if (line.charAt(idx) == '#')
+ continue; // skip cmake comment lines
+
+ if (idx < line.length()) {
+ line = line.substring(idx);
+
+ if (line.startsWith("//"))
+ continue; // ignore help string
+
+ // parse cache entry...
+ String key = null;
+ String value = null;
+ Matcher matcher;
+
+ if ((matcher = reg.matcher(line)).matches()
+ || (matcher = regQuoted.matcher(line)).matches()) {
+ // input line is: key:type=value
+ // input line is: "key":type=value
+ key = matcher.group(1);
+ // we do not need the type from group(2)
+ value = matcher.group(3);
+ } else if ((matcher = regNoType.matcher(line)).matches()
+ || (matcher = regQuotedNoType.matcher(line)).matches()) {
+ // input line is: key=value
+ // input line is: "key"=value
+ key = matcher.group(1);
+ value = matcher.group(2);
+ } else {
+ hasErrors |= true;
+ // add error message
+ if (errorLog != null) {
+ final String msg = MessageFormat.format(
+ "Error: Line {0,number,integer}: Offending entry: {1}",
+ reader.getLineNumber(), line);
+ errorLog.add(msg);
+ }
+ }
+
+ if (filter != null && parsedEntries != null) {
+ // no need to call the filter if nothing is to be returned
+ if (!filter.accept(key))
+ continue; // uninteresting entry, get next line
+ }
+
+ // if value is enclosed in single quotes ('foo') then remove them
+ // it is used to enclose trailing space or tab
+ if (key != null && value != null && value.length() >= 2
+ && value.charAt(0) == '\''
+ && value.charAt(value.length() - 1) == '\'') {
+
+ value = value.substring(1, value.length());
+ }
+
+ // store entry
+ if (parsedEntries != null) {
+ final SimpleCMakeCacheEntry entry = new SimpleCMakeCacheEntry(key,
+ value);
+ if (uniqueMap != null)
+ uniqueMap.put(key, entry);
+ else
+ parsedEntries.add(entry);
+ }
+ }
+ }
+ if (parsedEntries != null && uniqueMap != null)
+ parsedEntries.addAll(uniqueMap.values());
+ return hasErrors;
+ }
+
+ ////////////////////////////////////////////////////////////////////
+ // inner classes
+ ////////////////////////////////////////////////////////////////////
+ /**
+ * A filter for CMake cache file entry keys.
+ *
+ * Instances of this interface may be passed to the
+ * {@link CMakeCacheFileParser#CMakeCacheFileParser()} constructor of the
+ * {@code CMakeCacheFileParser} class.
+ *
+ * @author Martin Weber
+ */
+ public interface EntryFilter {
+ /**
+ * Tests whether or not the specified entry key should be included in a set
+ * returned by {@link CMakeCacheFileParser#parse}.
+ *
+ * @param key
+ * The entry key to be tested. Never {@code null}
+ * @return true
if and only if key
should be
+ * included
+ */
+ boolean accept(String key);
+ }
+}
diff --git a/src/main/java/de/marw/cmake/cmakecache/SimpleCMakeCacheEntry.java b/src/main/java/de/marw/cmake/cmakecache/SimpleCMakeCacheEntry.java
new file mode 100644
index 0000000..0c96219
--- /dev/null
+++ b/src/main/java/de/marw/cmake/cmakecache/SimpleCMakeCacheEntry.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2014 Martin Weber.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Martin Weber - Initial implementation
+ *******************************************************************************/
+package de.marw.cmake.cmakecache;
+
+/**
+ * Represents an entry of a CMakeCache.txt file in a simple form: Holds only
+ * key-value-pairs of an entry. It does not extract any help texts nor entry
+ * types.
+ *
+ * @author Martin Weber
+ */
+public class SimpleCMakeCacheEntry {
+ private final String key;
+ private final String value;
+
+ /**
+ * @throws IllegalArgumentException
+ * if {@code key} is empty
+ * @throws NullPointerException
+ * if {@code key} is {@code null} or if {@code value} is {@code null}
+ */
+ public SimpleCMakeCacheEntry(String key, String value) {
+ if (key == null) {
+ throw new NullPointerException("key");
+ }
+ if (key.length() == 0) {
+ throw new IllegalArgumentException("key");
+ }
+ if (value == null) {
+ throw new NullPointerException("value");
+ }
+
+ this.value = value;
+ this.key = key;
+ }
+
+ /**
+ * Gets the key property.
+ *
+ * @return the current key property.
+ */
+ public String getKey() {
+ return this.key;
+ }
+
+ /**
+ * Gets the value.
+ *
+ * @return the current value.
+ */
+ public String getValue() {
+ return this.value;
+ }
+
+ public String toString() {
+ return key + "=" + value;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/de/marw/cmake/cmakecache/SimpleCMakeCacheTxt.java b/src/main/java/de/marw/cmake/cmakecache/SimpleCMakeCacheTxt.java
new file mode 100644
index 0000000..83596cc
--- /dev/null
+++ b/src/main/java/de/marw/cmake/cmakecache/SimpleCMakeCacheTxt.java
@@ -0,0 +1,110 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Martin Weber.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Martin Weber - Initial implementation
+ *******************************************************************************/
+package de.marw.cmake.cmakecache;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Represents a simplistic subset of the parsed content of a CMake cache file
+ * (CMakeCache.txt).
+ *
+ * @author Martin Weber
+ */
+public class SimpleCMakeCacheTxt {
+
+ private String buildTool;
+ private List tools;
+ private List commands;
+
+ /**
+ * Creates a new object by parsing the specified file.
+ *
+ * @param file
+ * the file to parse.
+ * @throws IOException
+ * if the file could not be read
+ */
+ public SimpleCMakeCacheTxt(File file) throws IOException {
+ ArrayList tools = new ArrayList();
+ ArrayList commands = new ArrayList();
+
+ // parse CMakeCache.txt...
+ InputStream is = null;
+ try {
+ is = new FileInputStream(file);
+ final Set entries = new HashSet();
+ new CMakeCacheFileParser().parse(is, null, entries, null);
+ for (SimpleCMakeCacheEntry entry : entries) {
+ final String toolKey = entry.getKey();
+ final String tool = entry.getValue();
+ if ("CMAKE_BUILD_TOOL".equals(toolKey)) {
+ buildTool = tool;
+ } else if ("CMAKE_COMMAND".equals(toolKey)) {
+ commands.add(tool);
+ } else if ("CMAKE_CPACK_COMMAND".equals(toolKey)) {
+ commands.add(tool);
+ } else if ("CMAKE_CTEST_COMMAND".equals(toolKey)) {
+ commands.add(tool);
+ } else if ("CMAKE_C_COMPILER".equals(toolKey)) {
+ tools.add(tool);
+ } else if ("CMAKE_CXX_COMPILER".equals(toolKey)) {
+ tools.add(tool);
+ }
+ }
+ this.tools = Collections.unmodifiableList(tools);
+ this.commands = Collections.unmodifiableList(commands);
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException ignore) {
+ }
+ }
+ }
+ }
+
+ /**
+ * Gets the name of the tool that processes the generated build scripts. In
+ * most cases, this method will return the absolute file system path of the
+ * tool, such as {@code /usr/bin/make}.
+ *
+ * @return the CMAKE_BUILD_TOOL entry from the CMakeCache.txt file or
+ * {@code null} if the file could not be parsed
+ */
+ public String getBuildTool() {
+ return buildTool;
+ }
+
+ /**
+ * Gets the tools that process the source files to binary files (compilers,
+ * linkers). In most cases, this method will return the absolute file system
+ * paths of a tool, for example {@code /usr/bin/cc}.
+ */
+ public List getTools() {
+ return tools;
+ }
+
+ /**
+ * Gets the tools provided by CMake itself (cmake, cpack, ctest). In most
+ * cases, this method will return the absolute file system paths of a tool.
+ */
+ public List getCmakeCommands() {
+ return commands;
+ }
+}
diff --git a/src/main/java/hudson/plugins/cmake/BuildToolEntryParser.java b/src/main/java/hudson/plugins/cmake/BuildToolEntryParser.java
new file mode 100644
index 0000000..640aa21
--- /dev/null
+++ b/src/main/java/hudson/plugins/cmake/BuildToolEntryParser.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Martin Weber.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Martin Weber - Initial implementation
+ *******************************************************************************/
+package hudson.plugins.cmake;
+
+import hudson.FilePath;
+import hudson.remoting.VirtualChannel;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import jenkins.security.Roles;
+
+import org.jenkinsci.remoting.RoleChecker;
+
+import de.marw.cmake.cmakecache.CMakeCacheFileParser;
+import de.marw.cmake.cmakecache.CMakeCacheFileParser.EntryFilter;
+import de.marw.cmake.cmakecache.SimpleCMakeCacheEntry;
+
+/**
+ * Gets the value of the {@code "CMAKE_BUILD_TOOL"} entry from a cmake cache
+ * file.
+ *
+ * @author Martin Weber
+ */
+public class BuildToolEntryParser implements FilePath.FileCallable {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Parses the cach file and returns value of the {@code "CMAKE_BUILD_TOOL"}
+ * entry.
+ *
+ * @return the entry value or {@code null} if the file could not be parsed
+ */
+ @Override
+ public String invoke(File cmakeCacheFile, VirtualChannel channel)
+ throws IOException, InterruptedException {
+ BufferedInputStream is = new BufferedInputStream(
+ new FileInputStream(cmakeCacheFile));
+ List result = new ArrayList(
+ 1);
+ final CMakeCacheFileParser parser = new CMakeCacheFileParser();
+
+ parser.parse(is, new EntryFilter() {
+
+ @Override
+ public boolean accept(String key) {
+ return "CMAKE_BUILD_TOOL".equals(key);
+ }
+ }, result, null);
+ if (result.size() > 0) {
+ return result.get(0).getValue();
+ }
+ return null;
+ }
+
+ /*-
+ * @see org.jenkinsci.remoting.RoleSensitive#checkRoles(org.jenkinsci.remoting.RoleChecker)
+ */
+ @Override
+ public void checkRoles(RoleChecker checker) throws SecurityException {
+ checker.check(this, Roles.SLAVE);
+ }
+
+}
diff --git a/src/main/java/hudson/plugins/cmake/CmakeBuilder.java b/src/main/java/hudson/plugins/cmake/CmakeBuilder.java
index 63a3f6e..1f7a922 100644
--- a/src/main/java/hudson/plugins/cmake/CmakeBuilder.java
+++ b/src/main/java/hudson/plugins/cmake/CmakeBuilder.java
@@ -7,6 +7,7 @@
import hudson.Launcher;
import hudson.Util;
import hudson.model.BuildListener;
+import hudson.model.Environment;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.Computer;
@@ -30,11 +31,18 @@
/**
* Executes cmake as a build step.
*
- * @author Volker Kaiser
+ * @author Volker Kaiser (initial implementation)
* @author Martin Weber
*/
public class CmakeBuilder extends Builder {
+ /**
+ * the key for the build variable that holds the build tool that the
+ * build-scripts have been generated for (e.g. /usr/bin/make or
+ * /usr/bin/ninja)
+ */
+ public static final String ENV_VAR_NAME_CMAKE_BUILD_TOOL = "CMAKE_BUILD_TOOL";
+
private String sourceDir;
private String buildDir;
private String installDir;
@@ -254,6 +262,21 @@ public boolean perform(AbstractBuild, ?> build, Launcher launcher,
return false; // invokation failed
}
+ /* parse CMakeCache.txt to get the actual build tool */
+ FilePath cacheFile = theBuildDir.child("CMakeCache.txt");
+ String buildTool = cacheFile.act(new BuildToolEntryParser());
+ if (buildTool == null) {
+ listener.error("Failed to get CMAKE_BUILD_TOOL value from "
+ + cacheFile.getRemote());
+ return false; // abort build
+ }
+ // export the variable..
+ EnvVars envVars = new EnvVars(
+ CmakeBuilder.ENV_VAR_NAME_CMAKE_BUILD_TOOL, buildTool);
+ build.getEnvironments().add(Environment.create(envVars));
+ listener.getLogger().println(
+ "Exported CMAKE_BUILD_TOOL=" + buildTool);
+
/* invoke make in build dir */
if (0 != launcher
.launch()
@@ -280,6 +303,7 @@ public boolean perform(AbstractBuild, ?> build, Launcher launcher,
}
} catch (IOException e) {
Util.displayIOException(e, listener);
+ listener.error(e.getLocalizedMessage());
return false;
}
return true;
diff --git a/src/test/java/hudson/plugins/cmake/JobBuildTest.java b/src/test/java/hudson/plugins/cmake/JobBuildTest.java
index f29a612..a19cce5 100644
--- a/src/test/java/hudson/plugins/cmake/JobBuildTest.java
+++ b/src/test/java/hudson/plugins/cmake/JobBuildTest.java
@@ -1,6 +1,10 @@
package hudson.plugins.cmake;
+import static org.junit.Assert.assertNotNull;
+import hudson.Launcher;
+import hudson.model.BuildListener;
import hudson.model.FreeStyleBuild;
+import hudson.model.AbstractBuild;
import hudson.model.FreeStyleProject;
import hudson.model.Label;
import hudson.model.ParametersDefinitionProperty;
@@ -16,6 +20,7 @@
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.SingleFileSCM;
+import org.jvnet.hudson.test.TestBuilder;
/**
* Tests the CmakeBuilder in a running job.
@@ -128,4 +133,45 @@ public void testBuildVariables() throws Exception {
}
}
+ /**
+ * Verifies that the build-tool variable gets injected.
+ */
+ @Test
+ public void testBuildToolVariableInjected() throws Exception {
+ FreeStyleProject p = j.createFreeStyleProject();
+ p.setScm(scm);
+ CmakeBuilder cmb = new CmakeBuilder(CmakeTool.DEFAULT,
+ "Unix Makefiles", "src", "build/Debug", "make");
+ cmb.setCleanBuild(true);
+ cmb.setCleanInstallDir(true);
+ p.getBuildersList().add(cmb);
+ GetEnvVarBuilder gevb = new GetEnvVarBuilder();
+ p.getBuildersList().add(gevb);
+
+ FreeStyleBuild build = p.scheduleBuild2(0).get();
+ System.out.println(JenkinsRule.getLog(build));
+ assertNotNull(
+ CmakeBuilder.ENV_VAR_NAME_CMAKE_BUILD_TOOL,
+ gevb.value);
+ }
+
+ // //////////////////////////////////////////////////////////////////
+ // inner classes
+ // //////////////////////////////////////////////////////////////////
+ private static class GetEnvVarBuilder extends TestBuilder {
+ String value;
+
+ /*-
+ * @see org.jvnet.hudson.test.TestBuilder#perform(hudson.model.AbstractBuild, hudson.Launcher, hudson.model.BuildListener)
+ */
+ @Override
+ public boolean perform(AbstractBuild, ?> build, Launcher launcher,
+ BuildListener listener) throws InterruptedException,
+ IOException {
+ value = build.getEnvironment(listener).get(
+ CmakeBuilder.ENV_VAR_NAME_CMAKE_BUILD_TOOL);
+ return true;
+ }
+
+ }
}