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
;
20 import java
.io
.FileWriter
;
21 import java
.io
.IOException
;
22 import java
.util
.Enumeration
;
23 import java
.util
.Vector
;
25 import net
.sf
.antcontrib
.cpptasks
.CCTask
;
26 import net
.sf
.antcontrib
.cpptasks
.CUtil
;
27 import net
.sf
.antcontrib
.cpptasks
.LinkerDef
;
28 import net
.sf
.antcontrib
.cpptasks
.ProcessorDef
;
29 import net
.sf
.antcontrib
.cpptasks
.ProcessorParam
;
30 import net
.sf
.antcontrib
.cpptasks
.types
.CommandLineArgument
;
31 import net
.sf
.antcontrib
.cpptasks
.types
.LibrarySet
;
32 import net
.sf
.antcontrib
.cpptasks
.TargetDef
;
34 import org
.apache
.tools
.ant
.BuildException
;
35 import org
.apache
.tools
.ant
.types
.Environment
;
39 * An abstract Linker implementation that performs the link via an external
42 * @author Adam Murdoch
44 public abstract class CommandLineLinker
extends AbstractLinker
46 private String command
;
47 private Environment env
= null;
48 private String identifier
;
49 private String identifierArg
;
50 private boolean isLibtool
;
51 private String
[] librarySets
;
52 private CommandLineLinker libtoolLinker
;
53 private boolean newEnvironment
= false;
54 private String outputSuffix
;
57 /** Creates a comand line linker invocation */
58 public CommandLineLinker(String command
,
61 String
[] ignoredExtensions
, String outputSuffix
,
62 boolean isLibtool
, CommandLineLinker libtoolLinker
)
64 super(extensions
, ignoredExtensions
);
65 this.command
= command
;
66 this.identifierArg
= identifierArg
;
67 this.outputSuffix
= outputSuffix
;
68 this.isLibtool
= isLibtool
;
69 this.libtoolLinker
= libtoolLinker
;
71 protected abstract void addBase(long base
, Vector args
);
73 protected abstract void addFixed(Boolean fixed
, Vector args
);
75 abstract protected void addImpliedArgs(boolean debug
,
76 LinkType linkType
, Vector args
, Boolean defaultflag
);
77 protected abstract void addIncremental(boolean incremental
, Vector args
);
80 // Windows processors handle these through file list
82 protected String
[] addLibrarySets(CCTask task
, LibrarySet
[] libsets
, Vector preargs
,
83 Vector midargs
, Vector endargs
) {
86 protected abstract void addMap(boolean map
, Vector args
);
87 protected abstract void addStack(int stack
, Vector args
);
88 protected abstract void addEntry(String entry
, Vector args
);
90 protected LinkerConfiguration
createConfiguration(
93 ProcessorDef
[] baseDefs
, LinkerDef specificDef
, TargetDef targetPlatform
) {
95 Vector preargs
= new Vector();
96 Vector midargs
= new Vector();
97 Vector endargs
= new Vector();
98 Vector
[] args
= new Vector
[] { preargs
, midargs
, endargs
};
100 LinkerDef
[] defaultProviders
= new LinkerDef
[baseDefs
.length
+1];
101 defaultProviders
[0] = specificDef
;
102 for(int i
= 0; i
< baseDefs
.length
; i
++) {
103 defaultProviders
[i
+1] = (LinkerDef
) baseDefs
[i
];
106 // add command line arguments inherited from <cc> element
107 // any "extends" and finally the specific CompilerDef
108 CommandLineArgument
[] commandArgs
;
109 for(int i
= defaultProviders
.length
-1; i
>= 0; i
--) {
110 commandArgs
= defaultProviders
[i
].getActiveProcessorArgs();
111 for(int j
= 0; j
< commandArgs
.length
; j
++) {
112 args
[commandArgs
[j
].getLocation()].
113 addElement(commandArgs
[j
].getValue());
117 Vector params
= new Vector();
119 // add command line arguments inherited from <cc> element
120 // any "extends" and finally the specific CompilerDef
121 ProcessorParam
[] paramArray
;
122 for (int i
= defaultProviders
.length
- 1; i
>= 0; i
--) {
123 paramArray
= defaultProviders
[i
].getActiveProcessorParams();
124 for (int j
= 0; j
< paramArray
.length
; j
++) {
125 params
.add(paramArray
[j
]);
129 paramArray
= (ProcessorParam
[])(params
.toArray(new ProcessorParam
[params
.size()]));
131 boolean debug
= specificDef
.getDebug(baseDefs
,0);
134 String startupObject
= getStartupObject(linkType
);
135 Boolean defaultflag
= specificDef
.getDefaultflag(defaultProviders
, 1);
136 addImpliedArgs(debug
, linkType
, preargs
, defaultflag
);
137 addIncremental(specificDef
.getIncremental(defaultProviders
,1), preargs
);
138 addFixed(specificDef
.getFixed(defaultProviders
,1), preargs
);
139 addMap(specificDef
.getMap(defaultProviders
,1), preargs
);
140 addBase(specificDef
.getBase(defaultProviders
,1), preargs
);
141 addStack(specificDef
.getStack(defaultProviders
,1), preargs
);
142 addEntry(specificDef
.getEntry(defaultProviders
, 1), preargs
);
144 String
[] libnames
= null;
145 LibrarySet
[] libsets
= specificDef
.getActiveLibrarySets(defaultProviders
,1);
146 if (libsets
.length
> 0) {
147 libnames
= addLibrarySets(task
, libsets
, preargs
, midargs
, endargs
);
150 StringBuffer buf
= new StringBuffer(getIdentifier());
151 for (int i
= 0; i
< 3; i
++) {
152 Enumeration argenum
= args
[i
].elements();
153 while (argenum
.hasMoreElements()) {
155 buf
.append(argenum
.nextElement().toString());
158 String configId
= buf
.toString();
160 String
[][] options
= new String
[][] {
161 new String
[args
[0].size() + args
[1].size()],
162 new String
[args
[2].size()] };
163 args
[0].copyInto(options
[0]);
164 int offset
= args
[0].size();
165 for (int i
= 0; i
< args
[1].size(); i
++) {
166 options
[0][i
+offset
] = (String
) args
[1].elementAt(i
);
168 args
[2].copyInto(options
[1]);
171 boolean rebuild
= specificDef
.getRebuild(baseDefs
,0);
172 boolean map
= specificDef
.getMap(defaultProviders
,1);
174 //task.log("libnames:"+libnames.length, Project.MSG_VERBOSE);
175 return new CommandLineLinkerConfiguration(this,configId
,options
,
177 rebuild
,map
,libnames
, startupObject
);
181 * Allows drived linker to decorate linker option.
182 * Override by GccLinker to prepend a "-Wl," to
183 * pass option to through gcc to linker.
185 * @param buf buffer that may be used and abused in the decoration process,
187 * @param arg linker argument
189 protected String
decorateLinkerOption(StringBuffer buf
, String arg
) {
193 protected final String
getCommand() {
196 protected abstract String
getCommandFileSwitch(String commandFile
);
199 public String
getIdentifier() {
200 if(identifier
== null) {
201 if (identifierArg
== null) {
202 identifier
= getIdentifier(new String
[] { command
}, command
);
204 identifier
= getIdentifier(new String
[] { command
, identifierArg
},
210 public final CommandLineLinker
getLibtoolLinker() {
211 if (libtoolLinker
!= null) {
212 return libtoolLinker
;
216 protected abstract int getMaximumCommandLength();
218 public String
getOutputFileName(String baseName
) {
219 return baseName
+ outputSuffix
;
222 protected String
[] getOutputFileSwitch(CCTask task
, String outputFile
) {
223 return getOutputFileSwitch(outputFile
);
225 protected abstract String
[] getOutputFileSwitch(String outputFile
);
226 protected String
getStartupObject(LinkType linkType
) {
231 * Performs a link using a command line linker
234 public void link(CCTask task
,
236 String
[] sourceFiles
,
237 CommandLineLinkerConfiguration config
)
238 throws BuildException
240 File parentDir
= new File(outputFile
.getParent());
243 parentPath
= parentDir
.getCanonicalPath();
244 } catch(IOException ex
) {
245 parentPath
= parentDir
.getAbsolutePath();
247 String
[] execArgs
= prepareArguments(task
, parentPath
,outputFile
.getName(),
248 sourceFiles
, config
);
249 int commandLength
= 0;
250 for(int i
= 0; i
< execArgs
.length
; i
++) {
251 commandLength
+= execArgs
[i
].length() + 1;
255 // if command length exceeds maximum
256 // (1024 for Windows) then create a temporary
257 // file containing everything but the command name
258 if(commandLength
>= this.getMaximumCommandLength()) {
260 execArgs
= prepareResponseFile(outputFile
,execArgs
);
262 catch(IOException ex
) {
263 throw new BuildException(ex
);
267 int retval
= runCommand(task
,parentDir
,execArgs
);
269 // if the process returned a failure code then
270 // throw an BuildException
274 // construct the exception
276 throw new BuildException(this.getCommand() + " failed with return code " + retval
, task
.getLocation());
283 * Prepares argument list for exec command. Will return null
284 * if command line would exceed allowable command line buffer.
286 * @param outputFile linker output file
287 * @param sourceFiles linker input files (.obj, .o, .res)
288 * @param args linker arguments
289 * @return arguments for runTask
291 protected String
[] prepareArguments(
295 String
[] sourceFiles
,
296 CommandLineLinkerConfiguration config
) {
298 String
[] preargs
= config
.getPreArguments();
299 String
[] endargs
= config
.getEndArguments();
300 String outputSwitch
[] = getOutputFileSwitch(task
, outputFile
);
301 int allArgsCount
= preargs
.length
+ 1 + outputSwitch
.length
+
302 sourceFiles
.length
+ endargs
.length
;
306 String
[] allArgs
= new String
[allArgsCount
];
309 allArgs
[index
++] = "libtool";
311 allArgs
[index
++] = this.getCommand();
312 StringBuffer buf
= new StringBuffer();
313 for (int i
= 0; i
< preargs
.length
; i
++) {
314 allArgs
[index
++] = decorateLinkerOption(buf
, preargs
[i
]);
316 for (int i
= 0; i
< outputSwitch
.length
; i
++) {
317 allArgs
[index
++] = outputSwitch
[i
];
319 for (int i
= 0; i
< sourceFiles
.length
; i
++) {
320 allArgs
[index
++] = prepareFilename(buf
,outputDir
,sourceFiles
[i
]);
322 for (int i
= 0; i
< endargs
.length
; i
++) {
323 allArgs
[index
++] = decorateLinkerOption(buf
, endargs
[i
]);
329 * Processes filename into argument form
332 protected String
prepareFilename(StringBuffer buf
,
333 String outputDir
, String sourceFile
) {
334 String relativePath
= CUtil
.getRelativePath(outputDir
,
335 new File(sourceFile
));
336 return quoteFilename(buf
,relativePath
);
340 * Prepares argument list to execute the linker using a
343 * @param outputFile linker output file
344 * @param args output of prepareArguments
345 * @return arguments for runTask
347 protected String
[] prepareResponseFile(File outputFile
,String
[] args
) throws IOException
349 String baseName
= outputFile
.getName();
350 File commandFile
= new File(outputFile
.getParent(),baseName
+ ".rsp");
351 FileWriter writer
= new FileWriter(commandFile
);
352 int execArgCount
= 1;
356 String
[] execArgs
= new String
[execArgCount
+1];
357 for (int i
= 0; i
< execArgCount
; i
++) {
358 execArgs
[i
] = args
[i
];
360 execArgs
[execArgCount
] = getCommandFileSwitch(commandFile
.toString());
361 for(int i
= execArgCount
; i
< args
.length
; i
++) {
363 // if embedded space and not quoted then
365 if (args
[i
].indexOf(" ") >= 0 && args
[i
].charAt(0) != '\"') {
367 writer
.write(args
[i
]);
368 writer
.write("\"\n");
370 writer
.write(args
[i
]);
379 protected String
quoteFilename(StringBuffer buf
,String filename
) {
380 if(filename
.indexOf(' ') >= 0) {
383 buf
.append(filename
);
385 return buf
.toString();
391 * This method is exposed so test classes can overload
392 * and test the arguments without actually spawning the
395 protected int runCommand(CCTask task
, File workingDir
,String
[] cmdline
)
396 throws BuildException
{
397 return CUtil
.runCommand(task
,workingDir
,cmdline
, newEnvironment
, env
);
400 protected final void setCommand(String command
) {
401 this.command
= command
;