def __handle_new_tool_arg(in_args): if len(in_args) < 1: log.error("--new_tool requires a value.") log.normal("\t--new_tool [config_type]") return module_dir = os.path.relpath(__file__) module_dir = os.path.dirname(module_dir) config_type = in_args.pop(0) config_type_dir = os.path.join(module_dir, config_type) if not os.path.exists(config_type_dir): module_dir = os.path.join(module_dir, "new_tool") os.mkdir(config_type_dir) files = [ "__init__.py", "help.md", "default.py", "config/__init__.py", "options/__init__.py", "options/tool.py" ] for file_name in files: with open(os.path.join(module_dir, file_name)) as in_file: file_str = in_file.read() file_str = file_str.format_map({"config_type": config_type}) tool_file = os.path.join(config_type_dir, file_name) os.makedirs(os.path.dirname(tool_file), exist_ok=True) with open(tool_file, "w") as out_file: out_file.write(file_str) log.success("created " + config_type + " config_type!") log.normal("generate a config file by calling: " + "'./confply.py --new_config " + config_type + " my_config.py'") else: log.error(config_type + " already exists. (--new_tool)") pass
def __handle_in_arg(in_args): if len(in_args) < 1: log.error("--in requires a value.") log.normal("\t--in [config_path]") return in_path = in_args.pop(0) confply.config.config_path = in_path
def handle_args(): if "--cpp_clean" in config.confply.args or config.clean: if os.path.exists(config.object_path): log.normal("cleaning compiled objects") os.system("rm -r " + config.object_path) else: log.normal("no objects to remove")
def __handle_version_arg(in_args): log.linebreak() log.normal("Confply " + get_version()) log.normal("Copyright (C) 2021 Graham Hughes.") log.normal("License MIT.") log.normal("This is free software; " + "you are free to change and redistribute it.") log.normal("There is NO WARRANTY, to the extent permitted by law.")
def _print_tools(): num = -1 for k in in_tools.keys(): num += 1 if not in_tools[k].is_found(): log.warning("\t" + str(num) + ") " + k + " (not found)") else: log.normal("\t" + str(num) + ") " + k)
def on_complete(): import confply.log as log import os if os.path.exists("libfwcore.a"): os.system("rm libfwcore.a") os.system("ar rcs libfwcore.a objects/fwcore/*.o") log.normal("") log.normal("output built libs to libfwcore.a")
def __handle_listen_arg(in_args): if len(in_args) < 1: log.error("--listen requires a value.") log.normal("\t--listen [launcher_file]") return arguement = in_args.pop(0) launcher_path = os.path.abspath(os.path.curdir) + "/" + arguement if os.path.exists(launcher_path): with pushd(os.path.dirname(__file__)): confply.server.start_server(launcher=launcher_path) else: log.error(launcher_path + " not found!")
def __print_config(config_name, config): diff_config = __get_diff_config(config) config_type_path = config.__file__ if (os.path.exists(config_type_path)): log.normal(config_name + " configuration:") log.normal("") def print_dict(d1, depth=0): for k, v in d1.items(): if isinstance(v, list): log.bold("\t" * depth + log.format.bold(str(k)) + ": ") for i in v: log.normal("\t" * (depth + 1) + str(i)) elif isinstance(v, dict): log.normal("\t" * depth + log.format.bold(k) + ":") print_dict(v, depth=depth + 1) pass elif inspect.isfunction(v): log.normal("\t" * depth + log.format.bold(str(k)) + ": " + v.__name__) else: log.normal("\t" * depth + log.format.bold(str(k)) + ": " + str(v)) if "confply" in diff_config: del diff_config["confply"] print_dict(diff_config) log.normal("") else: log.error(confply.config_config_type + " is not a valid confply_config_type" + " and should not be set by users.") log.normal("\tuse: 'import confply.[config_type].config" + " as confply' to import confply_config_type.")
def __apply_overrides(config): # update the config and confply.config dictionaries with overrides if isinstance(confply.config.__override_dict, dict): confply_dict = confply.config.__override_dict config.confply.__dict__.update(confply_dict["confply"]) del confply_dict["confply"] config.__dict__.update(confply_dict) confply.config.__override_dict = {"confply": {}} # update the configs with further overrides if isinstance(confply.config.__override_list, list): for k, v in confply.config.__override_list: try: exec("{0} = v".format(k), globals(), locals()) except Exception: log.warning("failed to exec " + "{0} = {1}:".format(k, v)) log.normal("\t\tcheck calling option --" + k)
def __validate_config(config): tools = {} tool_dir = os.path.dirname(config.__file__) tool_dir = os.path.join(tool_dir, "..") if os.path.exists(tool_dir): files = os.listdir(tool_dir) else: log.error(config.__package__ + " is not a valid confply_config_type" + " and should not be set by users.") log.normal("\tuse: " + "'import confply.[config_type].config as config'" + " to import confply_config_type.") return None tools = {} module_path = config.__package__.rsplit(".", 1)[0] for py in files: if py.endswith(".py") and not py == "__init__.py": tool = py[0:-3] tools[tool] = \ importlib.import_module(module_path+"."+tool) tool = confply.config.tool pass ####### if tool in tools: if not tools[tool].is_found(): log.error("'" + tool + "' could not be found, is it installed?") if __tool_select(tools): return tools else: return None else: return tools else: log.error("'" + str(tool) + "' is not a valid tool.") if __tool_select(tools): return tools else: return None return tools
def __tool_select(in_tools): def _print_tools(): num = -1 for k in in_tools.keys(): num += 1 if not in_tools[k].is_found(): log.warning("\t" + str(num) + ") " + k + " (not found)") else: log.normal("\t" + str(num) + ") " + k) shown_options = False while True: if input_prompt("continue with a different tool?"): if not shown_options: log.normal("which tool? options:") _print_tools() log.normal("") shown_options = True log.normal("tool: ", end="", flush=True) in_tool = input("") if (in_tool.isdigit()): tool_num = int(in_tool) if tool_num < len(in_tools): tool_keys = list(in_tools.keys()) confply.config.tool = tool_keys[tool_num] return True else: log.error("'" + in_tool + "' is out of range.") pass else: if in_tool in in_tools: confply.config.tool = in_tool return True else: log.error("'" + in_tool + "' could not be found," + " is it installed?") else: if not shown_options: log.normal("options:") _print_tools() log.normal("") return False
def run_dict(in_dict): """ runs the confply dict for the supplied config_type usage: run_dict(in_dict) """ log.linebreak() log.header("run config") log.linebreak() # setup config run should_run = confply.config.run if ("config_path" in in_dict["confply"]): path = in_dict["confply"]["config_path"] else: path = None __load_vcs_info(path) confply.config.config_path = path config_name = os.path.basename(path) confply.config.config_name = config_name confply.config.modified = os.path.getmtime(path).real config_locals = apply_to_config(in_dict) if not config_locals: log.error("failed to load: json") return -1 if ("config" not in config_locals): log.error("confply failed import") log.normal( "\tuse: '__package__': 'confply.[config_type].config' in json/ini") clean_modules() return -1 if (not __validate_types(config_locals["config"])): log.error("failed to run config") return -1 # ensure we don't run if should_run was EVER false if should_run is not True: confply.config.run = should_run return __run_config(config_locals)
def __run_dependencies(config, should_run): store = config_to_dict(config) importlib.reload(config) importlib.reload(config.confply) confply.config.run = should_run depends_return = 0 if len(store["confply"]["dependencies"]) > 0: for d in store["confply"]["dependencies"]: if d not in __configs_run: confply.config.log_topic = store["confply"]["log_topic"] log.normal("running dependency: " + str(d)) confply.config.log_topic = "confply" log.linebreak() __configs_run.add(d) depends_return = run_commandline(["--in", d]) if depends_return < 0: confply.config.log_topic = store["confply"]["log_topic"] confply.config.log_file = store["confply"]["log_file"] log.error("failed to run: " + str(d)) if not input_prompt("continue execution?"): log.normal("aborting final commands") break else: log.normal("continuing execution.") pass # reset confply.config apply_to_config(store) # #todo: make this jump to the mail section return depends_return
def print_dict(d1, depth=0): for k, v in d1.items(): if isinstance(v, list): log.bold("\t" * depth + log.format.bold(str(k)) + ": ") for i in v: log.normal("\t" * (depth + 1) + str(i)) elif isinstance(v, dict): log.normal("\t" * depth + log.format.bold(k) + ":") print_dict(v, depth=depth + 1) pass elif inspect.isfunction(v): log.normal("\t" * depth + log.format.bold(str(k)) + ": " + v.__name__) else: log.normal("\t" * depth + log.format.bold(str(k)) + ": " + str(v))
def input_prompt(message): """ prompt the user for a Y/N answer on a 10 second timeout """ if os.name != "nt": while True: log.normal(message + " (Y/N): ", end="", flush=True) select_args = ([sys.stdin], [], [], 10) rlist, _, _ = select.select(*select_args) if rlist: answer = sys.stdin.readline() answer = answer.upper() answer = answer.replace("\n", "") if answer == "YES" or answer == "Y": return True elif answer == "NO" or answer == "N": return False else: print("") log.normal("timed out after 10 seconds") return False else: return False
def __handle_launcher_arg(in_args): if len(in_args) < 1: log.error("--launcher requires a value.") log.normal("\t--launcher [new_launcher_file]") return confply_dir = __get_confply_dir() arguement = in_args.pop(0) launcher_path = os.path.abspath(os.path.curdir) + "/" + arguement if not os.path.exists(launcher_path): with open(launcher_path, "w") as launcher_file: launcher_str = __new_launcher_str.format_map({ "confply_dir": confply_dir, "launcher": arguement, "comment": "{\n # 'myconfig':'path/to/config.py'\n}" }) launcher_file.write(launcher_str) st = os.stat(launcher_path) os.chmod(launcher_path, st.st_mode | stat.S_IEXEC) log.success("wrote: " + launcher_path) else: log.error(launcher_path + " already exists!")
def _handle_config_dict_arg(in_args): if len(in_args) < 1: log.error("--config requires a value.") log.normal("\t--config \"{'confply':{'tool':'cl'}}\"") return overide_dict = None try: overide_dict = ast.literal_eval(in_args.pop(0)) if not isinstance(overide_dict, dict): log.error("--config must be a dictionary.") log.normal("\t--config \"{'confply':{'tool':'cl'}}\"") return elif "confply" in overide_dict: if not isinstance(overide_dict["confply"], dict): log.error("--config.confply must be a dictionary.") log.normal("\t--config \"{'confply':{'tool':'cl'}}\"") return else: overide_dict["confply"] = {} except Exception: log.error("--config failed to parse argument as a dictionary") return confply.config.__override_dict.update(overide_dict)
#!../tools/confply/confply.py # generated using: # python ../tools/confply/confply.py --config cpp_compiler build.py import sys sys.path.append('../tools/confply') import confply.cpp_compiler.config as confply import confply.log as log import os confply.confply_log_topic = "fwcore" log.normal("loading build.py with confply_args: " + str(confply.confply_args)) confply.confply_tool = "clang++" confply.output_executable = False confply.object_path = "objects/fwcore" confply.include_paths = ["fmt/include/"] confply.source_files = [ "fmt/src/format.cc", "fmt/src/os.cc", ] def on_complete(): import confply.log as log import os if os.path.exists("libfwcore.a"): os.system("rm libfwcore.a") os.system("ar rcs libfwcore.a objects/fwcore/*.o") log.normal("") log.normal("output built libs to libfwcore.a")
#!../confply.py --in # generated using: # python ../confply.py --config cpp_compiler cpp_compiler.cpp.py import sys sys.path.append('..') import confply.cpp_compiler.config as config import confply.cpp_compiler.options as options import confply.log as log ############# modify_below ################ config.confply.log_topic = "cpp_compiler" log.normal("loading cpp_compiler with confply_args: " + str(config.confply.args)) config.source_files = ["main.cpp"] config.output_file = "hello_confply" config.link_libraries = ["stdc++"] config.standard = options.standard.cpp17 config.warnings = options.warnings.all_warnings config.confply.log_config = True def post_run(): import subprocess import sys subprocess.run("./hello_confply", stdout=sys.stdout, stderr=subprocess.STDOUT, shell=True) pass
#!/usr/bin/env python import os import sys import confply import confply.log as log if __name__ == "__main__": in_args = sys.argv[1:] version = sys.version_info version = (version.major, version.minor, version.micro) if "--no_header" not in in_args: log.confply_header() log.linebreak() log.normal("python" + str(version)) log.normal("confply " + confply.get_version()) log.normal("date: " + str(confply.datetime.now())) if (version[0] < 3 or (version[0] == 3 and version[1] < 8)): log.error("python version must be 3.8 or above") log.linebreak() exit(1) return_code = -999999 if len(in_args) != 0: while len(in_args) > 0: cmd_return = confply.run_commandline(in_args) if (cmd_return > return_code and cmd_return != 0): return_code = cmd_return # fatal error. if return_code == -1:
import ast import os sys.path.append('.') import confply.cpp_compiler.config as config import confply.cpp_compiler.options as options import confply.log as log ############# modify_below ################ # set the default compiler config.confply.tool = options.tool.clangpp # set debug_info from commandline args debug = False if "debug" in config.confply.args: debug = True config.object_path = "objects/debug" log.normal("set to debug config") config.debug_info = debug config.confply.mail_to = "*****@*****.**" config.confply.mail_from = "*****@*****.**" # config.confply.log_file = "confply.log" config.confply.log_debug = True mail_login = () slack_bot = "" if os.path.exists("mail_details.py"): with open("mail_details.py") as details_file: mail_login = ast.literal_eval(details_file.read()) if os.path.exists("slack_details.py"): with open("slack_details.py") as details_file: slack_bot = ast.literal_eval(details_file.read()) config.confply.__mail_login = mail_login
def __handle_new_config_arg(in_args): if len(in_args) < 2: log.error("--config requires two values:") log.normal("\t--new_config [config_type] [new_config_file]") log.normal("") log.normal("valid tool types:") module_dir = os.path.dirname(__file__) files = os.listdir(module_dir) for dir in files: if (os.path.isdir(os.path.join(module_dir, dir)) and not dir == "__pycache__" and not dir == "new_tool"): log.normal("\t" + dir) return confply_dir = __get_confply_dir() config_type_arg = in_args.pop(0) config_arg = in_args.pop(0) config_path = os.path.abspath(os.path.curdir) + "/" + config_arg config_type_dir = os.path.dirname(os.path.relpath(__file__)) config_type_dir = os.path.join(config_type_dir, config_type_arg) if not os.path.exists(config_type_dir): log.error(config_type_arg + " is not a valid config_type, consider:") for dir_file in os.listdir(confply_dir + "/confply/"): if os.path.isdir(confply_dir + "/confply/" + dir_file): if not dir_file == "__pycache__": log.normal("\t" + dir_file) return hash_file = os.path.join(config_type_dir, "config/__init__.py") config_hash = md5_file(hash_file) if not os.path.exists(config_path): with open(config_path, "w") as config_file: config_str = __new_config_str.format_map({ "confply_dir": confply_dir, "config_type_arg": config_type_arg, "config_file": config_arg, "config_hash": config_hash }) config_file.write(config_str) st = os.stat(config_path) os.chmod(config_path, st.st_mode | stat.S_IEXEC) log.success("wrote: " + config_path) else: log.error(config_path + " already exists!") log.normal("aborted --new_config.")
def __handle_config_arg(option, in_args): global logged_overrides if len(in_args) < 1: log.error("--config requires a value.") log.normal("\t--config.confply.tool \"cl\"") return path = option.split(".")[1:] if len(path) == 0: log.error("--config invalid path.") log.normal("\t--config.confply.tool \"cl\"") return if path[0] == "confply" and len(path) == 1: log.error("--config invalid path, cannot set confply") log.normal("\t--config.confply.tool \"cl\"") return # #todo: this is pretty shit, it doesn't solve the general case at all # but I'm being lazy you should be able to set values in a deep dictionary # but you can't. arg = in_args.pop(0) value = None try: value = ast.literal_eval(arg) except Exception: value = arg # #todo: this should be done prior to running the config. not now. # this logging section is lazy af. if not logged_overrides: logged_overrides = True log.normal("overrides:") if isinstance(value, str): log.normal("\t" + option[2:] + " = \"" + str(value) + "\" <" + type(value).__name__ + ">") else: log.normal("\t" + option[2:] + " = " + str(value) + " <" + type(value).__name__ + ">") confply.config.__override_list.append([option[2:], value])
def run_commandline(in_args): """ runs the confply config, with supplied arguements. confply reservered options will be stripped. e.g. --help see help.md usage: run_commandline(["path_to_config", "optional", "arguements"]) """ log.normal("called with args: " + str(in_args)) in_args = __strip_confply_args(in_args) confply.config.args = in_args if confply.config.config_path: config_path = confply.config.config_path if not os.path.exists(config_path): log.error("couldn't find: " + config_path) return -1 if config_path.endswith(".py"): log.linebreak() log.header("run config") log.linebreak() # setup config run should_run = confply.config.run config_locals = load_config(config_path) if not config_locals: log.error("failed to load: " + config_path) return -1 if ("config" not in config_locals): log.error("confply config incorrectly imported") log.normal( "\tuse: 'import confply.[config_type].config as config'") clean_modules() return -1 config = config_locals["config"] if (not __validate_types(config)): log.error("failed to run config") return -1 # ensure we don't run if should_run was EVER false if should_run is not True: confply.config.run = should_run # config config_hash = md5_file(config.__file__) if ("config_hash" not in config_locals or config_hash != config_locals["config_hash"]): log.warning("warning: config_hash doesn't match expected hash") log.normal("\tconfig file might not function correctly") log.normal("\texpected:") log.normal("\t\t" + "config_hash='" + config_hash + "'") log.normal("") return __run_config(config_locals) elif config_path.endswith(".json"): if os.path.exists(config_path): with open(config_path) as in_json: in_dict = json.loads(in_json.read()) in_dict["confply"].update({"config_path": config_path}) return run_dict(in_dict) elif config_path.endswith(".ini"): if os.path.exists(config_path): import configparser def parse_lit(in_val): try: return ast.literal_eval(in_val) except Exception: return in_val conf = configparser.ConfigParser() conf.read(config_path) in_dict = {"confply": {"config_path": config_path}} for (key, val) in conf["config"].items(): in_dict[key] = parse_lit(val) for (key, val) in conf["confply"].items(): in_dict["confply"][key] = parse_lit(val) return run_dict(in_dict) else: log.error("unsupported config type: " + config_path) return -1 else: return 0 return 0
def load_config(path): global config_modules if os.name == "nt": confply.config.platform = "windows" elif os.name == "posix": confply.config.platform = "linux" __load_vcs_info(path) # find group config in parent directories directory_paths = __get_group_configs(path) directory_paths.append(path) config_locals = {} # load and execute the config files for dir_path in directory_paths: if dir_path is None: continue if os.path.exists(dir_path) and os.path.isfile(dir_path): config_name = os.path.basename(dir_path) confply.config.config_name = config_name confply.config.modified = os.path.getmtime(dir_path).real with open(dir_path) as config_file: with pushd(os.path.dirname(dir_path)): try: exec(config_file.read(), {}, config_locals) # find imported confply configs for cleanup for k, v in config_locals.items(): m_valid = isinstance(v, types.ModuleType) if not m_valid: continue v_name = v.__name__ m_valid = m_valid and v not in config_modules m_valid = m_valid and v_name.startswith("confply.") m_valid = m_valid and v_name.endswith(".config") if m_valid: config_modules.add(v) # validate there are less than 2 imported configs if len(config_modules) > 1: log.error("too many confply configs imported:") for c in config_modules: log.normal("\t " + c) log.normal( "confply only supports one config import.") clean_modules() return None, None log.linebreak() log.success("loaded: " + str(dir_path)) except Exception: log.error("failed to exec: " + str(dir_path)) trace = traceback.format_exc().replace( "<string>", str(dir_path)) log.normal("traceback:\n\n" + trace) return None, None else: log.error("failed to find: " + str(dir_path)) return None, None return config_locals
def __run_config(config_locals): in_args = confply.config.args config = config_locals["config"] return_code = 0 should_run = confply.config.run file_path = confply.config.config_path __apply_overrides(config) new_working_dir = os.path.dirname(file_path) old_stdout = sys.stdout # setting confply command configuration up with pushd(new_working_dir): if confply.config.log_file: log.normal("writing to: " + confply.config.log_file + "....") try: sys.stdout = open(confply.config.log_file, "w") version = sys.version_info version = (version.major, version.minor, version.micro) if "--no_header" not in in_args: log.confply_header() log.linebreak() log.normal("python" + str(version)) log.normal("confply " + get_version()) log.normal("date: " + str(datetime.now())) log.linebreak() log.normal("confply logging to " + confply.config.log_file) except Exception: log.error("couldn't open " + confply.config.log_file + " for write.") return_code = -1 try: if (confply.config.post_load and inspect.isfunction(confply.config.post_load)): log.normal("running post load script: " + confply.config.post_load.__name__) sys.stdout.flush() exec(confply.config.post_load.__code__, config_locals, {}) log.linebreak() except Exception: log.error("failed to exec " + confply.config.post_load.__name__) trace = traceback.format_exc() log.normal("traceback:\n\n" + trace) pass diff_config = __get_diff_config(config) should_run = confply.config.run report = { "config_path": file_path, "config_json": json.dumps(diff_config, indent=4), "config_type": "unknown", "tool": "unknown", "status": "failure", "vcs_root": confply.config.vcs_root, "vcs_log": confply.config.vcs_log, "vcs": confply.config.vcs, "vcs_branch": confply.config.vcs_branch, "vcs_author": confply.config.vcs_author, "time_taken": "00:00:00" } if return_code >= 0: if confply.config.log_config is not False: __print_config(os.path.basename(file_path), config) try: time_start = timeit.default_timer() # #todo: tool selection phase should happen first. tools = __validate_config(config) config_type = config.__package__ tool = confply.config.tool report["tool"] = tool report["config_type"] = config_type if tools: shell_cmd = tools[tool] shell_cmd.handle_args() # #todo: rename generate to gen_config_type. shell_cmd = shell_cmd.generate() if tools else None else: shell_cmd = None __run_dependencies(config, should_run) if shell_cmd is not None: cmd_env = tools[tool].get_environ() if len(shell_cmd) > 0: if isinstance(shell_cmd, list): log.normal("final commands:\n") for shell_str in shell_cmd: cmd = confply.config.command_prepend + shell_str cmd = cmd + confply.config.command_append print(cmd) print("") else: cmd = confply.config.command_prepend + shell_cmd cmd = shell_cmd + confply.config.command_append log.normal("final command:\n\n" + str(cmd) + "\n") if should_run: log.header("begin " + tool) sys.stdout.flush() if should_run and isinstance(shell_cmd, list): for cmd in shell_cmd: cmd_time_start = timeit.default_timer() sys.stdout.flush() log.linebreak() cmd = confply.config.command_prepend + cmd cmd = cmd + confply.config.command_append log.normal(cmd) log.normal("", flush=True) return_code = __run_shell_cmd(cmd, cmd_env, tool) cmd_time_end = timeit.default_timer() # #todo: this can be tidied with format var capture s = cmd_time_end - cmd_time_start m = int(s / 60) h = int(m / 60) # time formating via format specifiers # https://docs.python.org/3.8/library/string.html#formatspec time = f"{h:0>2.0f}:{m:0>2.0f}:{s:0>5.2f}" log.normal("time elapsed " + time) elif should_run: return_code = __run_shell_cmd(shell_cmd, cmd_env, tool) else: log.warning("no commands run") else: log.error("failed to generate a valid command.") return_code = -1 time_end = timeit.default_timer() s = time_end - time_start m = int(s / 60) h = int(m / 60) # time formating via format specifiers # https://docs.python.org/3.8/library/string.html#formatspec time = f"{h:0>2.0f}:{m:0>2.0f}:{s:0>5.2f}" log.normal("total time elapsed " + time) report["time_taken"] = time report["status"] = "success" if return_code == 0 else "failure" except Exception: log.error("failed to run config: ") trace = traceback.format_exc() log.normal("traceback:\n\n" + trace) return_code = -1 sys.stdout.flush() if (confply.config.post_run and inspect.isfunction(confply.config.post_run)): try: log.linebreak() log.normal("running post run script: " + confply.config.post_run.__name__) sys.stdout.flush() exec(confply.config.post_run.__code__, config_locals, {}) log.linebreak() except Exception: log.error("failed to exec " + confply.config.post_run.__name__) trace = traceback.format_exc() log.normal("traceback:\n\n" + trace) log.normal("date: " + str(datetime.now())) log.linebreak() sys.stdout.flush() if sys.stdout != old_stdout: sys.stdout.close() sys.stdout = old_stdout if confply.config.log_echo_file: with open(confply.config.log_file) as out_log: log_str = out_log.read() log_str = log_str.split("confply logging to " + confply.config.log_file)[1] log.normal("wrote:" + log_str) if (confply.config.mail_send == report["status"] or confply.config.mail_send == "all"): mail.host = confply.config.__mail_host mail.sender = confply.config.mail_from mail.recipients = confply.config.mail_to mail.login = confply.config.__mail_login mail.attachments = confply.config.mail_attachments if (confply.config.log_file and report["status"] == "failure"): mail.attachments.append( os.path.abspath(confply.config.log_file)) pass if mail.login: mail.send_report(report) if (confply.config.slack_send == report["status"] or confply.config.slack_send == "all"): slack.bot_token = confply.config.__slack_bot_token slack.uploads = confply.config.slack_uploads if (confply.config.log_file and report["status"] == "failure"): slack.uploads.append( os.path.abspath(confply.config.log_file)) if slack.bot_token: slack.send_report(report) clean_modules() return return_code
def generate(): object_path = config.object_path object_path = os.path.join(object_path, tool) def gen_command(config, source=None): command = "" command += tool + " " + config.command_prepend + " " command += include + " " + (" " + include + " ").join( config.include_paths) + " " if config.include_paths else "" command += define + " " + (" " + define + " ").join( config.defines) + " " if config.defines else "" command += debug + " " if config.debug_info else "" command += standard + config.standard + " " if config.standard else "" command += gen_warnings() command += optimisation + str( config.optimisation) + " " if config.optimisation else "" if source is None: command += " ".join( config.source_files) + " " if config.source_files else "" command += output_exe + config.output_file + " " if config.output_file else output_exe + "app.bin" command += pass_to_linker + " " command += library + (" " + library).join( config.library_paths) + " " if config.library_paths else "" command += link + " " + (" " + link + " ").join( config.link_libraries) + " " if config.link_libraries else "" else: command += build_object + " " + source + " " + output_obj + os.path.join( object_path, os.path.basename(source) + object_ext + " ") command += exception_handling + " " if config.track_dependencies: command += dependencies + " " + dependencies_output + " " + os.path.join( object_path, os.path.basename(source) + ".d ") return command + " " + config.command_append if config.build_objects: os.makedirs(object_path, exist_ok=True) commands = [] sources = config.source_files objects = [] output_time = config.output_file if config.output_file else "app.bin" output_time = os.path.getmtime(output_time).real if os.path.exists( output_time) else 0 should_link = False tracking_md5 = config.track_checksums tracking_depends = config.track_dependencies config_name = config.confply.config_name if os.path.exists( config.confply.config_name) else None # generate checksums def md5(fname): hash_md5 = hashlib.md5() with open(fname, "rb") as f: for chunk in iter(lambda: f.read(4096), b""): hash_md5.update(chunk) return hash_md5.hexdigest() tracking = {} tracking_path = os.path.join(object_path, "tracking.py") if tracking_md5 or tracking_depends: if os.path.exists(tracking_path): with open(tracking_path, "r") as tracking_file: tracking = ast.literal_eval(tracking_file.read()) def update_tracking(file_path): nonlocal tracking if os.path.exists(file_path): file_time = os.path.getmtime(file_path).real if file_path in tracking: if file_time > tracking[file_path]["t"]: if tracking_md5: old_md5 = tracking[file_path][ "h"] if "h" in tracking[file_path] else None new_md5 = md5(file_path) tracking[file_path].update({ "t": file_time, "h": new_md5 }) return old_md5 != new_md5 else: tracking[file_path].update({"t": file_time}) return True else: return False elif tracking_md5: tracking[file_path] = {"t": file_time, "h": md5(file_path)} return True else: tracking[file_path] = {"t": file_time} return True pass return False compile_all = update_tracking( config_name) if config.rebuild_on_change else False for source_path in sources: should_compile = compile_all if os.path.exists(source_path): deps_path = os.path.join(object_path, os.path.basename(source_path + ".d")) obj_path = os.path.join( object_path, os.path.basename(source_path + object_ext)) obj_time = os.path.getmtime(obj_path).real if os.path.exists( obj_path) else 0 objects.append(obj_path) # dependency tracking if tracking_depends and os.path.exists(deps_path): with open(deps_path, "r") as deps_file: deps_string = deps_file.read() for dep_path in parse_deps(deps_string): should_compile = update_tracking( dep_path) or should_compile else: should_compile = update_tracking( source_path) or should_compile if should_compile or obj_time == 0: commands.append(gen_command(config, source_path)) should_link = True elif obj_time > output_time: should_link = True else: log.warning(source_path + " could not be found") if should_link and config.output_executable: config.source_files = objects commands.append(gen_command(config)) config.source_files = sources num_commands = len(commands) log.normal(str(num_commands) + " files to compile") else: log.normal("no files to compile") if tracking_md5 or tracking_depends: with open(tracking_path, "w") as tracking_file: tracking_file.write("# do not edit this file.\n") tracking_file.write(json.dumps(tracking, indent=4)) return commands else: return gen_command(config)
aliases = {comment} # "all" will run all of the aliases aliases["all"] = " -- ".join([val for key, val in aliases.items()]) if __name__ == "__main__": if "--listen" in sys.argv: return run_commandline(["--listen", __file__]) else: dir_name = os.path.dirname(__file__) if not dir_name == "": os.chdir(dir_name) launcher(sys.argv[1:], aliases) """ __new_config_str = """#!{confply_dir}/confply.py --in # generated using: # python {confply_dir}/confply.py --config {config_type_arg} {config_file} import sys sys.path.append('{confply_dir}') import confply.{config_type_arg}.config as config import confply.{config_type_arg}.options as options import confply.log as log config_hash = '{config_hash}' ############# modify_below ################ config.confply.log_topic = "{config_type_arg}" log.normal("loading {config_file} with confply_args: "+str(config.confply.args)) """ # list of configs that have already been run __configs_run = set()