Skip to content

Commit

Permalink
Merge pull request #3833 from Laudanum-dev/Laudanum-dev-compile_packa…
Browse files Browse the repository at this point in the history
…ges.py-add-argparse

added comand-line argument parsing to compile_packages.py
  • Loading branch information
TeamSpen210 authored Jun 26, 2021
2 parents 30fe416 + d14aa47 commit f0f225d
Showing 1 changed file with 152 additions and 48 deletions.
200 changes: 152 additions & 48 deletions compile_packages.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@
from zipfile import ZipFile, ZIP_LZMA, ZIP_DEFLATED

from srctools import Property, KeyValError, VMF, Entity, conv_bool
import argparse

OPTIMISE = False
# path to a folder, where all useful hammer files will be created, if it stated that they need to be created
HAMMER_PATH = None


def clean_vmf(vmf_path):
Expand All @@ -33,15 +36,17 @@ def clean_vmf(vmf_path):
print('Removing hammer_notes...')
inst.remove_ent(ent)
continue

# All instances must be in bee2/, so any reference outside there is a map error!
# It's ok if it's in p2editor and not in a subfolder though.
# There's also an exception needed for the Tag gun instance.
if ent['classname'] == 'func_instance':
inst_loc = ent['file'].casefold().replace('\\','/')
if not inst_loc.startswith('instances/bee2/') and not (inst_loc.startswith('instances/p2editor/') and inst_loc.count('/') == 2) and 'alatag' not in inst_loc:
inst_loc = ent['file'].casefold().replace('\\', '/')
if not inst_loc.startswith('instances/bee2/') and not (
inst_loc.startswith('instances/p2editor/') and inst_loc.count(
'/') == 2) and 'alatag' not in inst_loc:
input('Invalid instance path "{}" in\n"{}"! Press Enter to continue..'.format(ent['file'], vmf_path))
yield from clean_vmf(vmf_path) # Re-run so we check again..
yield from clean_vmf(vmf_path) # Re-run so we check again..

for solid in ent.solids[:]:
if all(face.mat.casefold() == 'tools/toolsskip' for face in solid):
Expand Down Expand Up @@ -79,29 +84,33 @@ def clean_vmf(vmf_path):
DELETE_EXTENSIONS = ['vmx', 'log', 'bsp', 'prt', 'lin']


def search_folder(zip_path, path):
"""Search the given folder for packages.
def search_list_of_dirs(list_of_dirs, zip_path):
"""Search the given list of folders for packages
zip_path is the folder the zips will be saved in,
and path is the location to search.
and list_of_dirs is the list of locations to search.
returns pairs (original_file_path, result_zip_path)
"""
for package in os.listdir(path):
package_path = os.path.join(path, package)
if not os.path.isdir(package_path):
for dir_path in list_of_dirs:
if not os.path.isdir(dir_path): # it's a file
continue
if 'info.txt' not in os.listdir(package_path):
yield from search_folder(zip_path, package_path)
if 'info.txt' not in os.listdir(dir_path): # it's a folder, probably with packages
# go search its contents
yield from search_list_of_dirs([os.path.join(dir_path, i) for i in os.listdir(dir_path)], zip_path)
continue

print('| ' + package + '.bee_pack')
pack_zip_path = os.path.join(zip_path, package) + '.bee_pack'
# do not preserve original file structure, dump all found packs in root of zip_path
pack_name = os.path.split(dir_path)[-1]
pack_zip_path = os.path.join(zip_path, pack_name) + ".bee_pack"
print('| ' + pack_name + '.bee_pack')

yield dir_path, pack_zip_path

yield package_path, pack_zip_path, zip_path


def build_package(package_path, pack_zip_path, zip_path):
def build_package(package_path, pack_zip_path):
"""Build the packages in a given folder."""

global HAMMER_PATH

zip_file = ZipFile(
pack_zip_path,
'w',
Expand Down Expand Up @@ -135,8 +144,16 @@ def build_package(package_path, pack_zip_path, zip_path):
# Skip music files..
hammer_path = None
else:
hammer_path = os.path.join('zips/hammer/', hammer_path)
os.makedirs(os.path.dirname(hammer_path), exist_ok=True)
# if this files can be added to HAMMER_PATH folder,
# check if this folder was specified (because if its not,
# then you should not create hammer files)
if HAMMER_PATH is not None:
hammer_path = os.path.join(HAMMER_PATH, hammer_path)
os.makedirs(os.path.dirname(hammer_path), exist_ok=True)
else:
hammer_path = None
# yea, if at this point hammer_path is None, then file we currently looking at do not
# need to be put into a HAMMER_PATH folder (including every case)

print('.', end='', flush=True)

Expand All @@ -159,41 +176,128 @@ def build_package(package_path, pack_zip_path, zip_path):


def main():
global OPTIMISE

OPTIMISE = conv_bool(input('Optimise zips? '))

print('Optimising: ', OPTIMISE)

zip_path = os.path.join(
os.getcwd(),
'zips',
'sml' if OPTIMISE else 'lrg',
)
parser = argparse.ArgumentParser(description="This is package compiler, which can compress packages "
"in order for them to be lighter. "
"Also provides an option to optimize them.\n"
"IMPORTANT NOTE: your packages should NOT be zipped before "
"compilation, also they should all have different names, "
"since they all will be dumped in the same directory.")
# argument that accepts one or more filepaths that then will be recursively searched and
# compiled all found stuff
parser.add_argument("input", nargs="+",
help="Specifies an input path or several input paths. If an input path is a "
"single package, then it will be compiled, otherwise, if an input path is "
"a folder, then it will be recursively searched for packages, and will compile "
"all it finds")
# will give args.optimize True value is specified, False otherwise;
parser.add_argument("-op", "--optimize", action="store_const", const=True, default=False,
help="Will optimize zips (not recommended and may make packages "
"unloadable for bee in current version).", dest="optimize")
# will give args.skip_confirm True value is specified, False otherwise;
parser.add_argument("-c", "--confirm", action="store_const", const=True, default=False,
help="Will skip a confirmation prompt.", dest="skip_confirm")
# will specify an output path. if not specified, args.output will be set to None, and nothing will be moved then
parser.add_argument("-o", "--output", default="zips",
help="Will specify an output folder, otherwise \"./zips\" will be used.", dest="output")
# during packing files, it can create several useful hammer files, which will be placed in specified directory,
# or won't be created if not specified
parser.add_argument("-hp", "--hammer_path", default=None,
help="Will create some useful files, which you can use in hammer later. "
"All files will be stored in specified directory.", dest="hammer")
# if NOT specified, then args.zip will be set to None, if specified BUT NOT set to any string value, then
# args.zip will be set to "" (empty string). Later, if args.zip remains set to "", then no zip will be created
parser.add_argument("--zip", nargs="?", default=None, const="",
help="Will put all generated files in one zip. Also skips the prompt at the end. "
"Using this option with a string, followed after it, will create a zip with a "
"specified name. Using this option without a string will just skip prompt "
"without creating zip. Not using this option will generate a prompt at the end",
dest="zip")
# if there is something left inside parse_args() brackets, then probably i forgot to remove after debugging
args = parser.parse_args()
inp_list = args.input
output = args.output
global OPTIMISE, HAMMER_PATH
OPTIMISE = args.optimize
do_zip = args.zip
HAMMER_PATH = args.hammer
if not args.skip_confirm:
# shows list with all files specified (in case using CLI is not so intuitive)
print("You specified these folders:\n-------------------------------")
for inp in inp_list:
print("|", inp)
print("-------------------------------")
print("Selected options:")
print("* Optimization: " + ("ON" if OPTIMISE else "OFF"))
print("* Output folder: \"" + output + "\"")
print("* Hammer folder: " + ("OFF" if (HAMMER_PATH is None) else ("\"" + HAMMER_PATH + "\"")))
print("* Final zip: " + ("skip prompt" if do_zip == "" else
("not skip prompt" if do_zip is None else
do_zip)))
print()
if not conv_bool(input("---> Continue? (y/n) ")):
sys.exit(0) # confirmation just in case

zip_path = output

# if there is no such dir, then create it. if there is, and its empty,
# then use it. if there is and its not empty - throw error (since whole directory will be erased)
if os.path.isdir(zip_path):
for file in os.listdir(zip_path):
print('Deleting', file)
os.remove(os.path.join(zip_path, file))
if os.listdir(zip_path):
raise ValueError("The output directory is not empty")
else:
os.makedirs(zip_path, exist_ok=True)

shutil.rmtree('zips/hammer/', ignore_errors=True)

path = os.path.join(os.getcwd(), 'packages\\', )

# A list of all the package zips.
for package in search_folder(zip_path, path):
# if hammer path was specified then
if HAMMER_PATH:
# if there is no such dir, then create it. if there is, and its empty,
# then use it. if there is and its not empty - throw error (why not)
if os.path.isdir(HAMMER_PATH):
if os.listdir(HAMMER_PATH):
raise ValueError("The hammer files directory is not empty")
else:
os.makedirs(HAMMER_PATH, exist_ok=True)

packages_list = [] # list with filenames of all packages
# yield will return found packages one by one, so it is easy to iterate them
# without the need to process everything at once
for package in search_list_of_dirs(inp_list, zip_path):
# package is a tuple (raw_package_directory, path_to_compiled_version)
packages_list.append(os.path.relpath(package[1], zip_path))
build_package(*package)
print('Done!')

print('Building main zip...')
# confusing section, determining whether it need to create main zip and how to name it if not specified
# makes it blank by default
pack_name = ""
if do_zip is None:
if conv_bool(input("Zip it all in one file? (y/n) ")):
pack_name = 'BEE{}_packages.zip'.format(input('Version: '))
else:
pack_name = do_zip # do_zip also can be blank
# if its not blank, and not ending with .zip, then add .zip to filename
if pack_name and pack_name[-4:] != ".zip":
pack_name += ".zip"
# if pack_name remained blank to this stage, then no zip will be created
if pack_name != "":
print('Building main zip...')
with ZipFile(os.path.join(zip_path, pack_name), 'w', compression=ZIP_DEFLATED) as zip_file:
for file in packages_list:
# previously it put in zip all contains of a folder, including itself (no clue why it
# haven't raised any error), also, since hammer files do not need to appear in zip,
# packages_list variable was created
zip_file.write(os.path.join(zip_path, file), os.path.join('packages/', file))
print('.', end='', flush=True)
print('Done!')
print("Deleting unzipped files...")
# removes all files that are in packages_list var
for file in packages_list:
os.remove(os.path.join(zip_path, file))
print("Done!")
else:
# dont make zip
# we dont actually need to do anything else
pass

pack_name = 'BEE{}_packages.zip'.format(input('Version: '))

with ZipFile(os.path.join('zips', pack_name), 'w', compression=ZIP_DEFLATED) as zip_file:
for file in os.listdir(zip_path):
zip_file.write(os.path.join(zip_path, file), os.path.join('packages/', file))
print('.', end='', flush=True)
print('Done!')

if __name__ == '__main__':
main()

0 comments on commit f0f225d

Please sign in to comment.