Some checks are pending
CI / Test (Python 3.10 on macos-latest) (push) Waiting to run
CI / Test (Python 3.11 on macos-latest) (push) Waiting to run
CI / Test (Python 3.12 on macos-latest) (push) Waiting to run
CI / Test (Python 3.8 on macos-latest) (push) Waiting to run
CI / Test (Python 3.9 on macos-latest) (push) Waiting to run
CI / Test (Python 3.10 on ubuntu-latest) (push) Waiting to run
CI / Test (Python 3.11 on ubuntu-latest) (push) Waiting to run
CI / Test (Python 3.12 on ubuntu-latest) (push) Waiting to run
CI / Test (Python 3.8 on ubuntu-latest) (push) Waiting to run
CI / Test (Python 3.9 on ubuntu-latest) (push) Waiting to run
CI / Test (Python 3.10 on windows-latest) (push) Waiting to run
CI / Test (Python 3.11 on windows-latest) (push) Waiting to run
CI / Test (Python 3.12 on windows-latest) (push) Waiting to run
CI / Test (Python 3.8 on windows-latest) (push) Waiting to run
CI / Test (Python 3.9 on windows-latest) (push) Waiting to run
CI / Lint (push) Waiting to run
CI / Release (push) Blocked by required conditions
Documentation / Build Documentation (push) Waiting to run
829 lines
25 KiB
Python
829 lines
25 KiB
Python
# sikuwa/cli.py
|
||
"""
|
||
Sikuwa 命令行界面
|
||
"""
|
||
|
||
import click
|
||
import sys
|
||
from pathlib import Path
|
||
from typing import Optional
|
||
|
||
from sikuwa.config import ConfigManager, BuildConfig, create_config
|
||
from sikuwa.builder import SikuwaBuilder, build_project, clean_project, sync_project
|
||
from sikuwa.log import get_logger, LogLevel
|
||
from sikuwa.i18n import _
|
||
|
||
|
||
# 全局日志器
|
||
logger = get_logger("sikuwa.cli")
|
||
|
||
|
||
@click.group()
|
||
@click.version_option(version="1.2.0", prog_name="sikuwa")
|
||
def cli():
|
||
"""
|
||
Sikuwa - Python 项目打包工具
|
||
|
||
基于 Nuitka 的跨平台 Python 应用构建工具
|
||
"""
|
||
pass
|
||
|
||
|
||
@cli.command()
|
||
@click.option(
|
||
'-c', '--config',
|
||
type=click.Path(exists=True),
|
||
help='配置文件路径 (默认: sikuwa.toml)'
|
||
)
|
||
@click.option(
|
||
'-p', '--platform',
|
||
type=click.Choice(['windows', 'linux', 'macos'], case_sensitive=False),
|
||
help='目标平台 (默认: 构建所有平台)'
|
||
)
|
||
@click.option(
|
||
'-m', '--mode',
|
||
type=click.Choice(['nuitka', 'native'], case_sensitive=False),
|
||
help='编译模式: nuitka (默认) | native (Python→C/C++→GCC→dll/so+exe)'
|
||
)
|
||
@click.option(
|
||
'-v', '--verbose',
|
||
is_flag=True,
|
||
help=_('详细输出模式')
|
||
)
|
||
@click.option(
|
||
'-f', '--force',
|
||
is_flag=True,
|
||
help=_('强制重新构建')
|
||
)
|
||
@click.option(
|
||
'--keep-c-source',
|
||
is_flag=True,
|
||
help='保留生成的 C/C++ 源码 (仅 native 模式)'
|
||
)
|
||
def build(config: Optional[str], platform: Optional[str], mode: Optional[str],
|
||
verbose: bool, force: bool, keep_c_source: bool):
|
||
"""
|
||
构建项目
|
||
|
||
支持两种编译模式:
|
||
|
||
\b
|
||
1. nuitka (默认): 使用 Nuitka 编译器
|
||
2. native: Python → C/C++ → GCC/G++ → dll/so + exe
|
||
生成通用动态链接库,不使用 Python 专用格式 (.pyd)
|
||
|
||
示例:
|
||
|
||
sikuwa build # 使用 Nuitka 构建
|
||
|
||
sikuwa build -m native # 使用原生编译器构建
|
||
|
||
sikuwa build -m native -v # 原生编译 + 详细输出
|
||
|
||
sikuwa build -p windows # 只构建 Windows 平台
|
||
|
||
sikuwa build -c my_config.toml # 使用指定配置文件
|
||
"""
|
||
try:
|
||
# 加载配置
|
||
logger.info_operation("加载构建配置...")
|
||
build_config = ConfigManager.load_config(config)
|
||
|
||
# 如果命令行指定了编译模式,覆盖配置文件中的设置
|
||
if mode:
|
||
build_config.compiler_mode = mode
|
||
if mode == 'native' and keep_c_source:
|
||
build_config.native_options.keep_c_source = True
|
||
|
||
# 验证配置
|
||
logger.info_operation("验证配置...")
|
||
build_config.validate()
|
||
|
||
# 执行构建
|
||
logger.info_operation(f"开始构建项目: {build_config.project_name}")
|
||
logger.info_operation(f"编译模式: {build_config.compiler_mode.upper()}")
|
||
|
||
success = build_project(
|
||
config=build_config,
|
||
platform=platform,
|
||
verbose=verbose,
|
||
force=force
|
||
)
|
||
|
||
if success:
|
||
click.echo("\n[OK] 构建成功完成!", err=False)
|
||
sys.exit(0)
|
||
else:
|
||
click.echo("\n[FAIL] 构建失败!", err=True)
|
||
sys.exit(1)
|
||
|
||
except FileNotFoundError as e:
|
||
click.echo(f"[FAIL] {e}", err=True)
|
||
click.echo("\n提示: 使用 'sikuwa init' 创建配置文件", err=True)
|
||
sys.exit(1)
|
||
except Exception as e:
|
||
click.echo(f"[FAIL] 构建失败: {e}", err=True)
|
||
if verbose:
|
||
import traceback
|
||
click.echo(traceback.format_exc(), err=True)
|
||
sys.exit(1)
|
||
|
||
|
||
@cli.command()
|
||
@click.option(
|
||
'-c', '--config',
|
||
type=click.Path(exists=True),
|
||
help='配置文件路径 (默认: sikuwa.toml)'
|
||
)
|
||
@click.option(
|
||
'-v', '--verbose',
|
||
is_flag=True,
|
||
help=_('详细输出模式')
|
||
)
|
||
def clean(config: Optional[str], verbose: bool):
|
||
"""
|
||
清理构建文件
|
||
|
||
删除构建过程中生成的所有文件和目录
|
||
|
||
示例:
|
||
|
||
sikuwa clean # 清理构建文件
|
||
|
||
sikuwa clean -v # 详细输出
|
||
"""
|
||
try:
|
||
# 加载配置
|
||
logger.info_operation("加载配置...")
|
||
build_config = ConfigManager.load_config(config)
|
||
|
||
# 执行清理
|
||
logger.info_operation("开始清理构建文件...")
|
||
|
||
success = clean_project(
|
||
config=build_config,
|
||
verbose=verbose
|
||
)
|
||
|
||
if success:
|
||
click.echo("\n[OK] 清理完成!", err=False)
|
||
sys.exit(0)
|
||
else:
|
||
click.echo("\n[FAIL] 清理失败!", err=True)
|
||
sys.exit(1)
|
||
|
||
except FileNotFoundError as e:
|
||
click.echo(f"[FAIL] {e}", err=True)
|
||
sys.exit(1)
|
||
except Exception as e:
|
||
click.echo(f"[FAIL] 清理失败: {e}", err=True)
|
||
if verbose:
|
||
import traceback
|
||
click.echo(traceback.format_exc(), err=True)
|
||
sys.exit(1)
|
||
|
||
|
||
@cli.command()
|
||
@click.option(
|
||
'-o', '--output',
|
||
default='sikuwa.toml',
|
||
help='输出配置文件名 (默认: sikuwa.toml)'
|
||
)
|
||
@click.option(
|
||
'--force',
|
||
is_flag=True,
|
||
help=_('覆盖已存在的文件')
|
||
)
|
||
def init(output: str, force: bool):
|
||
"""
|
||
初始化项目配置
|
||
|
||
创建默认的 sikuwa.toml 配置文件
|
||
|
||
示例:
|
||
|
||
sikuwa init # 创建 sikuwa.toml
|
||
|
||
sikuwa init -o custom.toml # 创建自定义配置文件
|
||
|
||
sikuwa init --force # 强制覆盖已存在的文件
|
||
"""
|
||
try:
|
||
output_path = Path(output)
|
||
|
||
# 检查文件是否已存在
|
||
if output_path.exists() and not force:
|
||
click.echo(f"[WARN] 配置文件已存在: {output}", err=True)
|
||
click.echo("使用 --force 选项强制覆盖", err=True)
|
||
sys.exit(1)
|
||
|
||
# 创建配置文件
|
||
logger.info_operation(f"创建配置文件: {output}")
|
||
create_config(output)
|
||
|
||
click.echo(f"\n[OK] 配置文件已创建: {output}")
|
||
click.echo("\n下一步:")
|
||
click.echo(" 1. 编辑 sikuwa.toml,配置项目信息")
|
||
click.echo(" 2. 运行 'sikuwa build' 开始构建")
|
||
|
||
sys.exit(0)
|
||
|
||
except Exception as e:
|
||
click.echo(f"[FAIL] 创建配置文件失败: {e}", err=True)
|
||
sys.exit(1)
|
||
|
||
|
||
@cli.command()
|
||
@click.option(
|
||
'-c', '--config',
|
||
type=click.Path(exists=True),
|
||
help='配置文件路径 (默认: sikuwa.toml)'
|
||
)
|
||
def info(config: Optional[str]):
|
||
"""
|
||
显示项目信息
|
||
|
||
显示当前项目的配置信息
|
||
|
||
示例:
|
||
|
||
sikuwa info # 显示项目信息
|
||
|
||
sikuwa info -c custom.toml # 显示指定配置文件的信息
|
||
"""
|
||
try:
|
||
# 加载配置
|
||
build_config = ConfigManager.load_config(config)
|
||
|
||
# 显示项目信息
|
||
click.echo("\n" + "=" * 70)
|
||
click.echo(f"项目信息: {build_config.project_name}")
|
||
click.echo("=" * 70)
|
||
|
||
click.echo(f"\n基础信息:")
|
||
click.echo(f" 项目名称: {build_config.project_name}")
|
||
click.echo(f" 版本: {build_config.version}")
|
||
if build_config.description:
|
||
click.echo(f" 描述: {build_config.description}")
|
||
if build_config.author:
|
||
click.echo(f" 作者: {build_config.author}")
|
||
|
||
click.echo(f"\n构建配置:")
|
||
click.echo(f" 入口文件: {build_config.main_script}")
|
||
click.echo(f" 源代码目录: {build_config.src_dir}")
|
||
click.echo(f" 输出目录: {build_config.output_dir}")
|
||
click.echo(f" 构建目录: {build_config.build_dir}")
|
||
click.echo(f" 目标平台: {', '.join(build_config.platforms)}")
|
||
|
||
click.echo(f"\nNuitka 选项:")
|
||
click.echo(f" Standalone: {build_config.nuitka_options.standalone}")
|
||
click.echo(f" OneFile: {build_config.nuitka_options.onefile}")
|
||
click.echo(f" Follow Imports: {build_config.nuitka_options.follow_imports}")
|
||
click.echo(f" Show Progress: {build_config.nuitka_options.show_progress}")
|
||
click.echo(f" Enable Console: {build_config.nuitka_options.enable_console}")
|
||
|
||
if build_config.nuitka_options.include_packages:
|
||
click.echo(f"\n包含的包:")
|
||
for pkg in build_config.nuitka_options.include_packages:
|
||
click.echo(f" - {pkg}")
|
||
|
||
if build_config.resources:
|
||
click.echo(f"\n资源文件:")
|
||
for resource in build_config.resources:
|
||
click.echo(f" - {resource}")
|
||
|
||
click.echo("\n" + "=" * 70 + "\n")
|
||
|
||
except FileNotFoundError as e:
|
||
click.echo(f"[FAIL] {e}", err=True)
|
||
sys.exit(1)
|
||
except Exception as e:
|
||
click.echo(f"[FAIL] 读取配置失败: {e}", err=True)
|
||
sys.exit(1)
|
||
|
||
|
||
@cli.command()
|
||
def version():
|
||
"""
|
||
显示版本信息
|
||
"""
|
||
click.echo("\nSikuwa v1.2.0")
|
||
click.echo("Python 项目打包工具")
|
||
click.echo("基于 Nuitka 的跨平台构建系统")
|
||
click.echo("\nGitHub: https://github.com/FORGE24/Sikuwa/")
|
||
click.echo("文档: https://www.sanrol-cloud.top\n")
|
||
|
||
|
||
@cli.command()
|
||
@click.option(
|
||
'-c', '--config',
|
||
type=click.Path(exists=True),
|
||
help='配置文件路径 (默认: sikuwa.toml)'
|
||
)
|
||
def validate(config: Optional[str]):
|
||
"""
|
||
验证配置文件
|
||
|
||
检查配置文件是否正确
|
||
|
||
示例:
|
||
|
||
sikuwa validate # 验证默认配置文件
|
||
|
||
sikuwa validate -c custom.toml # 验证指定配置文件
|
||
"""
|
||
try:
|
||
# 加载配置
|
||
logger.info_operation("加载配置文件...")
|
||
build_config = ConfigManager.load_config(config)
|
||
|
||
# 验证配置
|
||
logger.info_operation("验证配置...")
|
||
build_config.validate()
|
||
|
||
click.echo("\n[OK] 配置文件有效!")
|
||
|
||
# 显示摘要信息
|
||
click.echo(f"\n项目: {build_config.project_name}")
|
||
click.echo(f"版本: {build_config.version}")
|
||
click.echo(f"入口: {build_config.main_script}")
|
||
click.echo(f"平台: {', '.join(build_config.platforms)}\n")
|
||
|
||
sys.exit(0)
|
||
|
||
except FileNotFoundError as e:
|
||
click.echo(f"[FAIL] {e}", err=True)
|
||
sys.exit(1)
|
||
except ValueError as e:
|
||
click.echo(f"[FAIL] 配置验证失败: {e}", err=True)
|
||
sys.exit(1)
|
||
except Exception as e:
|
||
click.echo(f"[FAIL] 验证失败: {e}", err=True)
|
||
sys.exit(1)
|
||
|
||
|
||
@cli.command()
|
||
def doctor():
|
||
"""
|
||
检查构建环境
|
||
|
||
检查系统环境和依赖项是否满足构建要求
|
||
"""
|
||
import subprocess
|
||
import platform
|
||
|
||
click.echo("\n" + "=" * 70)
|
||
click.echo("Sikuwa 环境诊断")
|
||
click.echo("=" * 70)
|
||
|
||
# 检查 Python 版本
|
||
click.echo("\n[1] Python 环境")
|
||
python_version = sys.version_info
|
||
click.echo(f" 版本: {python_version.major}.{python_version.minor}.{python_version.micro}")
|
||
click.echo(f" 路径: {sys.executable}")
|
||
|
||
if python_version.major < 3 or (python_version.major == 3 and python_version.minor < 7):
|
||
click.echo(" [FAIL] Python 版本过低,需要 3.7+", err=True)
|
||
else:
|
||
click.echo(" [OK] Python 版本满足要求")
|
||
|
||
# 检查操作系统
|
||
click.echo("\n[2] 操作系统")
|
||
os_name = platform.system()
|
||
os_version = platform.version()
|
||
click.echo(f" 系统: {os_name}")
|
||
click.echo(f" 版本: {os_version}")
|
||
click.echo(f" 架构: {platform.machine()}")
|
||
|
||
# 检查 Nuitka
|
||
click.echo("\n[3] Nuitka")
|
||
try:
|
||
result = subprocess.run(
|
||
["nuitka3", "--version"],
|
||
capture_output=True,
|
||
text=True,
|
||
timeout=5
|
||
)
|
||
if result.returncode == 0:
|
||
version = result.stdout.strip().split('\n')[0]
|
||
click.echo(f" [OK] 已安装: {version}")
|
||
else:
|
||
click.echo(" [FAIL] Nuitka 未正确安装", err=True)
|
||
except FileNotFoundError:
|
||
click.echo(" [FAIL] Nuitka 未安装", err=True)
|
||
click.echo(" 安装命令: pip install nuitka")
|
||
except Exception as e:
|
||
click.echo(f" [WARN] 检查 Nuitka 时出错: {e}", err=True)
|
||
|
||
# 检查编译器 (Windows)
|
||
if os_name == "Windows":
|
||
click.echo("\n[4] C 编译器 (Windows)")
|
||
|
||
# 检查 MinGW
|
||
try:
|
||
result = subprocess.run(
|
||
["gcc", "--version"],
|
||
capture_output=True,
|
||
text=True,
|
||
timeout=5
|
||
)
|
||
if result.returncode == 0:
|
||
version = result.stdout.strip().split('\n')[0]
|
||
click.echo(f" [OK] GCC 已安装: {version}")
|
||
else:
|
||
click.echo(" [WARN] GCC 未找到", err=True)
|
||
except FileNotFoundError:
|
||
click.echo(" [WARN] GCC 未安装", err=True)
|
||
click.echo(" 推荐安装 MinGW-w64 或 MSVC")
|
||
except Exception as e:
|
||
click.echo(f" [WARN] 检查 GCC 时出错: {e}", err=True)
|
||
|
||
# 检查 MSVC
|
||
try:
|
||
result = subprocess.run(
|
||
["cl"],
|
||
capture_output=True,
|
||
text=True,
|
||
timeout=5
|
||
)
|
||
if "Microsoft" in result.stderr or "Microsoft" in result.stdout:
|
||
click.echo(" [OK] MSVC 已安装")
|
||
else:
|
||
click.echo(" [INFO] MSVC 未找到")
|
||
except FileNotFoundError:
|
||
click.echo(" [INFO] MSVC 未安装")
|
||
except Exception as e:
|
||
click.echo(f" [INFO] 检查 MSVC 时出错: {e}")
|
||
|
||
# 检查编译器 (Linux/macOS)
|
||
elif os_name in ["Linux", "Darwin"]:
|
||
click.echo(f"\n[4] C 编译器 ({os_name})")
|
||
|
||
try:
|
||
result = subprocess.run(
|
||
["gcc", "--version"],
|
||
capture_output=True,
|
||
text=True,
|
||
timeout=5
|
||
)
|
||
if result.returncode == 0:
|
||
version = result.stdout.strip().split('\n')[0]
|
||
click.echo(f" [OK] GCC 已安装: {version}")
|
||
else:
|
||
click.echo(" [FAIL] GCC 未找到", err=True)
|
||
except FileNotFoundError:
|
||
click.echo(" [FAIL] GCC 未安装", err=True)
|
||
if os_name == "Linux":
|
||
click.echo(" 安装命令: sudo apt install gcc # Debian/Ubuntu")
|
||
click.echo(" sudo yum install gcc # RedHat/CentOS")
|
||
elif os_name == "Darwin":
|
||
click.echo(" 安装命令: xcode-select --install")
|
||
except Exception as e:
|
||
click.echo(f" [WARN] 检查 GCC 时出错: {e}", err=True)
|
||
|
||
# 检查必需的 Python 包
|
||
click.echo("\n[5] Python 依赖包")
|
||
required_packages = {
|
||
'click': 'CLI 框架',
|
||
'tomli': 'TOML 解析器',
|
||
'tomli_w': 'TOML 写入器',
|
||
'nuitka': 'Python 编译器'
|
||
}
|
||
|
||
for package, description in required_packages.items():
|
||
try:
|
||
__import__(package)
|
||
click.echo(f" [OK] {package:15s} - {description}")
|
||
except ImportError:
|
||
click.echo(f" [FAIL] {package:15s} - {description} (未安装)", err=True)
|
||
|
||
# 检查可选包
|
||
click.echo("\n[6] 可选依赖包")
|
||
optional_packages = {
|
||
'ordered_set': _('有序集合支持'),
|
||
'zstandard': 'Zstandard 压缩',
|
||
}
|
||
|
||
for package, description in optional_packages.items():
|
||
try:
|
||
__import__(package)
|
||
click.echo(f" [OK] {package:15s} - {description}")
|
||
except ImportError:
|
||
click.echo(f" [INFO] {package:15s} - {description} (未安装)")
|
||
|
||
# 总结
|
||
click.echo("\n" + "=" * 70)
|
||
click.echo(_("诊断完成"))
|
||
click.echo("=" * 70)
|
||
click.echo("\n如果有 [FAIL] 项,请先解决这些问题后再进行构建。\n")
|
||
|
||
|
||
@cli.command()
|
||
@click.argument('query', required=False)
|
||
def help_cmd(query: Optional[str]):
|
||
"""
|
||
显示帮助信息
|
||
|
||
示例:
|
||
|
||
sikuwa help # 显示总体帮助
|
||
|
||
sikuwa help build # 显示 build 命令帮助
|
||
|
||
sikuwa help config # 显示配置文件帮助
|
||
"""
|
||
if not query:
|
||
# 显示总体帮助
|
||
click.echo("\n" + "=" * 70)
|
||
click.echo("Sikuwa - Python 项目打包工具")
|
||
click.echo("=" * 70)
|
||
|
||
click.echo("\n常用命令:")
|
||
click.echo(" sikuwa init 创建配置文件")
|
||
click.echo(" sikuwa build 构建项目")
|
||
click.echo(" sikuwa clean 清理构建文件")
|
||
click.echo(" sikuwa sync 同步项目依赖")
|
||
click.echo(" sikuwa info 显示项目信息")
|
||
click.echo(" sikuwa doctor 检查构建环境")
|
||
|
||
click.echo("\n获取更多帮助:")
|
||
click.echo(" sikuwa --help 显示所有命令")
|
||
click.echo(" sikuwa <command> --help 显示命令详细帮助")
|
||
click.echo(" sikuwa help config 配置文件帮助")
|
||
|
||
click.echo("\n快速开始:")
|
||
click.echo(" 1. sikuwa init # 创建配置文件")
|
||
click.echo(" 2. 编辑 sikuwa.toml # 配置项目")
|
||
click.echo(" 3. sikuwa sync # 同步项目依赖")
|
||
click.echo(" 4. sikuwa build # 构建项目")
|
||
|
||
click.echo("\n文档: https://www.sanrol-cloud.top")
|
||
click.echo("=" * 70 + "\n")
|
||
|
||
elif query.lower() == "config":
|
||
# 配置文件帮助
|
||
click.echo("\n" + "=" * 70)
|
||
click.echo("Sikuwa 配置文件说明")
|
||
click.echo("=" * 70)
|
||
|
||
click.echo("\n配置文件示例 (sikuwa.toml):\n")
|
||
click.echo("""[sikuwa]
|
||
project_name = "my_app"
|
||
version = "1.0.0"
|
||
description = "My Application"
|
||
author = "Your Name"
|
||
|
||
main_script = "main.py"
|
||
src_dir = "."
|
||
output_dir = "dist"
|
||
build_dir = "build"
|
||
|
||
platforms = ["windows", "linux"]
|
||
resources = ["config.json", "data/"]
|
||
|
||
[sikuwa.nuitka]
|
||
standalone = true
|
||
onefile = false
|
||
follow_imports = true
|
||
show_progress = true
|
||
enable_console = true
|
||
optimize = true
|
||
|
||
include_packages = ["requests", "numpy"]
|
||
include_modules = []
|
||
include_data_files = []
|
||
include_data_dirs = []
|
||
|
||
windows_icon = "icon.ico"
|
||
windows_company_name = "My Company"
|
||
windows_product_name = "My Product"
|
||
""")
|
||
|
||
click.echo("\n主要配置项:")
|
||
click.echo(" project_name 项目名称")
|
||
click.echo(" main_script 入口文件")
|
||
click.echo(" platforms 目标平台 (windows/linux/macos)")
|
||
click.echo(" standalone 独立模式")
|
||
click.echo(" onefile 单文件模式")
|
||
click.echo(" include_packages 包含的 Python 包")
|
||
|
||
click.echo("\n详细文档: https://www.sanrol-cloud.top")
|
||
click.echo("=" * 70 + "\n")
|
||
|
||
else:
|
||
# 显示特定命令的帮助
|
||
ctx = click.Context(cli)
|
||
cmd = cli.get_command(ctx, query)
|
||
if cmd:
|
||
click.echo(cmd.get_help(ctx))
|
||
else:
|
||
click.echo(f"[FAIL] 未知命令: {query}", err=True)
|
||
click.echo("使用 'sikuwa --help' 查看所有可用命令", err=True)
|
||
|
||
|
||
@cli.command()
|
||
@click.option(
|
||
'-c', '--config',
|
||
type=click.Path(exists=True),
|
||
help='配置文件路径 (默认: sikuwa.toml)'
|
||
)
|
||
@click.option(
|
||
'--format',
|
||
type=click.Choice(['text', 'json'], case_sensitive=False),
|
||
default='text',
|
||
help='输出格式 (默认: text)'
|
||
)
|
||
def show_config(config: Optional[str], format: str):
|
||
"""
|
||
显示完整配置
|
||
|
||
以易读的格式显示所有配置选项
|
||
|
||
示例:
|
||
|
||
sikuwa show-config # 显示配置 (文本格式)
|
||
|
||
sikuwa show-config --format json # 显示配置 (JSON 格式)
|
||
"""
|
||
try:
|
||
# 加载配置
|
||
build_config = ConfigManager.load_config(config)
|
||
|
||
if format == 'json':
|
||
# JSON 格式输出
|
||
import json
|
||
config_dict = build_config.to_dict()
|
||
click.echo(json.dumps(config_dict, indent=2, ensure_ascii=False))
|
||
else:
|
||
# 文本格式输出
|
||
config_dict = build_config.to_dict()
|
||
|
||
click.echo("\n" + "=" * 70)
|
||
click.echo(_("完整配置"))
|
||
click.echo("=" * 70)
|
||
|
||
def print_dict(d, indent=0):
|
||
for key, value in d.items():
|
||
if isinstance(value, dict):
|
||
click.echo(" " * indent + f"{key}:")
|
||
print_dict(value, indent + 1)
|
||
elif isinstance(value, list):
|
||
click.echo(" " * indent + f"{key}:")
|
||
for item in value:
|
||
click.echo(" " * (indent + 1) + f"- {item}")
|
||
else:
|
||
click.echo(" " * indent + f"{key}: {value}")
|
||
|
||
print_dict(config_dict)
|
||
click.echo("=" * 70 + "\n")
|
||
|
||
sys.exit(0)
|
||
|
||
except FileNotFoundError as e:
|
||
click.echo(f"[FAIL] {e}", err=True)
|
||
sys.exit(1)
|
||
except Exception as e:
|
||
click.echo(f"[FAIL] 读取配置失败: {e}", err=True)
|
||
sys.exit(1)
|
||
|
||
|
||
@cli.command()
|
||
@click.option(
|
||
'-c', '--config',
|
||
type=click.Path(exists=True),
|
||
help='配置文件路径 (默认: sikuwa.toml)'
|
||
)
|
||
@click.option(
|
||
'-v', '--verbose',
|
||
is_flag=True,
|
||
help=_('详细输出模式')
|
||
)
|
||
def sync(config: Optional[str], verbose: bool):
|
||
"""
|
||
同步项目依赖
|
||
|
||
自动创建或进入虚拟环境,并安装配置文件中指定的依赖包
|
||
|
||
示例:
|
||
|
||
sikuwa sync # 同步依赖
|
||
|
||
sikuwa sync -v # 详细输出
|
||
|
||
sikuwa sync -c my_config.toml # 使用指定配置文件
|
||
"""
|
||
try:
|
||
from sikuwa.builder import sync_project
|
||
|
||
# 加载配置
|
||
logger.info_operation("加载构建配置...")
|
||
build_config = ConfigManager.load_config(config)
|
||
|
||
# 验证配置
|
||
logger.info_operation("验证配置...")
|
||
build_config.validate()
|
||
|
||
# 执行同步
|
||
logger.info_operation(f"开始同步项目依赖: {build_config.project_name}")
|
||
|
||
success = sync_project(
|
||
config=build_config,
|
||
verbose=verbose
|
||
)
|
||
|
||
if success:
|
||
click.echo("\n[OK] 依赖同步成功完成!", err=False)
|
||
sys.exit(0)
|
||
else:
|
||
click.echo("\n[FAIL] 依赖同步失败!", err=True)
|
||
sys.exit(1)
|
||
|
||
except FileNotFoundError as e:
|
||
click.echo(f"[FAIL] {e}", err=True)
|
||
click.echo("\n提示: 使用 'sikuwa init' 创建配置文件", err=True)
|
||
sys.exit(1)
|
||
except Exception as e:
|
||
click.echo(f"[FAIL] 同步失败: {e}", err=True)
|
||
if verbose:
|
||
import traceback
|
||
click.echo(traceback.format_exc(), err=True)
|
||
sys.exit(1)
|
||
|
||
|
||
@cli.command()
|
||
@click.option(
|
||
'-c', '--config',
|
||
type=click.Path(exists=True),
|
||
help='配置文件路径 (默认: sikuwa.toml)'
|
||
)
|
||
@click.option(
|
||
'-v', '--verbose',
|
||
is_flag=True,
|
||
help='详细输出模式'
|
||
)
|
||
def build_sequence(config: Optional[str], verbose: bool):
|
||
"""
|
||
执行编译序列构建
|
||
|
||
支持按配置文件中定义的项目依赖关系进行拓扑排序,并可选择并行构建
|
||
|
||
示例:
|
||
|
||
sikuwa build-sequence # 执行编译序列构建
|
||
|
||
sikuwa build-sequence -v # 详细输出
|
||
|
||
sikuwa build-sequence -c my_config.toml # 使用指定配置文件
|
||
"""
|
||
try:
|
||
from sikuwa.builder import build_sequence
|
||
|
||
# 加载配置
|
||
logger.info_operation("加载构建配置...")
|
||
build_config = ConfigManager.load_config(config)
|
||
|
||
# 验证配置
|
||
logger.info_operation("验证配置...")
|
||
build_config.validate()
|
||
|
||
# 执行编译序列构建
|
||
success = build_sequence(
|
||
config=build_config,
|
||
verbose=verbose
|
||
)
|
||
|
||
if success:
|
||
click.echo("\n[OK] 编译序列构建成功完成!", err=False)
|
||
sys.exit(0)
|
||
else:
|
||
click.echo("\n[FAIL] 编译序列构建失败!", err=True)
|
||
sys.exit(1)
|
||
|
||
except FileNotFoundError as e:
|
||
click.echo(f"[FAIL] {e}", err=True)
|
||
click.echo("\n提示: 使用 'sikuwa init' 创建配置文件", err=True)
|
||
sys.exit(1)
|
||
except Exception as e:
|
||
click.echo(f"[FAIL] 构建失败: {e}", err=True)
|
||
if verbose:
|
||
import traceback
|
||
click.echo(traceback.format_exc(), err=True)
|
||
sys.exit(1)
|
||
|
||
|
||
def main():
|
||
"""主入口函数"""
|
||
try:
|
||
cli()
|
||
except KeyboardInterrupt:
|
||
click.echo("\n\n[WARN] 用户中断操作", err=True)
|
||
sys.exit(130)
|
||
except Exception as e:
|
||
click.echo(f"\n[FAIL] 未预期的错误: {e}", err=True)
|
||
import traceback
|
||
click.echo(traceback.format_exc(), err=True)
|
||
sys.exit(1)
|
||
|
||
|
||
if __name__ == '__main__':
|
||
main() |