1 /++
2  + Copyright: Copyright © 2018, Christian Köstlin
3  + License: MIT
4  + Authors: Christian Koestlin, Christian Köstlin
5  +/
6 module app;
7 
8 import std.stdio;
9 import std..string;
10 import std.process;
11 import std.file;
12 import std.path;
13 import std.file;
14 import std.regex;
15 import std.experimental.logger;
16 
17 void writeContent(string file, string content)
18 {
19     file.dirName.mkdirRecurse;
20     std.file.write(file, content);
21 }
22 
23 auto getFromDubSdl(string path, string what)
24 {
25     try
26     {
27         auto pattern = "^%1$s \"(?P<%1$s>.*)\"$".format(what);
28         auto text = readText(path);
29         auto match = matchFirst(text, regex(pattern, "m"));
30         if (match.empty)
31         {
32             return null;
33         }
34         return match[what];
35     }
36     catch (FileException e)
37     {
38         return null;
39     }
40 }
41 
42 auto getFromDubJson(string path, string what)
43 {
44     try
45     {
46         import std.json;
47 
48         return parseJSON(readText(path))[what].str;
49     }
50     catch (FileException e)
51     {
52         return null;
53     }
54 }
55 
56 import std.process;
57 
58 auto packageDir()
59 {
60     auto e = std.process.environment.toAA;
61     if ("DUB_PACKAGE_DIR" !in e)
62     {
63         return null;
64     }
65     return e["DUB_PACKAGE_DIR"];
66 }
67 
68 auto dubPackage()
69 {
70     auto e = std.process.environment.toAA;
71     if ("DUB_PACKAGE" !in e)
72     {
73         return null;
74     }
75     return e["DUB_PACKAGE"];
76 }
77 
78 auto getFromDubJsonFromPackageDir(string what)
79 {
80     if (string pd = packageDir)
81     {
82         return getFromDubJson(pd ~ "/dub.json", what);
83     }
84     return null;
85 }
86 
87 string getFromDubSdlFromPackageDir(string what)
88 {
89     if (string pd = packageDir)
90     {
91         return getFromDubSdl(pd ~ "/dub.sdl", what);
92     }
93     return null;
94 }
95 
96 string getFromGit()
97 {
98     auto gitCommand = ["git", "describe", "--dirty"].execute(null, Config.none,
99             size_t.max, packageDir);
100     if (gitCommand.status != 0)
101     {
102         "Cannot get version with git describe --dirty, make sure you have at least one annotated tag"
103             .info;
104         return null;
105     }
106 
107     return gitCommand.output.strip;
108 }
109 
110 string getLicense()
111 {
112     auto what = "license";
113     if (string res = getFromDubJsonFromPackageDir(what))
114     {
115         "Using %s from dub.json '%s'".format(what, res).warning;
116         return res;
117     }
118     if (string res = getFromDubSdlFromPackageDir(what))
119     {
120         "Using %s from dub.sdl '%s'".format(what, res).warning;
121         return res;
122     }
123     throw new Exception("Cannot determine %s".format(what));
124 
125 }
126 
127 string getVersion()
128 {
129     auto what = "version";
130     if (string res = getFromDubJsonFromPackageDir(what))
131     {
132         "Using %s from dub.json '%s'".format(what, res).warning;
133         return res;
134     }
135     if (string res = getFromDubSdlFromPackageDir(what))
136     {
137         "Using %s from dub.sdl '%s'".format(what, res).warning;
138         return res;
139     }
140     if (string res = getFromGit)
141     {
142         "Using %s from git '%s'".format(what, res).warning;
143         return res;
144     }
145     throw new Exception("Cannot determine %s".format(what));
146 }
147 
148 int main(string[] args)
149 {
150     import std.getopt;
151 
152     class CustomLogger : Logger
153     {
154         this(LogLevel lv) @safe
155         {
156             super(lv);
157         }
158 
159         override void writeLogMsg(ref LogEntry payload)
160         {
161             import std.stdio;
162 
163             writeln(payload.msg);
164         }
165     }
166 
167     sharedLog = new CustomLogger(LogLevel.trace);
168 
169     string packageName = std.process.environment.toAA["DUB_PACKAGE"];
170     auto info = getopt(args, "packageName", &packageName);
171     if (info.helpWanted)
172     {
173         import packageversion.packageversion;
174 
175         defaultGetoptPrinter("packageversion %s. Generate or update a simple packageversion module.".format(VERSION),
176                 info.options);
177         return 0;
178     }
179     if (packageName == null)
180     {
181         defaultGetoptPrinter("Packagename required.", info.options);
182         return 1;
183     }
184     "packageversion for '%s' in '%s'".format(packageName, packageDir).warning;
185 
186     if (packageName != dubPackage)
187     {
188         "Skipping packageversion".warning;
189         return 0;
190     }
191     auto versionText = getVersion();
192     auto license = getLicense();
193 
194     auto file = (packageDir ? packageDir ~ "/" : "./") ~ "out/generated/packageversion/"
195         ~ packageName.replace(".", "/") ~ "/packageversion.d";
196     auto moduleText = "module %s.packageversion;\n".format(packageName);
197     auto nameText = "const NAME = \"%s\";\n".format(packageName);
198     auto packageVersionText = "const VERSION = \"%s\";\n".format(versionText);
199     auto licenseText = "const LICENSE = \"%s\";\n".format(license);
200     auto registerVersionText = "static this()\n{\n    import packageversion;\n    packageversion.registerPackageVersion(NAME, VERSION, LICENSE);\n}\n";
201     auto totalText = moduleText ~ nameText ~ packageVersionText ~ licenseText ~ registerVersionText;
202 
203     if (exists(file))
204     {
205         auto content = file.readText;
206         if (content != totalText)
207         {
208             "Updating packageversion module".warning;
209             file.writeContent(totalText);
210         }
211         else
212         {
213             "Packageversion already up-to-date".warning;
214         }
215     }
216     else
217     {
218         "Writing packageversion module".warning;
219         file.writeContent(totalText);
220     }
221     return 0;
222 }