本程式碼研究室會逐步說明如何在 Fuchsia 平台中實作新功能,並根據產品或主機板設定有條件地納入及設定該功能。
必要條件
本程式碼研究室假設您熟悉下列項目:
- Fuchsia 的原始碼樹狀結構和建構系統 (GN 和 Bazel)。
- Rust 程式設計語言。
- Fuchsia 元件概念。
- 軟體組裝概念 (產品設定、主機板設定、平台)。
課程內容
- 如何判斷某項功能是否應納入平台。
- 如何在組件設定結構定義中定義功能標記。
- 如何建立及註冊 Assembly 子系統。
- 如何使用子系統 API 納入套件、設定和核心引數。
- 如何在產品或看板中啟用新平台功能。
功能擺放位置規範
平台功能一律在 fuchsia.git 中實作,且必須夠通用,才能在多個產品或開發板上啟用。如果某項功能僅適用於單一產品或開發板,則不屬於平台。
Fuchsia 平台是所有產品共用的核心基礎。在平台上新增產品專屬或開發板專屬功能,會增加平台大小和複雜度,並可能造成長期維護負擔。請務必保持平台通用。
請按照下列準則決定要將功能放在何處:
平台功能:適用於多項產品或開發板的功能。這項服務應為可設定的通用服務,例如可供不同產品選擇納入及設定的新選用網路服務。
產品功能:特定產品專屬的功能。這通常是使用者可見的功能,例如建構 UI 的 Fuchsia 套件,或是單一產品專用的字型集。
開發板功能:特定開發板硬體專屬的功能,例如特定硬體元件的驅動程式庫,或是該開發板專屬的設定值 (例如 GPIO 針腳編號)。
程式碼研究室步驟
導入平台功能需要執行下列步驟:
圖 1. 實作平台功能,並在產品設定中啟用該功能。
1. 在 config_schema 中宣告功能旗標
平台功能通常會根據產品或主機板設定中設定的旗標,有條件地啟用。首先,您必須定義這些旗標的結構。
所有平台功能旗標都會在 //src/lib/assembly/config_schema 的 Rust 結構體中宣告。這個目錄中的每個子系統通常都有自己的檔案 (例如 fonts_config.rs 或 network_config.rs)。
範例:如需範例,請按照下列步驟操作:
如要定義標記來啟用假設的「Tandem」網路功能,請建立新檔案
//src/lib/assembly/config_schema/src/platform_settings/tandem_config.rs:use serde::{Deserialize, Serialize}; /// Configuration for the Tandem networking feature. #[derive(Debug, Deserialize, Serialize, PartialEq)] #[serde(default, deny_unknown_fields)] pub struct TandemConfig { /// Enables the core Tandem service. pub enabled: bool, /// Specifies the maximum number of concurrent connections. pub max_connections: u32, } // Choose reasonable default values. impl Default for TandemConfig { fn default() -> Self { Self { enabled: false, max_connections: 10, } } }預設值的最佳做法:
- 請一律在
PlatformSettings內的結構體欄位上使用#[serde(default)](如下一步所示)。 - 為設定結構 (本例中為
TandemConfig) 導入impl Default,為每個欄位定義預設值。 - 請避免在結構體中同時使用欄位層級的
#[serde(default = "...")]和#[derive(Default)],否則可能會導致行為不一致。
- 請一律在
將這個新設定結構體新增至
//src/lib/assembly/config_schema/src/platform_settings.rs的主要PlatformSettings中:// ... other imports mod tandem_config; // ... other fields pub struct PlatformSettings { // ... other fields #[serde(default)] pub tandem: tandem_config::TandemConfig, }您現在可以在產品設定中啟用這項新功能。例如:
fuchsia_product_configuration( name = "my_product", product_config_json = { platform = { tandem = { enabled = True, max_connections = 20, }, }, }, )常見檢舉模式:
已在以下服務啟用這項功能... 當產品設定集... 或者,當主機板設定... 所有 eng的產品platform.build_type = eng特定產品 platform.tandem.enabled = True支援的開發板上的產品 provided_features = [ "tandem_hw" ]
2. 定義新的子系統
組件子系統是 Rust 模組,負責處理相關平台功能群組的設定。這個外掛程式會讀取 config_schema 中定義的標記,並使用 Assembly Builder API 納入及設定功能程式碼。
位置:子系統位於 //src/lib/assembly/platform_configuration/src/subsystems。
舉例來說,請按照下列步驟操作:
為「Tandem」功能建立新檔案:
//src/lib/assembly/platform_configuration/src/subsystems/tandem.rsuse crate::subsystems::prelude::*; use assembly_config_schema::platform_config::tandem_config::TandemConfig; pub(crate) struct TandemSubsystem; impl DefineSubsystemConfiguration<TandemConfig> for TandemSubsystem { fn define_configuration( context: &ConfigurationContext<'_>, tandem_config: &TandemConfig, builder: &mut dyn ConfigurationBuilder, ) -> anyhow::Result<()> { if tandem_config.enabled { // Actions to include the feature will go here // See Step 3 for details println!("Tandem feature enabled with max_connections: {}", tandem_config.max_connections); } Ok(()) } }說明:
- 結構體
TandemSubsystem會實作DefineSubsystemConfiguration特徵,並以先前定義的TandemConfig結構體輸入型別。 define_configuration函式會接收ConfigurationContext、我們的特定TandemConfig和ConfigurationBuilder。- 在這個函式中,我們會檢查
enabled標記。如果為 true,我們會使用builder將功能元件新增至系統映像檔。
- 結構體
將模組新增至
//src/lib/assembly/platform_configuration/src/subsystems.rs:// ... other mods mod tandem;在同一檔案的主要
Subsystems::define_configuration函式中呼叫其define_configuration函式,並傳遞平台設定的相關部分:// In Subsystems::define_configuration tandem::TandemSubsystem::define_configuration( context, &platform.tandem, builder, )?;
3. 實作子系統邏輯
在子系統的 define_configuration 函式中,您會使用 ConfigurationBuilder 方法,根據功能標記將功能新增至產品。
更新 tandem.rs,例如:
use crate::subsystems::prelude::*;
use assembly_config_schema::platform_config::tandem_config::TandemConfig;
use assembly_platform_configuration::{
ConfigurationBuilder,
KernelArg,
ConfigValueType,
Config,
PackageSetDestination,
PackageDestination,
FileEntry,
BuildType
};
pub(crate) struct TandemSubsystem;
impl DefineSubsystemConfiguration<TandemConfig> for TandemSubsystem {
fn define_configuration(
context: &ConfigurationContext<'_>,
tandem_config: &TandemConfig,
builder: &mut dyn ConfigurationBuilder,
) -> anyhow::Result<()> {
if tandem_config.enabled {
// 1. Add the feature's code at build time using an Assembly Input Bundle (AIB)
builder.platform_bundle("tandem_core");
// 2. Set a runtime configuration value
builder.set_config_capability(
"fuchsia.tandem.MaxConnections",
Config::new(ConfigValueType::Int32, tandem_config.max_connections.into()),
)?;
// 3. Conditionally add a runtime kernel argument based on build type
if context.build_type == &BuildType::Eng {
builder.kernel_arg(KernelArg::TandemEngDebug);
}
// 4. Include a domain config package for more complex runtime configuration
builder.add_domain_config(PackageSetDestination::Blob(PackageDestination::TandemConfigPkg))
.directory("config/data")
.entry(FileEntry {
source: "//path/to/tandem/configs:default.json".into(),
destination: "settings.json".into(),
})?;
}
Ok(())
}
}
建構時間與執行階段啟用:
- 建構時間:只有在啟用這項功能時,構件才會納入映像檔。建議您使用這項功能,以節省空間、加強安全性、啟用靜態分析,並提升其他不需要這項功能的產品效能。
- 執行階段:一律會納入 Artifact,但其行為會在執行階段受到控制 (例如透過設定值或核心引數)。
建構時間
組件會使用組件輸入套件 (AIB) 整理建構時間功能。功能擁有者可以在單一 AIB 中插入多種構件,並指示 Assembly 何時及如何將該 AIB 新增至產品。所有 AIB 皆定義於 //bundles/assembly/BUILD.gn。例如:
# Declares a new AIB with the name "tandem_core".
assembly_input_bundle("tandem_core") {
# 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/my/tandem:pkg" ]
# Include this file into BootFS.
# The provided targets must be bootfs_files_for_assembly().
bootfs_files_labels = [ "//path/to/my/tandem:bootfs" ]
}
如要加入 AIB,請在子系統中使用下列方法:
builder.platform_bundle("tandem_core");
如果您新增 AIB,請務必將其新增至 //bundles/assembly/platform_aibs.gni 中的適當清單,否則在建構時會發生錯誤,指出找不到 AIB。
執行階段
組件支援多種執行階段設定。這些類型會依偏好順序列出。
設定功能:Fuchsia 元件可在執行階段讀取設定功能的值,而組件會在建構時設定這些功能的預設值,例如:
// Add a config capability named `fuchsia.tandem.MaxConnections` to the config package.
builder.set_config_capability(
"fuchsia.tandem.MaxConnections",
Config::new(ConfigValueType::Int32, tandem_config.max_connections.into()),
)?;
組件會將所有預設設定功能新增至 BootFS 中的設定套件,因此能力必須從 /root 元件領域轉送至您的元件。
在元件中使用平台定義的設定功能
當元件需要使用平台定義及提供的設定能力 (透過子系統中的 builder.set_config_capability),元件的 CML 檔案必須包含 use 宣告。即使值是由上層領域提供,這項宣告仍必須指定設定值的 type。
元件 CML 範例 (my_component.cml):
{
use: [
{
config: "fuchsia.tandem.MaxConnections", // The capability name
from: "parent",
key: "max_conn", // The key used in this component's structured config
type: "int32", // The type MUST be specified here
},
],
// ... other parts of the manifest
config: {
max_conn: { type: "int32" },
},
}
元件代碼:
您也必須更新元件的原始碼,以便在結構化設定中預期會出現這個金鑰。這通常需要更新可還原序列化設定值的結構體,通常是由 ffx component config
get 指令或類似工具產生。定義用於還原序列化設定的結構體:
// Example in the component's config.rs (e.g., src/config.rs)
use serde::Deserialize;
// This struct should match the keys and types in the CML 'config' block.
#[derive(Debug, Deserialize)]
pub struct TandemComponentConfig {
pub max_conn: i32,
// ... other config fields
}
然後在元件的初始化程式碼中,擷取設定:
let config = fuchsia_component::config::Config::take_from_startup_handle();
let tandem_config: TandemComponentConfig = config.get();
重點:
type(例如"bool"、"int32"、"string") 必須納入平台提供的設定的use節。- 如果類型為
"string",則必須同時納入max_size。 use節中的key會將能力對應至元件本身config結構定義中的欄位名稱,進而對應至用於在元件程式碼中載入設定的結構體中的欄位。- 確認元件的程式碼 (例如 Rust、C++),以處理新的設定鍵。
網域設定:如果是複雜的設定、項目清單或需要自訂類型的設定,建議使用網域設定,而非設定功能。雖然通常可以將複雜的設定「扁平化」為一組簡單的鍵/值組合,以用於設定功能,但這可能會變得難以管理。
舉例來說,假設某個元件需要網路端點清單,而每個端點都有網址、通訊埠和通訊協定。使用設定功能時,您可能需要將這個項目扁平化為一系列鍵。例如:
// This approach is NOT recommended for lists or complex types.
"endpoint.0.url": "host1.example.com",
"endpoint.0.port": 443,
"endpoint.1.url": "host2.example.com",
"endpoint.1.port": 8080,
這會導致管理困難,尤其當端點數量不固定時更是如此。在這種情況下,網域設定是更簡潔的解決方案。您可以在套件中提供單一 JSON 檔案,供元件在執行階段剖析:
// A domain config file (e.g., tandem_config.json)
{
"endpoints": [
{
"url": "host1.example.com",
"port": 443,
"protocol": "HTTPS"
},
{
"url": "host2.example.com",
"port": 8080,
"protocol": "HTTP"
}
]
}
網域設定是 Fuchsia 套件,可為元件提供設定檔,以便在執行階段讀取及剖析,例如:
// Create a new domain config in BlobFS with a file at "config/tandem_config.json".
builder.add_domain_config(PackageSetDestination::Blob(PackageDestination::TandemConfigPkg))
.directory("config")
.entry(FileEntry {
source: config_src,
destination: "tandem_config.json".into(),
})?;
您的元件必須以子項形式啟動網域設定套件,並use目錄,例如:
{
children: [
{
name: "tandem-config",
url: "fuchsia-pkg://fuchsia.com/tandem-config#meta/tandem-config.cm",
},
],
use: [
{
directory: "config",
from: "#tandem-config",
path: "/config",
},
],
}
核心引數:核心引數僅用於啟用核心功能。組件會建構指令列,在執行階段傳遞至核心,例如:
builder.kernel_arg(KernelArg::TandemEngDebug);
4. 在產品/看板中啟用這項功能
實作子系統後,您可以在產品或主機板設定檔中,設定步驟 1 中定義的旗標,啟用這項功能。
在產品設定中啟用
如要為特定產品啟用「雙人」功能,請修改其 fuchsia_product_configuration 目標 (通常位於 BUILD.bazel 檔案中):
fuchsia_product_configuration(
name = "my_product",
product_config_json = {
platform = {
# ... other platform settings
tandem = {
enabled = True,
max_connections = 20,
},
},
},
# ... other attributes
)
根據主機板功能啟用
開發板功能可讓開發板聲明支援特定硬體。平台子系統可以讀取主機板 context.board_config.provided_features,有條件地啟用或停用功能。
如果子系統邏輯會檢查主機板功能,請確保主機板設定 (例如 //boards/my_board/BUILD.bazel) 包含在內。
例如:
fuchsia_board_configuration(
name = "my_board",
provided_features = [
"tandem_hw", # This will be seen by context.board_config.provided_features
],
# ...
)
修改設定後,重建產品組合時,系統會納入 Tandem 功能及其在子系統中定義的設定。