1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
// SPDX-License-Identifier: MIT OR Apache-2.0
mod cmd_check;
mod cmd_edit;
mod cmd_export;
mod cmd_new;
mod cmd_run;
mod cmd_toolchain;
mod cmd_vendor;
use anyhow::Result;
use clap::{CommandFactory, Parser, Subcommand};
#[derive(Debug, Parser)]
#[command(name = "pite", version, about = "Pite game engine CLI")]
struct Cli {
#[command(subcommand)]
command: Option<Command>,
}
#[derive(Debug, Subcommand)]
enum Command {
/// Scaffold a new game project from a template (or blank).
New {
/// Name of the new project directory.
name: String,
/// Project template to scaffold (available: "minimal-2d", or omit for blank).
#[arg(long, value_parser = ["minimal-2d"])]
template: Option<String>,
},
/// Run a scene in the standalone engine runtime.
Run {
/// Scene file to run (res:// path or filesystem path); defaults to the main scene.
#[arg(value_name = "SCENE")]
scene: Option<String>,
/// Disable hot reloading of scripts and assets.
#[arg(long, default_value_t = false)]
no_reload: bool,
},
/// Validate project manifests, scene files, assets, and Python scripts.
Check {
/// Path to the project root directory; defaults to current directory.
#[arg(value_name = "PATH")]
path: Option<String>,
/// Re-scan assets, reconcile UID registry, and update references before checking.
#[arg(long, default_value_t = false)]
reimport_assets: bool,
/// Treat warnings as errors and exit with a non-zero status.
#[arg(long, default_value_t = false)]
strict: bool,
/// Output the check report formatted as JSON.
#[arg(long, default_value_t = false)]
json: bool,
},
/// Launch the interactive graphical scene and script editor.
Edit {
/// Scene file to open initially; defaults to the project's main scene.
#[arg(value_name = "SCENE")]
scene: Option<String>,
},
/// Export a playable build with runtime and resolved game content.
Export {
/// Target platform.
#[arg(value_parser = ["linux", "windows", "macos"])]
platform: String,
/// Path to the project root directory; defaults to current directory.
#[arg(long)]
path: Option<String>,
/// Output directory for the exported build.
#[arg(long)]
out: Option<String>,
/// Path to a custom player binary to ship in the export.
#[arg(long)]
binary: Option<String>,
/// Skip the host Python version verification check.
#[arg(long, default_value_t = false)]
skip_python_check: bool,
/// Keep the system-Python requirement instead of bundling a pinned CPython runtime.
#[arg(long, default_value_t = false)]
no_bundle_python: bool,
},
/// Manage engine toolchains (CPython runtimes and export players).
Toolchain {
#[command(subcommand)]
action: cmd_toolchain::ToolchainAction,
},
/// Manage third-party vendored Python packages.
Vendor {
#[command(subcommand)]
action: VendorAction,
},
}
#[derive(Debug, Subcommand)]
enum VendorAction {
/// Download wheels (plus transitive deps) into vendor/ and pin them.
Add {
/// Package specification (`name` or `name==version`).
spec: String,
/// Keep the wheel file even for pure-Python packages.
#[arg(long, default_value_t = false)]
zipped: bool,
/// Unpack into vendor/ even without native modules.
#[arg(long, default_value_t = false)]
unpacked: bool,
},
/// Remove a vendored package and clean up its files from vendor/.
Remove {
/// Name of the package to remove.
name: String,
},
/// List all currently vendored packages and their pinned versions.
List,
/// Verify that all vendored packages match their pinned SHA-256 hashes.
Verify,
}
fn main() -> Result<()> {
tracing_subscriber::fmt()
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
.init();
let cli = Cli::parse();
match cli.command {
None => {
let cwd = std::env::current_dir()?;
if pite_project::find_project_root(&cwd).is_some() {
cmd_edit::run(None)
} else {
Cli::command().print_help()?;
println!();
Ok(())
}
}
Some(Command::New { name, template }) => cmd_new::run(&name, template.as_deref()),
Some(Command::Run { scene, no_reload }) => cmd_run::run(scene.as_deref(), no_reload),
Some(Command::Check {
path,
reimport_assets,
strict,
json,
}) => cmd_check::run(path.as_deref(), reimport_assets, strict, json),
Some(Command::Edit { scene }) => cmd_edit::run(scene.as_deref()),
Some(Command::Export {
platform,
path,
out,
binary,
skip_python_check,
no_bundle_python,
}) => cmd_export::run(
&platform,
path.as_deref(),
out.as_deref(),
binary.as_deref(),
skip_python_check,
no_bundle_python,
),
Some(Command::Toolchain { action }) => cmd_toolchain::run(action),
Some(Command::Vendor { action }) => match action {
VendorAction::Add {
spec,
zipped,
unpacked,
} => cmd_vendor::add(&spec, zipped, unpacked),
VendorAction::Remove { name } => cmd_vendor::remove(&name),
VendorAction::List => cmd_vendor::list(),
VendorAction::Verify => cmd_vendor::verify(),
},
}
}