blob: 4161469a8fe68e6bd0c9bdeb809144a354c679f1 [file] [log] [blame]
/*
*
* Copyright 2002-2004 The Ant-Contrib project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.sf.antcontrib.cpptasks.compiler;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Vector;
import net.sf.antcontrib.cpptasks.CCTask;
import net.sf.antcontrib.cpptasks.CUtil;
import net.sf.antcontrib.cpptasks.LinkerDef;
import net.sf.antcontrib.cpptasks.ProcessorDef;
import net.sf.antcontrib.cpptasks.ProcessorParam;
import net.sf.antcontrib.cpptasks.types.CommandLineArgument;
import net.sf.antcontrib.cpptasks.types.LibrarySet;
import net.sf.antcontrib.cpptasks.TargetDef;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.types.Environment;
/**
* An abstract Linker implementation that performs the link via an external
* command.
*
* @author Adam Murdoch
*/
public abstract class CommandLineLinker extends AbstractLinker
{
private String command;
private Environment env = null;
private String identifier;
private String identifierArg;
private boolean isLibtool;
private String[] librarySets;
private CommandLineLinker libtoolLinker;
private boolean newEnvironment = false;
private String outputSuffix;
/** Creates a comand line linker invocation */
public CommandLineLinker(String command,
String identifierArg,
String[] extensions,
String[] ignoredExtensions, String outputSuffix,
boolean isLibtool, CommandLineLinker libtoolLinker)
{
super(extensions, ignoredExtensions);
this.command = command;
this.identifierArg = identifierArg;
this.outputSuffix = outputSuffix;
this.isLibtool = isLibtool;
this.libtoolLinker = libtoolLinker;
}
protected abstract void addBase(long base, Vector args);
protected abstract void addFixed(Boolean fixed, Vector args);
abstract protected void addImpliedArgs(boolean debug,
LinkType linkType, Vector args, Boolean defaultflag);
protected abstract void addIncremental(boolean incremental, Vector args);
//
// Windows processors handle these through file list
//
protected String[] addLibrarySets(CCTask task, LibrarySet[] libsets, Vector preargs,
Vector midargs, Vector endargs) {
return null;
}
protected abstract void addMap(boolean map, Vector args);
protected abstract void addStack(int stack, Vector args);
protected abstract void addEntry(String entry, Vector args);
protected LinkerConfiguration createConfiguration(
CCTask task,
LinkType linkType,
ProcessorDef[] baseDefs, LinkerDef specificDef, TargetDef targetPlatform) {
Vector preargs = new Vector();
Vector midargs = new Vector();
Vector endargs = new Vector();
Vector[] args = new Vector[] { preargs, midargs, endargs };
LinkerDef[] defaultProviders = new LinkerDef[baseDefs.length+1];
defaultProviders[0] = specificDef;
for(int i = 0; i < baseDefs.length; i++) {
defaultProviders[i+1] = (LinkerDef) baseDefs[i];
}
//
// add command line arguments inherited from <cc> element
// any "extends" and finally the specific CompilerDef
CommandLineArgument[] commandArgs;
for(int i = defaultProviders.length-1; i >= 0; i--) {
commandArgs = defaultProviders[i].getActiveProcessorArgs();
for(int j = 0; j < commandArgs.length; j++) {
args[commandArgs[j].getLocation()].
addElement(commandArgs[j].getValue());
}
}
Vector params = new Vector();
//
// add command line arguments inherited from <cc> element
// any "extends" and finally the specific CompilerDef
ProcessorParam[] paramArray;
for (int i = defaultProviders.length - 1; i >= 0; i--) {
paramArray = defaultProviders[i].getActiveProcessorParams();
for (int j = 0; j < paramArray.length; j++) {
params.add(paramArray[j]);
}
}
paramArray = (ProcessorParam[])(params.toArray(new ProcessorParam[params.size()]));
boolean debug = specificDef.getDebug(baseDefs,0);
String startupObject = getStartupObject(linkType);
Boolean defaultflag = specificDef.getDefaultflag(defaultProviders, 1);
addImpliedArgs(debug, linkType, preargs, defaultflag);
addIncremental(specificDef.getIncremental(defaultProviders,1), preargs);
addFixed(specificDef.getFixed(defaultProviders,1), preargs);
addMap(specificDef.getMap(defaultProviders,1), preargs);
addBase(specificDef.getBase(defaultProviders,1), preargs);
addStack(specificDef.getStack(defaultProviders,1), preargs);
addEntry(specificDef.getEntry(defaultProviders, 1), preargs);
String[] libnames = null;
LibrarySet[] libsets = specificDef.getActiveLibrarySets(defaultProviders,1);
if (libsets.length > 0) {
libnames = addLibrarySets(task, libsets, preargs, midargs, endargs);
}
StringBuffer buf = new StringBuffer(getIdentifier());
for (int i = 0; i < 3; i++) {
Enumeration argenum = args[i].elements();
while (argenum.hasMoreElements()) {
buf.append(' ');
buf.append(argenum.nextElement().toString());
}
}
String configId = buf.toString();
String[][] options = new String[][] {
new String[args[0].size() + args[1].size()],
new String[args[2].size()] };
args[0].copyInto(options[0]);
int offset = args[0].size();
for (int i = 0; i < args[1].size(); i++) {
options[0][i+offset] = (String) args[1].elementAt(i);
}
args[2].copyInto(options[1]);
boolean rebuild = specificDef.getRebuild(baseDefs,0);
boolean map = specificDef.getMap(defaultProviders,1);
//task.log("libnames:"+libnames.length, Project.MSG_VERBOSE);
return new CommandLineLinkerConfiguration(this,configId,options,
paramArray,
rebuild,map,libnames, startupObject);
}
/**
* Allows drived linker to decorate linker option.
* Override by GccLinker to prepend a "-Wl," to
* pass option to through gcc to linker.
*
* @param buf buffer that may be used and abused in the decoration process,
* must not be null.
* @param arg linker argument
*/
protected String decorateLinkerOption(StringBuffer buf, String arg) {
return arg;
}
protected final String getCommand() {
return command;
}
protected abstract String getCommandFileSwitch(String commandFile);
public String getIdentifier() {
if(identifier == null) {
if (identifierArg == null) {
identifier = getIdentifier(new String[] { command }, command);
} else {
identifier = getIdentifier(new String[] { command, identifierArg },
command);
}
}
return identifier;
}
public final CommandLineLinker getLibtoolLinker() {
if (libtoolLinker != null) {
return libtoolLinker;
}
return this;
}
protected abstract int getMaximumCommandLength();
public String getOutputFileName(String baseName) {
return baseName + outputSuffix;
}
protected String[] getOutputFileSwitch(CCTask task, String outputFile) {
return getOutputFileSwitch(outputFile);
}
protected abstract String[] getOutputFileSwitch(String outputFile);
protected String getStartupObject(LinkType linkType) {
return null;
}
/**
* Performs a link using a command line linker
*
*/
public void link(CCTask task,
File outputFile,
String[] sourceFiles,
CommandLineLinkerConfiguration config)
throws BuildException
{
File parentDir = new File(outputFile.getParent());
String parentPath;
try {
parentPath = parentDir.getCanonicalPath();
} catch(IOException ex) {
parentPath = parentDir.getAbsolutePath();
}
String[] execArgs = prepareArguments(task, parentPath,outputFile.getName(),
sourceFiles, config);
int commandLength = 0;
for(int i = 0; i < execArgs.length; i++) {
commandLength += execArgs[i].length() + 1;
}
//
// if command length exceeds maximum
// (1024 for Windows) then create a temporary
// file containing everything but the command name
if(commandLength >= this.getMaximumCommandLength()) {
try {
execArgs = prepareResponseFile(outputFile,execArgs);
}
catch(IOException ex) {
throw new BuildException(ex);
}
}
int retval = runCommand(task,parentDir,execArgs);
//
// if the process returned a failure code then
// throw an BuildException
//
if(retval != 0) {
//
// construct the exception
//
throw new BuildException(this.getCommand() + " failed with return code " + retval, task.getLocation());
}
}
/**
* Prepares argument list for exec command. Will return null
* if command line would exceed allowable command line buffer.
*
* @param outputFile linker output file
* @param sourceFiles linker input files (.obj, .o, .res)
* @param args linker arguments
* @return arguments for runTask
*/
protected String[] prepareArguments(
CCTask task,
String outputDir,
String outputFile,
String[] sourceFiles,
CommandLineLinkerConfiguration config) {
String[] preargs = config.getPreArguments();
String[] endargs = config.getEndArguments();
String outputSwitch[] = getOutputFileSwitch(task, outputFile);
int allArgsCount = preargs.length + 1 + outputSwitch.length +
sourceFiles.length + endargs.length;
if (isLibtool) {
allArgsCount++;
}
String[] allArgs = new String[allArgsCount];
int index = 0;
if (isLibtool) {
allArgs[index++] = "libtool";
}
allArgs[index++] = this.getCommand();
StringBuffer buf = new StringBuffer();
for (int i = 0; i < preargs.length; i++) {
allArgs[index++] = decorateLinkerOption(buf, preargs[i]);
}
for (int i = 0; i < outputSwitch.length; i++) {
allArgs[index++] = outputSwitch[i];
}
for (int i = 0; i < sourceFiles.length; i++) {
allArgs[index++] = prepareFilename(buf,outputDir,sourceFiles[i]);
}
for (int i = 0; i < endargs.length; i++) {
allArgs[index++] = decorateLinkerOption(buf, endargs[i]);
}
return allArgs;
}
/**
* Processes filename into argument form
*
*/
protected String prepareFilename(StringBuffer buf,
String outputDir, String sourceFile) {
String relativePath = CUtil.getRelativePath(outputDir,
new File(sourceFile));
return quoteFilename(buf,relativePath);
}
/**
* Prepares argument list to execute the linker using a
* response file.
*
* @param outputFile linker output file
* @param args output of prepareArguments
* @return arguments for runTask
*/
protected String[] prepareResponseFile(File outputFile,String[] args) throws IOException
{
String baseName = outputFile.getName();
File commandFile = new File(outputFile.getParent(),baseName + ".rsp");
FileWriter writer = new FileWriter(commandFile);
int execArgCount = 1;
if (isLibtool) {
execArgCount++;
}
String[] execArgs = new String[execArgCount+1];
for (int i = 0; i < execArgCount; i++) {
execArgs[i] = args[i];
}
execArgs[execArgCount] = getCommandFileSwitch(commandFile.toString());
for(int i = execArgCount; i < args.length; i++) {
//
// if embedded space and not quoted then
// quote argument
if (args[i].indexOf(" ") >= 0 && args[i].charAt(0) != '\"') {
writer.write('\"');
writer.write(args[i]);
writer.write("\"\n");
} else {
writer.write(args[i]);
writer.write('\n');
}
}
writer.close();
return execArgs;
}
protected String quoteFilename(StringBuffer buf,String filename) {
if(filename.indexOf(' ') >= 0) {
buf.setLength(0);
buf.append('\"');
buf.append(filename);
buf.append('\"');
return buf.toString();
}
return filename;
}
/**
* This method is exposed so test classes can overload
* and test the arguments without actually spawning the
* compiler
*/
protected int runCommand(CCTask task, File workingDir,String[] cmdline)
throws BuildException {
return CUtil.runCommand(task,workingDir,cmdline, newEnvironment, env);
}
protected final void setCommand(String command) {
this.command = command;
}
}