3 * Copyright 2002-2004 The Ant-Contrib project
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 package net
.sf
.antcontrib
.cpptasks
.compiler
;
19 import java
.io
.IOException
;
20 import java
.util
.Enumeration
;
21 import java
.util
.Vector
;
22 import net
.sf
.antcontrib
.cpptasks
.CCTask
;
23 import net
.sf
.antcontrib
.cpptasks
.CUtil
;
24 import net
.sf
.antcontrib
.cpptasks
.CompilerDef
;
25 import net
.sf
.antcontrib
.cpptasks
.ProcessorDef
;
26 import net
.sf
.antcontrib
.cpptasks
.ProcessorParam
;
27 import net
.sf
.antcontrib
.cpptasks
.types
.CommandLineArgument
;
28 import net
.sf
.antcontrib
.cpptasks
.types
.UndefineArgument
;
29 import net
.sf
.antcontrib
.cpptasks
.TargetDef
;
30 import org
.apache
.tools
.ant
.BuildException
;
31 import org
.apache
.tools
.ant
.types
.Environment
;
32 import net
.sf
.antcontrib
.cpptasks
.OptimizationEnum
;;
34 * An abstract Compiler implementation which uses an external program to
35 * perform the compile.
37 * @author Adam Murdoch
39 public abstract class CommandLineCompiler
extends AbstractCompiler
{
40 private String command
;
41 private final Environment env
;
42 private String identifier
;
43 private String identifierArg
;
44 private boolean libtool
;
45 private CommandLineCompiler libtoolCompiler
;
46 private final boolean newEnvironment
;
47 protected CommandLineCompiler(String command
, String identifierArg
,
48 String
[] sourceExtensions
, String
[] headerExtensions
,
49 String outputSuffix
, boolean libtool
,
50 CommandLineCompiler libtoolCompiler
, boolean newEnvironment
,
52 super(sourceExtensions
, headerExtensions
, outputSuffix
);
53 this.command
= command
;
54 if (libtool
&& libtoolCompiler
!= null) {
55 throw new java
.lang
.IllegalArgumentException(
56 "libtoolCompiler should be null when libtool is true");
58 this.libtool
= libtool
;
59 this.libtoolCompiler
= libtoolCompiler
;
60 this.identifierArg
= identifierArg
;
61 this.newEnvironment
= newEnvironment
;
64 abstract protected void addImpliedArgs(Vector args
, boolean debug
,
65 boolean multithreaded
, boolean exceptions
, LinkType linkType
,
66 Boolean rtti
, OptimizationEnum optimization
, Boolean defaultflag
);
68 * Adds command-line arguments for include directories.
70 * If relativeArgs is not null will add corresponding relative paths
71 * include switches to that vector (for use in building a configuration
72 * identifier that is consistent between machines).
75 * A vector containing the parts of the working directory,
76 * produced by CUtil.DecomposeFile.
78 * Array of include directory paths
80 * Vector of command line arguments used to execute the task
82 * Vector of command line arguments used to build the
83 * configuration identifier
85 protected void addIncludes(String baseDirPath
, File
[] includeDirs
,
86 Vector args
, Vector relativeArgs
, StringBuffer includePathId
) {
87 for (int i
= 0; i
< includeDirs
.length
; i
++) {
88 args
.addElement(getIncludeDirSwitch(includeDirs
[i
]
90 if (relativeArgs
!= null) {
91 String relative
= CUtil
.getRelativePath(baseDirPath
,
93 relativeArgs
.addElement(getIncludeDirSwitch(relative
));
94 if (includePathId
!= null) {
95 if (includePathId
.length() == 0) {
96 includePathId
.append("/I");
98 includePathId
.append(" /I");
100 includePathId
.append(relative
);
105 abstract protected void addWarningSwitch(Vector args
, int warnings
);
106 protected void buildDefineArguments(CompilerDef
[] defs
, Vector args
) {
108 // assume that we aren't inheriting defines from containing <cc>
110 UndefineArgument
[] merged
= defs
[0].getActiveDefines();
111 for (int i
= 1; i
< defs
.length
; i
++) {
113 // if we are inheriting, merge the specific defines with the
114 // containing defines
115 merged
= UndefineArgument
.merge(defs
[i
].getActiveDefines(), merged
);
117 StringBuffer buf
= new StringBuffer(30);
118 for (int i
= 0; i
< merged
.length
; i
++) {
120 UndefineArgument current
= merged
[i
];
121 if (current
.isDefine()) {
122 getDefineSwitch(buf
, current
.getName(), current
.getValue());
124 getUndefineSwitch(buf
, current
.getName());
126 args
.addElement(buf
.toString());
130 * Compiles a source file.
132 * @author Curt Arnold
134 public void compile(CCTask task
, File outputDir
, String
[] sourceFiles
,
135 String
[] args
, String
[] endArgs
, boolean relentless
,
136 CommandLineCompilerConfiguration config
, ProgressMonitor monitor
)
137 throws BuildException
{
138 BuildException exc
= null;
140 // determine length of executable name and args
142 String command
= getCommand();
143 int baseLength
= command
.length() + args
.length
+ endArgs
.length
;
147 for (int i
= 0; i
< args
.length
; i
++) {
148 baseLength
+= args
[i
].length();
150 for (int i
= 0; i
< endArgs
.length
; i
++) {
151 baseLength
+= endArgs
[i
].length();
153 if (baseLength
> getMaximumCommandLength()) {
154 throw new BuildException(
155 "Command line is over maximum length without specifying source file");
158 // typically either 1 or Integer.MAX_VALUE
160 int maxInputFilesPerCommand
= getMaximumInputFilesPerCommand();
161 int argumentCountPerInputFile
= getArgumentCountPerInputFile();
162 for (int sourceIndex
= 0; sourceIndex
< sourceFiles
.length
;) {
163 int cmdLength
= baseLength
;
164 int firstFileNextExec
;
165 for (firstFileNextExec
= sourceIndex
; firstFileNextExec
< sourceFiles
.length
166 && (firstFileNextExec
- sourceIndex
) < maxInputFilesPerCommand
; firstFileNextExec
++) {
167 cmdLength
+= getTotalArgumentLengthForInputFile(outputDir
,
168 sourceFiles
[firstFileNextExec
]);
169 if (cmdLength
>= getMaximumCommandLength())
172 if (firstFileNextExec
== sourceIndex
) {
173 throw new BuildException(
174 "Extremely long file name, can't fit on command line");
176 int argCount
= args
.length
+ 1 + endArgs
.length
177 + (firstFileNextExec
- sourceIndex
)
178 * argumentCountPerInputFile
;
182 String
[] commandline
= new String
[argCount
];
185 commandline
[index
++] = "libtool";
187 commandline
[index
++] = command
;
188 for (int j
= 0; j
< args
.length
; j
++) {
189 commandline
[index
++] = args
[j
];
191 for (int j
= sourceIndex
; j
< firstFileNextExec
; j
++) {
192 for (int k
= 0; k
< argumentCountPerInputFile
; k
++) {
193 commandline
[index
++] = getInputFileArgument(outputDir
,
197 for (int j
= 0; j
< endArgs
.length
; j
++) {
198 commandline
[index
++] = endArgs
[j
];
200 int retval
= runCommand(task
, outputDir
, commandline
);
201 if (monitor
!= null) {
202 String
[] fileNames
= new String
[firstFileNextExec
- sourceIndex
];
203 for (int j
= 0; j
< fileNames
.length
; j
++) {
204 fileNames
[j
] = sourceFiles
[sourceIndex
+ j
];
206 monitor
.progress(fileNames
);
209 // if the process returned a failure code and
210 // we aren't holding an exception from an earlier
212 if (retval
!= 0 && exc
== null) {
214 // construct the exception
216 exc
= new BuildException(this.getCommand()
217 + " failed with return code " + retval
, task
220 // and throw it now unless we are relentless
226 sourceIndex
= firstFileNextExec
;
229 // if the compiler returned a failure value earlier
230 // then throw an exception
235 protected CompilerConfiguration
createConfiguration(final CCTask task
,
236 final LinkType linkType
,
237 final ProcessorDef
[] baseDefs
,
238 final CompilerDef specificDef
,
239 final TargetDef targetPlatform
) {
240 Vector args
= new Vector();
241 CompilerDef
[] defaultProviders
= new CompilerDef
[baseDefs
.length
+ 1];
242 for (int i
= 0; i
< baseDefs
.length
; i
++) {
243 defaultProviders
[i
+ 1] = (CompilerDef
) baseDefs
[i
];
245 defaultProviders
[0] = specificDef
;
246 Vector cmdArgs
= new Vector();
248 // add command line arguments inherited from <cc> element
249 // any "extends" and finally the specific CompilerDef
250 CommandLineArgument
[] commandArgs
;
251 for (int i
= defaultProviders
.length
- 1; i
>= 0; i
--) {
252 commandArgs
= defaultProviders
[i
].getActiveProcessorArgs();
253 for (int j
= 0; j
< commandArgs
.length
; j
++) {
254 if (commandArgs
[j
].getLocation() == 0) {
255 args
.addElement(commandArgs
[j
].getValue());
257 cmdArgs
.addElement(commandArgs
[j
]);
261 Vector params
= new Vector();
263 // add command line arguments inherited from <cc> element
264 // any "extends" and finally the specific CompilerDef
265 ProcessorParam
[] paramArray
;
266 for (int i
= defaultProviders
.length
- 1; i
>= 0; i
--) {
267 paramArray
= defaultProviders
[i
].getActiveProcessorParams();
268 for (int j
= 0; j
< paramArray
.length
; j
++) {
269 params
.add(paramArray
[j
]);
272 paramArray
= (ProcessorParam
[]) (params
273 .toArray(new ProcessorParam
[params
.size()]));
274 boolean multithreaded
= specificDef
.getMultithreaded(defaultProviders
,
276 boolean debug
= specificDef
.getDebug(baseDefs
, 0);
277 boolean exceptions
= specificDef
.getExceptions(defaultProviders
, 1);
278 Boolean rtti
= specificDef
.getRtti(defaultProviders
, 1);
279 Boolean defaultflag
= specificDef
.getDefaultflag(defaultProviders
, 1);
280 OptimizationEnum optimization
= specificDef
.getOptimization(defaultProviders
, 1);
281 this.addImpliedArgs(args
, debug
, multithreaded
, exceptions
, linkType
, rtti
, optimization
, defaultflag
);
283 // add all appropriate defines and undefines
285 buildDefineArguments(defaultProviders
, args
);
287 // Want to have distinct set of arguments with relative
288 // path names for includes that are used to build
289 // the configuration identifier
291 Vector relativeArgs
= (Vector
) args
.clone();
293 // add all active include and sysincludes
295 StringBuffer includePathIdentifier
= new StringBuffer();
296 File baseDir
= specificDef
.getProject().getBaseDir();
299 baseDirPath
= baseDir
.getCanonicalPath();
300 } catch (IOException ex
) {
301 baseDirPath
= baseDir
.toString();
303 Vector includePath
= new Vector();
304 Vector sysIncludePath
= new Vector();
305 for (int i
= defaultProviders
.length
- 1; i
>= 0; i
--) {
306 String
[] incPath
= defaultProviders
[i
].getActiveIncludePaths();
307 for (int j
= 0; j
< incPath
.length
; j
++) {
308 includePath
.addElement(incPath
[j
]);
310 incPath
= defaultProviders
[i
].getActiveSysIncludePaths();
311 for (int j
= 0; j
< incPath
.length
; j
++) {
312 sysIncludePath
.addElement(incPath
[j
]);
315 File
[] incPath
= new File
[includePath
.size()];
316 for (int i
= 0; i
< includePath
.size(); i
++) {
317 incPath
[i
] = new File((String
) includePath
.elementAt(i
));
319 File
[] sysIncPath
= new File
[sysIncludePath
.size()];
320 for (int i
= 0; i
< sysIncludePath
.size(); i
++) {
321 sysIncPath
[i
] = new File((String
) sysIncludePath
.elementAt(i
));
323 addIncludes(baseDirPath
, incPath
, args
, relativeArgs
,
324 includePathIdentifier
);
325 addIncludes(baseDirPath
, sysIncPath
, args
, null, null);
326 StringBuffer buf
= new StringBuffer(getIdentifier());
327 for (int i
= 0; i
< relativeArgs
.size(); i
++) {
328 buf
.append(relativeArgs
.elementAt(i
));
331 buf
.setLength(buf
.length() - 1);
332 String configId
= buf
.toString();
333 int warnings
= specificDef
.getWarnings(defaultProviders
, 0);
334 addWarningSwitch(args
, warnings
);
335 Enumeration argEnum
= cmdArgs
.elements();
337 while (argEnum
.hasMoreElements()) {
338 CommandLineArgument arg
= (CommandLineArgument
) argEnum
340 switch (arg
.getLocation()) {
342 args
.addElement(arg
.getValue());
349 String
[] endArgs
= new String
[endCount
];
350 argEnum
= cmdArgs
.elements();
352 while (argEnum
.hasMoreElements()) {
353 CommandLineArgument arg
= (CommandLineArgument
) argEnum
355 if (arg
.getLocation() == 2) {
356 endArgs
[index
++] = arg
.getValue();
359 String
[] argArray
= new String
[args
.size()];
360 args
.copyInto(argArray
);
361 boolean rebuild
= specificDef
.getRebuild(baseDefs
, 0);
362 File
[] envIncludePath
= getEnvironmentIncludePath();
363 return new CommandLineCompilerConfiguration(this, configId
, incPath
,
364 sysIncPath
, envIncludePath
, includePathIdentifier
.toString(),
365 argArray
, paramArray
, rebuild
, endArgs
);
367 protected int getArgumentCountPerInputFile() {
370 protected final String
getCommand() {
373 abstract protected void getDefineSwitch(StringBuffer buffer
, String define
,
375 protected abstract File
[] getEnvironmentIncludePath();
376 public String
getIdentifier() {
377 if (identifier
== null) {
378 if (identifierArg
== null) {
379 identifier
= getIdentifier(new String
[]{command
}, command
);
381 identifier
= getIdentifier(
382 new String
[]{command
, identifierArg
}, command
);
387 abstract protected String
getIncludeDirSwitch(String source
);
388 protected String
getInputFileArgument(File outputDir
, String filename
,
391 // if there is an embedded space,
392 // must enclose in quotes
393 if (filename
.indexOf(' ') >= 0) {
394 StringBuffer buf
= new StringBuffer("\"");
395 buf
.append(filename
);
397 return buf
.toString();
401 protected final boolean getLibtool() {
405 * Obtains the same compiler, but with libtool set
407 * Default behavior is to ignore libtool
409 public final CommandLineCompiler
getLibtoolCompiler() {
410 if (libtoolCompiler
!= null) {
411 return libtoolCompiler
;
415 abstract public int getMaximumCommandLength();
416 protected int getMaximumInputFilesPerCommand() {
417 return Integer
.MAX_VALUE
;
419 protected int getTotalArgumentLengthForInputFile(File outputDir
,
421 return inputFile
.length() + 1;
423 abstract protected void getUndefineSwitch(StringBuffer buffer
, String define
);
425 * This method is exposed so test classes can overload and test the
426 * arguments without actually spawning the compiler
428 protected int runCommand(CCTask task
, File workingDir
, String
[] cmdline
)
429 throws BuildException
{
430 return CUtil
.runCommand(task
, workingDir
, cmdline
, newEnvironment
, env
);
432 protected final void setCommand(String command
) {
433 this.command
= command
;