1 /** 2 Copyright: Copyright (c) 2015-2018 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 enum TEST_DIR_NAME = "test"; 19 string testDir; 20 21 version(Windows) 22 enum bool is_Windows = true; 23 else 24 enum bool is_Windows = false; 25 26 void makePackage(ReleasePackage* pack) 27 { 28 string archStr = archToString[pack.arch]; 29 pack.addFiles("builds/default", "*.exe"); 30 pack.addFiles("res", "*.ply"); 31 pack.addFiles("config", "*.sdl"); 32 pack.addFile("config/servers.txt"); 33 pack.addFiles("lib/"~archStr, "*.dll"); 34 pack.addFile("saves/test"~archStr~".db"); 35 pack.addFile("README.md"); 36 pack.addFile("CHANGELOG.md"); 37 pack.addFile("LICENSE.md"); 38 pack.addFile("pluginpacks/default.txt"); 39 pack.addFile("launcher.exe"); 40 41 pack.addFile("tools/minecraft_import/minecraft_import.exe"); 42 pack.addFile("tools/minecraft_import/readme.md"); 43 } 44 45 enum Compiler 46 { 47 dmd, 48 ldc, 49 gdc 50 } 51 string[] compilerExeNames = ["dmd", "ldc2", "gdc"]; 52 53 string semver = "0.8.0"; 54 Compiler compiler = Compiler.ldc; 55 string buildType = "release-debug"; 56 57 void main(string[] args) 58 { 59 testDir = buildNormalizedPath(absolutePath(buildPath(ROOT_PATH, TEST_DIR_NAME))); 60 if (exists(testDir) && isDir(testDir)) 61 { 62 writefln("Deleting test dir %s", testDir); 63 rmdirRecurse(testDir); 64 } 65 66 StopWatch sw; 67 sw.start(); 68 doWork(); 69 sw.stop(); 70 writefln("Finished in %.1fs", sw.peek().to!("seconds", real)); 71 } 72 73 void doWork() 74 { 75 completeBuild(Arch.x32, semver, compiler, buildType); 76 writeln; 77 completeBuild(Arch.x64, semver, compiler, buildType); 78 } 79 80 void completeBuild(Arch arch, string semver, Compiler compiler, string buildType) 81 { 82 buildApp(arch, semver, compiler, buildType, "tools/minecraft_import"); 83 84 string launcher_arch; 85 if (compiler == Compiler.dmd && is_Windows) 86 launcher_arch = arch == Arch.x32 ? "x86_mscoff" : "x86_64"; 87 else 88 launcher_arch = arch == Arch.x32 ? "x86" : "x86_64"; 89 90 string voxelman_arch = arch == Arch.x32 ? "32" : "64"; 91 92 string dubCom() { 93 return format(`dub run --root="tools/launcher" -q --nodeps --compiler=ldc2 --arch=%s --build=debug -- --arch=%s --compiler=%s --build=%s`, 94 launcher_arch, voxelman_arch, compiler, buildType); 95 } 96 97 string com = dubCom(); 98 writefln("Executing '%s'", com); stdout.flush(); 99 100 auto dub = executeShell(com, null, Config.none, size_t.max, ROOT_PATH); 101 102 dub.output.write; 103 if (dub.status != 0) { 104 writeln("Failed to run dub or launcher"); 105 return; 106 } 107 108 writefln("Packing %sbit", voxelman_arch); stdout.flush(); 109 pack(semver, arch, Platform.windows); 110 } 111 112 void buildApp(Arch arch, string semver, Compiler compiler, string buildType, string root = "./") 113 { 114 string app_arch; 115 if (compiler == Compiler.dmd && is_Windows) 116 app_arch = arch == Arch.x32 ? "x86_mscoff" : "x86_64"; 117 else 118 app_arch = arch == Arch.x32 ? "x86" : "x86_64"; 119 120 string dubCom() { 121 return format(`dub build --root="%s" -q --nodeps --compiler=%s --arch=%s --build=%s`, 122 root, compilerExeNames[compiler], app_arch, buildType); 123 } 124 125 string com = dubCom(); 126 writefln("Executing '%s'", com); stdout.flush(); 127 128 auto dub = executeShell(com, null, Config.none, size_t.max, ROOT_PATH); 129 130 dub.output.write; 131 if (dub.status != 0) { 132 writeln("Failed to run dub"); 133 } 134 } 135 136 struct ReleasePackage 137 { 138 string semver; 139 Arch arch; 140 Platform platform; 141 ZipArchive zip; 142 string fileRoot; 143 string archRoot; 144 } 145 146 void pack(string semver, Arch arch, Platform pl) 147 { 148 ReleasePackage pack = ReleasePackage( 149 semver, 150 arch, 151 pl, 152 new ZipArchive, 153 ROOT_PATH); 154 string archName = archName(&pack, "voxelman"); 155 pack.archRoot = archName; 156 157 makePackage(&pack); 158 string archiveName = buildNormalizedPath(ROOT_PATH, archName) ~ ".zip"; 159 writePackage(&pack, archiveName); 160 161 extractArchive(archiveName, testDir); 162 } 163 164 enum Arch { x64, x32 } 165 enum Platform { windows, linux, macos } 166 string[Platform] platformToString; 167 string[Arch] archToString; 168 static this() 169 { 170 platformToString = [Platform.windows : "win", Platform.linux : "linux", Platform.macos : "mac"]; 171 archToString = [Arch.x64 : "64", Arch.x32 : "32"]; 172 } 173 174 void writePackage(ReleasePackage* pack, string path) 175 { 176 writefln("Writing archive into %s", path); 177 std.file.write(path, pack.zip.build()); 178 } 179 180 string archName(R)(ReleasePackage* pack, R baseName) 181 { 182 string arch = archToString[pack.arch]; 183 string platform = platformToString[pack.platform]; 184 return format("%s-v%s-%s%s", baseName, pack.semver, platform, arch); 185 } 186 187 alias normPath = buildNormalizedPath; 188 alias absPath = absolutePath; 189 void addFiles(ReleasePackage* pack, string path, string pattern) 190 { 191 import std.file : dirEntries, SpanMode; 192 193 string absRoot = pack.fileRoot.absPath.normPath; 194 foreach (entry; dirEntries(buildPath(pack.fileRoot, path), pattern, SpanMode.depth)) 195 if (entry.isFile) { 196 string absPath = entry.name.absPath.normPath; 197 addFile(pack, absPath, relativePath(absPath, absRoot)); 198 } 199 } 200 201 void addFile(ReleasePackage* pack, string archive_name) 202 { 203 addFile(pack.zip, buildPath(pack.fileRoot, archive_name).absPath.normPath, buildPath(pack.archRoot, archive_name)); 204 } 205 206 void addFile(ReleasePackage* pack, string fs_name, string archive_name) 207 { 208 addFile(pack.zip, fs_name.absPath.normPath, buildPath(pack.archRoot, archive_name)); 209 } 210 211 void addFile(ZipArchive archive, string fs_name, string archive_name) 212 { 213 string norm = archive_name.normPath; 214 if (!exists(fs_name)) { 215 writefln("Cannot find %s at %s", fs_name, norm); 216 return; 217 } 218 writefln("Add %s as %s", fs_name, norm); 219 void[] data = std.file.read(fs_name); 220 ArchiveMember am = new ArchiveMember(); 221 am.name = norm; 222 am.compressionMethod = CompressionMethod.deflate; 223 am.expandedData(cast(ubyte[])data); 224 archive.addMember(am); 225 } 226 227 void extractArchive(string archive, string pathTo) 228 { 229 writefln("Extracting %s into %s", archive, pathTo); 230 extractArchive(new ZipArchive(std.file.read(archive)), pathTo); 231 } 232 233 void extractArchive(ZipArchive archive, string pathTo) 234 { 235 foreach (ArchiveMember am; archive.directory) 236 { 237 string targetPath = buildPath(pathTo, am.name); 238 mkdirRecurse(dirName(targetPath)); 239 archive.expand(am); 240 std.file.write(targetPath, am.expandedData); 241 } 242 }