| /******************************************************************************* |
| * 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; |
| } |
| |
| } |