<<../../_common/components/_declaring_intro.md>>
<<../../_common/components/_declaring_manifests.md>>
<<../../_common/components/_declaring_shards.md>>
The Fuchsia build system provides templates as GN imports in //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:
{% 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:
{% 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.
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 component called echo-args
in the //vendor/fuchsia-codelab
directory:
mkdir -p vendor/fuchsia-codelab/echo-args
Create the following file and directory structure in the new project directory:
{Rust}
//vendor/fuchsia-codelab/echo-args |- BUILD.gn |- meta | |- echo.cml | |- src |- main.rs
BUILD.gn
: GN build targets for the executable binaries, component, and package.meta/echo.cml
: Manifest declaring the component's executable and required capabilities.src/main.rs
: Source code for the Rust executable binary and unit tests.{C++}
//vendor/fuchsia-codelab/echo-args |- BUILD.gn |- meta | |- echo.cml | |- echo_component.cc |- echo_component.h |- main.cc
BUILD.gn
: GN build targets for the executable binaries, component, and package.meta/echo.cml
: Manifest declaring the component's executable and required capabilities.echo_component.cc
: Source code for the C++ component functionality.main.cc
: Source code for the C++ executable binary main entry point.The component manifest file defines the attributes of the component‘s executable, including program arguments, and the component’s capabilities. Add the following contents to meta/echo.cml
:
{Rust}
echo-args/meta/echo.cml
:
{ include: [ // Enable logging. "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/echo-args", // Program arguments args: [ "Alice", "Bob", ], // Program environment variables environ: [ "FAVORITE_ANIMAL=Spot" ], }, }
{C++}
echo-args/meta/echo.cml
:
{ include: [ // Enable logging. "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/echo-args", // Program arguments args: [ "Alice", "Bob", ], // Program environment variables environ: [ "FAVORITE_ANIMAL=Spot" ], }, }
Open the source file for the main executable and add the following import statements:
{Rust}
echo-args/src/main.rs
:
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/echo/rust/src/main.rs" region_tag="imports" adjust_indentation="auto" %}
{C++}
echo-args/main.cc
:
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/echo/cpp/main.cc" region_tag="imports" adjust_indentation="auto" %} #include "vendor/fuchsia-codelab/echo-args/echo_component.h"
Add the following code to implement the main()
function:
{Rust}
echo-args/src/main.rs
:
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/echo/rust/src/main.rs" region_tag="main" adjust_indentation="auto" %}
{C++}
echo-args/main.cc
:
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/echo/cpp/main.cc" region_tag="main" adjust_indentation="auto" %}
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:
{Rust}
echo-args/src/main.rs
:
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/echo/rust/src/main.rs" region_tag="greeting" adjust_indentation="auto" %}
{C++}
echo-args/echo_component.h
:
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/echo/cpp/echo_component.h" region_tag="greeting" adjust_indentation="auto" %}
echo-args/echo_component.cc
:
#include "vendor/fuchsia-codelab/echo-args/echo_component.h" {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/echo/cpp/echo_component.cc" 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.
Update the program's dependencies in your BUILD.gn
file:
{Rust}
echo-args/BUILD.gn
:
import("//build/components.gni") import("//build/rust/rustc_binary.gni") group("echo-args") { testonly = true deps = [ ":package", ] } rustc_binary("bin") { output_name = "echo-args" edition = "2021" with_unit_tests = true deps = [ "//src/lib/fuchsia", "//third_party/rust_crates:anyhow", "//third_party/rust_crates:tracing", ] sources = [ "src/main.rs" ] } fuchsia_component("component") { component_name = "echo-args" manifest = "meta/echo.cml" deps = [ ":bin" ] } fuchsia_package("package") { package_name = "echo-args" deps = [ ":component" ] }
{C++}
echo-args/BUILD.gn
:
import("//build/components.gni") group("echo-args") { testonly = true deps = [ ":package", ] } source_set("lib") { sources = [ "echo_component.cc", "echo_component.h", ] } executable("bin") { output_name = "echo-args" sources = [ "main.cc" ] deps = [ ":lib", "//sdk/lib/syslog/cpp", "//zircon/system/ulib/async-default", "//zircon/system/ulib/async-loop:async-loop-cpp", "//zircon/system/ulib/async-loop:async-loop-default", ] } fuchsia_component("component") { component_name = "echo-args" manifest = "meta/echo.cml" deps = [ ":bin" ] } fuchsia_package("package") { package_name = "echo-args" deps = [ ":component" ] }
Add your new component to the build configuration:
fx set workstation_eng.qemu-x64 --with //vendor/fuchsia-codelab/echo-args
Run fx build
and verify that the build completes successfully:
fx build
In the next section, you'll integrate this component into the build and test the output in the system log.