blob: 6fff0677116b58e97cb956515afcda1b4868db92 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016 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.core.build;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.ConsoleOutputStream;
import org.eclipse.cdt.core.ErrorParserManager;
import org.eclipse.cdt.core.IConsoleParser;
import org.eclipse.cdt.core.dom.ast.gnu.c.GCCLanguage;
import org.eclipse.cdt.core.dom.ast.gnu.cpp.GPPLanguage;
import org.eclipse.cdt.core.envvar.EnvironmentVariable;
import org.eclipse.cdt.core.envvar.IEnvironmentVariable;
import org.eclipse.cdt.core.model.ICModelMarker;
import org.eclipse.cdt.core.resources.IConsole;
import org.eclipse.cdt.internal.core.build.Messages;
import org.eclipse.core.resources.IBuildConfiguration;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
/**
* A Standard Build Configuration that simply calls a specified command for
* build and clean. By default, it calls 'make all' and 'make clean'.
*
* @since 6.2
*/
public class StandardBuildConfiguration extends CBuildConfiguration {
/**
* @since 6.4
*/
public static final String BUILD_CONTAINER = "stdbuild.build.container"; //$NON-NLS-1$
/**
* @since 6.4
*/
public static final String BUILD_COMMAND = "stdbuild.build.command"; //$NON-NLS-1$
/**
* @since 6.4
*/
public static final String CLEAN_COMMAND = "stdbuild.clean.command"; //$NON-NLS-1$
private static final String[] DEFAULT_BUILD_COMMAND = new String[] { "make" }; //$NON-NLS-1$
private static final String[] DEFAULT_CLEAN_COMMAND = new String[] { "make", "clean" }; //$NON-NLS-1$ //$NON-NLS-2$
private String[] buildCommand = DEFAULT_BUILD_COMMAND;
private String[] cleanCommand = DEFAULT_CLEAN_COMMAND;
private IContainer buildContainer;
private IEnvironmentVariable[] envVars;
public StandardBuildConfiguration(IBuildConfiguration config, String name) throws CoreException {
super(config, name);
applyProperties();
setupEnvVars();
}
public StandardBuildConfiguration(IBuildConfiguration config, String name, IToolChain toolChain,
String launchMode) throws CoreException {
super(config, name, toolChain, launchMode);
setupEnvVars();
}
private void applyProperties() {
String container = getProperty(BUILD_CONTAINER);
if (container != null && !container.trim().isEmpty()) {
IPath containerLoc = new org.eclipse.core.runtime.Path(container);
if (containerLoc.segmentCount() == 1) {
buildContainer = ResourcesPlugin.getWorkspace().getRoot().getProject(containerLoc.segment(0));
} else {
buildContainer = ResourcesPlugin.getWorkspace().getRoot().getFolder(containerLoc);
}
}
String buildCmd = getProperty(BUILD_COMMAND);
if (buildCmd != null && !buildCmd.trim().isEmpty()) {
buildCommand = buildCmd.split(" "); //$NON-NLS-1$
} else {
buildCommand = DEFAULT_BUILD_COMMAND;
}
String cleanCmd = getProperty(CLEAN_COMMAND);
if (cleanCmd != null && !cleanCmd.trim().isEmpty()) {
cleanCommand = cleanCmd.split(" "); //$NON-NLS-1$
} else {
cleanCommand = DEFAULT_CLEAN_COMMAND;
}
}
private void setupEnvVars() throws CoreException {
IToolChain toolchain = getToolChain();
List<IEnvironmentVariable> vars = new ArrayList<>();
String[] cc = toolchain.getCompileCommands(GCCLanguage.getDefault());
if (cc != null && cc.length > 0) {
vars.add(new EnvironmentVariable("CC", cc[0])); //$NON-NLS-1$
}
String[] cxx = toolchain.getCompileCommands(GPPLanguage.getDefault());
if (cxx != null && cxx.length > 0) {
vars.add(new EnvironmentVariable("CXX", cxx[0])); //$NON-NLS-1$
}
String mode = getLaunchMode();
if (mode != null && !mode.isEmpty()) {
vars.add(new EnvironmentVariable("BUILD_MODE", mode)); //$NON-NLS-1$
}
envVars = vars.toArray(new IEnvironmentVariable[0]);
}
@Override
public IEnvironmentVariable[] getVariables() {
return envVars;
}
public void setBuildContainer(IContainer buildContainer) {
this.buildContainer = buildContainer;
setProperty(BUILD_CONTAINER, buildContainer.getFullPath().toString());
}
public void setBuildCommand(String[] buildCommand) {
if (buildCommand != null) {
this.buildCommand = buildCommand;
setProperty(BUILD_COMMAND, String.join(" ", buildCommand)); //$NON-NLS-1$
} else {
this.buildCommand = DEFAULT_BUILD_COMMAND;
removeProperty(BUILD_COMMAND);
}
}
public void setCleanCommand(String[] cleanCommand) {
if (cleanCommand != null) {
this.cleanCommand = cleanCommand;
setProperty(CLEAN_COMMAND, String.join(" ", cleanCommand)); //$NON-NLS-1$
} else {
this.cleanCommand = DEFAULT_CLEAN_COMMAND;
removeProperty(CLEAN_COMMAND);
}
}
private void createBuildContainer(IContainer container, IProgressMonitor monitor) throws CoreException {
IContainer parent = container.getParent();
if (!(parent instanceof IProject) && !parent.exists()) {
createBuildContainer(parent, monitor);
}
if (container instanceof IFolder) {
((IFolder) container).create(IResource.FORCE | IResource.DERIVED, true, monitor);
}
}
@Override
public IContainer getBuildContainer() throws CoreException {
if (buildContainer == null) {
return super.getBuildContainer();
} else {
if (!(buildContainer instanceof IProject) && !buildContainer.exists()) {
createBuildContainer(buildContainer, new NullProgressMonitor());
}
}
return buildContainer != null ? buildContainer : super.getBuildContainer();
}
/**
* @since 6.4
*/
public IContainer getDefaultBuildContainer() throws CoreException {
return super.getBuildContainer();
}
@Override
public String getProperty(String name) {
String prop = super.getProperty(name);
if (prop != null) {
return prop;
}
switch (name) {
case BUILD_CONTAINER:
try {
return getBuildContainer().getFullPath().toString();
} catch (CoreException e) {
CCorePlugin.log(e.getStatus());
return null;
}
case BUILD_COMMAND:
return String.join(" ", buildCommand); //$NON-NLS-1$
case CLEAN_COMMAND:
return String.join(" ", cleanCommand); //$NON-NLS-1$
}
return null;
}
@Override
public boolean setProperties(Map<String, String> properties) {
if (!super.setProperties(properties)) {
return false;
}
applyProperties();
return true;
}
@Override
public IProject[] build(int kind, Map<String, String> args, IConsole console, IProgressMonitor monitor)
throws CoreException {
IProject project = getProject();
try {
project.deleteMarkers(ICModelMarker.C_MODEL_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE);
ConsoleOutputStream outStream = console.getOutputStream();
Path buildDir = getBuildDirectory();
outStream.write(String.format(Messages.StandardBuildConfiguration_0, buildDir.toString()));
List<String> command = new ArrayList<>();
command.add(buildCommand[0]);
if (!getBuildContainer().equals(getProject())) {
Path makefile = Paths.get(getProject().getFile("Makefile").getLocationURI()); //$NON-NLS-1$
Path relative = getBuildDirectory().relativize(makefile);
command.add("-f"); //$NON-NLS-1$
command.add(relative.toString());
}
for (int i = 1; i < buildCommand.length; i++) {
command.add(buildCommand[i]);
}
try (ErrorParserManager epm = new ErrorParserManager(project, getProject().getLocationURI(), this,
getToolChain().getErrorParserIds())) {
epm.setOutputStream(console.getOutputStream());
// run make
console.getOutputStream().write(String.format("%s\n", String.join(" ", command))); //$NON-NLS-1$ //$NON-NLS-2$
Process p = startBuildProcess(command, envVars, console, monitor);
if (p == null) {
console.getErrorStream().write(String.format(Messages.StandardBuildConfiguration_Failure, "")); //$NON-NLS-1$
return null;
}
IConsoleParser[] consoleParsers = new IConsoleParser[] { epm, this };
watchProcess(p, consoleParsers);
project.refreshLocal(IResource.DEPTH_INFINITE, monitor);
outStream.write(String.format(Messages.StandardBuildConfiguration_1, epm.getErrorCount(),
epm.getWarningCount(), buildDir.toString()));
}
return new IProject[] { project };
} catch (IOException e) {
throw new CoreException(
new Status(IStatus.ERROR, CCorePlugin.PLUGIN_ID, "Building " + project.getName(), e)); //$NON-NLS-1$
}
}
@Override
public void clean(IConsole console, IProgressMonitor monitor) throws CoreException {
IProject project = getProject();
try {
project.deleteMarkers(ICModelMarker.C_MODEL_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE);
ConsoleOutputStream outStream = console.getOutputStream();
Path buildDir = getBuildDirectory();
outStream.write(String.format(Messages.StandardBuildConfiguration_0, buildDir.toString()));
List<String> command = new ArrayList<>();
List<String> buildCommand;
if (cleanCommand != null) {
buildCommand = Arrays.asList(cleanCommand);
} else {
buildCommand = Arrays.asList(DEFAULT_CLEAN_COMMAND);
}
command.add(buildCommand.get(0));
// we need to add -f if the makefile isn't in the build directory
if (!getBuildContainer().equals(getProject())) {
Path makefile = Paths.get(getProject().getFile("Makefile").getLocationURI()); //$NON-NLS-1$
Path relative = getBuildDirectory().relativize(makefile);
command.add("-f"); //$NON-NLS-1$
command.add(relative.toString());
}
for (int i = 1; i < buildCommand.size(); ++i) {
command.add(buildCommand.get(i));
}
// run make
outStream.write(String.format("%s\n", String.join(" ", command))); //$NON-NLS-1$ //$NON-NLS-2$
Process p = startBuildProcess(command, envVars, console, monitor);
if (p == null) {
console.getErrorStream().write(String.format(Messages.StandardBuildConfiguration_Failure, "")); //$NON-NLS-1$
return;
}
watchProcess(p, console);
outStream.write(Messages.CBuildConfiguration_BuildComplete);
project.refreshLocal(IResource.DEPTH_INFINITE, monitor);
} catch (IOException e) {
throw new CoreException(
new Status(IStatus.ERROR, CCorePlugin.PLUGIN_ID, "Building " + project.getName(), e)); //$NON-NLS-1$
}
}
}