1 /++
2  + Copyright: Copyright © 2018, Christian Köstlin
3  + License: MIT
4  + Authors: Christian Koestlin
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 auto packageDir()
58 {
59     auto e = std.process.environment.toAA;
60     if ("DUB_PACKAGE_DIR" !in e)
61     {
62         return null;
63     }
64     return e["DUB_PACKAGE_DIR"];
65 }
66 
67 auto dubPackage()
68 {
69     auto e = std.process.environment.toAA;
70     if ("DUB_PACKAGE" !in e)
71     {
72         return null;
73     }
74     return e["DUB_PACKAGE"];
75 }
76 
77 auto getFromDubJsonFromPackageDir(string what)
78 {
79     if (string pd = packageDir)
80     {
81         return getFromDubJson(pd ~ "/dub.json", what);
82     }
83     return null;
84 }
85 
86 string getFromDubSdlFromPackageDir(string what)
87 {
88     if (string pd = packageDir)
89     {
90         return getFromDubSdl(pd ~ "/dub.sdl", what);
91     }
92     return null;
93 }
94 
95 string getFromGit()
96 {
97     auto gitCommand = ["git", "describe", "--dirty"].execute(null, Config.none,
98             size_t.max, packageDir);
99     if (gitCommand.status != 0)
100     {
101         "Cannot get version with git describe --dirty, make sure you have at least one annotated tag"
102             .info;
103         return null;
104     }
105 
106     return gitCommand.output.strip;
107 }
108 
109 string getLicense()
110 {
111     auto what = "license";
112     if (string res = getFromDubJsonFromPackageDir(what))
113     {
114         "Using %s from dub.json '%s'".format(what, res).warning;
115         return res;
116     }
117     if (string res = getFromDubSdlFromPackageDir(what))
118     {
119         "Using %s from dub.sdl '%s'".format(what, res).warning;
120         return res;
121     }
122     throw new Exception("Cannot determine %s".format(what));
123 
124 }
125 
126 string getVersion()
127 {
128     auto what = "version";
129     if (string res = getFromDubJsonFromPackageDir(what))
130     {
131         "Using %s from dub.json '%s'".format(what, res).warning;
132         return res;
133     }
134     if (string res = getFromDubSdlFromPackageDir(what))
135     {
136         "Using %s from dub.sdl '%s'".format(what, res).warning;
137         return res;
138     }
139     if (string res = getFromGit)
140     {
141         "Using %s from git '%s'".format(what, res).warning;
142         return res;
143     }
144     throw new Exception("Cannot determine %s".format(what));
145 }
146 
147 int main(string[] args)
148 {
149     import std.getopt;
150 
151     string packageName;
152     auto info = getopt(args, "packageName", &packageName);
153     if (info.helpWanted)
154     {
155         import packageversion.packageversion;
156         defaultGetoptPrinter("packageversion %s. Generate or update a simple packageversion module.".format(VERSION),
157                 info.options);
158         return 0;
159     }
160     if (packageName == null)
161     {
162         defaultGetoptPrinter("Packagename required.", info.options);
163         return 1;
164     }
165 
166     "packageversion for '%s' in '%s'".format(packageName, packageDir).warning;
167 
168     if (packageName != dubPackage) {
169         writeln(std.process.environment.toAA);
170         "Skipping packageversion".warning;
171         return 0;
172     }
173     auto versionText = getVersion();
174     auto license = getLicense();
175 
176     auto file = (packageDir ? packageDir ~ "/" : "./")
177         ~ "out/generated/packageversion/"
178         ~ packageName.replace(".",  "/") ~ "/packageversion.d";
179     auto moduleText = "module %s.packageversion;\n".format(packageName);
180     auto nameText = "const NAME = \"%s\";\n".format(packageName);
181     auto packageVersionText = "const VERSION = \"%s\";\n".format(versionText);
182     auto licenseText = "const LICENSE = \"%s\";\n".format(license);
183     auto registerVersionText = "static this()\n{\n    import packageversion;\n    packageversion.registerPackageVersion(NAME, VERSION, LICENSE);\n}\n";
184     auto totalText = moduleText ~ nameText ~ packageVersionText ~ licenseText ~ registerVersionText;
185 
186     if (exists(file))
187     {
188         auto content = file.readText;
189         if (content != totalText)
190         {
191             "Updating packageversion module".warning;
192             file.writeContent(totalText);
193         }
194     }
195     else
196     {
197         "Writing packageversion module".warning;
198         file.writeContent(totalText);
199     }
200     return 0;
201 }