cppmake 127.0.13__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.
- cppmake-127.0.16.dist-info/METADATA +191 -0
- cppmake-127.0.16.dist-info/RECORD +55 -0
- {cppmake-127.0.13.dist-info → cppmake-127.0.16.dist-info}/WHEEL +1 -1
- cppmake-127.0.16.dist-info/entry_points.txt +3 -0
- cppmake-127.0.16.dist-info/top_level.txt +3 -0
- cppmaked.py +2 -0
- cppmakelib/__init__.py +32 -0
- cppmakelib/basic/config.py +50 -0
- cppmakelib/basic/context.py +39 -0
- cppmakelib/builder/cmake.py +71 -0
- cppmakelib/builder/include.py +15 -0
- cppmakelib/builder/makefile.py +68 -0
- cppmakelib/compiler/all.py +26 -0
- cppmakelib/compiler/clang.py +196 -0
- cppmakelib/compiler/emcc.py +79 -0
- cppmakelib/compiler/gcc.py +237 -0
- cppmakelib/compiler/msvc.py +24 -0
- cppmakelib/error/config.py +5 -0
- cppmakelib/error/logic.py +6 -0
- cppmakelib/error/subprocess.py +9 -0
- cppmakelib/executor/operation.py +15 -0
- cppmakelib/executor/run.py +84 -0
- cppmakelib/executor/scheduler.py +91 -0
- cppmakelib/logger/compile_commands.py +30 -0
- cppmakelib/logger/make_progress.py +0 -0
- cppmakelib/logger/module_mapper.py +6 -0
- cppmakelib/logger/unit_status.py +224 -0
- cppmakelib/system/all.py +25 -0
- cppmakelib/system/linux.py +26 -0
- cppmakelib/system/macos.py +26 -0
- cppmakelib/system/windows.py +26 -0
- cppmakelib/unit/binary.py +26 -0
- cppmakelib/unit/code.py +62 -0
- cppmakelib/unit/dynamic.py +12 -0
- cppmakelib/unit/executable.py +35 -0
- cppmakelib/unit/header.py +63 -0
- cppmakelib/unit/module.py +69 -0
- cppmakelib/unit/object.py +71 -0
- cppmakelib/unit/package.py +87 -0
- cppmakelib/unit/precompiled.py +6 -0
- cppmakelib/unit/preparsed.py +6 -0
- cppmakelib/unit/preprocessed.py +3 -0
- cppmakelib/unit/source.py +64 -0
- cppmakelib/utility/algorithm.py +44 -0
- cppmakelib/utility/color.py +14 -0
- cppmakelib/utility/decorator.py +120 -0
- cppmakelib/utility/filesystem.py +71 -0
- cppmakelib/utility/import_.py +21 -0
- cppmakelib/utility/remote/client.py +2 -0
- cppmakelib/utility/remote/protocol.py +32 -0
- cppmakelib/utility/remote/remote.py +43 -0
- cppmakelib/utility/remote/server.py +0 -0
- cppmakelib/utility/time.py +1 -0
- cppmakelib/utility/version.py +65 -0
- cppmake-127.0.13.dist-info/METADATA +0 -9
- cppmake-127.0.13.dist-info/RECORD +0 -6
- cppmake-127.0.13.dist-info/entry_points.txt +0 -2
- cppmake-127.0.13.dist-info/top_level.txt +0 -1
- /cppmake/__main__.py → /cppmake.py +0 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
from cppmakelib.compiler.all import compiler
|
|
2
|
+
from cppmakelib.executor.operation import when_all
|
|
3
|
+
from cppmakelib.executor.scheduler import scheduler
|
|
4
|
+
from cppmakelib.system.all import system
|
|
5
|
+
from cppmakelib.unit.code import Code
|
|
6
|
+
from cppmakelib.unit.precompiled import Precompiled
|
|
7
|
+
from cppmakelib.utility.algorithm import recursive_collect
|
|
8
|
+
from cppmakelib.utility.decorator import member, once, relocatable, syncable, unique
|
|
9
|
+
from cppmakelib.utility.filesystem import path
|
|
10
|
+
import typing
|
|
11
|
+
|
|
12
|
+
class Module(Code):
|
|
13
|
+
def __new__ (cls: ..., file: path) -> Module : ...
|
|
14
|
+
async def __anew__ (cls: ..., file: path) -> Module : ...
|
|
15
|
+
def __init__ (self, file: path) -> None : ...
|
|
16
|
+
async def __ainit__ (self, file: path) -> None : ...
|
|
17
|
+
def precompile (self) -> Precompiled: ...
|
|
18
|
+
async def async_precompile (self) -> Precompiled: ...
|
|
19
|
+
def is_precompiled(self) -> bool : ...
|
|
20
|
+
async def async_is_precompiled(self) -> bool : ...
|
|
21
|
+
name : str
|
|
22
|
+
precompiled_file: path
|
|
23
|
+
object_file : path
|
|
24
|
+
diagnostic_file : path
|
|
25
|
+
import_modules : list[Module]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@member(Module)
|
|
30
|
+
@relocatable
|
|
31
|
+
@syncable
|
|
32
|
+
@unique
|
|
33
|
+
async def __ainit__(self: Module, file: path) -> None:
|
|
34
|
+
await super(Module, self).__ainit__(file)
|
|
35
|
+
self.name = await self.context_package.unit_status_logger.async_get_module_name(module=self)
|
|
36
|
+
self.precompiled_file = f'{self.context_package.build_module_dir}/{self.name.replace(':', '-')}{compiler.precompiled_suffix}'
|
|
37
|
+
self.object_file = f'{self.context_package.build_module_dir}/{self.name.replace(':', '-')}{system.object_suffix}'
|
|
38
|
+
self.diagnostic_file = f'{self.context_package.build_module_dir}/{self.name.replace(':', '.')}{compiler.diagnostic_suffix}'
|
|
39
|
+
self.import_modules = await when_all([Module.__anew__(Module, file) for file in await self.context_package.unit_status_logger.async_get_module_imports(module=self)])
|
|
40
|
+
|
|
41
|
+
@member(Module)
|
|
42
|
+
@syncable
|
|
43
|
+
@once
|
|
44
|
+
async def async_precompile(self: Module) -> Precompiled:
|
|
45
|
+
if not await self.async_is_precompiled():
|
|
46
|
+
await when_all([module.async_precompile() for module in self.import_modules])
|
|
47
|
+
await self.async_preprocess()
|
|
48
|
+
async with scheduler.schedule():
|
|
49
|
+
await compiler.async_precompile(
|
|
50
|
+
module_file =self.file,
|
|
51
|
+
precompiled_file=self.precompiled_file,
|
|
52
|
+
object_file =self.object_file,
|
|
53
|
+
compile_flags =self.compile_flags,
|
|
54
|
+
define_macros =self.define_macros,
|
|
55
|
+
include_dirs =[self.context_package.build_header_dir] + recursive_collect(self.context_package, next=lambda package: package.require_packages, collect=lambda package: package.install_include_dir),
|
|
56
|
+
import_dirs =[self.context_package.build_module_dir] + recursive_collect(self.context_package, next=lambda package: package.require_packages, collect=lambda package: package.install_import_dir),
|
|
57
|
+
diagnostic_file =self.diagnostic_file,
|
|
58
|
+
)
|
|
59
|
+
self.context_package.unit_status_logger.set_module_precompiled(module=self, precompiled=True)
|
|
60
|
+
return Precompiled(self.precompiled_file)
|
|
61
|
+
|
|
62
|
+
@member(Module)
|
|
63
|
+
@syncable
|
|
64
|
+
@once
|
|
65
|
+
async def async_is_precompiled(self: Module) -> bool:
|
|
66
|
+
return all(await when_all([module.async_is_precompiled() for module in self.import_modules ])) and \
|
|
67
|
+
await self.async_is_preprocessed() and \
|
|
68
|
+
self.context_package.unit_status_logger.get_module_precompiled(module=self)
|
|
69
|
+
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from cppmakelib.compiler.all import compiler
|
|
2
|
+
from cppmakelib.unit.binary import Binary
|
|
3
|
+
from cppmakelib.unit.dynamic import Dynamic
|
|
4
|
+
from cppmakelib.unit.executable import Executable
|
|
5
|
+
from cppmakelib.utility.algorithm import recursive_collect
|
|
6
|
+
from cppmakelib.utility.decorator import member, once, syncable, unique
|
|
7
|
+
from cppmakelib.utility.filesystem import iterate_dir, path
|
|
8
|
+
|
|
9
|
+
class Object(Binary):
|
|
10
|
+
def __new__ (cls, file: path) -> Object : ...
|
|
11
|
+
def __init__ (self, file: path) -> None : ...
|
|
12
|
+
def share (self) -> Dynamic : ...
|
|
13
|
+
async def async_share (self) -> Dynamic : ...
|
|
14
|
+
def is_shared(self) -> bool : ...
|
|
15
|
+
async def async_is_shared(self) -> bool : ...
|
|
16
|
+
def link (self) -> Executable: ...
|
|
17
|
+
async def async_link (self) -> Executable: ...
|
|
18
|
+
def is_linked(self) -> bool : ...
|
|
19
|
+
async def async_is_linked(self) -> bool : ...
|
|
20
|
+
static_file : path
|
|
21
|
+
dynamic_file : path
|
|
22
|
+
executable_file: path
|
|
23
|
+
lib_objects : list[Object]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@member(Object)
|
|
28
|
+
@unique
|
|
29
|
+
def __init__(self: Object, file: path) -> None:
|
|
30
|
+
super(Object, self).__init__(file)
|
|
31
|
+
self.lib_objects = [Object(file) for file in self.context_package.unit_status_logger.get_object_libs(object=self)]
|
|
32
|
+
|
|
33
|
+
@member(Object)
|
|
34
|
+
@syncable
|
|
35
|
+
@once
|
|
36
|
+
async def async_share(self: Object) -> Dynamic:
|
|
37
|
+
await compiler.async_share(
|
|
38
|
+
object_file =self.file,
|
|
39
|
+
dynamic_file=self.dynamic_file,
|
|
40
|
+
link_flags =self.link_flags,
|
|
41
|
+
lib_files =recursive_collect(self, next=lambda object : object.lib_objects, collect=lambda object : object.file) +
|
|
42
|
+
recursive_collect(self.context_package, next=lambda package: package.require_packages, collect=lambda package: [file for file in iterate_dir(package.install_lib_dir)], flatten=True)
|
|
43
|
+
)
|
|
44
|
+
return Dynamic(self.dynamic_file)
|
|
45
|
+
|
|
46
|
+
@member(Object)
|
|
47
|
+
@syncable
|
|
48
|
+
@once
|
|
49
|
+
async def async_is_shared(self: Object) -> bool:
|
|
50
|
+
return self.context_package.unit_status_logger.get_object_shared(object=self)
|
|
51
|
+
|
|
52
|
+
@member(Object)
|
|
53
|
+
@syncable
|
|
54
|
+
@once
|
|
55
|
+
async def async_link(self: Object) -> Executable:
|
|
56
|
+
await compiler.async_link(
|
|
57
|
+
object_file =self.file,
|
|
58
|
+
executable_file=self.executable_file,
|
|
59
|
+
link_flags =self.link_flags,
|
|
60
|
+
lib_files =recursive_collect(self, next=lambda object : object.lib_objects, collect=lambda object : object.file) +
|
|
61
|
+
recursive_collect(self.context_package, next=lambda package: package.require_packages, collect=lambda package: [file for file in iterate_dir(package.install_lib_dir)], flatten=True)
|
|
62
|
+
)
|
|
63
|
+
return Executable(self.executable_file)
|
|
64
|
+
|
|
65
|
+
# 1. Module.compile() -> Precompiled, Object, 需要Module(...).precompile()[0].install(), [0]来的莫名其妙, 返回struct更是莫名其妙
|
|
66
|
+
# 2. Module.compile() -> Precompiled, Precompiled.xxx() -> Object, 想不出xxx名字
|
|
67
|
+
# 3. Module.compile() -> Precompiled, 而Object直接link Precompiled本身, 不知道gcc/msvc如何支持, 且降低性能, Module需要预链接为.o的
|
|
68
|
+
# 4. Module.compile() -> Precompield (+ implicit Object), 可以Module(...).precompile().install, 这里Object的属性如何?
|
|
69
|
+
|
|
70
|
+
# 1. Object构造时传入所有依赖, 外部接口丑
|
|
71
|
+
# 2. Object由unit_status_logger记录来源于哪里,module/source->object反向记录, 需要提前Module.__anew__, 内部逻辑丑
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from cppmakelib.basic.config import config
|
|
2
|
+
from cppmakelib.basic.context import context
|
|
3
|
+
from cppmakelib.executor.operation import when_all
|
|
4
|
+
from cppmakelib.executor.scheduler import scheduler
|
|
5
|
+
from cppmakelib.logger.unit_status import UnitStatusLogger
|
|
6
|
+
from cppmakelib.utility.decorator import member, once, syncable, unique
|
|
7
|
+
from cppmakelib.utility.filesystem import path
|
|
8
|
+
from cppmakelib.utility.import_ import import_module
|
|
9
|
+
import types
|
|
10
|
+
import typing
|
|
11
|
+
|
|
12
|
+
class Package:
|
|
13
|
+
def __new__ (cls, dir: path) -> Package: ...
|
|
14
|
+
def __init__ (self, dir: path) -> None : ...
|
|
15
|
+
def build (self) -> None : ...
|
|
16
|
+
async def async_build (self) -> None : ...
|
|
17
|
+
def is_built(self) -> bool : ...
|
|
18
|
+
async def async_is_built(self) -> bool : ...
|
|
19
|
+
# ========
|
|
20
|
+
name : str
|
|
21
|
+
dir : path
|
|
22
|
+
# ========
|
|
23
|
+
search_header_dir : path # redefinable in cppmake.py
|
|
24
|
+
search_module_dir : path # redefinable in cppmake.py
|
|
25
|
+
# ========
|
|
26
|
+
build_dir : path
|
|
27
|
+
build_cache_dir : path
|
|
28
|
+
build_code_dir : path
|
|
29
|
+
build_header_dir : path
|
|
30
|
+
build_module_dir : path
|
|
31
|
+
build_source_dir : path
|
|
32
|
+
# ========
|
|
33
|
+
install_dir : path
|
|
34
|
+
install_bin_dir : path
|
|
35
|
+
install_import_dir : path
|
|
36
|
+
install_include_dir: path
|
|
37
|
+
install_lib_dir : path
|
|
38
|
+
# ========
|
|
39
|
+
compile_flags : list[str] # redefinable in cppmake.py
|
|
40
|
+
link_flags : list[str] # redefinable in cppmake.py
|
|
41
|
+
define_macros : dict[str, str] # redefinable in cppmake.py
|
|
42
|
+
# ========
|
|
43
|
+
require_packages : list[Package]
|
|
44
|
+
# ========
|
|
45
|
+
unit_status_logger : UnitStatusLogger
|
|
46
|
+
# ========
|
|
47
|
+
cppmake_file : path
|
|
48
|
+
cppmake : types.ModuleType
|
|
49
|
+
# ========
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@member(Package)
|
|
53
|
+
@unique
|
|
54
|
+
def __init__(self: Package, dir: path) -> None:
|
|
55
|
+
self.dir = dir
|
|
56
|
+
self.search_header_dir = f'{self.dir}/header'
|
|
57
|
+
self.search_module_dir = f'{self.dir}/module'
|
|
58
|
+
self.build_dir = f'binary/{config.type}/{self.name}/build'
|
|
59
|
+
self.build_cache_dir = f'binary/{config.type}/{self.name}/build/cache'
|
|
60
|
+
self.build_code_dir = f'binary/{config.type}/{self.name}/build/code'
|
|
61
|
+
self.build_header_dir = f'binary/{config.type}/{self.name}/build/header'
|
|
62
|
+
self.build_module_dir = f'binary/{config.type}/{self.name}/build/module'
|
|
63
|
+
self.build_source_dir = f'binary/{config.type}/{self.name}/build/source'
|
|
64
|
+
self.install_dir = f'binary/{config.type}/{self.name}/install'
|
|
65
|
+
self.install_bin_dir = f'binary/{config.type}/{self.name}/install/bin'
|
|
66
|
+
self.install_import_dir = f'binary/{config.type}/{self.name}/install/import'
|
|
67
|
+
self.install_include_dir = f'binary/{config.type}/{self.name}/install/include'
|
|
68
|
+
self.install_lib_dir = f'binary/{config.type}/{self.name}/install/lib'
|
|
69
|
+
self.compile_flags = []
|
|
70
|
+
self.link_flags = []
|
|
71
|
+
self.define_macros = {}
|
|
72
|
+
self.require_packages = []
|
|
73
|
+
self.unit_status_logger = UnitStatusLogger(build_cache_dir=self.build_cache_dir)
|
|
74
|
+
self.cppmake_file = f'{self.dir}/cppmake.py'
|
|
75
|
+
with context.switch(package=self):
|
|
76
|
+
self.cppmake = import_module(file=self.cppmake_file, globals={'self': self})
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@member(Package)
|
|
80
|
+
@syncable
|
|
81
|
+
@once
|
|
82
|
+
async def async_build(self: Package) -> None:
|
|
83
|
+
await when_all([package.async_build() for package in self.require_packages])
|
|
84
|
+
async with scheduler.schedule(scheduler.max):
|
|
85
|
+
with context.switch(package=self):
|
|
86
|
+
# lazy-print(f'build package {self.name}')
|
|
87
|
+
self.cppmake.build() if hasattr(self.cppmake, 'build') else None
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from cppmakelib.compiler.all import compiler
|
|
2
|
+
from cppmakelib.executor.operation import when_all
|
|
3
|
+
from cppmakelib.executor.scheduler import scheduler
|
|
4
|
+
from cppmakelib.system.all import system
|
|
5
|
+
from cppmakelib.unit.code import Code
|
|
6
|
+
from cppmakelib.unit.module import Module
|
|
7
|
+
from cppmakelib.unit.object import Object
|
|
8
|
+
from cppmakelib.utility.algorithm import recursive_collect
|
|
9
|
+
from cppmakelib.utility.decorator import member, once, relocatable, syncable, unique
|
|
10
|
+
from cppmakelib.utility.filesystem import path
|
|
11
|
+
|
|
12
|
+
class Source(Code):
|
|
13
|
+
def __new__ (cls: ..., file: path) -> Source: ...
|
|
14
|
+
async def __anew__ (cls: ..., file: path) -> Source: ...
|
|
15
|
+
def __init__ (self, file: path) -> None : ...
|
|
16
|
+
async def __ainit__ (self, file: path) -> None : ...
|
|
17
|
+
def compile (self) -> Object: ...
|
|
18
|
+
async def async_compile (self) -> Object: ...
|
|
19
|
+
def is_compiled(self) -> bool : ...
|
|
20
|
+
async def async_is_compiled(self) -> bool : ...
|
|
21
|
+
name : str
|
|
22
|
+
object_file : path
|
|
23
|
+
diagnostic_file: path
|
|
24
|
+
import_modules : list[Module]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@member(Source)
|
|
29
|
+
@relocatable
|
|
30
|
+
@syncable
|
|
31
|
+
@unique
|
|
32
|
+
async def __ainit__(self: Source, file: path) -> None:
|
|
33
|
+
await super(Source, self).__ainit__(file)
|
|
34
|
+
self.object_file = f'{self.context_package.build_source_dir}/{self.name}{system.object_suffix}'
|
|
35
|
+
self.diagnostic_file = f'{self.context_package.build_source_dir}/{self.name}{system.executable_suffix}'
|
|
36
|
+
self.import_modules = await when_all([Module.__anew__(Module, file) for file in await self.context_package.unit_status_logger.async_get_source_imports(source=self)])
|
|
37
|
+
|
|
38
|
+
@member(Source)
|
|
39
|
+
@syncable
|
|
40
|
+
@once
|
|
41
|
+
async def async_compile(self: Source) -> Object:
|
|
42
|
+
if not await self.async_is_compiled():
|
|
43
|
+
await when_all([module.async_precompile() for module in self.import_modules ])
|
|
44
|
+
await self.async_preprocess()
|
|
45
|
+
async with scheduler.schedule():
|
|
46
|
+
await compiler.async_compile(
|
|
47
|
+
source_file =self.file,
|
|
48
|
+
object_file =self.object_file,
|
|
49
|
+
compile_flags =self.compile_flags,
|
|
50
|
+
define_macros =self.define_macros,
|
|
51
|
+
include_dirs =[self.context_package.build_header_dir] + recursive_collect(self.context_package, next=lambda package: package.require_packages, collect=lambda package: package.install_include_dir),
|
|
52
|
+
import_dirs =[self.context_package.build_module_dir] + recursive_collect(self.context_package, next=lambda package: package.require_packages, collect=lambda package: package.install_import_dir),
|
|
53
|
+
diagnostic_file=self.diagnostic_file,
|
|
54
|
+
)
|
|
55
|
+
self.context_package.unit_status_logger.set_source_compiled(source=self, compiled=True)
|
|
56
|
+
return Object(self.object_file)
|
|
57
|
+
|
|
58
|
+
@member(Source)
|
|
59
|
+
@syncable
|
|
60
|
+
@once
|
|
61
|
+
async def async_is_compiled(self: Source) -> bool:
|
|
62
|
+
return all(await when_all([module.async_is_precompiled() for module in self.import_modules ])) and \
|
|
63
|
+
await self.async_is_preprocessed() and \
|
|
64
|
+
self.context_package.unit_status_logger.get_source_compiled(source=self)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
def recursive_collect[T, U, R](node: U, next: typing.Callable[[U | T], list[T]], collect: typing.Callable[[U | T], R | list[R] | None], flatten: bool = False) -> list[R]: ...
|
|
4
|
+
|
|
5
|
+
if typing.TYPE_CHECKING:
|
|
6
|
+
@typing.overload
|
|
7
|
+
def recursive_collect[T, U, R](node: U, next: typing.Callable[[U | T], list[T]], collect: typing.Callable[[T], R | None], *, flatten: typing.Literal[False] = False) -> list[R]: ...
|
|
8
|
+
@typing.overload
|
|
9
|
+
def recursive_collect[T, U, R](node: U, next: typing.Callable[[U | T], list[T]], collect: typing.Callable[[T], list[R]], *, flatten: typing.Literal[True]) -> list[R]: ...
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Our goal is to make the caller more pretty (with the algorithm itself to be maybe dirty).
|
|
14
|
+
# Feel free to overwrite the implemention when we need something new from it.
|
|
15
|
+
|
|
16
|
+
def recursive_collect[T, U, R](
|
|
17
|
+
node : U,
|
|
18
|
+
next : typing.Callable[[U | T], list[T]],
|
|
19
|
+
collect: typing.Callable[[T], R | list[R] | None],
|
|
20
|
+
flatten: bool = False
|
|
21
|
+
) -> list[R]:
|
|
22
|
+
return _recursive_collect_impl(node, next, collect, flatten, True, set(), list())
|
|
23
|
+
|
|
24
|
+
def _recursive_collect_impl[T, U, R](
|
|
25
|
+
node : U | T,
|
|
26
|
+
next : typing.Callable[[U | T], list[T]],
|
|
27
|
+
collect : typing.Callable[[T], R | list[R] | None],
|
|
28
|
+
flatten : bool,
|
|
29
|
+
root : bool,
|
|
30
|
+
visited : set[T],
|
|
31
|
+
collected: list[R]
|
|
32
|
+
) -> list[R]:
|
|
33
|
+
if node not in visited:
|
|
34
|
+
if not root:
|
|
35
|
+
visited |= {typing.cast(T, node)}
|
|
36
|
+
value = collect(typing.cast(T, node))
|
|
37
|
+
if value is not None and value not in collected:
|
|
38
|
+
if not flatten:
|
|
39
|
+
collected += [typing.cast(R, value)]
|
|
40
|
+
else:
|
|
41
|
+
collected += typing.cast(list[R], value)
|
|
42
|
+
for subnode in next(node):
|
|
43
|
+
_recursive_collect_impl(subnode, next, collect, flatten, True, visited, collected)
|
|
44
|
+
return collected
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
def red(str):
|
|
2
|
+
return f'\033[31m{str}\033[39m'
|
|
3
|
+
|
|
4
|
+
def green(str):
|
|
5
|
+
return f'\033[32m{str}\033[39m'
|
|
6
|
+
|
|
7
|
+
def yellow(str):
|
|
8
|
+
return f'\033[33m{str}\033[39m'
|
|
9
|
+
|
|
10
|
+
def blue(str):
|
|
11
|
+
return f'\033[34m{str}\033[39m'
|
|
12
|
+
|
|
13
|
+
def bold(str):
|
|
14
|
+
return f'\033[1m{str}\033[22m'
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
from cppmakelib.basic.context import context
|
|
2
|
+
from cppmakelib.executor.operation import sync_wait
|
|
3
|
+
from cppmakelib.utility.filesystem import path, relative_path
|
|
4
|
+
import asyncio
|
|
5
|
+
import functools
|
|
6
|
+
import inspect
|
|
7
|
+
import threading
|
|
8
|
+
import typing
|
|
9
|
+
|
|
10
|
+
def implement [**Ts, R](func: typing.Callable[Ts, R]) -> typing.Callable[Ts, R] : ...
|
|
11
|
+
def member (cls: type) -> typing.Callable[[typing.Callable[..., typing.Any]], None] : ...
|
|
12
|
+
def once [S, R] (func: typing.Callable[[S], typing.Coroutine[typing.Any, typing.Any, R]]) -> typing.Callable[[S], typing.Coroutine[typing.Any, typing.Any, R]] : ...
|
|
13
|
+
def relocatable[S, R] (func: typing.Callable[[S, path], R]) -> typing.Callable[[S, path], R] : ...
|
|
14
|
+
def syncable [**Ts, R](func: typing.Callable[Ts, typing.Coroutine[typing.Any, typing.Any, R]]) -> typing.Callable[Ts, typing.Coroutine[typing.Any, typing.Any, R]] : ...
|
|
15
|
+
def unique [S] (func: typing.Callable[[S, path], None | typing.Coroutine[typing.Any, typing.Any, None]]) -> typing.Callable[[S, path], None | typing.Coroutine[typing.Any, typing.Any, None]]: ...
|
|
16
|
+
|
|
17
|
+
if typing.TYPE_CHECKING:
|
|
18
|
+
@typing.overload
|
|
19
|
+
def unique[S](func: typing.Callable[[S, path], None ]) -> typing.Callable[[S, path], None ]: ...
|
|
20
|
+
@typing.overload
|
|
21
|
+
def unique[S](func: typing.Callable[[S, path], typing.Coroutine[typing.Any, typing.Any, None]]) -> typing.Callable[[S, path], typing.Coroutine[typing.Any, typing.Any, None]]: ...
|
|
22
|
+
|
|
23
|
+
# Every project has its own kind of shit mountain,
|
|
24
|
+
# and the main difference lies in where those mountains are placed.
|
|
25
|
+
# Some pile them up in plain sight, fully transparent to users
|
|
26
|
+
# (for example, C++ templates, which exposes every bit of the mess
|
|
27
|
+
# from their inner implementations but gains 'zero-cost abstraction'),
|
|
28
|
+
# while others prefer to hide them deep in a corner where no one can ever reach
|
|
29
|
+
# (for example, the Python GIL).
|
|
30
|
+
#
|
|
31
|
+
# Here, in this project, we've gathered and neatly buried
|
|
32
|
+
# all our shit in the file below. :)
|
|
33
|
+
|
|
34
|
+
def implement[**Ts, R](func: typing.Callable[Ts, R]) -> typing.Callable[Ts, R]:
|
|
35
|
+
if inspect.isfunction(func):
|
|
36
|
+
return func
|
|
37
|
+
elif isinstance(func, _MultiFunc):
|
|
38
|
+
for subfunc in func.functions:
|
|
39
|
+
setattr(inspect.getmodule(subfunc), subfunc.__name__, subfunc)
|
|
40
|
+
return typing.cast(_MultiFunc[Ts, R], func)
|
|
41
|
+
else:
|
|
42
|
+
assert False
|
|
43
|
+
|
|
44
|
+
def member(cls: type) -> typing.Callable[[typing.Callable[..., typing.Any]], None]:
|
|
45
|
+
def memberizer(func: typing.Callable[..., typing.Any]) -> None:
|
|
46
|
+
if inspect.isfunction(func):
|
|
47
|
+
setattr(cls, func.__name__, func)
|
|
48
|
+
elif isinstance(func, _MultiFunc):
|
|
49
|
+
for subfunc in func.functions:
|
|
50
|
+
setattr(cls, subfunc.__name__, subfunc)
|
|
51
|
+
else:
|
|
52
|
+
assert False
|
|
53
|
+
return memberizer
|
|
54
|
+
|
|
55
|
+
def once[S, R](func: typing.Callable[[S], typing.Coroutine[typing.Any, typing.Any, R]]) -> typing.Callable[[S], typing.Coroutine[typing.Any, typing.Any, R]]:
|
|
56
|
+
async def once_func(self: S) -> R:
|
|
57
|
+
if not hasattr (self, f'_once_{func.__name__}'):
|
|
58
|
+
setattr (self, f'_once_{func.__name__}', asyncio.create_task(func(self)))
|
|
59
|
+
return await getattr(self, f'_once_{func.__name__}')
|
|
60
|
+
once_func.__name__ = func.__name__
|
|
61
|
+
return once_func
|
|
62
|
+
|
|
63
|
+
def relocatable[S, R](func: typing.Callable[[S, path], R]) -> typing.Callable[[S, path], R]:
|
|
64
|
+
def relocatable_func(self: S, path: path) -> R:
|
|
65
|
+
relocated_path = relative_path(from_path='.', to_path=f'{context.package.dir}/{path}')
|
|
66
|
+
return func(self, relocated_path)
|
|
67
|
+
relocatable_func.__name__ = func.__name__
|
|
68
|
+
return relocatable_func
|
|
69
|
+
|
|
70
|
+
def syncable[**Ts, R](func: typing.Callable[Ts, typing.Coroutine[typing.Any, typing.Any, R]]) -> typing.Callable[Ts, typing.Coroutine[typing.Any, typing.Any, R]]:
|
|
71
|
+
if inspect.isfunction(func):
|
|
72
|
+
assert func.__name__.startswith('async_') or func.__name__.startswith('__a')
|
|
73
|
+
def sync_func(*args: Ts.args, **kwargs: Ts.kwargs) -> R:
|
|
74
|
+
return sync_wait(func(*args, **kwargs))
|
|
75
|
+
sync_func.__name__ = func.__name__.removeprefix('async_') if func.__name__.startswith('async_') else func.__name__.replace('__a', '__')
|
|
76
|
+
return _MultiFunc(func, sync_func)
|
|
77
|
+
elif isinstance(func, _MultiFunc):
|
|
78
|
+
return _MultiFunc[Ts, typing.Coroutine[typing.Any, typing.Any, R]](*[syncable(subfunc) for subfunc in func.functions])
|
|
79
|
+
else:
|
|
80
|
+
assert False
|
|
81
|
+
|
|
82
|
+
def unique[S](func: typing.Callable[[S, path], None | typing.Coroutine[typing.Any, typing.Any, None]]) -> typing.Callable[[S, path], None | typing.Coroutine[typing.Any, typing.Any, None]]:
|
|
83
|
+
if inspect.isfunction(func) and not inspect.iscoroutinefunction(func):
|
|
84
|
+
assert func.__name__ == '__init__'
|
|
85
|
+
def unique_func(cls: type, path: path):
|
|
86
|
+
if not hasattr (cls, f'_unique'):
|
|
87
|
+
setattr (cls, f'_unique', {})
|
|
88
|
+
if path not in getattr(cls, f'_unique').keys():
|
|
89
|
+
getattr (cls, f'_unique')[path] = super(cls, cls).__new__(cls)
|
|
90
|
+
return getattr (cls, f'_unique')[path]
|
|
91
|
+
unique_func.__name__ = '__new__'
|
|
92
|
+
return _MultiFunc(func, unique_func)
|
|
93
|
+
elif inspect.iscoroutinefunction(func):
|
|
94
|
+
assert func.__name__ == '__ainit__'
|
|
95
|
+
async def unique_func(cls: type, path: path):
|
|
96
|
+
if not hasattr (cls, f'_unique'):
|
|
97
|
+
setattr (cls, f'_unique', {})
|
|
98
|
+
if path not in getattr(cls, f'_unique').keys():
|
|
99
|
+
getattr (cls, f'_unique')[path] = super(cls, cls).__new__(cls)
|
|
100
|
+
await getattr (cls, f'_unique')[path].__ainit__(path)
|
|
101
|
+
return getattr (cls, f'_unique')[path]
|
|
102
|
+
unique_func.__name__ = '__anew__'
|
|
103
|
+
return _MultiFunc(func, unique_func)
|
|
104
|
+
elif isinstance(func, _MultiFunc):
|
|
105
|
+
return _MultiFunc(*[unique(subfunc) for subfunc in func.functions])
|
|
106
|
+
else:
|
|
107
|
+
assert False
|
|
108
|
+
|
|
109
|
+
class _MultiFunc[**Ts, R]:
|
|
110
|
+
def __init__ (self, first: typing.Callable[Ts, R], *other: ...) -> None: ...
|
|
111
|
+
def __call__ (self, *args: Ts.args, **kwargs: Ts.kwargs) -> R : ...
|
|
112
|
+
functions: tuple[typing.Callable[Ts, R], ...]
|
|
113
|
+
|
|
114
|
+
@member(_MultiFunc)
|
|
115
|
+
def __init__[**Ts, R](self: _MultiFunc[Ts, R], first: typing.Callable[Ts, R], *other: ...) -> None:
|
|
116
|
+
self.functions = (first, *other)
|
|
117
|
+
|
|
118
|
+
@member(_MultiFunc)
|
|
119
|
+
def __call__[**Ts, R](self: _MultiFunc[Ts, R], *args: Ts.args, **kwargs: Ts.kwargs) -> R:
|
|
120
|
+
return self.functions[0](*args, **kwargs)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from cppmakelib.utility.time import time
|
|
2
|
+
import os
|
|
3
|
+
import shutil
|
|
4
|
+
import typing
|
|
5
|
+
|
|
6
|
+
path = str
|
|
7
|
+
|
|
8
|
+
def absolute_path (path : path) -> path : ...
|
|
9
|
+
def copy_dir (from_dir : path, to_dir : path) -> None : ...
|
|
10
|
+
def copy_file (from_file: path, to_file: path) -> None : ...
|
|
11
|
+
def create_dir (dir : path) -> None : ...
|
|
12
|
+
def create_file (file : path) -> None : ...
|
|
13
|
+
def iterate_dir (dir : path) -> typing.Iterable[path]: ...
|
|
14
|
+
def exist_dir (dir : path) -> bool : ...
|
|
15
|
+
def exist_file (file : path) -> bool : ...
|
|
16
|
+
def modified_time_file(file : path) -> time : ...
|
|
17
|
+
def parent_dir (path : path) -> path : ...
|
|
18
|
+
def relative_path (from_path: path, to_path: path) -> path : ...
|
|
19
|
+
def remove_dir (dir : path) -> None : ...
|
|
20
|
+
def remove_file (file : path) -> None : ...
|
|
21
|
+
def root_dir (path : path) -> path : ...
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def absolute_path(path: path) -> path:
|
|
26
|
+
return os.path.abspath(path)
|
|
27
|
+
|
|
28
|
+
def copy_dir(from_dir: path, to_dir: path) -> None:
|
|
29
|
+
shutil.copytree(from_dir, to_dir, dirs_exist_ok=True)
|
|
30
|
+
|
|
31
|
+
def copy_file(from_file: path, to_file: path) -> None:
|
|
32
|
+
shutil.copyfile(from_file, to_file)
|
|
33
|
+
|
|
34
|
+
def create_dir(dir: path) -> None:
|
|
35
|
+
os.makedirs(dir, exist_ok=True)
|
|
36
|
+
|
|
37
|
+
def create_file(file: path) -> None:
|
|
38
|
+
create_dir(parent_dir(file))
|
|
39
|
+
open(file, 'w')
|
|
40
|
+
|
|
41
|
+
def iterate_dir(dir: path) -> typing.Iterable[path]:
|
|
42
|
+
for root, _, filenames in os.walk(dir):
|
|
43
|
+
for filename in filenames:
|
|
44
|
+
yield f'{path(root)}/{filename}'
|
|
45
|
+
|
|
46
|
+
def exist_dir(dir: path) -> bool:
|
|
47
|
+
return os.path.isdir(dir)
|
|
48
|
+
|
|
49
|
+
def exist_file(file: path) -> bool:
|
|
50
|
+
return os.path.isfile(file)
|
|
51
|
+
|
|
52
|
+
def modified_time_file(file: path) -> time:
|
|
53
|
+
return os.stat(file).st_mtime_ns
|
|
54
|
+
|
|
55
|
+
def parent_dir(self: path) -> path:
|
|
56
|
+
return os.path.dirname(self)
|
|
57
|
+
|
|
58
|
+
def relative_path(from_path: path, to_path: path) -> path:
|
|
59
|
+
return os.path.relpath(to_path, from_path)
|
|
60
|
+
|
|
61
|
+
def remove_dir(dir: path) -> None:
|
|
62
|
+
shutil.rmtree(dir, ignore_errors=True)
|
|
63
|
+
|
|
64
|
+
def remove_file(file: path) -> None:
|
|
65
|
+
try:
|
|
66
|
+
os.remove(file)
|
|
67
|
+
except FileNotFoundError:
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
def root_dir(path: path) -> path:
|
|
71
|
+
return os.path.splitroot(path)[0]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from cppmakelib.error.config import ConfigError
|
|
2
|
+
from cppmakelib.utility.filesystem import path
|
|
3
|
+
import importlib.util
|
|
4
|
+
import types
|
|
5
|
+
import typing
|
|
6
|
+
|
|
7
|
+
def import_module(file: path, globals: dict[str, typing.Any] = {}) -> types.ModuleType: ...
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def import_module(file: path, globals: dict[str, typing.Any] = {}) -> types.ModuleType:
|
|
12
|
+
try:
|
|
13
|
+
spec = importlib.util.spec_from_file_location(name=file, location=file)
|
|
14
|
+
except FileNotFoundError as error:
|
|
15
|
+
raise ConfigError(f'cppmake.py is not found') from error
|
|
16
|
+
if spec is None or spec.loader is None:
|
|
17
|
+
raise ConfigError(f'cppmake.py is not loaded (with file = {file})')
|
|
18
|
+
module = importlib.util.module_from_spec(spec)
|
|
19
|
+
module.__dict__.update(globals)
|
|
20
|
+
spec.loader.exec_module(module)
|
|
21
|
+
return module
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from cppmakelib.utility.filesystemimport path
|
|
2
|
+
from cppmakelib.utility.decorator import member
|
|
3
|
+
|
|
4
|
+
class Protocol:
|
|
5
|
+
async def async_send_file (self, file: path): ...
|
|
6
|
+
async def async_receive_file(self, file: path): ...
|
|
7
|
+
def relocate_client(self, file: path) -> str : ...
|
|
8
|
+
def relocate_server(self, file: str ) -> path: ...
|
|
9
|
+
|
|
10
|
+
class _Datagram:
|
|
11
|
+
...
|
|
12
|
+
async def _async_send_datagram(self, datagram: _Datagram) -> None : ...
|
|
13
|
+
async def _async_send_datagram(self) -> _Datagram: ...
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@member(Protocol)
|
|
18
|
+
async def async_send_file(self: Protocol, file: path):
|
|
19
|
+
with open(self.relocate_client(file), 'rb') as reader:
|
|
20
|
+
content = reader.read()
|
|
21
|
+
datagram = Protocol._Datagram({Protocol._Datagram{
|
|
22
|
+
"file" : self.reloate_client(file),
|
|
23
|
+
"content": content
|
|
24
|
+
})
|
|
25
|
+
await self._async_send_datagram(datagram)
|
|
26
|
+
|
|
27
|
+
@member(Protocol)
|
|
28
|
+
async def async_receive_file(self: Protocol, file: path):
|
|
29
|
+
datagram = await self._async_receive_datagram()
|
|
30
|
+
with open(self.relocate_server(file), 'wb') as writer:
|
|
31
|
+
writer.write(bytes)
|
|
32
|
+
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from cppmakelib.compiler.all import compiler
|
|
2
|
+
from cppmakelib.utility.filesystemimport path
|
|
3
|
+
from cppmakelib.utility.decorator import member
|
|
4
|
+
|
|
5
|
+
class RemoteCompiler:
|
|
6
|
+
def __init__(self, protocol: Protocol): ...
|
|
7
|
+
def preprocess(): ...
|
|
8
|
+
def precompile(): ...
|
|
9
|
+
def compile (): ...
|
|
10
|
+
def link (): ...
|
|
11
|
+
|
|
12
|
+
class DistributedCompiler:
|
|
13
|
+
def __init__(self, local_compiler: ..., remote_compilers: list[...]): ...
|
|
14
|
+
def preprocess(): ...
|
|
15
|
+
def precompile(): ...
|
|
16
|
+
def compile (): ...
|
|
17
|
+
def link (): ...
|
|
18
|
+
|
|
19
|
+
local_compiler = compiler
|
|
20
|
+
remote_compilers = [RemoteCompiler(compiler) for compiler in config.remote_compilers]
|
|
21
|
+
compiler = DistributeCompiler(local_compiler, remote_compilers)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@member(RemoteCompiler)
|
|
26
|
+
def preprocess(self: RemoteCompiler):
|
|
27
|
+
return local_compiler.preprocess()
|
|
28
|
+
|
|
29
|
+
@member(RemoteCompiler)
|
|
30
|
+
def precompile(
|
|
31
|
+
self : RemoteCompiler,
|
|
32
|
+
module_file : path,
|
|
33
|
+
precompiled_file: path,
|
|
34
|
+
object_file : path,
|
|
35
|
+
compiler_flags : list[str],
|
|
36
|
+
define_macros : dict[str, str],
|
|
37
|
+
include_dirs : list[path],
|
|
38
|
+
import_dirs : list[path]
|
|
39
|
+
) -> None:
|
|
40
|
+
await when_all(self.protocol.async_send_file(file) for file in recursive_collect(import_files...))
|
|
41
|
+
await self.protocol.async_send_command(precompile, module_file, precompiled_file, object_file, ...)
|
|
42
|
+
await self.protocol.async_receive_file(object_file)
|
|
43
|
+
|