diff --git a/_tools/cleanup.py b/_tools/cleanup.py index c25e2717f6..1a2d61c80d 100755 --- a/_tools/cleanup.py +++ b/_tools/cleanup.py @@ -10,6 +10,9 @@ if len(sys.argv) < 2 or sys.argv[1] != "--remove-everything": # Remove workspace folder shutil.rmtree("workspace", ignore_errors = True) +# Remove tup database +shutil.rmtree(".tup", ignore_errors = True) + # TODO: Make build.py remove the stuff it built # Remove files copied from _tools/workspace diff --git a/_tools/get_started.py b/_tools/get_started.py index 2644069b05..5d206a2cfe 100644 --- a/_tools/get_started.py +++ b/_tools/get_started.py @@ -32,3 +32,6 @@ if __name__ == "__main__": tools_workspace_build_py = os.path.join(tools_workspace, "build.py") create_workspace_script("run.py", tools_workspace_run_py) create_workspace_script("build.py", tools_workspace_build_py) + # Initalize tup here + # TODO: Do anything if tup doesn't exist + os.system("tup init") diff --git a/_tools/lib/tupfile_parser.py b/_tools/lib/tupfile_parser.py index 010c07e95e..0560ff306f 100644 --- a/_tools/lib/tupfile_parser.py +++ b/_tools/lib/tupfile_parser.py @@ -1,153 +1,74 @@ # Copyright Magomed Kostoev # Published under MIT license -class Rule: - pass +block_beginner2finisher = { + "(": ")", + "{": "}", + "\"": "\"", +} -def get_config(config_name): - if config_name == "KPACK_CMD": - # Just never pack the file for now - return "" - else: - print(f"Unknown config name: {config_name}") - exit(-1) +def get_to(src, ptr, c, backwards = False): + while src[ptr] != c: + if not backwards: + ptr += 1 + else: + ptr -= 1 + return ptr -def skip_whitespaces(src, ptr): - while len(src) > ptr and src[ptr] == " ": +# Assuming we are at beginning of some block (string, +# parenthesed expression, {}-like thing), get to end +# of the block (also handles the blocks openned in the +# way to the block ending character. So it's not just +# stupid searching for block closing character. +def get_to_block_finisher(src, ptr): + assert(src[ptr] in block_beginner2finisher) + + block_beginner = src[ptr] + ptr += 1 + while src[ptr] != block_beginner2finisher[block_beginner]: + # If any block starts here - get to its end and then continue + if src[ptr] in block_beginner2finisher: + ptr = get_to_block_finisher(src, ptr) ptr += 1 return ptr -# Returns True if @src has @string starting from @ptr index -def match_string(src, ptr, string): - if len(src) <= ptr + len(string): - return False - for i in range(len(string)): - if src[ptr + i] != string[i]: - return False - return True +def get_strnig(src, ptr): + # Strings starts with "\"" + assert(src[ptr] == "\"") -def parse_tup_getconfig(src, ptr): - # Skip get straight to the argument - ptr += len("tup.getconfig(") - ptr = skip_whitespaces(src, ptr) - if src[ptr] != "\"": - print("Expected \"config name\" as tup.getconfig parameter") - exit() - (config_name, ptr) = parse_string(src, ptr) - ptr = skip_whitespaces(src, ptr) - # Skip closing parenthese of the tup.getconfig call - assert(src[ptr] == ")") + result = "" + # Skip first "\"" of the string ptr += 1 - return (get_config(config_name), ptr) - -def parse_string(src, ptr): - ptr += 1 - string = "" while src[ptr] != "\"": - string += src[ptr] + result += src[ptr] ptr += 1 - # Skip the closing "\"" - ptr += 1 - ptr = skip_whitespaces(src, ptr) - # Check if we have concatination here - if match_string(src, ptr, ".."): - # Skip the ".." - ptr += 2 - # The expression parsing should result in a string - (string_to_add, ptr) = parse_expression(src, ptr) - # Concat our string to the resulting string - string += string_to_add - return (string, ptr) - -def parse_expression(src, ptr): - ptr = skip_whitespaces(src, ptr) - result = "WAT?!" - if src[ptr] == "\"": - (result, ptr) = parse_string(src, ptr) - elif match_string(src, ptr, "tup.getconfig("): - (result, ptr) = parse_tup_getconfig(src, ptr) - else: - print(f"Can't handle anything starting with '{src[ptr]}'") - exit(-1) - ptr = skip_whitespaces(src, ptr) - return (result, ptr) - -def expect_comma(src, ptr): - comma_skept = False - ptr = skip_whitespaces(src, ptr) - if src[ptr] == ",": - ptr += 1 - return (True, ptr) - else: - return (False, ptr) - -def parse_arguments(src, ptr): - result = [] - # Parse first argument - (argument, ptr) = parse_expression(src, ptr) - result.append(argument) - (comma_encoutered, ptr) = expect_comma(src, ptr) - # Parse the second argument if it's there - if comma_encoutered: - (argument, ptr) = parse_expression(src, ptr) - result.append(argument) - (comma_encoutered, ptr) = expect_comma(src, ptr) - # Parse third argument if it's there - if comma_encoutered: - (argument, ptr) = parse_expression(src, ptr) - result.append(argument) return result -def parse_rule(src, ptr): +def parse_rule_output(src, ptr): # Get straight to the first argument - ptr += len("tup.rule(") - # Parse the arguments - args = parse_arguments(src, ptr) - # Build the rule object - result = Rule() - if len(args) == 3: - result.input = args[0] - result.command = args[1] - result.output = args[2] - # Replace %f with input file in rule's command - if type(result.input == str): - result.command = result.command.replace("%f", result.input) - else: - print("Command building with non-string tup.rule's first argument" - + " isn't implemented") - exit() - # Replace %o with output file in rule's command - if type(result.output == str): - result.command = result.command.replace("%o", result.output) - else: - print("Command building with non-string tup.rule's first argument" - + " isn't implemented") - exit() - elif len(args) == 2: - result.input = [] - result.command = args[0] - result.output = args[1] - else: - print(f"tup.rule can only take 2 or 3 arguments, not {len(args)}") - exit(-1) - # Unify the API - return arrays as input and output - if type(result.input) == str: - result.input = [ result.input ] - else: - assert(type(result.input) == list) - if type(result.output) == str: - result.output = [ result.output ] - else: - assert(type(result.output) == list) - return result + ptr += len("tup.rule") + # Get to parenthese + ptr = get_to(src, ptr, "(") + # Get to the closing parenthese + ptr = get_to_block_finisher(src, ptr) + # We are at closing parenthese of argument list + # And the last argument is always output file + # Let's get to closing "\"" of the output file name + ptr = get_to(src, ptr, "\"", backwards = True) + # Get into the string + ptr -= 1 + # Then get to the beginning of the string + ptr = get_to(src, ptr, "\"", backwards = True) + # Now we can read the string + return get_strnig(src, ptr) -def parse(file_name): - rules = [] +def parse_tupfile_outputs(file_name): + outputs = [] with open(file_name) as f: tupfile = f.read() rule_begin_index = tupfile.find("tup.rule(") while (rule_begin_index != -1): - rules.append(parse_rule(tupfile, rule_begin_index)) + outputs.append(parse_rule_output(tupfile, rule_begin_index)) # Find the next tup.rule call rule_begin_index = tupfile.find("tup.rule(", rule_begin_index + len("tup.rule(")) - return rules + return outputs diff --git a/_tools/workspace/build.py b/_tools/workspace/build.py index 6b2e5ae510..9b4e281000 100644 --- a/_tools/workspace/build.py +++ b/_tools/workspace/build.py @@ -5,21 +5,15 @@ path_to_tools_workspace = os.path.dirname(os.path.abspath(__file__)) path_to_tools = os.path.dirname(path_to_tools_workspace) sys.path.append(path_to_tools) -from lib.tupfile_parser import parse as parse_tupfile +from lib.tupfile_parser import parse_tupfile_outputs def build(): - if not os.path.exists("Tupfile.lua"): - print("No Tupfile.lua, can't build anything") - exit() - - tup_rules = parse_tupfile("Tupfile.lua") - program_files = [] - for rule in tup_rules: - # TODO: Manage source dependencies - # TODO: Inform about tools required for the build - os.system(rule.command) - program_files += rule.output - return program_files + os.system("tup") + outputs = parse_tupfile_outputs("Tupfile.lua") + for name in outputs: + if name.endswith(".inc"): + continue + return name if __name__ == "__main__": build() diff --git a/_tools/workspace/run.py b/_tools/workspace/run.py index 1d4886f736..c5e1b195fd 100644 --- a/_tools/workspace/run.py +++ b/_tools/workspace/run.py @@ -51,7 +51,7 @@ def run_qemu(start_dir = "workspace"): return subprocess.Popen(a, bufsize = 0, stdout = qemu_stdout, stderr = qemu_stderr, stdin = subprocess.DEVNULL, start_new_session = True) if __name__ == "__main__": - program_files = build() + program_name = build() os.makedirs("workspace", exist_ok = True) @@ -72,19 +72,16 @@ if __name__ == "__main__": img.delete_path("DEMOS") img.delete_path("3D") - log("Moving program files into kolibri image... ", end = "") - for file_name in program_files: - with open(file_name, "rb") as file: - file_data = file.read() - if not img.add_file_path(file_name.upper(), file_data): - print(f"Coudn't move {file_name} into IMG") + log("Moving program into kolibri image... ", end = "") + with open(program_name, "rb") as file: + file_data = file.read() + if not img.add_file_path(program_name.upper(), file_data): + print(f"Coudn't move {program_name} into IMG") log("Done") # TODO: Figure out which of compiled files is a program executable and only run it - log("Adding program to autorun.dat", end = "") - lines_to_add = b"" - for file_name in program_files: - lines_to_add += bytes(f"\r\n/SYS/{file_name.upper()}\t\t""\t0\t# Your program", "ascii") + log("Adding program to autorun.dat... ", end = "") + lines_to_add = bytes(f"\r\n/SYS/{program_name.upper()}\t\t""\t0\t# Your program", "ascii") autorun_dat = img.extract_file_path("SETTINGS\AUTORUN.DAT") place_for_new_lines = autorun_dat.index(b"\r\n/SYS/@TASKBAR")# b"\r\n### Hello, ASM World! ###") autorun_dat = autorun_dat[:place_for_new_lines] + lines_to_add + autorun_dat[place_for_new_lines:]