1 /**
2 Copyright: Copyright (c) 2015-2016 Andrey Penechko.
3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 */
6 module packager;
7 
8 import std.file;
9 import std.string;
10 import std.digest.crc;
11 import std.stdio;
12 import std.zip;
13 import std.path;
14 import std.process;
15 import std.datetime;
16 
17 enum ROOT_PATH = "../..";
18 string testDir;
19 
20 void main(string[] args)
21 {
22 	string semver = "0.7.0";
23 	testDir = buildNormalizedPath(ROOT_PATH, "test");
24 	if (exists(testDir))
25 		rmdirRecurse(testDir);
26 
27 	StopWatch sw;
28 	sw.start();
29 	completeBuild(Arch.x32, semver);
30 	writeln;
31 	completeBuild(Arch.x64, semver);
32 	sw.stop();
33 	writefln("Finished in %.1fs", sw.peek().to!("seconds", real));
34 }
35 
36 void completeBuild(Arch arch, string semver)
37 {
38 	string dub_arch = arch == Arch.x32 ? "x86" : "x86_64";
39 	string launcher_arch = arch == Arch.x32 ? "32" : "64";
40 
41 	string dubCom(Arch arch) {
42 		return format(`dub run --root="tools/launcher" -q --nodeps --build=release --arch=%s -- --release=%s`,
43 				dub_arch, launcher_arch);
44 	}
45 
46 	string com = dubCom(arch);
47 	writefln("Executing '%s'", com); stdout.flush();
48 
49 	auto dub = executeShell(com, null, Config.none, size_t.max, ROOT_PATH);
50 
51 	if (dub.status != 0)
52 		writeln("Failed to run launcher");
53 	dub.output.write;
54 
55 	writefln("Packing %sbit", launcher_arch); stdout.flush();
56 	pack(semver, arch, Platform.windows);
57 }
58 
59 struct ReleasePackage
60 {
61 	string semver;
62 	Arch arch;
63 	Platform platform;
64 	ZipArchive zip;
65 	string fileRoot;
66 	string archRoot;
67 }
68 
69 void pack(string semver, Arch arch, Platform pl)
70 {
71 	ReleasePackage pack = ReleasePackage(
72 		semver,
73 		arch,
74 		pl,
75 		new ZipArchive,
76 		ROOT_PATH);
77 	string archName = archName(&pack, "voxelman");
78 	pack.archRoot = archName;
79 
80 	makePackage(&pack);
81 	string archiveName = buildNormalizedPath(ROOT_PATH, archName) ~ ".zip";
82 	writePackage(&pack, archiveName);
83 
84 	extractArchive(archiveName, testDir);
85 }
86 
87 enum Arch { x64, x32 }
88 enum Platform { windows, linux, macos }
89 string[Platform] platformToString;
90 string[Arch] archToString;
91 static this()
92 {
93 	platformToString = [Platform.windows : "win", Platform.linux : "linux", Platform.macos : "mac"];
94 	archToString = [Arch.x64 : "64", Arch.x32 : "32"];
95 }
96 
97 void makePackage(ReleasePackage* pack)
98 {
99 	pack.addFiles("builds/default", "*.exe");
100 	pack.addFiles("res", "*");
101 	pack.addFiles("config", "*.sdl");
102 	pack.addFiles("lib/"~archToString[pack.arch], "*.dll");
103 	pack.addFile("README.md");
104 	pack.addFile("CHANGELOG.md");
105 	pack.addFile("LICENSE.md");
106 	pack.addFile("pluginpacks/default.txt");
107 	pack.addFile("launcher.exe");
108 }
109 
110 void writePackage(ReleasePackage* pack, string path)
111 {
112 	std.file.write(path, pack.zip.build());
113 }
114 
115 string archName(R)(ReleasePackage* pack, R baseName)
116 {
117 	string arch = archToString[pack.arch];
118 	string platform = platformToString[pack.platform];
119 	return format("%s-v%s-%s%s", baseName, pack.semver, platform, arch);
120 }
121 
122 alias normPath = buildNormalizedPath;
123 alias absPath = absolutePath;
124 void addFiles(ReleasePackage* pack, string path, string pattern)
125 {
126 	import std.file : dirEntries, SpanMode;
127 
128 	string absRoot = pack.fileRoot.absPath.normPath;
129 	foreach (entry; dirEntries(buildPath(pack.fileRoot, path), pattern, SpanMode.depth))
130 	if (entry.isFile) {
131 		string absPath = entry.name.absPath.normPath;
132 		addFile(pack, absPath, relativePath(absPath, absRoot));
133 	}
134 }
135 
136 void addFile(ReleasePackage* pack, string arch_name)
137 {
138 	addFile(pack.zip, buildPath(pack.fileRoot, arch_name).absPath.normPath, buildPath(pack.archRoot, arch_name));
139 }
140 
141 void addFile(ReleasePackage* pack, string fs_name, string arch_name)
142 {
143 	addFile(pack.zip, fs_name.absPath.normPath, buildPath(pack.archRoot, arch_name));
144 }
145 
146 void addFile(ZipArchive arch, string fs_name, string arch_name)
147 {
148 	string norm = arch_name.normPath;
149 	writefln("Add %s as %s", fs_name, norm);
150 	void[] data = std.file.read(fs_name);
151 	ArchiveMember am = new ArchiveMember();
152 	am.name = norm;
153 	am.compressionMethod = CompressionMethod.deflate;
154 	am.expandedData(cast(ubyte[])data);
155 	arch.addMember(am);
156 }
157 
158 void extractArchive(string archive, string pathTo)
159 {
160 	extractArchive(new ZipArchive(std.file.read(archive)), pathTo);
161 }
162 
163 void extractArchive(ZipArchive archive, string pathTo)
164 {
165 	foreach (ArchiveMember am; archive.directory)
166 	{
167 		string targetPath = buildPath(pathTo, am.name);
168 		mkdirRecurse(dirName(targetPath));
169 		archive.expand(am);
170 		std.file.write(targetPath, am.expandedData);
171 	}
172 }