blob: 5d7c5211fc30a77cb6974e1c7c36571f3fbf2c91 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015 QNX Software Systems and others.
* 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
*******************************************************************************/
package org.eclipse.cdt.build.gcc.core;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.cdt.build.gcc.core.internal.Activator;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.build.IToolChain;
import org.eclipse.cdt.core.build.IToolChainType;
import org.eclipse.cdt.core.envvar.EnvironmentVariable;
import org.eclipse.cdt.core.envvar.IEnvironmentVariable;
import org.eclipse.cdt.core.parser.ExtendedScannerInfo;
import org.eclipse.cdt.core.parser.IExtendedScannerInfo;
import org.eclipse.core.resources.IBuildConfiguration;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.PlatformObject;
/**
* The GCC toolchain. Placing it in cdt.core for now.
*
* TODO move to it's own plug-in.
*
* @since 5.12
*/
public class GCCToolChain extends PlatformObject implements IToolChain {
private final IToolChainType type;
private final String name;
private final String command;
private String version;
private String target;
private Path path;
private IEnvironmentVariable pathVar;
private IEnvironmentVariable[] envVars;
public GCCToolChain(IToolChainType type, Path path, String command) {
this.type = type;
this.command = command;
getVersion(path.resolve(command).toString());
this.name = command + '-' + version;
this.path = path;
pathVar = new EnvironmentVariable("PATH", path.toString(), IEnvironmentVariable.ENVVAR_PREPEND, //$NON-NLS-1$
File.pathSeparator);
envVars = new IEnvironmentVariable[] { pathVar };
}
protected GCCToolChain(IToolChainType type, String name, String command) {
this.type = type;
this.name = name;
this.command = command;
// TODO need to pull the other info out of preferences
}
@Override
public IToolChainType getType() {
return type;
}
@Override
public String getName() {
return name;
}
@Override
public String getProperty(String key) {
// TODO for now assume it's a local gcc
// Later use the target, especially to find out arch
switch (key) {
case ATTR_OS:
return Platform.getOS();
case ATTR_ARCH:
return Platform.getOSArch();
}
return null;
}
private static Pattern versionPattern = Pattern.compile(".*(gcc|LLVM) version .*"); //$NON-NLS-1$
private static Pattern targetPattern = Pattern.compile("Target: (.*)"); //$NON-NLS-1$
private void getVersion(String command) {
try {
Process proc = new ProcessBuilder(new String[] { command, "-v" }).redirectErrorStream(true) //$NON-NLS-1$
.start();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()))) {
for (String line = reader.readLine(); line != null; line = reader.readLine()) {
Matcher versionMatcher = versionPattern.matcher(line);
if (versionMatcher.matches()) {
version = line.trim();
continue;
}
Matcher targetMatcher = targetPattern.matcher(line);
if (targetMatcher.matches()) {
target = targetMatcher.group(1);
continue;
}
}
}
} catch (IOException e) {
Activator.log(e);
}
}
public String getTarget() {
return target;
}
protected void addDiscoveryOptions(List<String> command) {
command.add("-E"); //$NON-NLS-1$
command.add("-P"); //$NON-NLS-1$
command.add("-v"); //$NON-NLS-1$
command.add("-dD"); //$NON-NLS-1$
}
@Override
public IExtendedScannerInfo getScannerInfo(IBuildConfiguration buildConfig, Path command, String[] args,
IExtendedScannerInfo baseScannerInfo, IResource resource, URI buildDirectoryURI) {
try {
Path buildDirectory = Paths.get(buildDirectoryURI);
List<String> commandLine = new ArrayList<>();
if (command.isAbsolute()) {
commandLine.add(command.toString());
} else {
commandLine.add(path.resolve(command).toString());
}
if (baseScannerInfo != null && baseScannerInfo.getIncludePaths() != null) {
for (String includePath : baseScannerInfo.getIncludePaths()) {
commandLine.add("-I" + includePath); //$NON-NLS-1$
}
}
addDiscoveryOptions(commandLine);
commandLine.addAll(Arrays.asList(args));
// Change output to stdout
for (int i = 0; i < commandLine.size() - 1; ++i) {
if (commandLine.get(i).equals("-o")) { //$NON-NLS-1$
commandLine.set(i + 1, "-"); //$NON-NLS-1$
break;
}
}
// Change source file to a tmp file (needs to be empty)
Path tmpFile = null;
for (int i = 1; i < commandLine.size(); ++i) {
if (!commandLine.get(i).startsWith("-")) { //$NON-NLS-1$
// TODO optimize by dealing with multi arg options like -o
Path filePath = buildDirectory.resolve(commandLine.get(i));
IFile[] files = ResourcesPlugin.getWorkspace().getRoot().findFilesForLocationURI(filePath.toUri());
if (files.length > 0) {
// replace it with a temp file
Path parentPath = filePath.getParent();
String extension = files[0].getFileExtension();
if (extension == null) {
// Not sure if this is a reasonable choice when
// there's
// no extension
extension = ".cpp"; //$NON-NLS-1$
} else {
extension = '.' + extension;
}
tmpFile = Files.createTempFile(parentPath, ".sc", extension); //$NON-NLS-1$
commandLine.set(i, tmpFile.toString());
}
}
}
if (tmpFile == null) {
// Have to assume there wasn't a source file. Add one in the
// resource's container
IPath parentPath = resource instanceof IFile ? resource.getParent().getLocation()
: resource.getLocation();
tmpFile = Files.createTempFile(parentPath.toFile().toPath(), ".sc", ".cpp"); //$NON-NLS-1$ //$NON-NLS-2$
commandLine.add(tmpFile.toString());
}
Files.createDirectories(buildDirectory);
// Startup the command
ProcessBuilder processBuilder = new ProcessBuilder(commandLine).directory(buildDirectory.toFile())
.redirectErrorStream(true);
CCorePlugin.getDefault().getBuildEnvironmentManager().setEnvironment(processBuilder.environment(),
buildConfig, true);
Process process = processBuilder.start();
// Scan for the scanner info
Map<String, String> symbols = new HashMap<>();
List<String> includePath = new ArrayList<>();
Pattern definePattern = Pattern.compile("#define (.*)\\s(.*)"); //$NON-NLS-1$
boolean inIncludePaths = false;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
for (String line = reader.readLine(); line != null; line = reader.readLine()) {
if (inIncludePaths) {
if (line.equals("End of search list.")) { //$NON-NLS-1$
inIncludePaths = false;
} else {
includePath.add(line.trim());
}
} else if (line.startsWith("#define ")) { //$NON-NLS-1$
Matcher matcher = definePattern.matcher(line);
if (matcher.matches()) {
symbols.put(matcher.group(1), matcher.group(2));
}
} else if (line.equals("#include <...> search starts here:")) { //$NON-NLS-1$
inIncludePaths = true;
}
}
}
try {
process.waitFor();
} catch (InterruptedException e) {
Activator.log(e);
}
Files.delete(tmpFile);
return new ExtendedScannerInfo(symbols, includePath.toArray(new String[includePath.size()]));
} catch (IOException e) {
Activator.log(e);
return null;
}
}
@Override
public String[] getErrorParserIds() {
return new String[] { "org.eclipse.cdt.core.GCCErrorParser", //$NON-NLS-1$
"org.eclipse.cdt.core.GASErrorParser", //$NON-NLS-1$
"org.eclipse.cdt.core.GLDErrorParser", //$NON-NLS-1$
"org.eclipse.cdt.core.GmakeErrorParser", //$NON-NLS-1$
"org.eclipse.cdt.core.CWDLocator" //$NON-NLS-1$
};
}
@Override
public IEnvironmentVariable getVariable(String name) {
if (pathVar.getName().equals(name)) {
return pathVar;
}
return null;
}
@Override
public IEnvironmentVariable[] getVariables() {
return envVars;
}
@Override
public Path getCommandPath(String command) {
return path.resolve(command);
}
@Override
public IResource[] getResourcesFromCommand(String[] cmd, URI buildDirectoryURI) {
// Make sure this is our command
boolean found = false;
for (String arg : cmd) {
if (arg.startsWith("-")) { //$NON-NLS-1$
break;
}
Path cmdPath = Paths.get(arg);
if (cmdPath.getFileName().toString().equals(command)) {
found = true;
break;
}
}
if (!found) {
// not our command
return null;
}
// Start at the back looking for arguments
List<IResource> resources = new ArrayList<>();
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
for (int i = cmd.length - 1; i >= 0; --i) {
String arg = cmd[i];
if (arg.startsWith("-")) { //$NON-NLS-1$
// ran into an option, we're done.
break;
}
for (IFile resource : root.findFilesForLocationURI(buildDirectoryURI.resolve(arg))) {
resources.add(resource);
}
}
return resources.toArray(new IResource[resources.size()]);
}
}