IncludeCPP 2.5.2__tar.gz → 2.7.2__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.
- {includecpp-2.5.2 → includecpp-2.7.2}/IncludeCPP.egg-info/PKG-INFO +16 -5
- {includecpp-2.5.2 → includecpp-2.7.2}/IncludeCPP.egg-info/SOURCES.txt +4 -0
- {includecpp-2.5.2 → includecpp-2.7.2}/PKG-INFO +16 -5
- {includecpp-2.5.2 → includecpp-2.7.2}/README.md +15 -4
- includecpp-2.7.2/includecpp/__init__.py +58 -0
- {includecpp-2.5.2 → includecpp-2.7.2}/includecpp/__init__.pyi +17 -0
- {includecpp-2.5.2 → includecpp-2.7.2}/includecpp/cli/commands.py +196 -37
- {includecpp-2.5.2 → includecpp-2.7.2}/includecpp/core/build_manager.py +147 -8
- includecpp-2.7.2/includecpp/core/error_catalog.py +756 -0
- {includecpp-2.5.2 → includecpp-2.7.2}/includecpp/core/error_formatter.py +45 -18
- includecpp-2.7.2/includecpp/py.typed +0 -0
- {includecpp-2.5.2 → includecpp-2.7.2}/pyproject.toml +6 -2
- {includecpp-2.5.2 → includecpp-2.7.2}/setup.py +5 -1
- includecpp-2.5.2/includecpp/__init__.py +0 -4
- {includecpp-2.5.2 → includecpp-2.7.2}/IncludeCPP.egg-info/dependency_links.txt +0 -0
- {includecpp-2.5.2 → includecpp-2.7.2}/IncludeCPP.egg-info/entry_points.txt +0 -0
- {includecpp-2.5.2 → includecpp-2.7.2}/IncludeCPP.egg-info/requires.txt +0 -0
- {includecpp-2.5.2 → includecpp-2.7.2}/IncludeCPP.egg-info/top_level.txt +0 -0
- {includecpp-2.5.2 → includecpp-2.7.2}/LICENSE +0 -0
- {includecpp-2.5.2 → includecpp-2.7.2}/MANIFEST.in +0 -0
- {includecpp-2.5.2 → includecpp-2.7.2}/includecpp/__main__.py +0 -0
- {includecpp-2.5.2 → includecpp-2.7.2}/includecpp/cli/__init__.py +0 -0
- {includecpp-2.5.2 → includecpp-2.7.2}/includecpp/cli/config_parser.py +0 -0
- {includecpp-2.5.2 → includecpp-2.7.2}/includecpp/core/__init__.py +0 -0
- {includecpp-2.5.2 → includecpp-2.7.2}/includecpp/core/cpp_api.py +0 -0
- {includecpp-2.5.2 → includecpp-2.7.2}/includecpp/core/cpp_api.pyi +0 -0
- {includecpp-2.5.2 → includecpp-2.7.2}/includecpp/core/exceptions.py +0 -0
- {includecpp-2.5.2 → includecpp-2.7.2}/includecpp/core/path_discovery.py +0 -0
- {includecpp-2.5.2 → includecpp-2.7.2}/includecpp/generator/__init__.py +0 -0
- {includecpp-2.5.2 → includecpp-2.7.2}/includecpp/generator/parser.cpp +0 -0
- {includecpp-2.5.2 → includecpp-2.7.2}/includecpp/generator/parser.h +0 -0
- {includecpp-2.5.2 → includecpp-2.7.2}/includecpp/generator/type_resolver.cpp +0 -0
- {includecpp-2.5.2 → includecpp-2.7.2}/includecpp/generator/type_resolver.h +0 -0
- {includecpp-2.5.2 → includecpp-2.7.2}/includecpp/templates/cpp.proj.template +0 -0
- {includecpp-2.5.2 → includecpp-2.7.2}/requirements.txt +0 -0
- {includecpp-2.5.2 → includecpp-2.7.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: IncludeCPP
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.7.2
|
|
4
4
|
Summary: Professional C++ Python bindings with type-generic templates, pystubs and native threading
|
|
5
5
|
Home-page: https://github.com/includecpp/includecpp
|
|
6
6
|
Author: IncludeCPP Team
|
|
@@ -116,17 +116,19 @@ includecpp rebuild [OPTIONS]
|
|
|
116
116
|
```
|
|
117
117
|
|
|
118
118
|
**Options:**
|
|
119
|
-
- `--clean` - Force clean rebuild (
|
|
120
|
-
- `--
|
|
119
|
+
- `--clean` - Force clean rebuild (delete all cached files)
|
|
120
|
+
- `--keep` - Keep existing generator (skip automatic generator rebuild)
|
|
121
121
|
- `--verbose` - Detailed build output
|
|
122
122
|
- `--no-incremental` - Disable incremental builds
|
|
123
123
|
- `--parallel/--no-parallel` - Enable/disable parallel compilation (default: enabled)
|
|
124
124
|
- `-j, --jobs <N>` - Max parallel jobs (default: 4)
|
|
125
125
|
- `-m, --modules <name>` - Rebuild specific modules only
|
|
126
126
|
|
|
127
|
+
**Note:** The generator is automatically rebuilt on each run to ensure it's up-to-date. Use `--keep` to skip this if you know the generator is current.
|
|
128
|
+
|
|
127
129
|
**Examples:**
|
|
128
130
|
```bash
|
|
129
|
-
includecpp rebuild --
|
|
131
|
+
includecpp rebuild --verbose
|
|
130
132
|
includecpp rebuild -m crypto -m networking
|
|
131
133
|
```
|
|
132
134
|
|
|
@@ -337,6 +339,15 @@ SHA256-based change detection. Only rebuilds modified modules.
|
|
|
337
339
|
|
|
338
340
|
## Python API
|
|
339
341
|
|
|
342
|
+
### Simple Import (v2.6+)
|
|
343
|
+
|
|
344
|
+
```python
|
|
345
|
+
from includecpp import fast_list
|
|
346
|
+
result = fast_list.fast_sort([3, 1, 2])
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
### Classic API
|
|
350
|
+
|
|
340
351
|
```python
|
|
341
352
|
from includecpp import CppApi
|
|
342
353
|
|
|
@@ -368,5 +379,5 @@ api.exists("module_name") # Check if module exists
|
|
|
368
379
|
---
|
|
369
380
|
|
|
370
381
|
**License:** MIT
|
|
371
|
-
**Version:** 2.
|
|
382
|
+
**Version:** 2.6.1
|
|
372
383
|
**Repository:** https://github.com/liliassg/IncludeCPP
|
|
@@ -7,6 +7,7 @@ setup.py
|
|
|
7
7
|
./includecpp/__init__.py
|
|
8
8
|
./includecpp/__init__.pyi
|
|
9
9
|
./includecpp/__main__.py
|
|
10
|
+
./includecpp/py.typed
|
|
10
11
|
./includecpp/cli/__init__.py
|
|
11
12
|
./includecpp/cli/commands.py
|
|
12
13
|
./includecpp/cli/config_parser.py
|
|
@@ -14,6 +15,7 @@ setup.py
|
|
|
14
15
|
./includecpp/core/build_manager.py
|
|
15
16
|
./includecpp/core/cpp_api.py
|
|
16
17
|
./includecpp/core/cpp_api.pyi
|
|
18
|
+
./includecpp/core/error_catalog.py
|
|
17
19
|
./includecpp/core/error_formatter.py
|
|
18
20
|
./includecpp/core/exceptions.py
|
|
19
21
|
./includecpp/core/path_discovery.py
|
|
@@ -32,6 +34,7 @@ IncludeCPP.egg-info/top_level.txt
|
|
|
32
34
|
includecpp/__init__.py
|
|
33
35
|
includecpp/__init__.pyi
|
|
34
36
|
includecpp/__main__.py
|
|
37
|
+
includecpp/py.typed
|
|
35
38
|
includecpp/cli/__init__.py
|
|
36
39
|
includecpp/cli/commands.py
|
|
37
40
|
includecpp/cli/config_parser.py
|
|
@@ -39,6 +42,7 @@ includecpp/core/__init__.py
|
|
|
39
42
|
includecpp/core/build_manager.py
|
|
40
43
|
includecpp/core/cpp_api.py
|
|
41
44
|
includecpp/core/cpp_api.pyi
|
|
45
|
+
includecpp/core/error_catalog.py
|
|
42
46
|
includecpp/core/error_formatter.py
|
|
43
47
|
includecpp/core/exceptions.py
|
|
44
48
|
includecpp/core/path_discovery.py
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: IncludeCPP
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.7.2
|
|
4
4
|
Summary: Professional C++ Python bindings with type-generic templates, pystubs and native threading
|
|
5
5
|
Home-page: https://github.com/includecpp/includecpp
|
|
6
6
|
Author: IncludeCPP Team
|
|
@@ -116,17 +116,19 @@ includecpp rebuild [OPTIONS]
|
|
|
116
116
|
```
|
|
117
117
|
|
|
118
118
|
**Options:**
|
|
119
|
-
- `--clean` - Force clean rebuild (
|
|
120
|
-
- `--
|
|
119
|
+
- `--clean` - Force clean rebuild (delete all cached files)
|
|
120
|
+
- `--keep` - Keep existing generator (skip automatic generator rebuild)
|
|
121
121
|
- `--verbose` - Detailed build output
|
|
122
122
|
- `--no-incremental` - Disable incremental builds
|
|
123
123
|
- `--parallel/--no-parallel` - Enable/disable parallel compilation (default: enabled)
|
|
124
124
|
- `-j, --jobs <N>` - Max parallel jobs (default: 4)
|
|
125
125
|
- `-m, --modules <name>` - Rebuild specific modules only
|
|
126
126
|
|
|
127
|
+
**Note:** The generator is automatically rebuilt on each run to ensure it's up-to-date. Use `--keep` to skip this if you know the generator is current.
|
|
128
|
+
|
|
127
129
|
**Examples:**
|
|
128
130
|
```bash
|
|
129
|
-
includecpp rebuild --
|
|
131
|
+
includecpp rebuild --verbose
|
|
130
132
|
includecpp rebuild -m crypto -m networking
|
|
131
133
|
```
|
|
132
134
|
|
|
@@ -337,6 +339,15 @@ SHA256-based change detection. Only rebuilds modified modules.
|
|
|
337
339
|
|
|
338
340
|
## Python API
|
|
339
341
|
|
|
342
|
+
### Simple Import (v2.6+)
|
|
343
|
+
|
|
344
|
+
```python
|
|
345
|
+
from includecpp import fast_list
|
|
346
|
+
result = fast_list.fast_sort([3, 1, 2])
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
### Classic API
|
|
350
|
+
|
|
340
351
|
```python
|
|
341
352
|
from includecpp import CppApi
|
|
342
353
|
|
|
@@ -368,5 +379,5 @@ api.exists("module_name") # Check if module exists
|
|
|
368
379
|
---
|
|
369
380
|
|
|
370
381
|
**License:** MIT
|
|
371
|
-
**Version:** 2.
|
|
382
|
+
**Version:** 2.6.1
|
|
372
383
|
**Repository:** https://github.com/liliassg/IncludeCPP
|
|
@@ -80,17 +80,19 @@ includecpp rebuild [OPTIONS]
|
|
|
80
80
|
```
|
|
81
81
|
|
|
82
82
|
**Options:**
|
|
83
|
-
- `--clean` - Force clean rebuild (
|
|
84
|
-
- `--
|
|
83
|
+
- `--clean` - Force clean rebuild (delete all cached files)
|
|
84
|
+
- `--keep` - Keep existing generator (skip automatic generator rebuild)
|
|
85
85
|
- `--verbose` - Detailed build output
|
|
86
86
|
- `--no-incremental` - Disable incremental builds
|
|
87
87
|
- `--parallel/--no-parallel` - Enable/disable parallel compilation (default: enabled)
|
|
88
88
|
- `-j, --jobs <N>` - Max parallel jobs (default: 4)
|
|
89
89
|
- `-m, --modules <name>` - Rebuild specific modules only
|
|
90
90
|
|
|
91
|
+
**Note:** The generator is automatically rebuilt on each run to ensure it's up-to-date. Use `--keep` to skip this if you know the generator is current.
|
|
92
|
+
|
|
91
93
|
**Examples:**
|
|
92
94
|
```bash
|
|
93
|
-
includecpp rebuild --
|
|
95
|
+
includecpp rebuild --verbose
|
|
94
96
|
includecpp rebuild -m crypto -m networking
|
|
95
97
|
```
|
|
96
98
|
|
|
@@ -301,6 +303,15 @@ SHA256-based change detection. Only rebuilds modified modules.
|
|
|
301
303
|
|
|
302
304
|
## Python API
|
|
303
305
|
|
|
306
|
+
### Simple Import (v2.6+)
|
|
307
|
+
|
|
308
|
+
```python
|
|
309
|
+
from includecpp import fast_list
|
|
310
|
+
result = fast_list.fast_sort([3, 1, 2])
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### Classic API
|
|
314
|
+
|
|
304
315
|
```python
|
|
305
316
|
from includecpp import CppApi
|
|
306
317
|
|
|
@@ -332,5 +343,5 @@ api.exists("module_name") # Check if module exists
|
|
|
332
343
|
---
|
|
333
344
|
|
|
334
345
|
**License:** MIT
|
|
335
|
-
**Version:** 2.
|
|
346
|
+
**Version:** 2.6.1
|
|
336
347
|
**Repository:** https://github.com/liliassg/IncludeCPP
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from .core.cpp_api import CppApi
|
|
2
|
+
import warnings
|
|
3
|
+
|
|
4
|
+
__version__ = "2.7.2"
|
|
5
|
+
__all__ = ["CppApi"]
|
|
6
|
+
|
|
7
|
+
# Module-level cache for C++ modules
|
|
8
|
+
_api_instance = None
|
|
9
|
+
_loaded_modules = {}
|
|
10
|
+
|
|
11
|
+
def _get_api():
|
|
12
|
+
"""Get or create singleton CppApi instance."""
|
|
13
|
+
global _api_instance
|
|
14
|
+
if _api_instance is None:
|
|
15
|
+
_api_instance = CppApi()
|
|
16
|
+
return _api_instance
|
|
17
|
+
|
|
18
|
+
def __getattr__(name: str):
|
|
19
|
+
"""Enable: from includecpp import fast_list
|
|
20
|
+
|
|
21
|
+
This hook is called when Python cannot find an attribute in this module.
|
|
22
|
+
It allows dynamic C++ module loading via the import system.
|
|
23
|
+
"""
|
|
24
|
+
if name.startswith('_'):
|
|
25
|
+
raise AttributeError(f"module 'includecpp' has no attribute '{name}'")
|
|
26
|
+
|
|
27
|
+
if name in _loaded_modules:
|
|
28
|
+
return _loaded_modules[name]
|
|
29
|
+
|
|
30
|
+
api = _get_api()
|
|
31
|
+
|
|
32
|
+
if name not in api.registry:
|
|
33
|
+
available = list(api.registry.keys())
|
|
34
|
+
raise AttributeError(
|
|
35
|
+
f"Module '{name}' not found. "
|
|
36
|
+
f"Available: {available}. "
|
|
37
|
+
f"Run 'includecpp rebuild' first."
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
if api.need_update(name):
|
|
41
|
+
warnings.warn(
|
|
42
|
+
f"Module '{name}' source files changed. "
|
|
43
|
+
f"Run 'includecpp rebuild' to update.",
|
|
44
|
+
UserWarning
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
module = api.include(name)
|
|
48
|
+
_loaded_modules[name] = module
|
|
49
|
+
return module
|
|
50
|
+
|
|
51
|
+
def __dir__():
|
|
52
|
+
"""List available attributes including C++ modules."""
|
|
53
|
+
base = ['CppApi', '__version__']
|
|
54
|
+
try:
|
|
55
|
+
api = _get_api()
|
|
56
|
+
return sorted(set(base + list(api.registry.keys())))
|
|
57
|
+
except Exception:
|
|
58
|
+
return base
|
|
@@ -2,9 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
from typing import Any, Dict, Optional, List, Literal, overload
|
|
4
4
|
from pathlib import Path
|
|
5
|
+
from types import ModuleType
|
|
6
|
+
|
|
7
|
+
# Import generated module wrappers for VSCode autocomplete
|
|
8
|
+
# These are created by 'includecpp rebuild' and provide module-specific type hints
|
|
9
|
+
try:
|
|
10
|
+
from .core.cpp_api_extensions import *
|
|
11
|
+
except ImportError:
|
|
12
|
+
pass # Generated during rebuild
|
|
5
13
|
|
|
6
14
|
__version__: str
|
|
7
15
|
|
|
16
|
+
# Dynamic module access via: from includecpp import <module_name>
|
|
17
|
+
# Auto-generated module declarations
|
|
18
|
+
# These allow: from includecpp import <module_name>
|
|
19
|
+
# (Run 'includecpp rebuild' to generate declarations for your modules)
|
|
20
|
+
|
|
21
|
+
def __dir__() -> List[str]:
|
|
22
|
+
"""List available modules including dynamically loaded C++ modules."""
|
|
23
|
+
...
|
|
24
|
+
|
|
8
25
|
class ModuleWrapper:
|
|
9
26
|
"""Wrapper for C++ modules with getInfo() and dynamic attributes."""
|
|
10
27
|
|
|
@@ -31,7 +31,7 @@ _SEC_TOP = "┌─────────────────────
|
|
|
31
31
|
_SEC_BOTTOM = "└─────────────────────────────────────────────────────────────┘" if _UNICODE else "+-------------------------------------------------------------+"
|
|
32
32
|
_SEC_SIDE = "│" if _UNICODE else "|"
|
|
33
33
|
_HLINE = "═" if _UNICODE else "-"
|
|
34
|
-
_CHECK = "
|
|
34
|
+
_CHECK = "" # Removed check marks per user request
|
|
35
35
|
_CROSS = "✗" if _UNICODE else "[X]"
|
|
36
36
|
_BULLET = "•" if _UNICODE else "*"
|
|
37
37
|
_ARROW = "→" if _UNICODE else "->"
|
|
@@ -49,7 +49,7 @@ def _safe_echo(text, **kwargs):
|
|
|
49
49
|
('╔', '+'), ('╗', '+'), ('╚', '+'), ('╝', '+'),
|
|
50
50
|
('┌', '+'), ('┐', '+'), ('└', '+'), ('┘', '+'),
|
|
51
51
|
('═', '-'), ('─', '-'), ('║', '|'), ('│', '|'),
|
|
52
|
-
('
|
|
52
|
+
('✗', '[X]'), ('•', '*'),
|
|
53
53
|
('→', '->'), ('▶', '>'), ('◆', '*')
|
|
54
54
|
]
|
|
55
55
|
for uni, ascii_char in replacements:
|
|
@@ -494,8 +494,11 @@ def _show_build_info_report(build_dir: Path, compiler: str):
|
|
|
494
494
|
f = first_info['functions'][0]
|
|
495
495
|
first_func = f.get('name', f) if isinstance(f, dict) else f
|
|
496
496
|
|
|
497
|
-
click.echo(f" from
|
|
498
|
-
click.echo(f"
|
|
497
|
+
click.echo(f" from includecpp import CppApi")
|
|
498
|
+
click.echo(f" api = CppApi()")
|
|
499
|
+
click.echo(f" {first_mod} = api.include(\"{first_mod}\")")
|
|
500
|
+
if first_func:
|
|
501
|
+
click.echo(f" result = {first_mod}.{first_func}(...)")
|
|
499
502
|
|
|
500
503
|
click.echo("")
|
|
501
504
|
_safe_echo(_HLINE * 64, fg='cyan')
|
|
@@ -531,9 +534,61 @@ def _check_for_updates_silent():
|
|
|
531
534
|
pass # Silently fail - don't interrupt user's workflow
|
|
532
535
|
return None
|
|
533
536
|
|
|
537
|
+
|
|
538
|
+
def _parse_cp_sources(cp_path):
|
|
539
|
+
"""Parse SOURCE() and HEADER() paths from an existing .cp file.
|
|
540
|
+
|
|
541
|
+
Returns:
|
|
542
|
+
Tuple of (source_files, header_files) as lists of Path objects
|
|
543
|
+
"""
|
|
544
|
+
import re
|
|
545
|
+
from pathlib import Path
|
|
546
|
+
|
|
547
|
+
source_files = []
|
|
548
|
+
header_files = []
|
|
549
|
+
|
|
550
|
+
try:
|
|
551
|
+
content = cp_path.read_text()
|
|
552
|
+
project_root = cp_path.parent.parent # plugins/ -> project root
|
|
553
|
+
|
|
554
|
+
# Find SOURCE(...) declarations
|
|
555
|
+
source_match = re.search(r'SOURCE\s*\(\s*([^)]+)\s*\)', content)
|
|
556
|
+
if source_match:
|
|
557
|
+
sources_str = source_match.group(1)
|
|
558
|
+
# Handle multiple files: SOURCE(file1.cpp file2.cpp) or SOURCE(file1.cpp, file2.cpp)
|
|
559
|
+
sources = re.split(r'[,\s]+', sources_str.strip())
|
|
560
|
+
for src in sources:
|
|
561
|
+
src = src.strip()
|
|
562
|
+
if src:
|
|
563
|
+
p = Path(src)
|
|
564
|
+
if not p.is_absolute():
|
|
565
|
+
p = project_root / src
|
|
566
|
+
if p.exists():
|
|
567
|
+
source_files.append(p)
|
|
568
|
+
|
|
569
|
+
# Find HEADER(...) declarations
|
|
570
|
+
header_match = re.search(r'HEADER\s*\(\s*([^)]+)\s*\)', content)
|
|
571
|
+
if header_match:
|
|
572
|
+
headers_str = header_match.group(1)
|
|
573
|
+
headers = re.split(r'[,\s]+', headers_str.strip())
|
|
574
|
+
for hdr in headers:
|
|
575
|
+
hdr = hdr.strip()
|
|
576
|
+
if hdr:
|
|
577
|
+
p = Path(hdr)
|
|
578
|
+
if not p.is_absolute():
|
|
579
|
+
p = project_root / hdr
|
|
580
|
+
if p.exists():
|
|
581
|
+
header_files.append(p)
|
|
582
|
+
|
|
583
|
+
except Exception as e:
|
|
584
|
+
pass # Return empty lists on error
|
|
585
|
+
|
|
586
|
+
return (source_files, header_files)
|
|
587
|
+
|
|
588
|
+
|
|
534
589
|
@cli.command()
|
|
535
590
|
@click.option('--clean', is_flag=True, help='Force clean rebuild (ignore incremental)')
|
|
536
|
-
@click.option('--
|
|
591
|
+
@click.option('--keep', is_flag=True, help='Keep existing plugin_gen.exe (skip generator rebuild)')
|
|
537
592
|
@click.option('--verbose', is_flag=True, help='Verbose output')
|
|
538
593
|
@click.option('--no-incremental', is_flag=True, help='Disable incremental builds')
|
|
539
594
|
@click.option('--incremental', is_flag=True, help='Explicitly enable incremental builds (default: auto)')
|
|
@@ -542,14 +597,14 @@ def _check_for_updates_silent():
|
|
|
542
597
|
@click.option('--modules', '-m', multiple=True, help='Specific modules to rebuild')
|
|
543
598
|
@click.option('--this', 'build_paths', multiple=True, type=click.Path(exists=True),
|
|
544
599
|
help='Build only from specific paths (can specify multiple)')
|
|
545
|
-
@click.option('--fast', '
|
|
546
|
-
help='Fast incremental rebuild
|
|
600
|
+
@click.option('--fast', 'fast_mode', is_flag=True,
|
|
601
|
+
help='Fast incremental rebuild mode (use with module name, e.g., rebuild --fast gamekit)')
|
|
547
602
|
@click.option('--all', 'fast_all', is_flag=True, help='With --fast: rebuild all plugins incrementally')
|
|
548
603
|
@click.option('--info', is_flag=True, help='Show detailed analysis report of the last build')
|
|
549
604
|
@click.argument('module_args', nargs=-1)
|
|
550
|
-
def rebuild(clean,
|
|
551
|
-
build_paths,
|
|
552
|
-
"""
|
|
605
|
+
def rebuild(clean, keep, verbose, no_incremental, incremental, parallel, jobs, modules,
|
|
606
|
+
build_paths, fast_mode, fast_all, info, module_args):
|
|
607
|
+
"""Rebuild C++ modules with automatic generator updates."""
|
|
553
608
|
from ..core.build_manager import BuildManager
|
|
554
609
|
from ..core.error_formatter import BuildErrorFormatter, BuildSuccessFormatter
|
|
555
610
|
import time
|
|
@@ -599,14 +654,17 @@ def rebuild(clean, full, verbose, no_incremental, incremental, parallel, jobs, m
|
|
|
599
654
|
_show_build_info_report(build_dir, compiler)
|
|
600
655
|
return
|
|
601
656
|
|
|
602
|
-
#
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
657
|
+
# v2.5.4: By default, always delete the generator to ensure it's up-to-date
|
|
658
|
+
# Use --keep to skip generator rebuild
|
|
659
|
+
if build_dir.exists():
|
|
660
|
+
gen_path = build_dir / "bin" / ".appc"
|
|
661
|
+
if not keep and gen_path.exists():
|
|
662
|
+
if verbose:
|
|
663
|
+
click.echo("Removing old generator to force rebuild...")
|
|
664
|
+
shutil.rmtree(gen_path)
|
|
607
665
|
|
|
608
|
-
# Clean if requested
|
|
609
|
-
|
|
666
|
+
# Clean if requested - delete entire build directory
|
|
667
|
+
if clean and build_dir.exists():
|
|
610
668
|
click.echo("Cleaning build directory...")
|
|
611
669
|
shutil.rmtree(build_dir)
|
|
612
670
|
|
|
@@ -623,14 +681,28 @@ def rebuild(clean, full, verbose, no_incremental, incremental, parallel, jobs, m
|
|
|
623
681
|
click.secho("Warning: --clean overrides --incremental", fg='yellow')
|
|
624
682
|
incremental = False
|
|
625
683
|
|
|
626
|
-
if
|
|
684
|
+
if fast_mode and build_paths:
|
|
627
685
|
click.secho("Error: Cannot use both --fast and --this", fg='red', err=True)
|
|
628
686
|
raise click.Abort()
|
|
629
687
|
|
|
630
|
-
|
|
631
|
-
|
|
688
|
+
# Validate --all requires --fast
|
|
689
|
+
if fast_all and not fast_mode:
|
|
690
|
+
click.secho("Error: --all requires --fast", fg='red', err=True)
|
|
632
691
|
raise click.Abort()
|
|
633
692
|
|
|
693
|
+
# Determine fast plugin from modules (positional args or -m option)
|
|
694
|
+
fast_plugin = None
|
|
695
|
+
if fast_mode and not fast_all:
|
|
696
|
+
if modules:
|
|
697
|
+
fast_plugin = modules[0] # Use first module from -m option
|
|
698
|
+
elif module_args:
|
|
699
|
+
fast_plugin = module_args[0] # Use first positional argument
|
|
700
|
+
else:
|
|
701
|
+
click.secho("Error: --fast requires a module name", fg='red', err=True)
|
|
702
|
+
click.echo("Usage: includecpp rebuild --fast <module_name>")
|
|
703
|
+
click.echo(" or: includecpp rebuild -m <module_name> --fast")
|
|
704
|
+
raise click.Abort()
|
|
705
|
+
|
|
634
706
|
final_incremental = True
|
|
635
707
|
if incremental:
|
|
636
708
|
final_incremental = True
|
|
@@ -694,7 +766,7 @@ def rebuild(clean, full, verbose, no_incremental, incremental, parallel, jobs, m
|
|
|
694
766
|
if verbose:
|
|
695
767
|
click.echo(f"Affected modules ({len(modules)}): {', '.join(modules)}")
|
|
696
768
|
|
|
697
|
-
if fast_all:
|
|
769
|
+
if fast_mode and fast_all:
|
|
698
770
|
if verbose:
|
|
699
771
|
click.echo("Fast rebuild mode: ALL plugins")
|
|
700
772
|
|
|
@@ -766,12 +838,12 @@ def rebuild(clean, full, verbose, no_incremental, incremental, parallel, jobs, m
|
|
|
766
838
|
# Print success message
|
|
767
839
|
click.echo("")
|
|
768
840
|
_safe_echo(_BOX_TOP, fg='green')
|
|
769
|
-
_safe_echo(f"{_BOX_SIDE}
|
|
841
|
+
_safe_echo(f"{_BOX_SIDE} BUILD SUCCESSFUL", fg='green')
|
|
770
842
|
_safe_echo(_BOX_BOTTOM, fg='green')
|
|
771
843
|
click.echo("")
|
|
772
844
|
click.echo(f"Modules built ({len(built_modules)}):")
|
|
773
845
|
for mod in built_modules:
|
|
774
|
-
_safe_echo(f" {
|
|
846
|
+
_safe_echo(f" {mod}", fg='green')
|
|
775
847
|
click.echo("")
|
|
776
848
|
click.echo(f"Build time: {build_time:.2f}s")
|
|
777
849
|
if stats:
|
|
@@ -782,8 +854,20 @@ def rebuild(clean, full, verbose, no_incremental, incremental, parallel, jobs, m
|
|
|
782
854
|
if 'total_structs' in stats:
|
|
783
855
|
click.echo(f" Structs exported: {stats['total_structs']}")
|
|
784
856
|
click.echo("")
|
|
785
|
-
|
|
786
|
-
|
|
857
|
+
# Show usage with real module name from build
|
|
858
|
+
if built_modules:
|
|
859
|
+
first_mod = built_modules[0]
|
|
860
|
+
first_func = None
|
|
861
|
+
if first_mod in modules and modules[first_mod].get('functions'):
|
|
862
|
+
func_info = modules[first_mod]['functions'][0]
|
|
863
|
+
first_func = func_info.get('name', func_info) if isinstance(func_info, dict) else func_info
|
|
864
|
+
|
|
865
|
+
click.echo("Usage:")
|
|
866
|
+
click.echo(" from includecpp import CppApi")
|
|
867
|
+
click.echo(" api = CppApi()")
|
|
868
|
+
click.echo(f" {first_mod} = api.include(\"{first_mod}\")")
|
|
869
|
+
if first_func:
|
|
870
|
+
click.echo(f" result = {first_mod}.{first_func}(...)")
|
|
787
871
|
click.echo("")
|
|
788
872
|
else:
|
|
789
873
|
click.echo("")
|
|
@@ -803,13 +887,9 @@ def rebuild(clean, full, verbose, no_incremental, incremental, parallel, jobs, m
|
|
|
803
887
|
except Exception as e:
|
|
804
888
|
build_time = time.time() - start_time
|
|
805
889
|
error_msg = str(e)
|
|
890
|
+
module_name = target_modules[0] if target_modules else ""
|
|
806
891
|
|
|
807
|
-
# v2.
|
|
808
|
-
formatted_error = BuildErrorFormatter.analyze_error(error_msg, target_modules[0] if target_modules else "")
|
|
809
|
-
|
|
810
|
-
click.echo("")
|
|
811
|
-
click.secho(formatted_error, fg='red', err=True)
|
|
812
|
-
|
|
892
|
+
# v2.7: Print verbose details FIRST (for those who read everything)
|
|
813
893
|
if verbose:
|
|
814
894
|
click.echo("")
|
|
815
895
|
_safe_echo(_HLINE * 60, fg='yellow')
|
|
@@ -817,9 +897,43 @@ def rebuild(clean, full, verbose, no_incremental, incremental, parallel, jobs, m
|
|
|
817
897
|
_safe_echo(_HLINE * 60, fg='yellow')
|
|
818
898
|
import traceback
|
|
819
899
|
traceback.print_exc()
|
|
900
|
+
click.echo("")
|
|
901
|
+
|
|
902
|
+
# v2.7: Print detailed error analysis
|
|
903
|
+
formatted_error = BuildErrorFormatter.analyze_error(error_msg, module_name)
|
|
904
|
+
click.echo("")
|
|
905
|
+
click.secho(formatted_error, fg='red', err=True)
|
|
906
|
+
|
|
907
|
+
# v2.7: Print short helpful message LAST (for lazy users who only read the end)
|
|
908
|
+
final_msg = BuildErrorFormatter.get_final_message(error_msg, module_name)
|
|
909
|
+
click.echo("")
|
|
910
|
+
click.secho(final_msg, fg='red', bold=True, err=True)
|
|
820
911
|
|
|
821
912
|
raise click.Abort()
|
|
822
913
|
|
|
914
|
+
@cli.command()
|
|
915
|
+
@click.option('--clean', is_flag=True, help='Force clean rebuild')
|
|
916
|
+
@click.option('--keep', is_flag=True, help='Keep existing plugin_gen.exe')
|
|
917
|
+
@click.option('-v', '--verbose', is_flag=True, help='Show detailed output')
|
|
918
|
+
@click.option('--no-incremental', is_flag=True, help='Disable incremental builds')
|
|
919
|
+
@click.option('--incremental', is_flag=True, help='Force incremental build')
|
|
920
|
+
@click.option('--parallel', is_flag=True, help='Build modules in parallel')
|
|
921
|
+
@click.option('-j', '--jobs', type=int, default=None, help='Number of parallel jobs')
|
|
922
|
+
@click.option('-m', '--modules', multiple=True, help='Specific modules to build')
|
|
923
|
+
@click.option('--this', 'build_paths', multiple=True, type=click.Path(exists=True), help='Build from path')
|
|
924
|
+
@click.option('--fast', 'fast_mode', is_flag=True, help='Fast incremental build')
|
|
925
|
+
@click.option('--all', 'fast_all', is_flag=True, help='With --fast: rebuild all plugins')
|
|
926
|
+
@click.option('--info', is_flag=True, help='Show build analysis report')
|
|
927
|
+
@click.argument('module_args', nargs=-1)
|
|
928
|
+
@click.pass_context
|
|
929
|
+
def build(ctx, clean, keep, verbose, no_incremental, incremental, parallel, jobs, modules,
|
|
930
|
+
build_paths, fast_mode, fast_all, info, module_args):
|
|
931
|
+
"""Build C++ modules (alias for rebuild)."""
|
|
932
|
+
ctx.invoke(rebuild, clean=clean, keep=keep, verbose=verbose, no_incremental=no_incremental,
|
|
933
|
+
incremental=incremental, parallel=parallel, jobs=jobs, modules=modules,
|
|
934
|
+
build_paths=build_paths, fast_mode=fast_mode, fast_all=fast_all, info=info,
|
|
935
|
+
module_args=module_args)
|
|
936
|
+
|
|
823
937
|
@cli.command()
|
|
824
938
|
@click.argument('module_name')
|
|
825
939
|
def add(module_name):
|
|
@@ -847,8 +961,8 @@ PUBLIC(
|
|
|
847
961
|
click.echo(f"Created {cp_file}")
|
|
848
962
|
click.echo(f"Now create include/{module_name}.cpp with your C++ code")
|
|
849
963
|
|
|
850
|
-
@cli.command()
|
|
851
|
-
def
|
|
964
|
+
@cli.command('list')
|
|
965
|
+
def list_modules():
|
|
852
966
|
"""List all available C++ modules."""
|
|
853
967
|
from ..core.build_manager import BuildManager
|
|
854
968
|
|
|
@@ -1707,12 +1821,22 @@ def reboot():
|
|
|
1707
1821
|
|
|
1708
1822
|
@cli.command()
|
|
1709
1823
|
@click.argument('plugin_name')
|
|
1710
|
-
@click.argument('files', nargs=-1, required=
|
|
1824
|
+
@click.argument('files', nargs=-1, required=False)
|
|
1711
1825
|
@click.option('--private', '-p', multiple=True, help='Private functions to exclude from public API')
|
|
1712
1826
|
def plugin(plugin_name, files, private):
|
|
1827
|
+
"""Generate or regenerate a .cp plugin definition from C++ source files.
|
|
1828
|
+
|
|
1829
|
+
If no files are provided and an existing .cp file exists, SOURCE() and HEADER()
|
|
1830
|
+
paths are read from it to auto-regenerate the plugin definition.
|
|
1831
|
+
"""
|
|
1713
1832
|
import re
|
|
1714
1833
|
from pathlib import Path
|
|
1715
1834
|
|
|
1835
|
+
# Normalize plugin_name: strip .cp extension and path prefixes
|
|
1836
|
+
if plugin_name.endswith('.cp'):
|
|
1837
|
+
plugin_name = plugin_name[:-3]
|
|
1838
|
+
plugin_name = Path(plugin_name).stem
|
|
1839
|
+
|
|
1716
1840
|
project_root = Path.cwd()
|
|
1717
1841
|
plugins_dir = project_root / "plugins"
|
|
1718
1842
|
|
|
@@ -1721,6 +1845,24 @@ def plugin(plugin_name, files, private):
|
|
|
1721
1845
|
click.echo("Run 'python -m includecpp init' first")
|
|
1722
1846
|
return
|
|
1723
1847
|
|
|
1848
|
+
# v2.5.3: Auto-regenerate from existing .cp file if no files provided
|
|
1849
|
+
if not files:
|
|
1850
|
+
existing_cp = plugins_dir / f"{plugin_name}.cp"
|
|
1851
|
+
if existing_cp.exists():
|
|
1852
|
+
click.echo(f"Auto-regenerating from existing: {existing_cp}")
|
|
1853
|
+
source_files, header_files = _parse_cp_sources(existing_cp)
|
|
1854
|
+
if not source_files:
|
|
1855
|
+
click.secho("Error: No SOURCE() found in existing .cp file", fg='red', err=True)
|
|
1856
|
+
click.echo("Usage: includecpp plugin <name> <file1.cpp> [file2.h] ...")
|
|
1857
|
+
return
|
|
1858
|
+
# Combine and use as input files
|
|
1859
|
+
files = tuple(source_files + header_files)
|
|
1860
|
+
click.echo(f"Found sources: {', '.join(str(f) for f in files)}")
|
|
1861
|
+
else:
|
|
1862
|
+
click.secho("Error: No files provided and no existing .cp file found", fg='red', err=True)
|
|
1863
|
+
click.echo("Usage: includecpp plugin <name> <file1.cpp> [file2.h] ...")
|
|
1864
|
+
return
|
|
1865
|
+
|
|
1724
1866
|
cpp_files = []
|
|
1725
1867
|
h_files = []
|
|
1726
1868
|
|
|
@@ -2329,11 +2471,28 @@ def bug(message, get_bugs):
|
|
|
2329
2471
|
|
|
2330
2472
|
click.echo("=" * 60)
|
|
2331
2473
|
|
|
2332
|
-
|
|
2474
|
+
# Compiler detection cache
|
|
2475
|
+
_compiler_cache = None
|
|
2476
|
+
|
|
2477
|
+
def detect_compiler(force_refresh=False):
|
|
2478
|
+
"""Detect available C++ compiler with caching.
|
|
2479
|
+
|
|
2480
|
+
Checks g++ first per user requirement, then clang++, then cl (MSVC).
|
|
2481
|
+
Results are cached to avoid repeated PATH scans.
|
|
2482
|
+
"""
|
|
2483
|
+
global _compiler_cache
|
|
2484
|
+
|
|
2485
|
+
if not force_refresh and _compiler_cache is not None:
|
|
2486
|
+
return _compiler_cache
|
|
2487
|
+
|
|
2488
|
+
# g++ first (user requirement)
|
|
2333
2489
|
for compiler in ['g++', 'clang++', 'cl']:
|
|
2334
2490
|
if shutil.which(compiler):
|
|
2335
|
-
|
|
2336
|
-
|
|
2491
|
+
_compiler_cache = compiler.replace('++', '').replace('cl', 'msvc')
|
|
2492
|
+
return _compiler_cache
|
|
2493
|
+
|
|
2494
|
+
_compiler_cache = 'gcc'
|
|
2495
|
+
return _compiler_cache
|
|
2337
2496
|
|
|
2338
2497
|
if __name__ == '__main__':
|
|
2339
2498
|
cli()
|