封裝既有的原生產品
當你已經有一個會自行建置執行檔、啟動指令碼與 DSM 視窗的產品,只需要在外面包一層 SPK 時,就使用 native layout。synopkgland 會執行你的建置 hook、檢查結果,並為每個架構輸出一個 SPK。它不會產生應用程式碼、後端或 UI。
如果你想要的是有產生程式碼的新 App,請改從建立原生 App開始。
請手寫 dsm-package.yaml。native layout 不是 init 的模板。
schema: 2backend: {kind: native}package: ExampleNativeversion: 1.2.3build: 8displayname: Example Nativedescription: Existing native productmaintainer: Exampleos_min_ver: 7.0-40000arches: [x86_64, armv8, armv7]layout: payload: target/native-layout/{arch}/payload metadata: target/spk-metadata binaries: - {path: bin/app, linkage: static, arches: [x86_64, armv8]} - {path: bin/app, linkage: dynamic, arches: [armv7]}native: prepare: {cwd: ., command: [python3, package/prepare.py]} hook: {cwd: ., command: [python3, package/compile.py]} toolkits: x86_64: - {url: 'https://example.com/toolkit.txz', sha256: '<64 lowercase hex digits>'}請把 toolkit 的 URL 與 digest 換成你自己的;如果編譯 hook 不需要 toolkit,就移除 toolkits。
然後執行:
synopkgland checksynopkgland build --arch x86_64,armv8,armv7synopkgland inspect dist/ExampleNative-1.2.3-0008-x86_64.spkbuild 會輸出 dist/<package>-<version>-<補滿 4 位數的 build>-<arch>.spk。沒有 --arch 時,會建置 arches 內的每個架構。
要安裝到 NAS,執行 synopkgland deploy --host <nas>。它只建置 NAS 的架構(在 aarch64 NAS 上,若你宣告了 armv8 就使用它)並安裝 SPK。每次部署前請提高 build。之後 synopkgland status --host <nas> 會回報安裝狀態。
Manifest 欄位
Section titled “Manifest 欄位”| 欄位 | 規則 |
|---|---|
| 允許的鍵 | schema、backend、package、version、build、displayname、description、maintainer、metadata、icon、os_min_ver、privilege、arches、layout、native |
schema | 2 |
backend | {kind: native} |
package | ^[A-Za-z][A-Za-z0-9]{1,63}$ |
version | ^\d+(\.\d+){0,3}$ |
build | 1 到 2147483647 的整數 |
os_min_ver | 7.x-<build>,例如 7.0-40000 |
privilege | none,或省略 |
arches | 非空、不可重複:x86_64、aarch64、armv8、armv7 |
layout.payload | 放入套件檔案的目錄。可含 {arch}。 |
layout.metadata | 放 SPK 中繼資料檔案的目錄(見下方) |
layout.binaries | 一個或多個 {path, linkage, arches?}。linkage 為 static 或 dynamic。arches 必須是 manifest arches 的子集。每個架構至少要有一個執行檔。 |
icon | 選用。專案內至少 256×256 的正方形 PNG,或含 icon_256.png 的 icon_<size>.png 目錄。build 由它產生 PACKAGE_ICON.PNG 與 PACKAGE_ICON_256.PNG;metadata 目錄不可同時放這兩個檔案。視窗圖示仍由 payload 提供。 |
layout.anchors | 選用。<key>_sha256: <payload 路徑>。為每個架構把該檔案的 SHA-256 寫進 INFO。見產品自帶的 root helper 與升級。 |
native.hook | 必填。為單一架構編譯或收集 payload。 |
native.prepare | 選填。在各架構的 hook 之前執行一次。 |
native.toolkits | 選填。<arch>: [{url, sha256}]。url 是 HTTPS。sha256 是 64 個小寫十六進位字元。 |
reproducible | 選填。build --verify-reproducible 用的 {inputs?: [檔案...], clean?: [路徑...]}。把 target/{arch} 這類建置產物列在 clean,第二次建置才會從頭編譯。參閱可重現建置。 |
manifest 中的路徑都相對於專案,不可是絕對路徑,也不可含 ..,除了 {arch} 之外不可含 { 或 }。同一專案中若有 compose.yaml 會被拒絕。
hook 的格式是 {command: [argv...], cwd?: '.', env?: {...}}。指令在專案內以引數陣列執行,不經過 shell。hook 必須以狀態 0 結束。env 的鍵須符合 ^[A-Z_][A-Z0-9_]*$,且不可以 SYNOPKGLAND_ 開頭。
| 變數 | 意義 |
|---|---|
SYNOPKGLAND_PACKAGE | 套件名稱 |
SYNOPKGLAND_VERSION、SYNOPKGLAND_BUILD | manifest 的版本與 build 編號 |
SYNOPKGLAND_SPK_VERSION | 版本加上補滿 4 位數的 build |
SYNOPKGLAND_PROJECT_DIR | 專案根目錄的絕對路徑 |
SYNOPKGLAND_ARCH | 目前的架構。僅編譯 hook 有。 |
SYNOPKGLAND_TOOLKIT_DIR | 剛解開的 toolkit 目錄樹。僅編譯 hook 有。 |
SYNOPKGLAND_WORK_DIR | 這個架構專用的空白暫存資料夾,建置後刪除。僅編譯 hook 有。 |
synopkgland build --jobs <n> 最多同時為 n 個架構執行編譯 hook。只有 hook 能把各架構分開時才使用:layout.payload 必須含 {arch}(否則 CLI 會拒絕大於 1 的 --jobs),hook 也不可寫入共用資料夾,例如單一的 build/ 或 out/。暫存檔請放在 SYNOPKGLAND_WORK_DIR。Cargo 在每個架構使用各自的 --target 時是安全的,因為它會鎖住共用的 target/ 資料夾,但各架構會在那裡互相等待。native.prepare 仍只在所有架構開始前執行一次。
toolkit 封存檔會透過 HTTPS 下載到快取,並在每次建置時以 sha256 檢查。用 SYNOPKGLAND_CACHE_DIR 指定快取資料夾。預設是每位使用者一份、所有專案與 worktree 共用的快取:Linux 為 $XDG_CACHE_HOME/synopkgland 或 ~/.cache/synopkgland,macOS 為 ~/Library/Caches/synopkgland。多個建置可以同時使用這個快取:每次下載先寫入私有的暫存檔,檢查通過後才改名放入快取,每個建置也各自解開一份 toolkit。快取資料夾不可是符號連結,也不可讓其他使用者寫入。
封存檔由 PATH 上的 tar 解開,它必須是 GNU tar 或 bsdtar(macOS 預設)。後面的封存檔可以寫進前面封存檔建立的資料夾符號連結。使用 bsdtar 時,CLI 會先拒絕含有絕對路徑或 .. 成員的封存檔。
Layout 規則
Section titled “Layout 規則”check 與 build 會驗證 payload 與中繼資料資料夾。
| 範圍 | 規則 |
|---|---|
| 檔案 | 不可有符號連結與特殊檔案。不可有 setuid 或 setgid 位元。 |
| 大小 | 每個檔案最多 256 MiB,所有檔案合計最多 1 GiB。 |
| 執行檔 | 每個列出的執行檔都必須是該架構、可執行的 ELF 檔案,且連結方式符合 linkage。 |
metadata 最上層 | 只能有 conf、scripts、WIZARD_UIFILES、PACKAGE_ICON.PNG 與 PACKAGE_ICON_256.PNG(設了 manifest icon 時不可放) |
scripts/start-stop-status | 必要 |
scripts/* | 不可含 sudo、chown root 或 chmod +s |
| 生命週期腳本 | 必須可執行(chmod +x):preinst、postinst、preuninst、postuninst、preupgrade、postupgrade、start-stop-status、prereplace、postreplace |
conf/privilege | JSON,defaults.run-as 須為 package。username 與 groupname 選填,且必須等於套件名稱。 |
conf/resource | 選填。只接受空的 systemd-unit 物件。 |
conf/systemd | 只能有最上層的 *.service 檔案。每個檔案恰有一個 User=,且等於套件名稱。若有 Group=,也必須等於套件名稱。 |
| Service unit | 不可有續行。User、Group 與 Exec* 行只能在 [Service] 內。不可有 AmbientCapabilities、SupplementaryGroups、PermissionsStartOnly、RootDirectory 或 RootImage。Exec* 指令不可有 + 或 ! 前綴。 |
套件永遠以自己的套件帳號執行。native layout 沒有 root 選項。
產品自帶的 root helper 與升級
Section titled “產品自帶的 root helper 與升級”如果你的產品自帶 root helper 並需要管理員授權,請讓授權綁定管理員實際同意的內容:capabilities、套件帳號與固定的排程任務指令。不要綁定 build、manifest 或檔案樹的 digest。若要讓新的 SPK 改變 root 程式碼而不需重新核准,請在 layout.anchors 宣告每個 root payload:
layout: anchors: example_helper_sha256: bin/helper example_bootstrap_sha256: bin/bootstrapbuild 會為每個架構把該架構 payload 檔案的 SHA-256 寫進 INFO,例如 example_helper_sha256="<64 個小寫 hex>"。Package Center 以 root 寫入 /var/packages/<package>/INFO,套件帳號改不了它。
| 規則 | 值 |
|---|---|
| Key | ^[a-z][a-z0-9_]{0,48}_sha256$,不可以 synopkgland_ 開頭,最多 16 個 |
| 路徑 | payload 中的一般檔案,相對於 payload 資料夾,不含 {arch} |
build | 檔案不存在,或 key 與其他 INFO 欄位衝突時失敗 |
inspect | INFO 錨點被改動、重複、缺少或未宣告時拒絕該 SPK |
你的 root helper 從 root 擁有的 INFO 為每個 key 讀取剛好一行有效值,把 payload 複製到受保護的資料夾,用該值檢查複本後才執行。固定的任務指令不要內嵌某個 build 專屬的 digest。值缺少、重複或不符時,授權維持停用,直到管理員核准。只有 capabilities 改變時才在升級時撤銷授權,並讓 authorization.status 回傳 reason: upgrade_reapproval。SDK executor 以 synopkgland_executor_sha256 遵循同一規則。
arches 值 | ELF 檢查 | 備註 |
|---|---|---|
x86_64 | 64 位元,machine 62 | |
aarch64 | 64 位元,machine 183 | INFO 的 arch 列出 aarch64 平台(armada37xx rtd1296 rtd1619 rtd1619b),與 native-go、binary 套件相同。 |
armv8 | 64 位元,machine 183 | 與 aarch64 的二進位格式相同。SPK 保留 armv8 這個名稱。 |
armv7 | 32 位元,machine 40 |
dynamic 連結表示你的執行檔相依於目標 DSM 的函式庫。請在 native.toolkits 中固定對應的 DSM toolkit。
| 限制 | 值 |
|---|---|
| 可用的指令 | check、build、inspect、deploy、status |
| 會拒絕它的指令 | logs |
build 範圍 | 1 到 2147483647 |
| 權限 | none(套件帳號) |
| 架構 | x86_64、aarch64、armv8、armv7 |
| 檔案大小 | 每個 256 MiB,合計 1 GiB |
inspect 只檢查封存檔,不會安裝或執行任何東西。它不能證明產品在裝置上可正常運作。
| 錯誤碼或訊息 | 原因 | 解法 |
|---|---|---|
NATIVE_LAYOUT_INVALID: schema 2 required | schema 不是 2,或缺少 backend。 | 設定 schema: 2 與 backend: {kind: native}。 |
NATIVE_LAYOUT_INVALID: unknown field | 有鍵不在允許清單內。 | 移除它。 |
NATIVE_LAYOUT_INVALID: path must stay inside project | 路徑是絕對路徑、含 ..,或含 {arch} 以外的 {。 | 使用專案內的相對路徑。 |
NATIVE_LAYOUT_INVALID: ELF architecture mismatch | 執行檔不是為正在建置的架構編譯的。 | 在 hook 中建置正確的執行檔。 |
NATIVE_LAYOUT_INVALID: ELF linkage mismatch | linkage 與執行檔不符。 | 修正 linkage 或建置方式。 |
NATIVE_LAYOUT_INVALID: native hook failed | hook 以非 0 狀態結束。 | 手動執行該 hook 指令。 |
NATIVE_LAYOUT_INVALID: toolkit checksum mismatch | 下載內容與 sha256 不符。 | 修正 URL 或 digest。 |
NATIVE_LAYOUT_INVALID: native layout requires package-only lifecycle privilege | conf/privilege 要求的權限超過套件帳號。 | 把 defaults.run-as 設為 package。 |
NATIVE_LAYOUT_INVALID: start-stop-status missing | metadata 內沒有 scripts/start-stop-status。 | 加入它。 |
NATIVE_TOOLKIT_TAR_UNSUPPORTED | PATH 上的 tar 不是 GNU tar 也不是 bsdtar。 | 安裝其中一個並放在 PATH 最前面。 |
NATIVE_LAYOUT_SCRIPT_NOT_EXECUTABLE | metadata/scripts 的生命週期腳本沒有執行權限。 | 對它執行 chmod +x。 |
所有錯誤碼請見錯誤碼。