Add deps-map component and related command

This commit is contained in:
crazywhalecc
2026-04-20 11:30:40 +08:00
parent 05900c2d6c
commit c39155898a
11 changed files with 1466 additions and 96 deletions

1
.gitignore vendored
View File

@@ -53,6 +53,7 @@ packlib_files.txt
/docs/.vitepress/dist/
/docs/.vitepress/cache/
/docs/.vitepress/ext-data.json
/docs/.vitepress/deps-data.json
package-lock.json
pnpm-lock.yaml

View File

@@ -0,0 +1,589 @@
<template>
<div class="deps-map">
<!-- Warning if data missing -->
<div v-if="missing" class="warning custom-block" style="margin-bottom: 16px">
<p class="custom-block-title">WARNING</p>
<p>Dependency data not generated yet. Run <code>bin/spc dev:gen-deps-data</code> to generate it.</p>
</div>
<template v-else>
<!-- Toolbar -->
<div class="deps-toolbar">
<input
class="deps-search"
v-model="searchText"
:placeholder="i18n.searchPlaceholder"
@input="selectedPkg = null"
/>
<div class="deps-filters">
<div class="filter-group">
<button
v-for="t in typeOptions"
:key="t.value"
:class="['filter-btn', { active: selectedType === t.value }]"
@click="selectedType = t.value; selectedPkg = null"
>{{ t.label }}</button>
</div>
<div class="filter-group">
<button
v-for="p in platformOptions"
:key="p.value"
:class="['filter-btn', { active: selectedPlatform === p.value }]"
@click="selectedPlatform = p.value"
>{{ p.label }}</button>
</div>
</div>
</div>
<div class="deps-layout">
<!-- Package list -->
<div class="deps-list">
<div v-if="filteredPackages.length === 0" class="no-results">{{ i18n.noResults }}</div>
<div
v-for="pkg in filteredPackages"
:key="pkg.name"
:class="['pkg-item', { selected: selectedPkg === pkg.name }]"
@click="selectPkg(pkg.name)"
>
<span class="pkg-name">{{ pkg.name }}</span>
<span :class="['pkg-badge', pkg.type === 'php-extension' ? 'badge-ext' : 'badge-lib']">
{{ typeLabel(pkg.type) }}
</span>
</div>
</div>
<!-- Detail panel -->
<div class="deps-detail" v-if="selectedPkg && selectedPkgData">
<h3 class="detail-title">{{ selectedPkg }}</h3>
<span :class="['detail-type-badge', selectedPkgData.type === 'php-extension' ? 'badge-ext' : 'badge-lib']">
{{ typeLabel(selectedPkgData.type) }}
</span>
<!-- OS support for extensions -->
<div v-if="selectedPkgData.type === 'php-extension' && selectedPkgData.os" class="detail-section">
<div class="detail-label">{{ i18n.supportedPlatforms }}</div>
<div class="detail-chips">
<span v-for="os in selectedPkgData.os" :key="os" class="chip chip-os">{{ os }}</span>
</div>
</div>
<!-- Required deps -->
<div class="detail-section">
<div class="detail-label">{{ i18n.requiredDeps }}</div>
<div class="detail-chips" v-if="currentDepends.length > 0">
<span
v-for="dep in currentDepends"
:key="dep"
:class="['chip', 'chip-required', { clickable: packages[dep] }]"
@click="packages[dep] && selectPkg(dep)"
>{{ dep }}</span>
</div>
<div v-else class="no-deps">{{ i18n.none }}</div>
</div>
<!-- Suggested deps -->
<div class="detail-section">
<div class="detail-label">{{ i18n.suggestedDeps }}</div>
<div class="detail-chips" v-if="currentSuggests.length > 0">
<span
v-for="dep in currentSuggests"
:key="dep"
:class="['chip', 'chip-suggested', { clickable: packages[dep] }]"
@click="packages[dep] && selectPkg(dep)"
>{{ dep }}</span>
</div>
<div v-else class="no-deps">{{ i18n.none }}</div>
</div>
<!-- Required by -->
<div class="detail-section">
<div class="detail-label">{{ i18n.requiredBy }}</div>
<div class="detail-chips" v-if="requiredBy.length > 0">
<span
v-for="dep in requiredBy"
:key="dep"
class="chip chip-rev clickable"
@click="selectPkg(dep)"
>{{ dep }}</span>
</div>
<div v-else class="no-deps">{{ i18n.none }}</div>
</div>
<!-- Suggested by -->
<div class="detail-section">
<div class="detail-label">{{ i18n.suggestedBy }}</div>
<div class="detail-chips" v-if="suggestedBy.length > 0">
<span
v-for="dep in suggestedBy"
:key="dep"
class="chip chip-rev-sug clickable"
@click="selectPkg(dep)"
>{{ dep }}</span>
</div>
<div v-else class="no-deps">{{ i18n.none }}</div>
</div>
<!-- Mermaid graph -->
<div class="detail-section" v-if="hasMermaid">
<div class="detail-label">{{ i18n.depGraph }}</div>
<div ref="mermaidEl" class="mermaid-wrap"></div>
</div>
</div>
<!-- Empty state -->
<div class="deps-detail deps-detail-empty" v-else>
<p>{{ i18n.selectHint }}</p>
</div>
</div>
</template>
</div>
</template>
<script setup>
import { ref, computed, watch, onMounted, nextTick } from 'vue'
import { useData } from 'vitepress'
import { data as depsData } from '../deps-map.data.js'
const { lang, isDark } = useData()
const missing = depsData.missing
const packages = depsData.packages ?? {}
// --- i18n ---
const I18N = {
zh: {
searchPlaceholder: '搜索包名...',
noResults: '未找到包。',
selectHint: '← 选择一个包以查看其依赖关系。',
supportedPlatforms: '支持的平台',
requiredDeps: '必需依赖',
suggestedDeps: '可选依赖',
requiredBy: '被哪些包依赖',
suggestedBy: '被哪些包建议',
depGraph: '依赖关系图',
none: '无',
},
en: {
searchPlaceholder: 'Search package...',
noResults: 'No packages found.',
selectHint: '← Select a package to view its dependency details.',
supportedPlatforms: 'Supported Platforms',
requiredDeps: 'Required Dependencies',
suggestedDeps: 'Suggested Dependencies',
requiredBy: 'Required By',
suggestedBy: 'Suggested By',
depGraph: 'Dependency Graph',
none: 'None',
},
}
const i18n = computed(() => I18N[lang.value] ?? I18N.en)
// --- State ---
const searchText = ref('')
const selectedType = ref('all')
const selectedPlatform = ref('linux')
const selectedPkg = ref(null)
const mermaidEl = ref(null)
// --- Options ---
const typeOptions = computed(() => [
{ value: 'all', label: lang.value === 'zh' ? '全部' : 'All' },
{ value: 'php-extension', label: lang.value === 'zh' ? '扩展' : 'Extensions' },
{ value: 'library', label: lang.value === 'zh' ? '库' : 'Libraries' },
])
const platformOptions = [
{ value: 'linux', label: 'Linux' },
{ value: 'macos', label: 'macOS' },
{ value: 'windows', label: 'Windows' },
]
function typeLabel(type) {
if (type === 'php-extension') return 'ext'
if (type === 'library') return 'lib'
return type
}
// --- Package list ---
const allPackages = computed(() =>
Object.entries(packages).map(([name, data]) => ({ name, ...data }))
)
const filteredPackages = computed(() => {
let list = allPackages.value
if (selectedType.value !== 'all') {
list = list.filter(p => p.type === selectedType.value)
}
if (searchText.value.trim()) {
const q = searchText.value.trim().toLowerCase()
list = list.filter(p => p.name.toLowerCase().includes(q))
}
return list
})
// --- Selected package ---
const selectedPkgData = computed(() =>
selectedPkg.value ? (packages[selectedPkg.value] ?? null) : null
)
const currentDepends = computed(() =>
selectedPkgData.value?.platforms?.[selectedPlatform.value]?.depends ?? []
)
const currentSuggests = computed(() =>
selectedPkgData.value?.platforms?.[selectedPlatform.value]?.suggests ?? []
)
const requiredBy = computed(() => {
if (!selectedPkg.value) return []
const name = selectedPkg.value
const plat = selectedPlatform.value
return Object.entries(packages)
.filter(([, d]) => (d.platforms?.[plat]?.depends ?? []).includes(name))
.map(([n]) => n)
})
const suggestedBy = computed(() => {
if (!selectedPkg.value) return []
const name = selectedPkg.value
const plat = selectedPlatform.value
return Object.entries(packages)
.filter(([, d]) => (d.platforms?.[plat]?.suggests ?? []).includes(name))
.map(([n]) => n)
})
// --- Mermaid ---
const hasMermaid = computed(
() => currentDepends.value.length > 0 || currentSuggests.value.length > 0
)
function buildMermaidCode() {
const deps = currentDepends.value
const sugs = currentSuggests.value
if (deps.length === 0 && sugs.length === 0) return ''
const safe = s => s.replace(/[^a-zA-Z0-9_]/g, '_')
const root = safe(selectedPkg.value)
const lines = ['graph LR', ` ${root}["${selectedPkg.value}"]`]
const MAX_DEPTH = 6 // max hops from root
const MAX_CHILDREN = 6 // per-node child limit for levels 2+
const visitedNodes = new Set([selectedPkg.value])
const visitedEdges = new Set()
const queue = []
// Level 1: direct required deps — solid arrows, no child limit
for (const dep of deps) {
const ek = `${selectedPkg.value}\0${dep}`
if (!visitedEdges.has(ek)) {
visitedEdges.add(ek)
lines.push(` ${root} --> ${safe(dep)}["${dep}"]`)
}
if (!visitedNodes.has(dep)) {
visitedNodes.add(dep)
queue.push({ name: dep, depth: 1 })
}
}
// BFS: levels 2MAX_DEPTH — dotted arrows, capped children per node
while (queue.length > 0) {
const { name, depth } = queue.shift()
if (depth >= MAX_DEPTH) continue
const children = packages[name]?.platforms?.[selectedPlatform.value]?.depends ?? []
for (const child of children.slice(0, MAX_CHILDREN)) {
const ek = `${name}\0${child}`
if (!visitedEdges.has(ek)) {
visitedEdges.add(ek)
lines.push(` ${safe(name)} -.-> ${safe(child)}["${child}"]`)
}
if (!visitedNodes.has(child)) {
visitedNodes.add(child)
queue.push({ name: child, depth: depth + 1 })
}
}
}
// Suggested deps from root (single level, optional dotted)
for (const sug of sugs) {
lines.push(` ${root} -. optional .-> ${safe(sug)}["${sug}"]`)
}
return lines.join('\n')
}
let mermaidLib = null
async function renderMermaid() {
if (!mermaidEl.value || !hasMermaid.value) return
const code = buildMermaidCode()
if (!code) return
try {
if (!mermaidLib) {
const m = await import('mermaid')
mermaidLib = m.default
}
mermaidLib.initialize({
startOnLoad: false,
theme: isDark.value ? 'dark' : 'default',
securityLevel: 'loose',
})
const id = 'deps-graph-' + Date.now()
const { svg } = await mermaidLib.render(id, code)
if (mermaidEl.value) {
mermaidEl.value.innerHTML = svg
}
} catch {
if (mermaidEl.value) {
mermaidEl.value.innerHTML = `<pre class="mermaid-fallback">${code}</pre>`
}
}
}
function selectPkg(name) {
selectedPkg.value = name
}
watch([selectedPkg, selectedPlatform, isDark], async () => {
await nextTick()
await renderMermaid()
})
onMounted(async () => {
await nextTick()
await renderMermaid()
})
</script>
<style scoped>
.deps-map {
font-size: 14px;
}
/* Toolbar */
.deps-toolbar {
display: flex;
flex-direction: column;
gap: 8px;
margin-bottom: 16px;
}
.deps-search {
width: 100%;
padding: 8px 12px;
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
font-size: 14px;
background: var(--vp-c-bg);
color: var(--vp-c-text-1);
outline: none;
box-sizing: border-box;
}
.deps-search:focus {
border-color: var(--vp-c-brand);
}
.deps-filters {
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.filter-group {
display: flex;
gap: 4px;
}
.filter-btn {
padding: 4px 12px;
border: 1px solid var(--vp-c-divider);
border-radius: 20px;
background: var(--vp-c-bg);
color: var(--vp-c-text-2);
font-size: 13px;
cursor: pointer;
transition: all 0.15s;
}
.filter-btn:hover {
border-color: var(--vp-c-brand);
color: var(--vp-c-brand);
}
.filter-btn.active {
background: var(--vp-c-brand);
border-color: var(--vp-c-brand);
color: #fff;
}
/* Layout */
.deps-layout {
display: flex;
gap: 16px;
min-height: 400px;
}
/* Package list */
.deps-list {
width: 260px;
flex-shrink: 0;
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
overflow-y: auto;
max-height: 600px;
}
.no-results {
padding: 16px;
color: var(--vp-c-text-3);
text-align: center;
}
.pkg-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 12px;
cursor: pointer;
border-bottom: 1px solid var(--vp-c-divider);
transition: background 0.1s;
}
.pkg-item:last-child {
border-bottom: none;
}
.pkg-item:hover {
background: var(--vp-c-bg-soft);
}
.pkg-item.selected {
background: var(--vp-c-brand-soft);
}
.pkg-name {
font-family: var(--vp-font-family-mono);
font-size: 13px;
word-break: break-all;
}
.pkg-badge {
font-size: 11px;
padding: 1px 6px;
border-radius: 10px;
flex-shrink: 0;
margin-left: 6px;
}
/* Detail panel */
.deps-detail {
flex: 1;
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 20px;
overflow-y: auto;
max-height: 600px;
}
.deps-detail-empty {
display: flex;
align-items: center;
justify-content: center;
color: var(--vp-c-text-3);
}
.detail-title {
margin: 0 0 8px 0;
font-size: 16px;
font-family: var(--vp-font-family-mono);
}
.detail-type-badge {
font-size: 12px;
padding: 2px 8px;
border-radius: 10px;
display: inline-block;
margin-bottom: 16px;
}
.detail-section {
margin-bottom: 16px;
}
.detail-label {
font-size: 12px;
font-weight: 600;
color: var(--vp-c-text-2);
text-transform: uppercase;
letter-spacing: 0.05em;
margin-bottom: 6px;
}
.detail-chips {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.no-deps {
color: var(--vp-c-text-3);
font-size: 13px;
}
/* Chips */
.chip {
font-family: var(--vp-font-family-mono);
font-size: 12px;
padding: 3px 10px;
border-radius: 12px;
border: 1px solid transparent;
display: inline-block;
}
.chip.clickable {
cursor: pointer;
transition: opacity 0.15s;
}
.chip.clickable:hover {
opacity: 0.75;
}
.chip-required {
background: var(--vp-c-danger-soft);
border-color: var(--vp-c-danger-1);
color: var(--vp-c-danger-1);
}
.chip-suggested {
background: var(--vp-c-warning-soft);
border-color: var(--vp-c-warning-1);
color: var(--vp-c-warning-1);
}
.chip-rev {
background: var(--vp-c-brand-soft);
border-color: var(--vp-c-brand-1);
color: var(--vp-c-brand-1);
}
.chip-rev-sug {
background: var(--vp-c-bg-soft);
border-color: var(--vp-c-divider);
color: var(--vp-c-text-2);
}
.chip-os {
background: var(--vp-c-bg-soft);
border-color: var(--vp-c-divider);
color: var(--vp-c-text-1);
}
/* Badges */
.badge-ext {
background: var(--vp-c-brand-soft);
color: var(--vp-c-brand-1);
}
.badge-lib {
background: var(--vp-c-tip-soft);
color: var(--vp-c-tip-1);
}
/* Mermaid */
.mermaid-wrap {
overflow-x: auto;
padding: 8px 0;
}
.mermaid-fallback {
font-size: 12px;
white-space: pre-wrap;
}
</style>

View File

@@ -0,0 +1,23 @@
import { readFileSync, existsSync } from 'node:fs'
import { resolve, dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
const __dirname = dirname(fileURLToPath(import.meta.url))
const DATA_PATH = resolve(__dirname, 'deps-data.json')
export default {
watch: [DATA_PATH],
load() {
if (!existsSync(DATA_PATH)) {
console.warn(
'[deps-map.data.js] deps-data.json not found. ' +
'Run `bin/spc dev:gen-deps-data` to generate it.'
)
return { packages: {}, missing: true }
}
const raw = JSON.parse(readFileSync(DATA_PATH, 'utf-8'))
return { packages: raw.packages ?? {}, missing: false }
},
}

View File

@@ -3,9 +3,13 @@ import DefaultTheme from 'vitepress/theme'
import {inBrowser, useData} from "vitepress";
import {watchEffect} from "vue";
import './style.css';
import DepsMap from '../components/DepsMap.vue';
export default {
...DefaultTheme,
enhanceApp({ app }) {
app.component('DepsMap', DepsMap)
},
setup() {
const { lang } = useData()
watchEffect(() => {

View File

@@ -22,26 +22,26 @@ spc download [artifacts] [options]
### Options
| Option | Short | Description |
|---|---|---|
| `--for-extensions=<list>` | `-e` | Download artifacts needed by the given extensions |
| `--for-libs=<list>` | `-l` | Download artifacts needed by the given libraries |
| `--for-packages=<list>` | | Download artifacts needed by the given packages |
| `--without-suggests` | | Skip suggested packages when using `--for-extensions` |
| `--clean` | | Delete existing download cache before fetching |
| `--with-php=<ver>` | | PHP version in `major.minor` format (default: `8.4`) |
| `--prefer-binary` | `-p` | Prefer pre-built binaries over source archives |
| `--prefer-source` | | Prefer source archives over pre-built binaries |
| `--source-only` | | Only download source artifacts |
| `--binary-only` | | Only download binary artifacts |
| `--parallel=<n>` | `-P` | Number of parallel downloads (default: `1`) |
| `--retry=<n>` | `-R` | Number of retries on failure (default: `0`) |
| `--ignore-cache=<list>` | | Force re-download the specified artifacts |
| `--no-alt` | | Do not use alternative mirror URLs |
| `--no-shallow-clone` | | Do not clone git repositories shallowly |
| `--custom-url=<src:url>` | `-U` | Override the download URL for a source |
| `--custom-git=<src:branch:url>` | `-G` | Override with a custom git repository |
| `--custom-local=<src:path>` | `-L` | Use a local path as a source override |
| Option | Short | Description |
|---------------------------------|-------|-------------------------------------------------------|
| `--for-extensions=<list>` | `-e` | Download artifacts needed by the given extensions |
| `--for-libs=<list>` | `-l` | Download artifacts needed by the given libraries |
| `--for-packages=<list>` | | Download artifacts needed by the given packages |
| `--without-suggests` | | Skip suggested packages when using `--for-extensions` |
| `--clean` | | Delete existing download cache before fetching |
| `--with-php=<ver>` | | PHP version in `major.minor` format (default: `8.4`) |
| `--prefer-binary` | `-p` | Prefer pre-built binaries over source archives |
| `--prefer-source` | | Prefer source archives over pre-built binaries |
| `--source-only` | | Only download source artifacts |
| `--binary-only` | | Only download binary artifacts |
| `--parallel=<n>` | `-P` | Number of parallel downloads (default: `1`) |
| `--retry=<n>` | `-R` | Number of retries on failure (default: `0`) |
| `--ignore-cache=<list>` | | Force re-download the specified artifacts |
| `--no-alt` | | Do not use alternative mirror URLs |
| `--no-shallow-clone` | | Do not clone git repositories shallowly |
| `--custom-url=<src:url>` | `-U` | Override the download URL for a source |
| `--custom-git=<src:branch:url>` | `-G` | Override with a custom git repository |
| `--custom-local=<src:path>` | `-L` | Use a local path as a source override |
### Examples
@@ -81,67 +81,73 @@ All `download` options are also available on `build:php` with the `--dl-` prefix
These flags apply only to the combined `build:php` target. To build a specific SAPI in isolation, use its dedicated command (e.g. `spc build:php-cli`).
| Option | Description |
|---|---|
| `--build-cli` | Build the `cli` SAPI (`php` / `php.exe`) |
| `--build-fpm` | Build `php-fpm` (Linux and macOS only) |
| `--build-cgi` | Build `php-cgi` |
| `--build-micro` | Build `micro.sfx` |
| `--build-embed` | Build the embed static library (`libphp.a` / `php8embed.lib`) |
| `--build-frankenphp` | Build the FrankenPHP binary |
| Option | Description |
|----------------------|---------------------------------------------------------------|
| `--build-cli` | Build the `cli` SAPI (`php` / `php.exe`) |
| `--build-fpm` | Build `php-fpm` (Linux and macOS only) |
| `--build-cgi` | Build `php-cgi` |
| `--build-micro` | Build `micro.sfx` |
| `--build-embed` | Build the embed static library (`libphp.a` / `php8embed.lib`) |
| `--build-frankenphp` | Build the FrankenPHP binary |
### Common Build Options {#common-build-options}
| Option | Short | Description |
|---|---|---|
| `--no-strip` | | Keep debug symbols; do not strip the binary |
| `--with-upx-pack` | | Compress the output binary with UPX (install first with `spc install-pkg upx`; Linux and Windows only) |
| `--disable-opcache-jit` | | Disable OPcache JIT |
| `--with-config-file-path=<path>` | | Directory where PHP looks for `php.ini` (default: `/usr/local/etc/php`) |
| `--with-config-file-scan-dir=<path>` | | Directory PHP scans for additional `.ini` files (default: `/usr/local/etc/php/conf.d`) |
| `--with-hardcoded-ini=<k=v>` | `-I` | Bake an INI setting into the binary at compile time (repeatable) |
| `--enable-zts` | | Enable thread-safe (ZTS) mode |
| `--no-smoke-test` | | Skip the post-build smoke tests |
| `--with-suggests` | `-L` / `-E` | Also resolve and install suggested packages |
| `--with-packages=<list>` | | Additional packages to install alongside the build |
| `--no-download` | | Skip the download step (use existing cached files) |
| `--with-added-patch=<file>` | `-P` | Inject an external PHP patch script (repeatable) |
| `--build-shared=<list>` | `-D` | Extensions to compile as shared `.so` / `.dll` instead of static |
| Option | Short | Description |
|--------------------------------------|-------|--------------------------------------------------------------------------------------------------------|
| `--no-strip` | | Keep debug symbols; do not strip the binary |
| `--with-upx-pack` | | Compress the output binary with UPX (install first with `spc install-pkg upx`; Linux and Windows only) |
| `--disable-opcache-jit` | | Disable OPcache JIT |
| `--with-config-file-path=<path>` | | Directory where PHP looks for `php.ini` (default: `/usr/local/etc/php`) |
| `--with-config-file-scan-dir=<path>` | | Directory PHP scans for additional `.ini` files (default: `/usr/local/etc/php/conf.d`) |
| `--with-hardcoded-ini=<k=v>` | `-I` | Bake an INI setting into the binary at compile time (repeatable) |
| `--enable-zts` | | Enable thread-safe (ZTS) mode |
| `--no-smoke-test` | | Skip the post-build smoke tests |
| `--with-suggests` | | Also resolve and install suggested packages |
| `--with-packages=<list>` | | Additional packages to install alongside the build |
| `--no-download` | | Skip the download step (use existing cached files) |
| `--build-shared=<list>` | `-D` | Extensions to compile as shared `.so` / `.dll` instead of static |
### micro Options {#micro-options}
| Option | Description |
|---|---|
| `--with-micro-fake-cli` | Make `micro`'s `PHP_SAPI` report `cli` instead of `micro` |
| `--without-micro-ext-test` | Disable the post-build extension test for `micro.sfx` |
| `--with-micro-logo=<path>` | Embed a custom `.ico` icon into `micro.sfx` (Windows only) |
| `--enable-micro-win32` | Build `micro.sfx` as a Win32 GUI application instead of a console app (Windows only) |
| Option | Description |
|----------------------------|--------------------------------------------------------------------------------------|
| `--with-micro-fake-cli` | Make `micro`'s `PHP_SAPI` report `cli` instead of `micro` |
| `--without-micro-ext-test` | Disable the post-build extension test for `micro.sfx` |
| `--with-micro-logo=<path>` | Embed a custom `.ico` icon into `micro.sfx` (Windows only) |
| `--enable-micro-win32` | Build `micro.sfx` as a Win32 GUI application instead of a console app (Windows only) |
### frankenphp Options {#frankenphp-options}
| Option | Description |
|---|---|
| `--enable-zts` | Required for FrankenPHP; enables thread-safe mode |
| `--with-frankenphp-app=<path>` | Embed a directory into the FrankenPHP binary |
| Option | Description |
|--------------------------------|---------------------------------------------------|
| `--enable-zts` | Required for FrankenPHP; enables thread-safe mode |
| `--with-frankenphp-app=<path>` | Embed a directory into the FrankenPHP binary |
### embed Options {#embed-options}
| Option | Description |
|---|---|
| Option | Description |
|-------------------------|-----------------------------------------------------------------------|
| `--build-shared=<list>` | Compile specific extensions as shared libraries (requires embed SAPI) |
### Download Pass-through Options {#download-options}
All downloader options are available with the `--dl-` prefix:
| Option | Description |
|---|---|
| `--dl-with-php=<ver>` | PHP version to download (default: `8.4`) |
| `--dl-prefer-binary` | Prefer pre-built binaries for dependencies |
| `--dl-parallel=<n>` | Number of parallel downloads |
| `--dl-retry=<n>` | Number of retries on failure |
| `--dl-custom-url=<src:url>` | Override a source download URL |
| `--dl-custom-git=<src:branch:url>` | Override with a custom git repository |
| Option | Description |
|------------------------------------|--------------------------------------------|
| `--dl-with-php=<ver>` | PHP version to download (default: `8.4`) |
| `--dl-prefer-binary` | Prefer pre-built binaries for dependencies |
| `--dl-parallel=<n>` | Number of parallel downloads |
| `--dl-retry=<n>` | Number of retries on failure |
| `--dl-custom-url=<src:url>` | Override a source download URL |
| `--dl-custom-git=<src:branch:url>` | Override with a custom git repository |
Downloader options passed to `build:php` are used by the automatic downloader that runs before the build.
This allows you to control the download behavior without needing a separate `spc download` command.
```bash
spc build:php "bcmath,openssl,curl" --build-cli --dl-with-php=8.3 --dl-prefer-binary --dl-parallel=4
```
### Examples
@@ -177,6 +183,42 @@ spc build:php-embed "bcmath,openssl"
spc build:frankenphp "bcmath,openssl,curl" --enable-zts
```
## build:libs
Build one or more library packages from source.
```bash
spc build:libs <libraries> [options]
```
`libraries` (required): Comma-separated list of library package names to build (e.g. `"openssl,curl,zlib"`).
All `download` options are available with the `--dl-` prefix.
### Options
| Option | Short | Description |
|---|---|---|
| `--with-suggests` | `-L`, `-E` | Also resolve and install suggested packages |
| `--with-packages=<list>` | | Additional packages to install alongside the build, comma-separated |
| `--no-download` | | Skip downloading artifacts (use existing cached files) |
### Examples
```bash
# Build a single library
spc build:libs openssl
# Build multiple libraries
spc build:libs "openssl,curl,zlib"
# Build with suggested packages included
spc build:libs openssl --with-suggests
# Skip the download step
spc build:libs openssl --no-download
```
## craft
Read a `craft.yml` and run the full build pipeline automatically.
@@ -195,10 +237,10 @@ Diagnose whether the current environment can compile PHP normally.
spc doctor [--auto-fix[=never]]
```
| Option | Description |
|---|---|
| `--auto-fix` | Automatically fix detected issues using the system package manager |
| `--auto-fix=never` | Report issues but never attempt automatic fixes |
| Option | Description |
|--------------------|--------------------------------------------------------------------|
| `--auto-fix` | Automatically fix detected issues using the system package manager |
| `--auto-fix=never` | Report issues but never attempt automatic fixes |
## dev:shell
@@ -209,3 +251,275 @@ spc dev:shell
```
Useful for compiling small programs against `libphp.a` (embed SAPI) or inspecting the build environment manually.
## check-update
Check whether newer versions are available for downloaded artifacts.
```bash
spc check-update [artifact] [options]
```
`artifact` (optional): Artifact names to check, comma-separated. Defaults to all currently downloaded artifacts.
### Options
| Option | Short | Description |
|---|---|---|
| `--json` | | Output results in JSON format |
| `--bare` | | Check without requiring the artifact to be downloaded first (old version will be `null`) |
| `--parallel=<n>` | `-p` | Number of parallel update checks (default: `10`) |
| `--with-php=<ver>` | | PHP version context in `major.minor` format (default: `8.4`) |
### Examples
```bash
# Check all downloaded artifacts
spc check-update
# Check specific artifacts
spc check-update "openssl,curl"
# Output as JSON
spc check-update --json
# Check without requiring a prior download
spc check-update "openssl" --bare
```
## dump-extensions
Analyse a Composer project and output the list of PHP extensions it requires.
```bash
spc dump-extensions [path] [options]
```
`path` (optional): Path to the project root (default: `.`).
### Options
| Option | Short | Description |
|---|---|---|
| `--format=<fmt>` | `-F` | Output format (default: `default`) |
| `--no-ext-output=<list>` | `-N` | When no extensions are found, output this default comma-separated list instead of exiting with failure |
| `--no-dev` | | Exclude dev dependencies |
| `--no-spc-filter` | `-S` | Do not apply the SPC filter when determining required extensions |
### Examples
```bash
# Analyse the current directory
spc dump-extensions
# Analyse a specific directory
spc dump-extensions /path/to/project
# Exclude dev dependencies
spc dump-extensions --no-dev
# Fall back to a default list when no extensions are found
spc dump-extensions --no-ext-output="bcmath,openssl"
```
## dump-license
Export open-source license files for artifacts.
```bash
spc dump-license [artifacts] [options]
```
`artifacts` (optional): Specific artifacts whose licenses should be dumped, comma-separated (e.g. `"php-src,openssl,curl"`).
### Options
| Option | Short | Description |
|---|---|---|
| `--for-extensions=<list>` | `-e` | Dump by extension names (automatically includes `php-src`), e.g. `"openssl,mbstring"` |
| `--for-libs=<list>` | `-l` | Dump by library names, e.g. `"openssl,zlib"` |
| `--for-packages=<list>` | `-p` | Dump by package names, e.g. `"php,libssl"` |
| `--dump-dir=<path>` | `-d` | Directory to write license files (default: `buildroot/license`) |
| `--without-suggests` | | Do not include licenses for suggested packages |
### Examples
```bash
# Dump licenses for the extensions you compiled
spc dump-license --for-extensions="bcmath,openssl,curl"
# Dump licenses for specific artifacts
spc dump-license "php-src,openssl"
# Write licenses to a custom directory
spc dump-license --for-extensions="bcmath,openssl" --dump-dir=/tmp/licenses
```
## extract
Extract downloaded artifacts to their target locations in the source tree.
```bash
spc extract [artifacts] [options]
```
`artifacts` (optional): Specific artifacts to extract, comma-separated (e.g. `"php-src,openssl,curl"`).
### Options
| Option | Short | Description |
|---|---|---|
| `--for-extensions=<list>` | `-e` | Extract artifacts needed by the given extensions, e.g. `"openssl,mbstring"` |
| `--for-libs=<list>` | `-l` | Extract artifacts needed by the given libraries, e.g. `"libcares,openssl"` |
| `--for-packages=<list>` | | Extract artifacts needed by the given packages, e.g. `"php,libssl,libcurl"` |
| `--without-suggests` | | Skip suggested packages when using `--for-extensions` |
| `--source-only` | | Force extraction from source even if a pre-built binary is available |
### Examples
```bash
# Extract artifacts for a set of extensions
spc extract --for-extensions="bcmath,openssl,curl"
# Extract specific artifacts
spc extract "php-src,openssl"
# Force source extraction
spc extract --for-extensions="bcmath,openssl" --source-only
```
## install-pkg
Install additional helper packages (e.g. UPX, toolchains). Aliases: `i`, `install-package`.
```bash
spc install-pkg <package> [options]
```
`package` (required): The name of the package to install.
All `download` options are available with the `--dl-` prefix.
### Examples
```bash
# Install the UPX compressor
spc install-pkg upx
```
## micro:combine
Merge `micro.sfx` with a PHP or PHAR file to produce a standalone executable.
```bash
spc micro:combine <file> [options]
```
`file` (required): Path to the PHP or PHAR file to combine.
### Options
| Option | Short | Description |
|---|---|---|
| `--with-micro=<path>` | `-M` | Path to a custom `micro.sfx` (default: `buildroot/bin/micro.sfx`) |
| `--with-ini-set=<k=v>` | `-I` | Inject an INI setting into the binary (repeatable) |
| `--with-ini-file=<path>` | `-N` | Inject INI settings from a file |
| `--output=<name>` | `-O` | Output file name (default: `my-app`) |
### Examples
```bash
# Combine a PHP script
spc micro:combine app.php
# Combine a PHAR with a custom output name
spc micro:combine app.phar --output my-app
# Inject INI settings
spc micro:combine app.php -I "memory_limit=512M" -I "disable_functions=system"
# Inject from an INI file
spc micro:combine app.php --with-ini-file=custom.ini
# Use a custom micro.sfx
spc micro:combine app.php --with-micro=/path/to/micro.sfx
```
## reset
Clean build directories and reset the build environment.
```bash
spc reset [options]
```
By default, removes `buildroot/` and `source/`.
### Options
| Option | Short | Description |
|---|---|---|
| `--with-pkgroot` | | Also remove the `pkgroot/` directory |
| `--with-download` | | Also remove the `downloads/` directory |
| `--yes` | `-y` | Skip the confirmation prompt |
### Examples
```bash
# Clean build directories (will prompt for confirmation)
spc reset
# Also clear the download cache
spc reset --with-download
# Full clean without prompting
spc reset --with-pkgroot --with-download --yes
```
## spc-config
Output compiler and linker flags needed to link your own program against the PHP embed static library.
```bash
spc spc-config [extensions] [options]
```
`extensions` (optional): Comma-separated list of extensions to include.
### Options
| Option | Short | Description |
|---|---|---|
| `--with-libs=<list>` | | Additional libraries to include, comma-separated |
| `--with-packages=<list>` | `-p` | Additional packages to include, comma-separated |
| `--with-suggested-libs` | `-L` | Include suggested libraries |
| `--with-suggests` | | Include all suggested packages |
| `--with-suggested-exts` | `-E` | Include suggested extensions |
| `--includes` | | Output only `-I` include paths (`CFLAGS`) |
| `--libs` | | Output only `-L` and `-l` linker flags (`LDFLAGS` + `LIBS`) |
| `--libs-only-deps` | | Output only `-l` dependency flags |
| `--absolute-libs` | | Use absolute paths for library files |
| `--no-php` | | Do not link against the PHP library |
### Examples
```bash
# Output full compiler + linker flags
spc spc-config "bcmath,openssl,curl"
# Output include paths only
spc spc-config "bcmath,openssl" --includes
# Output linker flags only
spc spc-config "bcmath,openssl" --libs
# Use absolute library paths
spc spc-config "bcmath,openssl" --libs --absolute-libs
```
Enter an interactive shell with StaticPHP's build environment pre-loaded (compiler wrappers, `buildroot/`, `pkgroot/` paths, etc. on `PATH`).
```bash
spc dev:shell
```
Useful for compiling small programs against `libphp.a` (embed SAPI) or inspecting the build environment manually.

View File

@@ -1,4 +1,19 @@
# Dependency Table
---
aside: false
---
<!-- TODO: Auto-generated by `bin/spc dev:gen-ext-dep-docs` and `dev:gen-lib-dep-docs`.
Placeholder until commands are implemented in v3. -->
# Dependency Map
This page lists all supported packages (extensions and libraries) together with their dependency relationships.
- **Required Dependencies**: packages that are always built alongside the selected package.
- **Suggested Dependencies**: packages that are not built by default; enable them with `--with-suggests` or by specifying them manually.
- **Required By / Suggested By**: which other packages need or suggest this package.
Run the following command to generate the dependency data (source mode required):
```bash
bin/spc dev:gen-deps-data
```
<DepsMap />

View File

@@ -92,30 +92,29 @@ spc build:php <extensions> [options]
### 通用构建选项 {#common-build-options}
| 选项 | 缩写 | 说明 |
|---|---|---|
| `--no-strip` | | 保留调试符号,不精简二进制 |
| `--with-upx-pack` | | 用 UPX 压缩产物(需先 `spc install-pkg upx`;仅 Linux 和 Windows|
| `--disable-opcache-jit` | | 禁用 OPcache JIT |
| `--with-config-file-path=<path>` | | PHP 查找 `php.ini` 的目录(默认:`/usr/local/etc/php`|
| `--with-config-file-scan-dir=<path>` | | PHP 扫描追加 `.ini` 文件的目录(默认:`/usr/local/etc/php/conf.d`|
| `--with-hardcoded-ini=<k=v>` | `-I` | 编译时将 INI 配置硬编码进二进制(可重复使用)|
| `--enable-zts` | | 启用线程安全ZTS模式 |
| `--no-smoke-test` | | 跳过构建后的冒烟测试 |
| `--with-suggests` | `-L` / `-E` | 同时解析并安装建议包 |
| `--with-packages=<list>` | | 额外安装的包 |
| `--no-download` | | 跳过下载步骤(使用已有缓存)|
| `--with-added-patch=<file>` | `-P` | 注入外部 PHP 补丁脚本(可重复使用)|
| `--build-shared=<list>` | `-D` | 指定编译为共享 `.so` / `.dll` 的扩展 |
| 选项 | 缩写 | 说明 |
|--------------------------------------|------|--------------------------------------------------------|
| `--no-strip` | | 保留调试符号,不精简二进制 |
| `--with-upx-pack` | | 用 UPX 压缩产物(需先 `spc install-pkg upx`;仅 Linux 和 Windows |
| `--disable-opcache-jit` | | 禁用 OPcache JIT |
| `--with-config-file-path=<path>` | | PHP 查找 `php.ini` 的目录(默认:`/usr/local/etc/php` |
| `--with-config-file-scan-dir=<path>` | | PHP 扫描追加 `.ini` 文件的目录(默认:`/usr/local/etc/php/conf.d` |
| `--with-hardcoded-ini=<k=v>` | `-I` | 编译时将 INI 配置硬编码进二进制(可重复使用) |
| `--enable-zts` | | 启用线程安全ZTS模式 |
| `--no-smoke-test` | | 跳过构建后的冒烟测试 |
| `--with-suggests` | | 同时解析并安装建议包 |
| `--with-packages=<list>` | | 额外安装的包 |
| `--no-download` | | 跳过下载步骤(使用已有缓存) |
| `--build-shared=<list>` | `-D` | 指定编译为共享 `.so` / `.dll` 的扩展 |
### micro 专用选项 {#micro-options}
| 选项 | 说明 |
|---|---|
| `--with-micro-fake-cli` | 让 `micro``PHP_SAPI` 报告为 `cli` 而非 `micro` |
| `--without-micro-ext-test` | 跳过构建后的 `micro.sfx` 扩展测试 |
| `--with-micro-logo=<path>` | 为 `micro.sfx` 嵌入自定义 `.ico` 图标(仅 Windows|
| `--enable-micro-win32` | 将 `micro.sfx` 构建为 Win32 GUI 程序而非控制台程序(仅 Windows|
| 选项 | 说明 |
|----------------------------|--------------------------------------------------|
| `--with-micro-fake-cli` | 让 `micro``PHP_SAPI` 报告为 `cli` 而非 `micro` |
| `--without-micro-ext-test` | 跳过构建后的 `micro.sfx` 扩展测试 |
| `--with-micro-logo=<path>` | 为 `micro.sfx` 嵌入自定义 `.ico` 图标(仅 Windows |
| `--enable-micro-win32` | 将 `micro.sfx` 构建为 Win32 GUI 程序而非控制台程序(仅 Windows |
### frankenphp 专用选项 {#frankenphp-options}
@@ -143,6 +142,13 @@ spc build:php <extensions> [options]
| `--dl-custom-url=<src:url>` | 覆盖指定源的下载地址 |
| `--dl-custom-git=<src:branch:url>` | 覆盖为自定义 git 仓库 |
Downloader 选项传递给 `build:php` 命令时,会被自动下载器在构建前使用。
这样你就可以直接通过构建命令控制下载行为,无需单独执行 `spc download` 命令。
```bash
spc build:php "bcmath,openssl,curl" --build-cli --dl-with-php=8.3 --dl-prefer-binary --dl-parallel=4
```
### 示例
```bash
@@ -177,6 +183,42 @@ spc build:php-embed "bcmath,openssl"
spc build:frankenphp "bcmath,openssl,curl" --enable-zts
```
## build:libs
从源码构建一个或多个库包。
```bash
spc build:libs <libraries> [options]
```
`libraries`(必填):要构建的库包名称列表,逗号分隔(如 `"openssl,curl,zlib"`)。
支持所有 `download` 命令的选项,加 `--dl-` 前缀传递。
### 选项
| 选项 | 缩写 | 说明 |
|---|---|---|
| `--with-suggests` | `-L``-E` | 同时解析并安装建议包 |
| `--with-packages=<list>` | | 额外安装的包,逗号分隔 |
| `--no-download` | | 跳过下载步骤(使用已有缓存) |
### 示例
```bash
# 构建单个库
spc build:libs openssl
# 构建多个库
spc build:libs "openssl,curl,zlib"
# 构建时包含建议包
spc build:libs openssl --with-suggests
# 跳过下载步骤
spc build:libs openssl --no-download
```
## craft
读取 `craft.yml` 并自动完成全流程构建。
@@ -195,10 +237,10 @@ spc craft [path/to/craft.yml]
spc doctor [--auto-fix[=never]]
```
| 选项 | 说明 |
|---|---|
| `--auto-fix` | 自动修复检测到的问题(使用系统包管理器)|
| `--auto-fix=never` | 仅报告问题,不尝试自动修复 |
| 选项 | 说明 |
|--------------------|----------------------|
| `--auto-fix` | 自动修复检测到的问题(使用系统包管理器) |
| `--auto-fix=never` | 仅报告问题,不尝试自动修复 |
## dev:shell
@@ -210,3 +252,271 @@ spc dev:shell
可用于在 embed SAPI 的 `libphp.a` 上编译小型 C 程序,或手动检查构建环境。
## check-update
检查指定制品是否有可用更新。
```bash
spc check-update [artifact] [options]
```
`artifact`(可选):要检查更新的制品名称,逗号分隔。默认检查所有已下载的制品。
### 选项
| 选项 | 缩写 | 说明 |
|---|---|---|
| `--json` | | 以 JSON 格式输出结果 |
| `--bare` | | 检查时不要求制品已下载(旧版本显示为 null|
| `--parallel=<n>` | `-p` | 并行检查数(默认 `10`|
| `--with-php=<ver>` | | PHP 版本上下文,格式为 `major.minor`(默认 `8.4`|
### 示例
```bash
# 检查所有已下载制品
spc check-update
# 检查指定制品
spc check-update "openssl,curl"
# 以 JSON 格式输出
spc check-update --json
# 无需先下载即可检查
spc check-update "openssl" --bare
```
## dump-extensions
从 Composer 项目中分析所需的 PHP 扩展列表。
```bash
spc dump-extensions [path] [options]
```
`path`(可选):项目根目录路径,默认为当前目录(`.`)。
### 选项
| 选项 | 缩写 | 说明 |
|---|---|---|
| `--format=<fmt>` | `-F` | 输出格式(默认 `default`|
| `--no-ext-output=<list>` | `-N` | 未找到扩展时输出的默认组合(逗号分隔),而不是以失败退出 |
| `--no-dev` | | 不包含 dev 依赖 |
| `--no-spc-filter` | `-S` | 不使用 SPC 过滤器筛选扩展 |
### 示例
```bash
# 分析当前目录的 Composer 项目
spc dump-extensions
# 分析指定目录
spc dump-extensions /path/to/project
# 不包含 dev 依赖
spc dump-extensions --no-dev
# 未找到扩展时输出默认组合
spc dump-extensions --no-ext-output="bcmath,openssl"
```
## dump-license
导出制品的开源许可证文件。
```bash
spc dump-license [artifacts] [options]
```
`artifacts`(可选):要导出许可证的制品名称,逗号分隔(如 `"php-src,openssl,curl"`)。
### 选项
| 选项 | 缩写 | 说明 |
|---|---|---|
| `--for-extensions=<list>` | `-e` | 按扩展名导出(自动包含 php-src`"openssl,mbstring"` |
| `--for-libs=<list>` | `-l` | 按库名导出,如 `"openssl,zlib"` |
| `--for-packages=<list>` | `-p` | 按包名导出,如 `"php,libssl"` |
| `--dump-dir=<path>` | `-d` | 许可证输出目录(默认 `buildroot/license`|
| `--without-suggests` | | 不包含建议包的许可证 |
### 示例
```bash
# 按扩展名导出许可证
spc dump-license --for-extensions="bcmath,openssl,curl"
# 导出指定制品的许可证
spc dump-license "php-src,openssl"
# 指定输出目录
spc dump-license --for-extensions="bcmath,openssl" --dump-dir=/tmp/licenses
```
## extract
将已下载的制品解压到对应的目标位置。
```bash
spc extract [artifacts] [options]
```
`artifacts`(可选):要解压的制品名称,逗号分隔(如 `"php-src,openssl,curl"`)。
### 选项
| 选项 | 缩写 | 说明 |
|---|---|---|
| `--for-extensions=<list>` | `-e` | 按扩展名解压所需制品,如 `"openssl,mbstring"` |
| `--for-libs=<list>` | `-l` | 按库名解压所需制品,如 `"libcares,openssl"` |
| `--for-packages=<list>` | | 按包名解压所需制品,如 `"php,libssl,libcurl"` |
| `--without-suggests` | | 使用 `--for-extensions` 时跳过建议包 |
| `--source-only` | | 强制解压源码,即使已有预编译二进制 |
### 示例
```bash
# 按扩展名解压
spc extract --for-extensions="bcmath,openssl,curl"
# 解压指定制品
spc extract "php-src,openssl"
# 强制解压源码
spc extract --for-extensions="bcmath,openssl" --source-only
```
## install-pkg
安装额外的辅助包(如 UPX、工具链等。别名`i``install-package`
```bash
spc install-pkg <package> [options]
```
`package`(必填):要安装的包名称。
支持所有 `download` 命令的选项,加 `--dl-` 前缀传递。
### 示例
```bash
# 安装 UPX 压缩工具
spc install-pkg upx
# 安装时优先使用预编译二进制(默认行为)
spc install-pkg upx
```
## micro:combine
`micro.sfx` 与 PHP/PHAR 文件合并为独立可执行文件。
```bash
spc micro:combine <file> [options]
```
`file`(必填):要合并的 PHP 或 PHAR 文件路径。
### 选项
| 选项 | 缩写 | 说明 |
|---|---|---|
| `--with-micro=<path>` | `-M` | 指定自定义 `micro.sfx` 文件路径(默认使用 `buildroot/bin/micro.sfx`|
| `--with-ini-set=<k=v>` | `-I` | 合并时注入 INI 配置(可重复使用)|
| `--with-ini-file=<path>` | `-N` | 合并时注入 INI 文件 |
| `--output=<name>` | `-O` | 自定义输出文件名(默认 `my-app`|
### 示例
```bash
# 合并 PHP 文件
spc micro:combine app.php
# 合并 PHAR 文件并指定输出名
spc micro:combine app.phar --output my-app
# 注入 INI 配置
spc micro:combine app.php -I "memory_limit=512M" -I "disable_functions=system"
# 注入 INI 文件
spc micro:combine app.php --with-ini-file=custom.ini
# 使用自定义 micro.sfx
spc micro:combine app.php --with-micro=/path/to/micro.sfx
```
## reset
清理构建目录,重置构建环境。
```bash
spc reset [options]
```
默认清理 `buildroot/``source/` 目录。
### 选项
| 选项 | 缩写 | 说明 |
|---|---|---|
| `--with-pkgroot` | | 同时删除 `pkgroot/` 目录 |
| `--with-download` | | 同时删除 `downloads/` 目录 |
| `--yes` | `-y` | 跳过确认提示 |
### 示例
```bash
# 清理构建目录(会提示确认)
spc reset
# 同时清理下载缓存
spc reset --with-download
# 完全清理(不提示)
spc reset --with-pkgroot --with-download --yes
```
## spc-config
输出静态编译所需的编译器和链接器标志,适用于将 PHP embed 库链接到自定义程序。
```bash
spc spc-config [extensions] [options]
```
`extensions`(可选):要包含的扩展名列表,逗号分隔。
### 选项
| 选项 | 缩写 | 说明 |
|---|---|---|
| `--with-libs=<list>` | | 额外包含的库,逗号分隔 |
| `--with-packages=<list>` | `-p` | 额外包含的包,逗号分隔 |
| `--with-suggested-libs` | `-L` | 包含建议库 |
| `--with-suggests` | | 包含所有建议包 |
| `--with-suggested-exts` | `-E` | 包含建议扩展 |
| `--includes` | | 仅输出 `-I` 头文件路径(`CFLAGS`|
| `--libs` | | 仅输出 `-L``-l` 链接标志(`LDFLAGS + LIBS`|
| `--libs-only-deps` | | 仅输出依赖库的 `-l` 标志 |
| `--absolute-libs` | | 使用库文件的绝对路径输出 |
| `--no-php` | | 不链接 PHP 库 |
### 示例
```bash
# 输出完整编译标志
spc spc-config "bcmath,openssl,curl"
# 仅输出头文件路径
spc spc-config "bcmath,openssl" --includes
# 仅输出链接标志
spc spc-config "bcmath,openssl" --libs
# 使用绝对路径
spc spc-config "bcmath,openssl" --libs --absolute-libs
```

View File

@@ -1,3 +1,19 @@
---
aside: false
---
# 依赖关系图
<!-- TODO: 由 `bin/spc dev:gen-ext-dep-docs``dev:gen-lib-dep-docs` 自动生成。v3 命令实现后填充。 -->
这里列出了所有支持的包(扩展、库)及其依赖关系。
- **必需依赖**:构建该包时会强制一同构建的包。
- **可选依赖**:默认不构建,使用 `--with-suggests` 参数可启用,或在构建命令中手动指定。
- **被依赖**:其他哪些包需要当前包。
运行以下命令生成依赖数据(需要在源码模式下):
```bash
bin/spc dev:gen-deps-data
```
<DepsMap />

View File

@@ -5,6 +5,7 @@
"docs:preview": "vitepress preview docs"
},
"devDependencies": {
"mermaid": "^11.0.0",
"vitepress": "^2.0.0-alpha.5",
"vue": "^3.2.47"
}

View File

@@ -0,0 +1,95 @@
<?php
declare(strict_types=1);
namespace StaticPHP\Command\Dev;
use StaticPHP\Command\BaseCommand;
use StaticPHP\Config\PackageConfig;
use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand('dev:gen-deps-data', 'Generate package dependency data JSON for documentation', [], true)]
class GenDepsDataCommand extends BaseCommand
{
private const PLATFORMS = ['linux', 'macos', 'windows'];
protected bool $no_motd = true;
public function handle(): int
{
if (!spc_mode(SPC_MODE_SOURCE)) {
$this->output->writeln('<error>This command is only available in source mode.</error>');
return static::USER_ERROR;
}
$all = PackageConfig::getAll();
$packages = [];
foreach ($all as $pkg_name => $config) {
$type = $config['type'] ?? 'unknown';
// Build platform-specific dep/suggest data
$platforms = [];
foreach (self::PLATFORMS as $platform) {
$platforms[$platform] = [
'depends' => $this->resolvePlatformList($config, 'depends', $platform),
'suggests' => $this->resolvePlatformList($config, 'suggests', $platform),
];
}
$entry = [
'type' => $type,
'platforms' => $platforms,
];
// For php-extension, add OS support info
if ($type === 'php-extension') {
$os_list = $config['php-extension']['os'] ?? null;
if ($os_list !== null) {
$entry['os'] = $os_list;
}
}
$packages[$pkg_name] = $entry;
}
// Sort by type then name for readability
uksort($packages, function ($a, $b) use ($packages) {
$ta = $packages[$a]['type'];
$tb = $packages[$b]['type'];
if ($ta !== $tb) {
return strcmp($ta, $tb);
}
return strcmp($a, $b);
});
$output_data = [
'generated_at' => date('c'),
'packages' => $packages,
];
$output_path = ROOT_DIR . '/docs/.vitepress/deps-data.json';
file_put_contents($output_path, json_encode($output_data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . PHP_EOL);
$this->output->writeln('<info>Generated ' . $output_path . ' with ' . count($packages) . ' packages.</info>');
return static::SUCCESS;
}
/**
* Resolve the value of a platform-specific array field, applying the suffix fallback chain.
*
* Fallback rules (same as PackageConfig::get):
* linux : @linux → @unix → (base)
* macos : @macos → @unix → (base)
* windows : @windows → (base)
*/
private function resolvePlatformList(array $config, string $field, string $platform): array
{
return match ($platform) {
'linux' => $config["{$field}@linux"] ?? $config["{$field}@unix"] ?? $config[$field] ?? [],
'macos' => $config["{$field}@macos"] ?? $config["{$field}@unix"] ?? $config[$field] ?? [],
'windows' => $config["{$field}@windows"] ?? $config[$field] ?? [],
default => $config[$field] ?? [],
};
}
}

View File

@@ -11,6 +11,7 @@ use StaticPHP\Command\CraftCommand;
use StaticPHP\Command\Dev\DumpCapabilitiesCommand;
use StaticPHP\Command\Dev\DumpStagesCommand;
use StaticPHP\Command\Dev\EnvCommand;
use StaticPHP\Command\Dev\GenDepsDataCommand;
use StaticPHP\Command\Dev\GenExtDocsCommand;
use StaticPHP\Command\Dev\IsInstalledCommand;
use StaticPHP\Command\Dev\LintConfigCommand;
@@ -83,6 +84,7 @@ class ConsoleApplication extends Application
new DumpCapabilitiesCommand(),
new PackageInfoCommand(),
new GenExtDocsCommand(),
new GenDepsDataCommand(),
]);
// add additional commands from registries