/* * * 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 net.sf.antcontrib.cpptasks.VersionInfo; 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); 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, VersionInfo versionInfo) { 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 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 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); addImpliedArgs(debug, linkType, preargs); 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); // FREEHEP call at all times // 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, debug,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[] getOutputFileNames(String baseName, VersionInfo versionInfo) { return new String[] { 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 // 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)); // FREEHEP, return the shortest return quoteFilename(buf, sourceFile.length() > relativePath.length() ? relativePath : sourceFile); } /** * 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; } }