multi-lang-build 0.2.1__py3-none-any.whl → 0.2.3__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.
- multi_lang_build/__init__.py +8 -1
- multi_lang_build/cli.py +87 -23
- multi_lang_build/compiler/base.py +135 -22
- multi_lang_build/compiler/go.py +54 -38
- multi_lang_build/compiler/pnpm.py +83 -33
- multi_lang_build/compiler/python.py +24 -15
- multi_lang_build/mirror/cli.py +340 -0
- {multi_lang_build-0.2.1.dist-info → multi_lang_build-0.2.3.dist-info}/METADATA +1 -1
- multi_lang_build-0.2.3.dist-info/RECORD +18 -0
- {multi_lang_build-0.2.1.dist-info → multi_lang_build-0.2.3.dist-info}/entry_points.txt +1 -0
- multi_lang_build-0.2.1.dist-info/RECORD +0 -17
- {multi_lang_build-0.2.1.dist-info → multi_lang_build-0.2.3.dist-info}/WHEEL +0 -0
- {multi_lang_build-0.2.1.dist-info → multi_lang_build-0.2.3.dist-info}/licenses/LICENSE +0 -0
multi_lang_build/__init__.py
CHANGED
|
@@ -7,7 +7,7 @@ from multi_lang_build.compiler.python import PythonCompiler
|
|
|
7
7
|
from multi_lang_build.mirror.config import MirrorConfig, get_mirror_config
|
|
8
8
|
from multi_lang_build.register import register_skill
|
|
9
9
|
|
|
10
|
-
__version__ = "0.2.
|
|
10
|
+
__version__ = "0.2.3"
|
|
11
11
|
__all__ = [
|
|
12
12
|
"BuildConfig",
|
|
13
13
|
"BuildResult",
|
|
@@ -55,3 +55,10 @@ def main_register() -> None:
|
|
|
55
55
|
args = parser.parse_args()
|
|
56
56
|
success = register_skill(args.ide)
|
|
57
57
|
sys.exit(0 if success else 1)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def main_mirror() -> None:
|
|
61
|
+
"""Entry point for the mirror CLI tool."""
|
|
62
|
+
from multi_lang_build.mirror.cli import mirror_main
|
|
63
|
+
|
|
64
|
+
mirror_main()
|
multi_lang_build/cli.py
CHANGED
|
@@ -61,7 +61,8 @@ Supported languages:
|
|
|
61
61
|
pnpm_parser.add_argument("--mirror/--no-mirror", default=True, help="Enable/disable mirror acceleration")
|
|
62
62
|
pnpm_parser.add_argument("--script", type=str, help="Run specific npm script")
|
|
63
63
|
pnpm_parser.add_argument("--install", action="store_true", help="Install dependencies only")
|
|
64
|
-
|
|
64
|
+
pnpm_parser.add_argument("--stream/--no-stream", dest="stream_output", default=True, help="Enable/disable real-time output streaming (default: enabled)")
|
|
65
|
+
|
|
65
66
|
go_parser = subparsers.add_parser(
|
|
66
67
|
"go",
|
|
67
68
|
help="Build Go projects",
|
|
@@ -75,7 +76,8 @@ Supported languages:
|
|
|
75
76
|
go_parser.add_argument("--test", action="store_true", help="Run tests instead of building")
|
|
76
77
|
go_parser.add_argument("--tidy", action="store_true", help="Run go mod tidy")
|
|
77
78
|
go_parser.add_argument("--all", action="store_true", help="Build all packages")
|
|
78
|
-
|
|
79
|
+
go_parser.add_argument("--stream/--no-stream", dest="stream_output", default=True, help="Enable/disable real-time output streaming (default: enabled)")
|
|
80
|
+
|
|
79
81
|
python_parser = subparsers.add_parser(
|
|
80
82
|
"python",
|
|
81
83
|
help="Build Python projects",
|
|
@@ -88,16 +90,59 @@ Supported languages:
|
|
|
88
90
|
python_parser.add_argument("--dev", action="store_true", help="Include development dependencies")
|
|
89
91
|
python_parser.add_argument("--poetry", action="store_true", help="Force using poetry")
|
|
90
92
|
python_parser.add_argument("--create-venv", type=Path, help="Create virtual environment at path")
|
|
93
|
+
python_parser.add_argument("--stream/--no-stream", dest="stream_output", default=True, help="Enable/disable real-time output streaming (default: enabled)")
|
|
91
94
|
|
|
92
95
|
mirror_parser = subparsers.add_parser(
|
|
93
96
|
"mirror",
|
|
94
97
|
help="Manage mirror configurations",
|
|
95
|
-
description="List or configure mirror settings",
|
|
98
|
+
description="List or configure domestic mirror acceleration settings",
|
|
99
|
+
epilog="""
|
|
100
|
+
Examples:
|
|
101
|
+
%(prog)s mirror list List all available mirrors
|
|
102
|
+
%(prog)s mirror set pip Configure pip mirror (default)
|
|
103
|
+
%(prog)s mirror set go Configure go proxy mirror
|
|
104
|
+
%(prog)s mirror set pnpm Configure pnpm registry mirror
|
|
105
|
+
%(prog)s mirror show Show current configuration
|
|
106
|
+
%(prog)s mirror reset Reset configuration
|
|
107
|
+
""",
|
|
96
108
|
)
|
|
97
109
|
mirror_subparsers = mirror_parser.add_subparsers(dest="mirror_action")
|
|
98
110
|
|
|
99
111
|
list_parser = mirror_subparsers.add_parser("list", help="List available mirrors")
|
|
100
|
-
list_parser.add_argument("--
|
|
112
|
+
list_parser.add_argument("--json", action="store_true", help="Output as JSON")
|
|
113
|
+
|
|
114
|
+
set_parser = mirror_subparsers.add_parser("set", help="Set mirror configuration")
|
|
115
|
+
set_parser.add_argument(
|
|
116
|
+
"type",
|
|
117
|
+
type=str,
|
|
118
|
+
default="pip",
|
|
119
|
+
nargs="?",
|
|
120
|
+
choices=["pip", "go", "npm", "pnpm"],
|
|
121
|
+
help="Package manager type (default: pip)",
|
|
122
|
+
)
|
|
123
|
+
set_parser.add_argument(
|
|
124
|
+
"mirror",
|
|
125
|
+
type=str,
|
|
126
|
+
nargs="?",
|
|
127
|
+
default="pip",
|
|
128
|
+
help="Mirror to use",
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
show_parser = mirror_subparsers.add_parser("show", help="Show current configuration")
|
|
132
|
+
show_parser.add_argument(
|
|
133
|
+
"--global",
|
|
134
|
+
dest="global_level",
|
|
135
|
+
action="store_true",
|
|
136
|
+
help="Show global config instead of project-level",
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
reset_parser = mirror_subparsers.add_parser("reset", help="Reset configuration")
|
|
140
|
+
reset_parser.add_argument(
|
|
141
|
+
"--global",
|
|
142
|
+
dest="global_level",
|
|
143
|
+
action="store_true",
|
|
144
|
+
help="Reset global config instead of project-level",
|
|
145
|
+
)
|
|
101
146
|
|
|
102
147
|
# Register subcommand for IDE integration
|
|
103
148
|
register_parser = subparsers.add_parser(
|
|
@@ -133,9 +178,9 @@ Examples:
|
|
|
133
178
|
try:
|
|
134
179
|
if parsed_args.language == "pnpm":
|
|
135
180
|
from multi_lang_build.compiler.pnpm import PnpmCompiler
|
|
136
|
-
|
|
181
|
+
|
|
137
182
|
compiler = PnpmCompiler()
|
|
138
|
-
|
|
183
|
+
|
|
139
184
|
if parsed_args.script:
|
|
140
185
|
result = compiler.run_script(
|
|
141
186
|
parsed_args.source_dir,
|
|
@@ -152,13 +197,14 @@ Examples:
|
|
|
152
197
|
parsed_args.source_dir,
|
|
153
198
|
parsed_args.output,
|
|
154
199
|
mirror_enabled=parsed_args.mirror,
|
|
200
|
+
stream_output=parsed_args.stream_output,
|
|
155
201
|
)
|
|
156
|
-
|
|
202
|
+
|
|
157
203
|
elif parsed_args.language == "go":
|
|
158
204
|
from multi_lang_build.compiler.go import GoCompiler
|
|
159
|
-
|
|
205
|
+
|
|
160
206
|
compiler = GoCompiler()
|
|
161
|
-
|
|
207
|
+
|
|
162
208
|
if parsed_args.test:
|
|
163
209
|
result = compiler.run_tests(
|
|
164
210
|
parsed_args.source_dir,
|
|
@@ -174,6 +220,7 @@ Examples:
|
|
|
174
220
|
parsed_args.source_dir,
|
|
175
221
|
parsed_args.output,
|
|
176
222
|
mirror_enabled=parsed_args.mirror,
|
|
223
|
+
stream_output=parsed_args.stream_output,
|
|
177
224
|
)
|
|
178
225
|
else:
|
|
179
226
|
tags = parsed_args.tags.split(",") if parsed_args.tags else None
|
|
@@ -183,13 +230,14 @@ Examples:
|
|
|
183
230
|
mirror_enabled=parsed_args.mirror,
|
|
184
231
|
ldflags=parsed_args.ldflags,
|
|
185
232
|
tags=tags,
|
|
233
|
+
stream_output=parsed_args.stream_output,
|
|
186
234
|
)
|
|
187
|
-
|
|
235
|
+
|
|
188
236
|
elif parsed_args.language == "python":
|
|
189
237
|
from multi_lang_build.compiler.python import PythonCompiler
|
|
190
|
-
|
|
238
|
+
|
|
191
239
|
compiler = PythonCompiler()
|
|
192
|
-
|
|
240
|
+
|
|
193
241
|
if parsed_args.create_venv:
|
|
194
242
|
result = compiler.create_venv(
|
|
195
243
|
parsed_args.create_venv,
|
|
@@ -207,21 +255,37 @@ Examples:
|
|
|
207
255
|
parsed_args.source_dir,
|
|
208
256
|
parsed_args.output,
|
|
209
257
|
mirror_enabled=parsed_args.mirror,
|
|
258
|
+
stream_output=parsed_args.stream_output,
|
|
210
259
|
)
|
|
211
260
|
|
|
212
261
|
elif parsed_args.language == "mirror":
|
|
213
|
-
|
|
214
|
-
from multi_lang_build.mirror.config import get_all_mirror_names, get_mirror_config
|
|
215
|
-
|
|
216
|
-
mirrors = get_all_mirror_names()
|
|
217
|
-
if parsed_args.language:
|
|
218
|
-
mirrors = [m for m in mirrors if m in parsed_args.language.split(",")]
|
|
262
|
+
from multi_lang_build.mirror.cli import configure_mirror, print_mirrors, get_current_config
|
|
219
263
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
264
|
+
if parsed_args.mirror_action == "list":
|
|
265
|
+
if getattr(parsed_args, "json", False):
|
|
266
|
+
from multi_lang_build.mirror.cli import get_all_mirrors
|
|
267
|
+
import json
|
|
268
|
+
print(json.dumps(get_all_mirrors(), indent=2, ensure_ascii=False))
|
|
269
|
+
else:
|
|
270
|
+
print_mirrors()
|
|
271
|
+
elif parsed_args.mirror_action == "set":
|
|
272
|
+
mirror_type = getattr(parsed_args, "type", "pip")
|
|
273
|
+
mirror_key = getattr(parsed_args, "mirror", "pip")
|
|
274
|
+
success = configure_mirror(mirror_type, mirror_key)
|
|
275
|
+
sys.exit(0 if success else 1)
|
|
276
|
+
elif parsed_args.mirror_action == "show":
|
|
277
|
+
config = get_current_config()
|
|
278
|
+
if config:
|
|
279
|
+
print("\n📦 Current Mirror Configuration:")
|
|
280
|
+
print("-" * 40)
|
|
281
|
+
for key, value in config.items():
|
|
282
|
+
print(f" • {key}: {value}")
|
|
283
|
+
print()
|
|
284
|
+
else:
|
|
285
|
+
print("\n📦 No mirror configured")
|
|
286
|
+
print(" Run 'multi-lang-build mirror set <type> <mirror>' to configure")
|
|
287
|
+
print()
|
|
288
|
+
print_mirrors()
|
|
225
289
|
else:
|
|
226
290
|
mirror_parser.print_help()
|
|
227
291
|
|
|
@@ -4,6 +4,8 @@ from abc import ABC, abstractmethod
|
|
|
4
4
|
from typing import TypeAlias, TypedDict, NotRequired
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
import time
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
7
9
|
|
|
8
10
|
|
|
9
11
|
class BuildConfig(TypedDict, total=False):
|
|
@@ -37,30 +39,30 @@ class CompilerInfo(TypedDict):
|
|
|
37
39
|
|
|
38
40
|
class CompilerBase(ABC):
|
|
39
41
|
"""Abstract base class for all language compilers."""
|
|
40
|
-
|
|
42
|
+
|
|
41
43
|
@property
|
|
42
44
|
@abstractmethod
|
|
43
45
|
def name(self) -> str:
|
|
44
46
|
"""Get the compiler name."""
|
|
45
47
|
...
|
|
46
|
-
|
|
48
|
+
|
|
47
49
|
@property
|
|
48
50
|
@abstractmethod
|
|
49
51
|
def version(self) -> str:
|
|
50
52
|
"""Get the compiler version."""
|
|
51
53
|
...
|
|
52
|
-
|
|
54
|
+
|
|
53
55
|
@property
|
|
54
56
|
@abstractmethod
|
|
55
57
|
def supported_mirrors(self) -> list[str]:
|
|
56
58
|
"""Get list of supported mirror configurations."""
|
|
57
59
|
...
|
|
58
|
-
|
|
60
|
+
|
|
59
61
|
@abstractmethod
|
|
60
62
|
def get_info(self) -> CompilerInfo:
|
|
61
63
|
"""Get compiler information."""
|
|
62
64
|
...
|
|
63
|
-
|
|
65
|
+
|
|
64
66
|
@abstractmethod
|
|
65
67
|
def build(
|
|
66
68
|
self,
|
|
@@ -70,33 +72,35 @@ class CompilerBase(ABC):
|
|
|
70
72
|
environment: dict[str, str] | None = None,
|
|
71
73
|
mirror_enabled: bool = True,
|
|
72
74
|
extra_args: list[str] | None = None,
|
|
75
|
+
stream_output: bool = True,
|
|
73
76
|
) -> BuildResult:
|
|
74
77
|
"""Execute the build process.
|
|
75
|
-
|
|
78
|
+
|
|
76
79
|
Args:
|
|
77
80
|
source_dir: Source code directory
|
|
78
81
|
output_dir: Build output directory
|
|
79
82
|
environment: Additional environment variables
|
|
80
83
|
mirror_enabled: Whether to use mirror acceleration
|
|
81
84
|
extra_args: Additional arguments to pass to the build command
|
|
82
|
-
|
|
85
|
+
stream_output: Whether to stream output in real-time (default: True)
|
|
86
|
+
|
|
83
87
|
Returns:
|
|
84
88
|
BuildResult containing success status and output information.
|
|
85
89
|
"""
|
|
86
90
|
...
|
|
87
|
-
|
|
91
|
+
|
|
88
92
|
@abstractmethod
|
|
89
93
|
def clean(self, directory: Path) -> bool:
|
|
90
94
|
"""Clean build artifacts in the specified directory.
|
|
91
|
-
|
|
95
|
+
|
|
92
96
|
Args:
|
|
93
97
|
directory: Directory to clean
|
|
94
|
-
|
|
98
|
+
|
|
95
99
|
Returns:
|
|
96
100
|
True if successful, False otherwise.
|
|
97
101
|
"""
|
|
98
102
|
...
|
|
99
|
-
|
|
103
|
+
|
|
100
104
|
def _run_build(
|
|
101
105
|
self,
|
|
102
106
|
command: list[str],
|
|
@@ -105,42 +109,151 @@ class CompilerBase(ABC):
|
|
|
105
109
|
*,
|
|
106
110
|
environment: dict[str, str] | None = None,
|
|
107
111
|
extra_args: list[str] | None = None,
|
|
112
|
+
stream_output: bool = True,
|
|
108
113
|
) -> BuildResult:
|
|
109
114
|
"""Execute a build command with timing and error handling.
|
|
110
|
-
|
|
115
|
+
|
|
111
116
|
Args:
|
|
112
117
|
command: Build command to execute
|
|
113
118
|
source_dir: Source directory for the build
|
|
114
119
|
output_dir: Output directory for build artifacts
|
|
115
120
|
environment: Additional environment variables
|
|
116
121
|
extra_args: Additional arguments to append to command
|
|
117
|
-
|
|
122
|
+
stream_output: Whether to stream output in real-time (default: True)
|
|
123
|
+
|
|
118
124
|
Returns:
|
|
119
125
|
BuildResult with success status and output information.
|
|
120
126
|
"""
|
|
121
|
-
import subprocess
|
|
122
|
-
|
|
123
127
|
full_command = command.copy()
|
|
124
128
|
if extra_args:
|
|
125
129
|
full_command.extend(extra_args)
|
|
126
|
-
|
|
130
|
+
|
|
127
131
|
env = environment.copy() if environment else {}
|
|
128
132
|
env.setdefault("PATH", "")
|
|
129
|
-
|
|
133
|
+
|
|
130
134
|
start_time = time.perf_counter()
|
|
131
|
-
|
|
135
|
+
|
|
136
|
+
if stream_output:
|
|
137
|
+
return self._run_build_stream(
|
|
138
|
+
full_command, source_dir, output_dir, env, start_time
|
|
139
|
+
)
|
|
140
|
+
else:
|
|
141
|
+
return self._run_build_capture(
|
|
142
|
+
full_command, source_dir, output_dir, env, start_time
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
def _run_build_stream(
|
|
146
|
+
self,
|
|
147
|
+
command: list[str],
|
|
148
|
+
source_dir: Path,
|
|
149
|
+
output_dir: Path,
|
|
150
|
+
env: dict[str, str],
|
|
151
|
+
start_time: float,
|
|
152
|
+
) -> BuildResult:
|
|
153
|
+
"""Execute build command with real-time output streaming.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
command: Build command to execute
|
|
157
|
+
source_dir: Source directory for the build
|
|
158
|
+
output_dir: Output directory for build artifacts
|
|
159
|
+
env: Environment variables
|
|
160
|
+
start_time: Start time for duration calculation
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
BuildResult with success status and output information.
|
|
164
|
+
"""
|
|
165
|
+
stdout_buffer = []
|
|
166
|
+
stderr_buffer = []
|
|
167
|
+
|
|
168
|
+
try:
|
|
169
|
+
process = subprocess.Popen(
|
|
170
|
+
command,
|
|
171
|
+
cwd=source_dir,
|
|
172
|
+
stdout=subprocess.PIPE,
|
|
173
|
+
stderr=subprocess.PIPE,
|
|
174
|
+
text=True,
|
|
175
|
+
env=env,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
# Read stdout in real-time
|
|
179
|
+
for line in process.stdout:
|
|
180
|
+
line = line.rstrip('\n\r')
|
|
181
|
+
stdout_buffer.append(line)
|
|
182
|
+
print(line)
|
|
183
|
+
sys.stdout.flush()
|
|
184
|
+
|
|
185
|
+
# Read stderr in real-time
|
|
186
|
+
for line in process.stderr:
|
|
187
|
+
line = line.rstrip('\n\r')
|
|
188
|
+
stderr_buffer.append(line)
|
|
189
|
+
print(line, file=sys.stderr)
|
|
190
|
+
sys.stderr.flush()
|
|
191
|
+
|
|
192
|
+
return_code = process.wait()
|
|
193
|
+
duration = time.perf_counter() - start_time
|
|
194
|
+
|
|
195
|
+
return BuildResult(
|
|
196
|
+
success=return_code == 0,
|
|
197
|
+
return_code=return_code,
|
|
198
|
+
stdout='\n'.join(stdout_buffer),
|
|
199
|
+
stderr='\n'.join(stderr_buffer),
|
|
200
|
+
output_path=output_dir if return_code == 0 else None,
|
|
201
|
+
duration_seconds=duration,
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
except subprocess.TimeoutExpired:
|
|
205
|
+
duration = time.perf_counter() - start_time
|
|
206
|
+
return BuildResult(
|
|
207
|
+
success=False,
|
|
208
|
+
return_code=-1,
|
|
209
|
+
stdout='\n'.join(stdout_buffer),
|
|
210
|
+
stderr="Build timed out after 1 hour",
|
|
211
|
+
output_path=None,
|
|
212
|
+
duration_seconds=duration,
|
|
213
|
+
)
|
|
214
|
+
except Exception as e:
|
|
215
|
+
duration = time.perf_counter() - start_time
|
|
216
|
+
return BuildResult(
|
|
217
|
+
success=False,
|
|
218
|
+
return_code=-2,
|
|
219
|
+
stdout='\n'.join(stdout_buffer),
|
|
220
|
+
stderr=f"Build error: {str(e)}",
|
|
221
|
+
output_path=None,
|
|
222
|
+
duration_seconds=duration,
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
def _run_build_capture(
|
|
226
|
+
self,
|
|
227
|
+
command: list[str],
|
|
228
|
+
source_dir: Path,
|
|
229
|
+
output_dir: Path,
|
|
230
|
+
env: dict[str, str],
|
|
231
|
+
start_time: float,
|
|
232
|
+
) -> BuildResult:
|
|
233
|
+
"""Execute build command with captured output (no real-time display).
|
|
234
|
+
|
|
235
|
+
Args:
|
|
236
|
+
command: Build command to execute
|
|
237
|
+
source_dir: Source directory for the build
|
|
238
|
+
output_dir: Output directory for build artifacts
|
|
239
|
+
env: Environment variables
|
|
240
|
+
start_time: Start time for duration calculation
|
|
241
|
+
|
|
242
|
+
Returns:
|
|
243
|
+
BuildResult with success status and output information.
|
|
244
|
+
"""
|
|
132
245
|
try:
|
|
133
246
|
result = subprocess.run(
|
|
134
|
-
|
|
247
|
+
command,
|
|
135
248
|
cwd=source_dir,
|
|
136
249
|
capture_output=True,
|
|
137
250
|
text=True,
|
|
138
251
|
env=env,
|
|
139
252
|
timeout=3600, # 1 hour timeout
|
|
140
253
|
)
|
|
141
|
-
|
|
254
|
+
|
|
142
255
|
duration = time.perf_counter() - start_time
|
|
143
|
-
|
|
256
|
+
|
|
144
257
|
return BuildResult(
|
|
145
258
|
success=result.returncode == 0,
|
|
146
259
|
return_code=result.returncode,
|
|
@@ -149,7 +262,7 @@ class CompilerBase(ABC):
|
|
|
149
262
|
output_path=output_dir if result.returncode == 0 else None,
|
|
150
263
|
duration_seconds=duration,
|
|
151
264
|
)
|
|
152
|
-
|
|
265
|
+
|
|
153
266
|
except subprocess.TimeoutExpired:
|
|
154
267
|
duration = time.perf_counter() - start_time
|
|
155
268
|
return BuildResult(
|