1 | """Сборщик Python программ в исполняемые файлы Windows.""" |
||
2 | # pylint: disable=import-error |
||
3 | # Какой-то баг линтера гитхаб с yaml |
||
4 | from __future__ import annotations |
||
5 | import zipfile |
||
6 | from os import path |
||
7 | from os import system as run_cmd |
||
8 | from sys import argv |
||
9 | from sys import exit as sysexit |
||
10 | from datetime import datetime |
||
11 | from yaml import full_load as loadyaml |
||
12 | from functions.log import Log |
||
13 | # from functions.man import manpage as man |
||
14 | |||
15 | class BuildConfig: |
||
16 | """Класс динамической конфигурации |
||
17 | """ |
||
18 | # pylint: disable=too-many-instance-attributes |
||
19 | # Может быть будет оптимизировано. А может и нет. |
||
20 | def __init__(self, build_config: str = 'configs/config.yaml') -> None: |
||
21 | # Тут нужно точно сделать оптимальнее |
||
22 | config_file = f'./configs/{build_config}.yaml' |
||
23 | try: |
||
24 | with open(config_file, 'r', encoding = 'utf8') as build_config_file: |
||
25 | self.config_file = loadyaml(build_config_file) |
||
26 | except FileNotFoundError: |
||
27 | sysexit(f'[Err.:6] Файл не найден или не указан: {config_file}') |
||
28 | # Cекция source конфига |
||
29 | # Есть идея, но потом (забить на переменные для каждой секции конфига.) |
||
30 | self.mainfile, self.outfile, self.workdir = [ |
||
31 | self.config_file['source']['mainfile'], |
||
32 | self.config_file['source']['outputfile'], |
||
33 | self.config_file['source']['workdir'] |
||
34 | ] |
||
35 | # Пригодится в будущем |
||
36 | # self.source = self.config_file['source'] |
||
37 | # self.main = self.config_file['main'] |
||
38 | # Секция main конфига |
||
39 | self.version, self.author, self.authorlink = [ |
||
40 | self.config_file['main']['version'], |
||
41 | self.config_file['main']['author'], |
||
42 | self.config_file['main']['authorlink'] |
||
43 | ] |
||
44 | # Прочие детали конфига |
||
45 | self.plugins = self.config_file['plugins'] |
||
46 | self.params: list = self.config_file['params'] |
||
47 | self.addition_files = self.config_file['files'] |
||
48 | self.product_name: str = self.config_file['main']['product_name'] |
||
49 | # Запуск сборки |
||
50 | def build(self) -> None: |
||
51 | """Непосредственный вызов Nuitka |
||
52 | """ |
||
53 | parameters_string = '--' + ' --'.join(self.params) |
||
54 | run_cmd(f'nuitka {parameters_string} \ |
||
55 | --plugin-enable={self.plugins}\ |
||
56 | --windows-icon-from-ico={self.config_file["main"]["icon"]}\ |
||
57 | {self.config_file["source"]["mainfile"]}') |
||
58 | # --include-data-files=../ui/imgs/*.png=imgs/\ |
||
59 | # Вывод параметров конфига, в случае необходимости |
||
60 | def outprint(self) -> None: |
||
61 | """Вывод параметров конфига |
||
62 | """ |
||
63 | today = datetime.today().strftime('%d-%m-%Y %H:%M:%S') |
||
64 | print('-' * 10, f'\n{today}\n', '-' * 10) |
||
65 | print('Config directives\n' |
||
66 | # Секция предварительных настроек |
||
67 | f'WorkDir: {self.workdir}\n' |
||
68 | f'Main File: {self.mainfile}\n' |
||
69 | f'Output File: {self.outfile}\n' |
||
70 | # Секция продукта / сборки |
||
71 | '\nProdut info\n' |
||
72 | f'Version: {self.version}\n' |
||
73 | f'Author: {self.author}\n' |
||
74 | f'Authorlink: {self.authorlink}\n' |
||
75 | f'Product Name: {self.product_name}\n' |
||
76 | # Секция плагинов |
||
77 | '\nBuild Plugins\n' |
||
78 | f'Plugins: {self.plugins}' |
||
79 | # Секция параметров |
||
80 | '\nBuild parameters\n' |
||
81 | 'Parameters:\n' |
||
82 | ) |
||
83 | for parameter in self.params: |
||
84 | print(parameter) |
||
85 | print('-' * 10) |
||
86 | # Упаковка сборки в архив |
||
87 | def zip_output(self) -> None: |
||
88 | """Упаковывает собранные файлы в архив |
||
89 | """ |
||
90 | with zipfile.ZipFile(f'{self.product_name}.zip', |
||
91 | 'w', |
||
92 | compression=zipfile.ZIP_DEFLATED, |
||
93 | compresslevel=9) as zip_arch: |
||
94 | zip_arch.write(self.outfile) |
||
95 | if len(self.addition_files) > 0: # Протестировать бы, но потом... |
||
96 | for other_files in self.addition_files: |
||
97 | secondpth = f'{path.basename(path.dirname(path.abspath(other_files)))}\ |
||
98 | /{path.basename(other_files)}' |
||
99 | zip_arch.write(path.abspath(other_files), secondpth) |
||
100 | zip_arch.close() # Но это не точно... |
||
101 | # Получение основного конфига (не тестировалось) |
||
102 | def get_core_config() -> dict: |
||
103 | """Читает конфиг сборщка |
||
104 | |||
105 | Returns: |
||
106 | dict: Словарь с параметрами конфигурации сборщика |
||
107 | """ |
||
108 | try: |
||
109 | with open('configs/core.yaml', 'r', encoding = 'utf8') as core_c: |
||
110 | core_config = loadyaml(core_c) |
||
111 | except FileNotFoundError: |
||
112 | print('File not found') |
||
113 | sysexit(6) # Потому что 42, вот почему! |
||
114 | return core_config |
||
0 ignored issues
–
show
introduced
by
![]() |
|||
115 | # Получение конфига сборки (не тестировалось) |
||
116 | def get_build_config(conf_path: str) -> dict | None: |
||
117 | """Читает конфиг сборки |
||
118 | |||
119 | Args: |
||
120 | conf_path (str): Путь до персонализированного конфига сборки |
||
121 | |||
122 | Returns: |
||
123 | dict: Словарь с параметрами конфигурации определенной сборки |
||
124 | """ |
||
125 | if conf_path == '': |
||
126 | print( |
||
127 | 'Не указан файл конфигурации сборки.\n' |
||
128 | f'Необходимо запускать "{argv[0]} <путь до конфига>"') |
||
129 | else: |
||
130 | with open(conf_path, 'r', encoding = 'utf8') as build_conf_yaml: |
||
131 | build_config = loadyaml(build_conf_yaml) |
||
132 | return build_config |
||
133 | return None |
||
134 | # Запуск алгоритма сборки |
||
135 | def build_start(config_input: str) -> None: |
||
136 | """Запуск алгоритма сборки |
||
137 | |||
138 | Args: |
||
139 | config_input (str): Путь к файлу конфига сборки |
||
140 | """ |
||
141 | config = BuildConfig(config_input) |
||
142 | config.outprint() |
||
143 | config.build() |
||
144 | log = Log(config_input) |
||
145 | log.start() |
||
146 | log.write(f'Используется основной файл: {config.config_file["source"]["mainfile"]}') |
||
147 | # Запуск скрипта |
||
148 | if __name__ == '__main__': |
||
149 | # man() |
||
150 | try: |
||
151 | build_start(argv[1]) |
||
152 | except IndexError: |
||
153 | print('Конфиг сборки не указан') |
||
154 | set_build_config = input('Укажите файл сборки конфига: ') |
||
155 | if set_build_config is None or set_build_config == '': |
||
156 | print('Конфиг сборки не указан') |
||
157 | else: |
||
158 | build_start(set_build_config) |
||
159 | ### Старые наработки, они будут понемногу переноситься в основной код, |
||
160 | ### Но в нормальном виде. После переноса и тестирования они должны быть удалены. |
||
161 | # |
||
162 | # |
||
163 | # def zipOutput(): |
||
164 | # """Упаковывает готовый файл в архив. |
||
165 | # """ |
||
166 | # with zipfile.ZipFile('SimpleTester.zip', 'w', |
||
167 | # compression=zipfile.ZIP_DEFLATED, |
||
168 | # compresslevel=9) as zipArch: |
||
169 | # zipArch.write('SimpleTester.exe') |
||
170 | # |
||
171 | # def makeParamStr(getconfig) -> str: |
||
172 | # """Создает строку параметров сборки из данных конфига. |
||
173 | # |
||
174 | # Args: |
||
175 | # getconfig (function): Принимает на вход функцию парсинга конфига |
||
176 | # |
||
177 | # Returns: |
||
178 | # str: Строка с набором параметров сборки |
||
179 | # """ |
||
180 | # paramList = [] |
||
181 | # for param in getconfig['params']: |
||
182 | # if param == 'windows-product-version': |
||
183 | # param = '{}="{}"'.format(param, getconfig['main']['version']) |
||
184 | # paramList.append(param) |
||
185 | # paramStr = '--' + ' --'.join(paramList) |
||
186 | # return paramStr |
||
187 | # |
||
188 | # if __name__ == '__main__': |
||
189 | # """Запуск сборки бинарного файла и его упаковка в архив. |
||
190 | # """ |
||
191 | # paramsStr = makeParamStr(getconfig()) |
||
192 | # plugins = getconfig()['plugins'] |
||
193 | # icon = getconfig()['main']['icon'] |
||
194 | # runCommand( |
||
195 | # f'nuitka {paramsStr} \ |
||
196 | # --plugin-enable={plugins}\ |
||
197 | # --windows-icon-from-ico={icon}\ |
||
198 | # --include-data-files=../ui/imgs/*.png=imgs/\ |
||
199 | # ../SimpleTester.py') |
||
200 | # zipOutput() |
||
201 |