/* * * Copyright 2001-2005 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.IOException; import java.util.Enumeration; import java.util.Vector; import net.sf.antcontrib.cpptasks.AssemblerDef; import net.sf.antcontrib.cpptasks.CCTask; import net.sf.antcontrib.cpptasks.CUtil; import net.sf.antcontrib.cpptasks.ProcessorDef; import net.sf.antcontrib.cpptasks.TargetDef; import net.sf.antcontrib.cpptasks.types.CommandLineArgument; import org.apache.tools.ant.BuildException; /** * An abstract Assembler implementation which uses an external program to * perform the assemble. * */ public abstract class CommandLineAssembler extends AbstractAssembler { private String command; private String identifier; private String identifierArg; protected CommandLineAssembler (String command, String identifierArg, String[] sourceExtensions, String[] headerExtensions, String outputSuffix) { super(sourceExtensions, headerExtensions, outputSuffix); this.command = command; this.identifierArg = identifierArg; } abstract protected void addImpliedArgs(Vector args, boolean debug, Boolean defaultflag); /** * Adds command-line arguments for include directories. * * If relativeArgs is not null will add corresponding relative paths include * switches to that vector (for use in building a configuration identifier * that is consistent between machines). * * @param baseDirPaths * A vector containing the parts of the working directory, * produced by CUtil.DecomposeFile. * @param includeDirs * Array of include directory paths * @param args * Vector of command line arguments used to execute the task * @param relativeArgs * Vector of command line arguments used to build the * configuration identifier */ protected void addIncludes(String baseDirPath, File[] includeDirs, Vector args, Vector relativeArgs, StringBuffer includePathId) { for (int i = 0; i < includeDirs.length; i++) { args.addElement(getIncludeDirSwitch(includeDirs[i] .getAbsolutePath())); if (relativeArgs != null) { String relative = CUtil.getRelativePath(baseDirPath, includeDirs[i]); relativeArgs.addElement(getIncludeDirSwitch(relative)); if (includePathId != null) { if (includePathId.length() == 0) { includePathId.append("/I"); } else { includePathId.append(" /I"); } includePathId.append(relative); } } } } abstract protected String getIncludeDirSwitch(String source); /** * Assembles a source file * */ public void assembler(CCTask task, File outputDir, String[] sourceFiles, String[] args, String[] endArgs) throws BuildException { String command = getCommand(); int baseLength = command.length() + args.length + endArgs.length; for (int i = 0; i < args.length; i++) { baseLength += args[i].length(); } for (int i = 0; i < endArgs.length; i++) { baseLength += endArgs[i].length(); } if (baseLength > getMaximumCommandLength()) { throw new BuildException( "Command line is over maximum length without sepcifying source file"); } int maxInputFilesPerCommand = getMaximumInputFilesPerCommand(); int argumentCountPerInputFile = getArgumentCountPerInputFIle(); for (int sourceIndex = 0; sourceIndex < sourceFiles.length;) { int cmdLength = baseLength; int firstFileNextExec; for (firstFileNextExec = sourceIndex; firstFileNextExec < sourceFiles.length && (firstFileNextExec - sourceIndex) < maxInputFilesPerCommand; firstFileNextExec++) { cmdLength += getTotalArgumentLengthForInputFile(outputDir, sourceFiles[firstFileNextExec]); if (cmdLength >= getMaximumCommandLength()) break; } if (firstFileNextExec == sourceIndex) { throw new BuildException( "Extremely long file name, can't fit on command line"); } int argCount = args.length + 1 + endArgs.length + (firstFileNextExec - sourceIndex) * argumentCountPerInputFile; String[] commandline = new String[argCount]; int index = 0; commandline[index++] = command; for (int j = 0; j < args.length; j++) { commandline[index++] = args[j]; } for (int j = sourceIndex; j < firstFileNextExec; j++) { for (int k = 0; k < argumentCountPerInputFile; k++) { commandline[index++] = getInputFileArgument(outputDir, sourceFiles[j], k); } } for (int j = 0; j < endArgs.length; j++) { commandline[index++] = endArgs[j]; } int retval = runCommand(task, outputDir, commandline); // if with monitor, add more code if (retval != 0) { throw new BuildException(this.getCommand() + " failed with return code " + retval, task .getLocation()); } sourceIndex = firstFileNextExec; } } protected AssemblerConfiguration createConfiguration(final CCTask task, final LinkType linkType, final ProcessorDef[] baseDefs, final AssemblerDef specificDef, final TargetDef targetPlatform) { Vector args = new Vector(); AssemblerDef[] defaultProviders = new AssemblerDef[baseDefs.length + 1]; for (int i = 0; i < baseDefs.length; i++) { defaultProviders[i + 1] = (AssemblerDef) baseDefs[i]; } defaultProviders[0] = specificDef; Vector cmdArgs = new Vector(); // // add command line arguments inherited from element // any "extends" and finally and specific AssemblerDef // CommandLineArgument[] commandArgs; for (int i = defaultProviders.length - 1; i >= 0; i--) { commandArgs = defaultProviders[i].getActiveProcessorArgs(); for (int j = 0; j < commandArgs.length; j++) { if (commandArgs[j].getLocation() == 0) { args.addElement(commandArgs[j].getValue()); } else { cmdArgs.addElement(commandArgs[j]); } } } // omit param boolean debug = specificDef.getDebug(baseDefs, 0); Boolean defaultflag = specificDef.getDefaultflag(defaultProviders, 1); this.addImpliedArgs(args, debug, defaultflag); // // Want to have distinct set of arguments with relative // path names for includes that are used to build // the configuration identifier // Vector relativeArgs = (Vector) args.clone(); // // add all active include an // StringBuffer includePathIdentifier = new StringBuffer(); File baseDir = specificDef.getProject().getBaseDir(); String baseDirPath; try { baseDirPath = baseDir.getCanonicalPath(); } catch (IOException ex) { baseDirPath = baseDir.toString(); } Vector includePath = new Vector(); Vector sysIncludePath = new Vector(); for (int i = defaultProviders.length - 1; i >= 0; i--) { String[] incPath = defaultProviders[i].getActiveIncludePaths(); for (int j = 0; j < incPath.length; j++) { includePath.addElement(incPath[j]); } incPath = defaultProviders[i].getActiveSysIncludePaths(); for (int j = 0; j < incPath.length; j++) { sysIncludePath.addElement(incPath[j]); } } File[] incPath = new File[includePath.size()]; for (int i = 0; i < includePath.size(); i++) { incPath[i] = new File((String) includePath.elementAt(i)); } File[] sysIncPath = new File[sysIncludePath.size()]; for (int i = 0; i < sysIncludePath.size(); i++) { sysIncPath[i] = new File((String) sysIncludePath.elementAt(i)); } addIncludes(baseDirPath, incPath, args, relativeArgs, includePathIdentifier); addIncludes(baseDirPath, sysIncPath, args, null, null); StringBuffer buf = new StringBuffer(getIdentifier()); for (int i = 0; i < relativeArgs.size(); i++) { buf.append(relativeArgs.elementAt(i)); buf.append(' '); } buf.setLength(buf.length() - 1); Enumeration argEnum = cmdArgs.elements(); int endCount = 0; while (argEnum.hasMoreElements()) { CommandLineArgument arg = (CommandLineArgument) argEnum .nextElement(); switch (arg.getLocation()) { case 1: args.addElement(arg.getValue()); break; case 2: endCount++; break; } } String[] endArgs = new String[endCount]; argEnum = cmdArgs.elements(); int index = 0; while (argEnum.hasMoreElements()) { CommandLineArgument arg = (CommandLineArgument) argEnum .nextElement(); if (arg.getLocation() == 2) { endArgs[index++] = arg.getValue(); } } String[] argArray = new String[args.size()]; args.copyInto(argArray); return new CommandLineAssemblerConfiguration(this, incPath, sysIncPath, new File[0], argArray, true, endArgs, new String[0]); } protected int getArgumentCountPerInputFile() { return 1; } protected abstract File[] getEnvironmentIncludePath(); 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 String getCommand() { return command; } abstract public int getMaximumCommandLength(); public void setCommand(String command) { this.command = command; } protected int getTotalArgumentLengthForInputFile(File outputDir, String inputFile) { return inputFile.length() + 1; } protected int runCommand(CCTask task, File workingDir, String[] cmdline) throws BuildException { return CUtil.runCommand(task, workingDir, cmdline, false, null); } protected int getMaximumInputFilesPerCommand() { return Integer.MAX_VALUE; } protected int getArgumentCountPerInputFIle() { return 1; } protected String getInputFileArgument(File outputDir, String filename, int index) { // // if there is an embedded space, // must enclose in quotes if (filename.indexOf(' ') >= 0) { StringBuffer buf = new StringBuffer("\""); buf.append(filename); buf.append("\""); return buf.toString(); } return filename; } }