cppmake 127.0.14__py3-none-any.whl → 127.0.16__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. cppmake-127.0.16.dist-info/METADATA +191 -0
  2. cppmake-127.0.16.dist-info/RECORD +55 -0
  3. {cppmake-127.0.14.dist-info → cppmake-127.0.16.dist-info}/WHEEL +1 -1
  4. cppmake-127.0.16.dist-info/entry_points.txt +3 -0
  5. cppmake-127.0.16.dist-info/top_level.txt +3 -0
  6. cppmaked.py +2 -0
  7. cppmakelib/__init__.py +32 -0
  8. cppmakelib/basic/config.py +50 -0
  9. cppmakelib/basic/context.py +39 -0
  10. cppmakelib/builder/cmake.py +71 -0
  11. cppmakelib/builder/include.py +15 -0
  12. cppmakelib/builder/makefile.py +68 -0
  13. cppmakelib/compiler/all.py +26 -0
  14. cppmakelib/compiler/clang.py +196 -0
  15. cppmakelib/compiler/emcc.py +79 -0
  16. cppmakelib/compiler/gcc.py +237 -0
  17. cppmakelib/compiler/msvc.py +24 -0
  18. cppmakelib/error/config.py +5 -0
  19. cppmakelib/error/logic.py +6 -0
  20. cppmakelib/error/subprocess.py +9 -0
  21. cppmakelib/executor/operation.py +15 -0
  22. cppmakelib/executor/run.py +84 -0
  23. cppmakelib/executor/scheduler.py +91 -0
  24. cppmakelib/logger/compile_commands.py +30 -0
  25. cppmakelib/logger/make_progress.py +0 -0
  26. cppmakelib/logger/module_mapper.py +6 -0
  27. cppmakelib/logger/unit_status.py +224 -0
  28. cppmakelib/system/all.py +25 -0
  29. cppmakelib/system/linux.py +26 -0
  30. cppmakelib/system/macos.py +26 -0
  31. cppmakelib/system/windows.py +26 -0
  32. cppmakelib/unit/binary.py +26 -0
  33. cppmakelib/unit/code.py +62 -0
  34. cppmakelib/unit/dynamic.py +12 -0
  35. cppmakelib/unit/executable.py +35 -0
  36. cppmakelib/unit/header.py +63 -0
  37. cppmakelib/unit/module.py +69 -0
  38. cppmakelib/unit/object.py +71 -0
  39. cppmakelib/unit/package.py +87 -0
  40. cppmakelib/unit/precompiled.py +6 -0
  41. cppmakelib/unit/preparsed.py +6 -0
  42. cppmakelib/unit/preprocessed.py +3 -0
  43. cppmakelib/unit/source.py +64 -0
  44. cppmakelib/utility/algorithm.py +44 -0
  45. cppmakelib/utility/color.py +14 -0
  46. cppmakelib/utility/decorator.py +120 -0
  47. cppmakelib/utility/filesystem.py +71 -0
  48. cppmakelib/utility/import_.py +21 -0
  49. cppmakelib/utility/remote/client.py +2 -0
  50. cppmakelib/utility/remote/protocol.py +32 -0
  51. cppmakelib/utility/remote/remote.py +43 -0
  52. cppmakelib/utility/remote/server.py +0 -0
  53. cppmakelib/utility/time.py +1 -0
  54. cppmakelib/utility/version.py +65 -0
  55. cppmake-127.0.14.dist-info/METADATA +0 -9
  56. cppmake-127.0.14.dist-info/RECORD +0 -6
  57. cppmake-127.0.14.dist-info/entry_points.txt +0 -2
  58. cppmake-127.0.14.dist-info/top_level.txt +0 -1
  59. /cppmake/__main__.py → /cppmake.py +0 -0
@@ -0,0 +1,196 @@
1
+ from cppmakelib.basic.config import config
2
+ from cppmakelib.compiler.gcc import Gcc
3
+ from cppmakelib.error.config import ConfigError
4
+ from cppmakelib.error.subprocess import SubprocessError
5
+ from cppmakelib.executor.run import async_run
6
+ from cppmakelib.utility.decorator import member, syncable, unique
7
+ from cppmakelib.utility.filesystem import create_dir, exist_file, parent_dir, path
8
+ from cppmakelib.utility.version import Version
9
+
10
+ class Clang(Gcc):
11
+ def __init__ (self, file: path = 'clang++') -> None: ...
12
+ async def __ainit__ (self, file: path = 'clang++') -> None: ...
13
+ def precompile(self, module_file: path, precompiled_file: path, object_file: path, compile_flags: list[str] = [], define_macros: dict[str, str] = {}, include_dirs: list[path] = [], import_dirs: list[path] = [], diagnostic_file: path | None = None) -> None: ...
14
+ async def async_precompile(self, module_file: path, precompiled_file: path, object_file: path, compile_flags: list[str] = [], define_macros: dict[str, str] = {}, include_dirs: list[path] = [], import_dirs: list[path] = [], diagnostic_file: path | None = None) -> None: ...
15
+ def preparse (self, header_file: path, preparsed_file : path, compile_flags: list[str] = [], define_macros: dict[str, str] = {}, include_dirs: list[path] = [], diagnostic_file: path | None = None) -> None: ...
16
+ async def async_preparse (self, header_file: path, preparsed_file : path, compile_flags: list[str] = [], define_macros: dict[str, str] = {}, include_dirs: list[path] = [], diagnostic_file: path | None = None) -> None: ...
17
+ def compile (self, source_file: path, object_file : path, compile_flags: list[str] = [], define_macros: dict[str, str] = {}, include_dirs: list[path] = [], import_dirs: list[path] = [], diagnostic_file: path | None = None) -> None: ...
18
+ async def async_compile (self, source_file: path, object_file : path, compile_flags: list[str] = [], define_macros: dict[str, str] = {}, include_dirs: list[path] = [], import_dirs: list[path] = [], diagnostic_file: path | None = None) -> None: ...
19
+ preparsed_suffix : str = '.pch'
20
+ precompiled_suffix: str = '.pcm'
21
+ file : path
22
+ version : Version
23
+ stdlib_name : str
24
+ stdlib_module_file: path
25
+ stdlib_static_file: path
26
+ stdlib_shared_file: path
27
+ compile_flags : list[str]
28
+ link_flags : list[str]
29
+ define_macros : dict[str, str]
30
+
31
+ async def _async_get_version (self) -> Version: ...
32
+ async def _async_get_stdlib_name (self) -> str: ...
33
+ async def _async_get_stdlib_module_file(self) -> path : ...
34
+
35
+
36
+
37
+ @member(Clang)
38
+ @syncable
39
+ @unique
40
+ async def __ainit__(
41
+ self: Clang,
42
+ file: path = 'clang++'
43
+ ) -> None:
44
+ self.file = file
45
+ self.version = await self._async_get_version()
46
+ self.stdlib_name = await self._async_get_stdlib_name()
47
+ self.stdlib_module_file = await self._async_get_stdlib_module_file()
48
+ self.compile_flags = [
49
+ f'-std={config.std}',
50
+ f'-stdlib={self.stdlib_name}',
51
+ *(['-O0', '-g'] if config.type == 'debug' else
52
+ ['-O3'] if config.type == 'release' else
53
+ ['-Os'] if config.type == 'size' else
54
+ [])
55
+ ]
56
+ self.link_flags = [
57
+ *(['-s'] if config.type == 'release' or config.type == 'size' else []),
58
+ *(['-lstdc++exp'] if self.stdlib_name == 'libstdc++' else [])
59
+ ]
60
+ self.define_macros = {
61
+ **({'DEBUG' : 'true'} if config.type == 'debug' else
62
+ {'NDEBUG': 'true'} if config.type == 'release' else
63
+ {})
64
+ }
65
+
66
+ @member(Clang)
67
+ @syncable
68
+ async def async_precompile(
69
+ self : Clang,
70
+ module_file : path,
71
+ precompiled_file: path,
72
+ object_file : path,
73
+ compile_flags : list[str] = [],
74
+ define_macros : dict[str, str] = {},
75
+ include_dirs : list[path] = [],
76
+ import_dirs : list[path] = [],
77
+ diagnostic_file : path | None = None
78
+ ) -> None:
79
+ create_dir(parent_dir(precompiled_file))
80
+ create_dir(parent_dir(object_file))
81
+ await async_run(
82
+ file=self.file,
83
+ args=[
84
+ *(self.compile_flags + compile_flags),
85
+ *[f'-D{key}={value}' for key, value in (self.define_macros | define_macros).items()],
86
+ *[f'-I{include_dir}' for include_dir in include_dirs],
87
+ *[f'-fprebuilt-module-path={import_dir}' for import_dir in import_dirs],
88
+ '--precompile', '-x', 'c++-module', module_file,
89
+ '-o', precompiled_file
90
+ ]
91
+ )
92
+ await async_run(
93
+ file=self.file,
94
+ args=[
95
+ *[f'-fprebuilt-module-path={import_dir}' for import_dir in import_dirs],
96
+ '-c', module_file,
97
+ '-o', object_file
98
+ ]
99
+ )
100
+
101
+ @member(Gcc)
102
+ @syncable
103
+ async def async_preparse(
104
+ self : Gcc,
105
+ header_file : path,
106
+ preparsed_file : path,
107
+ compile_flags : list[str] = [],
108
+ define_macros : dict[str, str] = {},
109
+ include_dirs : list[path] = [],
110
+ diagnostic_file: path | None = None
111
+ ) -> None:
112
+ create_dir(parent_dir(preparsed_file))
113
+ await async_run(
114
+ file=self.file,
115
+ args=[
116
+ *(self.compile_flags + compile_flags),
117
+ *[f'-D{key}={value}' for key, value in (self.define_macros | define_macros).items()],
118
+ *[f'-I{include_dir}' for include_dir in include_dirs],
119
+ '-c', '-x', 'c++-header', header_file,
120
+ '-o', preparsed_file
121
+ ]
122
+ )
123
+
124
+ @member(Clang)
125
+ @syncable
126
+ async def async_compile(
127
+ self: Clang,
128
+ source_file : path,
129
+ object_file : path,
130
+ compile_flags : list[str] = [],
131
+ define_macros : dict[str, str] = {},
132
+ include_dirs : list[path] = [],
133
+ import_dirs : list[path] = [],
134
+ diagnostic_file: path | None = None
135
+ ) -> None:
136
+ create_dir(parent_dir(object_file))
137
+ await async_run(
138
+ file=self.file,
139
+ args=[
140
+ *(self.compile_flags + compile_flags),
141
+ *[f'-D{key}={value}' for key, value in (self.define_macros | define_macros).items()],
142
+ *[f'-I{include_dir}' for include_dir in include_dirs],
143
+ *[f'-fprebuilt-module-path={import_dir}' for import_dir in import_dirs],
144
+ '-c', '-x', 'c++', source_file,
145
+ '-o', object_file
146
+ ]
147
+ )
148
+
149
+ @member(Clang)
150
+ async def _async_get_version(self: Clang) -> Version:
151
+ try:
152
+ stdout = await async_run(
153
+ file=self.file,
154
+ args=['--version'],
155
+ return_stdout=True
156
+ )
157
+ except SubprocessError as error:
158
+ raise ConfigError(f'clang check failed (with file = {self.file})') from error
159
+ if not stdout.startswith('clang') and 'clang version' not in stdout:
160
+ raise ConfigError(f'clang check failed (with file = {self.file}, subprocess = "{self.file} --version", stdout = "{stdout.splitlines()[0]} ...", requires = "clang++ ..." or "... clang version ...")')
161
+ version = Version.parse(stdout)
162
+ if version < 21:
163
+ raise ConfigError(f'clang version is too old (with file = {self.file}, version = {version}, requires = 21+)')
164
+ return version
165
+
166
+ async def _async_get_stdlib_name(self: Clang) -> str:
167
+ stderr = await async_run(
168
+ file=self.file,
169
+ args=[
170
+ *self.compile_flags,
171
+ '-v',
172
+ ],
173
+ return_stderr=True
174
+ )
175
+ if 'selected gcc installation' in stderr.lower():
176
+ return 'libstdc++'
177
+ else:
178
+ return 'libc++'
179
+
180
+ async def _async_get_stdlib_module_file(self: Clang) -> path:
181
+ if self.stdlib_name == 'libc++':
182
+ resource_dir = await async_run(
183
+ file=self.file,
184
+ args=['--print-resource-dir'],
185
+ return_stdout=True,
186
+ )
187
+ resource_dir = path(resource_dir.strip())
188
+ search_file = f'{resource_dir}/../../../share/libc++/v1/std.cppm'
189
+ if exist_file(search_file):
190
+ return search_file
191
+ else:
192
+ raise ConfigError(f'libc++ module_file is not found (with search_file = {search_file})')
193
+ elif self.stdlib_name == 'libstdc++':
194
+ return await Gcc._async_get_stdlib_module_file(self)
195
+ else:
196
+ assert False
@@ -0,0 +1,79 @@
1
+ from cppmakelib.basic.config import config
2
+ from cppmakelib.compiler.clang import Clang
3
+ from cppmakelib.error.config import ConfigError
4
+ from cppmakelib.error.subprocess import SubprocessError
5
+ from cppmakelib.executor.run import async_run
6
+ from cppmakelib.utility.decorator import member, syncable, unique
7
+ from cppmakelib.utility.filesystem import path
8
+ from cppmakelib.utility.version import Version
9
+
10
+ class Emcc(Clang):
11
+ def __init__(self, file: path = 'em++') -> None: ...
12
+ async def __ainit__(self, file: path = 'em++') -> None: ...
13
+ file : path
14
+ version : Version
15
+ stdlib_name : str = 'libc++'
16
+ stdlib_module_file: path
17
+ stdlib_static_file: path
18
+ stdlib_shared_file: path
19
+ compile_flags : list[str]
20
+ link_flags : list[str]
21
+ define_macros : dict[str, str]
22
+
23
+ async def _async_get_version (self) -> Version: ...
24
+ async def _async_get_stdlib_name(self) -> str : ...
25
+
26
+ @member(Emcc)
27
+ @syncable
28
+ @unique
29
+ async def __ainit__(
30
+ self: Emcc,
31
+ file: path = 'em++'
32
+ ) -> None:
33
+ self.file = file
34
+ self.version = await self._async_get_version()
35
+ self.stdlib_name = 'libc++'
36
+ self.stdlib_module_file = await self._async_get_stdlib_module_file()
37
+ self.compile_flags = [
38
+ f'-std={config.std}', '-fexceptions',
39
+ *(['-O0', '-g'] if config.type == 'debug' else
40
+ ['-O3'] if config.type == 'release' else
41
+ ['-Os'] if config.type == 'size' else
42
+ [])
43
+ ]
44
+ self.link_flags = [
45
+ *(['-s'] if config.type == 'release' or config.type == 'size' else []),
46
+ ]
47
+ self.define_macros = {
48
+ **({'DEBUG' : 'true'} if config.type == 'debug' else
49
+ {'NDEBUG': 'true'} if config.type == 'release' else
50
+ {})
51
+ }
52
+
53
+ @member(Emcc)
54
+ async def _async_get_version(self: Emcc) -> Version:
55
+ try:
56
+ stdout = await async_run(
57
+ file=self.file,
58
+ args=['--version'],
59
+ return_stdout=True
60
+ )
61
+ except SubprocessError as error:
62
+ raise ConfigError(f'emcc check failed (with file = {self.file})') from error
63
+ if not stdout.startswith('em++'):
64
+ raise ConfigError(f'emcc check failed (with file = {self.file}, subprocess = "{self.file} --version", stdout = "{stdout.splitlines()[0]} ...", requires = "em++ ...")')
65
+ version = Version.parse(stdout)
66
+ if version < 4:
67
+ raise ConfigError(f'emcc version is too old (with file = {self.file}, version = {version}, requires = 4+)')
68
+ return version
69
+
70
+ @member(Emcc)
71
+ async def _async_get_stdlib_module_file(self: Emcc) -> path:
72
+ clang_stdlib_name = await Clang._async_get_stdlib_name(self)
73
+ if clang_stdlib_name == 'libc++':
74
+ try:
75
+ return await Clang._async_get_stdlib_module_file(self)
76
+ except ConfigError as error:
77
+ raise ConfigError(f'libc++ module_file is not found (with file = {self.file}, subcompiler = clang++)') from error
78
+ else:
79
+ raise ConfigError(f'libc++ module_file is not found (with file = {self.file}, clang_stdlib_name = {clang_stdlib_name})')
@@ -0,0 +1,237 @@
1
+ from cppmakelib.basic.config import config
2
+ from cppmakelib.error.config import ConfigError
3
+ from cppmakelib.error.subprocess import SubprocessError
4
+ from cppmakelib.executor.run import async_run
5
+ from cppmakelib.logger.module_mapper import module_mapper_logger
6
+ from cppmakelib.utility.decorator import member, syncable, unique
7
+ from cppmakelib.utility.filesystem import create_dir, exist_file, parent_dir, path
8
+ from cppmakelib.utility.version import Version
9
+ import re
10
+
11
+ class Gcc:
12
+ def __init__ (self, file: path = 'g++') -> None: ...
13
+ async def __ainit__ (self, file: path = 'g++') -> None: ...
14
+ def preprocess(self, code_file : path, preprocessed_file: path, compile_flags: list[str] = [], define_macros: dict[str, str] = {}, include_dirs: list[path] = []) -> None: ...
15
+ async def async_preprocess(self, code_file : path, preprocessed_file: path, compile_flags: list[str] = [], define_macros: dict[str, str] = {}, include_dirs: list[path] = []) -> None: ...
16
+ def preparse (self, header_file: path, preparsed_file : path, compile_flags: list[str] = [], define_macros: dict[str, str] = {}, include_dirs: list[path] = [], diagnostic_file: path | None = None) -> None: ...
17
+ async def async_preparse (self, header_file: path, preparsed_file : path, compile_flags: list[str] = [], define_macros: dict[str, str] = {}, include_dirs: list[path] = [], diagnostic_file: path | None = None) -> None: ...
18
+ def precompile(self, module_file: path, precompiled_file : path, object_file: path, compile_flags: list[str] = [], define_macros: dict[str, str] = {}, include_dirs: list[path] = [], import_dirs: list[path] = [], diagnostic_file: path | None = None) -> None: ...
19
+ async def async_precompile(self, module_file: path, precompiled_file : path, object_file: path, compile_flags: list[str] = [], define_macros: dict[str, str] = {}, include_dirs: list[path] = [], import_dirs: list[path] = [], diagnostic_file: path | None = None) -> None: ...
20
+ def compile (self, source_file: path, object_file : path, compile_flags: list[str] = [], define_macros: dict[str, str] = {}, include_dirs: list[path] = [], import_dirs: list[path] = [], diagnostic_file: path | None = None) -> None: ...
21
+ async def async_compile (self, source_file: path, object_file : path, compile_flags: list[str] = [], define_macros: dict[str, str] = {}, include_dirs: list[path] = [], import_dirs: list[path] = [], diagnostic_file: path | None = None) -> None: ...
22
+ def share (self, object_file: path, dynamic_file : path, link_flags : list[str] = [], lib_files : list[path] = []) -> None: ...
23
+ async def async_share (self, object_file: path, dynamic_file : path, link_flags : list[str] = [], lib_files : list[path] = []) -> None: ...
24
+ def link (self, object_file: path, executable_file : path, link_flags : list[str] = [], lib_files : list[path] = []) -> None: ...
25
+ async def async_link (self, object_file: path, executable_file : path, link_flags : list[str] = [], lib_files : list[path] = []) -> None: ...
26
+ preprocessed_suffix: str = '.ipp'
27
+ preparsed_suffix : str = '.gch'
28
+ precompiled_suffix : str = '.gcm'
29
+ diagnostic_suffix : str = '.sarif'
30
+ file : path
31
+ version : Version
32
+ stdlib_name : str = 'libstdc++'
33
+ stdlib_module_file : path
34
+ stdlib_static_file : path
35
+ stdlib_shared_file : path
36
+ compile_flags : list[str]
37
+ link_flags : list[str]
38
+ define_macros : dict[str, str]
39
+
40
+ async def _async_get_version (self) -> Version: ...
41
+ async def _async_get_stdlib_module_file(self) -> path : ...
42
+
43
+
44
+
45
+ @member(Gcc)
46
+ @syncable
47
+ @unique
48
+ async def __ainit__(
49
+ self: Gcc,
50
+ file: path = 'g++'
51
+ ) -> None:
52
+ self.file = file
53
+ self.version = await self._async_get_version()
54
+ self.stdlib_module_file = await self._async_get_stdlib_module_file()
55
+ self.compile_flags = [
56
+ f'-std={config.std}', '-fmodules',
57
+ *(['-O0', '-g'] if config.type == 'debug' else
58
+ ['-O3'] if config.type == 'release' else
59
+ ['-Os'] if config.type == 'size' else
60
+ [])
61
+ ]
62
+ self.link_flags = [
63
+ *(['-s'] if config.type == 'release' or config.type == 'size' else []),
64
+ '-lstdc++exp'
65
+ ]
66
+ self.define_macros = {
67
+ **({'DEBUG' : 'true'} if config.type == 'debug' else
68
+ {'DNDEBUG': 'true'} if config.type == 'release' else
69
+ {})
70
+ }
71
+
72
+ @member(Gcc)
73
+ @syncable
74
+ async def async_preprocess(
75
+ self : Gcc,
76
+ unit_file : path,
77
+ preprocessed_file: path,
78
+ compile_flags : list[str] = [],
79
+ define_macros : dict[str, str] = {},
80
+ include_dirs : list[path] = []
81
+ ) -> None:
82
+ await async_run(
83
+ file=self.file,
84
+ args=[
85
+ *(self.compile_flags + compile_flags),
86
+ *[f'-D{key}={value}' for key, value in (self.define_macros | define_macros).items()],
87
+ *[f'-I{include_dir}' for include_dir in include_dirs],
88
+ '-E', unit_file,
89
+ '-o', preprocessed_file
90
+ ],
91
+ print_stdout=False,
92
+ )
93
+
94
+ @member(Gcc)
95
+ @syncable
96
+ async def async_preparse(
97
+ self : Gcc,
98
+ header_file : path,
99
+ preparsed_file : path,
100
+ compile_flags : list[str] = [],
101
+ define_macros : dict[str, str] = {},
102
+ include_dirs : list[path] = [],
103
+ diagnostic_file: path | None = None
104
+ ) -> None:
105
+ create_dir(parent_dir(preparsed_file))
106
+ create_dir(parent_dir(diagnostic_file)) if diagnostic_file is not None else None
107
+ await async_run(
108
+ file=self.file,
109
+ args=[
110
+ *(self.compile_flags + compile_flags),
111
+ *[f'-D{key}={value}' for key, value in (self.define_macros | define_macros).items()],
112
+ *[f'-I{include_dir}' for include_dir in include_dirs],
113
+ *([f'-fdiagnostics-add-output=sarif:file={diagnostic_file}'] if diagnostic_file is not None else []),
114
+ '-c', '-x', 'c++-header', header_file,
115
+ '-o', preparsed_file
116
+ ]
117
+ )
118
+
119
+
120
+ @member(Gcc)
121
+ @syncable
122
+ async def async_precompile(
123
+ self : Gcc,
124
+ module_file : path,
125
+ precompiled_file: path,
126
+ object_file : path,
127
+ compile_flags : list[str] = [],
128
+ define_macros : dict[str, str] = {},
129
+ include_dirs : list[path] = [],
130
+ import_dirs : list[path] = [],
131
+ diagnostic_file : path | None = None
132
+ ) -> None:
133
+ create_dir(parent_dir(precompiled_file))
134
+ create_dir(parent_dir(object_file))
135
+ create_dir(parent_dir(diagnostic_file)) if diagnostic_file is not None else None
136
+ await async_run(
137
+ file=self.file,
138
+ args=[
139
+ *(self.compile_flags + compile_flags),
140
+ *[f'-D{key}={value}' for key, value in (self.define_macros | define_macros).items()],
141
+ *[f'-I{include_dir}' for include_dir in include_dirs],
142
+ *[f'-fmodule-mapper={module_mapper_logger.mapper_file_of(import_dir=import_dir)}' for import_dir in import_dirs],
143
+ *([f'-fdiagnostics-add-output=sarif:file={diagnostic_file}'] if diagnostic_file is not None else []),
144
+ '-c', '-x', 'c++-module', module_file,
145
+ '-o', object_file
146
+ ]
147
+ )
148
+
149
+ @member(Gcc)
150
+ @syncable
151
+ async def async_compile(
152
+ self : Gcc,
153
+ source_file : path,
154
+ object_file : path,
155
+ compile_flags : list[str] = [],
156
+ define_macros : dict[str, str] = {},
157
+ include_dirs : list[path] =[],
158
+ import_dirs : list[path] = [],
159
+ diagnostic_file: path | None = None
160
+ ) -> None:
161
+ create_dir(parent_dir(object_file))
162
+ create_dir(parent_dir(diagnostic_file)) if diagnostic_file is not None else None
163
+ await async_run(
164
+ file=self.file,
165
+ args=[
166
+ *(self.compile_flags + compile_flags),
167
+ *[f'-D{key}={value}' for key, value in (self.define_macros | define_macros).items()],
168
+ *[f'-I{include_dir}' for include_dir in include_dirs],
169
+ *[f'-fmodule-mapper={module_mapper_logger.mapper_file_of(import_dir=import_dir)}' for import_dir in import_dirs],
170
+ *([f'-fdiagnostics-add-output=sarif:file={diagnostic_file}'] if diagnostic_file is not None else []),
171
+ '-c', '-x', 'c++', source_file,
172
+ '-o', object_file
173
+ ]
174
+ )
175
+
176
+ @member(Gcc)
177
+ @syncable
178
+ async def async_link(
179
+ self : Gcc,
180
+ object_file : path,
181
+ executable_file: path,
182
+ link_flags : list[str] = [],
183
+ lib_files : list[path] = []
184
+ ) -> None:
185
+ create_dir(parent_dir(executable_file))
186
+ await async_run(
187
+ file=self.file,
188
+ args=[
189
+ *(self.link_flags + link_flags),
190
+ *([object_file] + lib_files),
191
+ '-o', executable_file
192
+ ]
193
+ )
194
+
195
+ @member(Gcc)
196
+ async def _async_get_version(self: Gcc) -> Version:
197
+ try:
198
+ stdout = await async_run(
199
+ file=self.file,
200
+ args=['--version'],
201
+ return_stdout=True
202
+ )
203
+ except SubprocessError as error:
204
+ raise ConfigError(f'gcc check failed (with file = {self.file})') from error
205
+ if not stdout.startswith('g++'):
206
+ raise ConfigError(f'gcc check failed (with file = {self.file}, subprocess = "{self.file} --version", stdout = "{stdout.splitlines()[0]} ...", requires = "g++ ...")')
207
+ version = Version.parse(stdout)
208
+ if version < 15:
209
+ raise ConfigError(f'gcc version is too old (with file = {self.file}, version = {version}, requires = 15+)')
210
+ return version
211
+
212
+ @member(Gcc)
213
+ async def _async_get_stdlib_module_file(self: Gcc) -> path:
214
+ stderr = await async_run(
215
+ file=self.file,
216
+ args=[
217
+ *self.compile_flags,
218
+ '-E', '-x', 'c++', '-',
219
+ '-v'
220
+ ],
221
+ print_stderr =config.verbose,
222
+ return_stderr=True
223
+ )
224
+ search_dirs = re.search(
225
+ pattern=r'^#include <...> search starts here:$\n(.*)\n^end of search list.$',
226
+ string=stderr,
227
+ flags=re.MULTILINE | re.DOTALL | re.IGNORECASE
228
+ )
229
+ if search_dirs is None:
230
+ raise ConfigError(f'libstdc++ module_file is not found (with subprocess = "{self.file} -v -E -x c++ -", stdout = ..., requires = "... #include <...> starts here ... end of search list ...")')
231
+ search_dirs = [path(search_dir.strip()) for search_dir in search_dirs.group(1).splitlines()]
232
+ search_files = [f'{search_dir}/bits/std.cc' for search_dir in search_dirs]
233
+ for search_file in search_files:
234
+ if exist_file(search_file):
235
+ return search_file
236
+ else:
237
+ raise ConfigError(f'libstdc++ module_file is not found (with search_files = {search_files})')
@@ -0,0 +1,24 @@
1
+ from cppmakelib.utility.decorator import syncable, unique
2
+ from cppmakelib.utility.version import Version
3
+ import sys
4
+
5
+ class Msvc:
6
+ name = 'msvc'
7
+ intermediate_suffix = '.i'
8
+ precompiled_suffix = '.ixx'
9
+
10
+ @syncable
11
+ @unique
12
+ async def __ainit__(self, path='cl'):
13
+ self.path = path
14
+ self.version = await self._async_get_version()
15
+ assert False
16
+
17
+ async def _async_get_version(self):
18
+ return await Version.async_parse(
19
+ name =self.name,
20
+ command=[self.path],
21
+ pipe =sys.stderr,
22
+ check =lambda stderr: stderr.startswith('Microsoft') and 'msvc' in stderr,
23
+ lowest =19.36
24
+ )
@@ -0,0 +1,5 @@
1
+ from cppmakelib.utility.color import red, bold
2
+
3
+ class ConfigError(Exception):
4
+ def __str__(self):
5
+ return f'{red(bold('fatal error:'))} {super().__str__()}'
@@ -0,0 +1,6 @@
1
+ from cppmakelib.utility.color import red, bold
2
+
3
+ class LogicError(Exception):
4
+ def __str__(self):
5
+ return f'{red(bold('fatal error:'))} {super().__str__()}'
6
+
@@ -0,0 +1,9 @@
1
+ class SubprocessError(Exception):
2
+ def __init__(self, stderr, is_stderr_printed, code):
3
+ super().__init__(stderr)
4
+ self.is_stderr_printed = is_stderr_printed
5
+ self.code = code
6
+
7
+ def __str__(self):
8
+ return super().__str__() if not self.is_stderr_printed else ''
9
+
@@ -0,0 +1,15 @@
1
+ import asyncio
2
+ import typing
3
+
4
+ def sync_wait [T](task : typing.Coroutine[typing.Any, typing.Any, T] ) -> T : ...
5
+ def start_detached[T](task : typing.Coroutine[typing.Any, typing.Any, T] ) -> None : ...
6
+ def when_all [T](tasks: list[typing.Coroutine[typing.Any, typing.Any, T]]) -> list[T]: ...
7
+ def when_any [T](tasks: list[typing.Coroutine[typing.Any, typing.Any, T]]) -> T : ...
8
+
9
+
10
+
11
+ def sync_wait[T](task: typing.Coroutine[typing.Any, typing.Any, T]) -> T:
12
+ return asyncio.run(task)
13
+
14
+ async def when_all[T](tasks: list[typing.Coroutine[typing.Any, typing.Any, T]]) -> list[T]:
15
+ return await asyncio.gather(*tasks)
@@ -0,0 +1,84 @@
1
+ from cppmakelib.basic.config import config
2
+ from cppmakelib.error.subprocess import SubprocessError
3
+ from cppmakelib.executor.operation import when_all
4
+ from cppmakelib.executor.scheduler import Scheduler
5
+ from cppmakelib.utility.filesystem import path
6
+ from cppmakelib.logger.compile_commands import compile_commands_logger
7
+ from cppmakelib.utility.decorator import syncable
8
+ import asyncio
9
+ import sys
10
+ import typing
11
+
12
+ def run(file: path, args: list[str] = [], cwd: path = path('.'), print_command: bool = config.verbose, print_stdout: bool = config.verbose, print_stderr: bool = True, return_stdout: bool = False, return_stderr: bool = False) -> None | str | tuple[str, str]: ...
13
+ async def async_run(file: path, args: list[str] = [], cwd: path = path('.'), print_command: bool = config.verbose, print_stdout: bool = config.verbose, print_stderr: bool = True, return_stdout: bool = False, return_stderr: bool = False) -> None | str | tuple[str, str]: ...
14
+
15
+ if typing.TYPE_CHECKING:
16
+ @typing.overload
17
+ def run(file: path, args: list[str] = [], cwd: path = path('.'), print_command: bool = config.verbose, print_stdout: bool = config.verbose, print_stderr: bool = True, *, return_stdout: typing.Literal[False] = False, return_stderr: typing.Literal[False] = False) -> None : ...
18
+ @typing.overload
19
+ def run(file: path, args: list[str] = [], cwd: path = path('.'), print_command: bool = config.verbose, print_stdout: bool = config.verbose, print_stderr: bool = True, *, return_stdout: typing.Literal[False] = False, return_stderr: typing.Literal[True]) -> str : ...
20
+ @typing.overload
21
+ def run(file: path, args: list[str] = [], cwd: path = path('.'), print_command: bool = config.verbose, print_stdout: bool = config.verbose, print_stderr: bool = True, *, return_stdout: typing.Literal[True], return_stderr: typing.Literal[False] = False) -> str : ...
22
+ @typing.overload
23
+ def run(file: path, args: list[str] = [], cwd: path = path('.'), print_command: bool = config.verbose, print_stdout: bool = config.verbose, print_stderr: bool = True, *, return_stdout: typing.Literal[True], return_stderr: typing.Literal[True]) -> tuple[str, str]: ...
24
+ @typing.overload
25
+ async def async_run(file: path, args: list[str] = [], cwd: path = path('.'), print_command: bool = config.verbose, print_stdout: bool = config.verbose, print_stderr: bool = True, *, return_stdout: typing.Literal[False] = False, return_stderr: typing.Literal[False] = False) -> None : ...
26
+ @typing.overload
27
+ async def async_run(file: path, args: list[str] = [], cwd: path = path('.'), print_command: bool = config.verbose, print_stdout: bool = config.verbose, print_stderr: bool = True, *, return_stdout: typing.Literal[False] = False, return_stderr: typing.Literal[True]) -> str : ...
28
+ @typing.overload
29
+ async def async_run(file: path, args: list[str] = [], cwd: path = path('.'), print_command: bool = config.verbose, print_stdout: bool = config.verbose, print_stderr: bool = True, *, return_stdout: typing.Literal[True], return_stderr: typing.Literal[False] = False) -> str : ...
30
+ @typing.overload
31
+ async def async_run(file: path, args: list[str] = [], cwd: path = path('.'), print_command: bool = config.verbose, print_stdout: bool = config.verbose, print_stderr: bool = True, *, return_stdout: typing.Literal[True], return_stderr: typing.Literal[True]) -> tuple[str, str]: ...
32
+
33
+
34
+
35
+ _internal_scheduler = Scheduler(config.parallel)
36
+
37
+ @syncable
38
+ async def async_run(
39
+ file : path,
40
+ args : list[str] = [],
41
+ cwd : path = path('.'),
42
+ print_command : bool = config.verbose,
43
+ print_stdout : bool = config.verbose,
44
+ print_stderr : bool = True,
45
+ return_stdout : bool = False,
46
+ return_stderr : bool = False,
47
+ ) -> None | str | tuple[str, str]:
48
+ async with _internal_scheduler.schedule():
49
+ proc = await asyncio.subprocess.create_subprocess_exec(
50
+ file,
51
+ *args,
52
+ cwd =cwd,
53
+ stdout=asyncio.subprocess.PIPE,
54
+ stderr=asyncio.subprocess.PIPE
55
+ )
56
+ assert isinstance(proc.stdout, asyncio.StreamReader)
57
+ assert isinstance(proc.stderr, asyncio.StreamReader)
58
+ async def read(stream: asyncio.StreamReader, tee: typing.TextIO | None) -> str:
59
+ text = ''
60
+ while True:
61
+ line = await stream.readline()
62
+ if not stream.at_eof():
63
+ line = line.decode()
64
+ text += line
65
+ print(line, end='', file=tee) if tee is not None else None
66
+ else:
67
+ break
68
+ return text
69
+ stdout, stderr = await when_all([
70
+ read(stream=proc.stdout, tee=sys.stdout if print_stdout else None),
71
+ read(stream=proc.stderr, tee=sys.stderr if print_stderr else None)
72
+ ])
73
+ code = await proc.wait()
74
+ if code == 0:
75
+ if not return_stdout and not return_stderr:
76
+ return None
77
+ elif not return_stdout and return_stderr:
78
+ return stderr
79
+ elif return_stdout and not return_stderr:
80
+ return stdout
81
+ else: # return_stdout and return_stderr
82
+ return stdout, stderr
83
+ else:
84
+ raise SubprocessError(stderr=stderr, is_stderr_printed=print_stderr, code=code)