1 module mage.msbuild.vcxproj;
2 
3 import mage;
4 import mage.msbuild.clcompile;
5 import mage.msbuild.link;
6 import mage.msbuild.cpp;
7 import xml = mage.util.xml;
8 
9 
10 void generateFile(in MSBuildProject proj, in Path outFile)
11 {
12   import mage.util.stream : FileStream;
13 
14   auto _ = log.Block("Generate .vcxproj in %s", outFile);
15 
16   xml.Doc doc;
17   log.info("Generate vcxproj xml in memory...");
18   doc.append(proj);
19   log.info("Writing vcxproj file...");
20   if(!outFile.parent.exists) {
21     outFile.parent.mkdir();
22   }
23   auto s = FileStream(outFile);
24   xml.serialize(s, doc);
25 }
26 
27 // XML
28 xml.Element* append(P)(ref P parent, in MSBuildProject proj)
29   if(xml.isSomeParent!P)
30 {
31   auto c = parent.child("Project");
32   with(c)
33   {
34     attr("DefaultTargets", "Build");
35     attr("ToolsVersion", proj.toolsVersion);
36     attr("xmlns", "http://schemas.microsoft.com/developer/msbuild/2003");
37     with(child("ItemGroup")) {
38       attr("Label", "ProjectConfigurations");
39       foreach(cfg; proj.configs) {
40         with(child("ProjectConfiguration")) {
41           attr("Include", "%s|%s".format(cfg.name, cfg.architecture));
42           child("Configuration").text(cfg.name);
43           child("Platform").text(cfg.architecture);
44         }
45       }
46     }
47     with(child("PropertyGroup")) {
48       attr("Label", "Globals");
49       child("ProjectGuid").text(`{76793D1E-7BA3-4DBD-A492-2B831B56D616}`);
50       child("Keyword").text("Win32Proj");
51       child("RootNamespace").text("one");
52     }
53     with(child("Import")) {
54       attr("Project", `$(VCTargetsPath)\Microsoft.Cpp.Default.props`);
55     }
56     foreach(cfg; proj.configs) {
57       with(child("PropertyGroup")) {
58         attr("Condition", `'$(Configuration)|$(Platform)'=='%s|%s'`.format(cfg.name, cfg.architecture));
59         attr("Label", "Configuration");
60         assert(cfg.type, "Need a configuration type!");
61         child("ConfigurationType").text(cfg.type);
62         if(cfg.useDebugLibs) {
63           child("UseDebugLibraries").text(cfg.useDebugLibs.unwrap().to!string());
64         }
65         if(cfg.platformToolset) {
66           child("PlatformToolset").text(cfg.platformToolset);
67         }
68         if(cfg.wholeProgramOptimization) {
69           child("WholeProgramOptimization").text(cfg.wholeProgramOptimization.unwrap().to!string());
70         }
71         if(cfg.characterSet) {
72           child("CharacterSet").text(cfg.characterSet);
73         }
74       }
75     }
76     with(child("Import")) {
77       attr("Project", `$(VCTargetsPath)\Microsoft.Cpp.props`);
78     }
79     with(child("ImportGroup")) {
80       attr("Label", "ExtensionSettings");
81     }
82     with(child("ImportGroup")) {
83       attr("Label", "PropertySheets");
84       attr("Condition", "'$(Configuration)|$(Platform)'=='Debug|Win32'");
85       with(child("Import")) {
86         attr("Project", `$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props`);
87         attr("Condition", `exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')`);
88         attr("Label", `LocalAppDataPlatform`);
89       }
90     }
91     with(child("ImportGroup")) {
92       attr("Label", "PropertySheets");
93       attr("Condition", `'$(Configuration)|$(Platform)'=='Release|Win32'`);
94       with(child("Import")) {
95         attr("Project", `$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props`);
96         attr("Condition", `exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')`);
97         attr("Label", `LocalAppDataPlatform`);
98       }
99     }
100     with(child("PropertyGroup")) {
101       attr("Label", "UserMacros");
102     }
103     foreach(cfg; proj.configs) {
104       with(child("PropertyGroup")) {
105         attr("Condition", `'$(Configuration)|$(Platform)'=='%s|%s'`.format(cfg.name, cfg.architecture));
106         if(cfg.linkIncremental) {
107           child("LinkIncremental").text(cfg.linkIncremental.unwrap().to!string());
108         }
109         // Explicitly add a trailing slash to silence the MSBuild warning MSB8004.
110         child("OutDir").text(cfg.outputFile.parent.normalizedData ~ "/");
111         child("IntDir").text(cfg.intermediatesDir.normalizedData ~ "/");
112         child("TargetName").text(cfg.outputFile.stem);
113         child("TargetExt").text(cfg.outputFile.extension);
114       }
115     }
116     // Item definition groups
117     foreach(cfg; proj.configs) {
118       log.trace("Writing config: %s", cfg.name);
119       auto n = child("ItemDefinitionGroup");
120       with(n) {
121         attr("Condition", `'$(Configuration)|$(Platform)'=='%s|%s'`.format(cfg.name, cfg.architecture));
122         (*n).append(cfg.clCompile);
123         (*n).append(cfg.link);
124       }
125     }
126     foreach(cfg; proj.configs) {
127 
128       if(cfg.compilationUnits.length)
129       {
130         with(child("ItemGroup"))
131         {
132           attr("Condition", `'$(Configuration)|$(Platform)'=='%s|%s'`.format(cfg.name, cfg.architecture));
133           foreach(file; cfg.compilationUnits) {
134             with(child("ClCompile")) {
135               attr("Include", file.windowsData);
136             }
137           }
138         }
139       }
140 
141       if(cfg.headerFiles.length)
142       {
143         with(child("ItemGroup"))
144         {
145           attr("Condition", `'$(Configuration)|$(Platform)'=='%s|%s'`.format(cfg.name, cfg.architecture));
146           foreach(file; cfg.headerFiles) {
147             with(child("ClInclude")) {
148               attr("Include", file.windowsData);
149             }
150           }
151         }
152       }
153     }
154     with(child("Import")) {
155       attr("Project", `$(VCTargetsPath)\Microsoft.Cpp.targets`);
156     }
157     with(child("ImportGroup")) {
158       attr("Label", `ExtensionTargets`);
159     }
160   } // /Project
161   return c;
162 }
163 
164 xml.Element* append(P)(ref P parent, ref in ClCompile cl)
165   if(xml.isSomeParent!P)
166 {
167   auto n = parent.child("ClCompile");
168   with(n) {
169     if(!cl.includePaths.empty) {
170       auto paths = cl.includePaths.map!(a => normalizedData(a.exists ? a.resolved() : a)).array;
171       if(cl.inheritIncludePaths) {
172         paths ~= "%(AdditionalIncludeDirectories)";
173       }
174       child("AdditionalIncludeDirectories").text("%-(%s;%)".format(paths));
175     }
176     if(cl.pch) {
177       child("PrecompiledHeader").text(cl.pch);
178     }
179     if(cl.warningLevel) {
180       child("WarningLevel").text(cl.warningLevel);
181     }
182     if(cl.optimization) {
183       child("Optimization").text(cl.optimization);
184     }
185     if(cl.compileAs) {
186       child("CompileAs").text(cl.compileAs);
187     }
188     if(cl.functionLevelLinking)  {
189       child("FunctionLevelLinking").text(cl.functionLevelLinking.unwrap().to!string());
190     }
191     if(cl.intrinsicFunctions) {
192       child("IntrinsicFunctions").text(cl.intrinsicFunctions.unwrap().to!string());
193     }
194     if(!cl.defines.empty) {
195       auto defs = cl.defines.dup;
196       if(cl.inheritDefines) {
197         defs ~= "%(PreprocessorDefinitions)";
198       }
199       child("PreprocessorDefinitions").text("%-(%s;%)".format(defs));
200     }
201   }
202   return n;
203 }
204 
205 // XML
206 xml.Element* append(P)(ref P parent, in Link lnk)
207   if(xml.isSomeParent!P)
208 {
209   log.trace("Link (%s)", &lnk);
210   auto n = parent.child("Link");
211   with(n) {
212     auto deps = lnk.dependencies.dup;
213     if(lnk.inheritDependencies) {
214       deps ~= "%(AdditionalDependencies)";
215     }
216     if(!deps.empty) {
217       child("AdditionalDependencies").text("%-(%s;%)".format(deps));
218     }
219 
220     if(lnk.subSystem) {
221       child("SubSystem").text(lnk.subSystem);
222     }
223     if(lnk.genDebugInfo) {
224       child("GenerateDebugInformation").text(lnk.genDebugInfo);
225     }
226     if(lnk.enableCOMDATFolding) {
227       child("EnableCOMDATFolding").text(lnk.enableCOMDATFolding);
228     }
229     if(lnk.optimizeReferences) {
230       child("OptimizeReferences").text(lnk.optimizeReferences);
231     }
232     log.info("Writing debugSymbols if available.");
233     log.info("Variant value: %s", (cast()lnk.debugSymbols.value).toString());
234     if(lnk.debugSymbols) {
235       child("GenerateDebugInformation").text(lnk.debugSymbols.unwrap().to!string());
236       if(!lnk.debugSymbolsFile.isDot) {
237         child("ProgramDatabaseFile").text(lnk.debugSymbolsFile.normalizedData);
238       }
239     }
240   }
241   return n;
242 }