1 /**
2    A module for providing interop between reggae and dub
3 */
4 
5 module reggae.dub.interop;
6 
7 import reggae.options;
8 import reggae.dub.info;
9 import reggae.dub.call;
10 import reggae.dub.json;
11 import std.stdio;
12 import std.exception;
13 import std.conv;
14 import std.process;
15 
16 DubInfo[string] gDubInfos;
17 
18 
19 @safe:
20 
21 void maybeCreateReggaefile(in Options options) {
22     if(options.isDubProject && !projectBuildFile(options).exists) {
23         createReggaefile(options);
24     }
25 }
26 
27 void createReggaefile(in Options options) {
28     writeln("[Reggae] Creating reggaefile.d from dub information");
29     auto file = File("reggaefile.d", "w");
30     file.writeln("import reggae;");
31     file.writeln("mixin build!dubDefaultTarget;");
32 
33     if(!options.noFetch) dubFetch(_getDubInfo(options));
34 }
35 
36 
37 private DubInfo _getDubInfo(in Options options) {
38 
39     if("default" !in gDubInfos) {
40         immutable dubBuildArgs = ["dub", "--annotate", "build", "--compiler=dmd", "--print-configs"];
41         immutable dubBuildOutput = _callDub(options, dubBuildArgs);
42         immutable configs = getConfigurations(dubBuildOutput);
43 
44         if(configs.configurations.empty) {
45             immutable descArgs = ["dub", "describe"];
46             immutable descOutput = _callDub(options, descArgs);
47             gDubInfos["default"] = getDubInfo(descOutput);
48         } else {
49             foreach(config; configs.configurations) {
50                 immutable descArgs = ["dub", "describe", "-c", config];
51                 immutable descOutput = _callDub(options, descArgs);
52                 gDubInfos[config] = getDubInfo(descOutput);
53             }
54             gDubInfos["default"] = gDubInfos[configs.default_];
55         }
56     }
57 
58     return gDubInfos["default"];
59 }
60 
61 
62 private string _callDub(in Options options, in string[] args) {
63     import std.process;
64     const string[string] env = null;
65     Config config = Config.none;
66     size_t maxOutput = size_t.max;
67     immutable workDir = options.projectPath;
68 
69     immutable ret = execute(args, env, config, maxOutput, workDir);
70     enforce(ret.status == 0, text("Error calling ", args.join(" "), ":\n",
71                                   ret.output));
72     return ret.output;
73 }
74 
75 private void dubFetch(in DubInfo dubInfo) {
76     foreach(cmd; dubInfo.fetchCommands) {
77         immutable cmdStr = "'" ~ cmd.join(" ") ~ "'";
78         writeln("Fetching package with cmd ", cmdStr);
79         immutable ret = execute(cmd);
80         if(ret.status) {
81             () @trusted {
82                 stderr.writeln("Could not execute dub fetch with:\n", cmd.join(" "), "\n",
83                                ret.output);
84             }();
85         }
86     }
87 }
88 
89 
90 void writeDubConfig(in Options options, File file) {
91     file.writeln("import reggae.dub.info;");
92     if(options.isDubProject) {
93         file.writeln("enum isDubProject = true;");
94         auto dubInfo = _getDubInfo(options);
95         immutable targetType = dubInfo.packages[0].targetType;
96         enforce(targetType == "executable" || targetType == "library" || targetType == "staticLibrary",
97                 text("Unsupported dub targetType '", targetType, "'"));
98 
99         file.writeln(`const configToDubInfo = assocList([`);
100 
101         const keys = () @trusted { return gDubInfos.keys; }();
102         foreach(config; keys) {
103             file.writeln(`    assocEntry("`, config, `", `, gDubInfos[config], `),`);
104         }
105         file.writeln(`]);`);
106         file.writeln;
107     } else {
108         file.writeln("enum isDubProject = false;");
109     }
110 }