powermake 1.0.0__tar.gz
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.
- powermake-1.0.0/.gitignore +3 -0
- powermake-1.0.0/PKG-INFO +83 -0
- powermake-1.0.0/README.md +70 -0
- powermake-1.0.0/powermake/__init__.py +285 -0
- powermake-1.0.0/powermake/architecture.py +33 -0
- powermake-1.0.0/powermake/archivers.py +68 -0
- powermake-1.0.0/powermake/args_parser.py +184 -0
- powermake-1.0.0/powermake/compilers.py +123 -0
- powermake-1.0.0/powermake/config.py +301 -0
- powermake-1.0.0/powermake/linkers.py +84 -0
- powermake-1.0.0/powermake/operation.py +164 -0
- powermake-1.0.0/powermake/search_visual_studio.py +175 -0
- powermake-1.0.0/powermake/tools.py +62 -0
- powermake-1.0.0/pyproject.toml +22 -0
- powermake-1.0.0/tests/test.c +8 -0
- powermake-1.0.0/tests/test.py +13 -0
powermake-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: powermake
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Yet another makefile tool. This one is meant to be super fast, super easy and crossplatform while leaving full power to the user at any time.
|
|
5
|
+
Project-URL: Homepage, https://github.com/mactul/powermake
|
|
6
|
+
Project-URL: Issues, https://github.com/mactul/powermake/issues
|
|
7
|
+
Author: Macéo Tuloup
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Requires-Python: >=3.6
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# PowerMake
|
|
15
|
+
|
|
16
|
+
## What is PowerMake ?
|
|
17
|
+
|
|
18
|
+
Powermake is an utility to compile C/C++ code, just like Make, Ninja, cmake or xmake.
|
|
19
|
+
|
|
20
|
+
His goal is to give full power to the user, while being cross-platform, easier to use than Make and faster than Ninja.
|
|
21
|
+
|
|
22
|
+
## For which project is PowerMake suitable ?
|
|
23
|
+
|
|
24
|
+
PowerMake was specifically designed for all the complex projects that have very complicated compilation steps, with a lot of pre-built tasks and which need to be compiled on multiple operating systems with different options.
|
|
25
|
+
|
|
26
|
+
## Advantages of PowerMake
|
|
27
|
+
|
|
28
|
+
- Extremely fast:
|
|
29
|
+
- PowerMake is approximately 2x faster than Ninja, at least on relatively small projects
|
|
30
|
+
- With a project that has 10 000 lines of C:
|
|
31
|
+
- Ninja takes 1.484 seconds
|
|
32
|
+
- PowerMake takes 0.649 seconds
|
|
33
|
+
|
|
34
|
+
- Cross-Platform:
|
|
35
|
+
- PowerMake is able to detect the compiler installed on your machine and give you an abstraction of the compiler syntax.
|
|
36
|
+
- This currently works well with GCC/G++/Clang/Clang++/MSVC, but other compilers will be add.
|
|
37
|
+
- Because it's written in python it works in almost all machine and you can always write the compilation instructions for your machine and for your compiler.
|
|
38
|
+
|
|
39
|
+
- Gives you complete control of what you are doing. Nothing is hidden and any behavior can be overwritten.
|
|
40
|
+
|
|
41
|
+
## Disadvantages of PowerMake
|
|
42
|
+
|
|
43
|
+
- PowerMake is very young so it changes a lot at each version and you may have to write some features by yourself (the whole point of PowerMake is that you can write missing features).
|
|
44
|
+
|
|
45
|
+
- Because PowerMake gives you full control, the tool can't really now what's you are doing during the compilation step. For example, if we want to import dependencies from another PowerMake, the only thing we can do for you is running the PowerMake where it stands and scanning his output directory. This works well but has some limitations...
|
|
46
|
+
|
|
47
|
+
## Philosophy
|
|
48
|
+
|
|
49
|
+
All other Make-like utilities that I know parse a file to understand directives from it.
|
|
50
|
+
|
|
51
|
+
PowerMake does the opposite. You write a python script, you do whatever you like in this script and you call PowerMake functions to help you compiling your code.
|
|
52
|
+
This gives you a complete control; you can retrieve files from the web, read an write files, even train a Neural Network if you want and at any time you can use Powermake functions to help you in your compilation journey.
|
|
53
|
+
|
|
54
|
+
## Installation
|
|
55
|
+
|
|
56
|
+
Documentation in coming...
|
|
57
|
+
|
|
58
|
+
## Quick Example
|
|
59
|
+
|
|
60
|
+
This example compile all `.c` and `.cpp` files that are recursively in the same folder as the python script and generate an executable named `program_test`
|
|
61
|
+
|
|
62
|
+
**Warning !** PowerMake calculate all paths from his own location, not the location where it is run.
|
|
63
|
+
For example, `python ./folder/makefile.py` will do the same as `cd ./folder && python ./makefile.py`
|
|
64
|
+
|
|
65
|
+
```py
|
|
66
|
+
import powermake
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def on_build(config: powermake.Config):
|
|
70
|
+
|
|
71
|
+
files = powermake.get_files("**/*.c", "**/*.cpp")
|
|
72
|
+
|
|
73
|
+
objects = powermake.compile_files(config, files)
|
|
74
|
+
|
|
75
|
+
print(powermake.link_files(config, objects))
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
powermake.run("program_test", build_callback=on_build)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Documentation
|
|
82
|
+
|
|
83
|
+
Documentation in coming...
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# PowerMake
|
|
2
|
+
|
|
3
|
+
## What is PowerMake ?
|
|
4
|
+
|
|
5
|
+
Powermake is an utility to compile C/C++ code, just like Make, Ninja, cmake or xmake.
|
|
6
|
+
|
|
7
|
+
His goal is to give full power to the user, while being cross-platform, easier to use than Make and faster than Ninja.
|
|
8
|
+
|
|
9
|
+
## For which project is PowerMake suitable ?
|
|
10
|
+
|
|
11
|
+
PowerMake was specifically designed for all the complex projects that have very complicated compilation steps, with a lot of pre-built tasks and which need to be compiled on multiple operating systems with different options.
|
|
12
|
+
|
|
13
|
+
## Advantages of PowerMake
|
|
14
|
+
|
|
15
|
+
- Extremely fast:
|
|
16
|
+
- PowerMake is approximately 2x faster than Ninja, at least on relatively small projects
|
|
17
|
+
- With a project that has 10 000 lines of C:
|
|
18
|
+
- Ninja takes 1.484 seconds
|
|
19
|
+
- PowerMake takes 0.649 seconds
|
|
20
|
+
|
|
21
|
+
- Cross-Platform:
|
|
22
|
+
- PowerMake is able to detect the compiler installed on your machine and give you an abstraction of the compiler syntax.
|
|
23
|
+
- This currently works well with GCC/G++/Clang/Clang++/MSVC, but other compilers will be add.
|
|
24
|
+
- Because it's written in python it works in almost all machine and you can always write the compilation instructions for your machine and for your compiler.
|
|
25
|
+
|
|
26
|
+
- Gives you complete control of what you are doing. Nothing is hidden and any behavior can be overwritten.
|
|
27
|
+
|
|
28
|
+
## Disadvantages of PowerMake
|
|
29
|
+
|
|
30
|
+
- PowerMake is very young so it changes a lot at each version and you may have to write some features by yourself (the whole point of PowerMake is that you can write missing features).
|
|
31
|
+
|
|
32
|
+
- Because PowerMake gives you full control, the tool can't really now what's you are doing during the compilation step. For example, if we want to import dependencies from another PowerMake, the only thing we can do for you is running the PowerMake where it stands and scanning his output directory. This works well but has some limitations...
|
|
33
|
+
|
|
34
|
+
## Philosophy
|
|
35
|
+
|
|
36
|
+
All other Make-like utilities that I know parse a file to understand directives from it.
|
|
37
|
+
|
|
38
|
+
PowerMake does the opposite. You write a python script, you do whatever you like in this script and you call PowerMake functions to help you compiling your code.
|
|
39
|
+
This gives you a complete control; you can retrieve files from the web, read an write files, even train a Neural Network if you want and at any time you can use Powermake functions to help you in your compilation journey.
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
Documentation in coming...
|
|
44
|
+
|
|
45
|
+
## Quick Example
|
|
46
|
+
|
|
47
|
+
This example compile all `.c` and `.cpp` files that are recursively in the same folder as the python script and generate an executable named `program_test`
|
|
48
|
+
|
|
49
|
+
**Warning !** PowerMake calculate all paths from his own location, not the location where it is run.
|
|
50
|
+
For example, `python ./folder/makefile.py` will do the same as `cd ./folder && python ./makefile.py`
|
|
51
|
+
|
|
52
|
+
```py
|
|
53
|
+
import powermake
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def on_build(config: powermake.Config):
|
|
57
|
+
|
|
58
|
+
files = powermake.get_files("**/*.c", "**/*.cpp")
|
|
59
|
+
|
|
60
|
+
objects = powermake.compile_files(config, files)
|
|
61
|
+
|
|
62
|
+
print(powermake.link_files(config, objects))
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
powermake.run("program_test", build_callback=on_build)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Documentation
|
|
69
|
+
|
|
70
|
+
Documentation in coming...
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import glob
|
|
4
|
+
import fnmatch
|
|
5
|
+
import subprocess
|
|
6
|
+
import importlib.util
|
|
7
|
+
import __main__ as __makefile__
|
|
8
|
+
from threading import Lock
|
|
9
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
10
|
+
|
|
11
|
+
from .config import Config
|
|
12
|
+
from .operation import Operation, needs_update
|
|
13
|
+
from .args_parser import run, default_on_clean, default_on_install
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
os.chdir(os.path.dirname(os.path.realpath(__makefile__.__file__)))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def import_module(module_name: str, module_path: str = None):
|
|
20
|
+
"""Import a custom module from a path
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
module_name (str): The name of the module once it will be imported
|
|
24
|
+
module_path (str, optional): The path of the module, if None, it takes the module_name as a path.
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
module: an module object, that you can use as a namespace
|
|
28
|
+
"""
|
|
29
|
+
spec = importlib.util.spec_from_file_location(module_name, module_path)
|
|
30
|
+
module = importlib.util.module_from_spec(spec)
|
|
31
|
+
spec.loader.exec_module(module)
|
|
32
|
+
|
|
33
|
+
return module
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def get_files(*patterns: str) -> set[str]:
|
|
37
|
+
"""Return all files on the disk that matches one of the patterns given
|
|
38
|
+
|
|
39
|
+
Supported patterns are `*`, like `./*.c` and `**/` like `./**/*.c` to search all .c recursively, starting at `./`
|
|
40
|
+
|
|
41
|
+
Warning: `**.c` will not return all .c recursively, you have to use `**/*.c` for that.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
patterns (str): As many patterns as you want to include
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
set[str]: A set (unordered list) of files. You can use the methods `add` and `update` to add files to this set.
|
|
48
|
+
"""
|
|
49
|
+
files = set()
|
|
50
|
+
for pattern in patterns:
|
|
51
|
+
files.update(glob.glob(pattern, recursive=True))
|
|
52
|
+
return files
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def filter_files(files: set[str], *patterns: str) -> set[str]:
|
|
56
|
+
"""Create a copy of the set `files` with all elements that matches `pattern` removed.
|
|
57
|
+
|
|
58
|
+
This function will equally works if `files` is an iterable but will always returns a set.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
files (set[str]): The set of files to filter
|
|
62
|
+
patterns (str): As many patterns as you want to exclude
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
set[str]: the filtered set
|
|
66
|
+
"""
|
|
67
|
+
output = set()
|
|
68
|
+
for file in files:
|
|
69
|
+
for pattern in patterns:
|
|
70
|
+
if not fnmatch.fnmatch(file, pattern):
|
|
71
|
+
output.add(file)
|
|
72
|
+
return output
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def file_in_files_set(file: str, files_set: set[str]) -> bool:
|
|
76
|
+
for f in files_set:
|
|
77
|
+
if os.path.samefile(f, file):
|
|
78
|
+
return True
|
|
79
|
+
return False
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def compile_files(config: Config, files: set[str], force: bool = None) -> set[str]:
|
|
83
|
+
"""Compile each C/C++ file in the `files` set according to the compiler and options stored in `config`
|
|
84
|
+
|
|
85
|
+
The compilation is parallelized with `config.nb_jobs` threads
|
|
86
|
+
|
|
87
|
+
This function will equally works if `files` is an iterable but will always returns a set.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
config (Config): A powermake.Config object, containing all directives for the compilation. Either the one given to the build_callback or a modified copy.
|
|
91
|
+
files (set[str]): A set of files that ends by .c, .cpp, .cc or .C to compile in .o (or the specified compiler equivalent extension)
|
|
92
|
+
force (bool, optional): Whether the function should verify if a file needs to be recompiled or if it should recompile everything.
|
|
93
|
+
If not specified, this parameter takes the value of config.rebuild
|
|
94
|
+
|
|
95
|
+
Raises:
|
|
96
|
+
RuntimeError: if no compiler is found
|
|
97
|
+
ValueError: if `files` contains a file that doesn't ends with .c, .cpp, .cc or .C
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
set[str]: A set of object filepaths generated by by the compilation
|
|
101
|
+
"""
|
|
102
|
+
generated_objects: set[str] = set()
|
|
103
|
+
operations: set[Operation] = set()
|
|
104
|
+
|
|
105
|
+
if force is None:
|
|
106
|
+
force = config.rebuild
|
|
107
|
+
|
|
108
|
+
if config.single_file is not None:
|
|
109
|
+
if file_in_files_set(config.single_file, files):
|
|
110
|
+
files = {config.single_file}
|
|
111
|
+
else:
|
|
112
|
+
return set()
|
|
113
|
+
|
|
114
|
+
for file in files:
|
|
115
|
+
output_file = os.path.normpath(config.obj_build_directory + "/" + file.replace("..", "__") + config.c_compiler.obj_extension)
|
|
116
|
+
os.makedirs(os.path.dirname(output_file), exist_ok=True)
|
|
117
|
+
|
|
118
|
+
if config.c_compiler is not None:
|
|
119
|
+
c_args = config.c_compiler.format_args(config.defines, config.additional_includedirs, config.c_flags + config.c_cpp_flags)
|
|
120
|
+
else:
|
|
121
|
+
c_args = None
|
|
122
|
+
|
|
123
|
+
if config.cpp_compiler is not None:
|
|
124
|
+
cpp_args = config.cpp_compiler.format_args(config.defines, config.additional_includedirs, config.cpp_flags + config.c_cpp_flags)
|
|
125
|
+
else:
|
|
126
|
+
cpp_args = None
|
|
127
|
+
|
|
128
|
+
if file.endswith(".c"):
|
|
129
|
+
if config.c_compiler is None:
|
|
130
|
+
raise RuntimeError("No C compiler has been specified and the default config didn't find any")
|
|
131
|
+
command = config.c_compiler.basic_compile_command(output_file, file, c_args)
|
|
132
|
+
elif file.endswith((".cpp", ".cc", ".C")):
|
|
133
|
+
if config.cpp_compiler is None:
|
|
134
|
+
raise RuntimeError("No C++ compiler has been specified and the default config didn't find any")
|
|
135
|
+
command = config.cpp_compiler.basic_compile_command(output_file, file, cpp_args)
|
|
136
|
+
else:
|
|
137
|
+
raise ValueError("The file extension %s can't be compiled", (os.path.splitext(file)[1], ))
|
|
138
|
+
operations.add(Operation(output_file, [file], config, command))
|
|
139
|
+
|
|
140
|
+
if config.single_file is not None:
|
|
141
|
+
(op, ) = operations
|
|
142
|
+
op.execute(force)
|
|
143
|
+
exit(0)
|
|
144
|
+
|
|
145
|
+
print_lock = Lock()
|
|
146
|
+
with ThreadPoolExecutor(max_workers=config.nb_jobs) as executor:
|
|
147
|
+
generated_objects = set(executor.map(lambda op: op.execute(force, print_lock), operations))
|
|
148
|
+
|
|
149
|
+
if False in generated_objects:
|
|
150
|
+
generated_objects = False
|
|
151
|
+
|
|
152
|
+
return generated_objects
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def archive_files(config: Config, object_files: set[str], archive_name: str = None, force: bool = None) -> str:
|
|
156
|
+
"""Create a static library from A set of object files.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
config (Config): A powermake.Config object, containing all directives for the compilation. Either the one given to the build_callback or a modified copy.
|
|
160
|
+
object_files (set[str]): A set of object files, potentially the set generated by `compile_files`
|
|
161
|
+
archive_name (str, optional): The name of the static library you want to create, minus the extension. If None, it will be lib{config.target_name}
|
|
162
|
+
force (bool, optional): Whether the function should verify if the static library needs to be re-archived or if it should re-archive in any case.
|
|
163
|
+
If not specified, this parameter takes the value of config.rebuild
|
|
164
|
+
|
|
165
|
+
Raises:
|
|
166
|
+
RuntimeError: if no archiver is found
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
str: the path of the archive generated
|
|
170
|
+
"""
|
|
171
|
+
if force is None:
|
|
172
|
+
force = config.rebuild
|
|
173
|
+
|
|
174
|
+
if archive_name is None:
|
|
175
|
+
archive_name = "lib"+config.target_name
|
|
176
|
+
|
|
177
|
+
if config.archiver is None:
|
|
178
|
+
raise RuntimeError("No archiver has been specified and the default config didn't find any")
|
|
179
|
+
output_file = os.path.normpath(config.lib_build_directory + "/" + archive_name + config.archiver.static_lib_extension)
|
|
180
|
+
os.makedirs(os.path.dirname(output_file), exist_ok=True)
|
|
181
|
+
command = config.archiver.basic_archive_command(output_file, object_files)
|
|
182
|
+
return Operation(output_file, object_files, config, command).execute(force=force)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def link_files(config: Config, object_files: set[str], archives: list[str] = [], executable_name: str = None, force: bool = None) -> str:
|
|
186
|
+
"""Create an executable from a list of object files and a list of archive files.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
config (Config): A powermake.Config object, containing all directives for the compilation. Either the one given to the build_callback or a modified copy.
|
|
190
|
+
object_files (set[str]): A set of object files, potentially the set generated by `compile_files`
|
|
191
|
+
archives (list[str]): A set of static libraries filepaths
|
|
192
|
+
executable_name (str, optional): The name of the executable you want to create, minus the extension. If None, it will be config.target_name
|
|
193
|
+
force (bool, optional): Whether the function should verify if the executable needs to be re-linked or if it should re-link in any case.
|
|
194
|
+
If not specified, this parameter takes the value of config.rebuild
|
|
195
|
+
|
|
196
|
+
Raises:
|
|
197
|
+
RuntimeError: if no linker is found
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
str: the path of the executable generated
|
|
201
|
+
"""
|
|
202
|
+
if force is None:
|
|
203
|
+
force = config.rebuild
|
|
204
|
+
|
|
205
|
+
if executable_name is None:
|
|
206
|
+
executable_name = config.target_name
|
|
207
|
+
|
|
208
|
+
if config.linker is None:
|
|
209
|
+
raise RuntimeError("No linker has been specified and the default config didn't find any")
|
|
210
|
+
output_file = os.path.normpath(config.exe_build_directory + "/" + executable_name + config.linker.exe_extension)
|
|
211
|
+
os.makedirs(os.path.dirname(output_file), exist_ok=True)
|
|
212
|
+
command = config.linker.basic_link_command(output_file, object_files, archives)
|
|
213
|
+
return Operation(output_file, object_files.union(archives), config, command).execute(force=force)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def delete_files_from_disk(*filepaths: str) -> None:
|
|
217
|
+
"""Delete a set of filepaths from the disk and ignore them if they don't exists
|
|
218
|
+
"""
|
|
219
|
+
for filepath in filepaths:
|
|
220
|
+
try:
|
|
221
|
+
os.remove(filepath)
|
|
222
|
+
except OSError:
|
|
223
|
+
pass
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def run_another_powermake(config: Config, path: str, debug: bool = None, rebuild: bool = None, verbosity: int = None, nb_jobs: int = None) -> list[str]:
|
|
227
|
+
"""Run a powermake from another directory and returns a list of path to all libraries generated
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
config (Config): A powermake.Config object, containing all directives for the compilation. Either the one given to the build_callback or a modified copy.
|
|
231
|
+
path (str): The path of the powermake to run
|
|
232
|
+
debug (bool, optional): Whether the other powermake should be run in debug mode.
|
|
233
|
+
If not specified, this parameter takes the value of config.debug
|
|
234
|
+
rebuild (bool, optional): Whether the other powermake should be run in rebuild mode.
|
|
235
|
+
If not specified, this parameter takes the value of config.rebuild
|
|
236
|
+
verbosity (int, optional): With which verbosity level the other powermake should be run.
|
|
237
|
+
If not specified, this parameter takes the value of config.verbosity
|
|
238
|
+
nb_jobs (int, optional): With how many threads the other powermake should be run.
|
|
239
|
+
If not specified, this parameter takes the value of config.nb_jobs
|
|
240
|
+
|
|
241
|
+
Raises:
|
|
242
|
+
RuntimeError: if the other powermake fails
|
|
243
|
+
|
|
244
|
+
Returns:
|
|
245
|
+
list[str]: A list of path to all libraries generated
|
|
246
|
+
"""
|
|
247
|
+
if debug is None:
|
|
248
|
+
debug = config.debug
|
|
249
|
+
if rebuild is None:
|
|
250
|
+
rebuild = config.rebuild
|
|
251
|
+
if verbosity is None:
|
|
252
|
+
verbosity = config.verbosity
|
|
253
|
+
if nb_jobs is None:
|
|
254
|
+
nb_jobs = config.nb_jobs
|
|
255
|
+
|
|
256
|
+
command = [sys.executable, path, "--get-lib-build-folder", "-j", str(nb_jobs)]
|
|
257
|
+
if verbosity == 0:
|
|
258
|
+
command.append("-q")
|
|
259
|
+
elif verbosity >= 2:
|
|
260
|
+
command.append("-v")
|
|
261
|
+
|
|
262
|
+
if rebuild:
|
|
263
|
+
command.append("-r")
|
|
264
|
+
|
|
265
|
+
if debug:
|
|
266
|
+
command.append("-d")
|
|
267
|
+
|
|
268
|
+
if config.verbosity >= 1:
|
|
269
|
+
print("Running", path)
|
|
270
|
+
if config.verbosity >= 2:
|
|
271
|
+
print(command)
|
|
272
|
+
|
|
273
|
+
try:
|
|
274
|
+
output = subprocess.check_output(command, encoding="utf-8").splitlines()
|
|
275
|
+
except OSError:
|
|
276
|
+
raise RuntimeError(f"Failed to run powermake {path}")
|
|
277
|
+
|
|
278
|
+
if verbosity != 0:
|
|
279
|
+
for line in output[:-1]:
|
|
280
|
+
print(line)
|
|
281
|
+
|
|
282
|
+
path = output[-1]
|
|
283
|
+
if path != "":
|
|
284
|
+
return [os.path.join(path, file) for file in os.listdir(path)]
|
|
285
|
+
return None
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# - applexros: arm64 armv7 armv7s i386 x86_64
|
|
2
|
+
# - windows: x86 x64 arm64
|
|
3
|
+
# - appletvos: arm64 armv7 armv7s i386 x86_64
|
|
4
|
+
# - cross: i386 x86_64 arm arm64 mips mips64 riscv riscv64 loong64 s390x ppc ppc64 sh4
|
|
5
|
+
# - mingw: i386 x86_64 arm arm64
|
|
6
|
+
# - msys: i386 x86_64
|
|
7
|
+
# - linux: i386 x86_64 armv7 armv7s arm64-v8a mips mips64 mipsel mips64el loong64
|
|
8
|
+
# - harmony: armeabi-v7a arm64-v8a x86 x86_64
|
|
9
|
+
# - watchos: armv7k i386
|
|
10
|
+
# - wasm: wasm32 wasm64
|
|
11
|
+
# - iphoneos: arm64 x86_64
|
|
12
|
+
# - haiku: i386 x86_64
|
|
13
|
+
# - bsd: i386 x86_64
|
|
14
|
+
# - android: armeabi armeabi-v7a arm64-v8a x86 x86_64 mips mip64
|
|
15
|
+
# - macosx: x86_64 arm64
|
|
16
|
+
# - cygwin: i386 x86_64
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def simplify_architecture(architecture: str) -> str:
|
|
20
|
+
arch = architecture.lower()
|
|
21
|
+
if arch in ["x86", "x32", "80x86", "8086", "80386", "i286", "i386", "i486", "i586", "i686", "i786", "amd386", "am386", "amd486", "am486", "amd-k5", "amd-k6", "amd-k7"]:
|
|
22
|
+
return "x86"
|
|
23
|
+
|
|
24
|
+
if arch in ["x86_64", "x86-64", "x64", "amd64", "intel64"]:
|
|
25
|
+
return "x64"
|
|
26
|
+
|
|
27
|
+
if arch in ["arm32", "arm", "armv6", "armv6-m", "armv7a", "armv7s", "armv7m", "armv7r", "armv7-a", "armv7-m", "armv7-r", "armeabi", "armeabi-v7a"]:
|
|
28
|
+
return "arm32"
|
|
29
|
+
|
|
30
|
+
if arch in ["arm64", "armv8-a", "armv8.2-a", "armv8.3-a", "armv8-m", "armv8-r"]:
|
|
31
|
+
return "arm64"
|
|
32
|
+
|
|
33
|
+
return None
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
|
|
3
|
+
from .tools import Tool
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Archiver(Tool, abc.ABC):
|
|
7
|
+
static_lib_extension = None
|
|
8
|
+
|
|
9
|
+
def __init__(self, path):
|
|
10
|
+
Tool.__init__(self, path)
|
|
11
|
+
|
|
12
|
+
@abc.abstractmethod
|
|
13
|
+
def basic_archive_command(self, outputfile: str, inputfiles: set[str], args: list[str] = []) -> list[str]:
|
|
14
|
+
return []
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ArchiverGNU(Archiver):
|
|
18
|
+
type = "gnu"
|
|
19
|
+
static_lib_extension = ".a"
|
|
20
|
+
|
|
21
|
+
def __init__(self, path: str = "ar"):
|
|
22
|
+
super().__init__(path)
|
|
23
|
+
|
|
24
|
+
def basic_archive_command(self, outputfile: str, inputfiles: set[str], args: list[str] = []) -> list[str]:
|
|
25
|
+
return [self.path, "-cr", outputfile, *inputfiles, *args]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ArchiverAR(ArchiverGNU):
|
|
29
|
+
type = "ar"
|
|
30
|
+
|
|
31
|
+
def __init__(self, path: str = "ar"):
|
|
32
|
+
super().__init__(path)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class ArchiverLLVM_AR(ArchiverGNU):
|
|
36
|
+
type = "llvm-ar"
|
|
37
|
+
|
|
38
|
+
def __init__(self, path: str = "llvm-ar"):
|
|
39
|
+
super().__init__(path)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class ArchiverMSVC(Archiver):
|
|
43
|
+
type = "msvc"
|
|
44
|
+
static_lib_extension = ".lib"
|
|
45
|
+
|
|
46
|
+
def __init__(self, path: str = "lib"):
|
|
47
|
+
super().__init__(path)
|
|
48
|
+
|
|
49
|
+
def basic_archive_command(self, outputfile: str, inputfiles: set[str], args: list[str] = []) -> list[str]:
|
|
50
|
+
return [self.path, "/nologo", *args, "/out:"+outputfile, *inputfiles]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
_archiver_types: dict[str, Archiver] = {
|
|
54
|
+
"gnu": ArchiverGNU,
|
|
55
|
+
"ar": ArchiverAR,
|
|
56
|
+
"llvm-ar": ArchiverAR,
|
|
57
|
+
"msvc": ArchiverMSVC
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def GenericArchiver(archiver_type: str) -> Archiver:
|
|
62
|
+
if archiver_type not in _archiver_types:
|
|
63
|
+
return None
|
|
64
|
+
return _archiver_types[archiver_type]
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def get_all_archiver_types() -> set[str]:
|
|
68
|
+
return _archiver_types.keys()
|