| # Declaring components |
| |
| Every component has a declaration that describes the component's attributes and |
| capabilities. For components that are distributed in packages, the declaration |
| is expressed using a **component manifest file** and loaded with the help of a |
| **component resolver**. |
| |
| {: width="836"} |
| |
| You declare components using component manifest language (CML) files. At build |
| time, the Component Manifest Compiler (`cmc`) tool validates and compiles the |
| manifest source into a binary format (`.cm`) and stores it in the component's |
| package. At runtime, component resolvers load the manifest into a |
| [ComponentDecl](https://fuchsia.dev/reference/fidl/fuchsia.sys2#ComponentDecl) |
| FIDL structure for [Component Manager](/docs/glossary/README.md#Component-Manager). |
| |
| ## Component manifests |
| |
| CML files are JSON5 files that end with a `.cml` extension. Below is an example |
| CML manifest file for a simple component running an ELF binary that prints a |
| "Hello, World" message to the system log: |
| |
| ```json5 |
| { |
| // Information about the program to run. |
| program: { |
| // Use the built-in ELF runner. |
| runner: "elf", |
| // The binary to run for this component. |
| binary: "bin/hello", |
| // Program arguments |
| args: [ |
| "Hello", |
| "World!", |
| ], |
| }, |
| |
| // Capabilities used by this component. |
| use: [ |
| { protocol: "fuchsia.logger.LogSink" }, |
| ], |
| } |
| ``` |
| |
| This file declares two main sections of information about the component: |
| |
| Note: For more details on component manifests and CML syntax, see |
| [component manifests](/docs/concepts/components/v2/component_manifests.md). |
| |
| * `program`: Describes the executable information such as the binary file, |
| program arguments, and the associated runtime. In this example, a binary |
| written in C++ or Rust is compiled as an ELF executable and uses the built-in |
| [ELF runner](/docs/concepts/components/v2/elf_runner.md). |
| * `use`: Declares the capabilities this component requires to run. In this |
| example, the `fuchsia.logger.LogSink` protocol enables the component to write |
| messages to the system log (`syslog`). |
| |
| ## Manifest shards |
| |
| Some collections of capabilities represent use case requirements that are common |
| to many components in the system, such as logging. To simplify including these |
| capabilities in your components, the framework abstracts them into |
| **manifest shards** that can be included in your CML source file. |
| |
| Below is an equivalent CML to the previous example. In this case, the necessary |
| logging capabilities are provided by including |
| `diagnostics/syslog/client.shard.cml` instead of declaring |
| `fuchsia.logger.LogSink` explicitly: |
| |
| ```json5 |
| { |
| include: [ "syslog/client.shard.cml" ], |
| |
| // Information about the program to run. |
| program: { |
| // Use the built-in ELF runner. |
| runner: "elf", |
| // The binary to run for this component. |
| binary: "bin/hello-world", |
| // Program arguments |
| args: [ |
| "Hello", |
| "World!", |
| ], |
| }, |
| } |
| ``` |
| |
| <aside class="key-point"> |
| To review the merged CML output with all includes resolved, run the |
| <code>fx cmc include</code> command with your manifest. For more details, see |
| the <a href="/docs/reference/tools/sdk/cmc.md"> reference documentation</a>. |
| </aside> |
| |
| ## Building components |
| |
| The Fuchsia build system provides templates as GN imports in |
| [`//build/components.gni`](/build/components.gni) to build and package software |
| into Fuchsia components. Below is an example of a `BUILD.gn` file for a simple |
| C++ component: |
| |
| ```gn |
| {% verbatim %} |
| import("//build/components.gni") |
| |
| executable("bin") { |
| sources = [ "main.cc" ] |
| } |
| |
| resource("my_file") { |
| sources = [ "my_file.txt" ] |
| outputs = [ "data/{{source_file_part}}" ] |
| } |
| |
| fuchsia_component("hello-world-component") { |
| component_name = "hello-world" |
| deps = [ |
| ":bin", |
| ":my_file", |
| ] |
| manifest = "meta/hello-world.cml" |
| } |
| |
| fuchsia_package("hello-world") { |
| package-name = "hello-world" |
| deps = [ |
| ":hello-world-component", |
| ] |
| } |
| {% endverbatim %} |
| ``` |
| |
| This file contains the following main elements: |
| |
| * `executable()`: Compiles the source code into a binary. This target varies |
| depending on the programming language. For example, an `executable` target |
| can be used for C++, `rustc_binary` for Rust, `go_binary` for Golang. |
| * `resource()`: Optional named collection of data files to copy as resources |
| into another GN target. These files are accessible to the binary inside the |
| component's namespace. |
| * `fuchsia_component()`: Collects the binary, component manifest, and additional |
| resources together into a single target. This target compiles the manifest |
| source into a component declaration using `cmc`. |
| * `fuchsia_package()`: Unit of distribution for components. Allows one or more |
| components to be hosted in a package repository and included in the target |
| device's package sets. This target generates the package metadata and builds |
| the Fuchsia Archive (`.far`) file. |
| |
| Packages can contain multiple components, listed as `deps` in the |
| `fuchsia_package()` template. You can simplify the build file for packages |
| containing only one component using the `fuchsia_package_with_single_component()` |
| template. |
| |
| The following simplified `BUILD.gn` example is equivalent to to the previous |
| example: |
| |
| ```gn |
| {% verbatim %} |
| import("//build/components.gni") |
| |
| executable("bin") { |
| sources = [ "main.cc" ] |
| } |
| |
| resource("my_file") { |
| sources = [ "my_file.txt" ] |
| outputs = [ "data/{{source_file_part}}" ] |
| } |
| |
| fuchsia_package_with_single_component("hello-world") { |
| manifest = "meta/hello-world.cml" |
| deps = [ |
| ":bin", |
| ":my_file", |
| ] |
| } |
| {% endverbatim %} |
| ``` |
| |
| Note: For more details on the GN syntax of the component build rules, see the |
| [components build reference](/docs/development/components/build.md). |
| |
| ## Exercise: Create a new component |
| |
| In this exercise, you'll build and run a basic component that reads the program |
| arguments and echoes a greeting out the system log. |
| |
| To begin, create a project scaffold for a new Rust component called `echo-args` |
| under the `//vendor/fuchsia-codelab` directory: |
| |
| ```posix-terminal |
| fx create component --path vendor/fuchsia-codelab/echo-args --lang rust |
| ``` |
| |
| This creates a project directory structure with a basic component template: |
| |
| ```none {:.devsite-disable-click-to-copy} |
| echo-args |
| |- BUILD.gn |
| |- meta |
| | |- echo_args.cml |
| | |
| |- src |
| |- main.rs |
| ``` |
| |
| * `BUILD.gn`: GN build targets for the executable binaries, component, and |
| package. |
| * `meta/echo_args.cml`: Manifest declaring the component's executable and |
| required capabilities. |
| * `src/main.rs`: Source code for the Rust executable binary and unit tests. |
| |
| ### Add program arguments |
| |
| Open the `echo_args.cml` component manifest file in your editor and locate the |
| `program` block. This defines the attributes of the component's executable. |
| Add an `args` array to supply the list of names to greet: |
| |
| `echo-args/meta/echo_args.cml`: |
| |
| ```json5 |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/echo/meta/echo.cml" region_tag="manifest" adjust_indentation="auto" highlight="15,16,17,18,19,21,22" %} |
| ``` |
| |
| ### Log the arguments |
| |
| Open the `main.rs` source file and replace the import statements with the |
| following code: |
| |
| `echo-args/src/main.rs`: |
| |
| ```rust |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/echo/src/main.rs" region_tag="imports" adjust_indentation="auto" %} |
| ``` |
| |
| Replace the `main()` function with the following code: |
| |
| `echo-args/src/main.rs`: |
| |
| ```rust |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/echo/src/main.rs" region_tag="main" adjust_indentation="auto" %} |
| ``` |
| |
| <aside class="key-point"> |
| The <code>fuchsia::component</code> attribute removes some common boilerplate |
| for component execution in Rust, such as initializing logging or async execution |
| behavior. |
| </aside> |
| |
| This code reads the program arguments and passes them to a function called |
| `greeting()` to generate a response for the syslog entry. |
| |
| Add the following code to implement the `greeting()` function: |
| |
| `echo-args/src/main.rs`: |
| |
| ```rust |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/echo/src/main.rs" region_tag="greeting" adjust_indentation="auto" %} |
| ``` |
| |
| This function creates a simple string from the list of provided arguments based |
| on the length of the list. |
| |
| <aside class="key-point"> |
| <b>Logging and standard streams</b> |
| <p>Fuchsia has two main logging buffers; the system log (<code>syslog</code>) |
| and debug log (<code>klog</code>). By default, components do not have stream |
| handles for stdout and stderr available to record log messages from your code. |
| Instead, you must use one of Fuchsia's logging libraries or redirect these |
| streams to a Fuchsia log buffer.</p> |
| |
| <p>For more details on logging from your code, see |
| <a href="/docs/development/diagnostics/logs/recording.md">Recording Logs</a>.</p> |
| </aside> |
| |
| ### Add to the build configuration |
| |
| Add your new component to the build configuration: |
| |
| ```posix-terminal |
| fx set workstation.qemu-x64 --with //vendor/fuchsia-codelab/echo-args |
| ``` |
| |
| Run `fx build` and verify that the build completes successfully: |
| |
| ```posix-terminal |
| fx build |
| ``` |
| |
| In the next section, you'll integrate this component into the build and test the |
| output in the system log. |