diff --git a/.gitignore b/.gitignore index ed604af..7726dfc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ # disable distribution build folder redist/ +!redist/.gitkeep diff --git a/bbp_ng/tools/build_icons.py b/bbp_ng/tools/build_icons.py index 37e7751..7d27452 100644 --- a/bbp_ng/tools/build_icons.py +++ b/bbp_ng/tools/build_icons.py @@ -15,7 +15,7 @@ def resize_image(src_file: str, dst_file: str) -> None: def create_thumbnails() -> None: # get folder path - root_folder: str = os.path.dirname(os.path.dirname(__file__)) + root_folder: str = common.get_plugin_folder() # prepare handler def folder_handler(src_folder: str, dst_folder: str) -> None: diff --git a/bbp_ng/tools/build_jsons.py b/bbp_ng/tools/build_jsons.py index 72e2af1..5a7b469 100644 --- a/bbp_ng/tools/build_jsons.py +++ b/bbp_ng/tools/build_jsons.py @@ -14,7 +14,7 @@ def compress_json(src_file: str, dst_file: str) -> None: def create_compressed_jsons() -> None: # get folder path - root_folder: str = os.path.dirname(os.path.dirname(__file__)) + root_folder: str = common.get_plugin_folder() # prepare handler def folder_handler(src_folder: str, dst_folder: str) -> None: diff --git a/bbp_ng/tools/common.py b/bbp_ng/tools/common.py index bb4da68..00b7490 100644 --- a/bbp_ng/tools/common.py +++ b/bbp_ng/tools/common.py @@ -1,4 +1,12 @@ -import os, typing +import os, typing, fnmatch, shutil + +def get_plugin_folder() -> str: + """ + Get the absolute path to plugin root folder. + + @return The absolute path to plugin root folder. + """ + return os.path.dirname(os.path.dirname(__file__)) def relative_to_folder(abs_path: str, src_parent: str, dst_parent: str) -> str: """ @@ -55,3 +63,84 @@ def common_file_migrator( dst_file: str = relative_to_folder(src_file, from_folder, to_folder) # call handler fct_proc_file(src_file, dst_file) + +def conditional_file_copy( + from_folder: str, to_folder: str, + only_copy: tuple[str, ...] | None = None, + ignore_copy: tuple[str, ...] | None = None, + recursively: bool = False) -> None: + """ + The enhanced file tree copy function used in redist script. + + The name of file or folder will be checked by `only_copy` first, + it it decide this file or folder should be copied, we then check whether + it is in `ignore_copy`. + + @param from_folder[in] The folder need to be redist. + @param to_folder[in] The folder will be placed redist files. + @param only_copy[in] An Unix style pathname pattern tuple to instruct which files or folders should be copied, + or None if we want to copy every files and folders. + @param ignore_copy[in] An Unix style pathname pattern tuple to instruct which files or folders should not be copied, + or None if we want to copy every files and folders. + @param recursively[in] Whether recursively copy sub-folders and their files. + """ + # build a helper functions + def is_need_copy(checked_filename: str) -> bool: + # if only_copy enabled, check it. + # if no only_copy, pass the check because file should be copied in default. + if only_copy is not None: + for only_copy_item in only_copy: + # matched, should copy it, break this for syntax + if fnmatch.fnmatch(checked_filename, only_copy_item): + break + else: + # no matched item, this entry should not be copied. + return False + + if ignore_copy is not None: + # check whether given name is in ignore_copy + for ignore_copy_item in ignore_copy: + # matched, should not be copied + if fnmatch.fnmatch(checked_filename, only_copy_item): + return False + # no matched, copy it + return True + else: + # no ignore_copy, directly copy it + return True + + # iterate from_folder folder + for root, dirs, files in os.walk(from_folder, topdown = True): + # create self + src_self: str = root + dst_self: str = relative_to_folder(src_self, from_folder, to_folder) + print(f'Creating: {src_self} -> {dst_self}') + os.makedirs(dst_self, exist_ok=True) + + # iterate files + for name in files: + # get source file path + src_file: str = os.path.join(root, name) + # check whether copy it + if not is_need_copy(src_file): + continue + # build dst path and copy it + dst_file: str = relative_to_folder(src_file, from_folder, to_folder) + print(f'Copying: {src_file} -> {dst_file}') + shutil.copy(src_file, dst_file) + + # iterate folders when recursively flag enabled + # if recursively: + # for name in dirs: + # # get source folder path + # src_folder: str = os.path.join(root, name) + # # build dst path and create it + # dst_folder: str = relative_to_folder(src_folder, from_folder, to_folder) + # print(f'Copying: {src_folder} -> {dst_folder}') + # os.makedirs(dst_folder, exist_ok=True) + + # if we don't have recursively flag, + # we should exit at the end of first loop + if not recursively: + break + diff --git a/bbp_ng/tools/redist.py b/bbp_ng/tools/redist.py new file mode 100644 index 0000000..564dd17 --- /dev/null +++ b/bbp_ng/tools/redist.py @@ -0,0 +1,66 @@ +import os, argparse, shutil +import common + +def create_redist(redist_folder: str) -> None: + # get plugin root folder and redist folder + root_folder: str = common.get_plugin_folder() + + # we do not want to use script to recursively delete any folder + # because we are afraid of accident `rm -rf /*` disaster. + # but we still need a empty folder to copy file, + # so we check whether redist folder is existing and hope user manually clean it. + redist_folder = os.path.abspath(redist_folder) + if os.path.exists(redist_folder): + print(f'"{redist_folder}" is already existing. This may cause problem, please empty it first before running redist script.') + # make sure redist folder is existing. + os.makedirs(redist_folder, exist_ok=True) + + # copy core python files + common.conditional_file_copy( + root_folder, + redist_folder, + ('*.py', '*.toml', ), + None, + False + ) + + # copy jsons + common.conditional_file_copy( + os.path.join(root_folder, 'jsons'), + os.path.join(redist_folder, 'jsons'), + ('*.json', ), + None, + False + ) + # copy icons + common.conditional_file_copy( + os.path.join(root_folder, 'icons'), + os.path.join(redist_folder, 'icons'), + ('*.png', ), + None, + True + ) + # copy meshes + common.conditional_file_copy( + os.path.join(root_folder, 'meshes'), + os.path.join(redist_folder, 'meshes'), + ('*.bin', ), + None, + False + ) + # copy BMap library + common.conditional_file_copy( + os.path.join(root_folder, 'PyBMap'), + os.path.join(redist_folder, 'PyBMap'), + ('*.py', '*.dll', '*.so', '*.dylib', '*.bin', '*.pdb', ), + None, + False + ) + + print('Done.') + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='BBP NG Redist Script') + parser.add_argument('-o', '--output', required=True, action='store', dest='output', help='The path to redist folder.') + args = parser.parse_args() + create_redist(args.output)