multi-lang-build 0.2.7__py3-none-any.whl → 0.2.10__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 +4 -4
- multi_lang_build/cli.py +3 -2
- multi_lang_build/compiler/__init__.py +1 -1
- multi_lang_build/compiler/go_compiler.py +388 -0
- multi_lang_build/compiler/go_support/__init__.py +19 -0
- multi_lang_build/compiler/go_support/binary.py +117 -0
- multi_lang_build/compiler/go_support/builder.py +228 -0
- multi_lang_build/compiler/go_support/cleaner.py +40 -0
- multi_lang_build/compiler/go_support/env.py +41 -0
- multi_lang_build/compiler/go_support/mirror.py +111 -0
- multi_lang_build/compiler/go_support/module.py +77 -0
- multi_lang_build/compiler/go_support/tester.py +199 -0
- multi_lang_build/compiler/pnpm.py +61 -209
- multi_lang_build/compiler/pnpm_support/__init__.py +6 -0
- multi_lang_build/compiler/pnpm_support/executor.py +148 -0
- multi_lang_build/compiler/pnpm_support/project.py +53 -0
- multi_lang_build/compiler/python.py +65 -222
- multi_lang_build/compiler/python_support/__init__.py +13 -0
- multi_lang_build/compiler/python_support/cleaner.py +56 -0
- multi_lang_build/compiler/python_support/cli.py +46 -0
- multi_lang_build/compiler/python_support/detector.py +64 -0
- multi_lang_build/compiler/python_support/installer.py +162 -0
- multi_lang_build/compiler/python_support/venv.py +63 -0
- multi_lang_build/ide_register.py +97 -0
- multi_lang_build/mirror/config.py +19 -0
- multi_lang_build/register/support/__init__.py +13 -0
- multi_lang_build/register/support/claude.py +94 -0
- multi_lang_build/register/support/codebuddy.py +100 -0
- multi_lang_build/register/support/opencode.py +62 -0
- multi_lang_build/register/support/trae.py +72 -0
- {multi_lang_build-0.2.7.dist-info → multi_lang_build-0.2.10.dist-info}/METADATA +1 -1
- multi_lang_build-0.2.10.dist-info/RECORD +40 -0
- multi_lang_build/compiler/go.py +0 -532
- multi_lang_build/register.py +0 -412
- multi_lang_build-0.2.7.dist-info/RECORD +0 -18
- {multi_lang_build-0.2.7.dist-info → multi_lang_build-0.2.10.dist-info}/WHEEL +0 -0
- {multi_lang_build-0.2.7.dist-info → multi_lang_build-0.2.10.dist-info}/entry_points.txt +0 -0
- {multi_lang_build-0.2.7.dist-info → multi_lang_build-0.2.10.dist-info}/licenses/LICENSE +0 -0
|
@@ -5,35 +5,33 @@ from typing import Final
|
|
|
5
5
|
import shutil
|
|
6
6
|
import subprocess
|
|
7
7
|
import sys
|
|
8
|
-
import venv
|
|
9
|
-
import json
|
|
10
|
-
import time
|
|
11
8
|
|
|
12
9
|
from multi_lang_build.compiler.base import CompilerBase, BuildResult, CompilerInfo
|
|
13
10
|
from multi_lang_build.mirror.config import (
|
|
14
|
-
get_mirror_config,
|
|
15
11
|
apply_mirror_environment,
|
|
16
|
-
PYPI_MIRROR_TSINGHUA,
|
|
17
|
-
PYPI_MIRROR_ALIYUN,
|
|
18
|
-
PYPI_MIRROR_DOUBAN,
|
|
19
|
-
PYPI_MIRROR_HUAWEI,
|
|
20
12
|
DEFAULT_PIP_MIRROR,
|
|
21
13
|
)
|
|
14
|
+
from multi_lang_build.compiler.python_support import (
|
|
15
|
+
BuildSystemDetector,
|
|
16
|
+
DependencyInstaller,
|
|
17
|
+
VenvManager,
|
|
18
|
+
PythonCleaner,
|
|
19
|
+
)
|
|
22
20
|
|
|
23
21
|
|
|
24
22
|
class PythonCompiler(CompilerBase):
|
|
25
23
|
"""Compiler for Python projects with pip and poetry support."""
|
|
26
|
-
|
|
24
|
+
|
|
27
25
|
NAME: Final[str] = "python"
|
|
28
26
|
DEFAULT_MIRROR: Final[str] = DEFAULT_PIP_MIRROR
|
|
29
|
-
|
|
27
|
+
|
|
30
28
|
def __init__(
|
|
31
|
-
self,
|
|
29
|
+
self,
|
|
32
30
|
python_path: str | None = None,
|
|
33
31
|
mirror: str | None = None,
|
|
34
32
|
) -> None:
|
|
35
33
|
"""Initialize the Python compiler.
|
|
36
|
-
|
|
34
|
+
|
|
37
35
|
Args:
|
|
38
36
|
python_path: Optional path to python executable. If None, uses sys.executable.
|
|
39
37
|
mirror: Mirror configuration name (e.g., "pip", "pip_aliyun", "pip_douban", "pip_huawei").
|
|
@@ -42,20 +40,20 @@ class PythonCompiler(CompilerBase):
|
|
|
42
40
|
self._python_path = python_path
|
|
43
41
|
self._mirror = mirror
|
|
44
42
|
self._version_cache: str | None = None
|
|
45
|
-
|
|
43
|
+
|
|
46
44
|
@property
|
|
47
45
|
def name(self) -> str:
|
|
48
46
|
"""Get the compiler name."""
|
|
49
47
|
return self.NAME
|
|
50
|
-
|
|
48
|
+
|
|
51
49
|
@property
|
|
52
50
|
def version(self) -> str:
|
|
53
51
|
"""Get the Python version."""
|
|
54
52
|
if self._version_cache:
|
|
55
53
|
return self._version_cache
|
|
56
|
-
|
|
54
|
+
|
|
57
55
|
python_executable = self._get_executable_path()
|
|
58
|
-
|
|
56
|
+
|
|
59
57
|
try:
|
|
60
58
|
result = subprocess.run(
|
|
61
59
|
[python_executable, "--version"],
|
|
@@ -67,19 +65,19 @@ class PythonCompiler(CompilerBase):
|
|
|
67
65
|
self._version_cache = output.replace("Python ", "")
|
|
68
66
|
except Exception:
|
|
69
67
|
self._version_cache = sys.version.split()[0]
|
|
70
|
-
|
|
68
|
+
|
|
71
69
|
return self._version_cache
|
|
72
|
-
|
|
70
|
+
|
|
73
71
|
@property
|
|
74
72
|
def supported_mirrors(self) -> list[str]:
|
|
75
73
|
"""Get list of supported mirror configurations."""
|
|
76
74
|
return ["pip", "pip_aliyun", "pip_douban", "pip_huawei"]
|
|
77
|
-
|
|
75
|
+
|
|
78
76
|
@property
|
|
79
77
|
def current_mirror(self) -> str:
|
|
80
78
|
"""Get the current mirror configuration name."""
|
|
81
79
|
return self._mirror or "pip"
|
|
82
|
-
|
|
80
|
+
|
|
83
81
|
def get_info(self) -> CompilerInfo:
|
|
84
82
|
"""Get compiler information."""
|
|
85
83
|
return {
|
|
@@ -89,15 +87,15 @@ class PythonCompiler(CompilerBase):
|
|
|
89
87
|
"default_mirror": self.DEFAULT_MIRROR,
|
|
90
88
|
"executable": self._get_executable_path(),
|
|
91
89
|
}
|
|
92
|
-
|
|
90
|
+
|
|
93
91
|
def set_mirror(self, mirror: str) -> None:
|
|
94
92
|
"""Set the mirror configuration.
|
|
95
|
-
|
|
93
|
+
|
|
96
94
|
Args:
|
|
97
95
|
mirror: Mirror configuration name (e.g., "pip", "pip_aliyun", "pip_douban", "pip_huawei")
|
|
98
96
|
"""
|
|
99
97
|
self._mirror = mirror
|
|
100
|
-
|
|
98
|
+
|
|
101
99
|
def build(
|
|
102
100
|
self,
|
|
103
101
|
source_dir: Path,
|
|
@@ -122,20 +120,20 @@ class PythonCompiler(CompilerBase):
|
|
|
122
120
|
BuildResult containing success status and output information.
|
|
123
121
|
"""
|
|
124
122
|
python_executable = self._get_executable_path()
|
|
125
|
-
|
|
123
|
+
|
|
126
124
|
# Validate directories
|
|
127
125
|
source_dir = self._validate_directory(source_dir, create_if_not_exists=False)
|
|
128
126
|
output_dir = self._validate_directory(output_dir, create_if_not_exists=True)
|
|
129
|
-
|
|
127
|
+
|
|
130
128
|
# Prepare environment
|
|
131
129
|
env = environment.copy() if environment else {}
|
|
132
|
-
|
|
130
|
+
|
|
133
131
|
if mirror_enabled:
|
|
134
132
|
env = apply_mirror_environment("python", env)
|
|
135
133
|
env = apply_mirror_environment("pip", env)
|
|
136
|
-
|
|
134
|
+
|
|
137
135
|
# Determine build system
|
|
138
|
-
build_system =
|
|
136
|
+
build_system = BuildSystemDetector.detect(source_dir)
|
|
139
137
|
|
|
140
138
|
if build_system == "poetry":
|
|
141
139
|
return self._build_with_poetry(
|
|
@@ -159,42 +157,7 @@ class PythonCompiler(CompilerBase):
|
|
|
159
157
|
output_path=None,
|
|
160
158
|
duration_seconds=0.0,
|
|
161
159
|
)
|
|
162
|
-
|
|
163
|
-
def _detect_build_system(self, source_dir: Path) -> str:
|
|
164
|
-
"""Detect the build system used by the project.
|
|
165
|
-
|
|
166
|
-
Args:
|
|
167
|
-
source_dir: Source directory to check
|
|
168
|
-
|
|
169
|
-
Returns:
|
|
170
|
-
Build system name: "poetry", "setuptools", "pdm", or "none"
|
|
171
|
-
"""
|
|
172
|
-
pyproject = source_dir / "pyproject.toml"
|
|
173
|
-
setup_py = source_dir / "setup.py"
|
|
174
|
-
setup_cfg = source_dir / "setup.cfg"
|
|
175
|
-
pdm_pyproject = source_dir / "pyproject.toml"
|
|
176
|
-
|
|
177
|
-
# Check for poetry
|
|
178
|
-
if pyproject.exists():
|
|
179
|
-
try:
|
|
180
|
-
content = pyproject.read_text()
|
|
181
|
-
if "[tool.poetry]" in content:
|
|
182
|
-
return "poetry"
|
|
183
|
-
if "[tool.pdm]" in content:
|
|
184
|
-
return "pdm"
|
|
185
|
-
except Exception:
|
|
186
|
-
pass
|
|
187
|
-
|
|
188
|
-
# Check for setuptools
|
|
189
|
-
if setup_py.exists() or setup_cfg.exists():
|
|
190
|
-
return "setuptools"
|
|
191
|
-
|
|
192
|
-
# Check pdm
|
|
193
|
-
if pdm_pyproject.exists():
|
|
194
|
-
return "pdm"
|
|
195
|
-
|
|
196
|
-
return "none"
|
|
197
|
-
|
|
160
|
+
|
|
198
161
|
def _build_with_poetry(
|
|
199
162
|
self,
|
|
200
163
|
python_executable: str,
|
|
@@ -250,10 +213,10 @@ class PythonCompiler(CompilerBase):
|
|
|
250
213
|
else:
|
|
251
214
|
# Legacy setup.py
|
|
252
215
|
build_cmd = [python_executable, "setup.py", "bdist_wheel", "--dist-dir", str(output_dir)]
|
|
253
|
-
|
|
216
|
+
|
|
254
217
|
if extra_args:
|
|
255
218
|
build_cmd.extend(extra_args)
|
|
256
|
-
|
|
219
|
+
|
|
257
220
|
return self._run_build(
|
|
258
221
|
build_cmd,
|
|
259
222
|
source_dir,
|
|
@@ -285,7 +248,7 @@ class PythonCompiler(CompilerBase):
|
|
|
285
248
|
environment=env,
|
|
286
249
|
stream_output=stream_output,
|
|
287
250
|
)
|
|
288
|
-
|
|
251
|
+
|
|
289
252
|
def install_dependencies(
|
|
290
253
|
self,
|
|
291
254
|
source_dir: Path,
|
|
@@ -296,42 +259,37 @@ class PythonCompiler(CompilerBase):
|
|
|
296
259
|
poetry: bool = False,
|
|
297
260
|
) -> BuildResult:
|
|
298
261
|
"""Install Python dependencies.
|
|
299
|
-
|
|
262
|
+
|
|
300
263
|
Args:
|
|
301
264
|
source_dir: Source code directory
|
|
302
265
|
environment: Additional environment variables
|
|
303
266
|
mirror_enabled: Whether to use PyPI mirror
|
|
304
267
|
dev: Include development dependencies
|
|
305
268
|
poetry: Force using poetry
|
|
306
|
-
|
|
269
|
+
|
|
307
270
|
Returns:
|
|
308
271
|
BuildResult containing success status and output information.
|
|
309
272
|
"""
|
|
310
273
|
python_executable = self._get_executable_path()
|
|
311
|
-
|
|
274
|
+
|
|
312
275
|
source_dir = self._validate_directory(source_dir, create_if_not_exists=False)
|
|
313
|
-
|
|
276
|
+
|
|
314
277
|
env = environment.copy() if environment else {}
|
|
315
|
-
|
|
278
|
+
|
|
316
279
|
if mirror_enabled:
|
|
317
280
|
env = apply_mirror_environment("pip", env)
|
|
318
|
-
|
|
281
|
+
|
|
319
282
|
# Check for poetry first
|
|
320
|
-
if poetry or
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
283
|
+
if poetry or BuildSystemDetector.is_poetry(source_dir):
|
|
284
|
+
return DependencyInstaller.install_poetry(
|
|
285
|
+
python_executable, source_dir, env, dev, True
|
|
286
|
+
)
|
|
287
|
+
|
|
326
288
|
# Use pip
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
cmd.append(str(source_dir / "requirements-dev.txt"))
|
|
332
|
-
|
|
333
|
-
return self._run_build(cmd, source_dir, source_dir, environment=env)
|
|
334
|
-
|
|
289
|
+
return DependencyInstaller.install_pip(
|
|
290
|
+
python_executable, source_dir, env, dev, True
|
|
291
|
+
)
|
|
292
|
+
|
|
335
293
|
def create_venv(
|
|
336
294
|
self,
|
|
337
295
|
directory: Path,
|
|
@@ -340,52 +298,17 @@ class PythonCompiler(CompilerBase):
|
|
|
340
298
|
mirror_enabled: bool = True,
|
|
341
299
|
) -> BuildResult:
|
|
342
300
|
"""Create a virtual environment.
|
|
343
|
-
|
|
301
|
+
|
|
344
302
|
Args:
|
|
345
303
|
directory: Directory to create the venv in
|
|
346
304
|
python_path: Python interpreter to use
|
|
347
305
|
mirror_enabled: Whether to configure pip mirror
|
|
348
|
-
|
|
306
|
+
|
|
349
307
|
Returns:
|
|
350
308
|
BuildResult containing success status and output information.
|
|
351
309
|
"""
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
start_time = time.perf_counter() # noqa: F821
|
|
355
|
-
|
|
356
|
-
try:
|
|
357
|
-
env_builder.create(directory)
|
|
358
|
-
|
|
359
|
-
# Configure pip mirror if enabled
|
|
360
|
-
if mirror_enabled:
|
|
361
|
-
pip_config_dir = directory / "pip.conf"
|
|
362
|
-
pip_config_dir.write_text(
|
|
363
|
-
"[global]\n"
|
|
364
|
-
"index-url = https://pypi.tuna.tsinghua.edu.cn/simple\n"
|
|
365
|
-
"trusted-host = pypi.tuna.tsinghua.edu.cn\n"
|
|
366
|
-
)
|
|
367
|
-
|
|
368
|
-
duration = time.perf_counter() - start_time # noqa: F821
|
|
369
|
-
|
|
370
|
-
return BuildResult(
|
|
371
|
-
success=True,
|
|
372
|
-
return_code=0,
|
|
373
|
-
stdout=f"Virtual environment created at {directory}",
|
|
374
|
-
stderr="",
|
|
375
|
-
output_path=directory,
|
|
376
|
-
duration_seconds=duration,
|
|
377
|
-
)
|
|
378
|
-
except Exception as e:
|
|
379
|
-
duration = time.perf_counter() - start_time # noqa: F821
|
|
380
|
-
return BuildResult(
|
|
381
|
-
success=False,
|
|
382
|
-
return_code=-1,
|
|
383
|
-
stdout="",
|
|
384
|
-
stderr=f"Failed to create virtual environment: {str(e)}",
|
|
385
|
-
output_path=None,
|
|
386
|
-
duration_seconds=duration,
|
|
387
|
-
)
|
|
388
|
-
|
|
310
|
+
return VenvManager.create(directory, python_path, mirror_enabled)
|
|
311
|
+
|
|
389
312
|
def run_script(
|
|
390
313
|
self,
|
|
391
314
|
source_dir: Path,
|
|
@@ -395,145 +318,65 @@ class PythonCompiler(CompilerBase):
|
|
|
395
318
|
mirror_enabled: bool = True,
|
|
396
319
|
) -> BuildResult:
|
|
397
320
|
"""Run a Python script or module.
|
|
398
|
-
|
|
321
|
+
|
|
399
322
|
Args:
|
|
400
323
|
source_dir: Source code directory
|
|
401
324
|
script_name: Script or module name to run
|
|
402
325
|
environment: Additional environment variables
|
|
403
326
|
mirror_enabled: Whether to use PyPI mirror
|
|
404
|
-
|
|
327
|
+
|
|
405
328
|
Returns:
|
|
406
329
|
BuildResult containing success status and output information.
|
|
407
330
|
"""
|
|
408
331
|
python_executable = self._get_executable_path()
|
|
409
|
-
|
|
332
|
+
|
|
410
333
|
source_dir = self._validate_directory(source_dir, create_if_not_exists=False)
|
|
411
|
-
|
|
334
|
+
|
|
412
335
|
env = environment.copy() if environment else {}
|
|
413
|
-
|
|
336
|
+
|
|
414
337
|
if mirror_enabled:
|
|
415
338
|
env = apply_mirror_environment("pip", env)
|
|
416
|
-
|
|
339
|
+
|
|
417
340
|
return self._run_build(
|
|
418
341
|
[python_executable, "-m", script_name],
|
|
419
342
|
source_dir,
|
|
420
343
|
source_dir,
|
|
421
344
|
environment=env,
|
|
422
345
|
)
|
|
423
|
-
|
|
346
|
+
|
|
424
347
|
def clean(self, directory: Path) -> bool:
|
|
425
348
|
"""Clean Python build artifacts in the specified directory.
|
|
426
|
-
|
|
349
|
+
|
|
427
350
|
Args:
|
|
428
351
|
directory: Directory to clean
|
|
429
|
-
|
|
352
|
+
|
|
430
353
|
Returns:
|
|
431
354
|
True if successful, False otherwise.
|
|
432
355
|
"""
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
try:
|
|
436
|
-
directory = self._validate_directory(directory, create_if_not_exists=False)
|
|
437
|
-
|
|
438
|
-
# Remove __pycache__ directories
|
|
439
|
-
for pycache in directory.rglob("__pycache__"):
|
|
440
|
-
shutil.rmtree(pycache)
|
|
441
|
-
|
|
442
|
-
# Remove .pyc files
|
|
443
|
-
for pyc in directory.rglob("*.pyc"):
|
|
444
|
-
pyc.unlink()
|
|
445
|
-
|
|
446
|
-
# Remove .pytest_cache
|
|
447
|
-
pytest_cache = directory / ".pytest_cache"
|
|
448
|
-
if pytest_cache.exists():
|
|
449
|
-
shutil.rmtree(pytest_cache)
|
|
450
|
-
|
|
451
|
-
# Remove .tox
|
|
452
|
-
tox_dir = directory / ".tox"
|
|
453
|
-
if tox_dir.exists():
|
|
454
|
-
shutil.rmtree(tox_dir)
|
|
455
|
-
|
|
456
|
-
# Remove dist directory
|
|
457
|
-
dist_dir = directory / "dist"
|
|
458
|
-
if dist_dir.exists():
|
|
459
|
-
shutil.rmtree(dist_dir)
|
|
460
|
-
|
|
461
|
-
# Remove build directory
|
|
462
|
-
build_dir = directory / "build"
|
|
463
|
-
if build_dir.exists():
|
|
464
|
-
shutil.rmtree(build_dir)
|
|
465
|
-
|
|
466
|
-
# Remove *.egg-info directories
|
|
467
|
-
for egg_info in directory.rglob("*.egg-info"):
|
|
468
|
-
shutil.rmtree(egg_info)
|
|
469
|
-
|
|
470
|
-
return True
|
|
471
|
-
|
|
472
|
-
except Exception:
|
|
473
|
-
return False
|
|
474
|
-
|
|
356
|
+
return PythonCleaner.clean(directory)
|
|
357
|
+
|
|
475
358
|
def _get_executable_path(self) -> str:
|
|
476
359
|
"""Get the python executable path."""
|
|
477
360
|
if self._python_path:
|
|
478
361
|
return self._python_path
|
|
479
|
-
|
|
362
|
+
|
|
480
363
|
python_path = shutil.which("python3") or shutil.which("python")
|
|
481
364
|
if python_path is None:
|
|
482
365
|
return sys.executable
|
|
483
|
-
|
|
366
|
+
|
|
484
367
|
return python_path
|
|
485
|
-
|
|
368
|
+
|
|
486
369
|
@staticmethod
|
|
487
370
|
def create(source_dir: Path, *, mirror_enabled: bool = True) -> "PythonCompiler":
|
|
488
371
|
"""Factory method to create a PythonCompiler instance.
|
|
489
|
-
|
|
372
|
+
|
|
490
373
|
Args:
|
|
491
374
|
source_dir: Source directory for the project
|
|
492
375
|
mirror_enabled: Whether to enable mirror acceleration by default
|
|
493
|
-
|
|
376
|
+
|
|
494
377
|
Returns:
|
|
495
378
|
Configured PythonCompiler instance
|
|
496
379
|
"""
|
|
497
380
|
compiler = PythonCompiler()
|
|
498
381
|
return compiler
|
|
499
382
|
|
|
500
|
-
|
|
501
|
-
def main() -> None:
|
|
502
|
-
"""Python compiler CLI entry point."""
|
|
503
|
-
import argparse
|
|
504
|
-
import sys
|
|
505
|
-
|
|
506
|
-
parser = argparse.ArgumentParser(description="Python Build Compiler")
|
|
507
|
-
parser.add_argument("source_dir", type=Path, help="Source directory")
|
|
508
|
-
parser.add_argument("-o", "--output", type=Path, required=True, help="Output directory")
|
|
509
|
-
parser.add_argument("--mirror", action="store_true", default=True, help="Enable PyPI mirror")
|
|
510
|
-
parser.add_argument("--no-mirror", dest="mirror", action="store_false", help="Disable PyPI mirror")
|
|
511
|
-
parser.add_argument("--install", action="store_true", help="Install dependencies only")
|
|
512
|
-
parser.add_argument("--dev", action="store_true", help="Include dev dependencies")
|
|
513
|
-
parser.add_argument("--poetry", action="store_true", help="Force using poetry")
|
|
514
|
-
parser.add_argument("--create-venv", type=Path, help="Create virtual environment at path")
|
|
515
|
-
|
|
516
|
-
args = parser.parse_args()
|
|
517
|
-
|
|
518
|
-
compiler = PythonCompiler()
|
|
519
|
-
|
|
520
|
-
if args.create_venv:
|
|
521
|
-
result = compiler.create_venv(
|
|
522
|
-
args.create_venv,
|
|
523
|
-
mirror_enabled=args.mirror,
|
|
524
|
-
)
|
|
525
|
-
elif args.install:
|
|
526
|
-
result = compiler.install_dependencies(
|
|
527
|
-
args.source_dir,
|
|
528
|
-
mirror_enabled=args.mirror,
|
|
529
|
-
dev=args.dev,
|
|
530
|
-
poetry=args.poetry,
|
|
531
|
-
)
|
|
532
|
-
else:
|
|
533
|
-
result = compiler.build(
|
|
534
|
-
args.source_dir,
|
|
535
|
-
args.output,
|
|
536
|
-
mirror_enabled=args.mirror,
|
|
537
|
-
)
|
|
538
|
-
|
|
539
|
-
sys.exit(0 if result["success"] else 1)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Python compiler support modules."""
|
|
2
|
+
|
|
3
|
+
from multi_lang_build.compiler.python_support.detector import BuildSystemDetector
|
|
4
|
+
from multi_lang_build.compiler.python_support.installer import DependencyInstaller
|
|
5
|
+
from multi_lang_build.compiler.python_support.venv import VenvManager
|
|
6
|
+
from multi_lang_build.compiler.python_support.cleaner import PythonCleaner
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"BuildSystemDetector",
|
|
10
|
+
"DependencyInstaller",
|
|
11
|
+
"VenvManager",
|
|
12
|
+
"PythonCleaner",
|
|
13
|
+
]
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""Python build artifact cleaning utilities."""
|
|
2
|
+
|
|
3
|
+
import shutil
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class PythonCleaner:
|
|
8
|
+
"""Clean Python build artifacts."""
|
|
9
|
+
|
|
10
|
+
@staticmethod
|
|
11
|
+
def clean(directory: Path) -> bool:
|
|
12
|
+
"""Clean Python build artifacts in the specified directory.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
directory: Directory to clean
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
True if successful, False otherwise.
|
|
19
|
+
"""
|
|
20
|
+
try:
|
|
21
|
+
# Remove __pycache__ directories
|
|
22
|
+
for pycache in directory.rglob("__pycache__"):
|
|
23
|
+
shutil.rmtree(pycache)
|
|
24
|
+
|
|
25
|
+
# Remove .pyc files
|
|
26
|
+
for pyc in directory.rglob("*.pyc"):
|
|
27
|
+
pyc.unlink()
|
|
28
|
+
|
|
29
|
+
# Remove .pytest_cache
|
|
30
|
+
pytest_cache = directory / ".pytest_cache"
|
|
31
|
+
if pytest_cache.exists():
|
|
32
|
+
shutil.rmtree(pytest_cache)
|
|
33
|
+
|
|
34
|
+
# Remove .tox
|
|
35
|
+
tox_dir = directory / ".tox"
|
|
36
|
+
if tox_dir.exists():
|
|
37
|
+
shutil.rmtree(tox_dir)
|
|
38
|
+
|
|
39
|
+
# Remove dist directory
|
|
40
|
+
dist_dir = directory / "dist"
|
|
41
|
+
if dist_dir.exists():
|
|
42
|
+
shutil.rmtree(dist_dir)
|
|
43
|
+
|
|
44
|
+
# Remove build directory
|
|
45
|
+
build_dir = directory / "build"
|
|
46
|
+
if build_dir.exists():
|
|
47
|
+
shutil.rmtree(build_dir)
|
|
48
|
+
|
|
49
|
+
# Remove *.egg-info directories
|
|
50
|
+
for egg_info in directory.rglob("*.egg-info"):
|
|
51
|
+
shutil.rmtree(egg_info)
|
|
52
|
+
|
|
53
|
+
return True
|
|
54
|
+
|
|
55
|
+
except Exception:
|
|
56
|
+
return False
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Python compiler CLI entry point."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def main() -> None:
|
|
7
|
+
"""Python compiler CLI entry point."""
|
|
8
|
+
import argparse
|
|
9
|
+
import sys
|
|
10
|
+
|
|
11
|
+
parser = argparse.ArgumentParser(description="Python Build Compiler")
|
|
12
|
+
parser.add_argument("source_dir", type=Path, help="Source directory")
|
|
13
|
+
parser.add_argument("-o", "--output", type=Path, required=True, help="Output directory")
|
|
14
|
+
parser.add_argument("--mirror", action="store_true", default=True, help="Enable PyPI mirror")
|
|
15
|
+
parser.add_argument("--no-mirror", dest="mirror", action="store_false", help="Disable PyPI mirror")
|
|
16
|
+
parser.add_argument("--install", action="store_true", help="Install dependencies only")
|
|
17
|
+
parser.add_argument("--dev", action="store_true", help="Include dev dependencies")
|
|
18
|
+
parser.add_argument("--poetry", action="store_true", help="Force using poetry")
|
|
19
|
+
parser.add_argument("--create-venv", type=Path, help="Create virtual environment at path")
|
|
20
|
+
|
|
21
|
+
args = parser.parse_args()
|
|
22
|
+
|
|
23
|
+
from multi_lang_build.compiler.python import PythonCompiler
|
|
24
|
+
|
|
25
|
+
compiler = PythonCompiler()
|
|
26
|
+
|
|
27
|
+
if args.create_venv:
|
|
28
|
+
result = compiler.create_venv(
|
|
29
|
+
args.create_venv,
|
|
30
|
+
mirror_enabled=args.mirror,
|
|
31
|
+
)
|
|
32
|
+
elif args.install:
|
|
33
|
+
result = compiler.install_dependencies(
|
|
34
|
+
args.source_dir,
|
|
35
|
+
mirror_enabled=args.mirror,
|
|
36
|
+
dev=args.dev,
|
|
37
|
+
poetry=args.poetry,
|
|
38
|
+
)
|
|
39
|
+
else:
|
|
40
|
+
result = compiler.build(
|
|
41
|
+
args.source_dir,
|
|
42
|
+
args.output,
|
|
43
|
+
mirror_enabled=args.mirror,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
sys.exit(0 if result["success"] else 1)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Python build system detection."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BuildSystemDetector:
|
|
7
|
+
"""Detect Python build systems (Poetry, setuptools, PDM)."""
|
|
8
|
+
|
|
9
|
+
@staticmethod
|
|
10
|
+
def detect(source_dir: Path) -> str:
|
|
11
|
+
"""Detect the build system used by the project.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
source_dir: Source directory to check
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
Build system name: "poetry", "setuptools", "pdm", or "none"
|
|
18
|
+
"""
|
|
19
|
+
pyproject = source_dir / "pyproject.toml"
|
|
20
|
+
setup_py = source_dir / "setup.py"
|
|
21
|
+
setup_cfg = source_dir / "setup.cfg"
|
|
22
|
+
|
|
23
|
+
# Check for poetry
|
|
24
|
+
if pyproject.exists():
|
|
25
|
+
try:
|
|
26
|
+
content = pyproject.read_text()
|
|
27
|
+
if "[tool.poetry]" in content:
|
|
28
|
+
return "poetry"
|
|
29
|
+
if "[tool.pdm]" in content:
|
|
30
|
+
return "pdm"
|
|
31
|
+
except Exception:
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
# Check for setuptools
|
|
35
|
+
if setup_py.exists() or setup_cfg.exists():
|
|
36
|
+
return "setuptools"
|
|
37
|
+
|
|
38
|
+
# Check pdm
|
|
39
|
+
if pyproject.exists():
|
|
40
|
+
return "pdm"
|
|
41
|
+
|
|
42
|
+
return "none"
|
|
43
|
+
|
|
44
|
+
@staticmethod
|
|
45
|
+
def is_poetry(source_dir: Path) -> bool:
|
|
46
|
+
"""Check if project uses Poetry."""
|
|
47
|
+
pyproject = source_dir / "pyproject.toml"
|
|
48
|
+
if not pyproject.exists():
|
|
49
|
+
return False
|
|
50
|
+
try:
|
|
51
|
+
return "[tool.poetry]" in pyproject.read_text()
|
|
52
|
+
except Exception:
|
|
53
|
+
return False
|
|
54
|
+
|
|
55
|
+
@staticmethod
|
|
56
|
+
def is_pdm(source_dir: Path) -> bool:
|
|
57
|
+
"""Check if project uses PDM."""
|
|
58
|
+
pyproject = source_dir / "pyproject.toml"
|
|
59
|
+
if not pyproject.exists():
|
|
60
|
+
return False
|
|
61
|
+
try:
|
|
62
|
+
return "[tool.pdm]" in pyproject.read_text()
|
|
63
|
+
except Exception:
|
|
64
|
+
return False
|