-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathbuild.py
More file actions
300 lines (235 loc) · 9.9 KB
/
Copy pathbuild.py
File metadata and controls
300 lines (235 loc) · 9.9 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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
import argparse
import json
import os
import shutil
import subprocess
import tempfile
from pathlib import Path
from zipfile import ZipFile
def dotnet_clean(solution_file, configuration):
result = subprocess.run(["dotnet", "clean", solution_file, "-c", configuration])
return result.returncode == 0
def dotnet_publish_solution(solution_file, configuration, framework):
result = subprocess.run(["dotnet", "publish", solution_file, "-c", configuration, "-f", framework])
return result.returncode == 0
def create_localitem_json(output_dir):
local_item_contents = {
"hash": mod_name
}
local_item_json = json.dumps(local_item_contents, indent=2)
local_item_path = Path(output_dir, "LocalItem.json")
with open(local_item_path, "w") as local_item_file:
local_item_file.write(local_item_json)
return local_item_path
def create_synthmod(mod_name, input_dir, input_subpaths, output_dir, output_name, subfolder = "Mods"):
mod_name = str(mod_name)
tmpdir = tempfile.mkdtemp()
print(f"tmp directory is {tmpdir}")
print("Setting up output dir")
tmp_outputdir = Path(tmpdir, mod_name)
tmp_outputdir.mkdir(parents=True, exist_ok=True)
tmp_output_path = Path(tmp_outputdir, output_name)
print("Creating LocalItem.json")
localitem_path = create_localitem_json(tmp_outputdir)
print(f"Creating zipped synthmod file {tmp_output_path}")
with ZipFile(tmp_output_path, "w") as zip_file:
print(f"Adding {localitem_path} to LocalItem.json")
zip_file.write(localitem_path, "LocalItem.json")
add_files_to_zip(zip_file, input_dir, input_subpaths, subfolder)
if not tmp_output_path.exists():
print(f"Failed to create zip file {tmp_output_path}")
return False
final_path = Path(output_dir, output_name)
final_path.parent.mkdir(parents=True, exist_ok=True)
print(f"Copying tmp synthmod file to {final_path}")
shutil.copyfile(tmp_output_path, final_path)
print(f"synthmod file created at {final_path}")
return True
def copy_with_dir_create(input_dir, input_file, output_dir):
# Optionally, the input_file from the passed in mod/plugin file list may be comma-delimited,
# with the first entry being the source name, and the second entry being the path in the zip/synthmod.
if ',' in input_file:
parts = input_file.split(',')
input_file = parts[0]
output_path = Path(output_dir, parts[1])
else:
output_path = Path(output_dir, "Mods", input_file)
full_path = Path(input_dir, input_file)
output_path.parent.mkdir(parents=True, exist_ok=True)
print(f"Copying {full_path} to {output_path}")
shutil.copyfile(full_path, output_path)
def copy_raw(mod_name, input_dir, input_subpaths, output_dir):
mod_name = str(mod_name)
for subpath in input_subpaths:
copy_with_dir_create(input_dir, subpath, output_dir)
# Include dependencies as well (single level)
dep_dir = Path(input_dir, "libs")
if dep_dir.exists():
for dep_file in dep_dir.iterdir():
copy_with_dir_create(input_dir, dep_file, output_dir)
return True
def add_files_to_zip(zip_file, input_dir, input_subpaths, zip_dir = "Mods"):
for input_file in input_subpaths:
add_file_to_zip(zip_file, input_dir, input_file, zip_dir)
def add_file_to_zip(zip_file, input_dir, input_file, zip_dir = "Mods"):
# Optionally, the input_file from the passed in mod/plugin file list may be comma-delimited,
# with the first entry being the source name, and the second entry being the path in the zip/synthmod.
zip_path = Path(zip_dir, input_file)
if ',' in input_file:
parts = input_file.split(',')
input_file = parts[0]
zip_path = parts[1]
print(input_dir, input_file, zip_dir)
real_path = Path(input_dir, input_file)
print(f"Adding {real_path} to {zip_path}")
zip_file.write(real_path, zip_path)
def create_zip(mod_name, input_dir, input_subpaths, output_dir, output_name, subfolder = "Mods"):
mod_name = str(mod_name)
tmpdir = tempfile.mkdtemp()
print(f"tmp directory is {tmpdir}")
print("Setting up output dir")
# root_output_dir = Path("..", "Downloads")
tmp_outputdir = Path(tmpdir, mod_name)
tmp_outputdir.mkdir(parents=True, exist_ok=True)
tmp_output_path = Path(tmp_outputdir, output_name)
print(f"Creating zip file {tmp_output_path}")
with ZipFile(tmp_output_path, "w") as zip_file:
add_files_to_zip(zip_file, input_dir, input_subpaths, subfolder)
# Include dependencies as well (single level)
dep_dir = Path(input_dir, "libs")
if dep_dir.exists():
for dep_file in dep_dir.iterdir():
add_file_to_zip(zip_file, dep_dir, dep_file.name, subfolder)
if not tmp_output_path.exists():
print(f"Failed to create zip file {tmp_output_path}")
return False
final_path = Path(output_dir, output_name)
final_path.parent.mkdir(parents=True, exist_ok=True)
print(f"Copying tmp zip file to {final_path}")
shutil.copyfile(tmp_output_path, final_path)
print(f"zip file created at {final_path}")
return True
def add_git_tag(mod_name, version):
print(f"Adding git tag v{version}")
result = subprocess.run([
"git", "tag",
"-a",
"-m", f"{mod_name} v{version}",
f"v{version}"
])
print("Remember to push up with `git push --tags`")
return result.returncode == 0
def clean_output(output_dir):
if not Path.exists(output_dir):
return True
print(f"Cleaning directory {output_dir}")
shutil.rmtree(output_dir)
return True
if __name__ == "__main__":
parser = argparse.ArgumentParser(
prog='build.py',
description='Helper functions for building dev and release files (synthmod, zip)')
parser.add_argument(
"version",
help="Mod version being built. Ex. 1.3.0"
)
parser.add_argument(
"input_file",
type=Path,
help="File containing list of file names to include in copy/zip/synthmod, one per line, relative from the input directory"
)
parser.add_argument(
"--dotnet-version",
default="net6.0",
help="Dotnet version to use. Expecting 'net6.0' or 'net8.0'"
)
parser.add_argument(
"-n",
"--name",
help="Mod name. Defaults to current directory name."
)
parser.add_argument(
"-c",
"--configuration",
default="Release",
help="Build configuration, Debug or Release. Defaults to Release"
)
parser.add_argument(
"-o",
"--output-dir",
type=Path,
help="Output directory. Defaults to build/<ModName>_<Version>"
)
parser.add_argument(
"-i",
"--input-dir",
type=Path,
help="Input directory (build directory). Defaults to <ModName>/bin/<Configuration>/<dotnet_version>/publish"
)
parser.add_argument(
"--tag",
action="store_true",
help="If specified, tags the version and pushes tag to repo"
)
parser.add_argument(
"--clean",
action="store_true",
help="If specified, removes the output directory for this particular version before building"
)
parser.add_argument(
"--plugin",
action="store_true",
help="If specified, this is a plugin and outputs should go to the Plugins folder instead of the Mods folder"
)
parser.add_argument(
"-s",
"--solution-file",
type=Path,
help="Path to solution file. Defaults to the mod's name in the current directory, falling back on mod_name/mod_name.sln"
)
args = parser.parse_args()
configuration = args.configuration
version = str(args.version)
dotnet_version = args.dotnet_version
if not dotnet_version:
dotnet_version = "net6.0"
print(dotnet_version)
mod_name = args.name
if not mod_name:
mod_name = Path(".").resolve().stem
print(f"Mod name not specified; using '{mod_name}'")
input_dir = args.input_dir
if not input_dir:
input_dir = Path(".", mod_name, "bin", configuration, args.dotnet_version, "publish").resolve()
print(f"Input dir not specified; using '{input_dir}'")
output_dir = args.output_dir
if not output_dir:
output_dir = Path("build", f"{mod_name}_{version}")
print(f"Output dir not specified; using {output_dir}")
input_file_definitions = args.input_file
input_files = [line.rstrip() for line in open(input_file_definitions, "r")]
# Always build the whole solution to resolve dependencies correctly
# This can become a parameter if it ever differs.
# Try both the current directory and one directory lower (should be the common configurations)
solution_file = Path(str(args.solution_file))
if not solution_file.exists():
solution_file = Path(f"{mod_name}.sln")
if not solution_file.exists():
solution_file = Path(mod_name, f"{mod_name}.sln")
print("Using solution file", solution_file.resolve())
zip_output_file = f"{mod_name}_{version}.zip"
synthmod_output_file = f"{mod_name}_{version}.synthmod"
commands = [
lambda: clean_output(output_dir) if args.clean else True,
lambda: dotnet_clean(solution_file.resolve(), configuration),
lambda: dotnet_publish_solution(solution_file.resolve(), configuration, dotnet_version),
lambda: copy_raw(mod_name, input_dir, input_files, output_dir),
lambda: create_zip(mod_name, input_dir, input_files, output_dir, zip_output_file, subfolder="Plugins" if args.plugin else "Mods"),
lambda: create_synthmod(mod_name, input_dir, input_files, output_dir, synthmod_output_file, subfolder="Plugins" if args.plugin else "Mods"),
lambda: add_git_tag(mod_name, version) if args.tag else True,
]
for cmd in commands:
if not cmd():
print("Error, stopping")
exit(-1)
print()