# Настройки и интерфейс prepare_identity. Комментарии и исходный текст в UTF-8. # Продолжает текущий вызов, список или выражение: """Generate build identity using the release version from firmware_version.h."""; порядок аргументов сохраняется. """Generate build identity using the release version from firmware_version.h.""" # Подключает модуль argparse для этой проверки или подготовки сборки. import argparse # Подключает модуль importlib.util для этой проверки или подготовки сборки. import importlib.util # Подключает модуль json для этой проверки или подготовки сборки. import json # Импортирует необходимые имена: from pathlib import Path. from pathlib import Path # Подключает модуль re для этой проверки или подготовки сборки. import re # Подключает модуль tempfile для этой проверки или подготовки сборки. import tempfile # Объявляет функцию read_version и её входные параметры. def read_version(header): # Читает исходный файл или образ для последующей проверки и разбора. text = header.read_text(encoding="utf-8-sig") # Задаёт text по выражению справа; значение используется последующими операциями. text = re.sub(r"/\*.*?\*/|//[^\n]*", "", text, flags=re.S) # Задаёт version по выражению справа; значение используется последующими операциями. version = [] # Перебирает part, limit in (("MAJOR", 255), ("MINOR", 255), ("PATCH", 999)) в указанном порядке. for part, limit in (("MAJOR", 255), ("MINOR", 255), ("PATCH", 999)): # Задаёт name по выражению справа; значение используется последующими операциями. name = "FIRMWARE_VERSION_" + part # Задаёт definitions по выражению справа; значение используется последующими операциями. definitions = re.findall(r"^\s*#\s*define\s+" + name + r"\b([^\n]*)", text, re.M) # Проверяет условие if len(definitions) != 1 перед выполнением следующего блока. if len(definitions) != 1: # Останавливает текущую операцию с исключением ValueError(f"{header}: define {name} exactly once"). raise ValueError(f"{header}: define {name} exactly once") # Задаёт value по выражению справа; значение используется последующими операциями. value = definitions[0].strip() # Проверяет условие if not re.fullmatch(r"(?:0|[1-9][0-9]*)[uU]?", value) перед выполнением следующего блока. if not re.fullmatch(r"(?:0|[1-9][0-9]*)[uU]?", value): # Останавливает текущую операцию с исключением ValueError(f"{header}: {name} must be a decimal integer"). raise ValueError(f"{header}: {name} must be a decimal integer") # Задаёт number по выражению справа; значение используется последующими операциями. number = int(value.rstrip("uU")) # Проверяет условие if not 0 <= number <= limit перед выполнением следующего блока. if not 0 <= number <= limit: # Останавливает текущую операцию с исключением ValueError(f"{header}: {name} must be in 0..{limit}"). raise ValueError(f"{header}: {name} must be in 0..{limit}") # Продолжает текущий вызов, список или выражение: version.append(number); порядок аргументов сохраняется. version.append(number) # Возвращает version вызывающему коду и завершает функцию. return version # Объявляет функцию prepare и её входные параметры. def prepare(project, templates, output): # Читает исходный файл или образ для последующей проверки и разбора. settings = json.loads((project / "firmware_identity.json").read_text(encoding="utf-8-sig")) # Задаёт settings["version"] по выражению справа; значение используется последующими операциями. settings["version"] = read_version(project / "firmware_version.h") # Задаёт spec по выражению справа; значение используется последующими операциями. spec = importlib.util.spec_from_file_location("shared_firmware_identity", templates / "c/firmware-info/tools/identity.py") # Задаёт identity по выражению справа; значение используется последующими операциями. identity = importlib.util.module_from_spec(spec) # Продолжает текущий вызов, список или выражение: spec.loader.exec_module(identity); порядок аргументов сохраняется. spec.loader.exec_module(identity) # The source header owns SemVer; retain the shared generator and manifest format. # Открывает управляемый ресурс tempfile.TemporaryDirectory(prefix="balsam-identity-") as directory с автоматическим освобождением. with tempfile.TemporaryDirectory(prefix="balsam-identity-") as directory: # Задаёт config по выражению справа; значение используется последующими операциями. config = Path(directory) / "config.json" # Сохраняет подготовленный файл в указанном формате для следующего шага. config.write_text(json.dumps(settings), encoding="utf-8") # Возвращает identity.prepare(project, templates, config, output) вызывающему коду и завершает функцию. return identity.prepare(project, templates, config, output) # Проверяет условие if __name__ == "__main__" перед выполнением следующего блока. if __name__ == "__main__": # Задаёт parser по выражению справа; значение используется последующими операциями. parser = argparse.ArgumentParser(description=__doc__) # Задаёт parser.add_argument("--project", type по выражению справа; значение используется последующими операциями. parser.add_argument("--project", type=Path, required=True) # Задаёт parser.add_argument("--templates", type по выражению справа; значение используется последующими операциями. parser.add_argument("--templates", type=Path, required=True) # Задаёт parser.add_argument("--output", type по выражению справа; значение используется последующими операциями. parser.add_argument("--output", type=Path, required=True) # Задаёт args по выражению справа; значение используется последующими операциями. args = parser.parse_args() # Продолжает текущий вызов, список или выражение: print(json.dumps(prepare(args.project, args.templates, args.output))); порядок аргументов сохраняется. print(json.dumps(prepare(args.project, args.templates, args.output)))