diff --git a/docs/.vitepress/sidebar.en.ts b/docs/.vitepress/sidebar.en.ts index 0ec00d052..8a34aabad 100644 --- a/docs/.vitepress/sidebar.en.ts +++ b/docs/.vitepress/sidebar.en.ts @@ -33,14 +33,14 @@ export default { items: [ { text: 'Get Started', link: '/en/develop/' }, { text: 'Project Structure', link: '/en/develop/structure' }, - { text: 'PHP Source Modifications', link: '/en/develop/php-src-changes' }, ], }, { text: 'Concepts', items: [ + { text: 'Registry', link: '/en/develop/registry' }, { text: 'Package Model', link: '/en/develop/package-model' }, - { text: 'Registry & Plugin System', link: '/en/develop/registry' }, + { text: 'Artifact Model', link: '/en/develop/artifact-model' }, { text: 'Build Lifecycle', link: '/en/develop/build-lifecycle' }, ], }, @@ -58,11 +58,17 @@ export default { items: [ { text: 'Introduction', link: '/en/develop/vendor-mode/' }, { text: 'Writing Package Classes', link: '/en/develop/vendor-mode/package-classes' }, - { text: 'Annotations Reference', link: '/en/develop/vendor-mode/annotations' }, { text: 'Dependency Injection', link: '/en/develop/vendor-mode/dependency-injection' }, + { text: 'Annotations Reference', link: '/en/develop/vendor-mode/annotations' }, { text: 'Lifecycle Hooks', link: '/en/develop/vendor-mode/lifecycle-hooks' }, ], }, + { + text: 'Miscellaneous', + items: [ + { text: 'PHP Source Modifications', link: '/en/develop/php-src-changes' }, + ], + }, ], '/en/contributing/': [ { diff --git a/docs/.vitepress/sidebar.zh.ts b/docs/.vitepress/sidebar.zh.ts index 8c84f6457..904c9fa68 100644 --- a/docs/.vitepress/sidebar.zh.ts +++ b/docs/.vitepress/sidebar.zh.ts @@ -33,14 +33,14 @@ export default { items: [ { text: '开发简介', link: '/zh/develop/' }, { text: '项目结构', link: '/zh/develop/structure' }, - { text: '对 PHP 源码的修改', link: '/zh/develop/php-src-changes' }, ], }, { text: '核心概念', items: [ - { text: 'Package 模型', link: '/zh/develop/package-model' }, { text: 'Registry 与插件系统', link: '/zh/develop/registry' }, + { text: 'Package 模型', link: '/zh/develop/package-model' }, + { text: 'Artifact 模型', link: '/zh/develop/artifact-model' }, { text: '构建生命周期', link: '/zh/develop/build-lifecycle' }, ], }, @@ -58,11 +58,17 @@ export default { items: [ { text: '简介', link: '/zh/develop/vendor-mode/' }, { text: '编写 Package 类', link: '/zh/develop/vendor-mode/package-classes' }, - { text: '注解参考', link: '/zh/develop/vendor-mode/annotations' }, { text: '依赖注入', link: '/zh/develop/vendor-mode/dependency-injection' }, + { text: '注解参考', link: '/zh/develop/vendor-mode/annotations' }, { text: '生命周期 Hook', link: '/zh/develop/vendor-mode/lifecycle-hooks' }, ], }, + { + text: '杂项', + items: [ + { text: '对 PHP 源码的修改', link: '/zh/develop/php-src-changes' }, + ] + } ], '/zh/contributing/': [ { diff --git a/docs/en/develop/index.md b/docs/en/develop/index.md index 717f4cd4c..1c60a2a75 100644 --- a/docs/en/develop/index.md +++ b/docs/en/develop/index.md @@ -1,4 +1,49 @@ -# Start Developing +# Developer Guide - +This section covers the StaticPHP development workflow and the foundational knowledge needed to understand how StaticPHP works under the hood. + +## Overview + +StaticPHP is a binary build tool whose core purpose is managing the build pipeline — downloading and configuring PHP source code, resolving extension dependencies, and invoking the underlying build system (e.g., Docker or a local compiler). + +From a development perspective, StaticPHP is an open framework that provides the ability to statically build PHP and other open-source tools together. The project is maintained by [@crazywhalecc](https://github.com/crazywhalecc) and [@henderkes](https://github.com/henderkes), with contributions from the community. + +You can think of StaticPHP as a typical PHP CLI project built on [symfony/console](https://symfony.com/doc/current/components/console.html). + +## Development Environment + +To get started with StaticPHP development, you'll need a PHP development environment with the required dependencies installed. + +Requirements: + +- PHP 8.4 or later +- Composer +- Git +- PHP extensions: `curl, dom, filter, mbstring, openssl, pcntl, phar, posix, sodium, tokenizer, xml, xmlwriter` + +> These PHP extensions are required for StaticPHP's `dev` environment. + +### Setup Steps + +1. Clone the repository: + + ```bash + git clone https://github.com/crazywhalecc/static-php-cli.git + cd static-php-cli + ``` + +2. Install PHP dependencies: + + ```bash + composer install + ``` + +3. Verify the setup: + + ```bash + bin/spc --version + ``` + +--- + +You can continue reading [Project Structure](./structure) to learn more about StaticPHP's framework architecture. diff --git a/docs/en/develop/package-model.md b/docs/en/develop/package-model.md index 654153659..2e9a290d7 100644 --- a/docs/en/develop/package-model.md +++ b/docs/en/develop/package-model.md @@ -1,6 +1,247 @@ # Package Model - +## Package Definition + +A Package is the core concept in StaticPHP's build system, representing a buildable/installable unit such as a PHP extension, library, or build target. + +Each Package contains build information, dependencies, and build logic, forming StaticPHP's build model. Package definitions are primarily implemented through YAML/JSON configuration files. The package configuration files for the `core` registry are located in the `config/pkg/` directory, and the corresponding build classes are in the `src/Package/` directory. + +Packages are primarily divided into four types: + +- **php-extension**: A PHP extension package containing build information and logic for a PHP extension. +- **library**: A library package containing build information and logic for build tools, dependency libraries, etc. +- **target**: A build target package representing the final build artifact, such as a PHP binary or curl binary. Inherits from the `library` package type. +- **virtual-target**: A virtual build target package representing an abstract build target that doesn't directly correspond to a build artifact, primarily used for dependency management and build scheduling. + +```yaml +{pkg-name}: + type: {pkg-type} + ... +``` + +## Artifact Definition + +An Artifact is a definition independent of Packages. It contains the source archive file or pre-built binary for building packages. Each Artifact defines download URLs, extraction methods, and build artifact file paths. Packages can reference one or more Artifacts via the `artifact` field to obtain the source or binaries needed for building. + +In simple terms, by default one Package corresponds to one Artifact; if multiple Packages share the same source, you can define a single Artifact for multiple Packages to reference. Artifact definitions are located in the `config/artifact/` directory, and the corresponding custom download/extract logic classes are in the `src/Package/Artifact/` directory. For special package types like virtual targets and PHP built-in extensions, a Package may also omit the Artifact field entirely. + +Assuming `example-library-package` is a dependency library whose source archive is hosted at `https://example.com/example-library.tar.gz`, its Package and Artifact definitions would look like this: + +```yaml +example-library-package: + type: library + artifact: + source: + type: url + url: 'https://example.com/example-library.tar.gz' +``` + +For more on Artifact definitions, see the [Artifact Model](./artifact-model) chapter. + +## php-extension Package Type + +A php-extension package represents a PHP extension. Its configuration file is located in the `config/pkg/ext/` directory, and its build class inherits from `PhpExtensionPackage` in the `src/Package/Extension/` directory. PHP extension package configurations include extension name, version, dependencies, build options, and more. + +```yaml +ext-lz4: + type: php-extension + artifact: + source: + type: git + url: 'https://github.com/kjdev/php-ext-lz4.git' + rev: master + extract: php-src/ext/lz4 + metadata: + license-files: [LICENSE] + license: MIT + depends: + - liblz4 + php-extension: + arg-type@unix: '--enable-lz4=@shared_suffix@ --with-lz4-includedir=@build_root_path@' + arg-type@windows: '--enable-lz4' +``` + +Allowed fields for `php-extension`: + +```yaml +ext-{ext-name}: # Package name must start with ext- prefix + type: php-extension + + # ── Common Fields ──────────────────────────────────────────────────────── + description: '..' # Optional, human-readable package description + lang: c # Optional, implementation language of the extension (c / c++ etc.) + frameworks: [] # Optional, list of related macOS framework dependencies + + artifact: '{artifact-name}' # Optional; when a string, references an Artifact definition + # with the same name; when an object, is an inline Artifact + # (built-in extensions don't need this field) + + # depends / suggests support @windows / @unix / @linux / @macos suffixes + depends: [] # Optional, hard dependency list (library names as-is, PHP extensions need ext- prefix) + depends@unix: [] # Optional, hard dependencies only effective on Unix platforms + depends@windows: [] # Optional, hard dependencies only effective on Windows platforms + suggests: [] # Optional, optional dependency list (same format as depends) + suggests@unix: [] + + # ── php-extension Specific Fields (nested under php-extension: object) ──── + php-extension: + # arg-type determines the form of arguments passed to ./configure, supports platform suffixes + # Supported platform suffixes: @unix (Linux + macOS), @linux, @macos, @windows + # Priority (using Linux as example): arg-type@linux > arg-type@unix > arg-type (no suffix) + # Built-in keywords: + # enable → --enable-{extname} (default value, used when not configured) + # enable-path → --enable-{extname}={buildroot} + # with → --with-{extname} + # with-path → --with-{extname}={buildroot} + # custom/none → Pass no arguments (handled by the #[CustomPhpConfigureArg] method in the PHP class) + # You can also write the full argument string directly, supporting the following placeholders: + # @build_root_path@ → BUILD_ROOT_PATH (absolute path of buildroot) + # @shared_suffix@ → Expands to =shared in shared builds, empty in static builds + # @shared_path_suffix@ → Expands to =shared,{buildroot} in shared builds, + # expands to ={buildroot} in static builds + arg-type: enable + arg-type@unix: '--enable-{extname}=@shared_suffix@' + arg-type@windows: with-path + + zend-extension: false # Optional, true indicates this is a Zend extension (e.g., opcache, xdebug) + build-shared: true # Optional, whether building as a shared extension (.so) is allowed, default true + build-static: true # Optional, whether inline static building (compiled into PHP) is allowed, default true + build-with-php: true # Optional, true means the extension is built together via the PHP source tree + # (used for built-in extensions) + + # display-name affects the php --ri argument in smoke tests and the license export display name + # If not set, defaults to the extension name (the part after ext-); if set to empty string, skips --ri check + display-name: 'My Extension' + + # os restricts the extension to be available only on specified platforms; + # platforms not in the list will be rejected for building + # Allowed values: Linux, Darwin, Windows + os: [Linux, Darwin] +``` + +## library Package Type + +A library package represents a dependency library that needs to be compiled from source (such as openssl, zlib, etc.). Its configuration file is located in the `config/pkg/lib/` directory, and its build class inherits from `LibraryPackage` in the `src/Package/Library/` directory. + +Taking openssl as an example: + +```yaml +openssl: + type: library + artifact: + source: + type: ghrel + repo: openssl/openssl + match: openssl.+\.tar\.gz + prefer-stable: true + binary: hosted + metadata: + license-files: [LICENSE.txt] + license: OpenSSL + depends: + - zlib + depends@windows: + - zlib + - jom + headers: + - openssl + static-libs@unix: + - libssl.a + - libcrypto.a + static-libs@windows: + - libssl.lib + - libcrypto.lib +``` + +Allowed fields for `library`: + +```yaml +{lib-name}: + type: library # library or target (target inherits all fields from library) + + # ── Common Fields ───────────────────────────────────────────────────────── + description: '..' # Optional, human-readable package description + license: MIT # Optional, SPDX license identifier (for license export) + lang: c # Optional, implementation language of the library (c / c++ etc.) + frameworks: [] # Optional, list of related framework tags + + artifact: '{artifact-name}' # Required; when a string, references an Artifact definition + # with the same name; when an object, is an inline Artifact + + # depends / suggests support @windows / @unix / @linux / @macos suffixes + depends: [] # Optional, hard dependency list (library names or PHP extension names with ext- prefix) + depends@unix: [] + depends@windows: [] + suggests: [] # Optional, optional dependency list (same format as depends) + + # ── library / target Specific Fields ─────────────────────────────────────── + # The following fields are used to verify that artifacts have been correctly + # installed after the build. They support @unix / @windows / @linux / @macos suffixes. + + # Verify that specified header files or directories exist under buildroot/include/ + # Relative paths are based on buildroot/include/, absolute paths are used directly + headers: + - openssl # Corresponds to buildroot/include/openssl/ + - zlib.h # Corresponds to buildroot/include/zlib.h + headers@unix: + - ffi.h + + # Verify that specified static library files exist under buildroot/lib/ + # Relative paths are based on buildroot/lib/, absolute paths are used directly + static-libs@unix: + - libssl.a + static-libs@windows: + - libssl.lib + + # Verify that specified .pc files exist under buildroot/lib/pkgconfig/ + # Only checked on non-Windows platforms (pkg-config is not applicable on Windows) + pkg-configs: + - openssl # Corresponds to buildroot/lib/pkgconfig/openssl.pc + - libssl # Auto-completes .pc suffix + + # Verify that specified executable files exist under buildroot/bin/ + # Relative paths are based on buildroot/bin/, absolute paths are used directly + static-bins: + - my-tool + + # List of directories injected into the global PATH after the package is installed. + # Path placeholders are supported (see below for details). + path: + - '{pkg_root_path}/rust/bin' + + # Environment variables set after the package is installed (overwrites existing values). + # Path placeholders are supported. + env: + MY_VAR: '{build_root_path}/lib' + + # Values appended to the end of existing environment variables after the package is installed. + # Path placeholders are supported. + append-env: + CFLAGS: ' -I{build_root_path}/include' +``` + +The following path placeholders are supported in string values of the `path`, `env`, and `append-env` fields: + +| Placeholder | Actual Path | +|---|---| +| `{build_root_path}` | buildroot directory (`buildroot/`) | +| `{pkg_root_path}` | pkgroot directory (`pkgroot/`) | +| `{working_dir}` | Working directory (project root) | +| `{download_path}` | Download cache directory (`downloads/`) | +| `{source_path}` | Extracted source directory (`source/`) | +| `{php_sdk_path}` | Windows PHP SDK directory | + +## target Package Type + +A `target` package represents a final build artifact. It inherits from `library`, so it includes all definition fields of `library`. The configuration file for `target` packages is located in the `config/pkg/target/` directory, and its build class inherits from `TargetPackage` in the `src/Package/Target/` directory. + +The only difference from `library` is that a `target` package can be registered as a build target and automatically registers the build command `spc build:{target-name}`. + +## virtual-target Package Type + +Unlike `target`, a `virtual-target` may not include an `artifact`, meaning it doesn't directly correspond to a buildable entity but is instead an abstract build target, primarily used for dependency management and build scheduling. The configuration file for `virtual-target` is located in the `config/pkg/target/` directory, and its build class inherits from `TargetPackage` in the `src/Package/Target/` directory. Its definition is essentially the same as `target`, but the `artifact` field is optional and typically not set. `virtual-target` is primarily used in the following scenarios: + +- Defining an abstract build target for other packages to depend on, without directly corresponding to a buildable entity. +- Serving as a common dependency for multiple `target` packages, simplifying dependency management. + +A typical example is the `php-cli`, `php-fpm` build targets for PHP. They have no independent source code and depend on `php-src`, with the final build outcome (CLI or FPM binary) determined through build scheduling. diff --git a/docs/en/develop/registry.md b/docs/en/develop/registry.md index e21b5aa5b..110263685 100644 --- a/docs/en/develop/registry.md +++ b/docs/en/develop/registry.md @@ -1,6 +1,81 @@ # Registry & Plugin System - +## Overview + +The **Registry** is StaticPHP's core extension mechanism. Think of it as a "plugin package": a Registry consists of a declaration file (`spc.registry.yml`) and the configuration files and PHP classes it points to, describing a set of package definitions (YAML configuration) and their corresponding build logic (PHP classes). The build system loads all registered Registries at startup and merges their package definitions for use throughout the entire build process. + +StaticPHP ships with a built-in core registry (`core`) that contains all definitions for PHP and related extensions, libraries, build tools, and more. The declaration file for the `core` registry is `spc.registry.yml` in the project root, which describes the mapping between the configuration file directory (`config/pkg/`, `config/artifact/`) and the build class PSR-4 namespace (`src/Package/`). + +External Registries can only define new packages that don't already exist in `core`; they cannot override or modify existing definitions in the core registry. Depending on your needs, there are three ways to extend or modify StaticPHP's build capabilities: + +- **Modify the `core` registry**: Directly edit files under `src/Package` and `config/pkg/`, suitable when you want to contribute changes back to the StaticPHP mainline. Please read the [Contributing Guide](../contributing/) section on contributing new packages before submitting a PR. +- **Vendor Mode**: Package your custom packages as a standalone sub-registry distributed as a Composer package, suitable for private packages or scenarios where you want to reuse build logic as a library. See [Vendor Mode](./vendor-mode/) for details. +- **External Registry (`SPC_REGISTRIES`)**: Specify one or more external registry file paths via the `SPC_REGISTRIES` environment variable, which StaticPHP loads at startup. Suitable for temporary extensions or scenarios where packaging as a Composer package isn't practical, similar to external source mechanisms in other package managers. + +## Registry Declaration File + +Each Registry has a declaration file, typically named `spc.registry.yml`, located in the project root or the root of a Composer package. The file format supports YAML (`.yml` / `.yaml`) and JSON (`.json`). All paths within the file are resolved relative to the directory containing the declaration file itself. + +In source mode (direct git clone), StaticPHP loads `spc.registry.yml` in the project root as the core registry (`core`) by default. In Vendor mode, it automatically detects whether `spc.registry.yml` exists in the current Composer package root and loads it as a standalone registry. External registries specified via the `SPC_REGISTRIES` environment variable must also contain a valid declaration file. + +Below is a complete example with all available fields (based on the `core` registry): + +```yaml +# [Required] Unique registry name; loading a registry with a duplicate name is automatically skipped +name: my-registry + +# [Optional] Composer autoload file path, used when an external registry has its own dependencies +autoload: vendor/autoload.php + +# Package (library / php-extension / target) related configuration +package: + # YAML configuration file directory or specific file paths for packages, can be an array + config: + - config/pkg/lib/ + - config/pkg/target/ + - config/pkg/ext/ + # PSR-4 namespace → directory path mapping for package build classes; the loader scans all PHP classes in the directory + psr-4: + Package: src/Package + # You can also load specific classes as needed, supporting array format or {"ClassName": "file path"} mapping + # classes: + # - Package\Library\MyLib + # MyLib: src/Package/Library/MyLib.php + +# Artifact (build artifact) related configuration +artifact: + # YAML configuration file directory or specific file paths for artifacts + config: + - config/artifact/ + # PSR-4 namespace → directory path mapping for custom artifact download/extract classes + psr-4: + Package\Artifact: src/Package/Artifact + # classes: ... (same format as package.classes) + +# Doctor environment check configuration +doctor: + # PSR-4 namespace → directory path mapping for Doctor check item classes + psr-4: + StaticPHP\Doctor\Item: src/StaticPHP/Doctor/Item + # classes: ... (same format as package.classes) + +# Additional CLI command configuration +command: + # PSR-4 namespace → directory path mapping for custom command classes + psr-4: + Package\Command: src/Package\Command + # classes: ... (same format as package.classes) +``` + +Top-level field descriptions: + +| Field | Required | Description | +|---|---|---| +| `name` | ✅ | Unique registry name; loading a registry with a duplicate name is automatically skipped | +| `autoload` | | Composer autoload file path, for external registries that carry their own dependencies | +| `package` | | Package definition, including YAML config (`config`) and build classes (`psr-4` / `classes`) | +| `artifact` | | Artifact definition, including YAML config (`config`) and custom classes (`psr-4` / `classes`) | +| `doctor` | | Doctor check item definition, class loading only (`psr-4` / `classes`) | +| `command` | | Additional CLI command definition, class loading only (`psr-4` / `classes`) | + +The difference between `psr-4` and `classes`: `psr-4` scans all PHP classes in the entire directory that match the namespace rules and registers them in bulk; `classes` is used to precisely specify individual classes, supporting plain array format (`["ClassName"]`, must already be available in autoload) or key-value mapping format (`{"ClassName": "path/to/file.php"}`, the loader will automatically `require` the corresponding file). diff --git a/docs/en/develop/structure.md b/docs/en/develop/structure.md index cadef786d..6d3dfca1c 100644 --- a/docs/en/develop/structure.md +++ b/docs/en/develop/structure.md @@ -1,5 +1,84 @@ # Project Structure - +## Concepts + +StaticPHP is a CLI application built on `symfony/console`, with core code located in the `src/StaticPHP` directory. It is organized into several modules: + +- **Registry**: Manages registry data. Each registry contains multiple packages (Package), and the StaticPHP project ships with a built-in `core` registry that includes PHP and related extensions, dependencies, and more. +- **Package**: Represents a single package. There are four package types: `php-extension` (PHP extension), `library` (library), `target` (build target), and `virtual-target` (virtual build target). Each package contains build information, dependencies, and more. +- **Installer/Builder**: Handles installation and build logic for packages — executing build commands, extracting build artifacts, processing build results, etc. +- **Doctor**: Provides system environment checking, responsible for installing and verifying system-level dependencies such as `make`, `cmake`, `autoconf`, and more. +- **Runtime/Executor**: Contains runtime-related utility classes, such as shell command execution and CMake build execution. +- **Toolchain**: Provides toolchain abstraction interfaces for different operating systems and environments, handling system-level differences during the build process. +- **Utils**: General-purpose utility classes, such as file system operations, logging, and OS-specific helper methods. +- **DependencyResolver**: Resolves dependencies between packages and generates build order. + +## Directory Layout + +``` +static-php-cli/ +├── bin/ # Executable entry scripts (spc, spc.ps1, setup-runtime, etc.) +├── config/ +│ ├── env.ini # Default environment variable configuration +│ ├── env.custom.ini # User-defined environment variables (overrides env.ini) +│ ├── artifact/ # Build artifact configuration (toolchain downloads, pre-built binaries, etc.) +│ └── pkg/ # Package configuration files (YAML) +│ ├── ext/ # PHP extension package config (ext-*.yml, builtin-extensions.yml) +│ ├── lib/ # Library package config (*.yml) +│ └── target/ # Build target config (php.yml, curl.yml, etc.) +├── src/ +│ ├── bootstrap.php # Application bootstrap (auto-loading, DI container, etc.) +│ ├── globals/ # Global helper functions +│ ├── Package/ # Build logic implementations for each package (PHP classes) +│ │ ├── Artifact/ # Custom download/extract logic for build artifacts +│ │ ├── Command/ # Package-level custom commands +│ │ ├── Extension/ # PHP extension build classes (ext-*.php) +│ │ ├── Library/ # Library build classes (*.php) +│ │ └── Target/ # Build target classes (php.php, curl.php, etc.) +│ └── StaticPHP/ # Framework core code +│ ├── ConsoleApplication.php # Symfony Console application entry +│ ├── Artifact/ # Build artifact download and extraction (Downloader, Extractor, etc.) +│ ├── Attribute/ # PHP attribute definitions +│ │ ├── Artifact/ # Artifact-related attributes (CustomSource, BinaryExtract, etc.) +│ │ ├── Doctor/ # Doctor-related attributes (CheckItem, FixItem, etc.) +│ │ └── Package/ # Package build-related attributes (BuildFor, BeforeStage, AfterStage, +│ │ # CustomPhpConfigureArg, PatchBeforeBuild, etc.) +│ ├── Command/ # CLI command implementations (build-libs, build-target, doctor, etc.) +│ ├── Config/ # Configuration loading and validation (PackageConfig, ArtifactConfig, etc.) +│ ├── DI/ # Dependency injection container (ApplicationContext, CallbackInvoker, etc.) +│ ├── Doctor/ # System environment checking and fixing (Doctor, CheckResult) +│ ├── Exception/ # Custom exception classes +│ ├── Package/ # Core package models and build scheduling +│ │ ├── Package.php # Base package class +│ │ ├── LibraryPackage.php # Library package type +│ │ ├── PhpExtensionPackage.php # PHP extension package type +│ │ ├── TargetPackage.php # Build target package type +│ │ ├── PackageInstaller.php # Package installer (download, extract source) +│ │ └── PackageBuilder.php # Package builder (execute build pipeline) +│ ├── Registry/ # Registry management (Registry, PackageLoader, ArtifactLoader) +│ ├── Runtime/ # Runtime utilities +│ │ ├── Executor/ # Command executors (UnixAutoconfExecutor, UnixCMakeExecutor, +│ │ │ # WindowsCMakeExecutor, Executor base class) +│ │ ├── Shell/ # Shell abstraction (UnixShell, WindowsCmd, etc.) +│ │ └── SystemTarget.php # System target information +│ ├── Toolchain/ # Toolchain abstraction (GccNative, Musl, MSVC, Zig, ClangBrew, etc.) +│ └── Util/ # General utility classes +│ ├── System/ # OS platform utilities (LinuxUtil, MacOSUtil, WindowsUtil, etc.) +│ ├── BuildRootTracker.php # buildroot file tracking +│ ├── DependencyResolver.php # Dependency resolution and build order +│ ├── FileSystem.php # File system operations +│ ├── GlobalEnvManager.php # Global environment variable management +│ ├── InteractiveTerm.php # Interactive terminal output +│ ├── LicenseDumper.php # License export +│ ├── PkgConfigUtil.php # pkg-config utility wrapper +│ ├── SourcePatcher.php # Source code patching utility +│ └── SPCConfigUtil.php # SPC configuration reader +├── tests/ # Unit tests and integration tests +├── downloads/ # Download cache directory (source packages, pre-built binaries) +├── source/ # Extracted source code directory +├── buildroot/ # Build output directory (headers, static libraries, etc.) +├── pkgroot/ # Platform-archived build artifacts +└── spc.registry.yml # core registry definition file +``` + +Note that the classes in `src/Package` are responsible for implementing the build logic of specific packages, while the classes in `src/StaticPHP` provide the core functionality of the build framework, such as command scheduling, environment checking, and toolchain abstraction. The two are decoupled: `src/Package` corresponds to the packages in the `core` registry (including PHP, extensions, libraries, and build targets), while `src/StaticPHP` is the infrastructure that supports build needs across different registries and packages. diff --git a/docs/index.md b/docs/index.md index 75a1c3940..adbe0f138 100644 --- a/docs/index.md +++ b/docs/index.md @@ -7,7 +7,7 @@ hero: tagline: "StaticPHP is a powerful tool designed for building portable executables including PHP, extensions, and more." image: src: /images/static-php_nobg.png - alt: StaticPHP Logo + alt: logo actions: - theme: brand text: Get Started diff --git a/docs/zh/develop/artifact-model.md b/docs/zh/develop/artifact-model.md new file mode 100644 index 000000000..1f38c662a --- /dev/null +++ b/docs/zh/develop/artifact-model.md @@ -0,0 +1,448 @@ +--- +outline: 'deep' +--- + +# Artifact 模型 + +Artifact 是 StaticPHP 构建系统中的一个重要概念,代表构建包所需的源码归档文件或预构建的二进制文件。每个 Artifact 定义了下载 URL、解压方式、构建产物的文件路径等信息。Package 可以通过 `artifact` 字段引用一个或多个 Artifact 来获取构建所需的源码或二进制文件。 + +## Artifact 定义 + +下面是一个简单的包含源码的 artifact 单元对象示例(curl 源码): + +```yaml +&: + source: + type: ghrel + repo: curl/curl + match: curl.+\.tar\.xz + prefer-stable: true +``` + +有两种方式定义一个 artifact 并关联到 package,一种是独立定义 artifact(如上),另一种是直接在 package 定义中内联 artifact: + +::: code-group +```yaml [内联 Artifact 定义示例] +# 该文件为 package 声明 +curl: + type: target + artifact: + source: + type: ghrel + repo: curl/curl + match: curl.+\.tar\.xz + prefer-stable: true +``` +```yaml [独立 Artifact 定义示例] +# 该文件为 artifact 声明,通常位于 config/artifact/ 目录 +curl-src: + source: + type: ghrel + repo: curl/curl + match: curl.+\.tar\.xz + prefer-stable: true +``` +```yaml [Package 引用独立 Artifact 示例] +# 该文件为 package 声明 +curl: + type: target + artifact: curl-src +``` +::: + +## 类型 + +Artifact 包含 `source`、`binary` 和 `metadata` 三个部分。 + +其中,`source` 代表源码,`binary` 代表预构建的二进制文件,`metadata` 则包含一些额外的信息(如许可证文件路径等)。`source` 和 `binary` 都支持直接定义下载 URL 的方式,也支持引用同名 Artifact 定义的方式(如上例所示)。 + +下面是一个 artifact 配置的对象格式 + +```yaml +&: + source: {source-object} # (optional) + binary: + windows-x86_64: {source-object} # (optional) + linux-x86_64: {source-object} # (optional) + linux-aarch64: {source-object} # (optional) + macos-x86_64: {source-object} # (optional) + macos-aarch64: {source-object} # (optional) + metadata: # (optional) + license: "" # (optional) SPDX + license-files: ["LICENSE"] # License files from original source dir + source-root: "subdir" # (optional) If package source is in subdir, use this to change base +``` + +下面是 `source-object` 的基本格式: + +```yaml +&: + type: "url" # Download type + # ...: Different type requires differnt keys here, read below + extract: "path/to/dir" # (optional) Change extract dir, default: `SOURCE_PATH/{artifact-name}` +``` + +## Metadata + +`metadata` 字段用于声明 Artifact 的附加信息,目前支持以下三个子字段: + +### license + +- **类型**:`string`(选填) +- **说明**:该包的开源协议标识符,遵循 [SPDX License Identifier](https://spdx.org/licenses/) 规范(如 `MIT`、`Apache-2.0`、`GPL-2.0-only`)。仅用于在构建产物的 License 汇总中标注协议类型,不影响构建逻辑。 + +```yaml +metadata: + license: MIT +``` + +### license-files + +- **类型**:`string[]`(选填) +- **说明**:License 文件的路径列表。构建完成后,框架会自动将这些文件收集到构建产物的 `license/` 目录中。路径支持两种写法: + - **相对路径**(如 `LICENSE`、`COPYING`、`gettext-runtime/intl/COPYING.LIB`):相对于该 Artifact 的源码根目录解析。 + - **`@/` 前缀路径**(如 `@/bzip2.txt`):表示框架内置的 License 文件,路径解析为 `src/globals/licenses/` 目录下的文件。适用于源码包本身不附带 License 文件(或 License 文本嵌入在其他文档中)的场景,此时可将 License 文本预先放入框架内置目录并通过 `@/` 引用。 + +目前框架内置的 License 文件有:`bzip2.txt`、`gmp.txt`、`icu.txt`、`postgresql.txt`、`sqlite.txt`、`zlib.txt`。 + +```yaml +# 常见用法:从源码目录读取 +metadata: + license-files: [LICENSE] + +# 多个 License 文件 +metadata: + license-files: [LICENSE, COPYING.LESSER] + +# 子目录中的 License 文件 +metadata: + license-files: [gettext-runtime/intl/COPYING.LIB] + +# 使用框架内置 License(源码包不含 License 文件时) +metadata: + license-files: ['@/bzip2.txt'] +``` + +### source-root + +- **类型**:`string`(选填) +- **说明**:当 Artifact 解压后,实际的源码根目录位于解压目录的子目录中时,使用该字段指定子目录名。框架在执行构建时会将工作目录切换到该子目录,而非解压后的顶层目录。 + +```yaml +# krb5 的源码解压后实际根目录在 src/ 子目录下 +metadata: + source-root: src +``` + +## 下载来源 + +Artifact 支持多种下载来源类型。你可以根据实际情况选择对应包的下载来源。 + +| 类型 | 说明 | +|---|---| +| `url` | 直接下载固定 URL,支持 `filename`(自定义本地文件名)和 `version`(手动指定版本号)字段 | +| `git` | 从 Git 仓库克隆源码,支持 `rev`(分支/标签/commit)、`submodules`(是否拉取子模块)、`extract`(解压目标路径)等字段 | +| `ghrel` | 从 GitHub Release 的 Assets 中按正则匹配下载,必填 `repo`(`owner/repo` 格式)和 `match`(文件名正则),支持 `prefer-stable`(优先稳定版)| +| `ghtar` | 从 GitHub Release 下载源码 tarball(`/releases` API),按 `match` 正则匹配 Release 名称,支持 `prefer-stable` | +| `ghtagtar` | 从 GitHub Tag 下载源码 tarball(`/tags` API),按 `match` 正则匹配 Tag 名称,支持 `prefer-stable` | +| `filelist` | 抓取指定页面的 HTML,用 `regex` 从中提取文件名和版本号,再拼接 `url` 下载,适用于有版本列表页的官方站点(如 php.net、openssl.org)| +| `pecl` | 从 PECL(pecl.php.net)下载 PHP 扩展,指定 `name`(扩展包名),支持 `prefer-stable` | +| `pie` | 从 Packagist(repo.packagist.org)下载 PHP 扩展,指定 `repo`(`vendor/package` 格式),通过 Composer dist 获取下载链接 | +| `php-release` | 从 php.net 官方下载 PHP 源码,由 `domain` 指定镜像域名,版本由构建时的 `--with-php` 参数决定 | +| `bitbuckettag` | 从 Bitbucket Tag 下载源码 tarball,指定 `repo`(`workspace/repo` 格式),自动获取最新 Tag | +| `local` | 使用本地已有目录作为源码,指定 `dirname`(本地目录路径),适用于预先放置好源码的场景 | +| `custom` | 自定义下载逻辑,由 `src/Package/Artifact/` 下对应的 PHP 类实现,可指定 `func` 调用类中的特定方法 | + +## 下载来源详情 + +### url + +直接从固定 URL 下载文件。下载完成后自动解压到指定目录。 + +- **实现类**:`StaticPHP\Artifact\Downloader\Type\Url` +- **支持能力**:仅基础下载,不支持自动检查版本更新 +- **必填**:`url` — 下载地址 +- **选填**: + - `filename` — 保存到本地的文件名(默认取 URL 末段路径) + - `version` — 手动指定版本号(该类型无法自动检测版本) + - `extract` — 解压目标目录(默认为 `SOURCE_PATH/{artifact-name}`) + +```yaml +# sqlite 使用固定 URL 下载 +artifact: + source: + type: url + url: 'https://www.sqlite.org/2024/sqlite-autoconf-3450200.tar.gz' +``` + +::: tip + +在 artifact 中,以 `http://` 或 `https://` 开头的字符串会自动扩展为 `type: url` 对象,因此大多数情况下可以直接写裸 URL 字符串。 + +```yaml +artifact: + source: 'https://www.sqlite.org/2024/sqlite-autoconf-3450200.tar.gz' +``` +::: + +--- + +### git + +从 Git 仓库克隆源码。支持两种模式:指定固定分支/Tag/commit(`rev`),或通过正则从所有分支中匹配版本号(`regex`)。 + +- **实现类**:`StaticPHP\Artifact\Downloader\Type\Git` +- **支持能力**:检查版本更新(`CheckUpdateInterface`) +- **必填**:`url` — 仓库地址 +- **选填**(`rev` 和 `regex` 至少填一个): + - `rev` — 直接克隆指定分支、Tag 或 commit hash + - `regex` — 对所有远程分支名执行正则匹配,自动选取版本最高的分支(需包含命名捕获组 `(?P...)`) + - `submodules` — 是否拉取 git submodule(布尔值) + - `extract` — 克隆目标目录 + +```yaml +# php-glfw 使用 git 克隆 master 分支 +artifact: + source: + type: git + url: 'https://github.com/mario-deluna/php-glfw' + rev: master +``` + +--- + +### ghrel + +通过 GitHub Release Assets API 下载文件。适合仓库在 Release 页面上传了预编译包或源码压缩包的情况。需要指定文件名正则来匹配 Assets 中的目标文件。 + +- **实现类**:`StaticPHP\Artifact\Downloader\Type\GitHubRelease` +- **支持能力**:检查版本更新(`CheckUpdateInterface`)、下载完整性校验(`ValidatorInterface`,校验 SHA256) +- **必填**: + - `repo` — 仓库路径,格式 `owner/repo` + - `match` — 匹配 Asset 文件名的正则(不含分隔符,如 `openssl.+\.tar\.gz`) +- **选填**: + - `prefer-stable` — 是否跳过预发布版本(默认 `true`) + - `query` — 附加到 API URL 末尾的查询字符串(如 `?per_page=5`) + - `extract` — 解压目标目录 + +```yaml +# openssl 从 GitHub Release Assets 下载 +artifact: + source: + type: ghrel + repo: openssl/openssl + match: openssl.+\.tar\.gz + prefer-stable: true +``` + +--- + +### ghtar + +通过 GitHub **Releases** API 下载源码 tarball(即 Release 页面中的 Source code 包)。与 `ghrel` 的区别在于:`ghrel` 下载 Assets,`ghtar` 下载 Release 自动生成的源码 tarball。 + +- **实现类**:`StaticPHP\Artifact\Downloader\Type\GitHubTarball` +- **支持能力**:检查版本更新(`CheckUpdateInterface`) +- **必填**:`repo` — 仓库路径,格式 `owner/repo` +- **选填**: + - `prefer-stable` — 是否跳过预发布版本(默认 `true`) + - `match` — 对 `tarball_url` 进行正则过滤(不填则取第一个) + - `query` — 附加到 API URL 末尾的查询字符串 + - `extract` — 解压目标目录 + +```yaml +# librdkafka 从 GitHub Release tarball 下载 +artifact: + source: + type: ghtar + repo: confluentinc/librdkafka +``` + +--- + +### ghtagtar + +通过 GitHub **Tags** API 下载源码 tarball。与 `ghtar` 用法相同,区别仅在于使用 `/tags` 接口而非 `/releases` 接口,适合只打 Tag 而不发布 Release 的仓库。 + +- **实现类**:`StaticPHP\Artifact\Downloader\Type\GitHubTarball`(与 `ghtar` 共用同一实现类) +- **支持能力**:检查版本更新(`CheckUpdateInterface`) +- **必填**:`repo` — 仓库路径,格式 `owner/repo` +- **选填**: + - `prefer-stable` — 是否跳过预发布版本(默认 `true`) + - `match` — 对 Tag 名称进行正则过滤(不填则取最新 Tag) + - `query` — 附加到 API URL 末尾的查询字符串 + - `extract` — 解压目标目录 + +```yaml +# brotli 通过 Tag 下载,只匹配 v1.x 系列 +artifact: + source: + type: ghtagtar + repo: google/brotli + match: 'v1\.\d.*' + +# libpng 通过 Tag 下载,匹配 v1.6.x,并增加分页参数 +artifact: + source: + type: ghtagtar + repo: pnggroup/libpng + match: v1\.6\.\d+ + query: '?per_page=150' +``` + +--- + +### filelist + +抓取一个 HTML 页面(通常是官方下载列表页),用正则从页面内容中提取文件名和版本号,然后自动选择最高稳定版本进行下载。预发布版本(含 alpha/beta/rc/dev/nightly/snapshot 关键词)会被自动跳过。 + +**适用场景**:无 GitHub、只有官网下载索引页的开源项目,如 `https://ftp.gnu.org/pub/gnu/ncurses/`。 + +- **实现类**:`StaticPHP\Artifact\Downloader\Type\FileList` +- **支持能力**:检查版本更新(`CheckUpdateInterface`) +- **必填**: + - `url` — 包含文件列表的 HTML 页面地址 + - `regex` — 用于从页面中提取文件名和版本号的 PCRE 正则(需包含命名捕获组 `(?...)` 和 `(?...)`) +- **选填**: + - `extract` — 解压目标目录 + - `download-url` — 自定义下载 URL 模板,支持 `{file}` 和 `{version}` 占位符(默认直接拼接 `url` + 文件名) + +```yaml +# ncurses 从 GNU FTP 列表页抓取最新版本 +artifact: + source: + type: filelist + url: 'https://ftp.gnu.org/pub/gnu/ncurses/' + regex: '/href="(?ncurses-(?[^"]+)\.tar\.gz)"/' + +# openssl 镜像源同样使用 filelist +artifact: + source-mirror: + type: filelist + url: 'https://www.openssl.org/source/' + regex: '/href="(?openssl-(?[^"]+)\.tar\.gz)"/' +``` + +--- + +### pecl + +从 [PECL](https://pecl.php.net)(PHP 扩展库)下载 PHP 扩展源码包。通过 PECL REST API 获取版本列表,自动选取最新稳定版。 + +- **实现类**:`StaticPHP\Artifact\Downloader\Type\PECL` +- **支持能力**:检查版本更新(`CheckUpdateInterface`) +- **必填**:`name` — PECL 包名(大小写不敏感,如 `APCu`) +- **选填**: + - `prefer-stable` — 是否只下载稳定版(默认 `true`) + - `extract` — 解压目标目录(默认解压到 `php-src/ext/{name}`) + +```yaml +# APCu 从 PECL 下载 +artifact: + source: + type: pecl + name: APCu +``` + +--- + +### pie + +从 [Packagist](https://repo.packagist.org) 下载符合 [PIE](https://github.com/php/pie) 规范的 PHP 扩展包。通过 Packagist 的 `p2/` API 获取包信息,并从 `dist` 字段下载源码。 + +- **实现类**:`StaticPHP\Artifact\Downloader\Type\PIE` +- **支持能力**:检查版本更新(`CheckUpdateInterface`) +- **必填**:`repo` — Packagist 包路径,格式 `vendor/package` +- **选填**: + - `extract` — 解压目标目录 + +```yaml +# xdebug 从 Packagist 下载 +artifact: + source: + type: pie + repo: xdebug/xdebug + +# php-spx 指定自定义解压目录 +artifact: + source: + type: pie + repo: noisebynorthwest/php-spx + extract: php-src/ext/spx +``` + +--- + +### php-release + +从 [php.net](https://www.php.net) 官方下载 PHP 源码。版本号由构建时传入的 `--with-php` 参数决定,并会自动校验 SHA256 完整性。支持传入 `git` 作为版本号以直接克隆 `php/php-src` 的 master 分支。 + +- **实现类**:`StaticPHP\Artifact\Downloader\Type\PhpRelease` +- **支持能力**:检查版本更新(`CheckUpdateInterface`)、下载完整性校验(`ValidatorInterface`,校验 SHA256) +- **必填**:`domain` — 下载镜像域名(如 `https://www.php.net` 或自定义镜像) +- **选填**: + - `extract` — 解压目标目录 + +```yaml +# php-src 官方下载,同时配置镜像 +artifact: + source: + type: php-release + domain: 'https://www.php.net' + source-mirror: + type: php-release + domain: 'https://phpmirror.static-php.dev' +``` + +--- + +### bitbuckettag + +从 Bitbucket 仓库的最新 Tag 下载源码 tarball。通过 Bitbucket REST API 获取 Tag 列表,取第一条(即最新 Tag)进行下载。 + +- **实现类**:`StaticPHP\Artifact\Downloader\Type\BitBucketTag` +- **支持能力**:仅基础下载,不支持自动检查版本更新 +- **必填**:`repo` — 仓库路径,格式 `workspace/repo` +- **选填**: + - `extract` — 解压目标目录 + +```yaml +artifact: + source: + type: bitbuckettag + repo: snappy-m-o/php-snappy +``` + +--- + +### local + +直接使用本地已有目录作为源码,不执行任何下载操作。适用于源码已预先放置到本地的场景(如离线环境、本地开发调试)。 + +- **实现类**:`StaticPHP\Artifact\Downloader\Type\LocalDir` +- **支持能力**:仅基础下载,不支持自动检查版本更新 +- **必填**:`dirname` — 本地目录绝对路径 +- **选填**: + - `extract` — 解压目标目录 + +```yaml +artifact: + source: + type: local + dirname: /path/to/local/source +``` + +--- + +### custom + +完全自定义的下载逻辑,由 `src/Package/Artifact/` 目录下对应的 PHP 类实现。如果不指定 `func`,则调用类的默认下载方法。 + +- **选填**:`func` — 调用实现类中的指定方法名 + +```yaml +artifact: + source: + type: custom +``` + diff --git a/docs/zh/develop/index.md b/docs/zh/develop/index.md index eb2f68cdd..8ed8109ff 100644 --- a/docs/zh/develop/index.md +++ b/docs/zh/develop/index.md @@ -1,4 +1,48 @@ # 开发简介 - +本章节将介绍 StaticPHP 的开发流程,以及了解 StaticPHP 内部工作原理所需的基础知识。 + +## 概述 + +StaticPHP 是一个静态二进制的构建工具,核心功能是管理构建流程,包括下载和配置 PHP 源码、处理扩展依赖、调用底层构建系统(如 Docker 或本地编译器)等。 + +从开发的角度来看,StaticPHP 本身是一个开放的框架,它提供了静态构建包括 PHP 在内的各种开源工具的能力。项目主要由 [@crazywhalecc](https://github.com/crazywhalecc) 和 [@henderkes](https://github.com/henderkes) 维护,由广大社区成员贡献代码、完善构建脚本和修复问题。 + +你可以将 StaticPHP 当作一个典型的 PHP 开发的 CLI 项目来看待,它使用了 [symfony/console](https://symfony.com/doc/current/components/console.html) 来构建命令行界面。 + +## 开发环境 + +要开始开发 StaticPHP,你需要设置一个 PHP 开发环境,安装必要的依赖,并了解项目的构建流程。 + +StaticPHP 的开发环境要求如下: + +- PHP 8.4 或更高版本 +- Composer +- Git +- PHP 扩展:`curl,dom,filter,mbstring,openssl,pcntl,phar,posix,sodium,tokenizer,xml,xmlwriter` + +> 这些 PHP 扩展是 StaticPHP 的 `dev` 环境依赖。 + +以下是一些基本步骤: + +1. 克隆项目代码: + + ```bash + git clone https://github.com/crazywhalecc/static-php-cli.git + cd static-php-cli + ``` +2. 安装 PHP 依赖: + + ```bash + composer install + ``` + +3. 运行测试: + + ```bash + bin/spc --version + ``` + +------------------------------ + +你可以继续阅读 [项目结构](./structure) 来深入了解 StaticPHP 的框架结构。 diff --git a/docs/zh/develop/package-model.md b/docs/zh/develop/package-model.md index 3960139e6..c7fa9dff5 100644 --- a/docs/zh/develop/package-model.md +++ b/docs/zh/develop/package-model.md @@ -3,3 +3,239 @@ +## Package 定义 + +Package 是 StaticPHP 构建系统中的核心概念,代表一个可构建/可安装的单元,如 PHP 扩展、库、构建目标等。 + +每个 Package 包含构建信息、依赖关系、构建逻辑等,构成了 StaticPHP 的构建模型。Package 的定义主要通过 YAML/JSON 配置文件来实现。`core` 注册表的包配置文件位于 `config/pkg/` 目录下,对应的构建类位于 `src/Package/` 目录下。 + +Package 主要分为四种类型: + +- **php-extension**:PHP 扩展包,包含 PHP 扩展的构建信息和构建逻辑。 +- **library**:库包,包含构建工具链、依赖库等的构建信息和构建逻辑。 +- **target**:构建目标包,代表最终的构建产物,如 PHP 二进制、curl 二进制等,继承自 `library` 包类型。 +- **virtual-target**:虚构建目标包,代表一个抽象的构建目标,不直接对应构建产物,主要用于依赖管理和构建调度。 + +```yaml +{pkg-name}: + type: {pkg-type} + ... +``` + +## Artifact 定义 + +Artifact 是独立于 Package 的定义,它包含构建包的源码归档文件或预构建的二进制文件。每个 Artifact 定义了下载 URL、解压方式、构建产物的文件路径等信息。Package 可以通过 `artifact` 字段引用一个或多个 Artifact 来获取构建所需的源码或二进制文件。 + +简单来说,默认情况下,一个 Package 对应一个 Artifact;如果多个 Package 共用一份源码时,可以定义一个 Artifact 供多个 Package 引用。Artifact 的定义位于 `config/artifact/` 目录下,对应的自定义下载/解压逻辑类位于 `src/Package/Artifact/` 目录下;对于虚拟目标、PHP 内置扩展等特殊包类型,Package 也可以不设置 Artifact 字段。 + +我们假设 `example-library-package` 是一个依赖库,它的源码归档文件托管在 `https://example.com/example-library.tar.gz`,则它的 Package 定义和 Artifact 定义可以如下所示: + +```yaml +example-library-package: + type: library + artifact: + source: + type: url + url: 'https://example.com/example-library.tar.gz' +``` + +更多有关 Artifact 定义的内容,请参阅 [Artifact 模型](./artifact-model) 章节。 + +## php-extension 包类型 + +php-extension 一个包代表一个 PHP 扩展,它的配置文件位于 `config/pkg/ext/` 目录下,构建类继承自 `PhpExtensionPackage`,位于 `src/Package/Extension/` 目录下。PHP 扩展包的配置文件包含扩展名称、版本、依赖关系、构建选项等信息。 + +```yaml +ext-lz4: + type: php-extension + artifact: + source: + type: git + url: 'https://github.com/kjdev/php-ext-lz4.git' + rev: master + extract: php-src/ext/lz4 + metadata: + license-files: [LICENSE] + license: MIT + depends: + - liblz4 + php-extension: + arg-type@unix: '--enable-lz4=@shared_suffix@ --with-lz4-includedir=@build_root_path@' + arg-type@windows: '--enable-lz4' +``` + +`php-extension` 允许的定义字段: + +```yaml +ext-{ext-name}: # 包名必须以 ext- 前缀开头 + type: php-extension + + # ── 通用字段 ───────────────────────────────────────────────────────────── + description: '...' # 可选,人类可读的包描述 + lang: c # 可选,扩展的实现语言(c / c++ 等) + frameworks: [] # 可选,相关macOS框架依赖列表 + + artifact: '{artifact-name}' # 可选;字符串时引用同名 Artifact 定义, + # 对象时为内联 Artifact(内置扩展无需此字段) + + # depends / suggests 支持 @windows / @unix / @linux / @macos 后缀 + depends: [] # 可选,硬依赖列表(库名直接写,PHP 扩展需加 ext- 前缀) + depends@unix: [] # 可选,仅 Unix 平台生效的硬依赖 + depends@windows: [] # 可选,仅 Windows 平台生效的硬依赖 + suggests: [] # 可选,可选依赖列表(格式同 depends) + suggests@unix: [] + + # ── php-extension 专属字段(嵌套在 php-extension: 对象中)───────────────── + php-extension: + # arg-type 决定传递给 ./configure 的参数形式,支持平台后缀 + # 支持的平台后缀:@unix(Linux + macOS)、@linux、@macos、@windows + # 优先级(以 Linux 为例):arg-type@linux > arg-type@unix > arg-type(无后缀) + # 内置关键字: + # enable → --enable-{extname}(默认值,未配置时使用) + # enable-path → --enable-{extname}={buildroot} + # with → --with-{extname} + # with-path → --with-{extname}={buildroot} + # custom/none → 不传递任何参数(由 PHP 类的 #[CustomPhpConfigureArg] 方法处理) + # 也可直接写完整参数字符串,支持以下占位符: + # @build_root_path@ → BUILD_ROOT_PATH(buildroot 绝对路径) + # @shared_suffix@ → 共享构建时展开为 =shared,静态构建时为空 + # @shared_path_suffix@ → 共享构建时展开为 =shared,{buildroot},静态构建时为 ={buildroot} + arg-type: enable + arg-type@unix: '--enable-{extname}=@shared_suffix@' + arg-type@windows: with-path + + zend-extension: false # 可选,true 表示这是 Zend 扩展(如 opcache、xdebug) + build-shared: true # 可选,是否允许构建为共享扩展(.so),默认 true + build-static: true # 可选,是否允许内联静态构建(编译进 PHP),默认 true + build-with-php: true # 可选,true 表示该扩展通过 PHP 源码树一同编译(内置扩展使用) + + # display-name 影响 smoke test 中 php --ri 的参数及许可证导出显示名称 + # 不填时默认使用扩展名(ext- 后缀部分);填空字符串则跳过 --ri 检查 + display-name: 'My Extension' + + # os 限制该扩展仅在指定平台上可用,不在列表内的平台会拒绝构建 + # 可选值:Linux、Darwin、Windows + os: [Linux, Darwin] +``` + +## library 包类型 + +library 包代表一个需要从源码编译的依赖库(如 openssl、zlib 等),其配置文件位于 `config/pkg/lib/` 目录下,构建类继承自 `LibraryPackage`,位于 `src/Package/Library/` 目录下。 + +以 openssl 为例: + +```yaml +openssl: + type: library + artifact: + source: + type: ghrel + repo: openssl/openssl + match: openssl.+\.tar\.gz + prefer-stable: true + binary: hosted + metadata: + license-files: [LICENSE.txt] + license: OpenSSL + depends: + - zlib + depends@windows: + - zlib + - jom + headers: + - openssl + static-libs@unix: + - libssl.a + - libcrypto.a + static-libs@windows: + - libssl.lib + - libcrypto.lib +``` + +`library` 允许的定义字段: + +```yaml +{lib-name}: + type: library # library 或 target(target 继承 library 的所有字段) + + # ── 通用字段 ───────────────────────────────────────────────────────────── + description: '...' # 可选,人类可读的包描述 + license: MIT # 可选,SPDX 许可证标识符(用于许可证导出) + lang: c # 可选,库的实现语言(c / c++ 等) + frameworks: [] # 可选,相关框架标签列表 + + artifact: '{artifact-name}' # 必填;字符串时引用同名 Artifact 定义,对象时为内联 Artifact + + # depends / suggests 支持 @windows / @unix / @linux / @macos 后缀 + depends: [] # 可选,硬依赖列表(库名或 ext- 前缀的 PHP 扩展名) + depends@unix: [] + depends@windows: [] + suggests: [] # 可选,可选依赖列表(格式同 depends) + + # ── library / target 专属字段 ──────────────────────────────────────────── + # 以下字段用于构建完成后验证产物是否已正确安装,支持 @unix / @windows / @linux / @macos 后缀 + + # 验证 buildroot/include/ 下是否存在指定头文件或目录 + # 相对路径基于 buildroot/include/,绝对路径直接使用 + headers: + - openssl # 对应 buildroot/include/openssl/ + - zlib.h # 对应 buildroot/include/zlib.h + headers@unix: + - ffi.h + + # 验证 buildroot/lib/ 下是否存在指定静态库文件 + # 相对路径基于 buildroot/lib/,绝对路径直接使用 + static-libs@unix: + - libssl.a + static-libs@windows: + - libssl.lib + + # 验证 buildroot/lib/pkgconfig/ 下是否存在指定 .pc 文件 + # 仅在非 Windows 平台检查(pkg-config 在 Windows 上不适用) + pkg-configs: + - openssl # 对应 buildroot/lib/pkgconfig/openssl.pc + - libssl # 自动补全 .pc 后缀 + + # 验证 buildroot/bin/ 下是否存在指定可执行文件 + # 相对路径基于 buildroot/bin/,绝对路径直接使用 + static-bins: + - my-tool + + # 包安装完成后注入到全局 PATH 的目录列表,支持路径占位符(见下方说明) + path: + - '{pkg_root_path}/rust/bin' + + # 包安装完成后设置的环境变量(覆盖已有值),支持路径占位符 + env: + MY_VAR: '{build_root_path}/lib' + + # 包安装完成后追加到已有环境变量末尾的值,支持路径占位符 + append-env: + CFLAGS: ' -I{build_root_path}/include' +``` + +`path`、`env`、`append-env` 字段的字符串值中支持以下路径占位符: + +| 占位符 | 实际路径 | +|---|---| +| `{build_root_path}` | buildroot 目录(`buildroot/`) | +| `{pkg_root_path}` | pkgroot 目录(`pkgroot/`) | +| `{working_dir}` | 工作目录(项目根目录) | +| `{download_path}` | 下载缓存目录(`downloads/`) | +| `{source_path}` | 解压源码目录(`source/`) | +| `{php_sdk_path}` | Windows PHP SDK 目录 | + +## target 包类型 + +`target` 包代表一个最终的构建产物,它继承于 `library`,所以包含 `library` 的所有定义字段。`target` 包的配置文件位于 `config/pkg/target/` 目录下,构建类继承自 `TargetPackage`,位于 `src/Package/Target/` 目录下。 + +与 `library` 的唯一区别是,`target` 包可以注册成为构建目标,且自动注册构建命令 `spc build:{target-name}`。 + +## virtual-target 包类型 + +与 `target` 不同的是,`virtual-target` 可以不包含 `artifact`,即不直接对应一个可构建的实体,而是一个抽象的构建目标,主要用于依赖管理和构建调度。`virtual-target` 的配置文件位于 `config/pkg/target/` 目录下,构建类继承自 `TargetPackage`,位于 `src/Package/Target/` 目录下。它的定义与 `target` 基本相同,但 `artifact` 字段可选且通常不设置。`virtual-target` 主要用于以下场景: + +- 定义一个抽象的构建目标,供其他包依赖,但不直接对应一个可构建的实体。 +- 作为多个 `target` 包的公共依赖,简化依赖关系管理。 + +典型例子就是 PHP 包的 `php-cli`、`php-fpm` 等构建目标,他们没有独立的源码,依赖于 `php-src`,通过构建调度来决定最终构建成 CLI 还是 FPM 二进制。 diff --git a/docs/zh/develop/registry.md b/docs/zh/develop/registry.md index d617ebf0b..7bc8fc54c 100644 --- a/docs/zh/develop/registry.md +++ b/docs/zh/develop/registry.md @@ -3,3 +3,82 @@ +## 概述 + +**Registry(注册表)** 是 StaticPHP 的核心扩展机制。你可以把它理解成一个"插件包":一个 Registry 由一个声明文件(`spc.registry.yml`)和它所指向的配置文件、PHP 类共同组成,描述了一组包的定义(YAML 配置)和对应的构建逻辑(PHP 类)。构建系统在启动时会加载所有已注册的 Registry,将它们的包定义合并后用于整个构建流程。 + +StaticPHP 本身携带一个内置的核心注册表(`core`),其中包含了 PHP 及相关扩展、库、构建工具等的全部定义。`core` 注册表的声明文件即项目根目录下的 `spc.registry.yml`,它描述了配置文件目录(`config/pkg/`、`config/artifact/`)和构建类的 PSR-4 命名空间(`src/Package/`)之间的映射关系。 + +外部 Registry 只能定义 `core` 中尚不存在的新包,不能覆盖或修改核心注册表中已有的定义。根据你的需求,有以下三种方式来扩展或修改 StaticPHP 的构建能力: + +- **修改 `core` 注册表**:直接修改 `src/Package` 和 `config/pkg/` 下的文件,适用于希望将改动合并回 StaticPHP 主线的情况。请先阅读 [贡献指南](../contributing/) 中关于贡献新包的部分,再提交 PR。 +- **Vendor 模式**:将自定义包封装为一个独立的子注册表,以 Composer 包的形式分发,适用于需要私有包或希望以库的形式复用构建逻辑的场景。详见 [Vendor 模式](./vendor-mode/)。 +- **外部注册表(`SPC_REGISTRIES`)**:通过环境变量 `SPC_REGISTRIES` 指定一个或多个外部注册表文件的路径,StaticPHP 会在启动时加载它们。适用于临时扩展或不便打包为 Composer 包的场景,与其他包管理器的外部源机制类似。 + +## Registry 定义文件 + +每个 Registry 都有一个声明文件,通常命名为 `spc.registry.yml`,位于项目根目录或 Composer 包的根目录下。文件格式支持 YAML(`.yml` / `.yaml`)和 JSON(`.json`)。文件中所有路径均相对于声明文件自身所在目录解析。 + +StaticPHP 在源码模式(直接 git clone)下,会默认加载项目根目录下的 `spc.registry.yml` 作为核心注册表(`core`)。在 Vendor 模式下,会自动检测当前 Composer 包根目录下是否存在 `spc.registry.yml`,如果存在则加载为一个独立的注册表。通过 `SPC_REGISTRIES` 环境变量指定的外部注册表也必须包含一个有效的声明文件。 + +下面是一个包含所有可用字段的完整示例(参照 `core` 注册表): + +```yaml +# [必填] 注册表唯一名称,重复加载同名注册表时会自动跳过 +name: my-registry + +# [可选] Composer autoload 文件路径,外部注册表有自己的依赖时使用 +autoload: vendor/autoload.php + +# 包(library / php-extension / target)相关配置 +package: + # 包的 YAML 配置文件目录或具体文件路径,可以是数组 + config: + - config/pkg/lib/ + - config/pkg/target/ + - config/pkg/ext/ + # 包构建类的 PSR-4 命名空间 → 目录路径映射,加载器会扫描目录下所有 PHP 类 + psr-4: + Package: src/Package + # 也可以按需加载指定的类,支持数组格式或 {"类名": "文件路径"} 映射格式 + # classes: + # - Package\Library\MyLib + # MyLib: src/Package/Library/MyLib.php + +# 构建产物(Artifact)相关配置 +artifact: + # Artifact 的 YAML 配置文件目录或具体文件路径 + config: + - config/artifact/ + # Artifact 自定义下载/解压类的 PSR-4 命名空间 → 目录路径映射 + psr-4: + Package\Artifact: src/Package/Artifact + # classes: ...(同 package.classes 格式) + +# Doctor 环境检查项配置 +doctor: + # Doctor 检查项类的 PSR-4 命名空间 → 目录路径映射 + psr-4: + StaticPHP\Doctor\Item: src/StaticPHP/Doctor/Item + # classes: ...(同 package.classes 格式) + +# 额外的 CLI 命令配置 +command: + # 自定义命令类的 PSR-4 命名空间 → 目录路径映射 + psr-4: + Package\Command: src/Package/Command + # classes: ...(同 package.classes 格式) +``` + +各顶层字段说明: + +| 字段 | 必填 | 说明 | +|---|---|---| +| `name` | ✅ | 注册表唯一名称,重复加载同名注册表时自动跳过 | +| `autoload` | | Composer autoload 文件路径,适用于外部注册表携带自己的依赖时 | +| `package` | | 包定义,含 YAML 配置(`config`)和构建类(`psr-4` / `classes`) | +| `artifact` | | Artifact 定义,含 YAML 配置(`config`)和自定义类(`psr-4` / `classes`) | +| `doctor` | | Doctor 检查项定义,仅含类加载(`psr-4` / `classes`) | +| `command` | | 额外的 CLI 命令定义,仅含类加载(`psr-4` / `classes`) | + +其中 `psr-4` 和 `classes` 的区别:`psr-4` 会扫描整个目录下所有符合命名空间规则的 PHP 类并批量注册;`classes` 则用于精确指定某几个类,支持纯数组格式(`["ClassName"]`,需已在 autoload 中可用)或键值映射格式(`{"ClassName": "path/to/file.php"}`,加载器会自动 `require` 对应文件)。 diff --git a/docs/zh/develop/structure.md b/docs/zh/develop/structure.md index dde6e68f4..714f4c3c0 100644 --- a/docs/zh/develop/structure.md +++ b/docs/zh/develop/structure.md @@ -1,4 +1,85 @@ # 项目结构 - +## 概念 + +StaticPHP 本身是一个基于 `symfony/console` 的 CLI 应用,核心代码位于 `src/StaticPHP` 目录下。 +它主要分为几个模块: + +- **Registry**:负责管理注册表数据,每个注册表含有多个包(Package),StaticPHP 项目本身内置一个 `core` 注册表,包含 PHP 及相关扩展、依赖等。 +- **Package**:代表一个包,包的种类有四种:`php-extension`(PHP 扩展)、`library`(库)、`target`(构建目标)、`virtual-target`(虚构建目标)。每个包包含构建信息、依赖关系等。 +- **Installer/Builder**:负责处理包的安装和构建逻辑,调用构建命令、解压构建产物、处理构建结果等。 +- **Doctor**:提供系统环境检查功能,负责安装和检查系统层面依赖的工具、需要的文件等,如 `make`、`cmake`、`autoconf` 等。 +- **Runtime/Executor**:包含运行时相关的工具类,如执行 shell 命令、执行 CMake 构建等。 +- **Toolchain**:对不同操作系统及环境,提供对应系统的工具链抽象接口,负责处理构建过程中与系统环境相关的差异。 +- **Utils**:一些通用的工具类,如文件系统操作、日志记录、操作系统相关助手方法等。 +- **DependencyResolver**:负责解析包之间的依赖关系,生成构建顺序等。 + +## 目录结构 + +``` +static-php-cli/ +├── bin/ # 可执行入口脚本(spc、spc.ps1、setup-runtime 等) +├── config/ +│ ├── env.ini # 默认环境变量配置 +│ ├── env.custom.ini # 用户自定义环境变量(覆盖 env.ini) +│ ├── artifact/ # 构建产物配置(下载工具链、预构建二进制等) +│ └── pkg/ # 包配置文件(YAML) +│ ├── ext/ # PHP 扩展包配置(ext-*.yml、builtin-extensions.yml) +│ ├── lib/ # 库包配置(*.yml) +│ └── target/ # 构建目标配置(php.yml、curl.yml 等) +├── src/ +│ ├── bootstrap.php # 应用引导(注册自动加载、DI 容器等) +│ ├── globals/ # 全局辅助函数 +│ ├── Package/ # 各包的构建逻辑实现(PHP 类) +│ │ ├── Artifact/ # 构建产物的自定义下载/解压逻辑 +│ │ ├── Command/ # 包级别自定义命令 +│ │ ├── Extension/ # PHP 扩展构建类(ext-*.php) +│ │ ├── Library/ # 库构建类(*.php) +│ │ └── Target/ # 构建目标类(php.php、curl.php 等) +│ └── StaticPHP/ # 框架核心代码 +│ ├── ConsoleApplication.php # Symfony Console 应用入口 +│ ├── Artifact/ # 构建产物下载与解压(Downloader、Extractor 等) +│ ├── Attribute/ # PHP 注解定义 +│ │ ├── Artifact/ # 产物相关注解(CustomSource、BinaryExtract 等) +│ │ ├── Doctor/ # Doctor 相关注解(CheckItem、FixItem 等) +│ │ └── Package/ # 包构建相关注解(BuildFor、BeforeStage、AfterStage、 +│ │ # CustomPhpConfigureArg、PatchBeforeBuild 等) +│ ├── Command/ # CLI 命令实现(build-libs、build-target、doctor 等) +│ ├── Config/ # 配置加载与验证(PackageConfig、ArtifactConfig 等) +│ ├── DI/ # 依赖注入容器(ApplicationContext、CallbackInvoker) +│ ├── Doctor/ # 系统环境检查与修复(Doctor、CheckResult) +│ ├── Exception/ # 自定义异常类 +│ ├── Package/ # 包核心模型与构建调度 +│ │ ├── Package.php # 包基类 +│ │ ├── LibraryPackage.php # 库包类型 +│ │ ├── PhpExtensionPackage.php # PHP 扩展包类型 +│ │ ├── TargetPackage.php # 构建目标包类型 +│ │ ├── PackageInstaller.php # 包安装器(下载、解压源码) +│ │ └── PackageBuilder.php # 包构建器(执行构建流程) +│ ├── Registry/ # 注册表管理(Registry、PackageLoader、ArtifactLoader) +│ ├── Runtime/ # 运行时工具 +│ │ ├── Executor/ # 命令执行器(UnixAutoconfExecutor、UnixCMakeExecutor、 +│ │ │ # WindowsCMakeExecutor、Executor 基类) +│ │ ├── Shell/ # Shell 抽象(UnixShell、WindowsCmd 等) +│ │ └── SystemTarget.php # 系统目标信息 +│ ├── Toolchain/ # 工具链抽象(GccNative、Musl、MSVC、Zig、ClangBrew 等) +│ └── Util/ # 通用工具类 +│ ├── System/ # 系统平台工具(LinuxUtil、MacOSUtil、WindowsUtil 等) +│ ├── BuildRootTracker.php # buildroot 文件追踪 +│ ├── DependencyResolver.php # 依赖解析与构建顺序 +│ ├── FileSystem.php # 文件系统操作 +│ ├── GlobalEnvManager.php # 全局环境变量管理 +│ ├── InteractiveTerm.php # 交互式终端输出 +│ ├── LicenseDumper.php # 开源协议导出 +│ ├── PkgConfigUtil.php # pkg-config 工具封装 +│ ├── SourcePatcher.php # 源码补丁工具 +│ └── SPCConfigUtil.php # SPC 配置读取工具 +├── tests/ # 单元测试与集成测试 +├── downloads/ # 下载缓存目录(源码包、预构建二进制) +├── source/ # 解压后的源码目录 +├── buildroot/ # 构建输出目录(头文件、静态库等) +├── pkgroot/ # 按平台归档的构建产物 +└── spc.registry.yml # core 注册表定义文件 +``` + +需要注意的是,`src/Package` 目录下的类主要负责实现具体包的构建逻辑,而 `src/StaticPHP` 目录下的类则提供了构建框架的核心功能,如命令调度、环境检查、工具链抽象等,两者是解耦的。`src/Package` 对应的是 `core` 注册表中的包,其中包含 PHP 及相关扩展、库、构建目标等的具体实现,而 `src/StaticPHP` 则是整个构建系统的基础设施,支持不同注册表和包的构建需求。