結構化設定可讓 C++/Rust 元件直接在資訊清單中宣告設定結構定義。使用結構化設定的好處包括:
- 系統會在建構與組合時偵測設定中的錯誤。
- 您可以使用相同的元件和不同的設定值建立多個套件。
- 元件會透過靜態類型的程式庫讀取其設定。
- 元件架構只會啟動具備有效設定的元件。
- 您可以在執行階段使用
ffx
工具查看設定。 - 這些值可在執行階段設定。
如要進一步瞭解結構化設定的實作和行為,請參閱其參考資料。
如要在元件中使用結構化設定,您必須更新建構規則、宣告結構定義、定義值,並產生用戶端程式庫。
更新建構規則
為避免在產生用戶端程式庫時產生循環依附元件,請定義編譯元件資訊清單的 fuchsia_component_manifest
規則。請將這個已編譯的資訊清單 GN 標籤傳遞至 fuchsia_component
規則。
fuchsia_component_manifest("manifest") {
component_name = "config_example"
manifest = "meta/config_example.cml"
}
fuchsia_component("component") {
cm_label = ":manifest"
deps = [ ":bin" ]
}
宣告設定結構定義
您必須在元件的資訊清單中宣告設定結構定義:
{
...
config: {
greeting: {
type: "string",
max_size: 512,
},
delay_ms: { type: "uint64" },
},
}
結構化設定支援下列類型:
bool
uint8
、int8
uint16
、int16
uint32
、int32
uint64
、int64
string
(需要max_size
屬性)vector
(需要element
和max_count
屬性)
如需設定結構定義的完整語法,請參閱 CML 參考文件。
元件建立設定結構定義後,您必須使用軟體組合或 GN 為宣告的欄位定義值。
僅限 Google 員工:如果您的元件針對 eng 和非參與建構類型分別設定不同設定,則必須先新增結構化設定驗證,才能檢查元件。
有兩種方式可以定義設定值:在 JSON5 檔案中以內嵌方式定義。
使用軟體組合定義設定值
如果您的元件設定會因產品而異,請參閱組合結構化設定說明文件。如果元件的設定僅在測試和實際工作環境之間有所差異,請參閱下一節。
使用 GN 定義及套件設定值
fuchsia_structured_config_values
GN 範本會根據設定結構定義驗證已定義的值,並將其編譯為必須隨元件一併封裝的 .cvf
檔案。
您可以透過兩種方式在 GN 中定義設定值:在 JSON5 檔案或內嵌中定義。
JSON5 檔案
您可以在 JSON5 檔案中編寫元件的設定值。由於 JSON5 是 JSON 的嚴格集合,因此現有的 JSON 設定檔也可重複用於結構化的設定。
JSON 物件中的每個鍵都必須對應結構定義中的設定鍵,且值必須是相容的 JSON 類型:
{
// Print "Hello, World!" by default.
greeting: "World",
// Sleep for only 100ms by default.
delay_ms: 100,
}
在 fuchsia_structured_config_values
規則中提供 JSON5 檔案的路徑。
fuchsia_structured_config_values("values_from_json_file") {
cm_label = ":manifest"
values_source = "../config_example_default_values.json5"
}
內嵌值
fuchsia_structured_config_values
範本也支援以內嵌方式定義設定值:
C++
declare_args() {
# Set this in args.gn to override the greeting emitted by this example.
config_example_cpp_greeting = "World"
}
fuchsia_structured_config_values("values_from_gn") {
cm_label = ":manifest"
values = {
greeting = config_example_cpp_greeting
delay_ms = 100
}
}
Rust
declare_args() {
# Set this in args.gn to override the greeting emitted by this example.
config_example_rust_greeting = "World"
}
fuchsia_structured_config_values("values_from_gn") {
cm_label = ":manifest"
values = {
greeting = config_example_rust_greeting
delay_ms = 100
}
}
使用 declare_args
時,您可以在建構時變更指令列上的設定值:
C++
$ fx set core.x64 \
--with //examples/components/config \
--args='config_example_cpp_greeting="C++ CLI Override"'
Rust
$ fx set core.x64 \
--with //examples/components/config \
--args='config_example_rust_greeting="Rust CLI Override"'
將元件和值打包
如要將元件和一組值套件在一起,請新增 fuchsia_component
和 fuchsia_structured_config_values
規則做為 fuchsia_package
的依附元件。
C++
fuchsia_package("cpp_config_example") {
deps = [
":component",
":values_from_gn",
]
}
Rust
fuchsia_package("rust_config_example") {
deps = [
":component",
":values_from_gn",
]
}
建構系統會驗證元件的設定結構定義和值檔案。含有設定錯誤設定的元件 (例如欄位不相符、錯誤限制、缺少值檔案) 將無法建構。
檢查設定
元件管理員會在元件解析後驗證元件設定。
使用 ffx component show
輸出元件設定鍵/值組合。元件無須執行也能執行這項操作。
$ ffx component show config_example
Moniker: /core/ffx-laboratory:config_example
Component State: Resolved
...
Configuration: greeting -> "World"
...
CVF 檔案必須與元件的設定結構定義完全相符。設定不完整或遺漏的元件無法解析,因此無法啟動。
讀取設定
元件會使用產生的程式庫讀取其解析的設定值。使用下列建構範本產生程式庫:
C++
executable("bin") {
# ...
deps = [
":example_config",
# ...
]
}
fuchsia_structured_config_cpp_elf_lib("example_config") {
cm_label = ":manifest"
}
Rust
rustc_binary("bin") {
edition = "2021"
# ...
deps = [
":example_config",
# ...
]
}
fuchsia_structured_config_rust_lib("example_config") {
cm_label = ":manifest"
}
請使用程式庫中的以下函式來讀取設定值:
C++
// Import the header as if it's located in the same directory as BUILD.gn:
#include "examples/components/config/cpp/example_config.h"
...
// Retrieve configuration
auto c = example_config::Config::TakeFromStartupHandle();
Rust
use example_config::Config;
...
// Retrieve configuration
let config = Config::take_from_startup_handle();
將設定匯出以進行檢查
您可以將元件的設定匯出到檢查,以便在當機報告中使用。用戶端程式庫的函式可將元件設定匯出至檢查樹狀結構:
C++
// Record configuration to inspect
inspect::ComponentInspector inspector(loop.dispatcher(), inspect::PublishOptions{});
inspect::Node config_node = inspector.root().CreateChild("config");
c.RecordInspect(&config_node);
Rust
// Record configuration to inspect
let inspector = fuchsia_inspect::component::inspector();
inspector.root().record_child("config", |config_node| config.record_inspect(config_node));
使用 ffx inspect show
列印元件的匯出設定:
$ ffx inspect show core/ffx-laboratory\*config_example
core/ffx-laboratory\:config_example:
...
payload:
root:
config:
greeting = World
元件外的執行階段值
根據預設,元件只會從解析器傳回的 .cvf
檔案接收設定值,檔案通常來自套件。如要由其他元件取代這些值,必須具有 mutability
屬性,可將其選擇採用特定的變異來源:
config: {
greeting: {
// ...
mutability: [ /* ... */ ],
},
},
提供父項元件的值
當子項已選擇接收值時,父項元件即可為集合中啟動的子項提供值。
首先,子項必須在適當的設定欄位中新增 mutability
屬性:
{
...
config: {
greeting: {
type: "string",
max_size: 512,
mutability: [ "parent" ],
},
},
}
使用 Realm
通訊協定啟動子項時,請將設定值做為覆寫值:
C++
fuchsia_component_decl::Child child_decl;
child_decl.name(child_name);
child_decl.url(kChildUrl);
child_decl.startup(fuchsia_component_decl::StartupMode::kLazy);
fuchsia_component_decl::ConfigOverride greeting_override;
greeting_override.key("greeting");
greeting_override.value(fuchsia_component_decl::ConfigValue::WithSingle(
fuchsia_component_decl::ConfigSingleValue::WithString(expected_greeting)));
child_decl.config_overrides({{greeting_override}});
fuchsia_component_decl::CollectionRef collection;
collection.name(kCollectionName);
fuchsia_component::CreateChildArgs child_args;
realm->CreateChild({collection, child_decl, std::move(child_args)})
.ThenExactlyOnce([this](fidl::Result<fuchsia_component::Realm::CreateChild>& result) {
if (!result.is_ok()) {
FX_LOGS(ERROR) << "CreateChild failed: " << result.error_value();
ZX_PANIC("%s", result.error_value().FormatDescription().c_str());
}
QuitLoop();
});
RunLoop();
Rust
let child_decl = Child {
name: Some(child_name.to_string()),
url: Some(String::from(CHILD_URL)),
startup: Some(StartupMode::Lazy),
config_overrides: Some(vec![ConfigOverride {
key: Some("greeting".to_string()),
value: Some(ConfigValue::Single(ConfigSingleValue::String(
expected_greeting.to_string(),
))),
..ConfigOverride::default()
}]),
..Child::default()
};
let collection_ref = CollectionRef { name: COLLECTION_NAME.to_string() };
realm
.create_child(&collection_ref, &child_decl, CreateChildArgs::default())
.await
.context("sending create child message")?
.map_err(|e| anyhow::format_err!("creating child: {e:?}"))?;
詳情請參閱完整範例。
透過 ffx component
提供值
使用 ffx component create
建立元件或使用 ffx component run
執行元件時,您可以覆寫設定值,就像使用父項元件一樣。
如果您執行上述設定範例且沒有任何覆寫值,就可看到它將預設設定列印至記錄:
$ ffx component run /core/ffx-laboratory:hello-default-value fuchsia-pkg://fuchsia.com/rust_config_from_parent_example#meta/config_example.cm --follow-logs
...
[02655.273631][ffx-laboratory:hello-default-value] INFO: Hello, World! (from Rust)
傳遞 --config
可讓您覆寫該問候語:
$ ffx component run /core/ffx-laboratory:hello-from-parent fuchsia-pkg://fuchsia.com/rust_config_from_parent_example#meta/config_example.cm --config 'greeting="parent component"' --follow-logs
...
[02622.752978][ffx-laboratory:hello-from-parent] INFO: Hello, parent component! (from Rust)`
每個設定欄位都會指定為 KEY=VALUE
,其中 KEY
必須是元件設定結構定義中的欄位 (內含 mutability: [ "parent" ]
,VALUE
則是可剖析為 JSON 且符合該欄位類型的字串)。
使用 Realm Builder 進行測試
您可以使用 Realm Builder 動態替換元件的設定值,無論設定欄位的 mutability
為何。
TODO(https://fxbug.dev/42053123) 新增涵蓋子套件元件使用狀況的區段。
C++
realm_builder.SetConfigValue(child_name, "greeting", "Fuchsia");
Rust
builder.set_config_value(&config_component, "greeting", "Fuchsia".into()).await.unwrap();
Realm 建構工具會根據元件的設定結構定義,驗證替換的值。
以動態方式設定值時,Realm Builder 會要求使用者選擇是否要為已啟動的元件載入封裝設定值。
如要在程式碼中提供部分值時,載入元件的封裝值,請按照下列步驟操作:
C++
realm_builder.InitMutableConfigFromPackage(child_name);
Rust
builder.init_mutable_config_from_package(&config_component).await.unwrap();
如要在不使用封裝值的情況下,在程式碼中設定元件的所有值,請按照下列步驟操作:
C++
realm_builder.InitMutableConfigToEmpty(child_name);
Rust
builder.init_mutable_config_to_empty(&config_component).await.unwrap();