Add AFS Studio CLI helpers and versioning
This commit is contained in:
@@ -28,3 +28,8 @@ Quickstart:
|
||||
- `python -m afs orchestrator list`
|
||||
|
||||
Discovery skips directory names in `general.discovery_ignore` (default: legacy, archive, archives).
|
||||
|
||||
Studio:
|
||||
- `python -m afs studio build`
|
||||
- `python -m afs studio run --build`
|
||||
- `python -m afs studio install --prefix ~/.local`
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
project(afs_studio LANGUAGES CXX)
|
||||
project(afs_studio VERSION 0.0.0 LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
@@ -131,6 +131,7 @@ target_include_directories(afs_studio PRIVATE
|
||||
|
||||
target_compile_definitions(afs_studio PRIVATE
|
||||
IMGUI_IMPL_OPENGL_LOADER_GLAD=0
|
||||
AFS_STUDIO_VERSION="${PROJECT_VERSION}"
|
||||
)
|
||||
|
||||
if(TARGET OpenGL::OpenGL)
|
||||
|
||||
@@ -5,15 +5,39 @@ Native C++17 visualization and training management application for AFS.
|
||||
## Build
|
||||
|
||||
```bash
|
||||
# From project root
|
||||
cmake -B build -S . -DAFS_BUILD_STUDIO=ON
|
||||
cmake --build build --target afs_studio
|
||||
# From AFS repo root
|
||||
cmake -S apps/studio -B build/studio
|
||||
cmake --build build/studio --target afs_studio
|
||||
```
|
||||
|
||||
## Run
|
||||
|
||||
```bash
|
||||
./build/apps/studio/afs_studio
|
||||
./build/studio/afs_studio --data ~/src/training
|
||||
```
|
||||
|
||||
## Install (local)
|
||||
|
||||
```bash
|
||||
cmake --install build/studio --prefix ~/.local
|
||||
# Ensure ~/.local/bin is on PATH
|
||||
```
|
||||
|
||||
## CLI helpers
|
||||
|
||||
```bash
|
||||
python -m afs studio build
|
||||
python -m afs studio run --build
|
||||
python -m afs studio install --prefix ~/.local
|
||||
python -m afs studio alias
|
||||
```
|
||||
|
||||
## Quick aliases
|
||||
|
||||
```bash
|
||||
export AFS_ROOT=~/src/trunk/lab/afs
|
||||
alias afs-studio='PYTHONPATH="$AFS_ROOT/src" python -m afs studio run --build'
|
||||
alias afs-studio-build='PYTHONPATH="$AFS_ROOT/src" python -m afs studio build'
|
||||
```
|
||||
|
||||
## Data sources
|
||||
@@ -23,6 +47,11 @@ cmake --build build --target afs_studio
|
||||
- Dataset registry: `AFS_DATASET_REGISTRY` or `${AFS_TRAINING_ROOT}/index/dataset_registry.json`.
|
||||
- Resource index: `AFS_RESOURCE_INDEX` or `${AFS_TRAINING_ROOT}/index/resource_index.json`.
|
||||
|
||||
## Flags
|
||||
|
||||
- `--data` or `--data-path`: override training root
|
||||
- `--version`: print version and exit
|
||||
|
||||
## Features
|
||||
|
||||
- **Dashboard**: Training metrics overview
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#include "app.h"
|
||||
#include "core/logger.h"
|
||||
#include "core/context.h"
|
||||
#include "core/assets.h"
|
||||
#include "core/context.h"
|
||||
#include "core/logger.h"
|
||||
#include "core/version.h"
|
||||
#include "ui/panels/chat_panel.h"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -41,6 +42,9 @@ App::App(const std::string& data_path)
|
||||
: data_path_(data_path), loader_(data_path) {
|
||||
LOG_INFO("AFS Studio initialize with data path: " + data_path);
|
||||
|
||||
state_.studio_version = studio::core::StudioVersion();
|
||||
state_.studio_data_root = data_path_;
|
||||
|
||||
std::snprintf(state_.new_agent_role.data(), state_.new_agent_role.size(), "Evaluator");
|
||||
std::snprintf(state_.new_mission_owner.data(), state_.new_mission_owner.size(), "Ops");
|
||||
std::snprintf(state_.system_prompt.data(), state_.system_prompt.size(),
|
||||
@@ -69,7 +73,12 @@ App::App(const std::string& data_path)
|
||||
SeedDefaultState();
|
||||
|
||||
// Create graphics context
|
||||
context_ = std::make_unique<studio::core::GraphicsContext>("AFS Studio", 1400, 900);
|
||||
std::string title = "AFS Studio";
|
||||
if (!state_.studio_version.empty()) {
|
||||
title += " ";
|
||||
title += state_.studio_version;
|
||||
}
|
||||
context_ = std::make_unique<studio::core::GraphicsContext>(title, 1400, 900);
|
||||
if (context_->IsValid()) {
|
||||
fonts_ = studio::core::AssetLoader::LoadFonts();
|
||||
themes::ApplyHafsTheme();
|
||||
|
||||
15
apps/studio/src/core/version.h
Normal file
15
apps/studio/src/core/version.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
namespace afs {
|
||||
namespace studio {
|
||||
namespace core {
|
||||
|
||||
#ifndef AFS_STUDIO_VERSION
|
||||
#define AFS_STUDIO_VERSION "0.0.0"
|
||||
#endif
|
||||
|
||||
inline const char* StudioVersion() { return AFS_STUDIO_VERSION; }
|
||||
|
||||
} // namespace core
|
||||
} // namespace studio
|
||||
} // namespace afs
|
||||
@@ -41,10 +41,14 @@ std::optional<std::filesystem::path> ResolveHafsScawfulRoot() {
|
||||
|
||||
auto trunk_root = ResolveTrunkRoot();
|
||||
if (trunk_root) {
|
||||
auto candidate = *trunk_root / "scawful" / "research" / "afs_scawful";
|
||||
auto candidate = *trunk_root / "lab" / "afs_scawful";
|
||||
if (studio::core::FileSystem::Exists(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
auto legacy = *trunk_root / "scawful" / "research" / "afs_scawful";
|
||||
if (studio::core::FileSystem::Exists(legacy)) {
|
||||
return legacy;
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
@@ -1019,11 +1023,17 @@ void DataLoader::MountDrive(const std::string& name) {
|
||||
} else {
|
||||
auto trunk_root = ResolveTrunkRoot();
|
||||
if (trunk_root) {
|
||||
script_path = *trunk_root / "lab" / "afs_scawful" / "scripts" / "mount_windows.sh";
|
||||
if (!studio::core::FileSystem::Exists(script_path)) {
|
||||
script_path = *trunk_root / "scawful" / "research" / "afs_scawful" / "scripts" / "mount_windows.sh";
|
||||
}
|
||||
} else {
|
||||
script_path = studio::core::FileSystem::ResolvePath("~/src/trunk/lab/afs_scawful/scripts/mount_windows.sh");
|
||||
if (!studio::core::FileSystem::Exists(script_path)) {
|
||||
script_path = studio::core::FileSystem::ResolvePath("~/src/trunk/scawful/research/afs_scawful/scripts/mount_windows.sh");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (studio::core::FileSystem::Exists(script_path)) {
|
||||
LOG_INFO("DataLoader: Triggering mount using " + script_path.string());
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/// AFS Training Data Visualization - Main Entry Point
|
||||
/// AFS Studio - Main Entry Point
|
||||
///
|
||||
/// Usage: afs_viz [data_path]
|
||||
/// data_path: Path to training data directory (default: ~/src/training if present)
|
||||
/// Usage: afs_studio [--data PATH]
|
||||
/// --data PATH: Path to training data directory (default: ~/src/training if present)
|
||||
/// --version: Print version and exit
|
||||
///
|
||||
/// Build:
|
||||
/// cmake -B build -S src/cc -DAFS_BUILD_VIZ=ON
|
||||
/// cmake --build build
|
||||
/// cmake -S apps/studio -B build/studio
|
||||
/// cmake --build build/studio --target afs_studio
|
||||
///
|
||||
/// Keys:
|
||||
/// F5 - Refresh data
|
||||
@@ -17,29 +18,62 @@
|
||||
#include <string>
|
||||
|
||||
#include "app.h"
|
||||
#include "core/logger.h"
|
||||
#include "core/filesystem.h"
|
||||
#include "core/logger.h"
|
||||
#include "core/version.h"
|
||||
|
||||
namespace {
|
||||
|
||||
void PrintUsage() {
|
||||
std::cout << "afs_studio [--data PATH]\n"
|
||||
<< " --data PATH Training data root (default: ~/src/training)\n"
|
||||
<< " --version Print version and exit\n"
|
||||
<< " -h, --help Show this help\n";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
using afs::studio::core::FileSystem;
|
||||
|
||||
// Determine data path
|
||||
std::string data_path_str;
|
||||
if (argc > 1) {
|
||||
data_path_str = argv[1];
|
||||
} else {
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
std::string arg = argv[i];
|
||||
if (arg == "--help" || arg == "-h") {
|
||||
PrintUsage();
|
||||
return 0;
|
||||
}
|
||||
if (arg == "--version" || arg == "-v") {
|
||||
std::cout << "AFS Studio " << afs::studio::core::StudioVersion() << "\n";
|
||||
return 0;
|
||||
}
|
||||
if (arg == "--data" || arg == "--data-path") {
|
||||
if (i + 1 < argc) {
|
||||
data_path_str = argv[++i];
|
||||
continue;
|
||||
}
|
||||
std::cerr << "Missing value for --data\n";
|
||||
return 1;
|
||||
}
|
||||
if (data_path_str.empty() && !arg.empty() && arg[0] != '-') {
|
||||
data_path_str = arg;
|
||||
}
|
||||
}
|
||||
if (data_path_str.empty()) {
|
||||
const char* env_root = std::getenv("AFS_TRAINING_ROOT");
|
||||
if (env_root && env_root[0] != '\0') {
|
||||
data_path_str = env_root;
|
||||
} else {
|
||||
auto preferred = FileSystem::ResolvePath("~/src/training");
|
||||
data_path_str = FileSystem::Exists(preferred) ? preferred.string() : "~/.context/training";
|
||||
data_path_str = FileSystem::Exists(preferred) ? preferred.string()
|
||||
: "~/.context/training";
|
||||
}
|
||||
}
|
||||
|
||||
std::filesystem::path data_path = FileSystem::ResolvePath(data_path_str);
|
||||
|
||||
LOG_INFO("AFS Studio Starting...");
|
||||
LOG_INFO(std::string("AFS Studio ") + afs::studio::core::StudioVersion());
|
||||
LOG_INFO("Data path: " + data_path.string());
|
||||
LOG_INFO("Press F5 to refresh data");
|
||||
|
||||
|
||||
@@ -122,6 +122,7 @@ struct AppState {
|
||||
bool show_quality_trends = false; // Default off, let layout init handle it
|
||||
bool show_generator_efficiency = false;
|
||||
bool show_coverage_density = false;
|
||||
bool show_about_modal = false;
|
||||
bool enable_viewports = true;
|
||||
bool enable_docking = true;
|
||||
bool reset_layout_on_workspace_change = false;
|
||||
@@ -160,6 +161,8 @@ struct AppState {
|
||||
bool force_reset_layout = false;
|
||||
bool lock_layout = false;
|
||||
double last_refresh_time = 0.0;
|
||||
std::string studio_version;
|
||||
std::string studio_data_root;
|
||||
|
||||
// Advanced Interaction
|
||||
std::vector<PlotKind> active_floaters;
|
||||
|
||||
@@ -37,16 +37,24 @@ std::filesystem::path ResolveHafsScawfulRoot() {
|
||||
const char* trunk_env = std::getenv("TRUNK_ROOT");
|
||||
if (trunk_env && trunk_env[0] != '\0') {
|
||||
auto trunk_root = studio::core::FileSystem::ResolvePath(trunk_env);
|
||||
auto candidate = trunk_root / "scawful" / "research" / "afs_scawful";
|
||||
auto candidate = trunk_root / "lab" / "afs_scawful";
|
||||
if (studio::core::FileSystem::Exists(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
auto legacy = trunk_root / "scawful" / "research" / "afs_scawful";
|
||||
if (studio::core::FileSystem::Exists(legacy)) {
|
||||
return legacy;
|
||||
}
|
||||
}
|
||||
|
||||
auto fallback_path = studio::core::FileSystem::ResolvePath("~/src/trunk/scawful/research/afs_scawful");
|
||||
auto fallback_path = studio::core::FileSystem::ResolvePath("~/src/trunk/lab/afs_scawful");
|
||||
if (studio::core::FileSystem::Exists(fallback_path)) {
|
||||
return fallback_path;
|
||||
}
|
||||
auto legacy_fallback = studio::core::FileSystem::ResolvePath("~/src/trunk/scawful/research/afs_scawful");
|
||||
if (studio::core::FileSystem::Exists(legacy_fallback)) {
|
||||
return legacy_fallback;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
@@ -1458,11 +1466,33 @@ void RenderMenuBar(AppState& state,
|
||||
if (show_shortcuts_window) *show_shortcuts_window = true;
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("About AFS Viz")) {}
|
||||
if (ImGui::MenuItem("About AFS Studio")) {
|
||||
state.show_about_modal = true;
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::EndMainMenuBar();
|
||||
}
|
||||
|
||||
if (state.show_about_modal) {
|
||||
ImGui::OpenPopup("About AFS Studio");
|
||||
}
|
||||
if (ImGui::BeginPopupModal("About AFS Studio", &state.show_about_modal,
|
||||
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::Text("AFS Studio");
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Version: %s", state.studio_version.empty()
|
||||
? "(unknown)"
|
||||
: state.studio_version.c_str());
|
||||
ImGui::Text("Data root: %s", state.studio_data_root.empty()
|
||||
? "(not set)"
|
||||
: state.studio_data_root.c_str());
|
||||
ImGui::Spacing();
|
||||
if (ImGui::Button("Close")) {
|
||||
state.show_about_modal = false;
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
void RenderSidebar(AppState& state, const DataLoader& loader, ImFont* font_ui, ImFont* font_header) {
|
||||
|
||||
@@ -108,11 +108,11 @@ bool TrainingStatusWidget::FetchHealthData() {
|
||||
} else {
|
||||
const char* trunk_root = std::getenv("TRUNK_ROOT");
|
||||
if (trunk_root && trunk_root[0] != '\0') {
|
||||
root = std::string(trunk_root) + "/scawful/research/afs";
|
||||
root = std::string(trunk_root) + "/lab/afs";
|
||||
} else {
|
||||
const char* home = std::getenv("HOME");
|
||||
if (home && home[0] != '\0') {
|
||||
root = std::string(home) + "/src/trunk/scawful/research/afs";
|
||||
root = std::string(home) + "/src/trunk/lab/afs";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
Scope: inventory of legacy features and their status in the current AFS/AFS Scawful repos. Use this as a porting checklist; verify specifics when moving code.
|
||||
|
||||
Sources (local workspace):
|
||||
- Previous core: `trunk/scawful/research/legacy/afs_legacy`
|
||||
- Previous plugin: `trunk/scawful/research/legacy/afs_scawful_legacy`
|
||||
- Current core: `trunk/scawful/research/afs`
|
||||
- Current plugin: `trunk/scawful/research/afs_scawful`
|
||||
- Previous core: `trunk/lab/legacy/afs_legacy`
|
||||
- Previous plugin: `trunk/lab/legacy/afs_scawful_legacy`
|
||||
- Current core: `trunk/lab/afs`
|
||||
- Current plugin: `trunk/lab/afs_scawful`
|
||||
|
||||
Status legend: Ported, Partial, Planned, Not started.
|
||||
|
||||
|
||||
197
src/afs/cli.py
197
src/afs/cli.py
@@ -3,6 +3,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from typing import Iterable
|
||||
|
||||
@@ -38,6 +40,75 @@ def _parse_mount_type(value: str) -> MountType:
|
||||
raise argparse.ArgumentTypeError(f"Unknown mount type: {value}") from exc
|
||||
|
||||
|
||||
def _resolve_studio_root() -> Path:
|
||||
candidates: list[Path] = []
|
||||
env_root = os.getenv("AFS_ROOT")
|
||||
if env_root:
|
||||
candidates.append(Path(env_root).expanduser().resolve())
|
||||
trunk_root = os.getenv("TRUNK_ROOT")
|
||||
if trunk_root:
|
||||
candidates.append(Path(trunk_root).expanduser().resolve() / "lab" / "afs")
|
||||
candidates.append(Path(__file__).resolve().parents[2])
|
||||
|
||||
for candidate in candidates:
|
||||
if (candidate / "apps" / "studio" / "CMakeLists.txt").exists():
|
||||
return candidate
|
||||
|
||||
raise FileNotFoundError(
|
||||
"AFS studio source not found. Set AFS_ROOT to the repo root."
|
||||
)
|
||||
|
||||
|
||||
def _studio_binary_name() -> str:
|
||||
return "afs_studio.exe" if os.name == "nt" else "afs_studio"
|
||||
|
||||
|
||||
def _studio_build_dir(root: Path, override: str | None) -> Path:
|
||||
return (
|
||||
Path(override).expanduser().resolve()
|
||||
if override
|
||||
else root / "build" / "studio"
|
||||
)
|
||||
|
||||
|
||||
def _studio_binary_path(build_dir: Path, config: str | None) -> Path:
|
||||
if config:
|
||||
candidate = build_dir / config / _studio_binary_name()
|
||||
if candidate.exists():
|
||||
return candidate
|
||||
return build_dir / _studio_binary_name()
|
||||
|
||||
|
||||
def _run_command(cmd: list[str]) -> int:
|
||||
try:
|
||||
subprocess.run(cmd, check=True)
|
||||
except FileNotFoundError:
|
||||
print(f"command not found: {cmd[0]}")
|
||||
return 1
|
||||
except subprocess.CalledProcessError as exc:
|
||||
return exc.returncode
|
||||
return 0
|
||||
|
||||
|
||||
def _studio_build(
|
||||
root: Path,
|
||||
build_dir: Path,
|
||||
build_type: str | None,
|
||||
config: str | None,
|
||||
) -> int:
|
||||
src_dir = root / "apps" / "studio"
|
||||
cmake_cmd = ["cmake", "-S", str(src_dir), "-B", str(build_dir)]
|
||||
if build_type:
|
||||
cmake_cmd.append(f"-DCMAKE_BUILD_TYPE={build_type}")
|
||||
status = _run_command(cmake_cmd)
|
||||
if status != 0:
|
||||
return status
|
||||
build_cmd = ["cmake", "--build", str(build_dir), "--target", "afs_studio"]
|
||||
if config:
|
||||
build_cmd.extend(["--config", config])
|
||||
return _run_command(build_cmd)
|
||||
|
||||
|
||||
def _load_manager(config_path: Path | None) -> AFSManager:
|
||||
config = load_config_model(config_path=config_path, merge_user=True)
|
||||
return AFSManager(config=config)
|
||||
@@ -197,6 +268,90 @@ def _orchestrator_plan_command(args: argparse.Namespace) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
def _studio_build_command(args: argparse.Namespace) -> int:
|
||||
try:
|
||||
root = _resolve_studio_root()
|
||||
except FileNotFoundError as exc:
|
||||
print(str(exc))
|
||||
return 1
|
||||
build_dir = _studio_build_dir(root, args.build_dir)
|
||||
status = _studio_build(root, build_dir, args.build_type, args.config)
|
||||
if status == 0:
|
||||
print(f"build_dir: {build_dir}")
|
||||
return status
|
||||
|
||||
|
||||
def _studio_run_command(args: argparse.Namespace) -> int:
|
||||
try:
|
||||
root = _resolve_studio_root()
|
||||
except FileNotFoundError as exc:
|
||||
print(str(exc))
|
||||
return 1
|
||||
build_dir = _studio_build_dir(root, args.build_dir)
|
||||
binary = _studio_binary_path(build_dir, args.config)
|
||||
if not binary.exists() and args.build:
|
||||
status = _studio_build(root, build_dir, args.build_type, args.config)
|
||||
if status != 0:
|
||||
return status
|
||||
binary = _studio_binary_path(build_dir, args.config)
|
||||
if not binary.exists():
|
||||
print(f"binary not found: {binary}")
|
||||
return 1
|
||||
cmd = [str(binary)]
|
||||
if args.args:
|
||||
cmd.extend(args.args)
|
||||
return _run_command(cmd)
|
||||
|
||||
|
||||
def _studio_install_command(args: argparse.Namespace) -> int:
|
||||
try:
|
||||
root = _resolve_studio_root()
|
||||
except FileNotFoundError as exc:
|
||||
print(str(exc))
|
||||
return 1
|
||||
build_dir = _studio_build_dir(root, args.build_dir)
|
||||
if not build_dir.exists():
|
||||
print(f"build dir missing: {build_dir}")
|
||||
return 1
|
||||
prefix = (
|
||||
Path(args.prefix).expanduser().resolve()
|
||||
if args.prefix
|
||||
else Path.home() / ".local"
|
||||
)
|
||||
cmd = ["cmake", "--install", str(build_dir), "--prefix", str(prefix)]
|
||||
if args.config:
|
||||
cmd.extend(["--config", args.config])
|
||||
status = _run_command(cmd)
|
||||
if status == 0:
|
||||
print(f"installed: {prefix / 'bin' / _studio_binary_name()}")
|
||||
return status
|
||||
|
||||
|
||||
def _studio_path_command(args: argparse.Namespace) -> int:
|
||||
try:
|
||||
root = _resolve_studio_root()
|
||||
except FileNotFoundError as exc:
|
||||
print(str(exc))
|
||||
return 1
|
||||
build_dir = _studio_build_dir(root, args.build_dir)
|
||||
binary = _studio_binary_path(build_dir, args.config)
|
||||
print(binary)
|
||||
return 0
|
||||
|
||||
|
||||
def _studio_alias_command(args: argparse.Namespace) -> int:
|
||||
try:
|
||||
root = _resolve_studio_root()
|
||||
except FileNotFoundError as exc:
|
||||
print(str(exc))
|
||||
return 1
|
||||
root_value = os.getenv("AFS_ROOT") or str(root)
|
||||
print(f"export AFS_ROOT=\"{root_value}\"")
|
||||
print("alias afs-studio='PYTHONPATH=\"$AFS_ROOT/src\" python -m afs studio run --build'")
|
||||
print("alias afs-studio-build='PYTHONPATH=\"$AFS_ROOT/src\" python -m afs studio build'")
|
||||
return 0
|
||||
|
||||
|
||||
def _status_command(args: argparse.Namespace) -> int:
|
||||
start_dir = Path(args.start_dir).expanduser().resolve() if args.start_dir else None
|
||||
root = find_root(start_dir)
|
||||
@@ -580,6 +735,45 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
orch_plan.add_argument("--role", help="Role to match.")
|
||||
orch_plan.set_defaults(func=_orchestrator_plan_command)
|
||||
|
||||
studio_parser = subparsers.add_parser("studio", help="AFS Studio helpers.")
|
||||
studio_sub = studio_parser.add_subparsers(dest="studio_command")
|
||||
|
||||
studio_build = studio_sub.add_parser("build", help="Build AFS Studio.")
|
||||
studio_build.add_argument("--build-dir", help="Build directory override.")
|
||||
studio_build.add_argument(
|
||||
"--build-type",
|
||||
default="RelWithDebInfo",
|
||||
help="CMake build type (default: RelWithDebInfo).",
|
||||
)
|
||||
studio_build.add_argument("--config", help="Multi-config build name.")
|
||||
studio_build.set_defaults(func=_studio_build_command)
|
||||
|
||||
studio_run = studio_sub.add_parser("run", help="Run AFS Studio.")
|
||||
studio_run.add_argument("--build", action="store_true", help="Build if missing.")
|
||||
studio_run.add_argument("--build-dir", help="Build directory override.")
|
||||
studio_run.add_argument(
|
||||
"--build-type",
|
||||
default="RelWithDebInfo",
|
||||
help="CMake build type (default: RelWithDebInfo).",
|
||||
)
|
||||
studio_run.add_argument("--config", help="Multi-config build name.")
|
||||
studio_run.add_argument("args", nargs=argparse.REMAINDER, help="Arguments for afs_studio.")
|
||||
studio_run.set_defaults(func=_studio_run_command)
|
||||
|
||||
studio_install = studio_sub.add_parser("install", help="Install AFS Studio.")
|
||||
studio_install.add_argument("--prefix", help="Install prefix (default: ~/.local).")
|
||||
studio_install.add_argument("--build-dir", help="Build directory override.")
|
||||
studio_install.add_argument("--config", help="Multi-config build name.")
|
||||
studio_install.set_defaults(func=_studio_install_command)
|
||||
|
||||
studio_path = studio_sub.add_parser("path", help="Print studio binary path.")
|
||||
studio_path.add_argument("--build-dir", help="Build directory override.")
|
||||
studio_path.add_argument("--config", help="Multi-config build name.")
|
||||
studio_path.set_defaults(func=_studio_path_command)
|
||||
|
||||
studio_alias = studio_sub.add_parser("alias", help="Print alias suggestions.")
|
||||
studio_alias.set_defaults(func=_studio_alias_command)
|
||||
|
||||
status_parser = subparsers.add_parser("status", help="Show context root status.")
|
||||
status_parser.add_argument("--start-dir", help="Directory to search from.")
|
||||
status_parser.set_defaults(func=_status_command)
|
||||
@@ -777,6 +971,9 @@ def main(argv: Iterable[str] | None = None) -> int:
|
||||
if args.command == "orchestrator" and not getattr(args, "orchestrator_command", None):
|
||||
parser.print_help()
|
||||
return 1
|
||||
if args.command == "studio" and not getattr(args, "studio_command", None):
|
||||
parser.print_help()
|
||||
return 1
|
||||
return args.func(args)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user