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