blob: 77c646079e974dc7571fc7054005d7e186d24d5e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2003, 2016 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Rational Software - Initial API and implementation
* ARM Ltd. - Minor changes to echo commands
* IBM Corporation
* Anna Dushistova (Mentor Graphics) - [307244] extend visibility of fields in GnuMakefileGenerator
* James Blackburn (Broadcom Corp.)
* Marc-Andre Laperle
* Liviu Ionescu - [322168]
* Dorothea Pilz-Roeder (Advantest Europe GmbH) - [180451]
*******************************************************************************/
package org.eclipse.cdt.managedbuilder.makegen.gnu;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.settings.model.CSourceEntry;
import org.eclipse.cdt.core.settings.model.ICConfigurationDescription;
import org.eclipse.cdt.core.settings.model.ICSettingEntry;
import org.eclipse.cdt.core.settings.model.ICSourceEntry;
import org.eclipse.cdt.core.settings.model.util.CDataUtil;
import org.eclipse.cdt.core.settings.model.util.IPathSettingsContainerVisitor;
import org.eclipse.cdt.core.settings.model.util.PathSettingsContainer;
import org.eclipse.cdt.managedbuilder.core.BuildException;
import org.eclipse.cdt.managedbuilder.core.IBuildObject;
import org.eclipse.cdt.managedbuilder.core.IBuilder;
import org.eclipse.cdt.managedbuilder.core.IConfiguration;
import org.eclipse.cdt.managedbuilder.core.IFileInfo;
import org.eclipse.cdt.managedbuilder.core.IFolderInfo;
import org.eclipse.cdt.managedbuilder.core.IInputType;
import org.eclipse.cdt.managedbuilder.core.IManagedBuildInfo;
import org.eclipse.cdt.managedbuilder.core.IManagedCommandLineGenerator;
import org.eclipse.cdt.managedbuilder.core.IManagedCommandLineInfo;
import org.eclipse.cdt.managedbuilder.core.IManagedOutputNameProvider;
import org.eclipse.cdt.managedbuilder.core.IOption;
import org.eclipse.cdt.managedbuilder.core.IOutputType;
import org.eclipse.cdt.managedbuilder.core.IResourceInfo;
import org.eclipse.cdt.managedbuilder.core.ITool;
import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager;
import org.eclipse.cdt.managedbuilder.core.ManagedBuilderCorePlugin;
import org.eclipse.cdt.managedbuilder.internal.core.ManagedMakeMessages;
import org.eclipse.cdt.managedbuilder.internal.core.Tool;
import org.eclipse.cdt.managedbuilder.internal.macros.BuildMacroProvider;
import org.eclipse.cdt.managedbuilder.internal.macros.FileContextData;
import org.eclipse.cdt.managedbuilder.macros.BuildMacroException;
import org.eclipse.cdt.managedbuilder.macros.IBuildMacroProvider;
import org.eclipse.cdt.managedbuilder.makegen.IManagedBuilderMakefileGenerator;
import org.eclipse.cdt.managedbuilder.makegen.IManagedBuilderMakefileGenerator2;
import org.eclipse.cdt.managedbuilder.makegen.IManagedDependencyCalculator;
import org.eclipse.cdt.managedbuilder.makegen.IManagedDependencyCommands;
import org.eclipse.cdt.managedbuilder.makegen.IManagedDependencyGenerator;
import org.eclipse.cdt.managedbuilder.makegen.IManagedDependencyGenerator2;
import org.eclipse.cdt.managedbuilder.makegen.IManagedDependencyGeneratorType;
import org.eclipse.cdt.managedbuilder.makegen.IManagedDependencyInfo;
import org.eclipse.cdt.managedbuilder.makegen.IManagedDependencyPreBuild;
import org.eclipse.cdt.utils.EFSExtensionManager;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.IResourceProxy;
import org.eclipse.core.resources.IResourceProxyVisitor;
import org.eclipse.core.resources.IResourceStatus;
import org.eclipse.core.resources.IWorkspaceRoot;
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.MultiStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
/**
* This is a specialized makefile generator that takes advantage of the
* extensions present in Gnu Make.
*
* @since 1.2
* @noinstantiate This class is not intended to be instantiated by clients.
*/
public class GnuMakefileGenerator implements IManagedBuilderMakefileGenerator2 {
private static final IPath DOT_SLASH_PATH = new Path("./"); //$NON-NLS-1$
private Pattern doubleQuotedOption = Pattern.compile("--?[a-zA-Z]+.*?\\\".*?\\\".*"); //$NON-NLS-1$
private Pattern singleQuotedOption = Pattern.compile("--?[a-zA-Z]+.*?'.*?'.*"); //$NON-NLS-1$
/**
* This class walks the delta supplied by the build system to determine
* what resources have been changed. The logic is very simple. If a
* buildable resource (non-header) has been added or removed, the directories
* in which they are located are "dirty" so the makefile fragments for them
* have to be regenerated.
* <p>
* The actual dependencies are recalculated as a result of the build step
* itself. We are relying on make to do the right things when confronted
* with a dependency on a moved header file. That said, make will treat
* the missing header file in a dependency rule as a target it has to build
* unless told otherwise. These dummy targets are added to the makefile
* to avoid a missing target error.
*/
public class ResourceDeltaVisitor implements IResourceDeltaVisitor {
private final GnuMakefileGenerator generator;
// private IManagedBuildInfo info;
private final IConfiguration config;
/**
* The constructor
*/
public ResourceDeltaVisitor(GnuMakefileGenerator generator, IManagedBuildInfo info) {
this.generator = generator;
this.config = info.getDefaultConfiguration();
}
public ResourceDeltaVisitor(GnuMakefileGenerator generator, IConfiguration cfg) {
this.generator = generator;
this.config = cfg;
}
/* (non-Javadoc)
* @see org.eclipse.core.resources.IResourceDeltaVisitor#visit(org.eclipse.core.resources.IResourceDelta)
*/
@Override
public boolean visit(IResourceDelta delta) throws CoreException {
// Should the visitor keep iterating in current directory
boolean keepLooking = false;
IResource resource = delta.getResource();
IResourceInfo rcInfo = config.getResourceInfo(resource.getProjectRelativePath(), false);
IFolderInfo fo = null;
boolean isSource = isSource(resource.getProjectRelativePath());
if (rcInfo instanceof IFolderInfo) {
fo = (IFolderInfo) rcInfo;
}
// What kind of resource change has occurred
if (/*!rcInfo.isExcluded() && */isSource) {
if (resource.getType() == IResource.FILE) {
String ext = resource.getFileExtension();
switch (delta.getKind()) {
case IResourceDelta.ADDED:
if (!generator.isGeneratedResource(resource)) {
// This is a source file so just add its container
if (fo == null || fo.buildsFileType(ext)) {
generator.appendModifiedSubdirectory(resource);
}
}
break;
case IResourceDelta.REMOVED:
// we get this notification if a resource is moved too
if (!generator.isGeneratedResource(resource)) {
// This is a source file so just add its container
if (fo == null || fo.buildsFileType(ext)) {
generator.appendDeletedFile(resource);
generator.appendModifiedSubdirectory(resource);
}
}
break;
default:
keepLooking = true;
break;
}
}
if (resource.getType() == IResource.FOLDER) {
// I only care about delete event
switch (delta.getKind()) {
case IResourceDelta.REMOVED:
if (!generator.isGeneratedResource(resource)) {
generator.appendDeletedSubdirectory((IContainer) resource);
}
break;
}
}
}
if (resource.getType() == IResource.PROJECT) {
// If there is a zero-length delta, something the project depends on has changed so just call make
IResourceDelta[] children = delta.getAffectedChildren();
if (children != null && children.length > 0) {
keepLooking = true;
}
} else {
// If the resource is part of the generated directory structure don't recurse
if (resource.getType() == IResource.ROOT || (isSource && !generator.isGeneratedResource(resource))) {
keepLooking = true;
}
}
return keepLooking;
}
}
/**
* This class is used to recursively walk the project and determine which
* modules contribute buildable source files.
*/
protected class ResourceProxyVisitor implements IResourceProxyVisitor {
private final GnuMakefileGenerator generator;
private final IConfiguration config;
// private IManagedBuildInfo info;
/**
* Constructs a new resource proxy visitor to quickly visit project
* resources.
*/
public ResourceProxyVisitor(GnuMakefileGenerator generator, IManagedBuildInfo info) {
this.generator = generator;
this.config = info.getDefaultConfiguration();
}
public ResourceProxyVisitor(GnuMakefileGenerator generator, IConfiguration cfg) {
this.generator = generator;
this.config = cfg;
}
/* (non-Javadoc)
* @see org.eclipse.core.resources.IResourceProxyVisitor#visit(org.eclipse.core.resources.IResourceProxy)
*/
@Override
public boolean visit(IResourceProxy proxy) throws CoreException {
// No point in proceeding, is there
if (generator == null) {
return false;
}
IResource resource = proxy.requestResource();
boolean isSource = isSource(resource.getProjectRelativePath());
// Is this a resource we should even consider
if (proxy.getType() == IResource.FILE) {
// If this resource has a Resource Configuration and is not excluded or
// if it has a file extension that one of the tools builds, add the sudirectory to the list
// boolean willBuild = false;
IResourceInfo rcInfo = config.getResourceInfo(resource.getProjectRelativePath(), false);
if (isSource/* && !rcInfo.isExcluded()*/) {
boolean willBuild = false;
if (rcInfo instanceof IFolderInfo) {
String ext = resource.getFileExtension();
if (((IFolderInfo) rcInfo).buildsFileType(ext) &&
// If this file resource is a generated resource, then it is uninteresting
!generator.isGeneratedResource(resource)) {
willBuild = true;
}
} else {
willBuild = true;
}
if (willBuild)
generator.appendBuildSubdirectory(resource);
}
// if (willBuild) {
// if ((resConfig == null) || (!(resConfig.isExcluded()))) {
// generator.appendBuildSubdirectory(resource);
// }
// }
return false;
} else if (proxy.getType() == IResource.FOLDER) {
if (!isSource || generator.isGeneratedResource(resource))
return false;
return true;
}
// Recurse into subdirectories
return true;
}
}
// String constants for makefile contents and messages
private static final String COMMENT = "MakefileGenerator.comment"; //$NON-NLS-1$
//private static final String AUTO_DEP = COMMENT + ".autodeps"; //$NON-NLS-1$
//private static final String MESSAGE = "ManagedMakeBuilder.message"; //$NON-NLS-1$
//private static final String BUILD_ERROR = MESSAGE + ".error"; //$NON-NLS-1$
//private static final String DEP_INCL = COMMENT + ".module.dep.includes"; //$NON-NLS-1$
private static final String HEADER = COMMENT + ".header"; //$NON-NLS-1$
protected static final String MESSAGE_FINISH_BUILD = ManagedMakeMessages
.getResourceString("MakefileGenerator.message.finish.build"); //$NON-NLS-1$
protected static final String MESSAGE_FINISH_FILE = ManagedMakeMessages
.getResourceString("MakefileGenerator.message.finish.file"); //$NON-NLS-1$
protected static final String MESSAGE_START_BUILD = ManagedMakeMessages
.getResourceString("MakefileGenerator.message.start.build"); //$NON-NLS-1$
protected static final String MESSAGE_START_FILE = ManagedMakeMessages
.getResourceString("MakefileGenerator.message.start.file"); //$NON-NLS-1$
protected static final String MESSAGE_START_DEPENDENCY = ManagedMakeMessages
.getResourceString("MakefileGenerator.message.start.dependency"); //$NON-NLS-1$
protected static final String MESSAGE_NO_TARGET_TOOL = ManagedMakeMessages
.getResourceString("MakefileGenerator.message.no.target"); //$NON-NLS-1$
//private static final String MOD_INCL = COMMENT + ".module.make.includes"; //$NON-NLS-1$
private static final String MOD_LIST = COMMENT + ".module.list"; //$NON-NLS-1$
private static final String MOD_VARS = COMMENT + ".module.variables"; //$NON-NLS-1$
private static final String MOD_RULES = COMMENT + ".build.rule"; //$NON-NLS-1$
private static final String BUILD_TOP = COMMENT + ".build.toprules"; //$NON-NLS-1$
private static final String ALL_TARGET = COMMENT + ".build.alltarget"; //$NON-NLS-1$
private static final String MAINBUILD_TARGET = COMMENT + ".build.mainbuildtarget"; //$NON-NLS-1$
private static final String BUILD_TARGETS = COMMENT + ".build.toptargets"; //$NON-NLS-1$
private static final String SRC_LISTS = COMMENT + ".source.list"; //$NON-NLS-1$
private static final String EMPTY_STRING = ""; //$NON-NLS-1$
private static final String[] EMPTY_STRING_ARRAY = new String[0];
private static final String OBJS_MACRO = "OBJS"; //$NON-NLS-1$
private static final String MACRO_ADDITION_ADDPREFIX_HEADER = "${addprefix "; //$NON-NLS-1$
private static final String MACRO_ADDITION_ADDPREFIX_SUFFIX = "," + WHITESPACE + LINEBREAK; //$NON-NLS-1$
private static final String MACRO_ADDITION_PREFIX_SUFFIX = "+=" + WHITESPACE + LINEBREAK; //$NON-NLS-1$
private static final String PREBUILD = "pre-build"; //$NON-NLS-1$
private static final String MAINBUILD = "main-build"; //$NON-NLS-1$
private static final String POSTBUILD = "post-build"; //$NON-NLS-1$
private static final String SECONDARY_OUTPUTS = "secondary-outputs"; //$NON-NLS-1$
// Enumerations
public static final int PROJECT_RELATIVE = 1, PROJECT_SUBDIR_RELATIVE = 2, ABSOLUTE = 3;
class ToolInfoHolder {
ITool[] buildTools;
boolean[] buildToolsUsed;
ManagedBuildGnuToolInfo[] gnuToolInfos;
Set<String> outputExtensionsSet;
List<IPath> dependencyMakefiles;
}
// Local variables needed by generator
private String buildTargetName;
private String buildTargetExt;
private IConfiguration config;
private IBuilder builder;
// private ITool[] buildTools;
// private boolean[] buildToolsUsed;
// private ManagedBuildGnuToolInfo[] gnuToolInfos;
private PathSettingsContainer toolInfos;
private Vector<IResource> deletedFileList;
private Vector<IResource> deletedDirList;
// private IManagedBuildInfo info;
// private IConfiguration cfg
private Vector<IResource> invalidDirList;
/** Collection of Folders in which sources files have been modified */
private Collection<IContainer> modifiedList;
private IProgressMonitor monitor;
private IProject project;
private IResource[] projectResources;
private Vector<String> ruleList;
private Vector<String> depLineList; // String's of additional dependency lines
private Vector<String> depRuleList; // String's of rules for generating dependency files
/** Collection of Containers which contribute source files to the build */
private Collection<IContainer> subdirList;
private IPath topBuildDir; // Build directory - relative to the workspace
// private Set outputExtensionsSet;
//=== Maps of macro names (String) to values (List)
// Map of source file build variable names to a List of source file Path's
private final HashMap<String, List<IPath>> buildSrcVars = new HashMap<>();
// Map of output file build variable names to a List of output file Path's
private final HashMap<String, List<IPath>> buildOutVars = new HashMap<>();
// Map of dependency file build variable names to a List of GnuDependencyGroupInfo objects
private final HashMap<String, GnuDependencyGroupInfo> buildDepVars = new HashMap<>();
private final LinkedHashMap<String, String> topBuildOutVars = new LinkedHashMap<>();
// Dependency file variables
// private Vector dependencyMakefiles; // IPath's - relative to the top build directory or absolute
private ICSourceEntry srcEntries[];
public GnuMakefileGenerator() {
super();
}
/*************************************************************************
* IManagedBuilderMakefileGenerator M E T H O D S
************************************************************************/
/* (non-Javadoc)
* @see org.eclipse.cdt.managedbuilder.makegen.IManagedBuilderMakefileGenerator#initialize(IProject, IManagedBuildInfo, IProgressMonitor)
*/
@Override
public void initialize(IProject project, IManagedBuildInfo info, IProgressMonitor monitor) {
// Save the project so we can get path and member information
this.project = project;
try {
projectResources = project.members();
} catch (CoreException e) {
projectResources = null;
}
// Save the monitor reference for reporting back to the user
this.monitor = monitor;
// Get the build info for the project
// this.info = info;
// Get the name of the build target
buildTargetName = info.getBuildArtifactName();
// Get its extension
buildTargetExt = info.getBuildArtifactExtension();
try {
//try to resolve the build macros in the target extension
buildTargetExt = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(buildTargetExt,
"", //$NON-NLS-1$
" ", //$NON-NLS-1$
IBuildMacroProvider.CONTEXT_CONFIGURATION, info.getDefaultConfiguration());
} catch (BuildMacroException e) {
}
try {
//try to resolve the build macros in the target name
String resolved = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(buildTargetName,
"", //$NON-NLS-1$
" ", //$NON-NLS-1$
IBuildMacroProvider.CONTEXT_CONFIGURATION, info.getDefaultConfiguration());
if (resolved != null && (resolved = resolved.trim()).length() > 0)
buildTargetName = resolved;
} catch (BuildMacroException e) {
}
if (buildTargetExt == null) {
buildTargetExt = ""; //$NON-NLS-1$
}
// Cache the build tools
config = info.getDefaultConfiguration();
builder = config.getEditableBuilder();
initToolInfos();
//set the top build dir path
initializeTopBuildDir(info.getConfigurationName());
}
/**
* This method calls the dependency postprocessors defined for the tool chain
*/
private void callDependencyPostProcessors(IResourceInfo rcInfo, ToolInfoHolder h, IFile depFile,
IManagedDependencyGenerator2[] postProcessors, // This array is the same size as the buildTools array and has
// an entry set when the corresponding tool has a dependency calculator
boolean callPopulateDummyTargets, boolean force) throws CoreException {
try {
// IPath path = depFile.getFullPath();
// path = inFullPathFromOutFullPath(path);
// IResourceInfo rcInfo = config.getResourceInfo(path, false);
// IFolderInfo fo;
// if(rcInfo instanceof IFileInfo){
// fo = (IFolderInfo)config.getResourceInfo(path.removeLastSegments(1), false);
// } else {
// fo = (IFolderInfo)rcInfo;
// }
// ToolInfoHolder h = getToolInfo(fo.getPath());
updateMonitor(ManagedMakeMessages.getFormattedString("GnuMakefileGenerator.message.postproc.dep.file", //$NON-NLS-1$
depFile.getName()));
if (postProcessors != null) {
IPath absolutePath = new Path(
EFSExtensionManager.getDefault().getPathFromURI(depFile.getLocationURI()));
// Convert to build directory relative
IPath depPath = ManagedBuildManager.calculateRelativePath(getTopBuildDir(), absolutePath);
for (int i = 0; i < postProcessors.length; i++) {
IManagedDependencyGenerator2 depGen = postProcessors[i];
if (depGen != null) {
depGen.postProcessDependencyFile(depPath, config, h.buildTools[i], getTopBuildDir());
}
}
}
if (callPopulateDummyTargets) {
populateDummyTargets(rcInfo, depFile, force);
}
} catch (CoreException e) {
throw e;
} catch (IOException e) {
}
}
/**
* This method collects the dependency postprocessors and file extensions defined for the tool chain
*/
private boolean collectDependencyGeneratorInformation(ToolInfoHolder h, Vector<String> depExts, // Vector of dependency file extensions
IManagedDependencyGenerator2[] postProcessors) {
boolean callPopulateDummyTargets = false;
for (int i = 0; i < h.buildTools.length; i++) {
ITool tool = h.buildTools[i];
IManagedDependencyGeneratorType depType = tool
.getDependencyGeneratorForExtension(tool.getDefaultInputExtension());
if (depType != null) {
int calcType = depType.getCalculatorType();
if (calcType <= IManagedDependencyGeneratorType.TYPE_OLD_TYPE_LIMIT) {
if (calcType == IManagedDependencyGeneratorType.TYPE_COMMAND) {
callPopulateDummyTargets = true;
depExts.add(DEP_EXT);
}
} else {
if (calcType == IManagedDependencyGeneratorType.TYPE_BUILD_COMMANDS
|| calcType == IManagedDependencyGeneratorType.TYPE_PREBUILD_COMMANDS) {
IManagedDependencyGenerator2 depGen = (IManagedDependencyGenerator2) depType;
String depExt = depGen.getDependencyFileExtension(config, tool);
if (depExt != null) {
postProcessors[i] = depGen;
depExts.add(depExt);
}
}
}
}
}
return callPopulateDummyTargets;
}
protected boolean isSource(IPath path) {
return !CDataUtil.isExcluded(path, srcEntries);
// path = path.makeRelative();
// for(int i = 0; i < srcPaths.length; i++){
// if(srcPaths[i].isPrefixOf(path))
// return true;
// }
// return false;
}
private class DepInfo {
Vector<String> depExts;
IManagedDependencyGenerator2[] postProcessors;
boolean callPopulateDummyTargets;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.managedbuilder.makegen.IManagedBuilderMakefileGenerator#generateDependencies()
*/
@Override
public void generateDependencies() throws CoreException {
final PathSettingsContainer postProcs = PathSettingsContainer.createRootContainer();
// Note: PopulateDummyTargets is a hack for the pre-3.x GCC compilers
// Collect the methods that will need to be called
toolInfos.accept(new IPathSettingsContainerVisitor() {
@Override
public boolean visit(PathSettingsContainer container) {
ToolInfoHolder h = (ToolInfoHolder) container.getValue();
Vector<String> depExts = new Vector<>(); // Vector of dependency file extensions
IManagedDependencyGenerator2[] postProcessors = new IManagedDependencyGenerator2[h.buildTools.length];
boolean callPopulateDummyTargets = collectDependencyGeneratorInformation(h, depExts, postProcessors);
// Is there anyone to call if we do find dependency files?
if (!callPopulateDummyTargets) {
int i;
for (i = 0; i < postProcessors.length; i++) {
if (postProcessors[i] != null)
break;
}
if (i == postProcessors.length)
return true;
}
PathSettingsContainer child = postProcs.getChildContainer(container.getPath(), true, true);
DepInfo di = new DepInfo();
di.depExts = depExts;
di.postProcessors = postProcessors;
di.callPopulateDummyTargets = callPopulateDummyTargets;
child.setValue(di);
return true;
}
});
IWorkspaceRoot root = CCorePlugin.getWorkspace().getRoot();
for (IResource res : getSubdirList()) {
// The builder creates a subdir with same name as source in the build location
IContainer subDir = (IContainer) res;
IPath projectRelativePath = subDir.getProjectRelativePath();
IResourceInfo rcInfo = config.getResourceInfo(projectRelativePath, false);
PathSettingsContainer cr = postProcs.getChildContainer(rcInfo.getPath(), false, true);
if (cr == null || cr.getValue() == null)
continue;
DepInfo di = (DepInfo) cr.getValue();
ToolInfoHolder h = getToolInfo(projectRelativePath);
IPath buildRelativePath = topBuildDir.append(projectRelativePath);
IFolder buildFolder = root.getFolder(buildRelativePath);
if (buildFolder == null)
continue;
// Find all of the dep files in the generated subdirectories
IResource[] files = buildFolder.members();
for (IResource file : files) {
String fileExt = file.getFileExtension();
for (String ext : di.depExts) {
if (ext.equals(fileExt)) {
IFile depFile = root.getFile(file.getFullPath());
if (depFile == null)
continue;
callDependencyPostProcessors(rcInfo, h, depFile, di.postProcessors, di.callPopulateDummyTargets,
false);
}
}
}
}
}
/* (non-Javadoc)
* @see org.eclipse.cdt.managedbuilder.makegen.IManagedBuilderMakefileGenerator#generateMakefiles(org.eclipse.core.resources.IResourceDelta)
*/
@Override
public MultiStatus generateMakefiles(IResourceDelta delta) throws CoreException {
/*
* Let's do a sanity check right now.
*
* 1. This is an incremental build, so if the top-level directory is not
* there, then a rebuild is needed.
*/
IFolder folder = project.getFolder(computeTopBuildDir(config.getName()));
if (!folder.exists()) {
return regenerateMakefiles();
}
// Return value
MultiStatus status;
// Visit the resources in the delta and compile a list of subdirectories to regenerate
updateMonitor(
ManagedMakeMessages.getFormattedString("MakefileGenerator.message.calc.delta", project.getName())); //$NON-NLS-1$
ResourceDeltaVisitor visitor = new ResourceDeltaVisitor(this, config);
delta.accept(visitor);
checkCancel();
// Get all the subdirectories participating in the build
updateMonitor(
ManagedMakeMessages.getFormattedString("MakefileGenerator.message.finding.sources", project.getName())); //$NON-NLS-1$
ResourceProxyVisitor resourceVisitor = new ResourceProxyVisitor(this, config);
project.accept(resourceVisitor, IResource.NONE);
checkCancel();
// Bug 303953: Ensure that if all resources have been removed from a folder, than the folder still
// appears in the subdir list so it's subdir.mk is correctly regenerated
getSubdirList().addAll(getModifiedList());
// Make sure there is something to build
if (getSubdirList().isEmpty()) {
String info = ManagedMakeMessages.getFormattedString("MakefileGenerator.warning.no.source", //$NON-NLS-1$
project.getName());
updateMonitor(info);
status = new MultiStatus(ManagedBuilderCorePlugin.getUniqueIdentifier(), IStatus.INFO, "", //$NON-NLS-1$
null);
status.add(new Status(IStatus.INFO, ManagedBuilderCorePlugin.getUniqueIdentifier(), NO_SOURCE_FOLDERS, info,
null));
return status;
}
// Make sure the build directory is available
ensureTopBuildDir();
checkCancel();
// Make sure that there is a makefile containing all the folders participating
IPath srcsFilePath = topBuildDir.append(SRCSFILE_NAME);
IFile srcsFileHandle = createFile(srcsFilePath);
buildSrcVars.clear();
buildOutVars.clear();
buildDepVars.clear();
topBuildOutVars.clear();
populateSourcesMakefile(srcsFileHandle);
checkCancel();
// Regenerate any fragments that are missing for the exisiting directories NOT modified
for (IResource res : getSubdirList()) {
IContainer subdirectory = (IContainer) res;
if (!getModifiedList().contains(subdirectory)) {
// Make sure the directory exists (it may have been deleted)
if (!subdirectory.exists()) {
appendDeletedSubdirectory(subdirectory);
continue;
}
// Make sure a fragment makefile exists
IPath fragmentPath = getBuildWorkingDir().append(subdirectory.getProjectRelativePath())
.append(MODFILE_NAME);
IFile makeFragment = project.getFile(fragmentPath);
if (!makeFragment.exists()) {
// If one or both are missing, then add it to the list to be generated
getModifiedList().add(subdirectory);
}
}
}
// Delete the old dependency files for any deleted resources
for (IResource deletedFile : getDeletedFileList()) {
deleteDepFile(deletedFile);
deleteBuildTarget(deletedFile);
}
// Regenerate any fragments for modified directories
for (IResource res : getModifiedList()) {
IContainer subDir = (IContainer) res;
// Make sure the directory exists (it may have been deleted)
if (!subDir.exists()) {
appendDeletedSubdirectory(subDir);
continue;
}
//populateFragmentMakefile(subDir); // See below
checkCancel();
}
// Recreate all module makefiles
// NOTE WELL: For now, always recreate all of the fragment makefile. This is necessary
// in order to re-populate the buildVariable lists. In the future, the list could
// possibly segmented by subdir so that all fragments didn't need to be
// regenerated
for (IResource res : getSubdirList()) {
IContainer subDir = (IContainer) res;
try {
populateFragmentMakefile(subDir);
} catch (CoreException e) {
// Probably should ask user if they want to continue
checkCancel();
continue;
}
checkCancel();
}
// Calculate the inputs and outputs of the Tools to be generated in the main makefile
calculateToolInputsOutputs();
checkCancel();
// Re-create the top-level makefile
IPath makefilePath = topBuildDir.append(MAKEFILE_NAME);
IFile makefileHandle = createFile(makefilePath);
populateTopMakefile(makefileHandle, false);
checkCancel();
// Remove deleted folders from generated build directory
for (IResource res : getDeletedDirList()) {
IContainer subDir = (IContainer) res;
removeGeneratedDirectory(subDir);
checkCancel();
}
// How did we do
if (!getInvalidDirList().isEmpty()) {
status = new MultiStatus(ManagedBuilderCorePlugin.getUniqueIdentifier(), IStatus.WARNING, "", //$NON-NLS-1$
null);
// Add a new status for each of the bad folders
// TODO: fix error message
for (IResource res : getInvalidDirList()) {
IContainer subDir = (IContainer) res;
status.add(new Status(IStatus.WARNING, ManagedBuilderCorePlugin.getUniqueIdentifier(), SPACES_IN_PATH,
subDir.getFullPath().toString(), null));
}
} else {
status = new MultiStatus(ManagedBuilderCorePlugin.getUniqueIdentifier(), IStatus.OK, "", //$NON-NLS-1$
null);
}
return status;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.managedbuilder.makegen.IManagedBuilderMakefileGenerator#getBuildWorkingDir()
*/
@Override
public IPath getBuildWorkingDir() {
if (topBuildDir != null) {
return topBuildDir.removeFirstSegments(1);
}
return null;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.managedbuilder.makegen.IManagedBuilderMakefileGenerator#getMakefileName()
*/
@Override
public String getMakefileName() {
return MAKEFILE_NAME;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.managedbuilder.makegen.IManagedBuilderMakefileGenerator#isGeneratedResource(org.eclipse.core.resources.IResource)
*/
@Override
public boolean isGeneratedResource(IResource resource) {
// Is this a generated directory ...
IPath path = resource.getProjectRelativePath();
//TODO: fix to use builder output dir instead
String[] configNames = ManagedBuildManager.getBuildInfo(project).getConfigurationNames();
for (String name : configNames) {
IPath pathOfConfig = computeTopBuildDir(name);
if (pathOfConfig.isPrefixOf(path)) {
return true;
}
}
return false;
}
private static void save(StringBuffer buffer, IFile file) throws CoreException {
String encoding = null;
try {
encoding = file.getCharset();
} catch (CoreException ce) {
// use no encoding
}
byte[] bytes = null;
if (encoding != null) {
try {
bytes = buffer.toString().getBytes(encoding);
} catch (Exception e) {
}
} else {
bytes = buffer.toString().getBytes();
}
ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
// use a platform operation to update the resource contents
boolean force = true;
file.setContents(stream, force, false, null); // Don't record history
}
/* (non-Javadoc)
* @see org.eclipse.cdt.managedbuilder.makegen.IManagedBuilderMakefileGenerator#regenerateDependencies()
*/
@Override
public void regenerateDependencies(boolean force) throws CoreException {
// A hack for the pre-3.x GCC compilers is to put dummy targets for deps
final IWorkspaceRoot root = CCorePlugin.getWorkspace().getRoot();
final CoreException[] es = new CoreException[1];
toolInfos.accept(new IPathSettingsContainerVisitor() {
@Override
public boolean visit(PathSettingsContainer container) {
ToolInfoHolder h = (ToolInfoHolder) container.getValue();
// Collect the methods that will need to be called
Vector<String> depExts = new Vector<>(); // Vector of dependency file extensions
IManagedDependencyGenerator2[] postProcessors = new IManagedDependencyGenerator2[h.buildTools.length];
boolean callPopulateDummyTargets = collectDependencyGeneratorInformation(h, depExts, postProcessors);
// Is there anyone to call if we do find dependency files?
if (!callPopulateDummyTargets) {
int i;
for (i = 0; i < postProcessors.length; i++) {
if (postProcessors[i] != null)
break;
}
if (i == postProcessors.length)
return true;
}
IResourceInfo rcInfo = config.getResourceInfo(container.getPath(), false);
for (IPath path : getDependencyMakefiles(h)) {
// The path to search for the dependency makefile
IPath relDepFilePath = topBuildDir.append(path);
IFile depFile = root.getFile(relDepFilePath);
if (depFile == null || !depFile.isAccessible())
continue;
try {
callDependencyPostProcessors(rcInfo, h, depFile, postProcessors, callPopulateDummyTargets,
true);
} catch (CoreException e) {
es[0] = e;
return false;
}
}
return true;
}
});
if (es[0] != null)
throw es[0];
}
/* (non-Javadoc)
* @see org.eclipse.cdt.managedbuilder.makegen.IManagedBuilderMakefileGenerator#regenerateMakefiles()
*/
@Override
public MultiStatus regenerateMakefiles() throws CoreException {
MultiStatus status;
// Visit the resources in the project
ResourceProxyVisitor visitor = new ResourceProxyVisitor(this, config);
project.accept(visitor, IResource.NONE);
// See if the user has cancelled the build
checkCancel();
// Populate the makefile if any buildable source files have been found in the project
if (getSubdirList().isEmpty()) {
String info = ManagedMakeMessages.getFormattedString("MakefileGenerator.warning.no.source", //$NON-NLS-1$
project.getName());
updateMonitor(info);
status = new MultiStatus(ManagedBuilderCorePlugin.getUniqueIdentifier(), IStatus.INFO, "", //$NON-NLS-1$
null);
status.add(new Status(IStatus.INFO, ManagedBuilderCorePlugin.getUniqueIdentifier(), NO_SOURCE_FOLDERS, info,
null));
return status;
}
// Create the top-level directory for the build output
ensureTopBuildDir();
checkCancel();
// Get the list of subdirectories
IPath srcsFilePath = topBuildDir.append(SRCSFILE_NAME);
IFile srcsFileHandle = createFile(srcsFilePath);
buildSrcVars.clear();
buildOutVars.clear();
buildDepVars.clear();
topBuildOutVars.clear();
populateSourcesMakefile(srcsFileHandle);
checkCancel();
// Now populate the module makefiles
for (IResource res : getSubdirList()) {
IContainer subDir = (IContainer) res;
try {
populateFragmentMakefile(subDir);
} catch (CoreException e) {
// Probably should ask user if they want to continue
checkCancel();
continue;
}
checkCancel();
}
// Calculate the inputs and outputs of the Tools to be generated in the main makefile
calculateToolInputsOutputs();
checkCancel();
// Create the top-level makefile
IPath makefilePath = topBuildDir.append(MAKEFILE_NAME);
IFile makefileHandle = createFile(makefilePath);
populateTopMakefile(makefileHandle, true);
checkCancel();
// Now finish up by adding all the object files
IPath objFilePath = topBuildDir.append(OBJECTS_MAKFILE);
IFile objsFileHandle = createFile(objFilePath);
populateObjectsMakefile(objsFileHandle);
checkCancel();
// How did we do
if (!getInvalidDirList().isEmpty()) {
status = new MultiStatus(ManagedBuilderCorePlugin.getUniqueIdentifier(), IStatus.WARNING, "", //$NON-NLS-1$
null);
// Add a new status for each of the bad folders
// TODO: fix error message
for (IResource dir : getInvalidDirList()) {
status.add(new Status(IStatus.WARNING, ManagedBuilderCorePlugin.getUniqueIdentifier(), SPACES_IN_PATH,
dir.getFullPath().toString(), null));
}
} else {
status = new MultiStatus(ManagedBuilderCorePlugin.getUniqueIdentifier(), IStatus.OK, "", //$NON-NLS-1$
null);
}
return status;
}
/*************************************************************************
* M A K E F I L E S P O P U L A T I O N M E T H O D S
************************************************************************/
/**
* This method generates a "fragment" make file (subdir.mk).
* One of these is generated for each project directory/subdirectory
* that contains source files.
*/
protected void populateFragmentMakefile(IContainer module) throws CoreException {
// Calculate the new directory relative to the build output
IPath moduleRelativePath = module.getProjectRelativePath();
IPath buildRoot = getBuildWorkingDir();
if (buildRoot == null) {
return;
}
IPath moduleOutputPath = buildRoot.append(moduleRelativePath);
updateMonitor(ManagedMakeMessages.getFormattedString("MakefileGenerator.message.gen.source.makefile", //$NON-NLS-1$
moduleOutputPath.toString()));
// Now create the directory
IPath moduleOutputDir = createDirectory(moduleOutputPath.toString());
// Create a module makefile
IFile modMakefile = createFile(moduleOutputDir.append(MODFILE_NAME));
StringBuffer makeBuf = new StringBuffer();
makeBuf.append(addFragmentMakefileHeader());
makeBuf.append(addSources(module));
// Save the files
save(makeBuf, modMakefile);
}
/**
* The makefile generator generates a Macro for each type of output, other than final artifact,
* created by the build.
*
* @param fileHandle The file that should be populated with the output
*/
protected void populateObjectsMakefile(IFile fileHandle) throws CoreException {
// Master list of "object" dependencies, i.e. dependencies between input files and output files.
StringBuffer macroBuffer = new StringBuffer();
List<String> valueList;
macroBuffer.append(addDefaultHeader());
// Map of macro names (String) to its definition (List of Strings)
HashMap<String, List<String>> outputMacros = new HashMap<>();
// Add the predefined LIBS, USER_OBJS macros
// Add the libraries this project depends on
valueList = new ArrayList<>();
String[] libs = config.getLibs(buildTargetExt);
for (String lib : libs) {
valueList.add(lib);
}
outputMacros.put("LIBS", valueList); //$NON-NLS-1$
// Add the extra user-specified objects
valueList = new ArrayList<>();
String[] userObjs = config.getUserObjects(buildTargetExt);
for (String obj : userObjs) {
valueList.add(obj);
}
outputMacros.put("USER_OBJS", valueList); //$NON-NLS-1$
// Write every macro to the file
for (Entry<String, List<String>> entry : outputMacros.entrySet()) {
macroBuffer.append(entry.getKey()).append(" :="); //$NON-NLS-1$
valueList = entry.getValue();
for (String path : valueList) {
// These macros will also be used within commands.
// Make all the slashes go forward so they aren't
// interpreted as escapes and get lost.
// See https://bugs.eclipse.org/163672.
path = path.replace('\\', '/');
path = ensurePathIsGNUMakeTargetRuleCompatibleSyntax(path);
macroBuffer.append(WHITESPACE);
macroBuffer.append(path);
}
// terminate the macro definition line
macroBuffer.append(NEWLINE);
// leave a blank line before the next macro
macroBuffer.append(NEWLINE);
}
// For now, just save the buffer that was populated when the rules were created
save(macroBuffer, fileHandle);
}
protected void populateSourcesMakefile(IFile fileHandle) throws CoreException {
// Add the comment
StringBuffer buffer = addDefaultHeader();
// Determine the set of macros
toolInfos.accept(new IPathSettingsContainerVisitor() {
@Override
public boolean visit(PathSettingsContainer container) {
ToolInfoHolder h = (ToolInfoHolder) container.getValue();
ITool[] buildTools = h.buildTools;
HashSet<String> handledInputExtensions = new HashSet<>();
String buildMacro;
for (ITool buildTool : buildTools) {
if (buildTool.getCustomBuildStep())
continue;
// Add the known sources macros
String[] extensionsList = buildTool.getAllInputExtensions();
for (String ext : extensionsList) {
// create a macro of the form "EXTENSION_SRCS :="
String extensionName = ext;
if (//!getOutputExtensions().contains(extensionName) &&
!handledInputExtensions.contains(extensionName)) {
handledInputExtensions.add(extensionName);
buildMacro = getSourceMacroName(extensionName).toString();
if (!buildSrcVars.containsKey(buildMacro)) {
buildSrcVars.put(buildMacro, new ArrayList<IPath>());
}
// Add any generated dependency file macros
IManagedDependencyGeneratorType depType = buildTool
.getDependencyGeneratorForExtension(extensionName);
if (depType != null) {
int calcType = depType.getCalculatorType();
if (calcType == IManagedDependencyGeneratorType.TYPE_COMMAND
|| calcType == IManagedDependencyGeneratorType.TYPE_BUILD_COMMANDS
|| calcType == IManagedDependencyGeneratorType.TYPE_PREBUILD_COMMANDS) {
buildMacro = getDepMacroName(extensionName).toString();
if (!buildDepVars.containsKey(buildMacro)) {
buildDepVars.put(buildMacro, new GnuDependencyGroupInfo(buildMacro,
(calcType != IManagedDependencyGeneratorType.TYPE_PREBUILD_COMMANDS)));
}
if (!buildOutVars.containsKey(buildMacro)) {
buildOutVars.put(buildMacro, new ArrayList<IPath>());
}
}
}
}
}
// Add the specified output build variables
IOutputType[] outTypes = buildTool.getOutputTypes();
if (outTypes != null && outTypes.length > 0) {
for (IOutputType outputType : outTypes) {
buildMacro = outputType.getBuildVariable();
if (!buildOutVars.containsKey(buildMacro)) {
buildOutVars.put(buildMacro, new ArrayList<IPath>());
}
}
} else {
// For support of pre-CDT 3.0 integrations.
buildMacro = OBJS_MACRO;
if (!buildOutVars.containsKey(buildMacro)) {
buildOutVars.put(buildMacro, new ArrayList<IPath>());
}
}
}
return true;
}
});
// Add the macros to the makefile
for (Entry<String, List<IPath>> entry : buildSrcVars.entrySet()) {
String macroName = entry.getKey();
buffer.append(macroName).append(WHITESPACE).append(":=").append(WHITESPACE).append(NEWLINE); //$NON-NLS-1$
}
Set<Entry<String, List<IPath>>> set = buildOutVars.entrySet();
for (Entry<String, List<IPath>> entry : set) {
String macroName = entry.getKey();
buffer.append(macroName).append(WHITESPACE).append(":=").append(WHITESPACE).append(NEWLINE); //$NON-NLS-1$
}
// Add a list of subdirectories to the makefile
buffer.append(NEWLINE).append(addSubdirectories());
// Save the file
save(buffer, fileHandle);
}
/**
* Create the entire contents of the makefile.
*
* @param fileHandle The file to place the contents in.
* @param rebuild FLag signaling that the user is doing a full rebuild
*/
protected void populateTopMakefile(IFile fileHandle, boolean rebuild) throws CoreException {
StringBuffer buffer = new StringBuffer();
// Add the header
buffer.append(addTopHeader());
// Add the macro definitions
buffer.append(addMacros());
// List to collect needed build output variables
List<String> outputVarsAdditionsList = new ArrayList<>();
// Determine target rules
StringBuffer targetRules = addTargets(outputVarsAdditionsList, rebuild);
// Add outputMacros that were added to by the target rules
buffer.append(writeTopAdditionMacros(outputVarsAdditionsList, getTopBuildOutputVars()));
// Add target rules
buffer.append(targetRules);
// Save the file
save(buffer, fileHandle);
}
/*************************************************************************
* M A I N (makefile) M A K E F I L E M E T H O D S
************************************************************************/
/**
* Answers a <code>StringBuffer</code> containing the comment(s)
* for the top-level makefile.
*/
protected StringBuffer addTopHeader() {
return addDefaultHeader();
}
/**
*/
private StringBuffer addMacros() {
StringBuffer buffer = new StringBuffer();
// Add the ROOT macro
//buffer.append("ROOT := ..").append(NEWLINE); //$NON-NLS-1$
//buffer.append(NEWLINE);
// include makefile.init supplementary makefile
buffer.append("-include " + reachProjectRoot() + SEPARATOR + MAKEFILE_INIT).append(NEWLINE); //$NON-NLS-1$
buffer.append(NEWLINE);
// Get the clean command from the build model
buffer.append("RM := "); //$NON-NLS-1$
// support macros in the clean command
String cleanCommand = config.getCleanCommand();
try {
cleanCommand = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(
config.getCleanCommand(), EMPTY_STRING, WHITESPACE, IBuildMacroProvider.CONTEXT_CONFIGURATION,
config);
} catch (BuildMacroException e) {
}
buffer.append(cleanCommand).append(NEWLINE);
buffer.append(NEWLINE);
// Now add the source providers
buffer.append(COMMENT_SYMBOL).append(WHITESPACE).append(ManagedMakeMessages.getResourceString(SRC_LISTS))
.append(NEWLINE);
buffer.append("-include sources.mk").append(NEWLINE); //$NON-NLS-1$
// Add includes for each subdir in child-subdir-first order (required for makefile rule matching to work).
List<String> subDirList = new ArrayList<>();
for (IContainer subDir : getSubdirList()) {
String projectRelativePath = subDir.getProjectRelativePath().toString();
if (!projectRelativePath.isEmpty())
subDirList.add(0, projectRelativePath);
}
Collections.sort(subDirList, Collections.reverseOrder());
for (String dir : subDirList) {
buffer.append("-include ").append(escapeWhitespaces(dir)).append(SEPARATOR).append("subdir.mk") //$NON-NLS-1$//$NON-NLS-2$
.append(NEWLINE);
}
buffer.append("-include subdir.mk").append(NEWLINE); //$NON-NLS-1$
buffer.append("-include objects.mk").append(NEWLINE).append(NEWLINE); //$NON-NLS-1$
// Include generated dependency makefiles if non-empty AND a "clean" has not been requested
if (!buildDepVars.isEmpty()) {
buffer.append("ifneq ($(MAKECMDGOALS),clean)").append(NEWLINE); //$NON-NLS-1$
for (Entry<String, GnuDependencyGroupInfo> entry : buildDepVars.entrySet()) {
String depsMacro = entry.getKey();
GnuDependencyGroupInfo info = entry.getValue();
buffer.append("ifneq ($(strip $(").append(depsMacro).append(")),)").append(NEWLINE); //$NON-NLS-1$ //$NON-NLS-2$
if (info.conditionallyInclude) {
buffer.append("-include $(").append(depsMacro).append(')').append(NEWLINE); //$NON-NLS-1$
} else {
buffer.append("include $(").append(depsMacro).append(')').append(NEWLINE); //$NON-NLS-1$
}
buffer.append("endif").append(NEWLINE); //$NON-NLS-1$
}
buffer.append("endif").append(NEWLINE).append(NEWLINE); //$NON-NLS-1$
}
// Include makefile.defs supplemental makefile
buffer.append("-include ").append(reachProjectRoot()).append(SEPARATOR).append(MAKEFILE_DEFS).append(NEWLINE); //$NON-NLS-1$
String ext = config.getArtifactExtension();
// try to resolve the build macros in the artifact extension
try {
ext = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(ext, EMPTY_STRING,
WHITESPACE, IBuildMacroProvider.CONTEXT_CONFIGURATION, config);
} catch (BuildMacroException e) {
}
String name = config.getArtifactName();
// try to resolve the build macros in the artifact name
try {
String resolved = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(name,
EMPTY_STRING, WHITESPACE, IBuildMacroProvider.CONTEXT_CONFIGURATION, config);
if ((resolved = resolved.trim()).length() > 0) {
name = resolved;
}
} catch (BuildMacroException e) {
}
String prefix = EMPTY_STRING;
ITool targetTool = config.calculateTargetTool();
if (targetTool != null) {
prefix = targetTool.getOutputPrefix();
if (prefix == null) {
prefix = EMPTY_STRING;
}
}
// try to resolve the build macros in the artifact prefix
try {
String resolved = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(prefix,
EMPTY_STRING, WHITESPACE, IBuildMacroProvider.CONTEXT_CONFIGURATION, config);
if ((resolved = resolved.trim()).length() > 0) {
prefix = resolved;
}
} catch (BuildMacroException e) {
}
@SuppressWarnings("nls")
String[][] buildArtifactVars = new String[][] { //
{ "BUILD_ARTIFACT_NAME", name }, //
{ "BUILD_ARTIFACT_EXTENSION", ext }, //
{ "BUILD_ARTIFACT_PREFIX", prefix }, //
{ "BUILD_ARTIFACT",
"$(BUILD_ARTIFACT_PREFIX)$(BUILD_ARTIFACT_NAME)$(if $(BUILD_ARTIFACT_EXTENSION),.$(BUILD_ARTIFACT_EXTENSION),)" }, //
};
buffer.append(NEWLINE);
for (String[] var : buildArtifactVars) {
buffer.append(var[0]).append(" :="); //$NON-NLS-1$
if (!var[1].isEmpty()) {
buffer.append(WHITESPACE).append(var[1]);
}
buffer.append(NEWLINE);
}
return (buffer.append(NEWLINE));
}
/**
* Answers a <code>StringBuffer</code> containing all of the required targets to
* properly build the project.
*
* @param outputVarsAdditionsList list to add needed build output variables to
*/
private StringBuffer addTargets(List<String> outputVarsAdditionsList, boolean rebuild) {
StringBuffer buffer = new StringBuffer();
// IConfiguration config = info.getDefaultConfiguration();
// Assemble the information needed to generate the targets
String prebuildStep = config.getPrebuildStep();
try {
//try to resolve the build macros in the prebuild step
prebuildStep = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(prebuildStep,
EMPTY_STRING, WHITESPACE, IBuildMacroProvider.CONTEXT_CONFIGURATION, config);
} catch (BuildMacroException e) {
}
prebuildStep = prebuildStep.trim(); // Remove leading and trailing whitespace (and control characters)
String postbuildStep = config.getPostbuildStep();
try {
//try to resolve the build macros in the postbuild step
postbuildStep = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(postbuildStep,
EMPTY_STRING, WHITESPACE, IBuildMacroProvider.CONTEXT_CONFIGURATION, config);
} catch (BuildMacroException e) {
}
postbuildStep = postbuildStep.trim(); // Remove leading and trailing whitespace (and control characters)
String preannouncebuildStep = config.getPreannouncebuildStep();
String postannouncebuildStep = config.getPostannouncebuildStep();
String targets = rebuild ? "clean all" : "all"; //$NON-NLS-1$ //$NON-NLS-2$
ITool targetTool = config.calculateTargetTool();
// if (targetTool == null) {
// targetTool = info.getToolFromOutputExtension(buildTargetExt);
// }
// Get all the projects the build target depends on
// If this configuration produces a static archive, building the archive doesn't depend on the output
// from any of the referenced configurations
IConfiguration[] refConfigs = new IConfiguration[0];
if (config.getBuildArtefactType() == null || !ManagedBuildManager.BUILD_ARTEFACT_TYPE_PROPERTY_STATICLIB
.equals(config.getBuildArtefactType().getId()))
refConfigs = ManagedBuildManager.getReferencedConfigurations(config);
/* try {
refdProjects = project.getReferencedProjects();
} catch (CoreException e) {
// There are 2 exceptions; the project does not exist or it is not open
// and neither conditions apply if we are building for it ....
}
*/
// If a prebuild step exists, redefine the all target to be
// all:
// $(MAKE) pre-build
// $(MAKE) main-build
// and then reset the "traditional" all target to main-build
// This will allow something meaningful to happen if the generated
// makefile is
// extracted and run standalone via "make all"
//
String defaultTarget = "all:"; //$NON-NLS-1$
if (prebuildStep.length() > 0) {
// Add the comment for the "All" target
buffer.append(COMMENT_SYMBOL).append(WHITESPACE).append(ManagedMakeMessages.getResourceString(ALL_TARGET))
.append(NEWLINE);
// Invoke make multiple times to ensure pre-build is executed before main-build
buffer.append(defaultTarget).append(NEWLINE);
buffer.append(TAB).append(MAKE).append(WHITESPACE).append(NO_PRINT_DIR).append(WHITESPACE).append(PREBUILD)
.append(NEWLINE);
buffer.append(TAB).append(MAKE).append(WHITESPACE).append(NO_PRINT_DIR).append(WHITESPACE).append(MAINBUILD)
.append(NEWLINE);
buffer.append(NEWLINE);
// Update the defaultTarget, main-build, by adding a colon, which is
// needed below
defaultTarget = MAINBUILD.concat(COLON);
// Add the comment for the "main-build" target
buffer.append(COMMENT_SYMBOL).append(WHITESPACE)
.append(ManagedMakeMessages.getResourceString(MAINBUILD_TARGET)).append(NEWLINE);
} else
// Add the comment for the "All" target
buffer.append(COMMENT_SYMBOL).append(WHITESPACE).append(ManagedMakeMessages.getResourceString(ALL_TARGET))
.append(NEWLINE);
// Write out the all target first in case someone just runs make
// all: <target_name> or mainbuild: <target_name>
String outputPrefix = EMPTY_STRING;
if (targetTool != null) {
outputPrefix = targetTool.getOutputPrefix();
}
buffer.append(defaultTarget).append(WHITESPACE).append(outputPrefix)
.append(ensurePathIsGNUMakeTargetRuleCompatibleSyntax(buildTargetName));
if (buildTargetExt.length() > 0) {
buffer.append(DOT).append(buildTargetExt);
}
// Add the Secondary Outputs to the all target, if any
IOutputType[] secondaryOutputs = config.getToolChain().getSecondaryOutputs();
if (secondaryOutputs.length > 0) {
buffer.append(WHITESPACE).append(SECONDARY_OUTPUTS);
}
buffer.append(NEWLINE).append(NEWLINE);
/*
* The build target may depend on other projects in the workspace. These
* are captured in the deps target: deps: <cd <Proj_Dep_1/build_dir>;
* $(MAKE) [clean all | all]>
*/
// Vector managedProjectOutputs = new Vector(refdProjects.length);
// if (refdProjects.length > 0) {
Vector<String> managedProjectOutputs = new Vector<>(refConfigs.length);
if (refConfigs.length > 0) {
boolean addDeps = true;
// if (refdProjects != null) {
for (IConfiguration depCfg : refConfigs) {
// IProject dep = refdProjects[i];
if (!depCfg.isManagedBuildOn())
continue;
// if (!dep.exists()) continue;
if (addDeps) {
buffer.append("dependents:").append(NEWLINE); //$NON-NLS-1$
addDeps = false;
}
String buildDir = depCfg.getOwner().getLocation().toString();
String depTargets = targets;
// if (ManagedBuildManager.manages(dep)) {
// Add the current configuration to the makefile path
// IManagedBuildInfo depInfo = ManagedBuildManager.getBuildInfo(dep);
buildDir += SEPARATOR + depCfg.getName();
// Extract the build artifact to add to the dependency list
String depTarget = depCfg.getArtifactName();
String depExt = depCfg.getArtifactExtension();
try {
//try to resolve the build macros in the artifact extension
depExt = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(depExt, "", //$NON-NLS-1$
" ", //$NON-NLS-1$
IBuildMacroProvider.CONTEXT_CONFIGURATION, depCfg);
} catch (BuildMacroException e) {
}
try {
//try to resolve the build macros in the artifact name
String resolved = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(
depTarget, "", //$NON-NLS-1$
" ", //$NON-NLS-1$
IBuildMacroProvider.CONTEXT_CONFIGURATION, depCfg);
if ((resolved = resolved.trim()).length() > 0)
depTarget = resolved;
} catch (BuildMacroException e) {
}
String depPrefix = depCfg.getOutputPrefix(depExt);
if (depCfg.needsRebuild()) {
depTargets = "clean all"; //$NON-NLS-1$
}
String dependency = buildDir + SEPARATOR + depPrefix + depTarget;
if (depExt.length() > 0) {
dependency += DOT + depExt;
}
dependency = escapeWhitespaces(dependency);
managedProjectOutputs.add(dependency);
//}
buffer.append(TAB).append("-cd").append(WHITESPACE).append(escapeWhitespaces(buildDir)) //$NON-NLS-1$
.append(WHITESPACE).append(LOGICAL_AND).append(WHITESPACE).append("$(MAKE) ").append(depTargets) //$NON-NLS-1$
.append(NEWLINE);
}
// }
buffer.append(NEWLINE);
}
// Add the targets tool rules
buffer.append(addTargetsRules(targetTool, outputVarsAdditionsList, managedProjectOutputs,
(postbuildStep.length() > 0)));
// Add the prebuild step target, if specified
if (prebuildStep.length() > 0) {
buffer.append(PREBUILD).append(COLON).append(NEWLINE);
if (preannouncebuildStep.length() > 0) {
buffer.append(TAB).append(DASH).append(AT).append(escapedEcho(preannouncebuildStep));
}
buffer.append(TAB).append(DASH).append(prebuildStep).append(NEWLINE);
buffer.append(TAB).append(DASH).append(AT).append(ECHO_BLANK_LINE).append(NEWLINE);
}
// Add the postbuild step, if specified
if (postbuildStep.length() > 0) {
buffer.append(POSTBUILD).append(COLON).append(NEWLINE);
if (postannouncebuildStep.length() > 0) {
buffer.append(TAB).append(DASH).append(AT).append(escapedEcho(postannouncebuildStep));
}
buffer.append(TAB).append(DASH).append(postbuildStep).append(NEWLINE);
buffer.append(TAB).append(DASH).append(AT).append(ECHO_BLANK_LINE).append(NEWLINE);
}
// Add the Secondary Outputs target, if needed
if (secondaryOutputs.length > 0) {
buffer.append(SECONDARY_OUTPUTS).append(COLON);
Vector<String> outs2 = calculateSecondaryOutputs(secondaryOutputs);
for (int i = 0; i < outs2.size(); i++) {
buffer.append(WHITESPACE).append("$(").append(outs2.get(i)).append(')'); //$NON-NLS-1$
}
buffer.append(NEWLINE).append(NEWLINE);
}
// Add all the needed dummy and phony targets
buffer.append(".PHONY: all clean dependents"); //$NON-NLS-1$
if (prebuildStep.length() > 0) {
buffer.append(WHITESPACE).append(MAINBUILD).append(WHITESPACE).append(PREBUILD);
}
if (postbuildStep.length() > 0) {
buffer.append(WHITESPACE).append(POSTBUILD);
}
buffer.append(NEWLINE);
for (String output : managedProjectOutputs) {
buffer.append(output).append(COLON).append(NEWLINE);
}
buffer.append(NEWLINE);
// Include makefile.targets supplemental makefile
buffer.append("-include ").append(reachProjectRoot()).append(SEPARATOR).append(MAKEFILE_TARGETS) //$NON-NLS-1$
.append(NEWLINE);
return buffer;
}
/**
* Returns the targets rules. The targets make file (top makefile) contains:
* 1 the rule for the final target tool
* 2 the rules for all of the tools that use multipleOfType in their primary input type
* 3 the rules for all tools that use the output of #2 tools
*
* @param outputVarsAdditionsList list to add needed build output variables to
* @param managedProjectOutputs Other projects in the workspace that this project depends upon
* @return StringBuffer
*/
private StringBuffer addTargetsRules(ITool targetTool, List<String> outputVarsAdditionsList,
Vector<String> managedProjectOutputs, boolean postbuildStep) {
StringBuffer buffer = new StringBuffer();
// Add the comment
buffer.append(COMMENT_SYMBOL).append(WHITESPACE).append(ManagedMakeMessages.getResourceString(BUILD_TOP))
.append(NEWLINE);
ToolInfoHolder h = (ToolInfoHolder) toolInfos.getValue();
ITool[] buildTools = h.buildTools;
boolean[] buildToolsUsed = h.buildToolsUsed;
// Get the target tool and generate the rule
if (targetTool != null) {
// Note that the name of the target we pass to addRuleForTool does not
// appear to be used there (and tool outputs are consulted directly), but
// we quote it anyway just in case it starts to use it in future.
if (addRuleForTool(targetTool, buffer, true, ensurePathIsGNUMakeTargetRuleCompatibleSyntax(buildTargetName),
buildTargetExt, outputVarsAdditionsList, managedProjectOutputs, postbuildStep)) {
// Mark the target tool as processed
for (int i = 0; i < buildTools.length; i++) {
if (targetTool == buildTools[i]) {
buildToolsUsed[i] = true;
}
}
}
} else {
buffer.append(TAB).append(AT).append(escapedEcho(MESSAGE_NO_TARGET_TOOL + WHITESPACE + OUT_MACRO));
}
// Generate the rules for all Tools that specify InputType.multipleOfType, and any Tools that
// consume the output of those tools. This does not apply to pre-3.0 integrations, since
// the only "multipleOfType" tool is the "target" tool
for (int i = 0; i < buildTools.length; i++) {
ITool tool = buildTools[i];
IInputType type = tool.getPrimaryInputType();
if (type != null && type.getMultipleOfType()) {
if (!buildToolsUsed[i]) {
addRuleForTool(tool, buffer, false, null, null, outputVarsAdditionsList, null, false);
// Mark the target tool as processed
buildToolsUsed[i] = true;
// Look for tools that consume the output
generateRulesForConsumers(tool, outputVarsAdditionsList, buffer);
}
}
}
// Add the comment
buffer.append(COMMENT_SYMBOL).append(WHITESPACE).append(ManagedMakeMessages.getResourceString(BUILD_TARGETS))
.append(NEWLINE);
// Always add a clean target
buffer.append("clean:").append(NEWLINE); //$NON-NLS-1$
buffer.append(TAB).append("-$(RM)").append(WHITESPACE); //$NON-NLS-1$
for (Entry<String, List<IPath>> entry : buildOutVars.entrySet()) {
String macroName = entry.getKey();
buffer.append("$(").append(macroName).append(')'); //$NON-NLS-1$
}
String outputPrefix = EMPTY_STRING;
if (targetTool != null) {
outputPrefix = targetTool.getOutputPrefix();
}
String completeBuildTargetName = outputPrefix + buildTargetName;
if (buildTargetExt.length() > 0) {
completeBuildTargetName = completeBuildTargetName + DOT + buildTargetExt;
}
if (completeBuildTargetName.contains(" ")) { //$NON-NLS-1$
buffer.append(WHITESPACE).append('"').append(completeBuildTargetName).append('"');
} else {
buffer.append(WHITESPACE).append(completeBuildTargetName);
}
buffer.append(NEWLINE);
buffer.append(TAB).append(DASH).append(AT).append(ECHO_BLANK_LINE).append(NEWLINE);
return buffer;
}
/**
* Create the rule
*
* @param buffer Buffer to add makefile rules to
* @param bTargetTool True if this is the target tool
* @param targetName If this is the "targetTool", the target file name, else <code>null</code>
* @param targetExt If this is the "targetTool", the target file extension, else <code>null</code>
* @param outputVarsAdditionsList list to add needed build output variables to
* @param managedProjectOutputs Other projects in the workspace that this project depends upon
* @param bEmitPostBuildStepCall Emit post-build step invocation
*/
protected boolean addRuleForTool(ITool tool, StringBuffer buffer, boolean bTargetTool, String targetName,
String targetExt, List<String> outputVarsAdditionsList, Vector<String> managedProjectOutputs,
boolean bEmitPostBuildStepCall) {
// Get the tool's inputs and outputs
Vector<String> inputs = new Vector<>();
Vector<String> dependencies = new Vector<>();
Vector<String> outputs = new Vector<>();
Vector<String> enumeratedPrimaryOutputs = new Vector<>();
Vector<String> enumeratedSecondaryOutputs = new Vector<>();
Vector<String> outputVariables = new Vector<>();
Vector<String> additionalTargets = new Vector<>();
String outputPrefix = EMPTY_STRING;
if (!getToolInputsOutputs(tool, inputs, dependencies, outputs, enumeratedPrimaryOutputs,
enumeratedSecondaryOutputs, outputVariables, additionalTargets, bTargetTool, managedProjectOutputs)) {
return false;
}
// If we have no primary output, make all of the secondary outputs the primary output
if (enumeratedPrimaryOutputs.size() == 0) {
enumeratedPrimaryOutputs = enumeratedSecondaryOutputs;
enumeratedSecondaryOutputs.clear();
}
// Add the output variables for this tool to our list
outputVarsAdditionsList.addAll(outputVariables);
// Create the build rule
String buildRule = EMPTY_STRING;
String outflag = tool.getOutputFlag();
String primaryOutputs = EMPTY_STRING;
String primaryOutputsQuoted = EMPTY_STRING;
boolean first = true;
for (int i = 0; i < enumeratedPrimaryOutputs.size(); i++) {
String output = enumeratedPrimaryOutputs.get(i);
if (!first) {
primaryOutputs += WHITESPACE;
primaryOutputsQuoted += WHITESPACE;
}
first = false;
primaryOutputs += output;
primaryOutputsQuoted += ensurePathIsGNUMakeTargetRuleCompatibleSyntax(output);
}
buildRule += (primaryOutputsQuoted + COLON + WHITESPACE);
first = true;
String calculatedDependencies = EMPTY_STRING;
for (int i = 0; i < dependencies.size(); i++) {
String input = dependencies.get(i);
if (!first)
calculatedDependencies += WHITESPACE;
first = false;
calculatedDependencies += input;
}
buildRule += calculatedDependencies;
// We can't have duplicates in a makefile
if (getRuleList().contains(buildRule)) {
} else {
getRuleList().add(buildRule);
buffer.append(buildRule).append(NEWLINE);
if (bTargetTool) {
buffer.append(TAB).append(AT).append(escapedEcho(MESSAGE_START_BUILD + WHITESPACE + OUT_MACRO));
}
buffer.append(TAB).append(AT).append(escapedEcho(tool.getAnnouncement()));
// Get the command line for this tool invocation
String[] flags;
try {
flags = tool.getToolCommandFlags(null, null);
} catch (BuildException ex) {
// TODO report error
flags = EMPTY_STRING_ARRAY;
}
String command = tool.getToolCommand();
try {
//try to resolve the build macros in the tool command
String resolvedCommand = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(
command, EMPTY_STRING, WHITESPACE, IBuildMacroProvider.CONTEXT_FILE,
new FileContextData(null, null, null, tool));
if ((resolvedCommand = resolvedCommand.trim()).length() > 0)
command = resolvedCommand;
} catch (BuildMacroException e) {
}
String[] cmdInputs = inputs.toArray(new String[inputs.size()]);
IManagedCommandLineGenerator gen = tool.getCommandLineGenerator();
IManagedCommandLineInfo cmdLInfo = gen.generateCommandLineInfo(tool, command, flags, outflag, outputPrefix,
primaryOutputs, cmdInputs, tool.getCommandLinePattern());
// The command to build
String buildCmd = null;
if (cmdLInfo == null) {
String toolFlags;
try {
toolFlags = tool.getToolCommandFlagsString(null, null);
} catch (BuildException ex) {
// TODO report error
toolFlags = EMPTY_STRING;
}
buildCmd = command + WHITESPACE + toolFlags + WHITESPACE + outflag + WHITESPACE + outputPrefix
+ primaryOutputs + WHITESPACE + IN_MACRO;
} else
buildCmd = cmdLInfo.getCommandLine();
// resolve any remaining macros in the command after it has been
// generated
try {
String resolvedCommand = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(
buildCmd, EMPTY_STRING, WHITESPACE, IBuildMacroProvider.CONTEXT_FILE,
new FileContextData(null, null, null, tool));
if ((resolvedCommand = resolvedCommand.trim()).length() > 0)
buildCmd = resolvedCommand;
} catch (BuildMacroException e) {
}
//buffer.append(TAB).append(AT).append(escapedEcho(buildCmd));
//buffer.append(TAB).append(AT).append(buildCmd);
buffer.append(TAB).append(buildCmd);
// TODO
// NOTE WELL: Dependency file generation is not handled for this type of Tool
// Echo finished message
buffer.append(NEWLINE);
buffer.append(TAB).append(AT).append(
escapedEcho((bTargetTool ? MESSAGE_FINISH_BUILD : MESSAGE_FINISH_FILE) + WHITESPACE + OUT_MACRO));
buffer.append(TAB).append(AT).append(ECHO_BLANK_LINE);
// If there is a post build step, then add a recursive invocation of MAKE to invoke it after the main build
// Note that $(MAKE) will instantiate in the recusive invocation to the make command that was used to invoke
// the makefile originally
if (bEmitPostBuildStepCall) {
buffer.append(TAB).append(MAKE).append(WHITESPACE).append(NO_PRINT_DIR).append(WHITESPACE)
.append(POSTBUILD).append(NEWLINE).append(NEWLINE);
} else {
// Just emit a blank line
buffer.append(NEWLINE);
}
}
// If we have secondary outputs, output dependency rules without commands
if (enumeratedSecondaryOutputs.size() > 0 || additionalTargets.size() > 0) {
String primaryOutput = enumeratedPrimaryOutputs.get(0);
Vector<String> addlOutputs = new Vector<>();
addlOutputs.addAll(enumeratedSecondaryOutputs);
addlOutputs.addAll(additionalTargets);
for (int i = 0; i < addlOutputs.size(); i++) {
String output = addlOutputs.get(i);
String depLine = output + COLON + WHITESPACE + primaryOutput + WHITESPACE + calculatedDependencies
+ NEWLINE;
if (!getDepLineList().contains(depLine)) {
getDepLineList().add(depLine);
buffer.append(depLine);
}
}
buffer.append(NEWLINE);
}
return true;
}
/**
* @param outputVarsAdditionsList list to add needed build output variables to
* @param buffer buffer to add rules to
*/
private void generateRulesForConsumers(ITool generatingTool, List<String> outputVarsAdditionsList,
StringBuffer buffer) {
// Generate a build rule for any tool that consumes the output of this tool
ToolInfoHolder h = (ToolInfoHolder) toolInfos.getValue();
ITool[] buildTools = h.buildTools;
boolean[] buildToolsUsed = h.buildToolsUsed;
IOutputType[] outTypes = generatingTool.getOutputTypes();
for (IOutputType outType : outTypes) {
String[] outExts = outType.getOutputExtensions(generatingTool);
String outVariable = outType.getBuildVariable();
if (outExts != null) {
for (String outExt : outExts) {
for (int k = 0; k < buildTools.length; k++) {
ITool tool = buildTools[k];
if (!buildToolsUsed[k]) {
// Also has to match build variables if specified
IInputType inType = tool.getInputType(outExt);
if (inType != null) {
String inVariable = inType.getBuildVariable();
if ((outVariable == null && inVariable == null) || (outVariable != null
&& inVariable != null && outVariable.equals(inVariable))) {
if (addRuleForTool(buildTools[k], buffer, false, null, null,
outputVarsAdditionsList, null, false)) {
buildToolsUsed[k] = true;
// Look for tools that consume the output
generateRulesForConsumers(buildTools[k], outputVarsAdditionsList, buffer);
}
}
}
}
}
}
}
}
}
protected boolean getToolInputsOutputs(ITool tool, Vector<String> inputs, Vector<String> dependencies,
Vector<String> outputs, Vector<String> enumeratedPrimaryOutputs, Vector<String> enumeratedSecondaryOutputs,
Vector<String> outputVariables, Vector<String> additionalTargets, boolean bTargetTool,
Vector<String> managedProjectOutputs) {
ToolInfoHolder h = (ToolInfoHolder) toolInfos.getValue();
ITool[] buildTools = h.buildTools;
ManagedBuildGnuToolInfo[] gnuToolInfos = h.gnuToolInfos;
// Get the information regarding the tool's inputs and outputs from the objects
// created by calculateToolInputsOutputs
IManagedBuildGnuToolInfo toolInfo = null;
for (int i = 0; i < buildTools.length; i++) {
if (tool == buildTools[i]) {
toolInfo = gnuToolInfos[i];
break;
}
}
if (toolInfo == null)
return false;
// Populate the output Vectors
inputs.addAll(toolInfo.getCommandInputs());
outputs.addAll(toolInfo.getCommandOutputs());
enumeratedPrimaryOutputs.addAll(toolInfo.getEnumeratedPrimaryOutputs());
enumeratedSecondaryOutputs.addAll(toolInfo.getEnumeratedSecondaryOutputs());
outputVariables.addAll(toolInfo.getOutputVariables());
Vector<String> unprocessedDependencies = toolInfo.getCommandDependencies();
for (String path : unprocessedDependencies) {
dependencies.add(ensurePathIsGNUMakeTargetRuleCompatibleSyntax(path));
}
additionalTargets.addAll(toolInfo.getAdditionalTargets());
if (bTargetTool && managedProjectOutputs != null) {
for (String output : managedProjectOutputs) {
dependencies.add(output);
}
}
return true;
}
protected Vector<String> calculateSecondaryOutputs(IOutputType[] secondaryOutputs) {
ToolInfoHolder h = (ToolInfoHolder) toolInfos.getValue();
ITool[] buildTools = h.buildTools;
Vector<String> buildVars = new Vector<>();
for (int i = 0; i < buildTools.length; i++) {
// Add the specified output build variables
IOutputType[] outTypes = buildTools[i].getOutputTypes();
if (outTypes != null && outTypes.length > 0) {
for (int j = 0; j < outTypes.length; j++) {
IOutputType outType = outTypes[j];
// Is this one of the secondary outputs?
// Look for an outputType with this ID, or one with a superclass with this id
thisType: for (int k = 0; k < secondaryOutputs.length; k++) {
IOutputType matchType = outType;
do {
if (matchType.getId().equals(secondaryOutputs[k].getId())) {
buildVars.add(outType.getBuildVariable());
break thisType;
}
matchType = matchType.getSuperClass();
} while (matchType != null);
}
}
}
}
return buildVars;
}
protected boolean isSecondaryOutputVar(ToolInfoHolder h, IOutputType[] secondaryOutputs, String varName) {
ITool[] buildTools = h.buildTools;
for (ITool buildTool : buildTools) {
// Add the specified output build variables
IOutputType[] outTypes = buildTool.getOutputTypes();
if (outTypes != null && outTypes.length > 0) {
for (IOutputType outType : outTypes) {
// Is this one of the secondary outputs?
// Look for an outputType with this ID, or one with a superclass with this id
for (IOutputType secondaryOutput : secondaryOutputs) {
IOutputType matchType = outType;
do {
if (matchType.getId().equals(secondaryOutput.getId())) {
if (outType.getBuildVariable().equals(varName)) {
return true;
}
}
matchType = matchType.getSuperClass();
} while (matchType != null);
}
}
}
}
return false;
}
/*************************************************************************
* S O U R C E S (sources.mk) M A K E F I L E M E T H O D S
************************************************************************/
private StringBuffer addSubdirectories() {
StringBuffer buffer = new StringBuffer();
// Add the comment
buffer.append(COMMENT_SYMBOL).append(WHITESPACE).append(ManagedMakeMessages.getResourceString(MOD_LIST))
.append(NEWLINE);
buffer.append("SUBDIRS := ").append(LINEBREAK); //$NON-NLS-1$
// Get all the module names
for (IResource container : getSubdirList()) {
updateMonitor(ManagedMakeMessages.getFormattedString("MakefileGenerator.message.adding.source.folder", //$NON-NLS-1$
container.getFullPath().toString()));
// Check the special case where the module is the project root
if (container.getFullPath() == project.getFullPath()) {
buffer.append(DOT).append(WHITESPACE).append(LINEBREAK);
} else {
IPath path = container.getProjectRelativePath();
buffer.append(escapeWhitespaces(path.toString())).append(WHITESPACE).append(LINEBREAK);
}
}
buffer.append(NEWLINE);
return buffer;
}
/*************************************************************************
* F R A G M E N T (subdir.mk) M A K E F I L E M E T H O D S
************************************************************************/
/**
* Returns a <code>StringBuffer</code> containing the comment(s)
* for a fragment makefile (subdir.mk).
*/
protected StringBuffer addFragmentMakefileHeader() {
return addDefaultHeader();
}
/**
* Returns a <code>StringBuffer</code> containing makefile text for all of the sources
* contributed by a container (project directory/subdirectory) to the fragement makefile
*
* @param module project resource directory/subdirectory
* @return StringBuffer generated text for the fragement makefile
*/
protected StringBuffer addSources(IContainer module) throws CoreException {
// Calculate the new directory relative to the build output
IPath moduleRelativePath = module.getProjectRelativePath();
String relativePath = moduleRelativePath.toString();
relativePath += relativePath.length() == 0 ? "" : SEPARATOR; //$NON-NLS-1$
// For build macros in the configuration, create a map which will map them
// to a string which holds its list of sources.
LinkedHashMap<String, String> buildVarToRuleStringMap = new LinkedHashMap<>();
// Add statements that add the source files in this folder,
// and generated source files, and generated dependency files
// to the build macros
for (Entry<String, List<IPath>> entry : buildSrcVars.entrySet()) {
String macroName = entry.getKey();
addMacroAdditionPrefix(buildVarToRuleStringMap, macroName, null, false);
}
for (Entry<String, List<IPath>> entry : buildOutVars.entrySet()) {
String macroName = entry.getKey();
addMacroAdditionPrefix(buildVarToRuleStringMap, macroName, "./" + relativePath, false); //$NON-NLS-1$
}
// String buffers
StringBuffer buffer = new StringBuffer(); // Return buffer
StringBuffer ruleBuffer = new StringBuffer(
COMMENT_SYMBOL + WHITESPACE + ManagedMakeMessages.getResourceString(MOD_RULES) + NEWLINE);
// Visit the resources in this folder and add each one to a sources macro, and generate a build rule, if appropriate
IResource[] resources = module.members();
IResourceInfo rcInfo;
IFolder folder = project.getFolder(computeTopBuildDir(config.getName()));
for (IResource resource : resources) {
if (resource.getType() == IResource.FILE) {
// Check whether this resource is excluded from build
IPath rcProjRelPath = resource.getProjectRelativePath();
if (!isSource(rcProjRelPath))
continue;
rcInfo = config.getResourceInfo(rcProjRelPath, false);
// if( (rcInfo.isExcluded()) )
// continue;
addFragmentMakefileEntriesForSource(buildVarToRuleStringMap, ruleBuffer, folder, relativePath, resource,
getPathForResource(resource), rcInfo, null, false);
}
}
// Write out the macro addition entries to the buffer
buffer.append(writeAdditionMacros(buildVarToRuleStringMap));
return buffer.append(ruleBuffer).append(NEWLINE);
}
/* (non-Javadoc
* Adds the entries for a particular source file to the fragment makefile
*
* @param buildVarToRuleStringMap map of build variable names to the list of files assigned to the variable
* @param ruleBuffer buffer to add generated nmakefile text to
* @param folder the top level build output directory
* @param relativePath build output directory relative path of the current output directory
* @param resource the source file for this invocation of the tool - this may be null for a generated output
* @param sourceLocation the full path of the source
* @param resConfig the IResourceConfiguration associated with this file or null
* @param varName the build variable to add this invocation's outputs to
* if <code>null</code>, use the file extension to find the name
* @param generatedSource if <code>true</code>, this file was generated by another tool in the tool-chain
*/
protected void addFragmentMakefileEntriesForSource(LinkedHashMap<String, String> buildVarToRuleStringMap,
StringBuffer ruleBuffer, IFolder folder, String relativePath, IResource resource, IPath sourceLocation,
IResourceInfo rcInfo, String varName, boolean generatedSource) {
// Determine which tool, if any, builds files with this extension
String ext = sourceLocation.getFileExtension();
ITool tool = null;
//TODO: remove
// IResourceConfiguration resConfig = null;
// if(rcInfo instanceof IFileInfo){
// resConfig = (IFileInfo)rcInfo;
// }
//end remove
// Use the tool from the resource configuration if there is one
if (rcInfo instanceof IFileInfo) {
IFileInfo fi = (IFileInfo) rcInfo;
ITool[] tools = fi.getToolsToInvoke();
if (tools != null && tools.length > 0) {
tool = tools[0];
// if(!tool.getCustomBuildStep())
addToBuildVar(buildVarToRuleStringMap, ext, varName, relativePath, sourceLocation, generatedSource);
}
}
ToolInfoHolder h = getToolInfo(rcInfo.getPath());
ITool buildTools[] = h.buildTools;
// if(tool == null){
// for (int j=0; j<buildTools.length; j++) {
// if (buildTools[j].buildsFileType(ext)) {
// if (tool == null) {
// tool = buildTools[j];
// }
// addToBuildVar(buildVarToRuleStringMap, ext, varName, relativePath, sourceLocation, generatedSource);
// break;
// }
// }
// }
//
// if(tool == null && rcInfo.getPath().segmentCount() != 0){
if (tool == null) {
h = getToolInfo(Path.EMPTY);
buildTools = h.buildTools;
for (ITool buildTool : buildTools) {
if (buildTool.buildsFileType(ext)) {
tool = buildTool;
addToBuildVar(buildVarToRuleStringMap, ext, varName, relativePath, sourceLocation, generatedSource);
break;
}
}
}
if (tool != null) {
// Generate the rule to build this source file
IInputType primaryInputType = tool.getPrimaryInputType();
IInputType inputType = tool.getInputType(ext);
if ((primaryInputType != null && !primaryInputType.getMultipleOfType())
|| (inputType == null && tool != config.calculateTargetTool())) {
// Try to add the rule for the file
Vector<IPath> generatedOutputs = new Vector<>(); // IPath's - build directory relative
Vector<IPath> generatedDepFiles = new Vector<>(); // IPath's - build directory relative or absolute
addRuleForSource(relativePath, ruleBuffer, resource, sourceLocation, rcInfo, generatedSource,
generatedDepFiles, generatedOutputs);
// If the rule generates a dependency file(s), add the file(s) to the variable
if (generatedDepFiles.size() > 0) {
for (int k = 0; k < generatedDepFiles.size(); k++) {
IPath generatedDepFile = generatedDepFiles.get(k);
addMacroAdditionFile(buildVarToRuleStringMap, getDepMacroName(ext).toString(),
(generatedDepFile.isAbsolute() ? "" : "./") + //$NON-NLS-1$ //$NON-NLS-2$
generatedDepFile.toString());
}
}
// If the generated outputs of this tool are input to another tool,
// 1. add the output to the appropriate macro
// 2. If the tool does not have multipleOfType input, generate the rule.
IOutputType outType = tool.getPrimaryOutputType();
String buildVariable = null;
if (outType != null) {
if (tool.getCustomBuildStep()) {
// TODO: This is somewhat of a hack since a custom build step
// tool does not currently define a build variable
if (generatedOutputs.size() > 0) {
IPath firstOutput = generatedOutputs.get(0);
String firstExt = firstOutput.getFileExtension();
ToolInfoHolder tmpH = getFolderToolInfo(rcInfo.getPath());
ITool[] tmpBuildTools = tmpH.buildTools;
for (ITool tmpBuildTool : tmpBuildTools) {
if (tmpBuildTool.buildsFileType(firstExt)) {
String bV = tmpBuildTool.getPrimaryInputType().getBuildVariable();
if (bV.length() > 0) {
buildVariable = bV;
break;
}
}
}
}
} else {
buildVariable = outType.getBuildVariable();
}
} else {
// For support of pre-CDT 3.0 integrations.
buildVariable = OBJS_MACRO;
}
for (int k = 0; k < generatedOutputs.size(); k++) {
IPath generatedOutput;
IResource generateOutputResource;
if (generatedOutputs.get(k).isAbsolute()) {
// TODO: Should we use relative paths when possible (e.g., see MbsMacroSupplier.calculateRelPath)
generatedOutput = generatedOutputs.get(k);
// If this file has an absolute path, then the generateOutputResource will not be correct
// because the file is not under the project. We use this resource in the calls to the dependency generator
generateOutputResource = project.getFile(generatedOutput);
} else {
generatedOutput = getPathForResource(project).append(getBuildWorkingDir())
.append(generatedOutputs.get(k));
generateOutputResource = project.getFile(getBuildWorkingDir().append(generatedOutputs.get(k)));
}
IResourceInfo nextRcInfo;
if (rcInfo instanceof IFileInfo) {
nextRcInfo = config.getResourceInfo(rcInfo.getPath().removeLastSegments(1), false);
} else {
nextRcInfo = rcInfo;
}
addFragmentMakefileEntriesForSource(buildVarToRuleStringMap, ruleBuffer, folder, relativePath,
generateOutputResource, generatedOutput, nextRcInfo, buildVariable, true);
}
}
} else {
// If this is a secondary input, add it to build vars
if (varName == null) {
for (ITool buildTool : buildTools) {
if (buildTool.isInputFileType(ext)) {
addToBuildVar(buildVarToRuleStringMap, ext, varName, relativePath, sourceLocation,
generatedSource);
break;
}
}
}
// If this generated output is identified as a secondary output, add the file to the build variable
else {
IOutputType[] secondaryOutputs = config.getToolChain().getSecondaryOutputs();
if (secondaryOutputs.length > 0) {
if (isSecondaryOutputVar(h, secondaryOutputs, varName)) {
addMacroAdditionFile(buildVarToRuleStringMap, varName, relativePath, sourceLocation,
generatedSource);
}
}
}
}
}
/**
* Gets a path for a resource by extracting the Path field from its
* location URI.
* @return IPath
* @since 6.0
*/
protected IPath getPathForResource(IResource resource) {
return new Path(resource.getLocationURI().getPath());
}
/**
* Adds the source file to the appropriate build variable
*
* @param buildVarToRuleStringMap map of build variable names to the list of files assigned to the variable
* @param ext the file extension of the file
* @param varName the build variable to add this invocation's outputs to
* if <code>null</code>, use the file extension to find the name
* @param relativePath build output directory relative path of the current output directory
* @param sourceLocation the full path of the source
* @param generatedSource if <code>true</code>, this file was generated by another tool in the tool-chain
*/
protected void addToBuildVar(LinkedHashMap<String, String> buildVarToRuleStringMap, String ext, String varName,
String relativePath, IPath sourceLocation, boolean generatedSource) {
List<IPath> varList = null;
if (varName == null) {
// Get the proper source build variable based upon the extension
varName = getSourceMacroName(ext).toString();
varList = buildSrcVars.get(varName);
} else {
varList = buildOutVars.get(varName);
}
// Add the resource to the list of all resources associated with a variable.
// Do not allow duplicates - there is no reason to and it can be 'bad' -
// e.g., having the same object in the OBJS list can cause duplicate symbol errors from the linker
if ((varList != null) && !(varList.contains(sourceLocation))) {
// Since we don't know how these files will be used, we store them using a "location"
// path rather than a relative path
varList.add(sourceLocation);
if (!buildVarToRuleStringMap.containsKey(varName)) {
// TODO - is this an error?
} else {
// Add the resource name to the makefile line that adds resources to the build variable
addMacroAdditionFile(buildVarToRuleStringMap, varName, relativePath, sourceLocation, generatedSource);
}
}
}
private IManagedCommandLineInfo generateToolCommandLineInfo(ITool tool, String sourceExtension, String[] flags,
String outputFlag, String outputPrefix, String outputName, String[] inputResources, IPath inputLocation,
IPath outputLocation) {
String cmd = tool.getToolCommand();
//try to resolve the build macros in the tool command
try {
String resolvedCommand = null;
if ((inputLocation != null && inputLocation.toString().indexOf(" ") != -1) || //$NON-NLS-1$
(outputLocation != null && outputLocation.toString().indexOf(" ") != -1)) //$NON-NLS-1$
{
resolvedCommand = ManagedBuildManager.getBuildMacroProvider().resolveValue(cmd, "", //$NON-NLS-1$
" ", //$NON-NLS-1$
IBuildMacroProvider.CONTEXT_FILE,
new FileContextData(inputLocation, outputLocation, null, tool));
}
else {
resolvedCommand = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(cmd, "", //$NON-NLS-1$
" ", //$NON-NLS-1$
IBuildMacroProvider.CONTEXT_FILE,
new FileContextData(inputLocation, outputLocation, null, tool));
}
if ((resolvedCommand = resolvedCommand.trim()).length() > 0)
cmd = resolvedCommand;
} catch (BuildMacroException e) {
}
IManagedCommandLineGenerator gen = tool.getCommandLineGenerator();
return gen.generateCommandLineInfo(tool, cmd, flags, outputFlag, outputPrefix, outputName, inputResources,
tool.getCommandLinePattern());
}
/**
* Create a rule for this source file. We create a pattern rule if possible.
*
* This is an example of a pattern rule:
*
* <relative_path>/%.<outputExtension>: ../<relative_path>/%.<inputExtension>
* @echo Building file: $<
* @echo Invoking tool xxx
* @echo <tool> <flags> <output_flag><output_prefix>$@ $<
* @<tool> <flags> <output_flag><output_prefix>$@ $< && \
* echo -n $(@:%.o=%.d) ' <relative_path>/' >> $(@:%.o=%.d) && \
* <tool> -P -MM -MG <flags> $< >> $(@:%.o=%.d)
* @echo Finished building: $<
* @echo ' '
*
* Note that the macros all come from the build model and are
* resolved to a real command before writing to the module
* makefile, so a real command might look something like:
* source1/%.o: ../source1/%.cpp
* @echo Building file: $<
* @echo Invoking tool xxx
* @echo g++ -g -O2 -c -I/cygdrive/c/eclipse/workspace/Project/headers -o$@ $<
* @g++ -g -O2 -c -I/cygdrive/c/eclipse/workspace/Project/headers -o$@ $< && \
* echo -n $(@:%.o=%.d) ' source1/' >> $(@:%.o=%.d) && \
* g++ -P -MM -MG -g -O2 -c -I/cygdrive/c/eclipse/workspace/Project/headers $< >> $(@:%.o=%.d)
* @echo Finished building: $<
* @echo ' '
*
* @param relativePath top build output directory relative path of the current output directory
* @param buffer buffer to populate with the build rule
* @param resource the source file for this invocation of the tool
* @param sourceLocation the full path of the source
* @param rcInfo the IResourceInfo associated with this file or null
* @param generatedSource <code>true</code> if the resource is a generated output
* @param enumeratedOutputs vector of the filenames that are the output of this rule
*/
protected void addRuleForSource(String relativePath, StringBuffer buffer, IResource resource, IPath sourceLocation,
IResourceInfo rcInfo, boolean generatedSource, Vector<IPath> generatedDepFiles,
Vector<IPath> enumeratedOutputs) {
String fileName = sourceLocation.removeFileExtension().lastSegment();
String inputExtension = sourceLocation.getFileExtension();
String outputExtension = null;
ITool tool = null;
if (rcInfo instanceof IFileInfo) {
IFileInfo fi = (IFileInfo) rcInfo;
ITool[] tools = fi.getToolsToInvoke();
if (tools != null && tools.length > 0) {
tool = tools[0];
}
} else {
IFolderInfo foInfo = (IFolderInfo) rcInfo;
tool = foInfo.getToolFromInputExtension(inputExtension);
}
ToolInfoHolder h = getToolInfo(rcInfo.getPath());
if (tool != null)
outputExtension = tool.getOutputExtension(inputExtension);
if (outputExtension == null)
outputExtension = EMPTY_STRING;
// Get the dependency generator information for this tool and file extension
IManagedDependencyGenerator oldDepGen = null; // This interface is deprecated but still supported
IManagedDependencyGenerator2 depGen = null; // This is the recommended interface
IManagedDependencyInfo depInfo = null;
IManagedDependencyCommands depCommands = null;
IManagedDependencyPreBuild depPreBuild = null;
IPath[] depFiles = null;
boolean doDepGen = false;
{
IManagedDependencyGeneratorType t = null;
if (tool != null)
t = tool.getDependencyGeneratorForExtension(inputExtension);
if (t != null) {
int calcType = t.getCalculatorType();
if (calcType <= IManagedDependencyGeneratorType.TYPE_OLD_TYPE_LIMIT) {
oldDepGen = (IManagedDependencyGenerator) t;
doDepGen = (calcType == IManagedDependencyGeneratorType.TYPE_COMMAND);
if (doDepGen) {
IPath depFile = Path.fromOSString(relativePath + fileName + DOT + DEP_EXT);
getDependencyMakefiles(h).add(depFile);
generatedDepFiles.add(depFile);
}
} else {
depGen = (IManagedDependencyGenerator2) t;
doDepGen = (calcType == IManagedDependencyGeneratorType.TYPE_BUILD_COMMANDS);
IBuildObject buildContext = rcInfo;//(resConfig != null) ? (IBuildObject)resConfig : (IBuildObject)config;
depInfo = depGen.getDependencySourceInfo(resource.getProjectRelativePath(), resource, buildContext,
tool, getBuildWorkingDir());
if (calcType == IManagedDependencyGeneratorType.TYPE_BUILD_COMMANDS) {
depCommands = (IManagedDependencyCommands) depInfo;
depFiles = depCommands.getDependencyFiles();
} else if (calcType == IManagedDependencyGeneratorType.TYPE_PREBUILD_COMMANDS) {
depPreBuild = (IManagedDependencyPreBuild) depInfo;
depFiles = depPreBuild.getDependencyFiles();
}
if (depFiles != null) {
for (IPath depFile : depFiles) {
getDependencyMakefiles(h).add(depFile);
generatedDepFiles.add(depFile);
}
}
}
}
}
// Figure out the output paths
String optDotExt = EMPTY_STRING;
if (outputExtension.length() > 0)
optDotExt = DOT + outputExtension;
Vector<IPath> ruleOutputs = new Vector<>();
Vector<IPath> enumeratedPrimaryOutputs = new Vector<>(); // IPaths relative to the top build directory
Vector<IPath> enumeratedSecondaryOutputs = new Vector<>(); // IPaths relative to the top build directory
calculateOutputsForSource(tool, relativePath, resource, sourceLocation, ruleOutputs, enumeratedPrimaryOutputs,
enumeratedSecondaryOutputs);
enumeratedOutputs.addAll(enumeratedPrimaryOutputs);
enumeratedOutputs.addAll(enumeratedSecondaryOutputs);
String primaryOutputName = null;
if (enumeratedPrimaryOutputs.size() > 0) {
primaryOutputName = escapeWhitespaces(enumeratedPrimaryOutputs.get(0).toString());
} else {
primaryOutputName = escapeWhitespaces(relativePath + fileName + optDotExt);
}
String otherPrimaryOutputs = EMPTY_STRING;
for (int i = 1; i < enumeratedPrimaryOutputs.size(); i++) { // Starting with 1 is intentional
otherPrimaryOutputs += WHITESPACE + escapeWhitespaces(enumeratedPrimaryOutputs.get(i).toString());
}
// Output file location needed for the file-specific build macros
IPath outputLocation = Path.fromOSString(primaryOutputName);
if (!outputLocation.isAbsolute()) {
outputLocation = getPathForResource(project).append(getBuildWorkingDir()).append(primaryOutputName);
}
// A separate rule is needed for the resource in the case where explicit file-specific macros
// are referenced, or if the resource contains special characters in its path (e.g., whitespace)
/* fix for 137674
*
* We only need an explicit rule if one of the following is true:
* - The resource is linked, and its full path to its real location contains special characters
* - The resource is not linked, but its project relative path contains special characters
*/
boolean resourceNameRequiresExplicitRule = (resource.isLinked()
&& containsSpecialCharacters(sourceLocation.toString()))
|| (!resource.isLinked() && containsSpecialCharacters(resource.getProjectRelativePath().toString()));
boolean needExplicitRuleForFile = resourceNameRequiresExplicitRule
|| BuildMacroProvider.getReferencedExplitFileMacros(tool).length > 0
|| BuildMacroProvider.getReferencedExplitFileMacros(tool.getToolCommand(),
IBuildMacroProvider.CONTEXT_FILE,
new FileContextData(sourceLocation, outputLocation, null, tool)).length > 0;
// Get and resolve the command
String cmd = tool.getToolCommand();
try {
String resolvedCommand = null;
if (!needExplicitRuleForFile) {
resolvedCommand = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(cmd,
EMPTY_STRING, WHITESPACE, IBuildMacroProvider.CONTEXT_FILE,
new FileContextData(sourceLocation, outputLocation, null, tool));
} else {
// if we need an explicit rule then don't use any builder
// variables, resolve everything
// to explicit strings
resolvedCommand = ManagedBuildManager.getBuildMacroProvider().resolveValue(cmd, EMPTY_STRING,
WHITESPACE, IBuildMacroProvider.CONTEXT_FILE,
new FileContextData(sourceLocation, outputLocation, null, tool));
}
if ((resolvedCommand = resolvedCommand.trim()).length() > 0)
cmd = resolvedCommand;
} catch (BuildMacroException e) {
}
String defaultOutputName = EMPTY_STRING;
String primaryDependencyName = EMPTY_STRING;
String patternPrimaryDependencyName = EMPTY_STRING;
String home = (generatedSource) ? DOT : reachProjectRoot();
String resourcePath = null;
boolean patternRule = true;
boolean isItLinked = false;
if (resource.isLinked(IResource.CHECK_ANCESTORS)) {
// it IS linked, so use the actual location
isItLinked = true;
resourcePath = sourceLocation.toString();
// Need a hardcoded rule, not a pattern rule, as a linked file
// can reside in any path
defaultOutputName = escapeWhitespaces(relativePath + fileName + optDotExt);
primaryDependencyName = escapeWhitespaces(resourcePath);
patternRule = false;
} else {
// Use the relative path (not really needed to store per se but in the future someone may want this)
resourcePath = relativePath;
// The rule and command to add to the makefile
if (rcInfo instanceof IFileInfo || needExplicitRuleForFile) {
// Need a hardcoded rule, not a pattern rule
defaultOutputName = escapeWhitespaces(resourcePath + fileName + optDotExt);
patternRule = false;
} else {
defaultOutputName = relativePath + WILDCARD + optDotExt;
}
primaryDependencyName = escapeWhitespaces(
home + SEPARATOR + resourcePath + fileName + DOT + inputExtension);
patternPrimaryDependencyName = home + SEPARATOR + resourcePath + WILDCARD + DOT + inputExtension;
} // end fix for PR 70491
// If the tool specifies a dependency calculator of TYPE_BUILD_COMMANDS, ask whether
// the dependency commands are "generic" (i.e., we can use a pattern rule)
boolean needExplicitDependencyCommands = false;
if (depCommands != null) {
needExplicitDependencyCommands = !depCommands.areCommandsGeneric();
}
// If we still think that we are using a pattern rule, check a few more things
if (patternRule) {
patternRule = false;
// Make sure that at least one of the rule outputs contains a %.
for (int i = 0; i < ruleOutputs.size(); i++) {
String ruleOutput = ruleOutputs.get(i).toString();
if (ruleOutput.indexOf('%') >= 0) {
patternRule = true;
break;
}
}
if (patternRule) {
patternRule = !needExplicitDependencyCommands;
}
}
// Begin building the rule for this source file
String buildRule = EMPTY_STRING;
if (patternRule) {
if (ruleOutputs.size() == 0) {
buildRule += defaultOutputName;
} else {
boolean first = true;
for (int i = 0; i < ruleOutputs.size(); i++) {
String ruleOutput = ruleOutputs.get(i).toString();
if (ruleOutput.indexOf('%') >= 0) {
if (first) {
first = false;
} else {
buildRule += WHITESPACE;
}
buildRule += ruleOutput;
}
}
}
} else {
buildRule += primaryOutputName;
}
String buildRuleDependencies = primaryDependencyName;
String patternBuildRuleDependencies = patternPrimaryDependencyName;
// Other additional inputs
// Get any additional dependencies specified for the tool in other InputType elements and AdditionalInput elements
IPath[] addlDepPaths = tool.getAdditionalDependencies();
for (IPath addlDepPath : addlDepPaths) {
// Translate the path from project relative to build directory relative
IPath addlPath = addlDepPath;
if (!(addlPath.toString().startsWith("$("))) { //$NON-NLS-1$
if (!addlPath.isAbsolute()) {
IPath tempPath = project.getLocation().append(new Path(ensureUnquoted(addlPath.toString())));
if (tempPath != null) {
addlPath = ManagedBuildManager.calculateRelativePath(getTopBuildDir(), tempPath);
}
}
}
String suitablePath = ensurePathIsGNUMakeTargetRuleCompatibleSyntax(addlPath.toString());
buildRuleDependencies += WHITESPACE + suitablePath;
patternBuildRuleDependencies += WHITESPACE + suitablePath;
}
buildRule += COLON + WHITESPACE + (patternRule ? patternBuildRuleDependencies : buildRuleDependencies);
// No duplicates in a makefile. If we already have this rule, don't add it or the commands to build the file
if (getRuleList().contains(buildRule)) {
// TODO: Should we assert that this is a pattern rule?
} else {
getRuleList().add(buildRule);
// Echo starting message
buffer.append(buildRule).append(NEWLINE);
buffer.append(TAB).append(AT).append(escapedEcho(MESSAGE_START_FILE + WHITESPACE + IN_MACRO));
buffer.append(TAB).append(AT).append(escapedEcho(tool.getAnnouncement()));
// If the tool specifies a dependency calculator of TYPE_BUILD_COMMANDS, ask whether
// there are any pre-tool commands.
if (depCommands != null) {
String[] preToolCommands = depCommands.getPreToolDependencyCommands();
if (preToolCommands != null && preToolCommands.length > 0) {
for (String preCmd : preToolCommands) {
try {
String resolvedCommand;
IBuildMacroProvider provider = ManagedBuildManager.getBuildMacroProvider();
if (!needExplicitRuleForFile) {
resolvedCommand = provider.resolveValueToMakefileFormat(preCmd, EMPTY_STRING,
WHITESPACE, IBuildMacroProvider.CONTEXT_FILE,
new FileContextData(sourceLocation, outputLocation, null, tool));
} else {
// if we need an explicit rule then don't use any builder
// variables, resolve everything to explicit strings
resolvedCommand = provider.resolveValue(preCmd, EMPTY_STRING, WHITESPACE,
IBuildMacroProvider.CONTEXT_FILE,
new FileContextData(sourceLocation, outputLocation, null, tool));
}
if (resolvedCommand != null)
buffer.append(resolvedCommand).append(NEWLINE);
} catch (BuildMacroException e) {
}
}
}
}
// Generate the command line
Vector<String> inputs = new Vector<>();
inputs.add(IN_MACRO);
// Other additional inputs
// Get any additional dependencies specified for the tool in other InputType elements and AdditionalInput elements
IPath[] addlInputPaths = getAdditionalResourcesForSource(tool);
for (IPath addlInputPath : addlInputPaths) {
// Translate the path from project relative to build directory relative
IPath addlPath = addlInputPath;
if (!(addlPath.toString().startsWith("$("))) { //$NON-NLS-1$
if (!addlPath.isAbsolute()) {
IPath tempPath = getPathForResource(project).append(addlPath);
if (tempPath != null) {
addlPath = ManagedBuildManager.calculateRelativePath(getTopBuildDir(), tempPath);
}
}
}
inputs.add(addlPath.toString());
}
String[] inputStrings = inputs.toArray(new String[inputs.size()]);
String[] flags = null;
// Get the tool command line options
try {
flags = tool.getToolCommandFlags(sourceLocation, outputLocation);
} catch (BuildException ex) {
// TODO add some routines to catch this
flags = EMPTY_STRING_ARRAY;
}
// If we have a TYPE_BUILD_COMMANDS dependency generator, determine if there are any options that
// it wants added to the command line
if (depCommands != null) {
flags = addDependencyOptions(depCommands, flags);
}
IManagedCommandLineInfo cmdLInfo = null;
String outflag = null;
String outputPrefix = null;
if (rcInfo instanceof IFileInfo || needExplicitRuleForFile || needExplicitDependencyCommands) {
outflag = tool.getOutputFlag();
outputPrefix = tool.getOutputPrefix();
// Call the command line generator
IManagedCommandLineGenerator cmdLGen = tool.getCommandLineGenerator();
cmdLInfo = cmdLGen.generateCommandLineInfo(tool, cmd, flags, outflag, outputPrefix,
OUT_MACRO + otherPrimaryOutputs, inputStrings, tool.getCommandLinePattern());
} else {
outflag = tool.getOutputFlag();//config.getOutputFlag(outputExtension);
outputPrefix = tool.getOutputPrefix();//config.getOutputPrefix(outputExtension);
// Call the command line generator
cmdLInfo = generateToolCommandLineInfo(tool, inputExtension, flags, outflag, outputPrefix,
OUT_MACRO + otherPrimaryOutputs, inputStrings, sourceLocation, outputLocation);
}
// The command to build
String buildCmd;
if (cmdLInfo != null) {
buildCmd = cmdLInfo.getCommandLine();
} else {
StringBuffer buildFlags = new StringBuffer();
for (String flag : flags) {
if (flag != null) {
buildFlags.append(flag).append(WHITESPACE);
}
}
buildCmd = cmd + WHITESPACE + buildFlags.toString().trim() + WHITESPACE + outflag + WHITESPACE
+ outputPrefix + OUT_MACRO + otherPrimaryOutputs + WHITESPACE + IN_MACRO;
}
// resolve any remaining macros in the command after it has been
// generated
try {
String resolvedCommand;
IBuildMacroProvider provider = ManagedBuildManager.getBuildMacroProvider();
if (!needExplicitRuleForFile) {
resolvedCommand = provider.resolveValueToMakefileFormat(buildCmd, EMPTY_STRING, WHITESPACE,
IBuildMacroProvider.CONTEXT_FILE,
new FileContextData(sourceLocation, outputLocation, null, tool));
} else {
// if we need an explicit rule then don't use any builder
// variables, resolve everything to explicit strings
resolvedCommand = provider.resolveValue(buildCmd, EMPTY_STRING, WHITESPACE,
IBuildMacroProvider.CONTEXT_FILE,
new FileContextData(sourceLocation, outputLocation, null, tool));
}
if ((resolvedCommand = resolvedCommand.trim()).length() > 0)
buildCmd = resolvedCommand;
} catch (BuildMacroException e) {
}
//buffer.append(TAB).append(AT).append(escapedEcho(buildCmd));
//buffer.append(TAB).append(AT).append(buildCmd);
buffer.append(TAB).append(buildCmd);
// Determine if there are any dependencies to calculate
if (doDepGen) {
// Get the dependency rule out of the generator
String[] depCmds = null;
if (oldDepGen != null) {
depCmds = new String[1];
depCmds[0] = oldDepGen.getDependencyCommand(resource, ManagedBuildManager.getBuildInfo(project));
} else {
if (depCommands != null) {
depCmds = depCommands.getPostToolDependencyCommands();
}
}
if (depCmds != null) {
for (String depCmd : depCmds) {
// Resolve any macros in the dep command after it has been generated.
// Note: do not trim the result because it will strip out necessary tab characters.
buffer.append(WHITESPACE).append(LOGICAL_AND).append(WHITESPACE).append(LINEBREAK);
try {
if (!needExplicitRuleForFile) {
depCmd = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(
depCmd, EMPTY_STRING, WHITESPACE, IBuildMacroProvider.CONTEXT_FILE,
new FileContextData(sourceLocation, outputLocation, null, tool));
}
else {
depCmd = ManagedBuildManager.getBuildMacroProvider().resolveValue(depCmd, EMPTY_STRING,
WHITESPACE, IBuildMacroProvider.CONTEXT_FILE,
new FileContextData(sourceLocation, outputLocation, null, tool));
}
} catch (BuildMacroException e) {
}
buffer.append(depCmd);
}
}
}
// Echo finished message
buffer.append(NEWLINE);
buffer.append(TAB).append(AT).append(escapedEcho(MESSAGE_FINISH_FILE + WHITESPACE + IN_MACRO));
buffer.append(TAB).append(AT).append(ECHO_BLANK_LINE).append(NEWLINE);
}
// Determine if there are calculated dependencies
IPath[] addlDeps = null; // IPath's that are relative to the build directory
IPath[] addlTargets = null; // IPath's that are relative to the build directory
String calculatedDependencies = null;
boolean addedDepLines = false;
String depLine;
if (oldDepGen != null && oldDepGen.getCalculatorType() != IManagedDependencyGeneratorType.TYPE_COMMAND) {
addlDeps = oldCalculateDependenciesForSource(oldDepGen, tool, relativePath, resource);
} else {
if (depGen != null && depGen.getCalculatorType() == IManagedDependencyGeneratorType.TYPE_CUSTOM) {
if (depInfo instanceof IManagedDependencyCalculator) {
IManagedDependencyCalculator depCalculator = (IManagedDependencyCalculator) depInfo;
addlDeps = calculateDependenciesForSource(depCalculator);
addlTargets = depCalculator.getAdditionalTargets();
}
}
}
if (addlDeps != null && addlDeps.length > 0) {
calculatedDependencies = ""; //$NON-NLS-1$
for (IPath addlDep : addlDeps) {
calculatedDependencies += WHITESPACE + escapeWhitespaces(addlDep.toString());
}
}
if (calculatedDependencies != null) {
depLine = primaryOutputName + COLON + calculatedDependencies + NEWLINE;
if (!getDepLineList().contains(depLine)) {
getDepLineList().add(depLine);
addedDepLines = true;
buffer.append(depLine);
}
}
// Add any additional outputs here using dependency lines
Vector<IPath> addlOutputs = new Vector<>();
if (enumeratedPrimaryOutputs.size() > 1) {
// Starting with 1 is intentional in order to skip the primary output
for (int i = 1; i < enumeratedPrimaryOutputs.size(); i++)
addlOutputs.add(enumeratedPrimaryOutputs.get(i));
}
addlOutputs.addAll(enumeratedSecondaryOutputs);
if (addlTargets != null) {
for (IPath addlTarget : addlTargets)
addlOutputs.add(addlTarget);
}
for (int i = 0; i < addlOutputs.size(); i++) {
depLine = escapeWhitespaces(addlOutputs.get(i).toString()) + COLON + WHITESPACE + primaryOutputName;
if (calculatedDependencies != null)
depLine += calculatedDependencies;
depLine += NEWLINE;
if (!getDepLineList().contains(depLine)) {
getDepLineList().add(depLine);
addedDepLines = true;
buffer.append(depLine);
}
}
if (addedDepLines) {
buffer.append(NEWLINE);
}
// If we are using a dependency calculator of type TYPE_PREBUILD_COMMANDS,
// get the rule to build the dependency file
if (depPreBuild != null && depFiles != null) {
addedDepLines = false;
String[] preBuildCommands = depPreBuild.getDependencyCommands();
if (preBuildCommands != null) {
depLine = ""; //$NON-NLS-1$
// Can we use a pattern rule?
patternRule = !isItLinked && !needExplicitRuleForFile && depPreBuild.areCommandsGeneric();
// Begin building the rule
for (int i = 0; i < depFiles.length; i++) {
if (i > 0)
depLine += WHITESPACE;
if (patternRule) {
optDotExt = EMPTY_STRING;
String depExt = depFiles[i].getFileExtension();
if (depExt != null && depExt.length() > 0)
optDotExt = DOT + depExt;
depLine += escapeWhitespaces(relativePath + WILDCARD + optDotExt);
} else {
depLine += escapeWhitespaces((depFiles[i]).toString());
}
}
depLine += COLON + WHITESPACE + (patternRule ? patternBuildRuleDependencies : buildRuleDependencies);
if (!getDepRuleList().contains(depLine)) {
getDepRuleList().add(depLine);
addedDepLines = true;
buffer.append(depLine).append(NEWLINE);
buffer.append(TAB).append(AT)
.append(escapedEcho(MESSAGE_START_DEPENDENCY + WHITESPACE + OUT_MACRO));
for (String preBuildCommand : preBuildCommands) {
depLine = preBuildCommand;
// Resolve macros
try {
if (!needExplicitRuleForFile) {
depLine = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(
depLine, EMPTY_STRING, WHITESPACE, IBuildMacroProvider.CONTEXT_FILE,
new FileContextData(sourceLocation, outputLocation, null, tool));
}
else {
depLine = ManagedBuildManager.getBuildMacroProvider().resolveValue(depLine,
EMPTY_STRING, WHITESPACE, IBuildMacroProvider.CONTEXT_FILE,
new FileContextData(sourceLocation, outputLocation, null, tool));
}
} catch (BuildMacroException e) {
}
//buffer.append(TAB).append(AT).append(escapedEcho(depLine));
//buffer.append(TAB).append(AT).append(depLine).append(NEWLINE);
buffer.append(TAB).append(depLine).append(NEWLINE);
}
}
if (addedDepLines) {
buffer.append(TAB).append(AT).append(ECHO_BLANK_LINE).append(NEWLINE);
}
}
}
}
/*
* Add any dependency calculator options to the tool options
*/
private String[] addDependencyOptions(IManagedDependencyCommands depCommands, String[] flags) {
String[] depOptions = depCommands.getDependencyCommandOptions();
if (depOptions != null && depOptions.length > 0) {
int flagsLen = flags.length;
String[] flagsCopy = new String[flags.length + depOptions.length];
for (int i = 0; i < flags.length; i++) {
flagsCopy[i] = flags[i];
}
for (int i = 0; i < depOptions.length; i++) {
flagsCopy[i + flagsLen] = depOptions[i];
}
flags = flagsCopy;
}
return flags;
}
/**
* Returns any additional resources specified for the tool in other InputType elements and AdditionalInput elements
*/
protected IPath[] getAdditionalResourcesForSource(ITool tool) {
List<IPath> allRes = new ArrayList<>();
IInputType[] types = tool.getInputTypes();
for (IInputType type : types) {
// Additional resources come from 2 places.
// 1. From AdditionalInput childen
IPath[] res = type.getAdditionalResources();
for (IPath re : res) {
allRes.add(re);
}
// 2. From InputTypes that other than the primary input type
if (!type.getPrimaryInput() && type != tool.getPrimaryInputType()) {
String var = type.getBuildVariable();
if (var != null && var.length() > 0) {
allRes.add(Path.fromOSString("$(" + type.getBuildVariable() + ")")); //$NON-NLS-1$ //$NON-NLS-2$
} else {
// Use file extensions
String[] typeExts = type.getSourceExtensions(tool);
for (IResource projectResource : projectResources) {
if (projectResource.getType() == IResource.FILE) {
String fileExt = projectResource.getFileExtension();
if (fileExt == null) {
fileExt = ""; //$NON-NLS-1$
}
for (String typeExt : typeExts) {
if (fileExt.equals(typeExt)) {
allRes.add(projectResource.getProjectRelativePath());
break;
}
}
}
}
}
// If an assignToOption has been specified, set the value of the option to the inputs
IOption assignToOption = tool.getOptionBySuperClassId(type.getAssignToOptionId());
IOption option = tool.getOptionBySuperClassId(type.getOptionId());
if (assignToOption != null && option == null) {
try {
int optType = assignToOption.getValueType();
IResourceInfo rcInfo = tool.getParentResourceInfo();
if (rcInfo != null) {
if (optType == IOption.STRING) {
String optVal = ""; //$NON-NLS-1$
for (int j = 0; j < allRes.size(); j++) {
if (j != 0) {
optVal += " "; //$NON-NLS-1$
}
String resPath = allRes.get(j).toString();
if (!resPath.startsWith("$(")) { //$NON-NLS-1$
IResource addlResource = project.getFile(resPath);
if (addlResource != null) {
IPath addlPath = addlResource.getLocation();
if (addlPath != null) {
resPath = ManagedBuildManager
.calculateRelativePath(getTopBuildDir(), addlPath).toString();
}
}
}
optVal += ManagedBuildManager
.calculateRelativePath(getTopBuildDir(), Path.fromOSString(resPath))
.toString();
}
ManagedBuildManager.setOption(rcInfo, tool, assignToOption, optVal);
} else if (optType == IOption.STRING_LIST || optType == IOption.LIBRARIES
|| optType == IOption.OBJECTS || optType == IOption.INCLUDE_FILES
|| optType == IOption.LIBRARY_PATHS || optType == IOption.LIBRARY_FILES
|| optType == IOption.MACRO_FILES) {
//TODO: do we need to do anything with undefs here?
// Note that the path(s) must be translated from project relative
// to top build directory relative
String[] paths = new String[allRes.size()];
for (int j = 0; j < allRes.size(); j++) {
paths[j] = allRes.get(j).toString();
if (!paths[j].startsWith("$(")) { //$NON-NLS-1$
IResource addlResource = project.getFile(paths[j]);
if (addlResource != null) {
IPath addlPath = addlResource.getLocation();
if (addlPath != null) {
paths[j] = ManagedBuildManager
.calculateRelativePath(getTopBuildDir(), addlPath).toString();
}
}
}
}
ManagedBuildManager.setOption(rcInfo, tool, assignToOption, paths);
} else if (optType == IOption.BOOLEAN) {
boolean b = false;
if (allRes.size() > 0)
b = true;
ManagedBuildManager.setOption(rcInfo, tool, assignToOption, b);
} else if (optType == IOption.ENUMERATED || optType == IOption.TREE) {
if (allRes.size() > 0) {
String s = allRes.get(0).toString();
ManagedBuildManager.setOption(rcInfo, tool, assignToOption, s);
}
}
allRes.clear();
}
} catch (BuildException ex) {
}
}
}
}
return allRes.toArray(new IPath[allRes.size()]);
}
/**
* Returns the output <code>IPath</code>s for this invocation of the tool with the specified source file
*
* The priorities for determining the names of the outputs of a tool are:
* 1. If the tool is the build target and primary output, use artifact name & extension -
* This case does not apply here...
* 2. If an option is specified, use the value of the option
* 3. If a nameProvider is specified, call it
* 4. If outputNames is specified, use it
* 5. Use the name pattern to generate a transformation macro
* so that the source names can be transformed into the target names
* using the built-in string substitution functions of <code>make</code>.
*
* @param relativePath build output directory relative path of the current output directory
* @param ruleOutputs Vector of rule IPaths that are relative to the build directory
* @param enumeratedPrimaryOutputs Vector of IPaths of primary outputs
* that are relative to the build directory
* @param enumeratedSecondaryOutputs Vector of IPaths of secondary outputs
* that are relative to the build directory
*/
protected void calculateOutputsForSource(ITool tool, String relativePath, IResource resource, IPath sourceLocation,
Vector<IPath> ruleOutputs, Vector<IPath> enumeratedPrimaryOutputs,
Vector<IPath> enumeratedSecondaryOutputs) {
String inExt = sourceLocation.getFileExtension();
String outExt = tool.getOutputExtension(inExt);
IResourceInfo rcInfo = tool.getParentResourceInfo();
IOutputType[] outTypes = tool.getOutputTypes();
if (outTypes != null && outTypes.length > 0) {
for (IOutputType type : outTypes) {
boolean primaryOutput = (type == tool.getPrimaryOutputType());
//if (primaryOutput && ignorePrimary) continue;
String outputPrefix = type.getOutputPrefix();
// Resolve any macros in the outputPrefix
// Note that we cannot use file macros because if we do a clean
// we need to know the actual name of the file to clean, and
// cannot use any builder variables such as $@. Hence we use the
// next best thing, i.e. configuration context.
// figure out the configuration we're using
// IBuildObject toolParent = tool.getParent();
// IConfiguration config = null;
// if the parent is a config then we're done
// if (toolParent instanceof IConfiguration)
// config = (IConfiguration) toolParent;
// else if (toolParent instanceof IToolChain) {
// // must be a toolchain
// config = (IConfiguration) ((IToolChain) toolParent)
// .getParent();
// }
//
// else if (toolParent instanceof IResourceConfiguration) {
// config = (IConfiguration) ((IResourceConfiguration) toolParent)
// .getParent();
// }
// else {
// // bad
// throw new AssertionError(
// "tool parent must be one of configuration, toolchain, or resource configuration");
// }
// if (config != null) {
try {
if (containsSpecialCharacters(sourceLocation.toString())) {
outputPrefix = ManagedBuildManager.getBuildMacroProvider().resolveValue(outputPrefix, "", //$NON-NLS-1$
" ", //$NON-NLS-1$
IBuildMacroProvider.CONTEXT_CONFIGURATION, config);
} else {
outputPrefix = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(
outputPrefix, "", //$NON-NLS-1$
" ", //$NON-NLS-1$
IBuildMacroProvider.CONTEXT_CONFIGURATION, config);
}
}
catch (BuildMacroException e) {
}
// }
boolean multOfType = type.getMultipleOfType();
IOption option = tool.getOptionBySuperClassId(type.getOptionId());
IManagedOutputNameProvider nameProvider = type.getNameProvider();
String[] outputNames = type.getOutputNames();
// 1. If the tool is the build target and this is the primary output,
// use artifact name & extension
// Not appropriate here...
// 2. If an option is specified, use the value of the option
if (option != null) {
try {
List<String> outputList = new ArrayList<>();
int optType = option.getValueType();
if (optType == IOption.STRING) {
outputList.add(outputPrefix + option.getStringValue());
} else if (optType == IOption.STRING_LIST || optType == IOption.LIBRARIES
|| optType == IOption.OBJECTS || optType == IOption.INCLUDE_FILES
|| optType == IOption.LIBRARY_PATHS || optType == IOption.LIBRARY_FILES
|| optType == IOption.MACRO_FILES) {
@SuppressWarnings("unchecked")
List<String> value = (List<String>) option.getValue();
outputList = value;
((Tool) tool).filterValues(optType, outputList);
// Add outputPrefix to each if necessary
if (outputPrefix.length() > 0) {
for (int j = 0; j < outputList.size(); j++) {
outputList.set(j, outputPrefix + outputList.get(j));
}
}
}
for (int j = 0; j < outputList.size(); j++) {
String outputName = outputList.get(j);
// try to resolve the build macros in the output
// names
try {
String resolved = null;
if (containsSpecialCharacters(sourceLocation.toString())) {
resolved = ManagedBuildManager.getBuildMacroProvider().resolveValue(outputName, "", //$NON-NLS-1$
" ", //$NON-NLS-1$
IBuildMacroProvider.CONTEXT_FILE,
new FileContextData(sourceLocation, null, option, tool));
}
else {
resolved = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(
outputName, "", //$NON-NLS-1$
" ", //$NON-NLS-1$
IBuildMacroProvider.CONTEXT_FILE,
new FileContextData(sourceLocation, null, option, tool));
}
if ((resolved = resolved.trim()).length() > 0)
outputName = resolved;
} catch (BuildMacroException e) {
}
IPath outPath = Path.fromOSString(outputName);
// If only a file name is specified, add the relative path of this output directory
if (outPath.segmentCount() == 1) {
outPath = Path.fromOSString(relativePath + outputList.get(j));
}
if (primaryOutput) {
ruleOutputs.add(j, outPath);
enumeratedPrimaryOutputs.add(j, resolvePercent(outPath, sourceLocation));
} else {
ruleOutputs.add(outPath);
enumeratedSecondaryOutputs.add(resolvePercent(outPath, sourceLocation));
}
}
} catch (BuildException ex) {
}
} else
// 3. If a nameProvider is specified, call it
if (nameProvider != null) {
IPath[] inPaths = new IPath[1];
inPaths[0] = sourceLocation;
IPath[] outPaths = nameProvider.getOutputNames(tool, inPaths);
if (outPaths != null) {
for (int j = 0; j < outPaths.length; j++) {
IPath outPath = outPaths[j];
String outputName = outPaths[j].toString();
// try to resolve the build macros in the output names
try {
String resolved = null;
if (containsSpecialCharacters(sourceLocation.toString())) {
resolved = ManagedBuildManager.getBuildMacroProvider().resolveValue(outputName, "", //$NON-NLS-1$
" ", //$NON-NLS-1$
IBuildMacroProvider.CONTEXT_FILE,
new FileContextData(sourceLocation, null, option, tool));
}
else {
resolved = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(
outputName, "", //$NON-NLS-1$
" ", //$NON-NLS-1$
IBuildMacroProvider.CONTEXT_FILE,
new FileContextData(sourceLocation, null, option, tool));
}
if ((resolved = resolved.trim()).length() > 0)
outputName = resolved;
} catch (BuildMacroException e) {
}
// If only a file name is specified, add the relative path of this output directory
if (outPath.segmentCount() == 1) {
outPath = Path.fromOSString(relativePath + outPath.toString());
}
if (primaryOutput) {
ruleOutputs.add(j, outPath);
enumeratedPrimaryOutputs.add(j, resolvePercent(outPath, sourceLocation));
} else {
ruleOutputs.add(outPath);
enumeratedSecondaryOutputs.add(resolvePercent(outPath, sourceLocation));
}
}
}
} else
// 4. If outputNames is specified, use it
if (outputNames != null) {
for (int j = 0; j < outputNames.length; j++) {
String outputName = outputNames[j];
try {
//try to resolve the build macros in the output names
String resolved = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(
outputName, "", //$NON-NLS-1$
" ", //$NON-NLS-1$
IBuildMacroProvider.CONTEXT_FILE,
new FileContextData(sourceLocation, null, option, tool));
if ((resolved = resolved.trim()).length() > 0)
outputName = resolved;
} catch (BuildMacroException e) {
}
IPath outPath = Path.fromOSString(outputName);
// If only a file name is specified, add the relative path of this output directory
if (outPath.segmentCount() == 1) {
outPath = Path.fromOSString(relativePath + outPath.toString());
}
if (primaryOutput) {
ruleOutputs.add(j, outPath);
enumeratedPrimaryOutputs.add(j, resolvePercent(outPath, sourceLocation));
} else {
ruleOutputs.add(outPath);
enumeratedSecondaryOutputs.add(resolvePercent(outPath, sourceLocation));
}
}
} else {
// 5. Use the name pattern to generate a transformation macro
// so that the source names can be transformed into the target names
// using the built-in string substitution functions of <code>make</code>.
if (multOfType) {
// This case is not handled - a nameProvider or outputNames must be specified
// TODO - report error
} else {
String namePattern = type.getNamePattern();
IPath namePatternPath = null;
if (namePattern == null || namePattern.length() == 0) {
namePattern = relativePath + outputPrefix + IManagedBuilderMakefileGenerator.WILDCARD;
if (outExt != null && outExt.length() > 0) {
namePattern += DOT + outExt;
}
namePatternPath = Path.fromOSString(namePattern);
} else {
if (outputPrefix.length() > 0) {
namePattern = outputPrefix + namePattern;
}
namePatternPath = Path.fromOSString(namePattern);
// If only a file name is specified, add the relative path of this output directory
if (namePatternPath.segmentCount() == 1) {
namePatternPath = Path.fromOSString(relativePath + namePatternPath.toString());
}
}
if (primaryOutput) {
ruleOutputs.add(0, namePatternPath);
enumeratedPrimaryOutputs.add(0, resolvePercent(namePatternPath, sourceLocation));
} else {
ruleOutputs.add(namePatternPath);
enumeratedSecondaryOutputs.add(resolvePercent(namePatternPath, sourceLocation));
}
}
}
}
} else {
// For support of pre-CDT 3.0 integrations.
// NOTE WELL: This only supports the case of a single "target tool"
// that consumes exactly all of the object files, $OBJS, produced
// by other tools in the build and produces a single output.
// In this case, the output file name is the input file name with
// the output extension.
//if (!ignorePrimary) {
String outPrefix = tool.getOutputPrefix();
IPath outPath = Path.fromOSString(relativePath + outPrefix + WILDCARD);
outPath = outPath.addFileExtension(outExt);
ruleOutputs.add(0, outPath);
enumeratedPrimaryOutputs.add(0, resolvePercent(outPath, sourceLocation));
//}
}
}
/**
* If the path contains a %, returns the path resolved using the resource name
*
*/
protected IPath resolvePercent(IPath outPath, IPath sourceLocation) {
// Get the input file name
String fileName = sourceLocation.removeFileExtension().lastSegment();
// Replace the % with the file name
String outName = outPath.toOSString().replaceAll("%", fileName); //$NON-NLS-1$
IPath result = Path.fromOSString(outName);
return DOT_SLASH_PATH.isPrefixOf(outPath) ? DOT_SLASH_PATH.append(result) : result;
}
/**
* Returns the dependency <code>IPath</code>s for this invocation of the tool with the specified source file
*
* @param depGen the dependency calculator
* @param tool tool used to build the source file
* @param relativePath build output directory relative path of the current output directory
* @param resource source file to scan for dependencies
* @return Vector of IPaths that are relative to the build directory
*/
protected IPath[] oldCalculateDependenciesForSource(IManagedDependencyGenerator depGen, ITool tool,
String relativePath, IResource resource) {
Vector<IPath> deps = new Vector<>();
int type = depGen.getCalculatorType();
switch (type) {
case IManagedDependencyGeneratorType.TYPE_INDEXER:
case IManagedDependencyGeneratorType.TYPE_EXTERNAL:
IResource[] res = depGen.findDependencies(resource, project);
if (res != null) {
for (IResource re : res) {
IPath dep = null;
if (re != null) {
IPath addlPath = re.getLocation();
if (addlPath != null) {
dep = ManagedBuildManager.calculateRelativePath(getTopBuildDir(), addlPath);
}
}
if (dep != null) {
deps.add(dep);
}
}
}
break;
case IManagedDependencyGeneratorType.TYPE_NODEPS:
default:
break;
}
return deps.toArray(new IPath[deps.size()]);
}
/**
* Returns the dependency <code>IPath</code>s relative to the build directory
*
* @param depCalculator the dependency calculator
* @return IPath[] that are relative to the build directory
*/
protected IPath[] calculateDependenciesForSource(IManagedDependencyCalculator depCalculator) {
IPath[] addlDeps = depCalculator.getDependencies();
if (addlDeps != null) {
for (int i = 0; i < addlDeps.length; i++) {
if (!addlDeps[i].isAbsolute()) {
// Convert from project relative to build directory relative
IPath absolutePath = project.getLocation().append(addlDeps[i]);
addlDeps[i] = ManagedBuildManager.calculateRelativePath(getTopBuildDir(), absolutePath);
}
}
}
return addlDeps;
}
/*************************************************************************
* M A K E F I L E G E N E R A T I O N C O M M O N M E T H O D S
************************************************************************/
/**
* Generates a source macro name from a file extension
*/
public StringBuffer getSourceMacroName(String extensionName) {
StringBuffer macroName = new StringBuffer();
// We need to handle case sensitivity in file extensions (e.g. .c vs .C), so if the
// extension was already upper case, tack on an "UPPER_" to the macro name.
// In theory this means there could be a conflict if you had for example,
// extensions .c_upper, and .C, but realistically speaking the chances of this are
// practically nil so it doesn't seem worth the hassle of generating a truly
// unique name.
if (extensionName.equals(extensionName.toUpperCase())) {
macroName.append(extensionName.toUpperCase()).append("_UPPER"); //$NON-NLS-1$
} else {
// lower case... no need for "UPPER_"
macroName.append(extensionName.toUpperCase());
}
macroName.append("_SRCS"); //$NON-NLS-1$
return macroName;
}
/**
* Generates a generated dependency file macro name from a file extension
*/
public StringBuffer getDepMacroName(String extensionName) {
StringBuffer macroName = new StringBuffer();
// We need to handle case sensitivity in file extensions (e.g. .c vs .C), so if the
// extension was already upper case, tack on an "UPPER_" to the macro name.
// In theory this means there could be a conflict if you had for example,
// extensions .c_upper, and .C, but realistically speaking the chances of this are
// practically nil so it doesn't seem worth the hassle of generating a truly
// unique name.
if (extensionName.equals(extensionName.toUpperCase())) {
macroName.append(extensionName.toUpperCase()).append("_UPPER"); //$NON-NLS-1$
} else {
// lower case... no need for "UPPER_"
macroName.append(extensionName.toUpperCase());
}
macroName.append("_DEPS"); //$NON-NLS-1$
return macroName;
}
/**
* Answers all of the output extensions that the target
* of the build has tools defined to work on.
*
* @return a <code>Set</code> containing all of the output extensions
*/
public Set<String> getOutputExtensions(ToolInfoHolder h) {
if (h.outputExtensionsSet == null) {
// The set of output extensions which will be produced by this tool.
// It is presumed that this set is not very large (likely < 10) so
// a HashSet should provide good performance.
h.outputExtensionsSet = new HashSet<>();
// For each tool for the target, lookup the kinds of sources it outputs
// and add that to our list of output extensions.
for (ITool tool : h.buildTools) {
String[] outputs = tool.getAllOutputExtensions();
if (outputs != null) {
h.outputExtensionsSet.addAll(Arrays.asList(outputs));
}
}
}
return h.outputExtensionsSet;
}
/**
* This method postprocesses a .d file created by a build.
* It's main job is to add dummy targets for the header files dependencies.
* This prevents make from aborting the build if the header file does not exist.
*
* A secondary job is to work in tandem with the "echo" command that is used
* by some tool-chains in order to get the "targets" part of the dependency rule
* correct.
*
* This method adds a comment to the beginning of the dependency file which it
* checks for to determine if this dependency file has already been updated.
*
* @return a <code>true</code> if the dependency file is modified
*/
static public boolean populateDummyTargets(IConfiguration cfg, IFile makefile, boolean force)
throws CoreException, IOException {
return populateDummyTargets(cfg.getRootFolderInfo(), makefile, force);
}
static public boolean populateDummyTargets(IResourceInfo rcInfo, IFile makefile, boolean force)
throws CoreException, IOException {
if (makefile == null || !makefile.exists())
return false;
// Get the contents of the dependency file
StringBuffer inBuffer;
InputStream contentStream = makefile.getContents(false);
try (Reader in = new InputStreamReader(contentStream)) {
int chunkSize = contentStream.available();
inBuffer = new StringBuffer(chunkSize);
char[] readBuffer = new char[chunkSize];
int n = in.read(readBuffer);
while (n > 0) {
inBuffer.append(readBuffer);
n = in.read(readBuffer);
}
}
// The rest of this operation is equally expensive, so
// if we are doing an incremental build, only update the
// files that do not have a comment
String inBufferString = inBuffer.toString();
if (!force && inBufferString.startsWith(COMMENT_SYMBOL)) {
return false;
}
// Try to determine if this file already has dummy targets defined.
// If so, we will only add the comment.
String[] bufferLines = inBufferString.split("[\\r\\n]"); //$NON-NLS-1$
for (String bufferLine : bufferLines) {
if (bufferLine.endsWith(":")) { //$NON-NLS-1$
StringBuffer outBuffer = addDefaultHeader();
outBuffer.append(inBuffer);
save(outBuffer, makefile);
return true;
}
}
// Reconstruct the buffer tokens into useful chunks of dependency information
Vector<String> bufferTokens = new Vector<>(Arrays.asList(inBufferString.split("\\s"))); //$NON-NLS-1$
Vector<String> deps = new Vector<>(bufferTokens.size());
Iterator<String> tokenIter = bufferTokens.iterator();
while (tokenIter.hasNext()) {
String token = tokenIter.next();
if (token.lastIndexOf("\\") == token.length() - 1 && token.length() > 1) { //$NON-NLS-1$
// This is escaped so keep adding to the token until we find the end
while (tokenIter.hasNext()) {
String nextToken = tokenIter.next();
token += WHITESPACE + nextToken;
if (!nextToken.endsWith("\\")) { //$NON-NLS-1$
//$NON-NLS-1$
break;
}
}
}
deps.add(token);
}
deps.trimToSize();
// Now find the header file dependencies and make dummy targets for them
boolean save = false;
StringBuffer outBuffer = null;
// If we are doing an incremental build, only update the files that do not have a comment
String firstToken;
try {
firstToken = deps.get(0);
} catch (ArrayIndexOutOfBoundsException e) {
// This makes no sense so bail
return false;
}
// Put the generated comments in the output buffer
if (!firstToken.startsWith(COMMENT_SYMBOL)) {
outBuffer = addDefaultHeader();
} else {
outBuffer = new StringBuffer();
}
// Some echo implementations misbehave and put the -n and newline in the output
if (firstToken.startsWith("-n")) { //$NON-NLS-1$
// Now let's parse:
// Win32 outputs -n '<path>/<file>.d <path>/'
// POSIX outputs -n <path>/<file>.d <path>/
// Get the dep file name
String secondToken;
try {
secondToken = deps.get(1);
} catch (ArrayIndexOutOfBoundsException e) {
secondToken = ""; //$NON-NLS-1$
}
if (secondToken.startsWith("'")) { //$NON-NLS-1$
// This is the Win32 implementation of echo (MinGW without MSYS)
outBuffer.append(secondToken.substring(1)).append(WHITESPACE);
} else {
outBuffer.append(secondToken).append(WHITESPACE);
}
// The relative path to the build goal comes next
String thirdToken;
try {
thirdToken = deps.get(2);
} catch (ArrayIndexOutOfBoundsException e) {
thirdToken = ""; //$NON-NLS-1$
}
int lastIndex = thirdToken.lastIndexOf("'"); //$NON-NLS-1$
if (lastIndex != -1) {
if (lastIndex == 0) {
outBuffer.append(WHITESPACE);
} else {
outBuffer.append(thirdToken.substring(0, lastIndex - 1));
}
} else {
outBuffer.append(thirdToken);
}
// Followed by the target output by the compiler plus ':'
// If we see any empty tokens here, assume they are the result of
// a line feed output by "echo" and skip them
String fourthToken;
int nToken = 3;
try {
do {
fourthToken = deps.get(nToken++);
} while (fourthToken.length() == 0);
} catch (ArrayIndexOutOfBoundsException e) {
fourthToken = ""; //$NON-NLS-1$
}
outBuffer.append(fourthToken).append(WHITESPACE);
// Followed by the actual dependencies
try {
for (String nextElement : deps) {
if (nextElement.endsWith("\\")) { //$NON-NLS-1$
outBuffer.append(nextElement).append(NEWLINE).append(WHITESPACE);
} else {
outBuffer.append(nextElement).append(WHITESPACE);
}
}
} catch (IndexOutOfBoundsException e) {
}
} else {
outBuffer.append(inBuffer);
}
outBuffer.append(NEWLINE);
save = true;
IFolderInfo fo = null;
if (rcInfo instanceof IFolderInfo) {
fo = (IFolderInfo) rcInfo;
} else {
IConfiguration c = rcInfo.getParent();
fo = (IFolderInfo) c.getResourceInfo(rcInfo.getPath().removeLastSegments(1), false);
}
// Dummy targets to add to the makefile
for (String dummy : deps) {
IPath dep = new Path(dummy);
String extension = dep.getFileExtension();
if (fo.isHeaderFile(extension)) {
/*
* The formatting here is
* <dummy_target>:
*/
outBuffer.append(dummy).append(COLON).append(NEWLINE).append(NEWLINE);
}
}
// Write them out to the makefile
if (save) {
save(outBuffer, makefile);
return true;
}
return false;
}
/**
* prepend all instanced of '\' or '"' with a backslash
*
* @return resulting string
*/
static public String escapedEcho(String string) {
String escapedString = string.replaceAll("'", "'\"'\"'"); //$NON-NLS-1$ //$NON-NLS-2$
return ECHO + WHITESPACE + SINGLE_QUOTE + escapedString + SINGLE_QUOTE + NEWLINE;
}
static public String ECHO_BLANK_LINE = ECHO + WHITESPACE + SINGLE_QUOTE + WHITESPACE + SINGLE_QUOTE + NEWLINE;
/**
* Outputs a comment formatted as follows:
* ##### ....... #####
* # <Comment message>
* ##### ....... #####
*/
static protected StringBuffer addDefaultHeader() {
StringBuffer buffer = new StringBuffer();
outputCommentLine(buffer);
buffer.append(COMMENT_SYMBOL).append(WHITESPACE).append(ManagedMakeMessages.getResourceString(HEADER))
.append(NEWLINE);
outputCommentLine(buffer);
buffer.append(NEWLINE);
return buffer;
}
/**
* Put COLS_PER_LINE comment charaters in the argument.
*/
static protected void outputCommentLine(StringBuffer buffer) {
for (int i = 0; i < COLS_PER_LINE; i++) {
buffer.append(COMMENT_SYMBOL);
}
buffer.append(NEWLINE);
}
static public boolean containsSpecialCharacters(String path) {
return path.matches(".*(\\s|[\\{\\}\\(\\)\\$\\@%=;]).*"); //$NON-NLS-1$
}
/**
* Answers the argument with all whitespaces replaced with an escape sequence.
*/
static public String escapeWhitespaces(String path) {
// Escape the spaces in the path/filename if it has any
String[] segments = path.split("\\s"); //$NON-NLS-1$
if (segments.length > 1) {
StringBuffer escapedPath = new StringBuffer();
for (int index = 0; index < segments.length; ++index) {
escapedPath.append(segments[index]);
if (index + 1 < segments.length) {
escapedPath.append("\\ "); //$NON-NLS-1$
}
}
return escapedPath.toString().trim();
} else {
return path;
}
}
/**
* Adds a macro addition prefix to a map of macro names to entries.
* Entry prefixes look like:
* C_SRCS += \
* ${addprefix $(ROOT)/, \
*/
// TODO fix comment
protected void addMacroAdditionPrefix(LinkedHashMap<String, String> map, String macroName, String relativePath,
boolean addPrefix) {
// there is no entry in the map, so create a buffer for this macro
StringBuffer tempBuffer = new StringBuffer();
tempBuffer.append(macroName).append(WHITESPACE).append(MACRO_ADDITION_PREFIX_SUFFIX);
if (addPrefix) {
tempBuffer.append(MACRO_ADDITION_ADDPREFIX_HEADER).append(relativePath)
.append(MACRO_ADDITION_ADDPREFIX_SUFFIX);
}
// have to store the buffer in String form as StringBuffer is not a sublcass of Object
map.put(macroName, tempBuffer.toString());
}
/**
* Adds a file to an entry in a map of macro names to entries.
* File additions look like:
* example.c, \
*/
protected void addMacroAdditionFile(HashMap<String, String> map, String macroName, String filename) {
StringBuffer buffer = new StringBuffer();
buffer.append(map.get(macroName));
// escape whitespace in the filename
filename = escapeWhitespaces(filename);
buffer.append(filename).append(WHITESPACE).append(LINEBREAK);
// re-insert string in the map
map.put(macroName, buffer.toString());
}
/**
* Adds a file to an entry in a map of macro names to entries.
* File additions look like:
* example.c, \
*/
protected void addMacroAdditionFile(HashMap<String, String> map, String macroName, String relativePath,
IPath sourceLocation, boolean generatedSource) {
// Add the source file path to the makefile line that adds source files to the build variable
String srcName;
IPath projectLocation = getPathForResource(project);
IPath dirLocation = projectLocation;
if (generatedSource) {
dirLocation = dirLocation.append(getBuildWorkingDir());
}
if (dirLocation.isPrefixOf(sourceLocation)) {
IPath srcPath = sourceLocation.removeFirstSegments(dirLocation.segmentCount()).setDevice(null);
if (generatedSource) {
srcName = "./" + srcPath.toString(); //$NON-NLS-1$
} else {
srcName = reachProjectRoot() + SEPARATOR + srcPath.toString();
}
} else {
if (generatedSource && !sourceLocation.isAbsolute()) {
srcName = "./" + relativePath + sourceLocation.lastSegment().toString(); //$NON-NLS-1$
} else {
// TODO: Should we use relative paths when possible (e.g., see MbsMacroSupplier.calculateRelPath)
srcName = sourceLocation.toString();
}
}
addMacroAdditionFile(map, macroName, srcName);
}
/**
* Adds file(s) to an entry in a map of macro names to entries.
* File additions look like:
* example.c, \
*/
public void addMacroAdditionFiles(HashMap<String, String> map, String macroName, Vector<String> filenames) {
StringBuffer buffer = new StringBuffer();
buffer.append(map.get(macroName));
for (int i = 0; i < filenames.size(); i++) {
String filename = filenames.get(i);
if (filename.length() > 0) {
// Bug 417288, ilg@livius.net & freidin.alex@gmail.com
filename = ensurePathIsGNUMakeTargetRuleCompatibleSyntax(filename);
buffer.append(filename).append(WHITESPACE).append(LINEBREAK);
}
}
// re-insert string in the map
map.put(macroName, buffer.toString());
}
/**
* Write all macro addition entries in a map to the buffer
*/
protected StringBuffer writeAdditionMacros(LinkedHashMap<String, String> map) {
StringBuffer buffer = new StringBuffer();
// Add the comment
buffer.append(COMMENT_SYMBOL).append(WHITESPACE).append(ManagedMakeMessages.getResourceString(MOD_VARS))
.append(NEWLINE);
for (String macroString : map.values()) {
// Check if we added any files to the rule
// Currently, we do this by comparing the end of the rule buffer to MACRO_ADDITION_PREFIX_SUFFIX
if (!(macroString.endsWith(MACRO_ADDITION_PREFIX_SUFFIX))) {
StringBuffer currentBuffer = new StringBuffer();
// Remove the final "/"
if (macroString.endsWith(LINEBREAK)) {
macroString = macroString.substring(0, (macroString.length() - 2)) + NEWLINE;
}
currentBuffer.append(macroString);
currentBuffer.append(NEWLINE);
// append the contents of the buffer to the master buffer for
// the whole file
buffer.append(currentBuffer);
}
}
return buffer.append(NEWLINE);
}
/**
* Write all macro addition entries in a map to the buffer
*/
protected StringBuffer writeTopAdditionMacros(List<String> varList, HashMap<String, String> varMap) {
StringBuffer buffer = new StringBuffer();
// Add the comment
buffer.append(COMMENT_SYMBOL).append(WHITESPACE).append(ManagedMakeMessages.getResourceString(MOD_VARS))
.append(NEWLINE);
for (int i = 0; i < varList.size(); i++) {
String addition = varMap.get(varList.get(i));
StringBuffer currentBuffer = new StringBuffer();
currentBuffer.append(addition);
currentBuffer.append(NEWLINE);
// append the contents of the buffer to the master buffer for the whole file
buffer.append(currentBuffer);
}
return buffer.append(NEWLINE);
}
/**
* Calculates the inputs and outputs for tools that will be generated in the top makefile.
* This information is used by the top level makefile generation methods.
*/
protected void calculateToolInputsOutputs() {
toolInfos.accept(new IPathSettingsContainerVisitor() {
@Override
public boolean visit(PathSettingsContainer container) {
ToolInfoHolder h = (ToolInfoHolder) container.getValue();
ITool[] buildTools = h.buildTools;
ManagedBuildGnuToolInfo[] gnuToolInfos = h.gnuToolInfos;
// We are "done" when the information for all tools has been calculated,
// or we are not making any progress
boolean done = false;
boolean lastChance = false;
int[] doneState = new int[buildTools.length];
// Identify the target tool
ITool targetTool = config.calculateTargetTool();
// if (targetTool == null) {
// targetTool = info.getToolFromOutputExtension(buildTargetExt);
// }
// Initialize the tool info array and the done state
if (buildTools.length != 0 && buildTools[0].getCustomBuildStep())
return true;
for (int i = 0; i < buildTools.length; i++) {
if ((buildTools[i] == targetTool)) {
String ext = config.getArtifactExtension();
//try to resolve the build macros in the artifact extension
try {
ext = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(ext, "", //$NON-NLS-1$
" ", //$NON-NLS-1$
IBuildMacroProvider.CONTEXT_CONFIGURATION, config);
} catch (BuildMacroException e) {
}
String name = config.getArtifactName();
//try to resolve the build macros in the artifact name
try {
String resolved = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(
name, "", //$NON-NLS-1$
" ", //$NON-NLS-1$
IBuildMacroProvider.CONTEXT_CONFIGURATION, config);
if ((resolved = resolved.trim()).length() > 0)
name = resolved;
} catch (BuildMacroException e) {
}
gnuToolInfos[i] = new ManagedBuildGnuToolInfo(project, buildTools[i], true, name, ext);
} else {
gnuToolInfos[i] = new ManagedBuildGnuToolInfo(project, buildTools[i], false, null, null);
}
doneState[i] = 0;
}
// Initialize the build output variable to file additions map
LinkedHashMap<String, String> map = getTopBuildOutputVars();
Set<Entry<String, List<IPath>>> set = buildOutVars.entrySet();
for (Entry<String, List<IPath>> entry : set) {
String macroName = entry.getKey();
// for projects with specific setting on folders/files do
// not clear the macro value on subsequent passes
if (!map.containsKey(macroName)) {
addMacroAdditionPrefix(map, macroName, "", false); //$NON-NLS-1$
}
}
// Set of input extensions for which macros have been created so far
HashSet<String> handledDepsInputExtensions = new HashSet<>();
HashSet<String> handledOutsInputExtensions = new HashSet<>();
while (!done) {
int[] testState = new int[doneState.length];
for (int i = 0; i < testState.length; i++)
testState[i] = 0;
// Calculate inputs
for (int i = 0; i < gnuToolInfos.length; i++) {
if (gnuToolInfos[i].areInputsCalculated()) {
testState[i]++;
} else {
if (gnuToolInfos[i].calculateInputs(GnuMakefileGenerator.this, config, projectResources, h,
lastChance)) {
testState[i]++;
}
}
}
// Calculate dependencies
for (int i = 0; i < gnuToolInfos.length; i++) {
if (gnuToolInfos[i].areDependenciesCalculated()) {
testState[i]++;
} else {
if (gnuToolInfos[i].calculateDependencies(GnuMakefileGenerator.this, config,
handledDepsInputExtensions, h, lastChance)) {
testState[i]++;
}
}
}
// Calculate outputs
for (int i = 0; i < gnuToolInfos.length; i++) {
if (gnuToolInfos[i].areOutputsCalculated()) {
testState[i]++;
} else {
if (gnuToolInfos[i].calculateOutputs(GnuMakefileGenerator.this, config,
handledOutsInputExtensions, lastChance)) {
testState[i]++;
}
}
}
// Are all calculated? If so, done.
done = true;
for (int element : testState) {
if (element != 3) {
done = false;
break;
}
}
// Test our "done" state vs. the previous "done" state.
// If we have made no progress, give it a "last chance" and then quit
if (!done) {
done = true;
for (int i = 0; i < testState.length; i++) {
if (testState[i] != doneState[i]) {
done = false;
break;
}
}
}
if (done) {
if (!lastChance) {
lastChance = true;
done = false;
}
}
if (!done) {
doneState = testState;
}
}
return true;
}
});
}
/**
* Returns the (String) list of files associated with the build variable
*
* @param variable the variable name
* @param locationType the format in which we want the filenames returned
* @param directory project relative directory path used with locationType == DIRECTORY_RELATIVE
* @param getAll only return the list if all tools that are going to contrubute to this
* variable have done so.
* @return List
*/
public List<String> getBuildVariableList(ToolInfoHolder h, String variable, int locationType, IPath directory,
boolean getAll) {
ManagedBuildGnuToolInfo[] gnuToolInfos = h.gnuToolInfos;
boolean done = true;
for (int i = 0; i < gnuToolInfos.length; i++) {
if (!gnuToolInfos[i].areOutputVariablesCalculated()) {
done = false;
}
}
if (!done && getAll)
return null;
List<IPath> list = buildSrcVars.get(variable);
if (list == null) {
list = buildOutVars.get(variable);
}
List<String> fileList = null;
if (list != null) {
// Convert the items in the list to the location-type wanted by the caller,
// and to a string list
IPath dirLocation = null;
if (locationType != ABSOLUTE) {
dirLocation = project.getLocation();
if (locationType == PROJECT_SUBDIR_RELATIVE) {
dirLocation = dirLocation.append(directory);
}
}
for (int i = 0; i < list.size(); i++) {
IPath path = list.get(i);
if (locationType != ABSOLUTE) {
if (dirLocation != null && dirLocation.isPrefixOf(path)) {
path = path.removeFirstSegments(dirLocation.segmentCount()).setDevice(null);
}
}
if (fileList == null) {
fileList = new Vector<>();
}
fileList.add(path.toString());
}
}
return fileList;
}
/**
* Returns the map of build variables to list of files
*
* @return HashMap
*/
public HashMap<String, List<IPath>> getBuildOutputVars() {
return buildOutVars;
}
/**
* Returns the map of build variables used in the top makefile to list of files
*
* @return HashMap
*/
public LinkedHashMap<String, String> getTopBuildOutputVars() {
return topBuildOutVars;
}
/**
* Returns the list of known build rules. This keeps me from generating duplicate
* rules for known file extensions.
*
* @return List
*/
protected Vector<String> getRuleList() {
if (ruleList == null) {
ruleList = new Vector<>();
}
return ruleList;
}
/**
* Returns the list of known dependency lines. This keeps me from generating duplicate
* lines.
*
* @return List
*/
protected Vector<String> getDepLineList() {
if (depLineList == null) {
depLineList = new Vector<>();
}
return depLineList;
}
/**
* Returns the list of known dependency file generation lines. This keeps me from
* generating duplicate lines.
*
* @return List
*/
protected Vector<String> getDepRuleList() {
if (depRuleList == null) {
depRuleList = new Vector<>();
}
return depRuleList;
}
/*************************************************************************
* R E S O U R C E V I S I T O R M E T H O D S
************************************************************************/
/**
* Adds the container of the argument to the list of folders in the project that
* contribute source files to the build. The resource visitor has already established
* that the build model knows how to build the files. It has also checked that
* the resource is not generated as part of the build.
*/
protected void appendBuildSubdirectory(IResource resource) {
IContainer container = resource.getParent();
// Only add the container once
if (!getSubdirList().contains(container))
getSubdirList().add(container);
}
/**
* Adds the container of the argument to a list of subdirectories that are to be
* deleted. As a result, the directories that are generated for the output
* should be removed as well.
*/
protected void appendDeletedSubdirectory(IContainer container) {
// No point in adding a folder if the parent is already there
IContainer parent = container.getParent();
if (!getDeletedDirList().contains(container) && !getDeletedDirList().contains(parent)) {
getDeletedDirList().add(container);
}
}
/**
* If a file is removed from a source folder (either because of a delete
* or move action on the part of the user), the makefilegenerator has to
* remove the dependency makefile along with the old build goal
*/
protected void appendDeletedFile(IResource resource) {
// Cache this for now
getDeletedFileList().add(resource);
}
/**
* Adds the container of the argument to a list of subdirectories that are part
* of an incremental rebuild of the project. The makefile fragments for these
* directories will be regenerated as a result of the build.
*/
protected void appendModifiedSubdirectory(IResource resource) {
IContainer container = resource.getParent();
if (!getModifiedList().contains(container)) {
getModifiedList().add(container);
}
}
/*************************************************************************
* O T H E R M E T H O D S
************************************************************************/
protected void cancel(String message) {
if (monitor != null && !monitor.isCanceled()) {
throw new OperationCanceledException(message);
}
}
/**
* Check whether the build has been cancelled. Cancellation requests
* propagated to the caller by throwing <code>OperationCanceledException</code>.
*
* @see org.eclipse.core.runtime.OperationCanceledException#OperationCanceledException()
*/
protected void checkCancel() {
if (monitor != null && monitor.isCanceled()) {
throw new OperationCanceledException();
}
}
/**
* Return or create the folder needed for the build output. If we are
* creating the folder, set the derived bit to true so the CM system
* ignores the contents. If the resource exists, respect the existing
* derived setting.
*/
private IPath createDirectory(String dirName) throws CoreException {
// Create or get the handle for the build directory
IFolder folder = project.getFolder(dirName);
if (!folder.exists()) {
// Make sure that parent folders exist
IPath parentPath = (new Path(dirName)).removeLastSegments(1);
// Assume that the parent exists if the path is empty
if (!parentPath.isEmpty()) {
IFolder parent = project.getFolder(parentPath);
if (!parent.exists()) {
createDirectory(parentPath.toString());
}
}
// Now make the requested folder
try {
folder.create(true, true, null);
} catch (CoreException e) {
if (e.getStatus().getCode() == IResourceStatus.PATH_OCCUPIED)
folder.refreshLocal(IResource.DEPTH_ZERO, null);
else
throw e;
}
// Make sure the folder is marked as derived so it is not added to CM
if (!folder.isDerived()) {
folder.setDerived(true, null);
}
}
return folder.getFullPath();
}
/**
* Return or create the makefile needed for the build. If we are creating
* the resource, set the derived bit to true so the CM system ignores
* the contents. If the resource exists, respect the existing derived
* setting.
*/
private IFile createFile(IPath makefilePath) throws CoreException {
// Create or get the handle for the makefile
IWorkspaceRoot root = CCorePlugin.getWorkspace().getRoot();
IFile newFile = root.getFileForLocation(makefilePath);
if (newFile == null) {
newFile = root.getFile(makefilePath);
}
// Create the file if it does not exist
ByteArrayInputStream contents = new ByteArrayInputStream(new byte[0]);
try {
newFile.create(contents, false, new SubProgressMonitor(monitor, 1));
// Make sure the new file is marked as derived
if (!newFile.isDerived()) {
newFile.setDerived(true, null);
}
} catch (CoreException e) {
// If the file already existed locally, just refresh to get contents
if (e.getStatus().getCode() == IResourceStatus.PATH_OCCUPIED)
newFile.refreshLocal(IResource.DEPTH_ZERO, null);
else
throw e;
}
return newFile;
}
private void deleteBuildTarget(IResource deletedFile) {
// Get the project relative path of the file
String fileName = getFileName(deletedFile);
String srcExtension = deletedFile.getFileExtension();
IPath folderPath = inFullPathFromOutFullPath(deletedFile.getFullPath().removeLastSegments(1));
if (folderPath != null) {
folderPath = folderPath.removeFirstSegments(1);
} else {
folderPath = new Path(""); //$NON-NLS-1$
}
IResourceInfo rcInfo = config.getResourceInfo(folderPath, false);
if (rcInfo instanceof IFileInfo) {
rcInfo = config.getResourceInfo(folderPath.removeLastSegments(1), false);
}
String targetExtension = ((IFolderInfo) rcInfo).getOutputExtension(srcExtension);
if (!targetExtension.isEmpty())
fileName += DOT + targetExtension;
IPath projectRelativePath = deletedFile.getProjectRelativePath().removeLastSegments(1);
IPath targetFilePath = getBuildWorkingDir().append(projectRelativePath).append(fileName);
IResource depFile = project.findMember(targetFilePath);
if (depFile != null && depFile.exists()) {
try {
depFile.delete(true, new SubProgressMonitor(monitor, 1));
} catch (CoreException e) {
// This had better be allowed during a build
}
}
}
private IPath inFullPathFromOutFullPath(IPath path) {
IPath inPath = null;
if (topBuildDir.isPrefixOf(path)) {
inPath = path.removeFirstSegments(topBuildDir.segmentCount());
inPath = project.getFullPath().append(path);
}
return inPath;
}
private void deleteDepFile(IResource deletedFile) {
// Calculate the top build directory relative paths of the dependency files
IPath[] depFilePaths = null;
ITool tool = null;
IManagedDependencyGeneratorType depType = null;
String sourceExtension = deletedFile.getFileExtension();
IPath folderPath = inFullPathFromOutFullPath(deletedFile.getFullPath().removeLastSegments(1));
if (folderPath != null) {
folderPath = folderPath.removeFirstSegments(1);
} else {
folderPath = new Path(""); //$NON-NLS-1$
}
ToolInfoHolder h = getToolInfo(folderPath);
ITool[] tools = h.buildTools;
for (int index = 0; index < tools.length; ++index) {
if (tools[index].buildsFileType(sourceExtension)) {
tool = tools[index];
depType = tool.getDependencyGeneratorForExtension(sourceExtension);
break;
}
}
if (depType != null) {
int calcType = depType.getCalculatorType();
if (calcType == IManagedDependencyGeneratorType.TYPE_COMMAND) {
depFilePaths = new IPath[1];
IPath absolutePath = deletedFile.getLocation();
depFilePaths[0] = ManagedBuildManager.calculateRelativePath(getTopBuildDir(), absolutePath);
depFilePaths[0] = depFilePaths[0].removeFileExtension().addFileExtension(DEP_EXT);
} else if (calcType == IManagedDependencyGeneratorType.TYPE_BUILD_COMMANDS
|| calcType == IManagedDependencyGeneratorType.TYPE_PREBUILD_COMMANDS) {
IManagedDependencyGenerator2 depGen = (IManagedDependencyGenerator2) depType;
IManagedDependencyInfo depInfo = depGen.getDependencySourceInfo(deletedFile.getProjectRelativePath(),
deletedFile, config, tool, getBuildWorkingDir());
if (depInfo != null) {
if (calcType == IManagedDependencyGeneratorType.TYPE_BUILD_COMMANDS) {
IManagedDependencyCommands depCommands = (IManagedDependencyCommands) depInfo;
depFilePaths = depCommands.getDependencyFiles();
} else if (calcType == IManagedDependencyGeneratorType.TYPE_PREBUILD_COMMANDS) {
IManagedDependencyPreBuild depPreBuild = (IManagedDependencyPreBuild) depInfo;
depFilePaths = depPreBuild.getDependencyFiles();
}
}
}
}
// Delete the files if they exist
if (depFilePaths != null) {
for (IPath dfp : depFilePaths) {
IPath depFilePath = getBuildWorkingDir().append(dfp);
IResource depFile = project.findMember(depFilePath);
if (depFile != null && depFile.exists()) {
try {
depFile.delete(true, new SubProgressMonitor(monitor, 1));
} catch (CoreException e) {
// This had better be allowed during a build
}
}
}
}
}
/**
* Returns the current build configuration.
*
* @return String
* @since 8.0
*/
protected IConfiguration getConfig() {
return config;
}
/**
* Returns the build target extension.
*
* @return String
* @since 8.0
*/
protected String getBuildTargetExt() {
return buildTargetExt;
}
/**
* Returns the build target name.
*
* @return String
* @since 8.0
*/
protected String getBuildTargetName() {
return buildTargetName;
}
/**
* @return Returns the deletedDirList.
*/
private Vector<IResource> getDeletedDirList() {
if (deletedDirList == null) {
deletedDirList = new Vector<>();
}
return deletedDirList;
}
private Vector<IResource> getDeletedFileList() {
if (deletedFileList == null) {
deletedFileList = new Vector<>();
}
return deletedFileList;
}
private List<IPath> getDependencyMakefiles(ToolInfoHolder h) {
if (h.dependencyMakefiles == null) {
h.dependencyMakefiles = new ArrayList<>();
}
return h.dependencyMakefiles;
}
/**
* Strips off the file extension from the argument and returns
* the name component in a <code>String</code>
*/
private String getFileName(IResource file) {
String answer = ""; //$NON-NLS-1$
String lastSegment = file.getName();
int extensionSeparator = lastSegment.lastIndexOf(DOT);
if (extensionSeparator != -1) {
answer = lastSegment.substring(0, extensionSeparator);
}
return answer;
}
/**
* Answers a Vector containing a list of directories that are invalid for the
* build for some reason. At the moment, the only reason a directory would
* not be considered for the build is if it contains a space in the relative
* path from the project root.
*
* @return a a list of directories that are invalid for the build
*/
private Vector<IResource> getInvalidDirList() {
if (invalidDirList == null) {
invalidDirList = new Vector<>();
}
return invalidDirList;
}
/**
* @return Collection of Containers which contain modified source files
*/
private Collection<IContainer> getModifiedList() {
if (modifiedList == null)
modifiedList = new LinkedHashSet<>();
return modifiedList;
}
/**
* @return Collection of subdirectories (IContainers) contributing source code to the build
*/
private Collection<IContainer> getSubdirList() {
if (subdirList == null)
subdirList = new LinkedHashSet<>();
return subdirList;
}
private void removeGeneratedDirectory(IContainer subDir) {
try {
// The source directory isn't empty
if (subDir.exists() && subDir.members().length > 0)
return;
} catch (CoreException e) {
// The resource doesn't exist so we should delete the output folder
}
// Figure out what the generated directory name is and delete it
IPath moduleRelativePath = subDir.getProjectRelativePath();
IPath buildRoot = getBuildWorkingDir();
if (buildRoot == null) {
return;
}
IPath moduleOutputPath = buildRoot.append(moduleRelativePath);
IFolder folder = project.getFolder(moduleOutputPath);
if (folder.exists()) {
try {
folder.delete(true, new SubProgressMonitor(monitor, 1));
} catch (CoreException e) {
// TODO Log this
}
}
}
protected void updateMonitor(String msg) {
if (monitor != null && !monitor.isCanceled()) {
monitor.subTask(msg);
monitor.worked(1);
}
}
/**
* Return the configuration's top build directory as an absolute path
*/
public IPath getTopBuildDir() {
return getPathForResource(project).append(getBuildWorkingDir());
}
/**
* Process a String denoting a filepath in a way compatible for GNU Make rules, handling
* windows drive letters and whitespace appropriately.
* <p><p>
* The context these paths appear in is on the right hand side of a rule header. i.e.
* <p><p>
* target : dep1 dep2 dep3
* <p>
* @param path the String denoting the path to process
* @throws NullPointerException is path is null
* @return a suitable Make rule compatible path
*/
/* see https://bugs.eclipse.org/bugs/show_bug.cgi?id=129782 */
public String ensurePathIsGNUMakeTargetRuleCompatibleSyntax(String path) {
boolean isQuotedOption = false;
if (path.startsWith("-")) { //$NON-NLS-1$
isQuotedOption = checkIfQuotedOption(path);
}
if (!isQuotedOption)
return escapeWhitespaces(ensureUnquoted(path));
return path;
}
private boolean checkIfQuotedOption(String path) {
Matcher m1 = doubleQuotedOption.matcher(path);
if (m1.matches())
return true;
Matcher m2 = singleQuotedOption.matcher(path);
if (m2.matches())
return true;
return false;
}
/**
* Strips outermost quotes of Strings of the form "a" and 'a' or returns the original
* string if the input is not of this form.
*
* @throws NullPointerException if path is null
* @return a String without the outermost quotes (if the input has them)
*/
public static String ensureUnquoted(String path) {
boolean doubleQuoted = path.startsWith("\"") && path.endsWith("\""); //$NON-NLS-1$ //$NON-NLS-2$
boolean singleQuoted = path.startsWith("'") && path.endsWith("'"); //$NON-NLS-1$ //$NON-NLS-2$
return doubleQuoted || singleQuoted ? path.substring(1, path.length() - 1) : path;
}
@Override
public void initialize(int buildKind, IConfiguration cfg, IBuilder builder, IProgressMonitor monitor) {
// Save the project so we can get path and member information
this.project = cfg.getOwner().getProject();
if (builder == null) {
builder = cfg.getEditableBuilder();
}
try {
projectResources = project.members();
} catch (CoreException e) {
projectResources = null;
}
// Save the monitor reference for reporting back to the user
this.monitor = monitor;
// Get the build info for the project
// this.info = info;
// Get the name of the build target
buildTargetName = cfg.getArtifactName();
// Get its extension
buildTargetExt = cfg.getArtifactExtension();
try {
//try to resolve the build macros in the target extension
buildTargetExt = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(buildTargetExt,
"", //$NON-NLS-1$
" ", //$NON-NLS-1$
IBuildMacroProvider.CONTEXT_CONFIGURATION, builder);
} catch (BuildMacroException e) {
}
try {
//try to resolve the build macros in the target name
String resolved = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(buildTargetName,
"", //$NON-NLS-1$
" ", //$NON-NLS-1$
IBuildMacroProvider.CONTEXT_CONFIGURATION, builder);
if (resolved != null) {
resolved = resolved.trim();
if (resolved.length() > 0)
buildTargetName = resolved;
}
} catch (BuildMacroException e) {
}
if (buildTargetExt == null) {
buildTargetExt = ""; //$NON-NLS-1$
}
// Cache the build tools
config = cfg;
this.builder = builder;
initToolInfos();
//set the top build dir path
initializeTopBuildDir(cfg.getName());
srcEntries = config.getSourceEntries();
if (srcEntries.length == 0) {
srcEntries = new ICSourceEntry[] {
new CSourceEntry(Path.EMPTY, null, ICSettingEntry.RESOLVED | ICSettingEntry.VALUE_WORKSPACE_PATH) };
} else {
ICConfigurationDescription cfgDes = ManagedBuildManager.getDescriptionForConfiguration(config);
srcEntries = CDataUtil.resolveEntries(srcEntries, cfgDes);
}
}
private void initToolInfos() {
toolInfos = PathSettingsContainer.createRootContainer();
IResourceInfo rcInfos[] = config.getResourceInfos();
for (IResourceInfo rcInfo : rcInfos) {
if (rcInfo.isExcluded() /*&& !((ResourceInfo)rcInfo).isRoot()*/)
continue;
ToolInfoHolder h = getToolInfo(rcInfo.getPath(), true);
if (rcInfo instanceof IFolderInfo) {
IFolderInfo fo = (IFolderInfo) rcInfo;
h.buildTools = fo.getFilteredTools();
h.buildToolsUsed = new boolean[h.buildTools.length];
h.gnuToolInfos = new ManagedBuildGnuToolInfo[h.buildTools.length];
} else {
IFileInfo fi = (IFileInfo) rcInfo;
h.buildTools = fi.getToolsToInvoke();
h.buildToolsUsed = new boolean[h.buildTools.length];
h.gnuToolInfos = new ManagedBuildGnuToolInfo[h.buildTools.length];
}
}
}
private ToolInfoHolder getToolInfo(IPath path) {
return getToolInfo(path, false);
}
private ToolInfoHolder getFolderToolInfo(IPath path) {
IResourceInfo rcInfo = config.getResourceInfo(path, false);
while (rcInfo instanceof IFileInfo) {
path = path.removeLastSegments(1);
rcInfo = config.getResourceInfo(path, false);
}
return getToolInfo(path, false);
}
private ToolInfoHolder getToolInfo(IPath path, boolean create) {
PathSettingsContainer child = toolInfos.getChildContainer(path, create, create);
ToolInfoHolder h = null;
if (child != null) {
h = (ToolInfoHolder) child.getValue();
if (h == null && create) {
h = new ToolInfoHolder();
child.setValue(h);
}
}
return h;
}
private void ensureTopBuildDir() throws CoreException {
IPath buildWorkingDir = getBuildWorkingDir();
if (buildWorkingDir != null) {
createDirectory(buildWorkingDir.toString());
}
}
private void initializeTopBuildDir(String configName) {
topBuildDir = project.getFolder(computeTopBuildDir(configName)).getFullPath();
}
/**
* Can be overwritten by a subclass to specify the top build directory to be
* used. Default implementation simply returns configuration name.
* <p>
* <strong>Note</strong>: be careful by overriding this method - all places in the custom code and related
* scripts using or referencing top build directory must also be changed to use same logic.
*
* @param configName name of the configuration
* @return project relative path for top build directory
* @since 8.7
*/
protected IPath computeTopBuildDir(String configName) {
return new Path(configName);
}
/**
* @return As many ".." as required to get from getBuildWorkingDir() to the project root.
*
* E.g. If getBuildWorkingDir() is "Debug", then the function returns "..". If
* getBuildWorkingDir() returns "x86/Debug" then "../.." is returned.
*
* @since 8.7
*/
public String reachProjectRoot() {
IPath buildWorkingDir = getBuildWorkingDir();
if (buildWorkingDir == null) {
return ROOT;
}
String root = ROOT;
int segCnt = buildWorkingDir.segmentCount();
for (int i = 1; i < segCnt; i++) {
root += SEPARATOR + ROOT;
}
return root;
}
}