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