1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-23 14:42:11 +02:00

Bug 156872: CDT Internal builder parallelization proposal

This commit is contained in:
Oleg Krasilnikov 2006-10-13 11:30:26 +00:00
parent 83d65a28ca
commit c3bcbb5d9e
7 changed files with 1132 additions and 50 deletions

View file

@ -0,0 +1,183 @@
/*******************************************************************************
* Copyright (c) 2006 Intel Corporation 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
*
* Contributors:
* Intel Corporation - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.managedbuilder.internal.buildmodel;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.cdt.managedbuilder.buildmodel.IBuildCommand;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
/**
* This class implements process pool management for internal builder
*
* NOTE: This class is subject to change and discuss,
* and is currently available in experimental mode only
*/
public class BuildProcessManager {
protected OutputStream out;
protected OutputStream err;
protected boolean show;
protected ProcessLauncher[] processes;
protected int maxProcesses;
// Number of CPUs is not dependent of object instance.
// But user can change UI settings for processes number.
// So we cannot set procNumber directly to maxProcesses.
static int procNumber = 0;
/**
* Initializes process manager
*
* @param _out Output stream
* @param _err Error output stream
* @param _show If true, print command line before launching
*/
public BuildProcessManager(OutputStream _out, OutputStream _err, boolean _show, int _procNumber) {
out = _out;
err = _err;
show = _show;
maxProcesses = _procNumber;
processes = new ProcessLauncher[maxProcesses];
}
/**
* Returns maximum number of processes
*/
public int getMaxProcesses() {
return maxProcesses;
}
/**
* Performs an attempt to launch new process. Returns BuildProcessLauncher
* if it was successfully launched, null if there is no room for it yet in
* the process pool.
*
* @param cmd Command to launch
* @param cwd Command working directory
* @param monitor Progress monitor for this task
*/
public ProcessLauncher launchProcess(IBuildCommand cmd, IPath cwd, IProgressMonitor monitor) {
if (hasEmpty()) {
int i = 0;
for (; i < maxProcesses; i++) {
if (processes[i] == null || processes[i].queryState() == ProcessLauncher.STATE_DONE) {
break;
}
}
if (i < maxProcesses) {
processes[i] = new ProcessLauncher(cmd.getCommand(), cmd.getArgs(), mapToStringArray(cmd.getEnvironment()), cwd, out, err, monitor, show);
processes[i].launch();
return processes[i];
}
}
return null;
}
/**
* Checks states of all currently running processes. If it finds
* one with state other than STATE_DONE or STATE_RUNNING, it is
* returned as a result. Otherwise this method returns null.
*/
public ProcessLauncher queryStates() {
ProcessLauncher result = null;
for (int i = 0; i < maxProcesses; i++) {
if (processes[i] != null) {
int state = processes[i].queryState();
if (state != ProcessLauncher.STATE_RUNNING) {
if (state != ProcessLauncher.STATE_DONE && result == null)
result = processes[i];
}
}
}
return result;
}
/**
* Checks states of all currently running processes.
*/
public boolean hasEmpty() {
for (int i = 0; i < maxProcesses; i++) {
if (processes[i] == null)
return true;
else {
if (processes[i].queryState() != ProcessLauncher.STATE_RUNNING)
return true;
}
}
return false;
}
/**
* Converts map to strings array
*/
protected String[] mapToStringArray(Map map){
if(map == null)
return null;
List list = new ArrayList();
for(Iterator iter = map.entrySet().iterator(); iter.hasNext();){
Map.Entry entry = (Map.Entry)iter.next();
list.add((String)entry.getKey() + "=" + (String)entry.getValue()); //$NON-NLS-1$
}
return (String[])list.toArray(new String[list.size()]);
}
/**
*
* @return
*/
static public int checkCPUNumber() {
if (procNumber > 0) return procNumber;
procNumber = 1;
int x = 0;
String os = System.getProperty("os.name"); //$NON-NLS-1$
if (os != null) {
if (os.startsWith("Win")) { //$NON-NLS-1$
try {
x = new Integer(System.getenv("NUMBER_OF_PROCESSORS")).intValue(); //$NON-NLS-1$
if (x > 0) { procNumber = x; }
} catch (NumberFormatException e) {} // fallthrough and return default
} else { // linux
String p = "/proc/cpuinfo"; //$NON-NLS-1$
try {
BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(p)));
String s;
while ((s = r.readLine() ) != null )
{ if (s.startsWith("processor\t:")) x++; } //$NON-NLS-1$
r.close();
if (x > 0) { procNumber = x; }
}
catch (IOException e) {} // fallthrough and return default
}
}
if(DbgUtil.DEBUG)
DbgUtil.trace("Number of processors detected: " + procNumber); //$NON-NLS-1$
return procNumber;
}
}

View file

@ -0,0 +1,483 @@
/*******************************************************************************
* Copyright (c) 2006 Intel Corporation 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
*
* Contributors:
* Intel Corporation - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.managedbuilder.internal.buildmodel;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.managedbuilder.buildmodel.IBuildCommand;
import org.eclipse.cdt.managedbuilder.buildmodel.IBuildDescription;
import org.eclipse.cdt.managedbuilder.buildmodel.IBuildResource;
import org.eclipse.cdt.managedbuilder.buildmodel.IBuildStep;
import org.eclipse.cdt.managedbuilder.core.IConfiguration;
import org.eclipse.cdt.managedbuilder.internal.core.Configuration;
import org.eclipse.cdt.managedbuilder.internal.core.ManagedMakeMessages;
import org.eclipse.core.resources.IWorkspaceRoot;
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.NullProgressMonitor;
/**
* This is the main class for parallel internal builder implementation
*
* NOTE: This class is subject to change and discuss,
* and is currently available in experimental mode only
*/
public class ParallelBuilder {
public static final int STATUS_OK = 0;
public static final int STATUS_ERROR = 1;
public static final int STATUS_CANCELED = 2;
public static final int STATUS_INVALID = -1;
public static final long MAIN_LOOP_DELAY = 50L;
private static final String BUILDER_MSG_HEADER = "InternalBuilder.msg.header"; //$NON-NLS-1$
private static final String LINE_SEPARATOR = System.getProperty("line.separator", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
public static int lastThreadsUsed = 0; // use externally for report purposes only
protected IPath cwd;
protected GenDirInfo dirs;
protected IProgressMonitor monitor;
protected OutputStream out;
protected OutputStream err;
protected boolean resumeOnErrors;
protected boolean buildIncrementally;
protected HashSet unsorted = new HashSet();
protected HashMap queueHash = new HashMap();
protected LinkedList queue = new LinkedList();
/**
* This class implements queue element
*/
protected class BuildQueueElement implements Comparable {
protected IBuildStep step;
protected int level;
public BuildQueueElement(IBuildStep _step, int _level) {
step = _step;
level = _level;
}
public IBuildStep getStep() {
return step;
}
public int getLevel() {
return level;
}
public void setLevel(int _level) {
level = _level;
}
public int hashCode() {
return step.hashCode();
}
public int compareTo(Object obj) {
if (obj == null)
throw new NullPointerException();
BuildQueueElement elem = (BuildQueueElement)obj;
if (elem.getLevel() > level)
return -1;
if (elem.getLevel() < level)
return 1;
return 0;
}
/**
* Updates level value
*
* @param _step
* @param _level
*/
public boolean check(IBuildStep _step, int _level) {
if (level < _level && step.equals(_step)) {
level = _level;
return true;
} else { return false; }
}
public String toString() {
return"[BuildQueueElement] " + DbgUtil.stepName(step) + " @ " + level; //$NON-NLS-1$ //$NON-NLS-2$
}
}
/**
* This class stores information about step being built
*/
protected class ActiveBuildStep {
protected IPath stepCwd;
protected GenDirInfo stepDirs;
protected IBuildStep step;
protected IBuildCommand[] cmds;
protected int activeCmd;
protected boolean done;
protected ProcessLauncher launcher;
public ActiveBuildStep(IBuildStep _step) {
step = _step;
if(dirs == null)
stepDirs = new GenDirInfo(step.getBuildDescription().getConfiguration());
else
stepDirs = dirs;
if (cwd == null)
stepCwd = step.getBuildDescription().getDefaultBuildDirLocation();
else
stepCwd = cwd;
cmds = step.getCommands(stepCwd, null, null, true);
activeCmd = -1;
done = false;
createOutDirs();
}
public boolean launchNextCmd(BuildProcessManager mgr) {
if (monitor.isCanceled()) {
done = true;
return false;
}
if (activeCmd + 1 >= cmds.length)
done = true;
else {
IBuildCommand cmd = cmds[++activeCmd];
launcher = mgr.launchProcess(cmd, stepCwd, monitor);
if (launcher != null) return true;
activeCmd--;
done = true; // temporary
}
return false;
}
public boolean isDone() {
return done;
}
public IBuildStep getStep() {
return step;
}
public ProcessLauncher getLauncher() {
return launcher;
}
protected void createOutDirs(){
IBuildResource rcs[] = step.getOutputResources();
for(int i = 0; i < rcs.length; i++){
dirs.createDir(rcs[i], new NullProgressMonitor());
}
}
}
/**
* Build process is divided into following steps:
* 1. Resources enqueueing & levelling
* 2. Queue sorting
* 3. Queue dispatching
*
* @param des Build description
* @param cwd Working directory
* @param dirs GenDirInfo?
* @param out Output stream
* @param err Error output stream
* @param monitor Progress monitor
* @param resumeOnErrors If true, build process will not stop when
* compilation errors encountered
*/
static public int build(IBuildDescription des, IPath cwd, GenDirInfo dirs, OutputStream out, OutputStream err, IProgressMonitor monitor, boolean resumeOnErrors, boolean buildIncrementally) {
IConfiguration cfg = des.getConfiguration();
if(dirs == null) dirs = new GenDirInfo(cfg);
if(cwd == null) cwd = des.getDefaultBuildDirLocation();
int threads = 1;
if (cfg instanceof Configuration) {
if (((Configuration)cfg).getParallelDef())
threads = BuildProcessManager.checkCPUNumber();
else
threads = ((Configuration)cfg).getParallelNumber();
}
ParallelBuilder builder = new ParallelBuilder(cwd, dirs, out, err, monitor, resumeOnErrors, buildIncrementally);
builder.enqueueAll(des);
builder.sortQueue();
monitor.beginTask("", builder.queue.size()); //$NON-NLS-1$
builder.dispatch(new BuildProcessManager(out, err, true, threads));
monitor.done();
lastThreadsUsed = threads;
return IBuildModelBuilder.STATUS_OK;
}
/**
* Initializes parallel builder
*/
protected ParallelBuilder(IPath _cwd, GenDirInfo _dirs, OutputStream _out, OutputStream _err, IProgressMonitor _monitor, boolean _resumeOnErrors, boolean _buildIncrementally) {
cwd = _cwd;
dirs = _dirs;
out = _out;
err = _err;
monitor = _monitor;
resumeOnErrors = _resumeOnErrors;
buildIncrementally = _buildIncrementally;
}
/**
* Enqueues build steps, calculating their levels
*/
protected void enqueueAll(IBuildDescription des) {
enqueueSteps(des.getInputStep(), 0);
}
/**
* Sorts the queue
*/
protected void sortQueue() {
Iterator iter = unsorted.iterator();
while (iter.hasNext()) {
queue.add(iter.next());
}
unsorted.clear();
unsorted = null;
queueHash.clear();
queueHash = null;
Collections.sort(queue);
}
/**
* Enqueues build steps directly accessed from the given one. Each
* new element will have level 1 if it needs rebuild and 0 otherwise.
*/
protected void enqueueSteps(IBuildStep step, int level) {
IBuildResource[] resources = step.getOutputResources();
for (int i = 0; i < resources.length; i++) {
IBuildStep steps[] = resources[i].getDependentSteps();
for (int j = 0; j < steps.length; j++) {
IBuildStep st = steps[j];
if (st != null && st.getBuildDescription().getOutputStep() != st) {
BuildQueueElement b = (BuildQueueElement)queueHash.get(st);
if (b != null){
if (b.level < level) b.setLevel(level);
} else {
//TODO: whether we need check isRemoved & needRebuild ?
if (!steps[j].isRemoved() && (!buildIncrementally || steps[j].needsRebuild())) {
addElement(steps[j], level);
}
enqueueSteps(steps[j], level + 1);
}
}
}
}
}
/**
* Adds new element to the build queue and step<->element hash map
*/
protected void addElement(IBuildStep step, int level) {
BuildQueueElement elem = new BuildQueueElement(step, level);
unsorted.add(elem);
queueHash.put(step, elem);
}
/**
* Dispatches the build queue and returns build status
*/
protected int dispatch(BuildProcessManager mgr) {
ActiveBuildStep[] active = new ActiveBuildStep[mgr.getMaxProcesses()];
for (int i = 0; i < active.length; i++) {
active[i] = null; // new ActiveBuildStep();
}
int activeCount = 0;
int maxLevel = 0;
int status = STATUS_OK;
String errorMsg = null;
// Going into "infinite" main loop
main_loop:
while (true) {
if (monitor.isCanceled()) {
status = STATUS_CANCELED;
errorMsg = CCorePlugin.getResourceString("CommandLauncher.error.commandCanceled"); //$NON-NLS-1$
break main_loop;
}
// Check build process states
ProcessLauncher launcher = mgr.queryStates();
if (launcher != null) {
// Build process has been canceled or failed to launch
if (launcher.queryState() == ProcessLauncher.STATE_CANCELED)
status = STATUS_CANCELED;
else
status = STATUS_INVALID;
errorMsg = launcher.getErrorMessage();
break main_loop;
}
// Everything goes OK.
boolean proceed = true;
// Check if there is room for new process
if (!mgr.hasEmpty()) {
proceed = false;
} else {
// Check "active steps" list for completed ones
for (int i = 0; i < active.length; i++) {
if (active[i] == null) continue;
ProcessLauncher pl = active[i].getLauncher();
if (pl == null) continue;
if (pl.queryState() == ProcessLauncher.STATE_DONE) {
// If process has terminated with error, break loop
// (except resumeOnErrors == true)
if (!resumeOnErrors && pl.getExitCode() != 0) {
status = STATUS_ERROR;
break main_loop;
}
// Try to launch next command for the current active step
if (active[i].isDone()) continue;
if (active[i].launchNextCmd(mgr)) {
// Command has been launched. Check if process pool is not maximized yet
if (!mgr.hasEmpty()) {
proceed = false;
break;
}
} else {
// Command has not been launched: step complete
refreshOutputs(active[i].getStep());
activeCount--;
monitor.worked(1);
}
}
}
}
// If nothing to do, then sleep and continue main loop
if (!proceed) {
try {
Thread.sleep(MAIN_LOOP_DELAY);
} catch (InterruptedException e) {
// do nothing
}
continue main_loop;
}
// Check if we need to schedule another process
if (queue.size() != 0 && activeCount < active.length) {
// Need to schedule another process
Iterator iter = queue.iterator();
// Iterate over build queue
while (iter.hasNext()) {
BuildQueueElement elem = (BuildQueueElement)iter.next();
// If "active steps" list is full, then break loop
if (activeCount == active.length)
break;
// If current element's level exceeds maximum level of currently built
// resources, then stop iteration (we can not build it anyway)
if (elem.getLevel() > maxLevel + 1)
break;
//Check if all prerequisites are built
IBuildResource[] res = elem.getStep().getInputResources();
boolean prereqBuilt = true;
for (int j = 0; j < res.length; j++) {
IBuildStep stp = res[j].getProducerStep(); // step which produces input for curr
boolean built = true;
if (stp != stp.getBuildDescription().getInputStep()) {
for (int k = 0; k < active.length; k++) {
if (active[k] != null && active[k].getStep().equals(stp) && !active[k].isDone()) {
built = false;
break;
}
}
}
if (!built) {
prereqBuilt = false;
break;
}
}
if (prereqBuilt) {
// All prereqs are built
IBuildStep step = elem.getStep();
// Remove element from the build queue and add it to the
// "active steps" list.
iter.remove();
for (int i = 0; i < active.length; i++) {
if (active[i] == null || active[i].isDone()) {
active[i] = new ActiveBuildStep(step);
if (active[i].launchNextCmd(mgr)) activeCount++;
break;
}
}
// Update maxLevel
if (elem.getLevel() > maxLevel)
maxLevel = elem.getLevel();
// We don't need to start new process immediately since
// it will be done on the next main loop iteration
}
}
}
// Now finally, check if we're done
if (activeCount <= 0 && queue.size() == 0)
break main_loop;
}
if (status != STATUS_OK && errorMsg != null)
printMessage(errorMsg, out);
return status;
}
/**
* Prints output to the console
*/
protected void printMessage(String msg, OutputStream out) {
if (out != null) {
msg = ManagedMakeMessages.getFormattedString(BUILDER_MSG_HEADER, msg) + LINE_SEPARATOR;
try {
out.write(msg.getBytes());
out.flush();
} catch (IOException e) {
// do nothing
}
}
}
/**
* Updates info about generated files (after step completed)
* @param step
*/
protected void refreshOutputs(IBuildStep step){
IProgressMonitor mon = new NullProgressMonitor();
IBuildResource outres[] = step.getOutputResources();
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
for(int i = 0; i < outres.length; i++){
IPath path = outres[i].getFullPath();
if(path != null){
try { root.getFile(path).refreshLocal(0, mon); }
catch (CoreException e) {}
}
}
}
}

View file

@ -0,0 +1,189 @@
/*******************************************************************************
* Copyright (c) 2006 Intel Corporation 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
*
* Contributors:
* Intel Corporation - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.managedbuilder.internal.buildmodel;
import java.io.*;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.internal.core.ProcessClosure;
import org.eclipse.cdt.utils.spawner.ProcessFactory;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
/**
* This class implements external process launching for internal builder.
*
* NOTE: This class is subject to change and discuss,
* and is currently available in experimental mode only
*/
public class ProcessLauncher {
public final static int STATE_DONE = 0;
public final static int STATE_RUNNING = 1;
public final static int STATE_CANCELED = 2;
public final static int STATE_ILLEGAL = -1;
protected String[] cmd;
protected String[] env;
protected File cwd;
protected OutputStream out;
protected OutputStream err;
protected IProgressMonitor monitor;
protected boolean show;
protected String error;
protected String lineSeparator;
protected Process process;
protected ProcessClosure closure = null;
protected int state;
/**
* Returns command line as a string array
*/
public String[] getCommandArray() {
return cmd;
}
/**
* Returns command line in a single string
*/
public String getCommandLine() {
StringBuffer buf = new StringBuffer();
if (cmd != null) {
for (int i = 0; i < cmd.length; i++) {
buf.append(cmd[i]);
buf.append(' ');
}
buf.append(lineSeparator);
}
return buf.toString();
}
/**
* Returns process environment
*/
public String[] getEnvironment() {
return env;
}
/**
* Returns command working directory
*/
public File getWorkingDir() {
return cwd;
}
/**
* Returns error message (if any)
*/
public String getErrorMessage() {
return error;
}
/**
* Returns exit code of a process
*/
public int getExitCode() {
if (process == null || closure.isAlive()) return 0;
try { return process.waitFor(); }
catch (InterruptedException e) { return 0; }
}
/**
* Initializes launcher
* @param _cmd Command path
* @param args Command arguments
* @param _env Environment
* @param _cwd Working directory
* @param _out Output stream
* @param _err Error output stream
* @param _monitor Progress monitor
* @param _show If true, print command line before launching
*/
public ProcessLauncher(IPath _cmd, String[] args, String[] _env, IPath _cwd, OutputStream _out, OutputStream _err, IProgressMonitor _monitor, boolean _show) {
cmd = createCmdArray(_cmd.toOSString(), args);
env = _env;
cwd = _cwd.toFile();
out = _out;
err = _err;
monitor = _monitor;
show = _show;
error = ""; //$NON-NLS-1$
lineSeparator = System.getProperty("line.separator", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
}
/**
* Launches a process
*/
public void launch() {
try {
if (show)
printCommandLine();
state = STATE_RUNNING;
process = ProcessFactory.getFactory().exec(cmd, env, cwd);
closure = new ProcessClosure(process, out, err);
// Close the input of the process since we will never write to it
try {
process.getOutputStream().close();
} catch (IOException e) {
// do nothing
}
closure.runNonBlocking();
} catch (IOException e) {
error = e.getMessage();
closure = null;
}
}
/**
* Returns process state
*/
public int queryState() {
if (state == STATE_RUNNING) {
if (closure == null)
state = STATE_ILLEGAL;
else if (monitor.isCanceled()) {
closure.terminate();
error = CCorePlugin.getResourceString("CommandLauncher.error.commandCanceled"); //$NON-NLS-1$
state = STATE_CANCELED;
} else if (!closure.isRunning()) {
state = STATE_DONE;
}
}
return state;
}
/**
* Creates a string array representing the command that will be passed
* to the process
*/
protected String[] createCmdArray(String cmdPath, String[] cmdArgs) {
String[] args = new String[1 + cmdArgs.length];
args[0] = cmdPath;
System.arraycopy(cmdArgs, 0, args, 1, cmdArgs.length);
return args;
}
/**
* Prints command line
*/
protected void printCommandLine() {
if (out != null) {
try {
out.write(getCommandLine().getBytes());
out.flush();
} catch (IOException e) {
// do nothing
}
}
}
}

View file

@ -37,6 +37,7 @@ import org.eclipse.cdt.managedbuilder.core.IToolChain;
import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager;
import org.eclipse.cdt.managedbuilder.core.ManagedBuilderCorePlugin;
import org.eclipse.cdt.managedbuilder.envvar.IConfigurationEnvironmentVariableSupplier;
import org.eclipse.cdt.managedbuilder.internal.buildmodel.BuildProcessManager;
import org.eclipse.cdt.managedbuilder.internal.envvar.EnvironmentVariableProvider;
import org.eclipse.cdt.managedbuilder.internal.envvar.UserDefinedEnvironmentSupplier;
import org.eclipse.cdt.managedbuilder.internal.macros.BuildMacroProvider;
@ -113,15 +114,27 @@ public class Configuration extends BuildObject implements IConfiguration {
//as a special Builder object of the tool-chain and implement the internal
//builder enabling/disabling as the Builder substitution functionality
//
private static final String INTERNAL_BUILDER = "internalBuilder";
private static final String INTERNAL_BUILDER = "internalBuilder"; //$NON-NLS-1$
//preference key that holds the Internal Builder enable state
private static final String INTERNAL_BUILDER_ENABLED = "enabled"; //$NON-NLS-1$
//preference key that holds the internal builder mode
private static final String INTERNAL_BUILDER_IGNORE_ERR = "ignoreErr"; //$NON-NLS-1$
//preference key that holds the internal builder mode
private static final String INTERNAL_BUILDER_PARALLEL = "parallel"; //$NON-NLS-1$
//preference key that holds the internal builder mode
private static final String INTERNAL_BUILDER_PARALLEL_DEF = "paralleldef"; //$NON-NLS-1$
//preference key that holds the internal builder mode
private static final String INTERNAL_BUILDER_PARALLELNUMBER = "parallelnumber"; //$NON-NLS-1$
//Internal Builder enable state
private boolean internalBuilderEnabled;
//Internal Builder mode
private boolean internalBuilderIgnoreErr = true;
//Internal Builder parallel mode
private boolean internalBuilderParallel = true;
//Internal Builder parallel mode - default jobs #
private boolean internalBuilderParallelDef = true;
//Number of parallel threads
private int internalBuilderParallelNumber = 1; // default value
/*
@ -335,6 +348,9 @@ public class Configuration extends BuildObject implements IConfiguration {
enableInternalBuilder(cloneConfig.isInternalBuilderEnabled());
setInternalBuilderIgnoreErr(cloneConfig.getInternalBuilderIgnoreErr());
setInternalBuilderParallel(cloneConfig.getInternalBuilderParallel());
setParallelDef(cloneConfig.getParallelDef());
setParallelNumber(cloneConfig.getParallelNumber());
// internalBuilderEnabled = cloneConfig.internalBuilderEnabled;
// internalBuilderIgnoreErr = cloneConfig.internalBuilderIgnoreErr;
@ -1741,6 +1757,22 @@ public class Configuration extends BuildObject implements IConfiguration {
*
*/
public void setInternalBuilderBoolean(boolean value, String pref) {
Preferences prefs = getPreferences(INTERNAL_BUILDER);
if(prefs != null){
prefs.putBoolean(pref, value);
try {
prefs.flush();
} catch (BackingStoreException e) {}
}
}
public boolean getInternalBuilderBoolean(String pref, boolean defaultValue) {
Preferences prefs = getPreferences(INTERNAL_BUILDER);
return prefs != null ?
prefs.getBoolean(pref, false) : defaultValue;
}
/*
* this method is used for enabling/disabling the internal builder
* for the given configuration
@ -1750,28 +1782,16 @@ public class Configuration extends BuildObject implements IConfiguration {
public void enableInternalBuilder(boolean enable){
// if(internalBuilderEnabled != enable){
internalBuilderEnabled = enable;
Preferences prefs = getPreferences(INTERNAL_BUILDER);
if(prefs != null){
prefs.putBoolean(INTERNAL_BUILDER_ENABLED, enable);
try {
prefs.flush();
} catch (BackingStoreException e) {
}
}
setInternalBuilderBoolean(enable, INTERNAL_BUILDER_ENABLED);
// }
}
/*
* returns whether the internal builder is enabled
*
* @return boolean
*/
public boolean isInternalBuilderEnabled(){
Preferences prefs = getPreferences(INTERNAL_BUILDER);
return prefs != null ?
prefs.getBoolean(INTERNAL_BUILDER_ENABLED, false) :
internalBuilderEnabled;
return getInternalBuilderBoolean(INTERNAL_BUILDER_ENABLED, internalBuilderEnabled);
}
/*
@ -1785,14 +1805,7 @@ public class Configuration extends BuildObject implements IConfiguration {
public void setInternalBuilderIgnoreErr(boolean ignore){
// if(internalBuilderIgnoreErr != ignore){
internalBuilderIgnoreErr = ignore;
Preferences prefs = getPreferences(INTERNAL_BUILDER);
if(prefs != null){
prefs.putBoolean(INTERNAL_BUILDER_IGNORE_ERR, ignore);
try {
prefs.flush();
} catch (BackingStoreException e) {
}
}
setInternalBuilderBoolean(ignore, INTERNAL_BUILDER_IGNORE_ERR);
// }
}
@ -1804,11 +1817,74 @@ public class Configuration extends BuildObject implements IConfiguration {
* @return boolean
*/
public boolean getInternalBuilderIgnoreErr(){
return getInternalBuilderBoolean(INTERNAL_BUILDER_IGNORE_ERR, internalBuilderIgnoreErr);
}
/**
*
* sets the Internal Builder Parallel mode
*
* @param parallel if true, internal builder will use parallel mode
*/
public void setInternalBuilderParallel(boolean parallel){
internalBuilderParallel = parallel;
setInternalBuilderBoolean(parallel, INTERNAL_BUILDER_PARALLEL);
}
/**
* returns the Internal Builder parallel mode
* if true, internal builder will work in parallel mode
* otherwise it will use only one thread
*
* @return boolean
*/
public boolean getInternalBuilderParallel(){
return getInternalBuilderBoolean(INTERNAL_BUILDER_PARALLEL, internalBuilderParallel);
}
/**
* @param parallel if true, internal builder will use parallel mode
*/
public void setParallelDef(boolean parallel_def){
internalBuilderParallelDef = parallel_def;
setInternalBuilderBoolean(parallel_def, INTERNAL_BUILDER_PARALLEL_DEF);
}
/**
* @return boolean
*/
public boolean getParallelDef(){
return getInternalBuilderBoolean(INTERNAL_BUILDER_PARALLEL_DEF, internalBuilderParallelDef);
}
/**
*
* sets number of Parallel threads
*
* @param int
*/
public void setParallelNumber(int n){
internalBuilderParallelNumber = n;
Preferences prefs = getPreferences(INTERNAL_BUILDER);
return prefs != null ?
prefs.getBoolean(INTERNAL_BUILDER_IGNORE_ERR, true) :
internalBuilderIgnoreErr;
if(prefs != null){
prefs.putInt(INTERNAL_BUILDER_PARALLELNUMBER, n);
try { prefs.flush(); }
catch (BackingStoreException e) {}
}
}
/**
* returns number of Parallel threads
*
* @return int
*/
public int getParallelNumber(){
Preferences prefs = getPreferences(INTERNAL_BUILDER);
int cpus = BuildProcessManager.checkCPUNumber();
int x = prefs != null ?
prefs.getInt(INTERNAL_BUILDER_PARALLELNUMBER, cpus) :
internalBuilderParallelNumber;
return (x > 0) ? x : 1;
}
private Preferences getPreferences(String name){

View file

@ -40,6 +40,7 @@ import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager;
import org.eclipse.cdt.managedbuilder.envvar.IBuildEnvironmentVariable;
import org.eclipse.cdt.managedbuilder.internal.buildmodel.DescriptionBuilder;
import org.eclipse.cdt.managedbuilder.internal.buildmodel.IBuildModelBuilder;
import org.eclipse.cdt.managedbuilder.internal.buildmodel.ParallelBuilder;
import org.eclipse.cdt.managedbuilder.internal.buildmodel.StepBuilder;
import org.eclipse.cdt.managedbuilder.macros.BuildMacroException;
import org.eclipse.cdt.managedbuilder.macros.IBuildMacroProvider;
@ -1186,6 +1187,9 @@ public class GeneratedMakefileBuilder extends ACBuilder {
boolean buildIncrementaly,
boolean resumeOnErr,
IProgressMonitor monitor) {
boolean isParallel = ((Configuration)cfg).getInternalBuilderParallel();
// Get the project and make sure there's a monitor to cancel the build
IProject currentProject = cfg.getOwner().getProject();
if (monitor == null) {
@ -1196,9 +1200,6 @@ public class GeneratedMakefileBuilder extends ACBuilder {
msgs[0] = ManagedMakeMessages.getResourceString(INTERNAL_BUILDER);
msgs[1] = currentProject.getName();
monitor.beginTask("", 1000); //$NON-NLS-1$
monitor.subTask(ManagedMakeMessages.getFormattedString(MAKE, msgs));
ConsoleOutputStream consoleOutStream = null;
IConsole console = null;
OutputStream epmOutputStream = null;
@ -1212,8 +1213,10 @@ public class GeneratedMakefileBuilder extends ACBuilder {
}
IBuildDescription des = BuildDescriptionManager.createBuildDescription(cfg, delta, flags);
DescriptionBuilder builder = new DescriptionBuilder(des, buildIncrementaly, resumeOnErr);
DescriptionBuilder builder = null;
if (!isParallel)
builder = new DescriptionBuilder(des, buildIncrementaly, resumeOnErr);
// Get a build console for the project
StringBuffer buf = new StringBuffer();
@ -1244,7 +1247,7 @@ public class GeneratedMakefileBuilder extends ACBuilder {
consoleOutStream.write(buf.toString().getBytes());
consoleOutStream.flush();
if(builder.getNumCommands() > 0) {
if(isParallel || builder.getNumCommands() > 0) {
// Remove all markers for this project
removeAllMarkers(currentProject);
@ -1256,8 +1259,15 @@ public class GeneratedMakefileBuilder extends ACBuilder {
// until we explicitly close it. See bug#123302.
epmOutputStream = epm.getOutputStream();
int status = builder.build(epmOutputStream, epmOutputStream, new SubProgressMonitor(monitor, 1000));
int status = 0;
long t1 = System.currentTimeMillis();
if (isParallel)
status = ParallelBuilder.build(des, null, null, epmOutputStream, epmOutputStream, monitor, resumeOnErr, buildIncrementaly);
else
status = builder.build(epmOutputStream, epmOutputStream, monitor);
long t2 = System.currentTimeMillis();
// Report either the success or failure of our mission
buf = new StringBuffer();
@ -1284,7 +1294,16 @@ public class GeneratedMakefileBuilder extends ACBuilder {
break;
}
buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$//$NON-NLS-2$
// Report time and number of threads used
buf.append("Time consumed: ");
buf.append(t2 - t1);
buf.append(" ms. ");
if (isParallel) {
buf.append("Parallel threads used: ");
buf.append(ParallelBuilder.lastThreadsUsed);
}
buf.append("\n");
// Write message on the console
consoleOutStream.write(buf.toString().getBytes());
consoleOutStream.flush();

View file

@ -17,6 +17,7 @@ import java.util.regex.Pattern;
import org.eclipse.cdt.managedbuilder.core.IBuilder;
import org.eclipse.cdt.managedbuilder.core.IConfiguration;
import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager;
import org.eclipse.cdt.managedbuilder.internal.buildmodel.BuildProcessManager;
import org.eclipse.cdt.managedbuilder.internal.core.Configuration;
import org.eclipse.cdt.managedbuilder.internal.macros.BuildMacroProvider;
import org.eclipse.cdt.managedbuilder.ui.properties.BuildPropertyPage;
@ -37,9 +38,11 @@ import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Spinner;
import org.eclipse.swt.widgets.Text;
public class BuildSettingsBlock extends AbstractCOptionPage {
@ -60,6 +63,11 @@ public class BuildSettingsBlock extends AbstractCOptionPage {
private static final String INTERNAL_BUILDER_GROUP = LABEL + ".internal.builder.group"; //$NON-NLS-1$
private static final String INTERNAL_BUILDER_ENABLE_BTN = LABEL + ".internal.builder.enable"; //$NON-NLS-1$
private static final String INTERNAL_BUILDER_IGNORE_ERR_BTN = LABEL + ".internal.builder.ignore.err"; //$NON-NLS-1$
private static final String INTERNAL_BUILDER_PARALLEL = LABEL + ".internal.builder.parallel.head"; //$NON-NLS-1$
private static final String INTERNAL_BUILDER_PARALLEL_BTN = LABEL + ".internal.builder.parallel.use"; //$NON-NLS-1$
private static final String INTERNAL_BUILDER_PARALLEL_DEF = LABEL + ".internal.builder.parallel.default"; //$NON-NLS-1$
private static final String INTERNAL_BUILDER_PARALLEL_NUM = LABEL + ".internal.builder.parallel.number"; //$NON-NLS-1$
private static final String INTERNAL_BUILDER_PARALLEL_TOOLTIP = LABEL + ".internal.builder.parallel.tooltip"; //$NON-NLS-1$
private static final String INTERNAL_BUILDER_EXPERIMENTAL_NOTE = LABEL + ".internal.builder.experimental.note"; //$NON-NLS-1$
private static final String EMPTY_STRING = new String();
@ -77,6 +85,11 @@ public class BuildSettingsBlock extends AbstractCOptionPage {
protected Group internalBuilderGroup;
protected Button internalBuilderEnable;
protected Button internalBuilderIgnoreErr;
protected Button internalBuilderParallel;
protected Button internalBuilderParallelDef1;
protected Button internalBuilderParallelDef2;
protected Spinner parallelProcesses;
protected final int cpuNumber = BuildProcessManager.checkCPUNumber();
/*
* Bookeeping variables
@ -296,18 +309,17 @@ public class BuildSettingsBlock extends AbstractCOptionPage {
internalBuilderGroup = new Group(parent, SWT.NONE);
internalBuilderGroup.setFont(parent.getFont());
internalBuilderGroup.setText(ManagedBuilderUIMessages.getResourceString(INTERNAL_BUILDER_GROUP));
internalBuilderGroup.setLayout(new GridLayout(1, true));
internalBuilderGroup.setLayout(new GridLayout(2, false));
internalBuilderGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
Label dotLabel = new Label(internalBuilderGroup, SWT.CENTER);
dotLabel.setFont(internalBuilderGroup.getFont());
dotLabel.setText(ManagedBuilderUIMessages.getResourceString(INTERNAL_BUILDER_EXPERIMENTAL_NOTE));
internalBuilderEnable = new Button(internalBuilderGroup, SWT.CHECK | SWT.LEFT);
internalBuilderEnable.setFont(internalBuilderGroup.getFont());
internalBuilderEnable.setText(ManagedBuilderUIMessages.getResourceString(INTERNAL_BUILDER_ENABLE_BTN));
internalBuilderEnable.setBackground(internalBuilderGroup.getBackground());
internalBuilderEnable.setForeground(internalBuilderGroup.getForeground());
GridData gd = new GridData(GridData.FILL_HORIZONTAL);
gd.horizontalSpan = 2;
dotLabel.setLayoutData(gd);
internalBuilderEnable = createInternalBuilderButton(ManagedBuilderUIMessages.getResourceString(INTERNAL_BUILDER_ENABLE_BTN), 2);
internalBuilderEnable.addSelectionListener(new SelectionAdapter () {
public void widgetSelected(SelectionEvent e) {
Configuration config = (Configuration)BuildSettingsBlock.this.parent.getSelectedConfigurationClone();
@ -322,11 +334,7 @@ public class BuildSettingsBlock extends AbstractCOptionPage {
}
});
internalBuilderIgnoreErr = new Button(internalBuilderGroup, SWT.CHECK | SWT.LEFT);
internalBuilderIgnoreErr.setFont(internalBuilderGroup.getFont());
internalBuilderIgnoreErr.setText(ManagedBuilderUIMessages.getResourceString(INTERNAL_BUILDER_IGNORE_ERR_BTN));
internalBuilderIgnoreErr.setBackground(internalBuilderGroup.getBackground());
internalBuilderIgnoreErr.setForeground(internalBuilderGroup.getForeground());
internalBuilderIgnoreErr = createInternalBuilderButton(ManagedBuilderUIMessages.getResourceString(INTERNAL_BUILDER_IGNORE_ERR_BTN), 2);
internalBuilderIgnoreErr.addSelectionListener(new SelectionAdapter () {
public void widgetSelected(SelectionEvent e) {
Configuration config = (Configuration)BuildSettingsBlock.this.parent.getSelectedConfigurationClone();
@ -341,8 +349,108 @@ public class BuildSettingsBlock extends AbstractCOptionPage {
}
});
Label parallelLabel = new Label(internalBuilderGroup, SWT.CENTER);
parallelLabel.setFont(internalBuilderGroup.getFont());
parallelLabel.setBackground(internalBuilderGroup.getBackground());
parallelLabel.setForeground(internalBuilderGroup.getForeground());
parallelLabel.setText(ManagedBuilderUIMessages.getResourceString(INTERNAL_BUILDER_PARALLEL));
gd = new GridData(GridData.FILL_HORIZONTAL);
gd.horizontalSpan = 2;
parallelLabel.setLayoutData(gd);
internalBuilderParallel = createInternalBuilderButton(ManagedBuilderUIMessages.getResourceString(INTERNAL_BUILDER_PARALLEL_BTN), 2);
internalBuilderParallel.addSelectionListener(new SelectionAdapter () {
public void widgetSelected(SelectionEvent e) {
Configuration config = (Configuration)BuildSettingsBlock.this.parent.getSelectedConfigurationClone();
config.setInternalBuilderParallel(internalBuilderParallel.getSelection());
setValues();
setDirty(true);
}
});
internalBuilderParallel.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent event) { internalBuilderParallel = null; }
});
internalBuilderParallelDef1 = new Button(internalBuilderGroup, SWT.RADIO);
internalBuilderParallelDef1.setFont(internalBuilderGroup.getFont());
internalBuilderParallelDef1.setBackground(internalBuilderGroup.getBackground());
internalBuilderParallelDef1.setForeground(internalBuilderGroup.getForeground());
internalBuilderParallelDef1.setText("Use optimal jobs number");
gd = new GridData(GridData.FILL_HORIZONTAL);
gd.horizontalSpan = 2;
gd.horizontalIndent = 15;
internalBuilderParallelDef1.setLayoutData(gd);
internalBuilderParallelDef1.setSelection(true);
internalBuilderParallelDef1.addSelectionListener(new SelectionAdapter () {
public void widgetSelected(SelectionEvent e) {
Configuration config = (Configuration)BuildSettingsBlock.this.parent.getSelectedConfigurationClone();
config.setParallelDef(internalBuilderParallelDef1.getSelection());
setValues();
setDirty(true);
}
});
internalBuilderParallelDef1.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent event) { internalBuilderParallelDef1 = null; }
});
internalBuilderParallelDef2 = new Button(internalBuilderGroup, SWT.RADIO);
internalBuilderParallelDef2.setFont(internalBuilderGroup.getFont());
internalBuilderParallelDef2.setBackground(internalBuilderGroup.getBackground());
internalBuilderParallelDef2.setForeground(internalBuilderGroup.getForeground());
internalBuilderParallelDef2.setText("Use parallel jobs :");
gd = new GridData(GridData.FILL_HORIZONTAL);
gd.horizontalIndent = 15;
internalBuilderParallelDef2.setLayoutData(gd);
internalBuilderParallelDef2.addSelectionListener(new SelectionAdapter () {
public void widgetSelected(SelectionEvent e) {
Configuration config = (Configuration)BuildSettingsBlock.this.parent.getSelectedConfigurationClone();
config.setParallelDef(!internalBuilderParallelDef2.getSelection());
setValues();
setDirty(true);
}
});
internalBuilderParallelDef2.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent event) { internalBuilderParallelDef2 = null; }
});
parallelProcesses = new Spinner(internalBuilderGroup, SWT.BORDER);
parallelProcesses.setFont(internalBuilderGroup.getFont());
parallelProcesses.setBackground(internalBuilderGroup.getBackground());
parallelProcesses.setForeground(internalBuilderGroup.getForeground());
parallelProcesses.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
parallelProcesses.setValues(cpuNumber, 1, 10000, 0, 1, 10);
parallelProcesses.setToolTipText(ManagedBuilderUIMessages.getResourceString(INTERNAL_BUILDER_PARALLEL_TOOLTIP));
parallelProcesses.addSelectionListener(new SelectionAdapter () {
public void widgetSelected(SelectionEvent e) {
Configuration config = (Configuration)BuildSettingsBlock.this.parent.getSelectedConfigurationClone();
config.setParallelNumber(parallelProcesses.getSelection());
setValues();
setDirty(true);
}
});
parallelProcesses.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent event) { parallelProcesses = null; }
});
}
private Button createInternalBuilderButton(String s, int hSpan) {
Button b = new Button(internalBuilderGroup, SWT.CHECK | SWT.LEFT);
b.setFont(internalBuilderGroup.getFont());
b.setText(s);
b.setBackground(internalBuilderGroup.getBackground());
b.setForeground(internalBuilderGroup.getForeground());
GridData gd = new GridData(GridData.FILL_HORIZONTAL);
gd.horizontalSpan = hSpan;
b.setLayoutData(gd);
return b;
}
protected void initializeValues() {
setValues();
setDirty(false);
@ -380,6 +488,10 @@ public class BuildSettingsBlock extends AbstractCOptionPage {
boolean internalBuilderOn = config.isInternalBuilderEnabled();
internalBuilderEnable.setSelection(internalBuilderOn);
internalBuilderIgnoreErr.setSelection(config.getInternalBuilderIgnoreErr());
internalBuilderParallel.setSelection(config.getInternalBuilderParallel());
internalBuilderParallelDef1.setSelection(config.getParallelDef());
internalBuilderParallelDef2.setSelection(!config.getParallelDef());
parallelProcesses.setSelection(config.getParallelNumber());
makeCommandDefault.setEnabled(!internalBuilderOn);
makeCommandEntry.setEnabled(!internalBuilderOn);
@ -387,6 +499,10 @@ public class BuildSettingsBlock extends AbstractCOptionPage {
buildMacrosExpand.setEnabled(!internalBuilderOn);
buildMacrosExpandGroup.setEnabled(!internalBuilderOn);
internalBuilderIgnoreErr.setEnabled(internalBuilderOn);
internalBuilderParallel.setEnabled(internalBuilderOn);
internalBuilderParallelDef1.setEnabled(internalBuilderOn && config.getInternalBuilderParallel());
internalBuilderParallelDef2.setEnabled(internalBuilderOn && config.getInternalBuilderParallel());
parallelProcesses.setEnabled(internalBuilderOn && config.getInternalBuilderParallel() && !config.getParallelDef());
// setDirty(false);
}
@ -456,6 +572,9 @@ public class BuildSettingsBlock extends AbstractCOptionPage {
selectedConfiguration.enableInternalBuilder(cloneConfig.isInternalBuilderEnabled());
selectedConfiguration.setInternalBuilderIgnoreErr(cloneConfig.getInternalBuilderIgnoreErr());
selectedConfiguration.setInternalBuilderParallel(cloneConfig.getInternalBuilderParallel());
selectedConfiguration.setParallelDef(cloneConfig.getParallelDef());
selectedConfiguration.setParallelNumber(cloneConfig.getParallelNumber());
setDirty(false);
}

View file

@ -113,7 +113,11 @@ BuildSettingsBlock.label.internal.builder.group=Internal Builder
BuildSettingsBlock.label.internal.builder.enable=Enable Internal Builder
BuildSettingsBlock.label.internal.builder.ignore.err=Ignore build errors
BuildSettingsBlock.label.internal.builder.experimental.note=NOTE: This is experimental functionality
BuildSettingsBlock.label.internal.builder.parallel.head=Parallel build
BuildSettingsBlock.label.internal.builder.parallel.use=Enable parallel build
BuildSettingsBlock.label.internal.builder.parallel.default=Use optimal jobs number
BuildSettingsBlock.label.internal.builder.parallel.number=Use parallel jobs:
BuildSettingsBlock.label.internal.builder.parallel.tooltip=Select number of jobs really used for build
# ----------- Build Steps Block -----------
BuildStepSettingsBlock.label.Settings=Build Steps
@ -234,6 +238,15 @@ ResourceBuildPropertyPage.config.notselected=No configurations selected
ResourceBuildPropertyPage.rc.non.build=Managed Build settings for this resource are not available
ResourceBuildPropertyPage.rc.generated=The selected resource is created by the buildfile generator
# ----------- Languages Page
CLanguagesPropertyPage.label.ActiveResource=Active Resource configuration
CLanguagesPropertyPage.label.ResourceSettings=Known languages
CLanguagesPropertyPage.label.ApplyToAllCheckBox=Apply changes to all configurations
CLanguagesPropertyPage.label.Configuration=Configuration:
CLanguagesPropertyPage.tip.applytoallcheck=Apply changes to all funcking configurations
# ----------- Resource Custom Build Step Block -----------
ResourceCustomBuildStepBlock.label.settings=Custom Build Steps
ResourceCustomBuildStepBlock.label.tool.group=Resource Custom Build Step