Initial actual commit
This commit is contained in:
parent
e555b62c06
commit
22d338eac2
7 changed files with 84 additions and 43 deletions
155
builder/__init__.py
Executable file
155
builder/__init__.py
Executable file
|
@ -0,0 +1,155 @@
|
|||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import os
|
||||
import json
|
||||
import sys
|
||||
import shutil
|
||||
import tarfile
|
||||
from .lowbar import lowbar
|
||||
from urllib.request import urlretrieve
|
||||
|
||||
progress_bar = lowbar(100)
|
||||
|
||||
def fail(reason: str):
|
||||
print("\033[91m"+ "A critical error occoured:" + "\033[0m")
|
||||
print("=> " + reason)
|
||||
sys.exit(1)
|
||||
|
||||
def report_hook(block_count, block_size, file_size):
|
||||
global progress_bar
|
||||
if file_size == -1:
|
||||
return
|
||||
downloaded = block_count * block_size
|
||||
percentage = round(downloaded / file_size * 100)
|
||||
if percentage > 100:
|
||||
percentage = 100
|
||||
elif percentage < 0:
|
||||
percentage = 0
|
||||
downloaded = round(downloaded / 1000000, 1)
|
||||
size = round(file_size / 1000000, 1)
|
||||
if downloaded > size:
|
||||
downloaded = size
|
||||
progress_bar.update(percentage)
|
||||
progress_bar.desc = f"{downloaded}MB/{size}MB"
|
||||
|
||||
def build(path: str, output: str | None, should_compress: bool, compile_static: bool):
|
||||
global progress_bar
|
||||
file_path = os.path.realpath(os.path.join(os.getcwd(), path))
|
||||
|
||||
if not os.path.exists(file_path):
|
||||
fail(f"Path not existing: {file_path}")
|
||||
if not os.path.isdir(file_path):
|
||||
fail(f"Not a directory: {file_path}")
|
||||
if (not os.path.exists(os.path.join(file_path, "rodeo-builder.json"))) or (not os.path.isfile(os.path.join(file_path, "rodeo-builder.json"))):
|
||||
fail("Could not find the rodeo-builder.json file")
|
||||
|
||||
os.chdir(file_path)
|
||||
|
||||
with open(os.path.join(file_path, "rodeo-builder.json"), "rb") as f:
|
||||
information = json.load(f)
|
||||
|
||||
if not "name" in information:
|
||||
fail("The rodeo-builder.json file does not contain the 'name' field")
|
||||
if not "version" in information:
|
||||
fail("The rodeo-builder.json file does not contain the 'version' field")
|
||||
if not "build" in information:
|
||||
fail("The rodeo-builder.json file does not contain the 'build' field")
|
||||
|
||||
if compile_static and "build_static" not in information:
|
||||
fail("The package does not support static builds")
|
||||
|
||||
cache_directory = os.getenv("XDG_CACHE_HOME", os.path.expanduser("~/.cache"))
|
||||
|
||||
build_directory = os.path.join(cache_directory, "rodeo-builder-build")
|
||||
|
||||
src_directory = os.path.join(build_directory, "package-source")
|
||||
|
||||
if os.path.exists(build_directory):
|
||||
shutil.rmtree(build_directory)
|
||||
os.makedirs(build_directory)
|
||||
|
||||
shutil.copytree(file_path, src_directory)
|
||||
|
||||
os.chdir(src_directory)
|
||||
|
||||
if "dependencies" in information:
|
||||
if "build_packages" in information["dependencies"]:
|
||||
for package in information["dependencies"]["build_packages"]:
|
||||
if not package in []:
|
||||
fail(f"The boundaries package {package} could not be found and is a build dependency")
|
||||
if "build_commands" in information["dependencies"]:
|
||||
for command in information["dependencies"]["build_commands"]:
|
||||
if shutil.which(command) is None:
|
||||
fail(f"The command {command} could not be found and is a build dependency")
|
||||
|
||||
if "web_sources" in information and isinstance(information["web_sources"], dict) and len(information["web_sources"]) > 0:
|
||||
for org_url, org_path in information["web_sources"].items():
|
||||
url = org_url.replace("$PKG_VERSION", information["version"])
|
||||
path = org_path.replace("$PKG_VERSION", information["version"])
|
||||
print(f"Downloading source {path} from {url}")
|
||||
progress_bar.new()
|
||||
urlretrieve(url, path, report_hook)
|
||||
print("")
|
||||
if not os.path.exists(path):
|
||||
fail(f"Download of {path} failed")
|
||||
|
||||
if "git_sources" in information and isinstance(information["git_sources"], dict) and len(information["git_sources"]) > 0:
|
||||
for org_url, org_path in information["git_sources"].items():
|
||||
url = org_url.replace("$PKG_VERSION", information["version"])
|
||||
path = org_path.replace("$PKG_VERSION", information["version"])
|
||||
print(f"Cloning {path} from {url}")
|
||||
os.system(f"git clone {url} {path}")
|
||||
if not os.path.exists(path):
|
||||
fail(f"Cloning of {path} failed")
|
||||
|
||||
print(f"Building {information['name']}")
|
||||
if compile_static:
|
||||
build_command = information['build_static']
|
||||
else:
|
||||
build_command = information['build']
|
||||
build_result = os.system(f"PKG_NAME={information['name']} PKG_VERSION={information['version']} OUT_DIR={build_directory} PKG_SRC={src_directory} {build_command}")
|
||||
if build_result != 0:
|
||||
fail("Build failed")
|
||||
|
||||
print("Creating rodeo package")
|
||||
os.chdir(os.path.join(file_path, build_directory))
|
||||
if "extra_package_info" in information:
|
||||
json_file = information["extra_package_info"]
|
||||
else:
|
||||
json_file = {}
|
||||
|
||||
json_file["name"] = information["name"]
|
||||
json_file["version"] = information["version"]
|
||||
|
||||
with open("rodeo-package.json", "wt") as f:
|
||||
json.dump(json_file, f)
|
||||
|
||||
os.chdir(file_path)
|
||||
|
||||
if output and not should_compress:
|
||||
if os.path.exists(os.path.join(file_path, output)):
|
||||
shutil.rmtree(os.path.join(file_path, output))
|
||||
shutil.move(os.path.join(file_path, build_directory), os.path.join(file_path, output))
|
||||
elif output and should_compress:
|
||||
print("Compressing package")
|
||||
with tarfile.open(output, "w:gz") as tar:
|
||||
tar.add(build_directory)
|
||||
shutil.rmtree(build_directory)
|
||||
elif should_compress:
|
||||
print("Compressing package")
|
||||
with tarfile.open("package.tar.gz", "w:gz") as tar:
|
||||
tar.add(build_directory)
|
||||
shutil.rmtree(build_directory)
|
||||
|
||||
print("\033[92m" + "Done! Package built" + "\033[0m")
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(prog="rodeo-builder", description="The rodeo package builder")
|
||||
parser.add_argument("--output", "-o", help="The path of the folder/archive to be created", default=None)
|
||||
parser.add_argument("--archive", "-a", help="Compress the output into a .tar.gz archive", action="store_true")
|
||||
parser.add_argument("--static", "-s", help="Try to build the package statically", action="store_true")
|
||||
parser.add_argument("path", help="The path to the archive or folder to build")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
build(path=args.path, output=args.output, should_compress=args.archive, compile_static=args.static)
|
175
builder/lowbar.py
Normal file
175
builder/lowbar.py
Normal file
|
@ -0,0 +1,175 @@
|
|||
"""
|
||||
https://github.com/AnnikaV9/lowbar
|
||||
The simplest no-nonsense progress bar for python.
|
||||
"""
|
||||
|
||||
import shutil
|
||||
|
||||
|
||||
class lowbar:
|
||||
"""
|
||||
The main lowbar class.
|
||||
"""
|
||||
def __init__(self, tasks: range | int = 0, desc: str = "",
|
||||
load_fill: str = "#", blank_fill: str = "-",
|
||||
remove_ends: bool = False, keep_receipt: bool = False) -> None:
|
||||
"""
|
||||
Checks and initializes the bar with the given
|
||||
parameters.
|
||||
"""
|
||||
if not isinstance(load_fill, str) or len(load_fill) != 1:
|
||||
raise TypeError("arg load_fill should be a single char str")
|
||||
|
||||
if not isinstance(blank_fill, str) or len(blank_fill) != 1:
|
||||
raise TypeError("arg blank_fill should be a single char str")
|
||||
|
||||
if not isinstance(desc, str):
|
||||
raise TypeError("arg desc should be type str")
|
||||
|
||||
if not isinstance(remove_ends, bool):
|
||||
raise TypeError("arg remove_ends should be type bool")
|
||||
|
||||
if not isinstance(keep_receipt, bool):
|
||||
raise TypeError("arg keep_receipt should be type bool")
|
||||
|
||||
if not isinstance(tasks, range):
|
||||
if not isinstance(tasks, int):
|
||||
raise TypeError("arg tasks should be either type range or int")
|
||||
|
||||
tasks = range(tasks)
|
||||
|
||||
self.tasks = tasks
|
||||
self.completion = 1
|
||||
self.load_fill = load_fill
|
||||
self.blank_fill = blank_fill
|
||||
self.desc = desc
|
||||
self.bar_ends = ("[", "]") if not remove_ends else (" ", " ")
|
||||
self.keep_receipt = keep_receipt
|
||||
|
||||
def __enter__(self) -> object:
|
||||
"""
|
||||
Context manager setup to automatically display bar
|
||||
without requiring new().
|
||||
"""
|
||||
self.new()
|
||||
return self
|
||||
|
||||
def __exit__(self, *exc) -> None:
|
||||
"""
|
||||
Context manager exit to clear the bar automatically
|
||||
without requiring clear().
|
||||
"""
|
||||
print() if self.keep_receipt else self.clear()
|
||||
|
||||
def __iter__(self) -> object:
|
||||
"""
|
||||
Iterable manager that automatically runs new() at the
|
||||
start and clear() at the end
|
||||
"""
|
||||
self.new()
|
||||
div = 100 / len(self.tasks)
|
||||
add = div
|
||||
try:
|
||||
for item in self.tasks:
|
||||
yield item
|
||||
self.update(int(div))
|
||||
div += add
|
||||
|
||||
finally:
|
||||
print() if self.keep_receipt else self.clear()
|
||||
|
||||
def _print(self, text: str) -> None:
|
||||
"""
|
||||
Writes data to stdout
|
||||
"""
|
||||
print(text, end='', flush=True)
|
||||
|
||||
def _get_columns(self) -> int:
|
||||
"""
|
||||
Returns the number of columns in the running console.
|
||||
"""
|
||||
return shutil.get_terminal_size().columns
|
||||
|
||||
def _update_bar(self) -> None:
|
||||
"""
|
||||
Refreshes the current bar with new values, and a
|
||||
possibly resized console.
|
||||
"""
|
||||
desc = self.desc if len(self.desc) + 20 < self._get_columns() else ""
|
||||
label = f"{desc} {str(self.completion)}" if self.completion < 10 else f"{desc} {str(self.completion)}"
|
||||
size = self._get_columns() - (len(label) + 7)
|
||||
load_size = int(size * (self.completion / 100))
|
||||
bar_blank_fill = size - load_size
|
||||
self._print(f"\r{label} % {self.bar_ends[0]}{load_size * self.load_fill}{bar_blank_fill * self.blank_fill}{self.bar_ends[1]} ")
|
||||
|
||||
def _overwrite_bar(self, text: str = "") -> None:
|
||||
"""
|
||||
Overwrite the loading bar with optional text.
|
||||
"""
|
||||
overwrite = " " * (self._get_columns() - len(text))
|
||||
self._print(f"\r{text}{overwrite}")
|
||||
|
||||
def update(self, percentage: int) -> None:
|
||||
"""
|
||||
Sets the current completion percentage.
|
||||
"""
|
||||
if not isinstance(percentage, int):
|
||||
raise TypeError("arg percentage should be type int")
|
||||
|
||||
if percentage < 0 or percentage > 100:
|
||||
raise ValueError("arg percentage out of range (0-100)")
|
||||
|
||||
self.completion = percentage
|
||||
self._update_bar()
|
||||
|
||||
def add(self, percentage: int) -> None:
|
||||
"""
|
||||
Adds to the current completion percentage.
|
||||
"""
|
||||
if not isinstance(percentage, int):
|
||||
raise TypeError("arg percentage should be type int")
|
||||
|
||||
self.completion += percentage
|
||||
self.completion = 100 if self.completion > 100 else self.completion
|
||||
self.completion = 0 if self.completion < 0 else self.completion
|
||||
self._update_bar()
|
||||
|
||||
def next(self, tasks: int = 0) -> None:
|
||||
"""
|
||||
Automatically adds to the completion percentage based
|
||||
on the number of tasks to be completed.
|
||||
"""
|
||||
if not isinstance(tasks, int):
|
||||
raise TypeError("arg tasks should be type int")
|
||||
|
||||
if tasks < 1:
|
||||
if not self.tasks:
|
||||
raise RuntimeError("empty `next` was called without `tasks` being set in the constructor")
|
||||
|
||||
self.add(int(100 / len(self.tasks)))
|
||||
return
|
||||
|
||||
self.add(int(100 / tasks))
|
||||
|
||||
def new(self) -> None:
|
||||
"""
|
||||
Alias for update(0)
|
||||
"""
|
||||
self.update(0)
|
||||
|
||||
def log(self, text: str) -> None:
|
||||
"""
|
||||
Log text to the console without affecting the bar.
|
||||
"""
|
||||
if not isinstance(text, str):
|
||||
raise TypeError("arg text should be type str")
|
||||
|
||||
self._overwrite_bar(text)
|
||||
print()
|
||||
self._update_bar()
|
||||
|
||||
def clear(self) -> None:
|
||||
"""
|
||||
Clears the bar completely from the console.
|
||||
"""
|
||||
self._overwrite_bar()
|
Loading…
Add table
Add a link
Reference in a new issue