diff --git a/src/build.py b/src/build.py new file mode 100644 index 0000000..8fcb72c --- /dev/null +++ b/src/build.py @@ -0,0 +1,95 @@ +from util import * + +import shutil +import os, sys +import subprocess +import configparser + +from rich.console import Console + + +console = Console() + + +def find_c_files(path: str): + paths = [] + + for entry in os.listdir(path): + full_path = os.path.join(path, entry) + if os.path.isdir(full_path): + list_files_recursive(full_path) + else: + if full_path.endswith(".c"): + paths.append(full_path) + + return paths + +def build_mineral(args): + # sanity checks + if not os.path.isdir(args.folder_path): + console.print("[b red]digpkg: failed to build mineral: specified folder doesn't exist![/]") + sys.exit(1) + if not shutil.which("gcc"): + console.print("[b red]digpkg: failed to build mineral: gcc was not found![/]") + sys.exit(1) + + # use gcc to compile the mineral + with console.status("Compiling", spinner="bouncingBall", spinner_style="green") as status: + c_files = find_c_files(args.folder_path) + + if subprocess.run([ + "gcc", + "-shared", + "-fPIC", + *c_files, + *[f"-{arg}" for arg in (args.gcc_args or [])], + "-o", + "main.so" + ], stdout=None).returncode: + console.print("[b red]digpkg: failed to build mineral: gcc exited with non-zero exit code.[/]") + sys.exit(1) + + console.print("[:white_check_mark:] Compile success!") + + +def build(args): + if args.package: + # build the mineral + build_mineral(args) + + # create the build dir and throw in the .so file + with console.status("Packaging...", spinner="bouncingBall", spinner_style="green"): + build_dir = f"{os.path.basename(args.folder_path)}_build" + + if os.path.isdir(build_dir): + shutil.rmtree(build_dir) + os.mkdir(build_dir) + + shutil.move("main.so", os.path.join(build_dir, "main.so")) + + # generate a mineral.ini file + config_parser = configparser.ConfigParser() + config_parser["package"] = { + "description": "Your description here", + "version": "1.0.0", + "config_version": "1" + } + config_parser["dependencies"] = {} + + # write it to our new mineral + with open(os.path.join(build_dir, "mineral.ini"), "w") as f: + config_parser.write(f) + + console.print("[:white_check_mark:] Packaged!") + console.print("\n[b cyan]note:[/] You will need to edit the [i]mineral.ini[/] file to make sure the version number and dependencies are correct.") + else: + check_sudo() + check_ground_libs_path() + + # build the mineral and move it straight to the ground libs folder + build_mineral(args) + with console.status("Installing...", spinner="bouncingBall", spinner_style="green"): + + shutil.move("main.so", os.path.join(os.getenv("GROUND_LIBS"), f"{os.path.basename(args.folder_path)}.so")) + + console.print("[:white_check_mark:] Installed!") \ No newline at end of file diff --git a/src/main.py b/src/main.py index 6e2a4ad..881041c 100644 --- a/src/main.py +++ b/src/main.py @@ -6,6 +6,7 @@ from publish import publish from remove import remove from list import list_cmd from uninstall import uninstall +from build import build def parse_arguments(): @@ -34,8 +35,14 @@ def parse_arguments(): remove_command = sub_parsers.add_parser(name="remove", description="remove a published package from the repository") remove_command.add_argument("name", help="name and version of the package") - # env command + # build command + build_command = sub_parsers.add_parser(name="build", description="build a folder as a mineral and either install it or prepare it for publishing") + build_command.add_argument("folder_path", help="path to the folder to build") + build_command.add_argument("--gcc-args", nargs="*", help="any extra args you want to give to gcc") + build_command.add_argument("--package", action="store_true", help="generate a folder with a mineral.ini and all the other files you need to publish the package") + + # parse arguments are run the command we chose args = arg_parser.parse_args() if not args.command: @@ -52,6 +59,8 @@ def parse_arguments(): list_cmd(args) elif args.command == "uninstall": uninstall(args) + elif args.command == "build": + build(args) def main(): parse_arguments() diff --git a/src/publish.py b/src/publish.py index cd24ba3..ca594f8 100644 --- a/src/publish.py +++ b/src/publish.py @@ -53,7 +53,7 @@ def publish(args): sys.exit(1) # compress to a tar file - console.status("Compressing") + console.status("Compressing", spinner_style="green") f = tempfile.TemporaryFile(mode="wb+") with tarfile.open(fileobj=f, mode="w:gz") as tar_file: tar_file.add(args.folder_path, arcname=os.path.basename(args.folder_path)) @@ -63,7 +63,7 @@ def publish(args): console.print("[d][:white_check_mark:] Compressed![/]") # send the request - status.update("Uploading...") + status.update("Uploading...", spinner_style="blue") response = requests.put( url=f"https://chookspace.com/api/packages/ground/generic/{mineral_name}/{version}/mineral.tar", data=f, diff --git a/src/uninstall.py b/src/uninstall.py index 076c87a..f751ab5 100644 --- a/src/uninstall.py +++ b/src/uninstall.py @@ -12,7 +12,7 @@ def uninstall(args): check_sudo() check_ground_libs_path() - with console.status(status=f"Looking for [i]{args.name}[/]...", spinner="bouncingBall", spinner_style="blue") as status: + with console.status(status=f"Looking for [i]{args.name}[/]...", spinner="bouncingBall", spinner_style="green") as status: mineral_path = os.path.join(os.getenv("GROUND_LIBS"), args.name) symlink_path = os.path.join(os.getenv("GROUND_LIBS"), f"{args.name}.so") diff --git a/src/util.py b/src/util.py index e78289d..32a3480 100644 --- a/src/util.py +++ b/src/util.py @@ -6,7 +6,7 @@ from rich import print def check_ground_libs_path(): # ensure the GROUND_LIBS var is set if not os.getenv("GROUND_LIBS"): - print("digpkg: the [i]GROUND_LIBS[/] environment variable is not set, defaulting to /usr/lib/ground/") + print("[d]digpkg: the [i]GROUND_LIBS[/] environment variable is not set, defaulting to /usr/lib/ground/") os.environ["GROUND_LIBS"] = "/usr/lib/ground/" def check_sudo():