軟體組合

軟體組裝可讓開發人員使用自訂作業系統快速建構產品。具體來說,軟體組裝是一組工具,可從輸入內容 (包括一組 Fuchsia 套件、核心和設定檔) 產生產品組合 (PB)。

產品組合

產品套件是經過妥善指定的構件目錄,可運送至任何環境,並用於刷寫、更新或模擬 Fuchsia 目標。開發人員不應直接檢查 Product Bundle 的內容,而是應使用 Fuchsia 提供的工具。舉例來說,ffx 工具可用於使用產品套件刷新或模擬裝置:

# Flash a hardware target with the product bundle.
$ ffx target flash --product-bundle <PATH/TO/PRODUCT_BUNDLE>

# Start a new emulator instance with the product bundle.
$ ffx emu start <PATH/TO/PRODUCT_BUNDLE>

產品組合可能包含主要復原產品,兩者是不同的可啟動體驗。main 通常用於實際產品體驗,如果 main 發生故障且無法啟動,則使用 recoveryrecovery通常是輕量型體驗,可透過恢復原廠設定或無線 (OTA) 更新,解決 main 插槽中的問題。一般來說,使用者可以在裝置啟動時按住實體按鈕,切換要啟動的產品。

軟體組裝提供建構產品組合的建構工具,定義輸入內容的格式 (平台、產品設定、主機板設定),並提供建構這些輸入內容的建構規則。

包含主要產品和復原產品的產品組合

圖 1. 產品組合可能包含主要產品和復原產品。

平台、產品設定和開發板設定

組裝的三項輸入內容 (平台產品設定主機板設定) 都是構件目錄。內部格式可能會變更,不應依此格式。開發人員需要使用提供的 Bazel 或 GN 建構規則,建構及使用這些輸入內容。

平台由 Fuchsia 團隊製作。其中包含所有編譯的平台程式碼位元,任何 Fuchsia 產品都可能使用。Fuchsia 團隊會將平台發布至 https://chrome-infra-packages.appspot.com/p/fuchsia/assembly/platform

產品設定是由開發人員定義使用者體驗所產生。可能包含指出要納入哪些平台功能的旗標。舉例來說,產品設定可以設定 platform.fonts.enabled=true,因此組裝作業會納入平台提供的相關字型支援。如要查看所有可用旗標,請參閱這份參考資料。產品設定檔也可以包含用於建構使用者體驗的自訂程式碼。

主機板設定是由支援特定硬體目標的開發人員產生。其中包含在該硬體上啟動的所有必要驅動程式。 此外,主機板設定可以宣告平台可用的硬體。舉例來說,如果硬體有即時時鐘 (RTC),主機板設定可以透過設定 provided_features=["fuchsia::real_time_clock"] 標記來指出這點。組件會讀取這個標記,並從平台納入使用這項硬體所需的程式碼。Fuchsia 團隊會維護一小組主機板設定,並發布至 https://chrome-infra-packages.appspot.com/p/fuchsia/assembly/boards

環境

其他作業系統也支援自訂功能,但 Fuchsia 軟體組件獨具在任何可想像的環境中執行的能力,而且速度很快。Fuchsia 產品的自訂和組裝時間不到一分鐘,比其他作業系統快得多。

目前下列環境支援 Fuchsia 軟體組裝 (雖然技術上沒有限制,但 Fuchsia 未來可能會擴大支援範圍):

  • Bazel
  • CLI (實驗功能)
  • GN (fuchsia.git 後)

Bazel

# A product bundle can contain both 'main' and 'recovery' products (systems/slots).
fuchsia_product_bundle(
    name = "my_product_bundle",
    main = ":main_product",
    recovery = "...",
)

# A product is a single bootable experience that is built by combining
# a platform, product, and board.
fuchsia_product(
    name = "main_product",
    platform = "//platform:x64",
    product = ":my_product",
    board = "//boards:x64",
)

# A product configuration defines the user experience by enabling
# platform features and including custom product code.
fuchsia_product_configuration(
    name = "my_product",
    product_config_json = {
        platform = {
            fonts = {
                enabled = True,
            },
        },
    },

    # The product code is included as packages.
    base_packages = [ ... ],
)

如需完整範例,請參閱 getting-started 存放區。

指令列介面

ffx product-bundle create --platform 28.20250718.3.1 \
                          --product-config <PATH/TO/MY_PRODUCT_CONFIG> \
                          --board-config cipd://fuchsia/assembly/boards/x64@version:28.20250718.3.1

您可以執行 ffx product-bundle create 指令,使用已建構的平台、開發板和產品構件,產生新的產品組合。

大小和審查

Software Assembly 提供工具,可驗證產品組合的品質。

大小檢查工具會告知使用者,產品組合是否符合目標硬體的磁碟分割大小限制。您可以使用下列 Bazel 規則產生產品大小報表

fuchsia_product_size_check(
    name = "main_product_size_report",
    product_image = ":main_product",
)

fuchsia_product(
    name = "main_product",
    ...
)

審查工具可確保產品組合符合一組安全標準。如果開發人員提供必要的審查設定,審查會在建構產品組合時執行。請參閱以下審查設定範例:

fuchsia_product_bundle(
    name = "my_product_bundle",
    main = ":main_product",
    main_scrutiny_config = ":main_scrutiny_config",
)

fuchsia_scrutiny_config(
    name = "main_scrutiny_config",
    base_packages = [ ... ],    # Allowlist of base packages to expect.
    kernel_cmdline = [ ... ],   # Allowlist of kernel arguments to expect.
    pre_signing_policy = "...", # File containing the policies to check before signing.
)

實作平台功能

本節說明如何在平台中實作新功能,並透過產品設定或主機板設定啟用。

平台功能一律在 fuchsia.git 中實作,且必須夠通用,才能在多個產品或主機板上啟用。如果該功能專屬於特定產品或開發板,請考慮將其放在產品或開發板設定中。

導入平台功能通常需要完成下列步驟:

  1. 在產品設定或主機板設定中撰寫功能標記
  2. 準備可讀取旗標的子系統
  3. 在子系統中編寫並啟用功能代碼

圖表:顯示平台功能的實作方式

圖 2. 實作平台功能,並在產品設定中啟用該功能。

1. 撰寫功能旗標 (如有必要)

平台功能通常不會預設新增至所有產品,但如果產品或主機板設定設有特定標記,通常就會新增平台功能。撰寫平台功能的第一步,就是判斷何時需要啟用功能,以及啟用功能所需的產品或主機板標記。

舉例來說,如果您想允許產品設定啟用名為 foo.bar 的功能,產品設定可以寫成如下所示:

fuchsia_product_configuration(
    name = "my_product",
    product_config_json = {
        platform = {
            foo = {
                bar = True,
            },
        },
    },
)

如果您有很多設定選項,且需要以不容易納入產品設定的方式整理這些選項,可以考慮使用個別的設定檔。設定檔可以是任何格式,但大多數團隊會選擇 json。設定檔可以做為單一檔案傳遞至產品設定,如下所示:

fuchsia_product_configuration(
    name = "my_product",
    product_config_json = {
        platform = {
            foo = {
                bar_config = "LABEL(//path/to/config:file.json)",
            },
        },
    },
)

以下列舉一些常見情況:

包含功能 當產品設定旗標為 且主機板設定旗標為
預設為所有 eng 產品 platform.build_type = eng
要求提供這項資訊的產品 platform.<SUBSYSTEM>.<FEATURE> = true
使用支援硬體的開發板的產品 provided_features = [ "<HARDWARE>" ]

組裝平台功能旗標會在 //src/lib/assembly/config_schema 中宣告。(這裡是先前提及的字型設定。)

2. 準備子系統

組件子系統是一組類似的平台功能 (例如,connectivity, diagnosticsfonts 都是獨立的子系統)。子系統的工作是讀取產品和主機板設定標記,並決定何時及如何納入平台功能。

以下是簡單的子系統。假設您已在 FooConfig 內定義新的功能標記,並在 define_configuration 函式中提供該標記。子系統需要讀取這些旗標,並呼叫組裝 API 來納入您的功能。

use crate::subsystems::prelude::*;
use assembly_config_schema::platform_config::foo_config::FooConfig;

pub(crate) struct FooSubsystem;
impl DefineSubsystemConfiguration<FooConfig> for FooSubsystem {
    fn define_configuration(
        context: &ConfigurationContext<'_>,
        foo_config: &FooConfig,
        builder: &mut dyn ConfigurationBuilder,
    ) -> anyhow::Result<()> {

        // Read the flag and enable the feature code.
        // See the below sections for more APIs.
        if foo_config.bar {
            builder.platform_bundle("foo");
        }

        Ok(())
    }
}

宣告新的子系統後,您可以在 subsystems.rs 中呼叫其 define_configuration,並傳遞設定。

請參閱下一節,瞭解可啟用子系統內功能的可用 API。

3. 啟用功能代碼

判斷程式碼是否需要在建構階段執行階段啟用。 在建構時啟用功能,表示當產品不需要程式碼時,組件會完全排除程式碼。但這也表示,如要啟用這項功能,就必須更新建構規則。在執行階段啟用功能可簡化開啟和關閉程序,不必更新建構規則,但會導致產品加入不必要的程式碼,而這些產品永遠不需要這項功能。

盡可能在建構時啟用,以節省空間、加強安全性、啟用靜態分析,並提高不需要這項功能的其他產品效能。

建構時間

組件會使用組件輸入套件 (AIB) 整理建構時間功能。功能擁有者可以在單一 AIB 中插入多種構件,並指示 Assembly 何時及如何將該 AIB 新增至產品。所有 AIB 都定義在 //bundles/assembly/BUILD.gn 中。範例如下:

# Declares a new AIB with the name "foo".
assembly_input_bundle("foo") {
  # Include this package into the "base package set".
  # See RFC-0212 for an explanation on package sets.
  # The provided targets must be fuchsia_package().
  base_packages = [ "//path/to/code:my_package" ]

  # Include this file into BootFS.
  # The provided targets must be bootfs_files_for_assembly().
  bootfs_files_labels = [ "//path/to/code:my_bootfs_file" ]
}

如要加入 AIB,請在子系統中使用下列方法:

builder.platform_bundle("foo");

如果您新增 AIB,請務必在 //bundles/assembly/platform_aibs.gni 中將其新增至適當的清單,否則在建構時會發生錯誤,指出找不到 AIB。

執行階段

組件支援多種執行階段設定。這些類型會依偏好順序列出。

設定功能:Fuchsia 元件可在執行階段讀取設定功能的值,而 Assembly 則會在建構階段設定這些功能的預設值,例如:

// Add a config capability named `fuchsia.foo.bar` to the config package.
builder.set_config_capability(
    "fuchsia.foo.bar",
    Config::new(ConfigValueType::String { max_size: 512 }, "my_string".into()),
)?;

組件會將所有預設設定功能新增至 BootFS 中的設定套件,因此能力必須從 /root 元件領域轉送至您的元件。

網域設定:如需複雜設定或自訂類型,建議使用網域設定,而非設定功能。網域設定是 Fuchsia 套件,可為元件提供設定檔,以便在執行階段讀取及剖析,例如:

// Create a new domain config in BlobFS with a file at "my_directory/foo_config.json".
builder.add_domain_config(PackageSetDestination::Blob(PackageDestination::FooConfigPkg))
      .directory("my_directory")
      .entry(FileEntry {
          source: config_src,
          destination: "foo_config.json".into(),
      })?;

您的元件必須以子項形式啟動網域設定套件,並use目錄,例如:

{
    children: [
        {
            name: "my-config",
            url: "fuchsia-pkg://fuchsia.com/foo-config#meta/foo-config.cm",
        },
    ],
    use: [
       {
            directory: "my_directory",
            from: "#foo-config",
            path: "/my_directory",
        },
    ],
}

核心引數:核心引數僅用於啟用核心功能。組件會建構指令列,在執行階段傳遞至核心,例如:

builder.kernel_arg(KernelArg::MyArgument);

附錄:開發人員覆寫

開發人員有時會想在現有產品上測試某些項目,方法是新增程式碼或切換功能標記。修改產品或主機板設定會污染 git 樹狀結構 (fuchsia.git),因此不建議這麼做。組裝作業支援使用開發人員覆寫,在不污染 git 樹狀結構的情況下,在本機修改現有產品。