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.msvc;
21
22 import java.io.File;
23 import java.io.FileOutputStream;
24 import java.io.IOException;
25 import java.io.OutputStream;
26 import java.util.Arrays;
27 import java.util.Comparator;
28 import java.util.List;
29 import java.util.Map;
30
31 import org.apache.tools.ant.BuildException;
32 import org.apache.xml.serialize.OutputFormat;
33 import org.apache.xml.serialize.XMLSerializer;
34 import org.xml.sax.ContentHandler;
35 import org.xml.sax.SAXException;
36 import org.xml.sax.helpers.AttributesImpl;
37
38 import com.github.maven_nar.cpptasks.CCTask;
39 import com.github.maven_nar.cpptasks.CUtil;
40 import com.github.maven_nar.cpptasks.TargetInfo;
41 import com.github.maven_nar.cpptasks.compiler.CommandLineCompilerConfiguration;
42 import com.github.maven_nar.cpptasks.compiler.CommandLineLinkerConfiguration;
43 import com.github.maven_nar.cpptasks.compiler.ProcessorConfiguration;
44 import com.github.maven_nar.cpptasks.ide.CommentDef;
45 import com.github.maven_nar.cpptasks.ide.DependencyDef;
46 import com.github.maven_nar.cpptasks.ide.ProjectDef;
47 import com.github.maven_nar.cpptasks.ide.ProjectWriter;
48
49
50
51
52
53
54 public final class VisualStudioNETProjectWriter implements ProjectWriter {
55
56
57
58
59
60
61
62
63
64
65 private static void addAttribute(final AttributesImpl attributes, final String attrName, final String attrValue) {
66 if (attrName == null) {
67 throw new IllegalArgumentException("attrName");
68 }
69 if (attrValue != null) {
70 attributes.addAttribute(null, attrName, attrName, "#PCDATA", attrValue);
71 }
72 }
73
74
75
76
77 private final String version;
78
79
80
81
82 private final String trueLiteral;
83
84
85
86
87 private final String falseLiteral;
88
89
90
91
92
93
94
95
96
97
98
99 public VisualStudioNETProjectWriter(final String versionArg, final String trueArg, final String falseArg) {
100 if (versionArg == null) {
101 throw new IllegalArgumentException("versionArg");
102 }
103 if (trueArg == null) {
104 throw new IllegalArgumentException("trueArg");
105 }
106 if (falseArg == null) {
107 throw new IllegalArgumentException("falseArg");
108 }
109 this.version = versionArg;
110 this.trueLiteral = trueArg;
111 this.falseLiteral = falseArg;
112 }
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127 private String getAdditionalDependencies(final TargetInfo linkTarget, final List<DependencyDef> projectDependencies,
128 final Map<String, TargetInfo> targets, final String basePath) {
129 String dependencies = null;
130 final File[] linkSources = linkTarget.getAllSources();
131 final StringBuffer buf = new StringBuffer();
132 for (final File linkSource : linkSources) {
133
134
135
136 if (targets.get(linkSource.getName()) == null) {
137
138
139
140
141
142 String relPath = linkSource.getName();
143
144
145
146 boolean fromDependency = false;
147 if (relPath.indexOf(".") > 0) {
148 final String baseName = relPath.substring(0, relPath.indexOf("."));
149 for (DependencyDef depend : projectDependencies) {
150 if (baseName.compareToIgnoreCase(depend.getName()) == 0) {
151 fromDependency = true;
152 }
153 }
154 }
155
156 if (!fromDependency) {
157 if (!CUtil.isSystemPath(linkSource)) {
158 relPath = CUtil.getRelativePath(basePath, linkSource);
159 }
160
161
162
163 if (relPath.indexOf(' ') > 0) {
164 buf.append('\"');
165 buf.append(CUtil.toWindowsPath(relPath));
166 buf.append('\"');
167 } else {
168 buf.append(relPath);
169 }
170 buf.append(' ');
171 }
172 }
173 }
174 if (buf.length() > 0) {
175 buf.setLength(buf.length() - 1);
176 dependencies = buf.toString();
177 }
178 return dependencies;
179
180 }
181
182
183
184
185
186
187
188
189
190
191 private String getAdditionalIncludeDirectories(final String baseDir,
192 final CommandLineCompilerConfiguration compilerConfig) {
193 final File[] includePath = compilerConfig.getIncludePath();
194 final StringBuffer includeDirs = new StringBuffer();
195
196
197 final String[] args = compilerConfig.getPreArguments();
198
199 for (final String arg : args) {
200 if (arg.startsWith("/I")) {
201 includeDirs.append(arg.substring(2));
202 includeDirs.append(';');
203 }
204
205 }
206
207
208 if (includeDirs.length() > 0) {
209 includeDirs.setLength(includeDirs.length() - 1);
210 }
211 return includeDirs.toString();
212 }
213
214
215
216
217
218
219
220
221
222 private CommandLineCompilerConfiguration getBaseCompilerConfiguration(final Map<String, TargetInfo> targets) {
223
224
225
226 for (final TargetInfo targetInfo : targets.values()) {
227 final ProcessorConfiguration config = targetInfo.getConfiguration();
228
229
230
231 if (config instanceof CommandLineCompilerConfiguration) {
232 final CommandLineCompilerConfiguration compilerConfig = (CommandLineCompilerConfiguration) config;
233 if (compilerConfig.getCompiler() instanceof MsvcCCompiler) {
234 return compilerConfig;
235 }
236 }
237 }
238 return null;
239 }
240
241
242
243
244
245
246
247
248 private String getBasicRuntimeChecks(final CommandLineCompilerConfiguration compilerConfig) {
249 String checks = "0";
250 final String[] args = compilerConfig.getPreArguments();
251 for (final String arg : args) {
252 if ("/RTCs".equals(arg)) {
253 checks = "1";
254 }
255 if ("/RTCu".equals(arg)) {
256 checks = "2";
257 }
258 if ("/RTC1".equals(arg) || "/GZ".equals(arg)) {
259 checks = "3";
260 }
261 }
262 return checks;
263 }
264
265
266
267
268
269
270
271
272 private String getCharacterSet(final CommandLineCompilerConfiguration compilerConfig) {
273 final String[] args = compilerConfig.getPreArguments();
274 String charset = "0";
275 for (final String arg : args) {
276 if ("/D_UNICODE".equals(arg) || "/DUNICODE".equals(arg)) {
277 charset = "1";
278 }
279 if ("/D_MBCS".equals(arg)) {
280 charset = "2";
281 }
282 }
283 return charset;
284 }
285
286
287
288
289
290
291
292
293 private String getConfigurationType(final CCTask task) {
294 final String outputType = task.getOuttype();
295 String targtype = "2";
296 if ("executable".equals(outputType)) {
297 targtype = "1";
298 } else if ("static".equals(outputType)) {
299 targtype = "4";
300 }
301 return targtype;
302 }
303
304
305
306
307
308
309
310
311 private String getDebugInformationFormat(final CommandLineCompilerConfiguration compilerConfig) {
312 String format = "0";
313 final String[] args = compilerConfig.getPreArguments();
314 for (final String arg : args) {
315 if ("/Z7".equals(arg)) {
316 format = "1";
317 }
318 if ("/Zd".equals(arg)) {
319 format = "2";
320 }
321 if ("/Zi".equals(arg)) {
322 format = "3";
323 }
324 if ("/ZI".equals(arg)) {
325 format = "4";
326 }
327 }
328 return format;
329 }
330
331
332
333
334
335
336
337
338 private String getDetect64BitPortabilityProblems(final CommandLineCompilerConfiguration compilerConfig) {
339 String warn64 = null;
340 final String[] args = compilerConfig.getPreArguments();
341 for (final String arg : args) {
342 if ("/Wp64".equals(arg)) {
343 warn64 = this.trueLiteral;
344 }
345 }
346 return warn64;
347 }
348
349
350
351
352
353
354
355
356 private String getLinkIncremental(final CommandLineLinkerConfiguration linkerConfig) {
357 String incremental = "0";
358 final String[] args = linkerConfig.getPreArguments();
359 for (final String arg : args) {
360 if ("/INCREMENTAL:NO".equals(arg)) {
361 incremental = "1";
362 }
363 if ("/INCREMENTAL:YES".equals(arg)) {
364 incremental = "2";
365 }
366 }
367 return incremental;
368 }
369
370
371
372
373
374
375
376
377 private String getOptimization(final CommandLineCompilerConfiguration compilerConfig) {
378 final String[] args = compilerConfig.getPreArguments();
379 String opt = "0";
380 for (final String arg : args) {
381 if ("/Od".equals(arg)) {
382 opt = "0";
383 }
384 if ("/O1".equals(arg)) {
385 opt = "1";
386 }
387 if ("/O2".equals(arg)) {
388 opt = "2";
389 }
390 if ("/Ox".equals(arg)) {
391 opt = "3";
392 }
393 }
394 return opt;
395 }
396
397
398
399
400
401
402
403
404 private String getPrecompiledHeaderFile(final CommandLineCompilerConfiguration compilerConfig) {
405 String pch = null;
406 final String[] args = compilerConfig.getPreArguments();
407 for (final String arg : args) {
408 if (arg.startsWith("/Fp")) {
409 pch = arg.substring(3);
410 }
411 }
412 return pch;
413 }
414
415
416
417
418
419
420
421
422
423
424 private String
425 getPreprocessorDefinitions(final CommandLineCompilerConfiguration compilerConfig, final boolean isDebug) {
426 final StringBuffer defines = new StringBuffer();
427 final String[] args = compilerConfig.getPreArguments();
428 for (final String arg : args) {
429 if (arg.startsWith("/D")) {
430 String macro = arg.substring(2);
431 if (isDebug) {
432 if (macro.equals("NDEBUG")) {
433 macro = "_DEBUG";
434 }
435 } else {
436 if (macro.equals("_DEBUG")) {
437 macro = "NDEBUG";
438 }
439 }
440 defines.append(macro);
441 defines.append(";");
442 }
443 }
444
445 if (defines.length() > 0) {
446 defines.setLength(defines.length() - 1);
447 }
448 return defines.toString();
449 }
450
451
452
453
454
455
456
457
458
459
460 private String getRuntimeLibrary(final CommandLineCompilerConfiguration compilerConfig, final boolean isDebug) {
461 String rtl = null;
462 final String[] args = compilerConfig.getPreArguments();
463 for (final String arg : args) {
464 if (arg.startsWith("/MT")) {
465 if (isDebug) {
466 rtl = "1";
467 } else {
468 rtl = "0";
469 }
470 } else if (arg.startsWith("/MD")) {
471 if (isDebug) {
472 rtl = "3";
473 } else {
474 rtl = "2";
475 }
476 }
477 }
478 return rtl;
479 }
480
481
482
483
484
485
486
487
488 private String getSubsystem(final CommandLineLinkerConfiguration linkerConfig) {
489 String subsystem = "0";
490 final String[] args = linkerConfig.getPreArguments();
491 for (final String arg : args) {
492 if ("/SUBSYSTEM:CONSOLE".equals(arg)) {
493 subsystem = "1";
494 }
495 if ("/SUBSYSTEM:WINDOWS".equals(arg)) {
496 subsystem = "2";
497 }
498 if ("/SUBSYSTEM:WINDOWSCE".equals(arg)) {
499 subsystem = "9";
500 }
501 }
502 return subsystem;
503 }
504
505
506
507
508
509
510
511
512 private String getTargetMachine(final CommandLineLinkerConfiguration linkerConfig) {
513 String subsystem = "0";
514 final String[] args = linkerConfig.getPreArguments();
515 for (final String arg : args) {
516 if ("/MACHINE:X86".equals(arg)) {
517 subsystem = "1";
518 }
519 }
520 return subsystem;
521 }
522
523
524
525
526
527
528
529
530 private String getUsePrecompiledHeader(final CommandLineCompilerConfiguration compilerConfig) {
531 String usePCH = "0";
532 final String[] args = compilerConfig.getPreArguments();
533 for (final String arg : args) {
534 if ("/Yc".equals(arg)) {
535 usePCH = "1";
536 }
537 if ("/Yu".equals(arg)) {
538 usePCH = "2";
539 }
540 }
541 return usePCH;
542 }
543
544
545
546
547
548
549
550
551 private String getWarningLevel(final CommandLineCompilerConfiguration compilerConfig) {
552 String warn = null;
553 final String[] args = compilerConfig.getPreArguments();
554 for (final String arg : args) {
555 if ("/W0".equals(arg)) {
556 warn = "0";
557 }
558 if ("/W1".equals(arg)) {
559 warn = "1";
560 }
561 if ("/W2".equals(arg)) {
562 warn = "2";
563 }
564 if ("/W3".equals(arg)) {
565 warn = "3";
566 }
567
568 if ("/W4".equals(arg)) {
569 warn = "4";
570 }
571
572 }
573 return warn;
574 }
575
576
577
578
579
580
581
582
583
584
585
586 private boolean isGroupMember(final String filter, final File candidate) {
587 final String fileName = candidate.getName();
588 final int lastDot = fileName.lastIndexOf('.');
589 if (lastDot >= 0 && lastDot < fileName.length() - 1) {
590 final String extension = ";" + fileName.substring(lastDot + 1).toLowerCase() + ";";
591 final String semiFilter = ";" + filter + ";";
592 return semiFilter.contains(extension);
593 }
594 return false;
595 }
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611 private void writeCompilerElement(final ContentHandler content, final boolean isDebug, final String basePath,
612 final CommandLineCompilerConfiguration compilerConfig) throws SAXException {
613 final AttributesImpl attributes = new AttributesImpl();
614 addAttribute(attributes, "Name", "VCCLCompilerTool");
615 String optimization = getOptimization(compilerConfig);
616 String debugFormat = getDebugInformationFormat(compilerConfig);
617 if (isDebug) {
618 optimization = "0";
619 if ("0".equals(debugFormat)) {
620 debugFormat = "4";
621 }
622 } else {
623 if ("0".equals(optimization)) {
624 optimization = "2";
625 }
626 debugFormat = "0";
627 }
628 addAttribute(attributes, "Optimization", optimization);
629 addAttribute(attributes, "AdditionalIncludeDirectories", getAdditionalIncludeDirectories(basePath, compilerConfig));
630 addAttribute(attributes, "PreprocessorDefinitions", getPreprocessorDefinitions(compilerConfig, isDebug));
631 addAttribute(attributes, "MinimalRebuild", this.trueLiteral);
632 addAttribute(attributes, "BasicRuntimeChecks", getBasicRuntimeChecks(compilerConfig));
633 addAttribute(attributes, "RuntimeLibrary", getRuntimeLibrary(compilerConfig, isDebug));
634 addAttribute(attributes, "UsePrecompiledHeader", getUsePrecompiledHeader(compilerConfig));
635 addAttribute(attributes, "PrecompiledHeaderFile", getPrecompiledHeaderFile(compilerConfig));
636 addAttribute(attributes, "WarningLevel", getWarningLevel(compilerConfig));
637 addAttribute(attributes, "Detect64BitPortabilityProblems", getDetect64BitPortabilityProblems(compilerConfig));
638 addAttribute(attributes, "DebugInformationFormat", debugFormat);
639 content.startElement(null, "Tool", "Tool", attributes);
640 content.endElement(null, "Tool", "Tool");
641
642 }
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658 private void writeConfigurationStartTag(final ContentHandler content, final boolean isDebug, final CCTask task,
659 final CommandLineCompilerConfiguration compilerConfig) throws SAXException {
660 final AttributesImpl attributes = new AttributesImpl();
661 if (isDebug) {
662 addAttribute(attributes, "Name", "Debug|Win32");
663 addAttribute(attributes, "OutputDirectory", "Debug");
664 addAttribute(attributes, "IntermediateDirectory", "Debug");
665 } else {
666 addAttribute(attributes, "Name", "Release|Win32");
667 addAttribute(attributes, "OutputDirectory", "Release");
668 addAttribute(attributes, "IntermediateDirectory", "Release");
669
670 }
671 addAttribute(attributes, "ConfigurationType", getConfigurationType(task));
672 addAttribute(attributes, "CharacterSet", getCharacterSet(compilerConfig));
673 content.startElement(null, "Configuration", "Configuration", attributes);
674 }
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692 private void writeFilteredSources(final String name, final String filter, final String basePath,
693 final File[] sortedSources, final ContentHandler content) throws SAXException {
694 final AttributesImpl filterAttrs = new AttributesImpl();
695 filterAttrs.addAttribute(null, "Name", "Name", "#PCDATA", name);
696 filterAttrs.addAttribute(null, "Filter", "Filter", "#PCDATA", filter);
697 content.startElement(null, "Filter", "Filter", filterAttrs);
698
699 final AttributesImpl fileAttrs = new AttributesImpl();
700 fileAttrs.addAttribute(null, "RelativePath", "RelativePath", "#PCDATA", "");
701
702 for (final File sortedSource : sortedSources) {
703 if (isGroupMember(filter, sortedSource)) {
704 final String relativePath = CUtil.getRelativePath(basePath, sortedSource);
705 fileAttrs.setValue(0, relativePath);
706 content.startElement(null, "File", "File", fileAttrs);
707 content.endElement(null, "File", "File");
708 }
709 }
710 content.endElement(null, "Filter", "Filter");
711
712 }
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732 private void writeLinkerElement(final ContentHandler content, final boolean isDebug,
733 final List<DependencyDef> dependencies, final String basePath, final TargetInfo linkTarget,
734 final Map<String, TargetInfo> targets) throws SAXException {
735 final AttributesImpl attributes = new AttributesImpl();
736 addAttribute(attributes, "Name", "VCLinkerTool");
737
738 final ProcessorConfiguration config = linkTarget.getConfiguration();
739 if (config instanceof CommandLineLinkerConfiguration) {
740 final CommandLineLinkerConfiguration linkerConfig = (CommandLineLinkerConfiguration) config;
741 if (linkerConfig.getLinker() instanceof MsvcCompatibleLinker) {
742 addAttribute(attributes, "LinkIncremental", getLinkIncremental(linkerConfig));
743 if (isDebug) {
744 addAttribute(attributes, "GenerateDebugInformation", this.trueLiteral);
745 } else {
746 addAttribute(attributes, "GenerateDebugInformation", this.falseLiteral);
747 }
748 addAttribute(attributes, "SubSystem", getSubsystem(linkerConfig));
749 addAttribute(attributes, "TargetMachine", getTargetMachine(linkerConfig));
750 }
751 }
752 addAttribute(attributes, "AdditionalDependencies",
753 getAdditionalDependencies(linkTarget, dependencies, targets, basePath));
754 content.startElement(null, "Tool", "Tool", attributes);
755 content.endElement(null, "Tool", "Tool");
756 }
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778 @Override
779 public void writeProject(final File fileName, final CCTask task, final ProjectDef projectDef,
780 final List<File> sources, final Map<String, TargetInfo> targets, final TargetInfo linkTarget)
781 throws IOException, SAXException {
782
783 String projectName = projectDef.getName();
784 if (projectName == null) {
785 projectName = fileName.getName();
786 }
787
788 final File vcprojFile = new File(fileName + ".vcproj");
789 if (!projectDef.getOverwrite() && vcprojFile.exists()) {
790 throw new BuildException("Not allowed to overwrite project file " + vcprojFile.toString());
791 }
792 final File slnFile = new File(fileName + ".sln");
793 if (!projectDef.getOverwrite() && slnFile.exists()) {
794 throw new BuildException("Not allowed to overwrite project file " + slnFile.toString());
795 }
796
797 final CommandLineCompilerConfiguration compilerConfig = getBaseCompilerConfiguration(targets);
798 if (compilerConfig == null) {
799 throw new BuildException("Unable to generate Visual Studio.NET project " + "when Microsoft C++ is not used.");
800 }
801
802 final OutputStream outStream = new FileOutputStream(fileName + ".vcproj");
803 final OutputFormat format = new OutputFormat("xml", "UTF-8", true);
804 final XMLSerializer serializer = new XMLSerializer(outStream, format);
805 final ContentHandler content = serializer.asContentHandler();
806 final String basePath = fileName.getParentFile().getAbsolutePath();
807 content.startDocument();
808
809 for (final CommentDef commentDef : projectDef.getComments()) {
810 final String comment = commentDef.getText();
811 serializer.comment(comment);
812 }
813
814 final AttributesImpl emptyAttrs = new AttributesImpl();
815
816 final AttributesImpl attributes = new AttributesImpl();
817 addAttribute(attributes, "ProjectType", "Visual C++");
818 addAttribute(attributes, "Version", this.version);
819 addAttribute(attributes, "Name", projectName);
820 content.startElement(null, "VisualStudioProject", "VisualStudioProject", attributes);
821
822 content.startElement(null, "Platforms", "Platforms", emptyAttrs);
823 attributes.clear();
824 addAttribute(attributes, "Name", "Win32");
825 content.startElement(null, "Platform", "Platform", attributes);
826 content.endElement(null, "Platform", "Platform");
827 content.endElement(null, "Platforms", "Platforms");
828 content.startElement(null, "Configurations", "Configurations", emptyAttrs);
829
830
831
832
833 writeConfigurationStartTag(content, true, task, compilerConfig);
834 writeCompilerElement(content, true, basePath, compilerConfig);
835 writeLinkerElement(content, true, projectDef.getDependencies(), basePath, linkTarget, targets);
836 content.endElement(null, "Configuration", "Configuration");
837
838
839
840
841 writeConfigurationStartTag(content, false, task, compilerConfig);
842 writeCompilerElement(content, false, basePath, compilerConfig);
843 writeLinkerElement(content, false, projectDef.getDependencies(), basePath, linkTarget, targets);
844 content.endElement(null, "Configuration", "Configuration");
845
846 content.endElement(null, "Configurations", "Configurations");
847 content.startElement(null, "References", "References", emptyAttrs);
848 content.endElement(null, "References", "References");
849 content.startElement(null, "Files", "Files", emptyAttrs);
850
851 final File[] sortedSources = new File[sources.size()];
852 sources.toArray(sortedSources);
853 Arrays.sort(sortedSources, new Comparator<File>() {
854 @Override
855 public int compare(final File o1, final File o2) {
856 return o1.getName().compareTo(o2.getName());
857 }
858 });
859
860 writeFilteredSources("Source Files", "cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx", basePath, sortedSources, content);
861
862 writeFilteredSources("Header Files", "h;hpp;hxx;hm;inl;inc;xsd", basePath, sortedSources, content);
863
864 writeFilteredSources("Resource Files", "rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx", basePath,
865 sortedSources, content);
866
867 content.endElement(null, "Files", "Files");
868 content.startElement(null, "Globals", "Globals", emptyAttrs);
869 content.endElement(null, "Globals", "Globals");
870 content.endElement(null, "VisualStudioProject", "VisualStudioProject");
871 content.endDocument();
872 }
873 }