Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,20 @@ Thank you for your interest in OpenViking! We welcome contributions of all kinds
### Prerequisites

- **Python**: 3.10+
- **Go**: 1.25.1+ (Required for building AGFS components)
- **Go**: 1.25.1+ (Required for building AGFS components from source)
- **C++ Compiler**: GCC 9+ or Clang 11+ (Required for building core extensions, must support C++17)
- **CMake**: 3.12+

#### Supported Platforms (Pre-compiled Wheels)

OpenViking provides pre-compiled **Wheel** packages for the following environments:

- **Windows**: x86_64
- **macOS**: x86_64, arm64 (Apple Silicon)
- **Linux**: x86_64 (manylinux)

For other platforms (e.g., Linux ARM64, FreeBSD), the package will be automatically compiled from source during installation via `pip`. Ensure you have the [Prerequisites](#prerequisites) installed.

### 1. Fork and Clone

```bash
Expand All @@ -37,9 +47,18 @@ curl -LsSf https://astral.sh/uv/install.sh | sh
uv sync --all-extras
source .venv/bin/activate # Linux/macOS
# or .venv\Scripts\activate # Windows
```

#### Local Development & AGFS Compilation

If you modify the **AGFS (Go)** code or **C++ extensions**, you need to re-compile and re-install them to make the changes take effect in your environment. Run the following command in the project root:

```bash
uv pip install -e . --force-reinstall
```

This command ensures that `setup.py` is re-executed, triggering the compilation of AGFS and C++ components.

### 3. Configure Environment

Create a configuration file `~/.openviking/ov.conf`:
Expand Down
22 changes: 21 additions & 1 deletion CONTRIBUTING_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,20 @@
### 前置要求

- **Python**: 3.10+
- **Go**: 1.25.1+ (构建 AGFS 组件需要)
- **Go**: 1.25.1+ (从源码构建 AGFS 组件需要)
- **C++ 编译器**: GCC 9+ 或 Clang 11+ (构建核心扩展需要,必须支持 C++17)
- **CMake**: 3.12+

#### 支持的平台 (预编译 Wheel 包)

OpenViking 为以下环境提供预编译的 **Wheel** 包,安装时无需本地编译:

- **Windows**: x86_64
- **macOS**: x86_64, arm64 (Apple Silicon)
- **Linux**: x86_64 (manylinux)

对于其他平台(如 Linux ARM64、FreeBSD 等),安装时 `pip` 将会自动从源码进行编译。请确保已安装上述[前置要求](#前置要求)中的工具。

### 1. Fork 并克隆

```bash
Expand All @@ -39,6 +49,16 @@ source .venv/bin/activate # Linux/macOS
# 或者 .venv\Scripts\activate # Windows
```

#### 本地开发与 AGFS 编译

如果你修改了 **AGFS (Go)** 代码或 **C++ 扩展**,你需要重新编译并安装它们,以使更改在本地环境中生效。在项目根目录下运行以下命令:

```bash
uv pip install -e . --force-reinstall
```

该命令会强制重新执行 `setup.py`,触发 AGFS 和 C++ 组件的编译与安装。

### 3. 配置环境

创建配置文件 `~/.openviking/ov.conf`:
Expand Down
7 changes: 5 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,11 @@ openviking = [
"prompts/templates/**/*.yaml",
"bin/agfs-server",
"bin/agfs-server.exe",
"bin/ov", # 新增
"bin/ov.exe" # 新增
"lib/libagfsbinding.so",
"lib/libagfsbinding.dylib",
"lib/libagfsbinding.dll",
"bin/ov",
"bin/ov.exe"
]

[tool.mypy]
Expand Down
144 changes: 125 additions & 19 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os
import shutil
import subprocess
import sys
import sysconfig
from pathlib import Path
Expand All @@ -15,25 +16,36 @@


class CMakeBuildExtension(build_ext):
"""Custom CMake build extension that builds AGFS and C++ extensions."""
"""Custom CMake build extension that builds AGFS, ov CLI and C++ extensions."""

def run(self):
self.build_agfs()
self.build_ov()
self.cmake_executable = CMAKE_PATH

for ext in self.extensions:
self.build_extension(ext)

def _copy_binary(self, src, dst):
"""Helper to copy binary and set permissions."""
print(f"Copying AGFS binary from {src} to {dst}")
print(f"Copying binary from {src} to {dst}")
dst.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(str(src), str(dst))
if sys.platform != "win32":
os.chmod(str(dst), 0o755)

def _ensure_build_lib_copied(self, target_binary=None, target_lib=None):
"""Ensure binaries are copied to the build directory (where wheel is packaged from)."""
if self.build_lib:
build_pkg_dir = Path(self.build_lib) / "openviking"
if target_binary and target_binary.exists():
self._copy_binary(target_binary, build_pkg_dir / "bin" / target_binary.name)
if target_lib and target_lib.exists():
# Libs go to lib/ as expected by agfs_utils.py
self._copy_binary(target_lib, build_pkg_dir / "lib" / target_lib.name)

def build_agfs(self):
"""Build AGFS server and binding library from source."""
"""Build AGFS server and binding library."""
# Paths
binary_name = "agfs-server.exe" if sys.platform == "win32" else "agfs-server"
if sys.platform == "win32":
Expand All @@ -51,14 +63,44 @@ def build_agfs(self):
agfs_target_binary = agfs_bin_dir / binary_name
agfs_target_lib = agfs_lib_dir / lib_name

# 1. Try to build from source
# 1. Check for pre-built binaries in a specified directory
prebuilt_dir = os.environ.get("OV_PREBUILT_BIN_DIR")
if prebuilt_dir:
prebuilt_path = Path(prebuilt_dir).resolve()
print(f"Checking for pre-built AGFS binaries in {prebuilt_path}...")
src_bin = prebuilt_path / binary_name
src_lib = prebuilt_path / lib_name

if src_bin.exists():
self._copy_binary(src_bin, agfs_target_binary)
if src_lib.exists():
self._copy_binary(src_lib, agfs_target_lib)

if agfs_target_binary.exists() and agfs_target_lib.exists():
print(f"[OK] Used pre-built AGFS binaries from {prebuilt_dir}")
self._ensure_build_lib_copied(agfs_target_binary, agfs_target_lib)
return

# 2. Skip build if requested and binaries exist
if os.environ.get("OV_SKIP_AGFS_BUILD") == "1":
if agfs_target_binary.exists() and agfs_target_lib.exists():
print("[OK] Skipping AGFS build, using existing binaries")
self._ensure_build_lib_copied(agfs_target_binary, agfs_target_lib)
return
else:
print("[Warning] OV_SKIP_AGFS_BUILD=1 but binaries not found. Will try to build.")

# 3. Try to build from source
if agfs_server_dir.exists() and shutil.which("go"):
print("Building AGFS from source...")
import subprocess

# Build server
try:
print(f"Building AGFS server: {binary_name}")
env = os.environ.copy()
if "GOOS" in env or "GOARCH" in env:
print(f"Cross-compiling with GOOS={env.get('GOOS')} GOARCH={env.get('GOARCH')}")

build_args = (
["go", "build", "-o", f"build/{binary_name}", "cmd/server/main.go"]
if sys.platform == "win32"
Expand All @@ -68,6 +110,7 @@ def build_agfs(self):
subprocess.run(
build_args,
cwd=str(agfs_server_dir),
env=env,
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
Expand Down Expand Up @@ -128,17 +171,80 @@ def build_agfs(self):

else:
if not agfs_server_dir.exists():
raise FileNotFoundError(f"AGFS source directory not found at {agfs_server_dir}")
print(f"[Warning] AGFS source directory not found at {agfs_server_dir}")
else:
raise RuntimeError("Go compiler not found. Please install Go to build AGFS.")
print("[Warning] Go compiler not found. Cannot build AGFS from source.")

# 2. Ensure binaries are copied to the build directory (where wheel is packaged from)
if self.build_lib:
agfs_bin_dir_build = Path(self.build_lib) / "openviking"
if agfs_target_binary.exists():
self._copy_binary(agfs_target_binary, agfs_bin_dir_build / "bin" / binary_name)
if agfs_target_lib.exists():
self._copy_binary(agfs_target_lib, agfs_bin_dir_build / "lib" / lib_name)
# Final check and copy to build dir
self._ensure_build_lib_copied(agfs_target_binary, agfs_target_lib)

def build_ov(self):
"""Build or copy ov Rust CLI."""
binary_name = "ov.exe" if sys.platform == "win32" else "ov"
ov_cli_dir = Path("crates/ov_cli").resolve()

agfs_bin_dir = Path("openviking/bin").resolve()
ov_target_binary = agfs_bin_dir / binary_name

# 1. Check for pre-built
prebuilt_dir = os.environ.get("OV_PREBUILT_BIN_DIR")
if prebuilt_dir:
src_bin = Path(prebuilt_dir).resolve() / binary_name
if src_bin.exists():
self._copy_binary(src_bin, ov_target_binary)
self._ensure_build_lib_copied(ov_target_binary, None)
return

# 2. Skip build if requested
if os.environ.get("OV_SKIP_OV_BUILD") == "1":
if ov_target_binary.exists():
print("[OK] Skipping ov CLI build, using existing binary")
self._ensure_build_lib_copied(ov_target_binary, None)
return
else:
print("[Warning] OV_SKIP_OV_BUILD=1 but binary not found. Will try to build.")

# 3. Build from source
if ov_cli_dir.exists() and shutil.which("cargo"):
print("Building ov CLI from source...")
try:
env = os.environ.copy()
build_args = ["cargo", "build", "--release"]

# Support cross-compilation via CARGO_BUILD_TARGET
target = env.get("CARGO_BUILD_TARGET")
if target:
print(f"Cross-compiling with CARGO_BUILD_TARGET={target}")
build_args.extend(["--target", target])

subprocess.run(
build_args,
cwd=str(ov_cli_dir),
env=env,
check=True,
)

# Find built binary
if target:
built_bin = ov_cli_dir / "target" / target / "release" / binary_name
else:
built_bin = ov_cli_dir / "target" / "release" / binary_name

if built_bin.exists():
self._copy_binary(built_bin, ov_target_binary)
print("[OK] ov CLI built successfully from source")
else:
print(f"[Warning] Built ov binary not found at {built_bin}")
except Exception as e:
print(f"[Warning] Failed to build ov CLI from source: {e}")
else:
if not ov_cli_dir.exists():
print(f"[Warning] ov CLI source directory not found at {ov_cli_dir}")
else:
print("[Warning] Cargo not found. Cannot build ov CLI from source.")

# Final check and copy to build dir
self._ensure_build_lib_copied(ov_target_binary, None)

def build_extension(self, ext):
"""Build a single C++ extension module using CMake."""
Expand Down Expand Up @@ -191,11 +297,11 @@ def build_extension(self, ext):
"openviking": [
"bin/agfs-server",
"bin/agfs-server.exe",
"bin/libagfsbinding.so",
"bin/libagfsbinding.dylib",
"bin/libagfsbinding.dll",
"bin/ov", # 新增
"bin/ov.exe", # 新增
"lib/libagfsbinding.so",
"lib/libagfsbinding.dylib",
"lib/libagfsbinding.dll",
"bin/ov",
"bin/ov.exe",
],
},
include_package_data=True,
Expand Down
Loading