1 module mage.msbuild.link;
2 
3 import mage;
4 import mage.msbuild : VSInfo;
5 import mage.msbuild.cpp;
6 import mage.util.option;
7 
8 import std.range;
9 import std.typetuple : allSatisfy;
10 
11 
12 struct Link
13 {
14   string subSystem;
15   string genDebugInfo;
16   string enableCOMDATFolding;
17   string optimizeReferences;
18   string[] dependencies;
19   bool inheritDependencies = true;
20   Option!bool debugSymbols;
21   Path debugSymbolsFile;
22 }
23 
24 auto createLink(ref MSBuildConfig cfg, ref in VSInfo info, ref Environment env)
25 {
26   // TODO Split this up so it becomes more readable and understandable...
27 
28   auto link = Link();
29 
30   auto _link = log.forcedBlock("Link +++ +++ +++ +++ +++ +++ +++ +++");
31 
32   env.prettyPrint();
33 
34   if(auto var = env.first("linkTargets"))
35   {
36     auto linkTargets = var.get!(Target[]);
37 
38     // Here's the idea:
39     // 1. Check for the existance of linkTargets (above).
40     // 2. Iterate all linkTargets and see whether their configurations match one of ours.
41     // 2.1.1 First, check for ExternalTarget types. These are pre-built binaries, specifying only a set of configurations and libs for them.
42     // 2.1.2 Set a linker dependency for the first matching configuration and continue with the loop.
43     // 2.2.1 Check for a vcxproj file property on the target. If it is a dependency of ours, this property is set and contains the outputFile.
44     // 2.2.2 Check for a matching config and use that outputFile as our dependency.
45 
46     auto _ = log.Block("Processing Link Targets");
47 
48     foreach(ref linkTarget; linkTargets)
49     {
50       auto linkTargetName = linkTarget["name"].toString();
51       log.info("Target: %s", linkTargetName);
52 
53       auto linkTargetEnv = Environment("%s_ltenv".format(linkTargetName), linkTarget.properties, G.env);
54       linkTargetEnv.prettyPrint();
55 
56       auto configMatches = matchingConfigurations(env, linkTargetEnv).filter!(a => a[0].name == cfg.name);
57 
58       if(configMatches.empty) {
59         log.error("No matching configurations found between `%s' and `%s'.", env["name"].toString(), linkTargetName);
60       }
61       else {
62         auto __ = log.Block("Matching configs");
63         foreach(ref match; configMatches)
64         {
65           auto ___ = log.Block("Match");
66           match[0].properties.prettyPrint();
67           match[1].properties.prettyPrint();
68         }
69       }
70 
71       if(linkTarget.isExternal)
72       {
73         foreach(ref match; configMatches)
74         {
75           log.trace(`External link dependency config: %s`, *match[1]);
76           auto libPath = (*match[1])["lib"].get!Path;
77           if(!libPath.isAbsolute) {
78             libPath = env["mageFilePath"].get!Path.parent ~ libPath;
79           }
80           link.dependencies ~= libPath.normalizedData;
81         }
82       }
83       else
84       {
85         auto pProj = linkTarget.tryGet("%s_vcxproj".format(info.genName));
86         if(pProj is null)
87         {
88           log.warning(`Link target "%s" can not be added to linker dependencies at this time. `
89                       `You might have a cyclic dependency.`.format(linkTargetName));
90           continue;
91         }
92 
93         auto proj = pProj.get!(MSBuildProject*);
94         foreach(ref match; configMatches)
95         {
96           // Find the matching msbuild project's config.
97           auto vcxprojConfig = proj.configs.find!(a => a.name == match[1].name);
98           link.dependencies ~= vcxprojConfig.front.link.dependencies[];
99           link.dependencies ~= vcxprojConfig.front.outputFile.normalizedData;
100         }
101       }
102     }
103   }
104 
105   if(auto var = env.first("debugSymbols")) {
106     link.debugSymbols = var.get!bool;
107   }
108 
109   log.trace("Looking for debugSymbolsFile property setting...");
110   if(auto var = env.first("debugSymbolsFile")) {
111     log.trace("Found debugSymbolsFile property setting.");
112     link.debugSymbolsFile = var.get!Path;
113   }
114 
115   // TODO
116 
117   return link;
118 }