blob: feb6995eba9249252bf6969a05714b37367d2318 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2016 Intel 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:
* Intel Corporation - Initial API and implementation
* IBM Corporation
*******************************************************************************/
package org.eclipse.cdt.managedbuilder.makegen.gnu;
import org.eclipse.cdt.managedbuilder.core.IBuildObject;
import org.eclipse.cdt.managedbuilder.core.IConfiguration;
import org.eclipse.cdt.managedbuilder.core.IResourceInfo;
import org.eclipse.cdt.managedbuilder.core.ITool;
import org.eclipse.cdt.managedbuilder.internal.macros.BuildMacroProvider;
import org.eclipse.cdt.managedbuilder.internal.macros.FileContextData;
import org.eclipse.cdt.managedbuilder.macros.IBuildMacroProvider;
import org.eclipse.cdt.managedbuilder.makegen.IManagedBuilderMakefileGenerator;
import org.eclipse.cdt.managedbuilder.makegen.IManagedDependencyCommands;
import org.eclipse.cdt.utils.EFSExtensionManager;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
/**
* This dependency calculator uses the GCC -MMD -MF -MP -MT options in order to
* generate .d files as a side effect of compilation.
* See bugzilla 108715 for the discussion of dependency management that led to
* the creation of this dependency calculator. Note also that this technique
* exhibits the failure modes discussed in comment #5.
*
* This class is used with DefaultGCCDependencyCalculator2.
*
* @since 3.1
* @noextend This class is not intended to be subclassed by clients.
* @noinstantiate This class is not intended to be instantiated by clients.
*/
public class DefaultGCCDependencyCalculator2Commands implements IManagedDependencyCommands {
// Member variables set by the constructor
IPath source;
IResource resource;
IBuildObject buildContext;
ITool tool;
IPath topBuildDirectory;
// Other Member variables
IProject project;
IPath sourceLocation;
IPath outputLocation;
boolean needExplicitRuleForFile;
Boolean genericCommands = null;
/**
* Constructor
*
* @param source The source file for which dependencies should be calculated
* The IPath can be either relative to the project directory, or absolute in the file system.
* @param resource The IResource corresponding to the source file.
* @param buildContext The IConfiguration or IResourceConfiguration that
* contains the context in which the source file will be built
* @param tool The tool associated with the source file
* @param topBuildDirectory The top build directory of the configuration. This is
* the working directory for the tool. This IPath is relative to the project directory.
*/
public DefaultGCCDependencyCalculator2Commands(IPath source, IResource resource, IBuildObject buildContext,
ITool tool, IPath topBuildDirectory) {
this.source = source;
this.resource = resource;
this.buildContext = buildContext;
this.tool = tool;
this.topBuildDirectory = topBuildDirectory;
// Compute the project
if (buildContext instanceof IConfiguration) {
IConfiguration config = (IConfiguration) buildContext;
project = (IProject) config.getOwner();
} else if (buildContext instanceof IResourceInfo) {
IResourceInfo rcInfo = (IResourceInfo) buildContext;
project = rcInfo.getParent().getOwner().getProject();
}
IPath projectPath = new Path(EFSExtensionManager.getDefault().getPathFromURI(project.getLocationURI()));
sourceLocation = (source.isAbsolute() ? source : projectPath.append(source));
outputLocation = projectPath.append(topBuildDirectory).append(getDependencyFiles()[0]);
// 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 = true;
if (resource != null) {
resourceNameRequiresExplicitRule = (resource.isLinked()
&& GnuMakefileGenerator.containsSpecialCharacters(sourceLocation.toString()))
|| (!resource.isLinked() && GnuMakefileGenerator
.containsSpecialCharacters(resource.getProjectRelativePath().toString()));
}
needExplicitRuleForFile = resourceNameRequiresExplicitRule
|| BuildMacroProvider.getReferencedExplitFileMacros(tool).length > 0
|| BuildMacroProvider.getReferencedExplitFileMacros(tool.getToolCommand(),
IBuildMacroProvider.CONTEXT_FILE,
new FileContextData(sourceLocation, outputLocation, null, tool)).length > 0;
if (needExplicitRuleForFile)
genericCommands = false;
}
/**
* Constructor. This constructor calls
* DefaultGCCDependencyCalculator2Commands(IPath source, IResource resource,
* IBuildObject buildContext, ITool tool, IPath topBuildDirectory) with a
* null resource. The net result of this is that dependency rules will
* always be explicit and will never use pattern rules, as it is impossible
* for the calculator to know whether the resource is linked or not.
*
* @param source
* The source file for which dependencies should be calculated
* The IPath can be either relative to the project directory, or
* absolute in the file system.
* @param buildContext
* The IConfiguration or IResourceConfiguration that contains the
* context in which the source file will be built
* @param tool
* The tool associated with the source file
* @param topBuildDirectory
* The top build directory of the configuration. This is the
* working directory for the tool. This IPath is relative to the
* project directory.
*
* @see #DefaultGCCDependencyCalculator2Commands(IPath source, IResource resource, IBuildObject buildContext, ITool tool, IPath topBuildDirectory)
*/
public DefaultGCCDependencyCalculator2Commands(IPath source, IBuildObject buildContext, ITool tool,
IPath topBuildDirectory) {
this(source, (IResource) null, buildContext, tool, topBuildDirectory);
}
@Override
public boolean areCommandsGeneric() {
if (genericCommands == null)
genericCommands = true;
return genericCommands.booleanValue();
}
@Override
public String[] getDependencyCommandOptions() {
String[] options = new String[4];
// -MMD
options[0] = "-MMD"; //$NON-NLS-1$
// -MP
options[1] = "-MP"; //$NON-NLS-1$
// -MF$(@:%.o=%.d)
// Due to bug in GNU make $(@:%.o=%.d) applied to "/buggy path_with_3_spaces/f.o"
// becomes "/buggy path_with_3_spaces/f.d". To avoid this we have to insert
// filename explicitly instead of substitution rule
if (needExplicitRuleForFile) {
IPath outPath = getDependencyFiles()[0];
options[2] = "-MF\"" + outPath.toString() + "\""; //$NON-NLS-1$ //$NON-NLS-2$
} else {
options[2] = "-MF\"$(@:%.o=%.d)\""; //$NON-NLS-1$
}
// -MT"$@"
options[3] = "-MT\"$@\""; //$NON-NLS-1$
return options;
}
@Override
public IPath[] getDependencyFiles() {
// The source file is project relative and the dependency file is top build directory relative
// Remove the source extension and add the dependency extension
IPath depFilePath = source.removeFileExtension().addFileExtension(IManagedBuilderMakefileGenerator.DEP_EXT);
// Remember that the source folder hierarchy and the build output folder hierarchy are the same
// but if this is a generated resource, then it may already be under the top build directory
if (!depFilePath.isAbsolute()) {
if (topBuildDirectory.isPrefixOf(depFilePath)) {
depFilePath = depFilePath.removeFirstSegments(1);
}
}
IPath[] paths = new IPath[1];
paths[0] = depFilePath;
return paths;
}
@Override
public String[] getPostToolDependencyCommands() {
// Nothing
return null;
}
@Override
public String[] getPreToolDependencyCommands() {
// Nothing
return null;
}
@Override
public IBuildObject getBuildContext() {
return buildContext;
}
@Override
public IPath getSource() {
return source;
}
@Override
public ITool getTool() {
return tool;
}
@Override
public IPath getTopBuildDirectory() {
return topBuildDirectory;
}
}