Skip to content

Commit

Permalink
Get build tool from CMakeCache.txt and expose it as env var
Browse files Browse the repository at this point in the history
`CMAKE_BUILD_TOOL`
  • Loading branch information
15knots committed Jun 28, 2015
1 parent 871ad76 commit 1d0738f
Show file tree
Hide file tree
Showing 6 changed files with 518 additions and 1 deletion.
193 changes: 193 additions & 0 deletions src/main/java/de/marw/cmake/cmakecache/CMakeCacheFileParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
/*******************************************************************************
* 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;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* A simple parser for CMake cache files ({@code CMakeCache.txt}). This
* implementation extracts only key-value-pairs corresponding to an entry. It
* does not extract any help texts nor entry types.
*
* @author Martin Weber
*/
public class CMakeCacheFileParser {

// input line is: key:type=value
private static final Pattern reg = Pattern
.compile("([^=:]*):([^=]*)=(.*[^\t ]|[\t ]*)[\t ]*");
// input line is: "key":type=value
private static final Pattern regQuoted = Pattern
.compile("\"([^=:]*)\":([^=]*)=(.*[^\t ]|[\t ]*)[\t ]*");
// input line is: key=value
private static final Pattern regNoType = Pattern
.compile("([^=]*)=(.*[^\t ]|[\t ]*)[\t ]*");
// input line is: "key"=value
private static final Pattern regQuotedNoType = Pattern
.compile("\"([^=]*)\"=(.*[^\t ]|[\t ]*)[\t ]*");

/**
* Parses the content of the specified input stream as a CMake cache file
* content. <br>
* This implementation is inspired by <a href=
* "https://github.com/Kitware/CMake/blob/master/Source/cmCacheManager.cxx"
* >cmCacheManager.cxx</a>.
*
* @param is
* the input stream that serves the content of the CMake cache file
* @param filter
* an optional filter for CMake cache file entries or {@code null} if
* all entries are of interest
* @param parsedEntries
* receives the parsed cache file entries. Specify {@code null}, if you
* want to verify the correct syntax of the cache file only. Specify an
* instance of {@link List}, if you expect multiple cache entires of
* the same key int the file. Normally, you would specify an instance
* of {@link Set} here.
* @param errorLog
* receives messages concerning parse errors. Specify {@code null}, if
* you are not interested in error messages.
* @return {@code true} if the file could be parsed without errors, otherwise
* {@code false}
* @throws IOException
* if an operation on the input stream failed
*/
public boolean parse(final InputStream is, final EntryFilter filter,
Collection<SimpleCMakeCacheEntry> parsedEntries, List<String> errorLog)
throws IOException {

final LineNumberReader reader = new LineNumberReader(new InputStreamReader(
is));
boolean hasErrors = false;

Map<String, SimpleCMakeCacheEntry> uniqueMap = null;
if (parsedEntries != null && parsedEntries instanceof Set) {
// avoid returning duplicate keys
uniqueMap = new HashMap<String, SimpleCMakeCacheEntry>();
}

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.
* <p>
* Instances of this interface may be passed to the
* {@link CMakeCacheFileParser#CMakeCacheFileParser()} constructor of the
* {@code CMakeCacheFileParser}</code> 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 <code>true</code> if and only if <code>key</code> should be
* included
*/
boolean accept(String key);
}
}
67 changes: 67 additions & 0 deletions src/main/java/de/marw/cmake/cmakecache/SimpleCMakeCacheEntry.java
Original file line number Diff line number Diff line change
@@ -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;
}

}
110 changes: 110 additions & 0 deletions src/main/java/de/marw/cmake/cmakecache/SimpleCMakeCacheTxt.java
Original file line number Diff line number Diff line change
@@ -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<String> tools;
private List<String> 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<String> tools = new ArrayList<String>();
ArrayList<String> commands = new ArrayList<String>();

// parse CMakeCache.txt...
InputStream is = null;
try {
is = new FileInputStream(file);
final Set<SimpleCMakeCacheEntry> entries = new HashSet<SimpleCMakeCacheEntry>();
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<String> 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<String> getCmakeCommands() {
return commands;
}
}
Loading

0 comments on commit 1d0738f

Please sign in to comment.