multi-lang-build 0.2.0__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.
@@ -0,0 +1,57 @@
1
+ """Multi-Lang Build - Multi-language automated build tool with domestic mirror acceleration."""
2
+
3
+ from multi_lang_build.compiler.base import BuildConfig, BuildResult, CompilerBase
4
+ from multi_lang_build.compiler.pnpm import PnpmCompiler
5
+ from multi_lang_build.compiler.go import GoCompiler
6
+ from multi_lang_build.compiler.python import PythonCompiler
7
+ from multi_lang_build.mirror.config import MirrorConfig, get_mirror_config
8
+ from multi_lang_build.register import register_skill
9
+
10
+ __version__ = "0.2.0"
11
+ __all__ = [
12
+ "BuildConfig",
13
+ "BuildResult",
14
+ "CompilerBase",
15
+ "PnpmCompiler",
16
+ "GoCompiler",
17
+ "PythonCompiler",
18
+ "MirrorConfig",
19
+ "get_mirror_config",
20
+ "register_skill",
21
+ ]
22
+
23
+
24
+ def main() -> None:
25
+ """Main entry point for the multi-lang-build CLI tool."""
26
+ import sys
27
+ from multi_lang_build.cli import run_cli
28
+
29
+ run_cli(sys.argv[1:])
30
+
31
+
32
+ def main_register() -> None:
33
+ """Entry point for the register CLI tool."""
34
+ import sys
35
+ import argparse
36
+ from multi_lang_build.register import register_skill
37
+
38
+ parser = argparse.ArgumentParser(
39
+ description="Register multi-lang-build as an IDE skill",
40
+ prog="multi-lang-register",
41
+ )
42
+ parser.add_argument(
43
+ "ide",
44
+ nargs="?",
45
+ default="claude",
46
+ choices=["claude", "opencode", "trae", "codebuddy", "all"],
47
+ help="IDE to register with (default: claude)",
48
+ )
49
+ parser.add_argument(
50
+ "--version",
51
+ action="version",
52
+ version=f"%(prog)s {__version__}",
53
+ )
54
+
55
+ args = parser.parse_args()
56
+ success = register_skill(args.ide)
57
+ sys.exit(0 if success else 1)
@@ -0,0 +1,256 @@
1
+ """CLI interface for auto-build tool."""
2
+
3
+ from pathlib import Path
4
+ from typing import Sequence, TypedDict
5
+
6
+
7
+ class BuildResult(TypedDict):
8
+ """Result of a build operation."""
9
+ success: bool
10
+ return_code: int
11
+ stdout: str
12
+ stderr: str
13
+ output_path: Path | None
14
+ duration_seconds: float
15
+
16
+
17
+ def run_cli(args: Sequence[str]) -> None:
18
+ """Run the CLI with the given arguments.
19
+
20
+ Args:
21
+ args: Command line arguments (excluding program name)
22
+ """
23
+ import argparse
24
+ import sys
25
+
26
+ parser = argparse.ArgumentParser(
27
+ description="AutoBuild - Multi-language automated build tool",
28
+ formatter_class=argparse.RawDescriptionHelpFormatter,
29
+ epilog="""
30
+ Examples:
31
+ %(prog)s pnpm ./src --output ./dist --mirror
32
+ %(prog)s go ./src --output ./bin/app --mirror
33
+ %(prog)s python ./src --output ./dist --mirror --poetry
34
+
35
+ Supported languages:
36
+ pnpm - Frontend JavaScript/TypeScript projects
37
+ go - Go projects with module support
38
+ python - Python projects with pip/poetry/pdm
39
+ """,
40
+ )
41
+
42
+ parser.add_argument(
43
+ "--version",
44
+ action="version",
45
+ version=f"%(prog)s {__import__('multi_lang_build').__version__}",
46
+ )
47
+
48
+ subparsers = parser.add_subparsers(
49
+ dest="language",
50
+ title="language",
51
+ description="Build tool for specific language",
52
+ )
53
+
54
+ pnpm_parser = subparsers.add_parser(
55
+ "pnpm",
56
+ help="Build pnpm-based frontend projects",
57
+ description="Build pnpm-based frontend projects with mirror acceleration",
58
+ )
59
+ pnpm_parser.add_argument("source_dir", type=Path, help="Source directory")
60
+ pnpm_parser.add_argument("-o", "--output", type=Path, required=True, help="Output directory")
61
+ pnpm_parser.add_argument("--mirror/--no-mirror", default=True, help="Enable/disable mirror acceleration")
62
+ pnpm_parser.add_argument("--script", type=str, help="Run specific npm script")
63
+ pnpm_parser.add_argument("--install", action="store_true", help="Install dependencies only")
64
+
65
+ go_parser = subparsers.add_parser(
66
+ "go",
67
+ help="Build Go projects",
68
+ description="Build Go projects with module support and mirror acceleration",
69
+ )
70
+ go_parser.add_argument("source_dir", type=Path, help="Source directory")
71
+ go_parser.add_argument("-o", "--output", type=Path, required=True, help="Output file or directory")
72
+ go_parser.add_argument("--mirror/--no-mirror", default=True, help="Enable/disable Go proxy mirror")
73
+ go_parser.add_argument("--ldflags", type=str, help="Linker flags")
74
+ go_parser.add_argument("--tags", type=str, help="Build tags (comma-separated)")
75
+ go_parser.add_argument("--test", action="store_true", help="Run tests instead of building")
76
+ go_parser.add_argument("--tidy", action="store_true", help="Run go mod tidy")
77
+ go_parser.add_argument("--all", action="store_true", help="Build all packages")
78
+
79
+ python_parser = subparsers.add_parser(
80
+ "python",
81
+ help="Build Python projects",
82
+ description="Build Python projects with pip/poetry/pdm and mirror acceleration",
83
+ )
84
+ python_parser.add_argument("source_dir", type=Path, help="Source directory")
85
+ python_parser.add_argument("-o", "--output", type=Path, required=True, help="Output directory")
86
+ python_parser.add_argument("--mirror/--no-mirror", default=True, help="Enable/disable PyPI mirror")
87
+ python_parser.add_argument("--install", action="store_true", help="Install dependencies only")
88
+ python_parser.add_argument("--dev", action="store_true", help="Include development dependencies")
89
+ python_parser.add_argument("--poetry", action="store_true", help="Force using poetry")
90
+ python_parser.add_argument("--create-venv", type=Path, help="Create virtual environment at path")
91
+
92
+ mirror_parser = subparsers.add_parser(
93
+ "mirror",
94
+ help="Manage mirror configurations",
95
+ description="List or configure mirror settings",
96
+ )
97
+ mirror_subparsers = mirror_parser.add_subparsers(dest="mirror_action")
98
+
99
+ list_parser = mirror_subparsers.add_parser("list", help="List available mirrors")
100
+ list_parser.add_argument("--language", type=str, help="Filter by language")
101
+
102
+ # Register subcommand for IDE integration
103
+ register_parser = subparsers.add_parser(
104
+ "register",
105
+ help="Register as IDE skill (Claude Code, OpenCode, Trae, CodeBuddy)",
106
+ description="Register multi-lang-build as a skill for various AI coding assistants and IDEs",
107
+ epilog="""
108
+ Examples:
109
+ %(prog)s register # Register with Claude Code (default)
110
+ %(prog)s register claude # Register with Claude Code
111
+ %(prog)s register opencode # Register with OpenCode
112
+ %(prog)s register trae # Register with Trae
113
+ %(prog)s register codebuddy # Register with CodeBuddy
114
+ %(prog)s register all # Register with all supported IDEs
115
+ """,
116
+ )
117
+ register_parser.add_argument(
118
+ "ide",
119
+ nargs="?",
120
+ default="claude",
121
+ choices=["claude", "opencode", "trae", "codebuddy", "all"],
122
+ help="IDE to register with (default: claude)",
123
+ )
124
+
125
+ parsed_args = parser.parse_args(args)
126
+
127
+ if parsed_args.language is None:
128
+ parser.print_help()
129
+ sys.exit(1)
130
+
131
+ result: BuildResult | None = None
132
+
133
+ try:
134
+ if parsed_args.language == "pnpm":
135
+ from multi_lang_build.compiler.pnpm import PnpmCompiler
136
+
137
+ compiler = PnpmCompiler()
138
+
139
+ if parsed_args.script:
140
+ result = compiler.run_script(
141
+ parsed_args.source_dir,
142
+ parsed_args.script,
143
+ mirror_enabled=parsed_args.mirror,
144
+ )
145
+ elif parsed_args.install:
146
+ result = compiler.install_dependencies(
147
+ parsed_args.source_dir,
148
+ mirror_enabled=parsed_args.mirror,
149
+ )
150
+ else:
151
+ result = compiler.build(
152
+ parsed_args.source_dir,
153
+ parsed_args.output,
154
+ mirror_enabled=parsed_args.mirror,
155
+ )
156
+
157
+ elif parsed_args.language == "go":
158
+ from multi_lang_build.compiler.go import GoCompiler
159
+
160
+ compiler = GoCompiler()
161
+
162
+ if parsed_args.test:
163
+ result = compiler.run_tests(
164
+ parsed_args.source_dir,
165
+ mirror_enabled=parsed_args.mirror,
166
+ )
167
+ elif parsed_args.tidy:
168
+ result = compiler.tidy_modules(
169
+ parsed_args.source_dir,
170
+ mirror_enabled=parsed_args.mirror,
171
+ )
172
+ elif parsed_args.all:
173
+ result = compiler.build_all(
174
+ parsed_args.source_dir,
175
+ parsed_args.output,
176
+ mirror_enabled=parsed_args.mirror,
177
+ )
178
+ else:
179
+ tags = parsed_args.tags.split(",") if parsed_args.tags else None
180
+ result = compiler.build_binary(
181
+ parsed_args.source_dir,
182
+ parsed_args.output,
183
+ mirror_enabled=parsed_args.mirror,
184
+ ldflags=parsed_args.ldflags,
185
+ tags=tags,
186
+ )
187
+
188
+ elif parsed_args.language == "python":
189
+ from multi_lang_build.compiler.python import PythonCompiler
190
+
191
+ compiler = PythonCompiler()
192
+
193
+ if parsed_args.create_venv:
194
+ result = compiler.create_venv(
195
+ parsed_args.create_venv,
196
+ mirror_enabled=parsed_args.mirror,
197
+ )
198
+ elif parsed_args.install:
199
+ result = compiler.install_dependencies(
200
+ parsed_args.source_dir,
201
+ mirror_enabled=parsed_args.mirror,
202
+ dev=parsed_args.dev,
203
+ poetry=parsed_args.poetry,
204
+ )
205
+ else:
206
+ result = compiler.build(
207
+ parsed_args.source_dir,
208
+ parsed_args.output,
209
+ mirror_enabled=parsed_args.mirror,
210
+ )
211
+
212
+ elif parsed_args.language == "mirror":
213
+ if parsed_args.mirror_action == "list":
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(",")]
219
+
220
+ print("Available mirrors:")
221
+ for mirror_name in sorted(mirrors):
222
+ config = get_mirror_config(mirror_name)
223
+ if config:
224
+ print(f" {mirror_name}: {config['url']}")
225
+ else:
226
+ mirror_parser.print_help()
227
+
228
+ elif parsed_args.language == "register":
229
+ from multi_lang_build.register import register_skill
230
+
231
+ success = register_skill(parsed_args.ide)
232
+ if success:
233
+ print("\n✅ Registration completed successfully!")
234
+ print(f"\nYou can now use 'multi-lang-build' commands in {parsed_args.ide}")
235
+ else:
236
+ print("\n❌ Registration failed for some IDEs.")
237
+ sys.exit(0 if success else 1)
238
+
239
+ else:
240
+ parser.print_help()
241
+ sys.exit(1)
242
+
243
+ if result is not None and result["success"]:
244
+ print(f"Build completed successfully in {result['duration_seconds']:.2f}s")
245
+ if result["output_path"]:
246
+ print(f"Output: {result['output_path']}")
247
+ sys.exit(0)
248
+ elif result is not None:
249
+ print(f"Build failed with code {result['return_code']}")
250
+ if result["stderr"]:
251
+ print(f"Error: {result['stderr']}")
252
+ sys.exit(1)
253
+
254
+ except Exception as e:
255
+ print(f"Error: {e}", file=sys.stderr)
256
+ sys.exit(2)
@@ -0,0 +1,22 @@
1
+ """Compiler module for auto-build tool."""
2
+
3
+ from multi_lang_build.compiler.base import (
4
+ BuildConfig,
5
+ BuildResult,
6
+ CompilerInfo,
7
+ CompilerBase,
8
+ )
9
+
10
+ from multi_lang_build.compiler.pnpm import PnpmCompiler
11
+ from multi_lang_build.compiler.go import GoCompiler
12
+ from multi_lang_build.compiler.python import PythonCompiler
13
+
14
+ __all__ = [
15
+ "BuildConfig",
16
+ "BuildResult",
17
+ "CompilerInfo",
18
+ "CompilerBase",
19
+ "PnpmCompiler",
20
+ "GoCompiler",
21
+ "PythonCompiler",
22
+ ]
@@ -0,0 +1,196 @@
1
+ """Base compiler interface and common types."""
2
+
3
+ from abc import ABC, abstractmethod
4
+ from typing import TypeAlias, TypedDict, NotRequired
5
+ from pathlib import Path
6
+ import time
7
+
8
+
9
+ class BuildConfig(TypedDict, total=False):
10
+ """Build configuration for compiler."""
11
+ source_dir: Path
12
+ output_dir: Path
13
+ environment: dict[str, str]
14
+ mirror_enabled: bool
15
+ mirror_region: str
16
+ extra_args: list[str]
17
+
18
+
19
+ class BuildResult(TypedDict):
20
+ """Result of a build operation."""
21
+ success: bool
22
+ return_code: int
23
+ stdout: str
24
+ stderr: str
25
+ output_path: Path | None
26
+ duration_seconds: float
27
+
28
+
29
+ class CompilerInfo(TypedDict):
30
+ """Information about a compiler."""
31
+ name: str
32
+ version: str
33
+ supported_mirrors: list[str]
34
+ default_mirror: str
35
+ executable: str
36
+
37
+
38
+ class CompilerBase(ABC):
39
+ """Abstract base class for all language compilers."""
40
+
41
+ @property
42
+ @abstractmethod
43
+ def name(self) -> str:
44
+ """Get the compiler name."""
45
+ ...
46
+
47
+ @property
48
+ @abstractmethod
49
+ def version(self) -> str:
50
+ """Get the compiler version."""
51
+ ...
52
+
53
+ @property
54
+ @abstractmethod
55
+ def supported_mirrors(self) -> list[str]:
56
+ """Get list of supported mirror configurations."""
57
+ ...
58
+
59
+ @abstractmethod
60
+ def get_info(self) -> CompilerInfo:
61
+ """Get compiler information."""
62
+ ...
63
+
64
+ @abstractmethod
65
+ def build(
66
+ self,
67
+ source_dir: Path,
68
+ output_dir: Path,
69
+ *,
70
+ environment: dict[str, str] | None = None,
71
+ mirror_enabled: bool = True,
72
+ extra_args: list[str] | None = None,
73
+ ) -> BuildResult:
74
+ """Execute the build process.
75
+
76
+ Args:
77
+ source_dir: Source code directory
78
+ output_dir: Build output directory
79
+ environment: Additional environment variables
80
+ mirror_enabled: Whether to use mirror acceleration
81
+ extra_args: Additional arguments to pass to the build command
82
+
83
+ Returns:
84
+ BuildResult containing success status and output information.
85
+ """
86
+ ...
87
+
88
+ @abstractmethod
89
+ def clean(self, directory: Path) -> bool:
90
+ """Clean build artifacts in the specified directory.
91
+
92
+ Args:
93
+ directory: Directory to clean
94
+
95
+ Returns:
96
+ True if successful, False otherwise.
97
+ """
98
+ ...
99
+
100
+ def _run_build(
101
+ self,
102
+ command: list[str],
103
+ source_dir: Path,
104
+ output_dir: Path,
105
+ *,
106
+ environment: dict[str, str] | None = None,
107
+ extra_args: list[str] | None = None,
108
+ ) -> BuildResult:
109
+ """Execute a build command with timing and error handling.
110
+
111
+ Args:
112
+ command: Build command to execute
113
+ source_dir: Source directory for the build
114
+ output_dir: Output directory for build artifacts
115
+ environment: Additional environment variables
116
+ extra_args: Additional arguments to append to command
117
+
118
+ Returns:
119
+ BuildResult with success status and output information.
120
+ """
121
+ import subprocess
122
+
123
+ full_command = command.copy()
124
+ if extra_args:
125
+ full_command.extend(extra_args)
126
+
127
+ env = environment.copy() if environment else {}
128
+ env.setdefault("PATH", "")
129
+
130
+ start_time = time.perf_counter()
131
+
132
+ try:
133
+ result = subprocess.run(
134
+ full_command,
135
+ cwd=source_dir,
136
+ capture_output=True,
137
+ text=True,
138
+ env=env,
139
+ timeout=3600, # 1 hour timeout
140
+ )
141
+
142
+ duration = time.perf_counter() - start_time
143
+
144
+ return BuildResult(
145
+ success=result.returncode == 0,
146
+ return_code=result.returncode,
147
+ stdout=result.stdout,
148
+ stderr=result.stderr,
149
+ output_path=output_dir if result.returncode == 0 else None,
150
+ duration_seconds=duration,
151
+ )
152
+
153
+ except subprocess.TimeoutExpired:
154
+ duration = time.perf_counter() - start_time
155
+ return BuildResult(
156
+ success=False,
157
+ return_code=-1,
158
+ stdout="",
159
+ stderr="Build timed out after 1 hour",
160
+ output_path=None,
161
+ duration_seconds=duration,
162
+ )
163
+ except Exception as e:
164
+ duration = time.perf_counter() - start_time
165
+ return BuildResult(
166
+ success=False,
167
+ return_code=-2,
168
+ stdout="",
169
+ stderr=f"Build error: {str(e)}",
170
+ output_path=None,
171
+ duration_seconds=duration,
172
+ )
173
+
174
+ def _validate_directory(self, directory: Path, create_if_not_exists: bool = True) -> Path:
175
+ """Validate and optionally create a directory.
176
+
177
+ Args:
178
+ directory: Directory path to validate
179
+ create_if_not_exists: Create directory if it doesn't exist
180
+
181
+ Returns:
182
+ Resolved absolute path
183
+
184
+ Raises:
185
+ ValueError: If directory cannot be created or is not a directory
186
+ """
187
+ if not directory.exists():
188
+ if create_if_not_exists:
189
+ directory.mkdir(parents=True, exist_ok=True)
190
+ else:
191
+ raise ValueError(f"Directory does not exist: {directory}")
192
+
193
+ if not directory.is_dir():
194
+ raise ValueError(f"Path is not a directory: {directory}")
195
+
196
+ return directory.resolve()