1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.github.maven_nar.cpptasks.compiler;
21
22 import java.io.File;
23 import java.io.FileWriter;
24 import java.io.IOException;
25 import java.util.*;
26
27 import org.apache.tools.ant.BuildException;
28 import org.apache.tools.ant.types.Environment;
29
30 import com.github.maven_nar.cpptasks.CCTask;
31 import com.github.maven_nar.cpptasks.CUtil;
32 import com.github.maven_nar.cpptasks.LinkerDef;
33 import com.github.maven_nar.cpptasks.ProcessorDef;
34 import com.github.maven_nar.cpptasks.ProcessorParam;
35 import com.github.maven_nar.cpptasks.TargetDef;
36 import com.github.maven_nar.cpptasks.VersionInfo;
37 import com.github.maven_nar.cpptasks.types.CommandLineArgument;
38 import com.github.maven_nar.cpptasks.types.LibrarySet;
39
40
41
42
43
44
45
46 public abstract class CommandLineLinker extends AbstractLinker {
47 private String command;
48 private String prefix;
49 private Environment env = null;
50 private String identifier;
51 private final String identifierArg;
52 private final boolean isLibtool;
53 private String[] librarySets;
54 private final CommandLineLinker libtoolLinker;
55 private final boolean newEnvironment = false;
56 private final String outputSuffix;
57
58
59 private final int maxPathLength = 250;
60
61
62 public CommandLineLinker(final String command, final String identifierArg, final String[] extensions,
63 final String[] ignoredExtensions, final String outputSuffix, final boolean isLibtool,
64 final CommandLineLinker libtoolLinker) {
65 super(extensions, ignoredExtensions);
66 this.command = command;
67 this.identifierArg = identifierArg;
68 this.outputSuffix = outputSuffix;
69 this.isLibtool = isLibtool;
70 this.libtoolLinker = libtoolLinker;
71 }
72
73 protected void addBase(final CCTask task, final long base, final Vector<String> args) {
74
75 }
76
77 protected void addEntry(final CCTask task, final String entry, final Vector<String> args) {
78
79 }
80
81 protected void addFixed(final CCTask task, final Boolean fixed, final Vector<String> args) {
82
83 }
84
85 protected void addImpliedArgs(final CCTask task, final boolean debug, final LinkType linkType,
86 final Vector<String> args) {
87
88 }
89
90 protected void addIncremental(final CCTask task, final boolean incremental, final Vector<String> args) {
91
92 }
93
94 protected void addLibraryDirectory(final File libraryDirectory, final Vector<String> preargs) {
95 try {
96 if (libraryDirectory != null && libraryDirectory.exists()) {
97 final File currentDir = new File(".").getParentFile();
98 String path = libraryDirectory.getCanonicalPath();
99 if (currentDir != null) {
100 final String currentPath = currentDir.getCanonicalPath();
101 path = CUtil.getRelativePath(currentPath, libraryDirectory);
102 }
103 addLibraryPath(preargs, path);
104 }
105 } catch (final IOException e) {
106 throw new RuntimeException("Unable to add library path: " + libraryDirectory);
107 }
108 }
109
110 protected void addLibraryPath(final Vector<String> preargs, final String path) {
111 }
112
113
114
115
116 protected String[] addLibrarySets(final CCTask task, final LibrarySet[] libsets, final Vector<String> preargs,
117 final Vector<String> midargs, final Vector<String> endargs) {
118 return null;
119 }
120
121 protected void addMap(final CCTask task, final boolean map, final Vector<String> args) {
122
123 }
124
125 protected void addStack(final CCTask task, final int stack, final Vector<String> args) {
126
127 }
128
129 @Override
130 protected LinkerConfiguration createConfiguration(final CCTask task, final LinkType linkType,
131 final ProcessorDef[] baseDefs, final LinkerDef specificDef, final TargetDef targetPlatform,
132 final VersionInfo versionInfo) {
133
134 final Vector<String> preargs = new Vector<>();
135 final Vector<String> midargs = new Vector<>();
136 final Vector<String> endargs = new Vector<>();
137 final Vector<String>[] args = new Vector[] {
138 preargs, midargs, endargs
139 };
140
141 this.prefix = specificDef.getLinkerPrefix();
142
143 final LinkerDef[] defaultProviders = new LinkerDef[baseDefs.length + 1];
144 defaultProviders[0] = specificDef;
145 for (int i = 0; i < baseDefs.length; i++) {
146 defaultProviders[i + 1] = (LinkerDef) baseDefs[i];
147 }
148
149
150
151
152 CommandLineArgument[] commandArgs;
153 for (int i = defaultProviders.length - 1; i >= 0; i--) {
154 final LinkerDef linkerDef = defaultProviders[i];
155 commandArgs = linkerDef.getActiveProcessorArgs();
156 for (final CommandLineArgument commandArg : commandArgs) {
157 args[commandArg.getLocation()].addElement(commandArg.getValue());
158 }
159 }
160
161 final Set<File> libraryDirectories = new LinkedHashSet<>();
162 for (int i = defaultProviders.length - 1; i >= 0; i--) {
163 final LinkerDef linkerDef = defaultProviders[i];
164 for (final File libraryDirectory : linkerDef.getLibraryDirectories()) {
165 if (libraryDirectories.add(libraryDirectory)) {
166 addLibraryDirectory(libraryDirectory, preargs);
167 }
168 }
169 }
170
171 final Vector<ProcessorParam> params = new Vector<>();
172
173
174
175 ProcessorParam[] paramArray;
176 for (int i = defaultProviders.length - 1; i >= 0; i--) {
177 paramArray = defaultProviders[i].getActiveProcessorParams();
178 Collections.addAll(params, paramArray);
179 }
180
181 paramArray = params.toArray(new ProcessorParam[params.size()]);
182
183 final boolean debug = specificDef.getDebug(baseDefs, 0);
184
185 final String startupObject = getStartupObject(linkType);
186
187 addImpliedArgs(task, debug, linkType, preargs);
188 addIncremental(task, specificDef.getIncremental(defaultProviders, 1), preargs);
189 addFixed(task, specificDef.getFixed(defaultProviders, 1), preargs);
190 addMap(task, specificDef.getMap(defaultProviders, 1), preargs);
191 addBase(task, specificDef.getBase(defaultProviders, 1), preargs);
192 addStack(task, specificDef.getStack(defaultProviders, 1), preargs);
193 addEntry(task, specificDef.getEntry(defaultProviders, 1), preargs);
194
195 String[] libnames = null;
196 final LibrarySet[] libsets = specificDef.getActiveLibrarySets(defaultProviders, 1);
197
198
199 libnames = addLibrarySets(task, libsets, preargs, midargs, endargs);
200
201
202 final StringBuffer buf = new StringBuffer(getIdentifier());
203 for (int i = 0; i < 3; i++) {
204 final Enumeration<String> argenum = args[i].elements();
205 while (argenum.hasMoreElements()) {
206 buf.append(' ');
207 buf.append(argenum.nextElement());
208 }
209 }
210 final String configId = buf.toString();
211
212 final String[][] options = new String[][] {
213 new String[args[0].size() + args[1].size()], new String[args[2].size()]
214 };
215 args[0].copyInto(options[0]);
216 final int offset = args[0].size();
217 for (int i = 0; i < args[1].size(); i++) {
218 options[0][i + offset] = args[1].elementAt(i);
219 }
220 args[2].copyInto(options[1]);
221
222
223
224 if (null != specificDef.getEnv() && null == this.env) {
225 this.env = specificDef.getEnv();
226 }
227 for (final ProcessorDef processorDef : baseDefs) {
228 final Environment environment = processorDef.getEnv();
229 if (null != environment && null == this.env) {
230 this.env = environment;
231 }
232 }
233 final boolean rebuild = specificDef.getRebuild(baseDefs, 0);
234 final boolean map = specificDef.getMap(defaultProviders, 1);
235 final String toolPath = specificDef.getToolPath();
236
237
238 return new CommandLineLinkerConfiguration(this, configId, options, paramArray, rebuild, map, debug, libnames,
239 startupObject, toolPath);
240 }
241
242
243
244
245
246
247
248
249
250
251
252
253 protected String decorateLinkerOption(final StringBuffer buf, final String arg) {
254 return arg;
255 }
256
257 protected final String getCommand() {
258 if (this.prefix != null && (!this.prefix.isEmpty())) {
259 return this.prefix + this.command;
260 } else {
261 return this.command;
262 }
263 }
264
265 protected abstract String getCommandFileSwitch(String commandFile);
266
267 public String getCommandWithPath(final CommandLineLinkerConfiguration config) {
268 if (config.getCommandPath() != null) {
269 final File command = new File(config.getCommandPath(), this.getCommand());
270 try {
271 return command.getCanonicalPath();
272 } catch (final IOException e) {
273 e.printStackTrace();
274 return command.getAbsolutePath();
275 }
276 } else {
277 return this.getCommand();
278 }
279 }
280
281 @Override
282 public String getIdentifier() {
283 if (this.identifier == null) {
284 if (this.identifierArg == null) {
285 this.identifier = getIdentifier(new String[] {
286 this.getCommand()
287 }, this.getCommand());
288 } else {
289 this.identifier = getIdentifier(new String[] {
290 this.getCommand(), this.identifierArg
291 }, this.getCommand());
292 }
293 }
294 return this.identifier;
295 }
296
297 public final CommandLineLinker getLibtoolLinker() {
298 if (this.libtoolLinker != null) {
299 return this.libtoolLinker;
300 }
301 return this;
302 }
303
304 protected abstract int getMaximumCommandLength();
305
306 @Override
307 public String[] getOutputFileNames(final String baseName, final VersionInfo versionInfo) {
308 return new String[] {
309 baseName + this.outputSuffix
310 };
311 }
312
313 protected String[] getOutputFileSwitch(final CCTask task, final String outputFile) {
314
315 if (isWindows() && outputFile.length() > this.maxPathLength) {
316 throw new BuildException("Absolute path too long, " + outputFile.length() + " > " + this.maxPathLength + ": '"
317 + outputFile);
318 }
319
320 return getOutputFileSwitch(outputFile);
321 }
322
323 protected abstract String[] getOutputFileSwitch(String outputFile);
324
325 protected String getStartupObject(final LinkType linkType) {
326 return null;
327 }
328
329
330
331
332
333 public void link(final CCTask task, final File outputFile, final String[] sourceFiles,
334 final CommandLineLinkerConfiguration config) throws BuildException {
335 final File parentDir = new File(outputFile.getParent());
336 String parentPath;
337 try {
338 parentPath = parentDir.getCanonicalPath();
339 } catch (final IOException ex) {
340 parentPath = parentDir.getAbsolutePath();
341 }
342 String[] execArgs = prepareArguments(task, parentPath, outputFile.getName(), sourceFiles, config);
343 int commandLength = 0;
344 for (final String execArg : execArgs) {
345 commandLength += execArg.length() + 1;
346 }
347
348
349
350
351
352 if (commandLength >= this.getMaximumCommandLength()) {
353 try {
354 execArgs = prepareResponseFile(outputFile, execArgs);
355 } catch (final IOException ex) {
356 throw new BuildException(ex);
357 }
358 }
359
360 final int retval = runCommand(task, parentDir, execArgs);
361
362
363
364
365 if (retval != 0) {
366
367
368
369 throw new BuildException(getCommandWithPath(config) + " failed with return code " + retval, task.getLocation());
370 }
371
372 }
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388 protected String[] prepareArguments(final CCTask task, final String outputDir, final String outputFile,
389 final String[] sourceFiles, final CommandLineLinkerConfiguration config) {
390
391 final String[] preargs = config.getPreArguments();
392 final String[] endargs = config.getEndArguments();
393 final String outputSwitch[] = getOutputFileSwitch(task, outputFile);
394 int allArgsCount = preargs.length + 1 + outputSwitch.length + sourceFiles.length + endargs.length;
395 if (this.isLibtool) {
396 allArgsCount++;
397 }
398 final String[] allArgs = new String[allArgsCount];
399 int index = 0;
400 if (this.isLibtool) {
401 allArgs[index++] = "libtool";
402 }
403 allArgs[index++] = getCommandWithPath(config);
404 final StringBuffer buf = new StringBuffer();
405
406 for (final String prearg : preargs) {
407 allArgs[index++] = task.isDecorateLinkerOptions() ? decorateLinkerOption(buf, prearg) : prearg;
408 }
409
410 for (final String element : outputSwitch) {
411 allArgs[index++] = element;
412 }
413 for (final String sourceFile : sourceFiles) {
414 allArgs[index++] = prepareFilename(buf, outputDir, sourceFile);
415 }
416 for (final String endarg : endargs) {
417 allArgs[index++] = task.isDecorateLinkerOptions() ? decorateLinkerOption(buf, endarg) : endarg;
418 }
419
420 return allArgs;
421 }
422
423
424
425
426
427 protected String prepareFilename(final StringBuffer buf, final String outputDir, final String sourceFile) {
428
429
430 if (isWindows() && sourceFile.length() > this.maxPathLength) {
431 throw new BuildException("Absolute path too long, " + sourceFile.length() + " > " + this.maxPathLength + ": '"
432 + sourceFile);
433 }
434
435 return quoteFilename(buf, sourceFile);
436 }
437
438
439
440
441
442
443
444
445
446
447
448 protected String[] prepareResponseFile(final File outputFile, final String[] args) throws IOException {
449 final String baseName = outputFile.getName();
450 final File commandFile = new File(outputFile.getParent(), baseName + ".rsp");
451 final FileWriter writer = new FileWriter(commandFile);
452 int execArgCount = 1;
453 if (this.isLibtool) {
454 execArgCount++;
455 }
456 final String[] execArgs = new String[execArgCount + 1];
457 System.arraycopy(args, 0, execArgs, 0, execArgCount);
458 execArgs[execArgCount] = getCommandFileSwitch(commandFile.toString());
459 for (int i = execArgCount; i < args.length; i++) {
460
461
462
463 if (args[i].contains(" ") && args[i].charAt(0) != '\"') {
464 writer.write('\"');
465 writer.write(args[i]);
466 writer.write("\"\n");
467 } else {
468 writer.write(args[i]);
469 writer.write('\n');
470 }
471 }
472 writer.close();
473 return execArgs;
474 }
475
476 protected String quoteFilename(final StringBuffer buf, final String filename) {
477 if (filename.indexOf(' ') >= 0) {
478 buf.setLength(0);
479 buf.append('\"');
480 buf.append(filename);
481 buf.append('\"');
482 return buf.toString();
483 }
484 return filename;
485 }
486
487
488
489
490
491
492 protected int runCommand(final CCTask task, final File workingDir, final String[] cmdline) throws BuildException {
493 return CUtil.runCommand(task, workingDir, cmdline, this.newEnvironment, this.env);
494 }
495
496 protected final void setCommand(final String command) {
497 this.command = command;
498 }
499
500 }