Repository navigation
Expand file tree
/
Copy path_build.js
More file actions
208 lines (187 loc) · 8.68 KB
/
Copy path_build.js
File metadata and controls
208 lines (187 loc) · 8.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
#!/usr/bin/env node
const packageinfo = require('./package.json');
const fs = require("fs-extra");
const path = require("path");
const cspawn = require("cross-spawn");
const minimist = require("minimist");
const node7z = require("node-7z");
const { exit } = require("process");
//must be before importing pkg/pkgFetch for rcedit cache replacement to work
process.env.PKG_CACHE_PATH = path.resolve('.pkg-cache');
const pkg = require("@yao-pkg/pkg");
const pkgFetch = require("@yao-pkg/pkg-fetch");
const rcedit = require("rcedit");
const PYTHON_EXE_VERSION_CONFIG_TEMPLATE_FILE = "python_exe_version_config_template.py";
const MAIN_ICON_FILE_NAME = "SHIELD_Logo.ico";
const MAIN_AUTHOR_STRING = "TheRealPSV @ GitHub/Tony Stark @ MarvelMods";
const main = async () => {
const args = minimist(process.argv.slice(2));
//generate .env file
if (args.e) {
fs.writeFileSync(".env", `PACKAGE_VERSION=${packageinfo.version}`);
exit(0);
}
//clean
if (args.c) {
fs.removeSync(path.resolve("build"));
fs.removeSync(path.resolve("dist"));
exit(0);
}
//fullclean
if (args.f) {
fs.removeSync(path.resolve("build"));
fs.removeSync(path.resolve("dist"));
fs.removeSync(path.resolve("temp"));
fs.removeSync(path.resolve(".pkg-cache"));
fs.removeSync(path.resolve("python_source", "__pycache__"));
fs.readdirSync("./").filter((item) => item.toLowerCase().endsWith(".exe.spec")).forEach(item => fs.removeSync(path.resolve(item)));
exit(0);
}
//package existing build folder
if (args.p) {
const compatString = (args.p === true) ? '' : `-${args.p}`;
fs.mkdirSync(path.resolve("temp"));
const packageFolder = `OpenHeroSelect${compatString}-v${packageinfo.version}`;
fs.copySync(path.resolve("build"), path.resolve("temp", packageFolder), { recursive: true });
fs.mkdirSync("dist");
fs.copyFileSync(path.resolve("LICENSE"), path.resolve("temp", packageFolder, "LICENSE.txt"));
fs.writeFile(path.resolve("temp", packageFolder, "Source Code.txt"), "Source code available at https://github.com/TheRealPSV/OpenHeroSelect");
const zipStream = node7z.add(path.resolve("dist", `OpenHeroSelect${compatString}.7z`), path.resolve("temp", packageFolder),
{
recursive: true
});
await streamToPromise(zipStream);
fs.removeSync(path.resolve("temp"));
exit(0);
}
if (args.t) {
console.log("building targets: " + args.t);
} else {
console.log("building all targets");
}
//ohs
if (!args.t || args.t.includes("ohs")) {
console.log("building ohs");
const description = "The main OpenHeroSelect program.";
if (args.a.slice(-2) == '86') {
console.log("attempting 32-bit compatibility mode");
}
await runPkg("index.js", MAIN_ICON_FILE_NAME, description, MAIN_AUTHOR_STRING, "OpenHeroSelect.exe", true, args.a);
}
//json2xmlb
if (!args.t || args.t.includes("json2xmlb")) {
console.log("building json2xmlb");
const description = "Converts between json files and xmlb/engb files.";
await runPyInstaller("xmlb (raven-formats by nikita488).py", MAIN_ICON_FILE_NAME, description, "nikita488 @ MarvelMods", "json2xmlb.exe", args.a.slice(0, 5) === 'linux');
}
//copyfiles
if (!args.t || args.t.includes("copyfiles")) {
console.log("building copyfiles");
//mua resources
fs.ensureDirSync(path.resolve("mua", "xml"));
fs.ensureDirSync(path.resolve("build", "mua"));
fs.copySync(path.resolve("mua", "xml"), path.resolve("build", "mua", "xml"), { recursive: true });
fs.copySync(path.resolve("mua", "rosters"), path.resolve("build", "mua", "rosters"), { recursive: true });
fs.copySync(path.resolve("mua", "menulocations"), path.resolve("build", "mua", "menulocations"), { recursive: true });
//xml2 resources
fs.ensureDirSync(path.resolve("xml2", "xml"));
fs.ensureDirSync(path.resolve("build", "xml2"));
fs.copySync(path.resolve("xml2", "xml"), path.resolve("build", "xml2", "xml"), { recursive: true });
fs.copySync(path.resolve("xml2", "rosters"), path.resolve("build", "xml2", "rosters"), { recursive: true });
//documentation
fs.copySync(path.resolve("help_files"), path.resolve("build", "help_files"), { recursive: true });
}
};
function streamToPromise(stream) {
return new Promise(function (resolve, reject) {
stream.on('end', resolve);
stream.on('error', reject);
});
}
async function runPkg(SourceJSFileName, iconFileName, fileDescription, author, exeOutputFileName, requireAdmin, platform) {
const pkgTarget = `latest-${platform}`;
const cacheExe = await downloadCache(pkgTarget);
if (platform.slice(0, 5) === 'linux') {
exeOutputFileName = exeOutputFileName.slice(0, -4);
} else {
await editNodeJSExeData(cacheExe, iconFileName, fileDescription, author, requireAdmin);
}
const commands = [path.resolve("js_source", SourceJSFileName), "--public",
"--targets", pkgTarget, "--compress", "Brotli",
"--output", path.resolve("build", exeOutputFileName)];
await pkg.exec(commands);
}
async function runPyInstaller(sourcePyFileName, iconFileName, fileDescription, author, exeOutputFileName, linux) {
fs.mkdirSync("temp", { recursive: true });
if (linux) {
exeOutputFileName = path.parse(path.resolve(exeOutputFileName)).name;
cspawn.sync("pyinstaller", ["--noconfirm", "--onefile",
"--distpath", pythonPathResolve("build"), "--specpath", "temp", "--workpath", "temp",
"-n", pythonPathResolve(exeOutputFileName),
pythonPathResolve("python_source", sourcePyFileName)], { stdio: 'inherit' });
} else {
const versionData = await buildPythonExeVersionData(fileDescription, author);
fs.writeFileSync(path.resolve("temp", "versiondata.py"), versionData);
cspawn.sync("pyinstaller", ["--noconfirm", "--onefile", "--console",
"--distpath", pythonPathResolve("build"), "--specpath", "temp", "--workpath", "temp",
"--version-file", pythonPathResolve("temp", "versiondata.py"),
"--icon", pythonPathResolve(iconFileName),
"-n", pythonPathResolve(exeOutputFileName),
pythonPathResolve("python_source", sourcePyFileName)], { stdio: 'inherit' });
}
fs.removeSync(path.resolve("temp"));
console.log(path.resolve(`${exeOutputFileName}.spec`));
fs.removeSync(path.resolve(`${exeOutputFileName}.spec`));
}
//required for compiling the python files, pyinstaller needs slashes to be escaped
function pythonPathResolve(...strings) {
return path.resolve(...strings).replaceAll("\\", "\\\\");
}
//needed to work around an issue with rcedit not being able to modify exported nodejs exe
async function downloadCache(pkgTarget) {
const [nodeRange, platform, arch] = pkgTarget.split('-');
const pathToFile = await pkgFetch.need({ nodeRange, platform, arch });
if (!pathToFile) {
throw new Error('Error downloading PKG cache');
}
let newPath = pathToFile;
const filename = path.basename(pathToFile);
if (filename.startsWith("fetched")) {
const newname = filename.replace("fetched", "built");
newPath = path.resolve(pathToFile.substring(0, pathToFile.length - filename.length) + newname);
fs.renameSync(pathToFile, newPath);
}
return newPath;
}
//only works for the nodejs files, not the python files
async function editNodeJSExeData(exePath, iconFileName, fileDescription, author, requireAdmin) {
const packageVersion = JSON.parse(fs.readFileSync(path.resolve("package.json"))).version;
await rcedit(path.resolve(exePath), {
'product-version': packageVersion,
'file-version': packageVersion,
icon: path.resolve(iconFileName),
'version-string': {
FileDescription: fileDescription,
ProductName: 'OpenHeroSelect',
CompanyName: author,
LegalCopyright: "",
OriginalFilename: ""
},
'requested-execution-level': requireAdmin ? 'requireAdministrator' : 'asInvoker'
});
}
//loads up and generates a version file string to be written to file for python exes since rcedit breaks python exes
async function buildPythonExeVersionData(fileDescription, author) {
let template = fs.readFileSync(PYTHON_EXE_VERSION_CONFIG_TEMPLATE_FILE, { encoding: 'utf8' });
const packageVersion = JSON.parse(fs.readFileSync(path.resolve("package.json"))).version.split(".");
template = template.replaceAll("~version-digit-1~", packageVersion[0]);
template = template.replaceAll("~version-digit-2~", packageVersion[1]);
template = template.replaceAll("~version-digit-3~", packageVersion[2]);
template = template.replaceAll("~product-name~", "OpenHeroSelect");
template = template.replaceAll("~product-name~", "OpenHeroSelect");
template = template.replaceAll("~description~", fileDescription.replaceAll("'", "\\'"));
template = template.replaceAll("~author~", author.replaceAll("'", "\\'"));
return template;
}
//actual function call, allows for usage of async/await
main();