--- /dev/null
+/*\r
+ * \r
+ * Copyright 2002-2004 The Ant-Contrib project\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package net.sf.antcontrib.cpptasks.compiler;\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.util.Enumeration;\r
+import java.util.Vector;\r
+import net.sf.antcontrib.cpptasks.CCTask;\r
+import net.sf.antcontrib.cpptasks.CUtil;\r
+import net.sf.antcontrib.cpptasks.CompilerDef;\r
+import net.sf.antcontrib.cpptasks.ProcessorDef;\r
+import net.sf.antcontrib.cpptasks.ProcessorParam;\r
+import net.sf.antcontrib.cpptasks.types.CommandLineArgument;\r
+import net.sf.antcontrib.cpptasks.types.UndefineArgument;\r
+import net.sf.antcontrib.cpptasks.TargetDef;\r
+import org.apache.tools.ant.BuildException;\r
+import org.apache.tools.ant.types.Environment;\r
+import net.sf.antcontrib.cpptasks.OptimizationEnum;;\r
+/**\r
+ * An abstract Compiler implementation which uses an external program to\r
+ * perform the compile.\r
+ * \r
+ * @author Adam Murdoch\r
+ */\r
+public abstract class CommandLineCompiler extends AbstractCompiler {\r
+ private String command;\r
+ private final Environment env;\r
+ private String identifier;\r
+ private String identifierArg;\r
+ private boolean libtool;\r
+ private CommandLineCompiler libtoolCompiler;\r
+ private final boolean newEnvironment;\r
+ protected CommandLineCompiler(String command, String identifierArg,\r
+ String[] sourceExtensions, String[] headerExtensions,\r
+ String outputSuffix, boolean libtool,\r
+ CommandLineCompiler libtoolCompiler, boolean newEnvironment,\r
+ Environment env) {\r
+ super(sourceExtensions, headerExtensions, outputSuffix);\r
+ this.command = command;\r
+ if (libtool && libtoolCompiler != null) {\r
+ throw new java.lang.IllegalArgumentException(\r
+ "libtoolCompiler should be null when libtool is true");\r
+ }\r
+ this.libtool = libtool;\r
+ this.libtoolCompiler = libtoolCompiler;\r
+ this.identifierArg = identifierArg;\r
+ this.newEnvironment = newEnvironment;\r
+ this.env = env;\r
+ }\r
+ abstract protected void addImpliedArgs(Vector args, boolean debug,\r
+ boolean multithreaded, boolean exceptions, LinkType linkType,\r
+ Boolean rtti, OptimizationEnum optimization, Boolean defaultflag);\r
+ /**\r
+ * Adds command-line arguments for include directories.\r
+ * \r
+ * If relativeArgs is not null will add corresponding relative paths\r
+ * include switches to that vector (for use in building a configuration\r
+ * identifier that is consistent between machines).\r
+ * \r
+ * @param baseDirPaths\r
+ * A vector containing the parts of the working directory,\r
+ * produced by CUtil.DecomposeFile.\r
+ * @param includeDirs\r
+ * Array of include directory paths\r
+ * @param args\r
+ * Vector of command line arguments used to execute the task\r
+ * @param relativeArgs\r
+ * Vector of command line arguments used to build the\r
+ * configuration identifier\r
+ */\r
+ protected void addIncludes(String baseDirPath, File[] includeDirs,\r
+ Vector args, Vector relativeArgs, StringBuffer includePathId) {\r
+ for (int i = 0; i < includeDirs.length; i++) {\r
+ args.addElement(getIncludeDirSwitch(includeDirs[i]\r
+ .getAbsolutePath()));\r
+ if (relativeArgs != null) {\r
+ String relative = CUtil.getRelativePath(baseDirPath,\r
+ includeDirs[i]);\r
+ relativeArgs.addElement(getIncludeDirSwitch(relative));\r
+ if (includePathId != null) {\r
+ if (includePathId.length() == 0) {\r
+ includePathId.append("/I");\r
+ } else {\r
+ includePathId.append(" /I");\r
+ }\r
+ includePathId.append(relative);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ abstract protected void addWarningSwitch(Vector args, int warnings);\r
+ protected void buildDefineArguments(CompilerDef[] defs, Vector args) {\r
+ //\r
+ // assume that we aren't inheriting defines from containing <cc>\r
+ //\r
+ UndefineArgument[] merged = defs[0].getActiveDefines();\r
+ for (int i = 1; i < defs.length; i++) {\r
+ //\r
+ // if we are inheriting, merge the specific defines with the\r
+ // containing defines\r
+ merged = UndefineArgument.merge(defs[i].getActiveDefines(), merged);\r
+ }\r
+ StringBuffer buf = new StringBuffer(30);\r
+ for (int i = 0; i < merged.length; i++) {\r
+ buf.setLength(0);\r
+ UndefineArgument current = merged[i];\r
+ if (current.isDefine()) {\r
+ getDefineSwitch(buf, current.getName(), current.getValue());\r
+ } else {\r
+ getUndefineSwitch(buf, current.getName());\r
+ }\r
+ args.addElement(buf.toString());\r
+ }\r
+ }\r
+ /**\r
+ * Compiles a source file.\r
+ * \r
+ * @author Curt Arnold\r
+ */\r
+ public void compile(CCTask task, File outputDir, String[] sourceFiles,\r
+ String[] args, String[] endArgs, boolean relentless,\r
+ CommandLineCompilerConfiguration config, ProgressMonitor monitor)\r
+ throws BuildException {\r
+ BuildException exc = null;\r
+ //\r
+ // determine length of executable name and args\r
+ //\r
+ String command = getCommand();\r
+ int baseLength = command.length() + args.length + endArgs.length;\r
+ if (libtool) {\r
+ baseLength += 8;\r
+ }\r
+ for (int i = 0; i < args.length; i++) {\r
+ baseLength += args[i].length();\r
+ }\r
+ for (int i = 0; i < endArgs.length; i++) {\r
+ baseLength += endArgs[i].length();\r
+ }\r
+ if (baseLength > getMaximumCommandLength()) {\r
+ throw new BuildException(\r
+ "Command line is over maximum length without specifying source file");\r
+ }\r
+ //\r
+ // typically either 1 or Integer.MAX_VALUE\r
+ //\r
+ int maxInputFilesPerCommand = getMaximumInputFilesPerCommand();\r
+ int argumentCountPerInputFile = getArgumentCountPerInputFile();\r
+ for (int sourceIndex = 0; sourceIndex < sourceFiles.length;) {\r
+ int cmdLength = baseLength;\r
+ int firstFileNextExec;\r
+ for (firstFileNextExec = sourceIndex; firstFileNextExec < sourceFiles.length\r
+ && (firstFileNextExec - sourceIndex) < maxInputFilesPerCommand; firstFileNextExec++) {\r
+ cmdLength += getTotalArgumentLengthForInputFile(outputDir,\r
+ sourceFiles[firstFileNextExec]);\r
+ if (cmdLength >= getMaximumCommandLength())\r
+ break;\r
+ }\r
+ if (firstFileNextExec == sourceIndex) {\r
+ throw new BuildException(\r
+ "Extremely long file name, can't fit on command line");\r
+ }\r
+ int argCount = args.length + 1 + endArgs.length\r
+ + (firstFileNextExec - sourceIndex)\r
+ * argumentCountPerInputFile;\r
+ if (libtool) {\r
+ argCount++;\r
+ }\r
+ String[] commandline = new String[argCount];\r
+ int index = 0;\r
+ if (libtool) {\r
+ commandline[index++] = "libtool";\r
+ }\r
+ commandline[index++] = command;\r
+ for (int j = 0; j < args.length; j++) {\r
+ commandline[index++] = args[j];\r
+ }\r
+ for (int j = sourceIndex; j < firstFileNextExec; j++) {\r
+ for (int k = 0; k < argumentCountPerInputFile; k++) {\r
+ commandline[index++] = getInputFileArgument(outputDir,\r
+ sourceFiles[j], k);\r
+ }\r
+ }\r
+ for (int j = 0; j < endArgs.length; j++) {\r
+ commandline[index++] = endArgs[j];\r
+ }\r
+ int retval = runCommand(task, outputDir, commandline);\r
+ if (monitor != null) {\r
+ String[] fileNames = new String[firstFileNextExec - sourceIndex];\r
+ for (int j = 0; j < fileNames.length; j++) {\r
+ fileNames[j] = sourceFiles[sourceIndex + j];\r
+ }\r
+ monitor.progress(fileNames);\r
+ }\r
+ //\r
+ // if the process returned a failure code and\r
+ // we aren't holding an exception from an earlier\r
+ // interation\r
+ if (retval != 0 && exc == null) {\r
+ //\r
+ // construct the exception\r
+ //\r
+ exc = new BuildException(this.getCommand()\r
+ + " failed with return code " + retval, task\r
+ .getLocation());\r
+ //\r
+ // and throw it now unless we are relentless\r
+ //\r
+ if (!relentless) {\r
+ throw exc;\r
+ }\r
+ }\r
+ sourceIndex = firstFileNextExec;\r
+ }\r
+ //\r
+ // if the compiler returned a failure value earlier\r
+ // then throw an exception\r
+ if (exc != null) {\r
+ throw exc;\r
+ }\r
+ }\r
+ protected CompilerConfiguration createConfiguration(final CCTask task,\r
+ final LinkType linkType, \r
+ final ProcessorDef[] baseDefs, \r
+ final CompilerDef specificDef,\r
+ final TargetDef targetPlatform) {\r
+ Vector args = new Vector();\r
+ CompilerDef[] defaultProviders = new CompilerDef[baseDefs.length + 1];\r
+ for (int i = 0; i < baseDefs.length; i++) {\r
+ defaultProviders[i + 1] = (CompilerDef) baseDefs[i];\r
+ }\r
+ defaultProviders[0] = specificDef;\r
+ Vector cmdArgs = new Vector();\r
+ //\r
+ // add command line arguments inherited from <cc> element\r
+ // any "extends" and finally the specific CompilerDef\r
+ CommandLineArgument[] commandArgs;\r
+ for (int i = defaultProviders.length - 1; i >= 0; i--) {\r
+ commandArgs = defaultProviders[i].getActiveProcessorArgs();\r
+ for (int j = 0; j < commandArgs.length; j++) {\r
+ if (commandArgs[j].getLocation() == 0) {\r
+ args.addElement(commandArgs[j].getValue());\r
+ } else {\r
+ cmdArgs.addElement(commandArgs[j]);\r
+ }\r
+ }\r
+ }\r
+ Vector params = new Vector();\r
+ //\r
+ // add command line arguments inherited from <cc> element\r
+ // any "extends" and finally the specific CompilerDef\r
+ ProcessorParam[] paramArray;\r
+ for (int i = defaultProviders.length - 1; i >= 0; i--) {\r
+ paramArray = defaultProviders[i].getActiveProcessorParams();\r
+ for (int j = 0; j < paramArray.length; j++) {\r
+ params.add(paramArray[j]);\r
+ }\r
+ }\r
+ paramArray = (ProcessorParam[]) (params\r
+ .toArray(new ProcessorParam[params.size()]));\r
+ boolean multithreaded = specificDef.getMultithreaded(defaultProviders,\r
+ 1);\r
+ boolean debug = specificDef.getDebug(baseDefs, 0);\r
+ boolean exceptions = specificDef.getExceptions(defaultProviders, 1);\r
+ Boolean rtti = specificDef.getRtti(defaultProviders, 1);\r
+ Boolean defaultflag = specificDef.getDefaultflag(defaultProviders, 1);\r
+ OptimizationEnum optimization = specificDef.getOptimization(defaultProviders, 1);\r
+ this.addImpliedArgs(args, debug, multithreaded, exceptions, linkType, rtti, optimization, defaultflag);\r
+ //\r
+ // add all appropriate defines and undefines\r
+ //\r
+ buildDefineArguments(defaultProviders, args);\r
+ //\r
+ // Want to have distinct set of arguments with relative\r
+ // path names for includes that are used to build\r
+ // the configuration identifier\r
+ //\r
+ Vector relativeArgs = (Vector) args.clone();\r
+ //\r
+ // add all active include and sysincludes\r
+ //\r
+ StringBuffer includePathIdentifier = new StringBuffer();\r
+ File baseDir = specificDef.getProject().getBaseDir();\r
+ String baseDirPath;\r
+ try {\r
+ baseDirPath = baseDir.getCanonicalPath();\r
+ } catch (IOException ex) {\r
+ baseDirPath = baseDir.toString();\r
+ }\r
+ Vector includePath = new Vector();\r
+ Vector sysIncludePath = new Vector();\r
+ for (int i = defaultProviders.length - 1; i >= 0; i--) {\r
+ String[] incPath = defaultProviders[i].getActiveIncludePaths();\r
+ for (int j = 0; j < incPath.length; j++) {\r
+ includePath.addElement(incPath[j]);\r
+ }\r
+ incPath = defaultProviders[i].getActiveSysIncludePaths();\r
+ for (int j = 0; j < incPath.length; j++) {\r
+ sysIncludePath.addElement(incPath[j]);\r
+ }\r
+ }\r
+ File[] incPath = new File[includePath.size()];\r
+ for (int i = 0; i < includePath.size(); i++) {\r
+ incPath[i] = new File((String) includePath.elementAt(i));\r
+ }\r
+ File[] sysIncPath = new File[sysIncludePath.size()];\r
+ for (int i = 0; i < sysIncludePath.size(); i++) {\r
+ sysIncPath[i] = new File((String) sysIncludePath.elementAt(i));\r
+ }\r
+ addIncludes(baseDirPath, incPath, args, relativeArgs,\r
+ includePathIdentifier);\r
+ addIncludes(baseDirPath, sysIncPath, args, null, null);\r
+ StringBuffer buf = new StringBuffer(getIdentifier());\r
+ for (int i = 0; i < relativeArgs.size(); i++) {\r
+ buf.append(relativeArgs.elementAt(i));\r
+ buf.append(' ');\r
+ }\r
+ buf.setLength(buf.length() - 1);\r
+ String configId = buf.toString();\r
+ int warnings = specificDef.getWarnings(defaultProviders, 0);\r
+ addWarningSwitch(args, warnings);\r
+ Enumeration argEnum = cmdArgs.elements();\r
+ int endCount = 0;\r
+ while (argEnum.hasMoreElements()) {\r
+ CommandLineArgument arg = (CommandLineArgument) argEnum\r
+ .nextElement();\r
+ switch (arg.getLocation()) {\r
+ case 1 :\r
+ args.addElement(arg.getValue());\r
+ break;\r
+ case 2 :\r
+ endCount++;\r
+ break;\r
+ }\r
+ }\r
+ String[] endArgs = new String[endCount];\r
+ argEnum = cmdArgs.elements();\r
+ int index = 0;\r
+ while (argEnum.hasMoreElements()) {\r
+ CommandLineArgument arg = (CommandLineArgument) argEnum\r
+ .nextElement();\r
+ if (arg.getLocation() == 2) {\r
+ endArgs[index++] = arg.getValue();\r
+ }\r
+ }\r
+ String[] argArray = new String[args.size()];\r
+ args.copyInto(argArray);\r
+ boolean rebuild = specificDef.getRebuild(baseDefs, 0);\r
+ File[] envIncludePath = getEnvironmentIncludePath();\r
+ return new CommandLineCompilerConfiguration(this, configId, incPath,\r
+ sysIncPath, envIncludePath, includePathIdentifier.toString(),\r
+ argArray, paramArray, rebuild, endArgs);\r
+ }\r
+ protected int getArgumentCountPerInputFile() {\r
+ return 1;\r
+ }\r
+ protected final String getCommand() {\r
+ return command;\r
+ }\r
+ abstract protected void getDefineSwitch(StringBuffer buffer, String define,\r
+ String value);\r
+ protected abstract File[] getEnvironmentIncludePath();\r
+ public String getIdentifier() {\r
+ if (identifier == null) {\r
+ if (identifierArg == null) {\r
+ identifier = getIdentifier(new String[]{command}, command);\r
+ } else {\r
+ identifier = getIdentifier(\r
+ new String[]{command, identifierArg}, command);\r
+ }\r
+ }\r
+ return identifier;\r
+ }\r
+ abstract protected String getIncludeDirSwitch(String source);\r
+ protected String getInputFileArgument(File outputDir, String filename,\r
+ int index) {\r
+ //\r
+ // if there is an embedded space,\r
+ // must enclose in quotes\r
+ if (filename.indexOf(' ') >= 0) {\r
+ StringBuffer buf = new StringBuffer("\"");\r
+ buf.append(filename);\r
+ buf.append("\"");\r
+ return buf.toString();\r
+ }\r
+ return filename;\r
+ }\r
+ protected final boolean getLibtool() {\r
+ return libtool;\r
+ }\r
+ /**\r
+ * Obtains the same compiler, but with libtool set\r
+ * \r
+ * Default behavior is to ignore libtool\r
+ */\r
+ public final CommandLineCompiler getLibtoolCompiler() {\r
+ if (libtoolCompiler != null) {\r
+ return libtoolCompiler;\r
+ }\r
+ return this;\r
+ }\r
+ abstract public int getMaximumCommandLength();\r
+ protected int getMaximumInputFilesPerCommand() {\r
+ return Integer.MAX_VALUE;\r
+ }\r
+ protected int getTotalArgumentLengthForInputFile(File outputDir,\r
+ String inputFile) {\r
+ return inputFile.length() + 1;\r
+ }\r
+ abstract protected void getUndefineSwitch(StringBuffer buffer, String define);\r
+ /**\r
+ * This method is exposed so test classes can overload and test the\r
+ * arguments without actually spawning the compiler\r
+ */\r
+ protected int runCommand(CCTask task, File workingDir, String[] cmdline)\r
+ throws BuildException {\r
+ return CUtil.runCommand(task, workingDir, cmdline, newEnvironment, env);\r
+ }\r
+ protected final void setCommand(String command) {\r
+ this.command = command;\r
+ }\r
+}\r