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.apple;
21
22 import java.io.File;
23 import java.io.IOException;
24 import java.text.NumberFormat;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.Collections;
28 import java.util.Comparator;
29 import java.util.HashMap;
30 import java.util.List;
31 import java.util.Locale;
32 import java.util.Map;
33
34 import javax.xml.transform.TransformerConfigurationException;
35
36 import org.apache.tools.ant.BuildException;
37 import org.xml.sax.SAXException;
38
39 import com.github.maven_nar.cpptasks.CCTask;
40 import com.github.maven_nar.cpptasks.CUtil;
41 import com.github.maven_nar.cpptasks.TargetInfo;
42 import com.github.maven_nar.cpptasks.compiler.CommandLineCompilerConfiguration;
43 import com.github.maven_nar.cpptasks.compiler.CommandLineLinkerConfiguration;
44 import com.github.maven_nar.cpptasks.compiler.ProcessorConfiguration;
45 import com.github.maven_nar.cpptasks.gcc.GccCCompiler;
46 import com.github.maven_nar.cpptasks.ide.CommentDef;
47 import com.github.maven_nar.cpptasks.ide.DependencyDef;
48 import com.github.maven_nar.cpptasks.ide.ProjectDef;
49 import com.github.maven_nar.cpptasks.ide.ProjectWriter;
50
51
52
53
54
55
56
57 public final class XcodeProjectWriter implements ProjectWriter {
58
59
60
61
62
63
64
65
66 private static final class PBXObjectRef {
67
68
69
70 private static int nextID = 0;
71
72
73
74 private final String id;
75
76
77
78 private final Map properties;
79
80
81
82
83
84
85
86 public PBXObjectRef(final Map props) {
87 if (props == null) {
88 throw new NullPointerException("props");
89 }
90 final StringBuffer buf = new StringBuffer("000000000000000000000000");
91 final String idStr = Integer.toHexString(nextID++);
92 buf.replace(buf.length() - idStr.length(), buf.length(), idStr);
93 this.id = buf.toString();
94 this.properties = props;
95 }
96
97
98
99
100
101
102 public String getID() {
103 return this.id;
104 }
105
106
107
108
109
110
111 public Map getProperties() {
112 return this.properties;
113 }
114
115
116
117
118
119
120 @Override
121 public String toString() {
122 return this.id;
123 }
124 }
125
126
127
128
129
130
131
132
133
134
135 private static PBXObjectRef createPBXBuildFile(final PBXObjectRef fileRef, final Map settings) {
136 final Map map = new HashMap();
137 map.put("fileRef", fileRef);
138 map.put("isa", "PBXBuildFile");
139 if (settings != null) {
140 map.put("settings", settings);
141 }
142 return new PBXObjectRef(map);
143 }
144
145
146
147
148
149
150
151
152
153
154 private static PBXObjectRef createPBXContainerItemProxy(final PBXObjectRef containerPortal, final int proxyType,
155 final String remoteInfo) {
156 final Map map = new HashMap();
157 map.put("isa", "PBXContainerItemProxy");
158 map.put("containerPortal", containerPortal);
159 map.put("proxyType", NumberFormat.getIntegerInstance(Locale.US).format(proxyType));
160 map.put("remoteInfo", remoteInfo);
161 return new PBXObjectRef(map);
162 }
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179 private static PBXObjectRef createPBXCopyFilesBuildPhase(final int buildActionMask, final String dstPath,
180 final String dstSubfolderSpec, final List files, final boolean runOnly) {
181 final Map map = new HashMap();
182 map.put("isa", "PBXCopyFilesBuildPhase");
183 map.put("buildActionMask", NumberFormat.getIntegerInstance(Locale.US).format(buildActionMask));
184 map.put("dstPath", dstPath);
185 map.put("dstSubfolderSpec", dstSubfolderSpec);
186 map.put("files", files);
187 map.put("runOnlyForDeploymentPostprocessing", toString(runOnly));
188 return new PBXObjectRef(map);
189 }
190
191
192
193
194
195
196
197
198
199
200
201
202 private static PBXObjectRef createPBXFileReference(final String sourceTree, final String baseDir, final File file) {
203 final Map map = new HashMap();
204 map.put("isa", "PBXFileReference");
205
206 final String relPath = CUtil.toUnixPath(CUtil.getRelativePath(baseDir, file));
207 map.put("path", relPath);
208 map.put("name", file.getName());
209 map.put("sourceTree", sourceTree);
210 return new PBXObjectRef(map);
211 }
212
213
214
215
216
217
218
219
220
221
222
223
224 private static PBXObjectRef createPBXFrameworksBuildPhase(final int buildActionMask, final List files,
225 final boolean runOnly) {
226 final Map map = new HashMap();
227 map.put("isa", "PBXFrameworksBuildPhase");
228 map.put("buildActionMask", NumberFormat.getIntegerInstance(Locale.US).format(buildActionMask));
229 map.put("files", files);
230 map.put("runOnlyForDeploymentPostprocessing", toString(runOnly));
231 return new PBXObjectRef(map);
232 }
233
234
235
236
237
238
239
240
241
242
243
244
245 private static PBXObjectRef createPBXGroup(final String name, final String sourceTree, final List children) {
246 final Map map = new HashMap();
247 map.put("isa", "PBXGroup");
248 map.put("name", name);
249 map.put("sourceTree", sourceTree);
250 map.put("children", children);
251 return new PBXObjectRef(map);
252 }
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277 private static PBXObjectRef createPBXNativeTarget(final String name, final PBXObjectRef buildConfigurationList,
278 final List buildPhases, final List buildRules, final List dependencies, final String productInstallPath,
279 final String productName, final PBXObjectRef productReference, final String productType) {
280 final Map map = new HashMap();
281 map.put("isa", "PBXNativeTarget");
282 map.put("buildConfigurationList", buildConfigurationList);
283 map.put("buildPhases", buildPhases);
284 map.put("buildRules", buildRules);
285 map.put("dependencies", dependencies);
286 map.put("name", name);
287 map.put("productInstallPath", productInstallPath);
288 map.put("productName", productName);
289 map.put("productReference", productReference);
290 map.put("productType", productType);
291 return new PBXObjectRef(map);
292 }
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309 private static PBXObjectRef createPBXProject(final PBXObjectRef buildConfigurationList, final PBXObjectRef mainGroup,
310 final String projectDirPath, final String projectRoot, final List targets) {
311 final Map map = new HashMap();
312 map.put("isa", "PBXProject");
313 map.put("buildConfigurationList", buildConfigurationList.getID());
314 map.put("hasScannedForEncodings", "0");
315 map.put("mainGroup", mainGroup.getID());
316 map.put("projectDirPath", projectDirPath);
317 map.put("targets", targets);
318 map.put("projectRoot", projectRoot);
319 return new PBXObjectRef(map);
320 }
321
322
323
324
325
326
327
328
329
330
331 private static PBXObjectRef createPBXReferenceProxy(final PBXObjectRef remoteRef, final DependencyDef dependency) {
332 final Map map = new HashMap();
333 map.put("isa", "PBXReferenceProxy");
334 final String fileType = "compiled.mach-o.dylib";
335 map.put("fileType", fileType);
336 map.put("remoteRef", remoteRef);
337 map.put("path", dependency.getFile().getName() + ".dylib");
338 map.put("sourceTree", "BUILT_PRODUCTS_DIR");
339 return new PBXObjectRef(map);
340 }
341
342
343
344
345
346
347
348
349
350
351
352
353 private static PBXObjectRef createPBXSourcesBuildPhase(final int buildActionMask, final List files,
354 final boolean runOnly) {
355 final Map map = new HashMap();
356 map.put("buildActionMask", String.valueOf(buildActionMask));
357 map.put("files", files);
358 map.put("isa", "PBXSourcesBuildPhase");
359 map.put("runOnlyForDeploymentPostprocessing", toString(runOnly));
360 return new PBXObjectRef(map);
361 }
362
363
364
365
366
367
368
369
370
371
372 private static PBXObjectRef createXCBuildConfiguration(final String name, final Map<String, ?> buildSettings) {
373 final Map map = new HashMap();
374 map.put("isa", "XCBuildConfiguration");
375 map.put("buildSettings", buildSettings);
376 map.put("name", name);
377 return new PBXObjectRef(map);
378 }
379
380
381
382
383
384
385
386
387 private static PBXObjectRef createXCConfigurationList(final List buildConfigurations) {
388 final Map map = new HashMap();
389 map.put("isa", "XCConfigurationList");
390 map.put("buildConfigurations", buildConfigurations);
391 return new PBXObjectRef(map);
392 }
393
394
395
396
397
398
399
400
401 private static String toString(final boolean b) {
402 if (b) {
403 return "1";
404 } else {
405 return "0";
406 }
407 }
408
409
410
411
412 public XcodeProjectWriter() {
413 }
414
415
416
417
418
419
420
421
422
423
424
425 private PBXObjectRef addDependency(final Map objects, final PBXObjectRef project, final List mainGroupChildren,
426 final String baseDir, final DependencyDef dependency) {
427 if (dependency.getFile() != null) {
428 final File xcodeDir = new File(dependency.getFile().getAbsolutePath() + ".xcodeproj");
429 if (xcodeDir.exists()) {
430 final PBXObjectRef xcodePrj = createPBXFileReference("SOURCE_ROOT", baseDir, xcodeDir);
431 mainGroupChildren.add(xcodePrj);
432 objects.put(xcodePrj.getID(), xcodePrj.getProperties());
433
434 final int proxyType = 2;
435 final PBXObjectRef proxy = createPBXContainerItemProxy(xcodePrj, proxyType, dependency.getName());
436 objects.put(proxy.getID(), proxy.getProperties());
437
438 final PBXObjectRef referenceProxy = createPBXReferenceProxy(proxy, dependency);
439 objects.put(referenceProxy.getID(), referenceProxy.getProperties());
440
441 final PBXObjectRef buildFile = createPBXBuildFile(referenceProxy, Collections.emptyMap());
442 objects.put(buildFile.getID(), buildFile.getProperties());
443
444 final List productsChildren = new ArrayList();
445 productsChildren.add(referenceProxy);
446 final PBXObjectRef products = createPBXGroup("Products", "<group>", productsChildren);
447 objects.put(products.getID(), products.getProperties());
448
449 final Map projectReference = new HashMap();
450 projectReference.put("ProductGroup", products);
451 projectReference.put("ProjectRef", xcodePrj);
452
453 List projectReferences = (List) project.getProperties().get("ProjectReferences");
454 if (projectReferences == null) {
455 projectReferences = new ArrayList();
456 project.getProperties().put("ProjectReferences", projectReferences);
457 }
458 projectReferences.add(projectReference);
459 return buildFile;
460 }
461 }
462 return null;
463 }
464
465
466
467
468
469
470
471
472
473
474 private PBXObjectRef addDocumentationGroup(final Map objects, final String sourceTree) {
475 final List productsList = new ArrayList();
476 final PBXObjectRef products = createPBXGroup("Documentation", sourceTree, productsList);
477 objects.put(products.getID(), products.getProperties());
478 return products;
479 }
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496 private PBXObjectRef addNativeTarget(final Map objects, final TargetInfo linkTarget, final PBXObjectRef product,
497 final String projectName, final List<PBXObjectRef> sourceGroupChildren,
498 final List<PBXObjectRef> frameworkBuildFiles) {
499
500 final PBXObjectRef buildConfigurations = addNativeTargetConfigurationList(objects, projectName);
501
502 int buildActionMask = 2147483647;
503 final List<PBXObjectRef> buildPhases = new ArrayList<>();
504
505 final Map settings = new HashMap();
506 settings.put("ATTRIBUTES", new ArrayList());
507 final List buildFiles = new ArrayList();
508 for (final PBXObjectRef sourceFile : sourceGroupChildren) {
509 final PBXObjectRef buildFile = createPBXBuildFile(sourceFile, settings);
510 buildFiles.add(buildFile);
511 objects.put(buildFile.getID(), buildFile.getProperties());
512 }
513
514 final PBXObjectRef sourcesBuildPhase = createPBXSourcesBuildPhase(buildActionMask, buildFiles, false);
515 objects.put(sourcesBuildPhase.getID(), sourcesBuildPhase.getProperties());
516 buildPhases.add(sourcesBuildPhase);
517
518 buildActionMask = 8;
519 final PBXObjectRef frameworksBuildPhase = createPBXFrameworksBuildPhase(buildActionMask, frameworkBuildFiles, false);
520 objects.put(frameworksBuildPhase.getID(), frameworksBuildPhase.getProperties());
521 buildPhases.add(frameworksBuildPhase);
522
523 final PBXObjectRef copyFilesBuildPhase = createPBXCopyFilesBuildPhase(8, "/usr/share/man/man1", "0",
524 new ArrayList(), true);
525 objects.put(copyFilesBuildPhase.getID(), copyFilesBuildPhase.getProperties());
526 buildPhases.add(copyFilesBuildPhase);
527
528 final List buildRules = new ArrayList();
529
530 final List dependencies = new ArrayList();
531
532 final String productInstallPath = "$(HOME)/bin";
533
534 final String productType = getProductType(linkTarget);
535
536 final PBXObjectRef nativeTarget = createPBXNativeTarget(projectName, buildConfigurations, buildPhases, buildRules,
537 dependencies, productInstallPath, projectName, product, productType);
538 objects.put(nativeTarget.getID(), nativeTarget.getProperties());
539
540 return nativeTarget;
541 }
542
543
544
545
546
547
548
549
550
551
552 private PBXObjectRef addNativeTargetConfigurationList(final Map objects, final String projectName) {
553
554
555
556
557
558 final List<PBXObjectRef> configurations = new ArrayList<>();
559 final Map debugSettings = new HashMap();
560 debugSettings.put("COPY_PHASE_STRIP", "NO");
561 debugSettings.put("GCC_DYNAMIC_NO_PIC", "NO");
562 debugSettings.put("GCC_ENABLE_FIX_AND_CONTINUE", "YES");
563 debugSettings.put("GCC_MODEL_TUNING", "G5");
564 debugSettings.put("GCC_OPTIMIZATION_LEVEL", "0");
565 debugSettings.put("INSTALL_PATH", "$(HOME)/bin");
566 debugSettings.put("PRODUCT_NAME", projectName);
567 debugSettings.put("ZERO_LINK", "YES");
568 final PBXObjectRef debugConfig = createXCBuildConfiguration("Debug", debugSettings);
569 objects.put(debugConfig.getID(), debugConfig.getProperties());
570 configurations.add(debugConfig);
571
572 final Map<String, Object> releaseSettings = new HashMap<>();
573 final List<String> archs = new ArrayList<>();
574 archs.add("ppc");
575 archs.add("i386");
576 releaseSettings.put("ARCHS", archs);
577 releaseSettings.put("GCC_GENERATE_DEBUGGING_SYMBOLS", "NO");
578 releaseSettings.put("GCC_MODEL_TUNING", "G5");
579 releaseSettings.put("INSTALL_PATH", "$(HOME)/bin");
580 releaseSettings.put("PRODUCT_NAME", projectName);
581 final PBXObjectRef releaseConfig = createXCBuildConfiguration("Release", releaseSettings);
582 objects.put(releaseConfig.getID(), releaseConfig.getProperties());
583 configurations.add(releaseConfig);
584
585 final PBXObjectRef configurationList = createXCConfigurationList(configurations);
586 objects.put(configurationList.getID(), configurationList.getProperties());
587 return configurationList;
588 }
589
590
591
592
593
594
595
596
597
598
599 private PBXObjectRef addProduct(final Map objects, final TargetInfo linkTarget) {
600
601
602
603
604 final PBXObjectRef executable = createPBXFileReference("BUILD_PRODUCTS_DIR", linkTarget.getOutput().getParent(),
605 linkTarget.getOutput());
606 final Map executableProperties = executable.getProperties();
607
608 final String fileType = getFileType(linkTarget);
609 executableProperties.put("explicitFileType", fileType);
610 executableProperties.put("includeInIndex", "0");
611 objects.put(executable.getID(), executableProperties);
612
613 return executable;
614 }
615
616
617
618
619
620
621
622
623
624
625
626
627 private PBXObjectRef addProjectConfigurationList(final Map objects, final String baseDir,
628 final List<DependencyDef> dependencies, final CommandLineCompilerConfiguration compilerConfig,
629 final CommandLineLinkerConfiguration linkerConfig) {
630
631
632
633
634 final List configurations = new ArrayList();
635 final Map<String, Object> debugSettings = new HashMap<>();
636 debugSettings.put("GCC_WARN_ABOUT_RETURN_TYPE", "YES");
637 debugSettings.put("GCC_WARN_UNUSED_VARIABLE", "YES");
638 debugSettings.put("PREBINDING", "NO");
639 debugSettings.put("SDKROOT", "/Developer/SDKs/MacOSX10.4u.sdk");
640
641 final PBXObjectRef debugConfig = createXCBuildConfiguration("Debug", debugSettings);
642 objects.put(debugConfig.getID(), debugConfig.getProperties());
643 configurations.add(debugConfig);
644
645 final Map<String, Object> releaseSettings = new HashMap<>();
646 releaseSettings.put("GCC_WARN_ABOUT_RETURN_TYPE", "YES");
647 releaseSettings.put("GCC_WARN_UNUSED_VARIABLE", "YES");
648 releaseSettings.put("PREBINDING", "NO");
649 releaseSettings.put("SDKROOT", "/Developer/SDKs/MacOSX10.4u.sdk");
650 final PBXObjectRef releaseConfig = createXCBuildConfiguration("Release", releaseSettings);
651 objects.put(releaseConfig.getID(), releaseConfig.getProperties());
652 configurations.add(releaseConfig);
653 final PBXObjectRef configurationList = createXCConfigurationList(configurations);
654 final Map projectConfigurationListProperties = configurationList.getProperties();
655 projectConfigurationListProperties.put("defaultConfigurationIsVisible", "0");
656 projectConfigurationListProperties.put("defaultConfigurationName", "Debug");
657 objects.put(configurationList.getID(), configurationList.getProperties());
658
659
660
661
662 final File[] includeDirs = compilerConfig.getIncludePath();
663 if (includeDirs.length > 0) {
664 final List<String> includePaths = new ArrayList<>();
665 final Map<String, String> includePathMap = new HashMap<>();
666 for (final File includeDir : includeDirs) {
667 if (!CUtil.isSystemPath(includeDir)) {
668 final String absPath = includeDir.getAbsolutePath();
669 if (!includePathMap.containsKey(absPath)) {
670 if (absPath.startsWith("/usr/")) {
671 includePaths.add(CUtil.toUnixPath(absPath));
672 } else {
673 final String relPath = CUtil.toUnixPath(CUtil.getRelativePath(baseDir, includeDir));
674 includePaths.add(relPath);
675 }
676 includePathMap.put(absPath, absPath);
677 }
678 }
679 }
680 includePaths.add("${inherited)");
681 debugSettings.put("HEADER_SEARCH_PATHS", includePaths);
682 releaseSettings.put("HEADER_SEARCH_PATHS", includePaths);
683 }
684
685
686
687
688
689 final String[] preArgs = compilerConfig.getPreArguments();
690 final List<String> defines = new ArrayList<>();
691 for (final String preArg : preArgs) {
692 if (preArg.startsWith("-D")) {
693 defines.add(preArg.substring(2));
694 }
695 }
696 if (defines.size() > 0) {
697 defines.add("$(inherited)");
698 debugSettings.put("GCC_PREPROCESSOR_DEFINITIONS", defines);
699 releaseSettings.put("GCC_PREPROCESSOR_DEFINITIONS", defines);
700 }
701
702 if (linkerConfig != null) {
703 final Map<String, String> librarySearchMap = new HashMap<>();
704 final List<String> librarySearchPaths = new ArrayList<>();
705 final List<String> otherLdFlags = new ArrayList<>();
706 final String[] linkerArgs = linkerConfig.getEndArguments();
707 for (final String linkerArg : linkerArgs) {
708 if (linkerArg.startsWith("-L")) {
709 final String libDir = linkerArg.substring(2);
710 if (!librarySearchMap.containsKey(libDir)) {
711 if (!libDir.equals("/usr/lib")) {
712 librarySearchPaths.add(CUtil.toUnixPath(CUtil.getRelativePath(baseDir, new File(libDir))));
713 }
714 librarySearchMap.put(libDir, libDir);
715
716 }
717 } else if (linkerArg.startsWith("-l")) {
718
719
720
721 final String libName = linkerArg.substring(2);
722 boolean found = false;
723 for (final DependencyDef dependency : dependencies) {
724 if (libName.startsWith(dependency.getName())) {
725 final File dependencyFile = dependency.getFile();
726 if (dependencyFile != null && new File(dependencyFile.getAbsolutePath() + ".xcodeproj").exists()) {
727 found = true;
728 break;
729 }
730 }
731 }
732 if (!found) {
733 otherLdFlags.add(linkerArg);
734 }
735 }
736 }
737
738 debugSettings.put("LIBRARY_SEARCH_PATHS", librarySearchPaths);
739 debugSettings.put("OTHER_LDFLAGS", otherLdFlags);
740 releaseSettings.put("LIBRARY_SEARCH_PATHS", librarySearchPaths);
741 releaseSettings.put("OTHER_LDFLAGS", otherLdFlags);
742 }
743 return configurationList;
744 }
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759 private List<PBXObjectRef> addSources(final Map objects, final String sourceTree, final String basePath,
760 final Map<String, TargetInfo> targets) {
761 final List<PBXObjectRef> sourceGroupChildren = new ArrayList<>();
762
763 final List<File> sourceList = new ArrayList<>(targets.size());
764 for (final TargetInfo info : targets.values()) {
765 final File[] targetsources = info.getSources();
766 Collections.addAll(sourceList, targetsources);
767 }
768 final File[] sortedSources = sourceList.toArray(new File[sourceList.size()]);
769 Arrays.sort(sortedSources, new Comparator<File>() {
770 @Override
771 public int compare(final File o1, final File o2) {
772 return o1.getName().compareTo(o2.getName());
773 }
774 });
775 for (final File sortedSource : sortedSources) {
776 final PBXObjectRef fileRef = createPBXFileReference(sourceTree, basePath, sortedSource);
777 sourceGroupChildren.add(fileRef);
778 objects.put(fileRef.getID(), fileRef.getProperties());
779 }
780
781 return sourceGroupChildren;
782 }
783
784
785
786
787
788
789
790
791
792 private CommandLineCompilerConfiguration getBaseCompilerConfiguration(final Map targets) {
793
794
795
796 CommandLineCompilerConfiguration compilerConfig;
797
798
799
800 for (final Object o : targets.values()) {
801 final TargetInfo targetInfo = (TargetInfo) o;
802 final ProcessorConfiguration config = targetInfo.getConfiguration();
803
804
805
806 if (config instanceof CommandLineCompilerConfiguration) {
807 compilerConfig = (CommandLineCompilerConfiguration) config;
808 if (compilerConfig.getCompiler() instanceof GccCCompiler) {
809 return compilerConfig;
810 }
811 }
812 }
813 return null;
814 }
815
816 private String getFileType(final TargetInfo linkTarget) {
817 switch (getProductTypeIndex(linkTarget)) {
818 case 1:
819 return "archive.ar";
820 case 2:
821 return "compiled.mach-o.dylib";
822 default:
823 return "compiled.mach-o.executable";
824 }
825 }
826
827 private String getProductType(final TargetInfo linkTarget) {
828 switch (getProductTypeIndex(linkTarget)) {
829 case 1:
830 return "com.apple.product-type.library.static";
831 case 2:
832 return "com.apple.product-type.library.dynamic";
833 default:
834 return "com.apple.product-type.tool";
835 }
836 }
837
838 private int getProductTypeIndex(final TargetInfo linkTarget) {
839 final String outPath = linkTarget.getOutput().getPath();
840 String outExtension = null;
841 final int lastDot = outPath.lastIndexOf('.');
842 if (lastDot != -1) {
843 outExtension = outPath.substring(lastDot);
844 }
845 if (".a".equalsIgnoreCase(outExtension) || ".lib".equalsIgnoreCase(outExtension)) {
846 return 1;
847 } else if (".dylib".equalsIgnoreCase(outExtension) || ".so".equalsIgnoreCase(outExtension)
848 || ".dll".equalsIgnoreCase(outExtension)) {
849 return 2;
850 }
851 return 0;
852 }
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870 @Override
871 public void writeProject(final File fileName, final CCTask task, final ProjectDef projectDef,
872 final List<File> sources, final Map<String, TargetInfo> targets, final TargetInfo linkTarget) throws IOException {
873
874 final File xcodeDir = new File(fileName + ".xcodeproj");
875 if (!projectDef.getOverwrite() && xcodeDir.exists()) {
876 throw new BuildException("Not allowed to overwrite project file " + xcodeDir.toString());
877 }
878
879 final CommandLineCompilerConfiguration compilerConfig = getBaseCompilerConfiguration(targets);
880 if (compilerConfig == null) {
881 throw new BuildException("Unable to find compilation target using GNU C++ compiler");
882 }
883
884 CommandLineLinkerConfiguration linkerConfig = null;
885 if (linkTarget.getConfiguration() instanceof CommandLineLinkerConfiguration) {
886 linkerConfig = (CommandLineLinkerConfiguration) linkTarget.getConfiguration();
887 }
888
889 String projectName = projectDef.getName();
890 if (projectName == null) {
891 projectName = fileName.getName();
892 }
893 final String basePath = fileName.getAbsoluteFile().getParent();
894
895 xcodeDir.mkdir();
896
897 final File xcodeProj = new File(xcodeDir, "project.pbxproj");
898
899
900
901
902 final Map<String, Object> propertyList = new HashMap<>();
903 propertyList.put("archiveVersion", "1");
904 propertyList.put("classes", new HashMap());
905 propertyList.put("objectVersion", "42");
906 final Map objects = new HashMap();
907
908 final String sourceTree = "<source>";
909
910
911
912
913 final List<PBXObjectRef> sourceGroupChildren = addSources(objects, "SOURCE_ROOT", basePath, targets);
914 final PBXObjectRef sourceGroup = createPBXGroup("Source", sourceTree, sourceGroupChildren);
915 objects.put(sourceGroup.getID(), sourceGroup.getProperties());
916
917
918
919
920 final PBXObjectRef product = addProduct(objects, linkTarget);
921 final List<PBXObjectRef> productsList = new ArrayList<>();
922 productsList.add(product);
923 final PBXObjectRef productsGroup = createPBXGroup("Products", sourceTree, productsList);
924 objects.put(productsGroup.getID(), productsGroup.getProperties());
925
926
927
928
929 final PBXObjectRef documentationGroup = addDocumentationGroup(objects, sourceTree);
930
931
932
933
934 final List<PBXObjectRef> groups = new ArrayList<>(3);
935 groups.add(sourceGroup);
936 groups.add(documentationGroup);
937 groups.add(productsGroup);
938 final PBXObjectRef mainGroup = createPBXGroup(projectName, sourceTree, groups);
939 final StringBuffer comments = new StringBuffer();
940 for (final CommentDef commentDef : projectDef.getComments()) {
941 comments.append(commentDef);
942 }
943 if (comments.length() > 0) {
944 mainGroup.getProperties().put("comments", comments.toString());
945 }
946 objects.put(mainGroup.getID(), mainGroup.getProperties());
947
948
949
950
951 final PBXObjectRef compilerConfigurations = addProjectConfigurationList(objects, basePath,
952 projectDef.getDependencies(), compilerConfig, linkerConfig);
953
954 final String projectDirPath = "";
955 final List<PBXObjectRef> projectTargets = new ArrayList<>();
956
957
958
959
960
961
962
963
964 final String projectRoot = CUtil.toUnixPath(CUtil.getRelativePath(basePath, projectDef.getProject().getBaseDir()));
965 final PBXObjectRef project = createPBXProject(compilerConfigurations, mainGroup, projectDirPath, projectRoot,
966 projectTargets);
967 objects.put(project.getID(), project.getProperties());
968
969 final List<PBXObjectRef> frameworkBuildFiles = new ArrayList<>();
970 for (final DependencyDef dependency : projectDef.getDependencies()) {
971 final PBXObjectRef buildFile = addDependency(objects, project, groups, basePath, dependency);
972 if (buildFile != null) {
973 frameworkBuildFiles.add(buildFile);
974 }
975 }
976
977
978
979
980 final PBXObjectRef nativeTarget = addNativeTarget(objects, linkTarget, product, projectName, sourceGroupChildren,
981 frameworkBuildFiles);
982 projectTargets.add(nativeTarget);
983
984
985
986
987 propertyList.put("objects", objects);
988 propertyList.put("rootObject", project.getID());
989
990
991
992
993 try {
994 PropertyListSerialization.serialize(propertyList, projectDef.getComments(), xcodeProj);
995 } catch (final TransformerConfigurationException ex) {
996 throw new IOException(ex.toString());
997 } catch (final SAXException ex) {
998 if (ex.getException() instanceof IOException) {
999 throw (IOException) ex.getException();
1000 }
1001 throw new IOException(ex.toString());
1002 }
1003 }
1004
1005 }