1 /// Common code for Visual Studio generators.
2 module mage.gen.vsCommon;
3 
4 import mage;
5 
6 import mage.util.option;
7 import mage.util.reflection;
8 import mage.msbuild;
9 import mage.msbuild.cpp;
10 import vcxproj = mage.msbuild.vcxproj;
11 import sln = mage.msbuild.sln;
12 
13 import std.format : format;
14 import std.conv : to;
15 import std.uuid;
16 
17 
18 class VSGeneratorBase : IGenerator
19 {
20   protected VSInfo info;
21 
22   this()
23   {
24     info.supportedLanguages ~= "c";
25     info.supportedLanguages ~= "cpp";
26   }
27 
28   override void generate(MagicContext context, Target[] targets)
29   {
30     MSBuildProject[] projects;
31     auto slnName = *G.first("name").enforce("Global property `name' must be set.");
32     auto _generateBlock = log.Block(`Generating for project "%s"`, slnName);
33 
34   targetProcessing:
35     foreach(target; targets)
36     {
37       auto targetName = target.properties["name"].get!string();
38       auto _ = log.Block(`Processing target "%s"`.format(targetName));
39 
40       auto targetEnv = Environment("%s_env".format(targetName), target.properties, G.env);
41       context.setActive(targetEnv);
42       scope(exit) context.popActive();
43 
44       auto targetType = targetEnv.first("type").get!string();
45       log.trace(`Target type is "%s"`, targetType);
46       if(targetType == "none") {
47         log.trace(`Skipping target.`);
48         continue;
49       }
50 
51       string lang;
52       {
53         auto varLang = targetEnv.first("language");
54         if(!(*varLang).hasValue()) {
55           log.error("The `language' property has to be set either as a global property, or on a per-Target basis.");
56           assert(0);
57         }
58         lang = varLang.get!string();
59       }
60       log.info(`Language "%s"`.format(lang));
61     languageProcessing:
62       foreach(supportedLang; info.supportedLanguages)
63       {
64         if(lang != supportedLang) {
65           continue languageProcessing;
66         }
67 
68         // Consolidate all "includePaths" in the target properties.
69         auto allIncludePaths = targetEnv.all("includePaths").map!(a => a.get!(Path[])).joiner();
70         foreach(ref path; allIncludePaths)
71         {
72           if(!path.isAbsolute) {
73             auto mageFilePath = target["mageFilePath"].get!Path;
74             path = mageFilePath.parent ~ path;
75           }
76         }
77         log.info("Include paths: %(\n...| - %s%)", allIncludePaths);
78         target["includePaths"] = allIncludePaths.array();
79         auto proj = createProject(context, this.info, target);
80         proj.toolsVersion = this.info.toolsVersion;
81         if(auto pValue = target.tryGet("toolsVersion")) {
82           proj.toolsVersion = pValue.get!string;
83         }
84         projects ~= proj;
85         target["%s_vcxproj".format(this.info.genName)] = &projects[$-1];
86         continue targetProcessing;
87       }
88 
89       assert(0, `Unsupported language: "%s"; Supported languages: [%-(%s, %)]`.format(lang, this.info.supportedLanguages));
90     }
91 
92     foreach(proj; projects)
93     {
94       auto filePath = Path(proj.name) ~ "%s.vcxproj".format(proj.name);
95       vcxproj.generateFile(proj, filePath);
96     }
97 
98     // Generate .sln file
99     sln.generateFile(this.info, projects, Path("%s.sln".format(slnName)));
100   }
101 }