| # Building components {#building-components} |
| |
| This document demonstrates how to build and test a component, highlighting best |
| practices for defining packages, components, and their tests. |
| |
| ## Concepts {#concepts} |
| |
| You should understand the following concepts before building a component: |
| |
| **[Packages][glossary-package]** are the unit of software distribution on |
| Fuchsia. Packages are a collection of files with associated paths that are |
| relative to the base of the package. For instance, a package might contain an |
| ELF binary under the path `bin/hello_world`, and a JSON file under the path |
| `data/config.json`. Grouping files into a package is required in order to push |
| these files to the device. |
| |
| **[Components][glossary-component]** are the unit of software execution on |
| Fuchsia. All software on Fuchsia except for the kernel image and usermode |
| bootstrap program is defined as a component. |
| |
| A component is defined by a |
| **[component manifest][glossary-component-manifest]**. Components typically |
| include additional files, such as executables and data assets that they need at |
| runtime. |
| |
| Developers must define their software in terms of packages and components, |
| whether for building production software or for writing their tests. |
| |
| At runtime, **[Component instances][glossary-component-instance]** see the |
| contents of their package as read-only files under the path `/pkg`. Defining two |
| or more components in the same package doesn't grant each component access to |
| the other's capabilities. However it can guarantee to one component that the |
| other is available. Therefore if a component attempts to launch an instance of |
| another component, such as in an integration test, it can be beneficial to |
| package both components together. |
| |
| Components are instantiated in a few ways, all of which somehow specify their |
| [URL][glossary-component-url]. Typically components are launched by specifying |
| their package names and path to their component manifest in the package, using |
| the **[`fuchsia-pkg://` scheme][glossary-fuchsia-pkg-url]**. |
| |
| ## GN templates |
| |
| [GN][glossary-gn] is the meta-build system used by Fuchsia. Fuchsia extends GN |
| by defining templates. Templates provide a way to add to GN's built-in target |
| types. This section reviews various GN templates that can be used to define |
| packages, components, and their tests. |
| |
| ### Defining components, packages, and tests using GN templates {#defining} |
| |
| Below is a hypothetical package containing one component that runs a C++ |
| program and a data file. The example uses the following templates: |
| |
| * [`fuchsia_component.gni`](/src/sys/build/fuchsia_component.gni) |
| * [`fuchsia_package.gni`](/src/sys/build/fuchsia_package.gni) |
| |
| ```gn |
| import("//src/sys/build/components.gni") |
| |
| executable("my_program") { |
| sources = [ "my_program.cc" ] |
| } |
| |
| fuchsia_component("my_component") { |
| manifest = "meta/my_program.cmx" |
| deps = [ ":my_program" ] |
| } |
| |
| fuchsia_package("my_package") { |
| deps = [ ":my_component" ] |
| } |
| ``` |
| |
| The file `my_program.cmx` should include at least the following: |
| |
| ```json |
| { |
| "program": { |
| "binary": "bin/my_program" |
| } |
| } |
| ``` |
| |
| Note the following details: |
| |
| * This example imports `"//src/sys/build/components.gni"`. This single import |
| includes all templates related to packages, components, and testing them. |
| * This example defines an `executable()`, which is used to build a C++ |
| program. This is for illustrative purposes - a component can launch all |
| sorts of programs. |
| * This example defines a `fuchsia_component()`, which depends on the |
| `executable()`. The component definition attaches a manifest, which |
| references the executable by its packaged path: `bin/my_program`. |
| For more details on the packaged path, see |
| [finding paths for built executables](#finding-paths-for-built-executables). |
| * The manifest must be either a `.cmx` file in [cmx format][cmx-format] or a |
| `.cml` file in [cml format][cml-format]. |
| * The destination path for the manifest is not specified, but rather |
| inferred from the component's name. In this example, the manifest path will |
| be `meta/my_component.cmx`. |
| * Both the component and package names are derived from their target names. |
| They both take an optional `component_name` and `package_name` parameter |
| respectively as an override. \ |
| In the example above, these names come together to form the URL for |
| launching the component: |
| `fuchsia-pkg://fuchsia.com/my_package#meta/my_component.cmx`. |
| |
| ### Language-specific component examples {#language-specific-component-examples} |
| |
| Below you'll find basic examples for defining a package with a single component |
| that launches a program in a variety of commonly used languages. The referenced |
| source files and component manifest are assumed to be present in the specified |
| paths. |
| |
| * {C++} |
| |
| ```gn |
| import("//src/sys/build/components.gni") |
| |
| executable("bin") { |
| output_name = "my_program" |
| sources = [ "main.cc" ] |
| } |
| |
| fuchsia_component("my_component") { |
| manifest = "meta/my_component.cmx" |
| deps = [ ":bin" ] |
| } |
| |
| fuchsia_package("my_package") { |
| deps = [ ":my_component" ] |
| } |
| ``` |
| |
| It's assumed that the file `meta/my_component.cmx` |
| contains at least the following: |
| |
| ```json |
| { |
| "program": { |
| "binary": "bin/my_program" |
| } |
| } |
| ``` |
| |
| * {Rust} |
| |
| ```gn |
| import("//build/rust/rustc_binary.gni") |
| import("//src/sys/build/components.gni") |
| |
| rustc_binary("bin") { |
| output_name = "my_program" |
| } |
| |
| fuchsia_component("my_component") { |
| manifest = "meta/my_component.cmx" |
| deps = [ ":bin" ] |
| } |
| |
| fuchsia_package("my_package") { |
| deps = [ ":my_component" ] |
| } |
| ``` |
| |
| It's assumed that the file `meta/my_component.cmx` |
| contains at least the following: |
| |
| ```json |
| { |
| "program": { |
| "binary": "bin/my_program" |
| } |
| } |
| ``` |
| |
| * {Go} |
| |
| ```gn |
| import("//build/go/go_binary.gni") |
| import("//src/sys/build/components.gni") |
| |
| go_binary("bin") { |
| output_name = "my_program" |
| } |
| |
| fuchsia_component("my_component") { |
| manifest = "meta/my_component.cmx" |
| deps = [ ":bin" ] |
| } |
| |
| fuchsia_package("my_package") { |
| deps = [ ":my_component" ] |
| } |
| ``` |
| |
| It's assumed that the file `meta/my_component.cmx` |
| contains at least the following: |
| |
| ```json |
| { |
| "program": { |
| "binary": "bin/my_program" |
| } |
| } |
| ``` |
| |
| * {Dart} |
| |
| ```gn |
| import("//build/dart/dart_component.gni") |
| import("//build/dart/dart_library.gni") |
| import("//src/sys/build/components.gni") |
| |
| dart_library("lib") { |
| package_name = "my_lib" |
| sources = [ "main.dart" ] |
| } |
| |
| dart_component("my_component") { |
| manifest = "meta/my_component.cmx" |
| deps = [ ":lib" ] |
| } |
| |
| fuchsia_package("my_package") { |
| deps = [ ":my_component" ] |
| } |
| ``` |
| |
| It's assumed that the file `meta/my_component.cmx` |
| contains at least the following: |
| |
| ```json |
| { |
| "program": { |
| "data": "data/my_component" |
| } |
| } |
| ``` |
| |
| * {Flutter} |
| |
| ```gn |
| import("//build/dart/dart_library.gni") |
| import("//build/flutter/flutter_component.gni") |
| import("//src/sys/build/components.gni") |
| |
| dart_library("lib") { |
| package_name = "my_lib" |
| sources = [ "main.dart" ] |
| } |
| |
| flutter_component("my_component") { |
| manifest = "meta/my_component.cmx" |
| deps = [ ":lib" ] |
| } |
| |
| fuchsia_package("my_package") { |
| deps = [ ":my_component" ] |
| } |
| ``` |
| |
| It's assumed that the file `meta/my_component.cmx` |
| contains at least the following: |
| |
| ```json |
| { |
| "program": { |
| "data": "data/my_component" |
| } |
| } |
| ``` |
| |
| ### Test packages {#test-packages} |
| |
| Test packages are packages that contain at least one component that is |
| launched as a test. Test packages are defined using |
| [`fuchsia_test_package.gni`](/src/sys/build/fuchsia_test_package.gni). This |
| template can be used to define all sorts of tests, although it's most useful for |
| integration tests -- tests where other components in addition to the test itself |
| participate in the test. See [below](#unit-tests) for templates that specialize |
| in unit testing. |
| |
| ```gn |
| import("//src/sys/build/components.gni") |
| |
| executable("my_test") { |
| sources = [ "my_test.cc" ] |
| testonly = true |
| deps = [ |
| "//src/lib/fxl/test:gtest_main", |
| "//third_party/googletest:gtest", |
| ] |
| } |
| |
| fuchsia_component("my_test_component") { |
| testonly = true |
| manifest = "meta/my_test.cmx" |
| deps = [ ":my_test" ] |
| } |
| |
| executable("my_program_under_test") { |
| sources = [ "my_program_under_test.cc" ] |
| } |
| |
| fuchsia_component("my_other_component_under_test") { |
| manifest = "meta/my_component_under_test.cmx" |
| deps = [ ":my_program_under_test" ] |
| } |
| |
| fuchsia_test_package("my_integration_test") { |
| test_components = [ ":my_test_component" ] |
| deps = [ ":my_other_component_under_test" ] |
| test_specs = { |
| environments = [ vim3_env ] |
| } |
| } |
| |
| group("tests") { |
| deps = [ ":my_integration_test" ] |
| testonly = true |
| } |
| ``` |
| |
| Note the following details: |
| |
| * This example defines `"my_test_component"`, which is assumed to implement a |
| test. Commonly this is done using some testing framework such as C++ |
| Googletest, Rust Cargo test, etc'. |
| * To launch this test, you can use [`fx test`][fx-test]. |
| * The test is packaged with another component, |
| `"my_other_component_under_test"`. Presumably this component participates in |
| the test. For instance, the component under test might implement a protocol, |
| and the test launches it and connects to it as a client while asserting |
| correct behavior from the client's perspective. \ |
| Packaging the component under test together with the test component |
| guarantees that the component under test is available for launch while |
| the test is running, and built at the same version as the test. If |
| this weren't the case, and instead the test assumed that the component under |
| test was present in another package that's already installed on the target |
| device, then the test would be exposed to side effects and version skew. |
| Packaging the test with its dependencies makes it more hermetic. |
| * Note the `environments` parameter. `fuchsia_test_package()` can optionally |
| take [`test_spec.gni`](/build/testing/test_spec.gni) parameters to override |
| the default testing behavior. In this example, this test is configured to |
| run on VIM3 devices. |
| * Finally, this example defines a `group()` to contain all the tests (which we |
| have exactly one of). This is a [recommended practice][source-code-layout] |
| for organizing targets across the source tree. |
| |
| An important limitation of `fuchsia_test_package()` is that any |
| `test_component` targets must be defined in the same `BUILD.gn` file as the |
| test package target. This is due to a [limitation in GN][gn-get-target-outputs]. |
| |
| It's possible to work around this limitation with an indirection through |
| `fuchsia_test()`. In one `BUILD.gn` file, define: |
| |
| ```gn |
| # Let this be //foo/BUILD.gn |
| import("//src/sys/build/components.gni") |
| |
| executable("my_test") { |
| sources = [ "my_test.cc" ] |
| testonly = true |
| deps = [ |
| "//src/lib/fxl/test:gtest_main", |
| "//third_party/googletest:gtest", |
| ] |
| } |
| |
| fuchsia_component("my_test_component") { |
| testonly = true |
| manifest = "meta/my_test.cmx" |
| deps = [ ":my_test" ] |
| } |
| |
| fuchsia_test("my_test_component_test") { |
| package = "//bar:my_test_package" |
| component = ":my_test_component" |
| } |
| |
| group("tests") { |
| testonly = true |
| deps = [ ":my_test_component_test" ] |
| } |
| ``` |
| |
| Then elsewhere, you can add the `fuchsia_component()` target to the `deps` of a |
| `fuchsia_package()` target. |
| |
| ```gn |
| # Let this be //bar/BUILD.gn |
| import("//src/sys/build/components.gni") |
| |
| fuchsia_package("my_test_package") { |
| testonly = true |
| deps = [ "//foo:my_test_component" ] |
| } |
| ``` |
| |
| This is slightly more verbose but achieves the same outcome. |
| |
| #### Dart and Flutter tests |
| |
| Dart and Flutter tests differ slightly in that they need to be built with a |
| `flutter_test_component()`, which collects all of the test mains into a single |
| main invocation. The `flutter_test_component()` can then be used by the |
| `fuchsia_test_package()`. |
| |
| ```gn |
| import("//build/dart/dart_test_component.gni") |
| import("//build/flutter/flutter_test_component.gni") |
| import("//src/sys/build/components.gni") |
| |
| flutter_test_component("my_flutter_test_component") { |
| testonly = true |
| manifest = "meta/my_flutter_test_component.cmx" |
| sources = [ "foo_flutter_test.dart" ] |
| } |
| |
| dart_test_component("my_dart_test_component") { |
| testonly = true |
| manifest = "meta/my_dart_test_component.cmx" |
| sources = [ "foo_dart_test.dart" ] |
| } |
| |
| fuchsia_test("my_test_component_test") { |
| test_components = [ |
| ":my_dart_test_component", |
| ":my_flutter_test_component" |
| ] |
| } |
| ``` |
| |
| ### Unit tests {#unit-tests} |
| |
| Since unit tests are very common, two simplified templates are provided: |
| |
| * [`fuchsia_unittest_component.gni`](/src/sys/build/fuchsia_unittest_component.gni) defines a |
| component to be run as a test, with the option to automatically generate a basic component |
| manifest, that must then be included in a package. |
| * [`fuchsia_unittest_package.gni`](/src/sys/build/fuchsia_unittest_package.gni) defines a |
| package with a single component to be run as a test, shorthand for a single |
| `fuchsia_unittest_component()` target paired with a `fuchsia_test_package()`. |
| |
| #### Unit tests with manifests {#unit-tests-with-manifests} |
| |
| The examples below demonstrate building a test executable and defining a |
| package and component for the test. |
| |
| * {C++} |
| |
| ```gn |
| import("//src/sys/build/components.gni") |
| |
| executable("my_test") { |
| sources = [ "test.cc" ] |
| deps = [ |
| "//src/lib/fxl/test:gtest_main", |
| "//third_party/googletest:gtest", |
| ] |
| testonly = true |
| } |
| |
| fuchsia_unittest_package("my_test") { |
| manifest = "meta/my_test.cmx" |
| deps = [ ":my_test" ] |
| } |
| ``` |
| |
| * {Rust} |
| |
| ```gn |
| import("//build/rust/rustc_test.gni") |
| import("//src/sys/build/components.gni") |
| |
| rustc_test("my_test") {} |
| |
| fuchsia_unittest_package("my_test") { |
| manifest = "meta/my_test.cmx" |
| deps = [ ":my_test" ] |
| } |
| ``` |
| |
| * {Go} |
| |
| ```gn |
| import("//build/go/go_test.gni") |
| import("//src/sys/build/components.gni") |
| |
| go_test("my_test") {} |
| |
| fuchsia_unittest_package("my_test") { |
| manifest = "meta/my_test.cmx" |
| deps = [ ":my_test" ] |
| } |
| ``` |
| |
| The manifest file `meta/my_test.cmx` may look like this: |
| |
| ```json |
| { |
| "program": { |
| "binary": "bin/my_test" |
| } |
| } |
| ``` |
| |
| The above is a minimal valid manifest file for this test. In practice a test |
| might require additional capabilities, to be specified in its manifest. |
| |
| The launch URL for the test is |
| `fuchsia-pkg://fuchsia.com/my_test#meta/my_test.cmx`. It can be launched using |
| `fx test` followed by the launch URL, or followed by the GN target name. |
| |
| #### Unit tests with _generated_ manifests |
| |
| The examples above specify a manifest for the test. However, it's possible for |
| unit tests to not require any particular capabilities. |
| |
| Below is an example for a test that performs ROT13 encryption and decryption. |
| The algorithm under test is pure logic that can be tested in complete |
| isolation. If we were to write a manifest for these tests, it would only |
| contain the test binary to be executed. In such cases, we can simply specify |
| the test executable path, and the template generates the trivial manifest |
| for us. |
| |
| * {C++} |
| |
| ```gn |
| import("//src/sys/build/components.gni") |
| |
| executable("rot13_test") { |
| sources = [ "rot13_test.cc" ] |
| deps = [ |
| "//src/lib/fxl/test:gtest_main", |
| "//third_party/googletest:gtest", |
| ] |
| testonly = true |
| } |
| |
| fuchsia_unittest_package("rot13_test") { |
| deps = [ ":rot13_test" ] |
| } |
| ``` |
| |
| * {Rust} |
| |
| ```gn |
| import("//build/rust/rustc_test.gni") |
| import("//src/sys/build/components.gni") |
| |
| rustc_test("rot13_test") {} |
| |
| fuchsia_unittest_package("rot13_test") { |
| deps = [ ":rot13_test" ] |
| } |
| ``` |
| |
| * {Go} |
| |
| ```gn |
| import("//build/go/go_test.gni") |
| import("//src/sys/build/components.gni") |
| |
| go_test("rot13_test") {} |
| |
| fuchsia_unittest_package("rot13_test") { |
| deps = [ ":rot13_test" ] |
| } |
| ``` |
| |
| It's also possible to generate multiple unit test components and include them in |
| a single package. |
| |
| ```gn |
| import("//src/sys/build/components.gni") |
| |
| fuchsia_unittest_component("rot13_encrypt_test") { |
| ... |
| } |
| |
| fuchsia_unittest_component("rot13_decrypt_test") { |
| ... |
| } |
| |
| fuchsia_test_package("rot13_tests") { |
| test_components = [ |
| ":rot13_encrypt_test", |
| ":rot13_decrypt_test", |
| ] |
| } |
| ``` |
| |
| The generated component manifest file can be found as follows: |
| |
| <pre class="prettyprint"> |
| <code class="devsite-terminal">fx gn outputs out/default <var>unittest target</var>_generated_manifest</code> |
| </pre> |
| |
| To print it directly: |
| |
| <pre class="prettyprint"> |
| <code class="devsite-terminal">fx build && cat out/default/$(fx gn outputs out/default <var>unittest target</var>_generated_manifest)</code> |
| </pre> |
| |
| Note that `fx gn outputs` prints an output path, but the file at the path |
| may not exist or may be stale if you haven't built. |
| |
| To launch the test: |
| |
| ```bash |
| # By launch URL |
| fx test fuchsia-pkg://fuchsia.com/rot13_test#meta/rot13_test.cmx |
| # By GN target name |
| fx test rot13_test |
| ``` |
| |
| See also: [`fx test`][fx-test] |
| |
| You can generate a [CFv2 test component][v2-test-component] by specifying: |
| |
| ```gn |
| import("//src/sys/build/components.gni") |
| |
| fuchsia_unittest_package("rot13_test") { |
| v2 = true |
| ... |
| } |
| ``` |
| |
| Or: |
| |
| ```gn |
| import("//src/sys/build/components.gni") |
| |
| fuchsia_unittest_component("rot13_encrypt_test") { |
| v2 = true |
| ... |
| } |
| |
| fuchsia_unittest_component("rot13_decrypt_test") { |
| v2 = true |
| ... |
| } |
| |
| fuchsia_test_package("rot13_tests") { |
| test_components = [ |
| ":rot13_encrypt_test", |
| ":rot13_decrypt_test", |
| ] |
| } |
| ``` |
| |
| #### Multiple unit tests in a single package |
| |
| The `fuchsia_unittest_component()` rule can be used instead of |
| `fuchsia_unittest_package()` to include multiple components in a single |
| `fuchsia_test_package()`. This can be useful to easily run all the test components |
| in a single package, e.g. with `fx test <package_name>`, rather than needing to |
| type many separate package names. |
| |
| The example below creates a single test package `rot13_tests` that contains two |
| separate test components, `rot13_decoder_test` and `rot13_encoder_test`. |
| Both tests can be run using `fx test rot13_tests`, or individual tests can be run |
| using either `fx test rot13_decoder_test` or the full URL |
| `fx test fuchsia-pkg://fuchsia.com/rot13_tests#meta/rot13_decoder_test.cmx`. |
| |
| * {C++} |
| |
| ```gn |
| import("//src/sys/build/components.gni") |
| |
| executable("rot13_decoder_bin_test") {} |
| |
| executable("rot13_encoder_bin_test") {} |
| |
| fuchsia_unittest_component("rot13_decoder_test") { |
| deps = [ ":rot13_decoder_bin_test" ] |
| } |
| |
| fuchsia_unittest_component("rot13_encoder_test") { |
| deps = [ ":rot13_encoder_bin_test" ] |
| } |
| |
| fuchsia_test_package("rot13_tests") { |
| test_components = [ |
| ":rot13_decoder_test", |
| ":rot13_encoder_test", |
| ] |
| } |
| ``` |
| |
| * {Rust} |
| |
| ```gn |
| import("//build/rust/rustc_test.gni") |
| import("//src/sys/build/components.gni") |
| |
| rustc_test("rot13_decoder_bin_test") {} |
| |
| rustc_test("rot13_encoder_bin_test") {} |
| |
| fuchsia_unittest_component("rot13_decoder_test") { |
| deps = [ ":rot13_decoder_bin_test" ] |
| } |
| |
| fuchsia_unittest_component("rot13_encoder_test") { |
| deps = [ ":rot13_encoder_bin_test" ] |
| } |
| |
| fuchsia_test_package("rot13_tests") { |
| test_components = [ |
| ":rot13_decoder_test", |
| ":rot13_encoder_test", |
| ] |
| } |
| ``` |
| |
| * {Go} |
| |
| ```gn |
| import("//build/go/go_test.gni") |
| import("//src/sys/build/components.gni") |
| |
| go_test("rot13_decoder_test") {} |
| |
| go_test("rot13_encoder_test") {} |
| |
| fuchsia_unittest_component("rot13_decoder_test") { |
| deps = [ ":rot13_decoder_bin_test" ] |
| } |
| |
| fuchsia_unittest_component("rot13_encoder_test") { |
| deps = [ ":rot13_encoder_bin_test" ] |
| } |
| |
| fuchsia_test_package("rot13_tests") { |
| test_components = [ |
| ":rot13_decoder_test", |
| ":rot13_encoder_test", |
| ] |
| } |
| ``` |
| |
| ### Packages with a single component {#packages-with-single-component} |
| |
| Packages are units of distribution. It is beneficial to define multiple |
| components in the same package if you need to guarantee that several |
| components are always co-present, or if you'd like to be able to update |
| several components at once (by updating a single package). |
| |
| This pattern is also commonly used to create hermetic integration tests. |
| For instance an integration test between two components where one is a client |
| of a service implemented in another component would include both the client |
| and server components. |
| |
| However, you may often define a package that only requires a single component. |
| In those cases, you can use the `fuchsia_package_with_single_component()` |
| template as a convenience. This template fuses together `fuchsia_package()` and |
| `fuchsia_component()`. |
| |
| * {C++} |
| |
| ```gn |
| import("//src/sys/build/components.gni") |
| |
| executable("rot13_encoder_decoder") { |
| sources = [ "rot13_encoder_decoder.cc" ] |
| } |
| |
| fuchsia_package_with_single_component("rot13") { |
| manifest = "meta/rot13.cmx" |
| deps = [ ":rot13_encoder_decoder" ] |
| } |
| ``` |
| |
| * {Rust} |
| |
| ```gn |
| import("//build/rust/rustc_binary.gni") |
| import("//src/sys/build/components.gni") |
| |
| rustc_binary("rot13_encoder_decoder") { |
| } |
| |
| fuchsia_package_with_single_component("rot13") { |
| manifest = "meta/rot13.cmx" |
| deps = [ ":rot13_encoder_decoder" ] |
| } |
| ``` |
| |
| * {Go} |
| |
| ```gn |
| import("//build/go/go_binary.gni") |
| import("//src/sys/build/components.gni") |
| |
| go_binary("rot13_encoder_decoder") { |
| } |
| |
| fuchsia_component("rot13") { |
| manifest = "meta/rot13.cmx" |
| deps = [ ":rot13_encoder_decoder" ] |
| } |
| ``` |
| |
| |
| ## Test-driven development |
| |
| The `fx smoke-test` command automatically detects all tests that are known to |
| the build system as affected by changes in your checkout. Try the following: |
| |
| <pre class="prettyprint"> |
| <code class="devsite-terminal">fx -i smoke-test --verbose</code> |
| </pre> |
| |
| In the command above, `--verbose` prints which tests `fx smoke-test` thinks |
| are affected by your change, and `-i` automatically repeats this command |
| every time you save your changes. For test-driven development, try launching |
| this command in a separate shell and watching your code rebuild and retest as |
| you're working on it. |
| |
| `fx smoke-test` works best with hermetic test packages. A test package is |
| hermetic if the package contains all the dependencies of any tests in it. |
| That is to say, any code changes that affect the outcome of this test should |
| require rebuilding that test's package as well. |
| |
| ## Additional packaged resources {#additional-packaged-resources} |
| |
| In the examples above we've demonstrated that a `deps` path from a package to a |
| target that produces an executable ensures that the executable is included in |
| the package. |
| |
| Sometimes there is the need to include additional files. Below we demonstrate |
| the use of the [`resource.gni`](/build/dist/resource.gni) template. |
| |
| ### Example: fonts |
| |
| {# Disable variable substition to avoid {{ being interpreted by the template engine #} |
| {% verbatim %} |
| |
| ```gn |
| import("//src/sys/build/components.gni") |
| |
| resource("roboto_family") { |
| sources = [ |
| "Roboto-Black.ttf", |
| "Roboto-Bold.ttf", |
| "Roboto-Light.ttf", |
| "Roboto-Medium.ttf", |
| "Roboto-Regular.ttf", |
| "Roboto-Thin.ttf", |
| ] |
| outputs = [ "data/fonts/{{source_file_part}}" ] |
| } |
| |
| fuchsia_component("text_viewer") { |
| ... |
| deps = [ |
| ":roboto_family", |
| ... |
| ] |
| } |
| ``` |
| |
| {# Re-enable variable substition #} |
| {% endverbatim %} |
| |
| In the example above, six files are provided to be packaged under `data/fonts/`, |
| producing the paths `data/fonts/Roboto-Black.ttf`, |
| `data/fonts/Roboto-Bold.ttf`, etc'. The format for `destination` accepts [GN |
| source expansion placeholders][source-expansion-placeholders]. |
| |
| Then, a text viewer component is defined to depend on the fonts. In this |
| example, the text viewer implementation renders text with Roboto fonts. The |
| component can read the given fonts in its sandbox under the path |
| `/pkg/data/fonts/...`. |
| |
| ### Example: integration test with golden data |
| |
| In this example we define a hypothetical service that minifies JSON files. The |
| service is said to receive a buffer containing JSON text, and returns a buffer |
| containing the same JSON data but with less whitespace. We present an |
| integration test where a test component acts as the client of the minifier |
| component, and compares the result for a given JSON file to be minified against |
| a known good result (or a "golden file"). |
| |
| {# Disable variable substition to avoid {{ being interpreted by the template engine #} |
| {% verbatim %} |
| |
| ``` |
| fuchsia_component("minifier_component") { |
| ... |
| } |
| |
| fuchsia_package("minifier_package") { |
| ... |
| } |
| |
| resource("testdata") { |
| sources = [ |
| "testdata/input.json", |
| "testdata/input_minified.json", |
| ] |
| outputs = [ "data/{{source_file_part}}" ] |
| } |
| |
| fuchsia_component("minifier_test_client") { |
| testonly = true |
| deps = [ |
| ":testdata", |
| ... |
| ] |
| ... |
| } |
| |
| fuchsia_test_package("minifier_integration_test") { |
| test_components = [ ":minifier_test_client" ] |
| deps = [ ":minifier_component" ] |
| } |
| ``` |
| |
| {# Re-enable variable substition #} |
| {% endverbatim %} |
| |
| Note that we place the `resource()` dependency on the test component. From the |
| build system's perspective the resource dependency could have been placed on |
| the test package and the same outcome would have been produced by the build. |
| However, it is a better practice to put dependencies on the targets that need |
| them. This way we could reuse the same test component target in a different |
| test package, for instance to test against a different minifier component, and |
| the test component would work the same. |
| |
| ## Component manifest includes {#component-manifest-includes} |
| |
| As shown above, component declarations have an associated [component |
| manifest][glossary-component-manifest]. The component manifest supports |
| "include" syntax, which allows referencing one or more files where additional |
| contents for the component manifest may be merged from. This is conceptually |
| similar for instance to `#include` directives in the C programming language. |
| These included files are also known as component manifest shards. |
| |
| Some dependencies, such as libraries, assume that dependent components have |
| certain capabilities available to them at runtime. |
| Practically this could mean that the code in question assumes that its |
| dependents include a certain file in their component manifests. For instance, |
| the [C++ Syslog library][cpp-syslog] makes such an assumption. |
| |
| Target owners can declare that dependent components must include one or more |
| files in their component manifest. For example we have the hypothetical file |
| `//sdk/lib/fonts/BUILD.gn` below: |
| |
| ```gn |
| import("//tools/cmc/build/expect_includes.gni") |
| |
| # Client library for components that want to use fonts |
| source_set("font_provider_client") { |
| sources = [ |
| "font_provider_client.cc", |
| ... |
| ] |
| deps = [ |
| ":font_provider_client_includes", |
| ... |
| ] |
| } |
| |
| expect_includes("font_provider_client_includes") { |
| includes = [ |
| "client.shard.cmx", |
| "client.shard.cml", |
| ] |
| } |
| ``` |
| |
| It is possible (and recommended) to provide both `.cmx` and `.cml` includes. |
| Dependent manifests are required to include the expected files with the |
| matching extension. |
| |
| * {.cmx} |
| |
| ```json |
| { |
| "include": [ |
| "sdk/lib/fonts/client.shard.cmx" |
| ] |
| ... |
| } |
| ``` |
| |
| * {.cml} |
| |
| ```json5 |
| { |
| include: [ |
| "sdk/lib/fonts/client.shard.cml", |
| ] |
| ... |
| } |
| ``` |
| |
| Include paths are resolved relative to the source root. |
| Transitive includes (includes of includes) are allowed. |
| Cycles are not allowed. |
| |
| By convention, component manifest shard files are named with the suffix |
| `.shard.cmx` or `.shard.cml`. |
| |
| When naming your shards, don't repeat yourself in relation to the full path. |
| In the example above it would have been repetitive to name the shard |
| `fonts.shard.cml` because then the full path would have been |
| `sdk/lib/fonts/fonts.shard.cml`, which is repetitive. Instead the file is |
| named `client.shard.cml`, to indicate that it is to be used by clients of the |
| SDK library for fonts. |
| |
| ## Troubleshooting {#troubleshooting} |
| |
| ### Listing the contents of a package {#listing-the-contents-of-a-package} |
| |
| Packages are described by a package manifest, which is a text file where every |
| line follows this structure: |
| |
| ``` |
| <packaged-path>=<source-file-path> |
| ``` |
| |
| To find the package manifest for a `fuchsia_package()` or |
| `fuchsia_test_package()` target, use the following command: |
| |
| <pre class="prettyprint"> |
| <code class="devsite-terminal">fx gn outputs out/default <var>package target</var>_manifest</code> |
| </pre> |
| |
| The package target is a fully-qualified target name, i.e. in the form |
| `//path/to/your:target`. |
| |
| Combine this with another command to print the package manifest: |
| |
| <pre class="prettyprint"> |
| <code class="devsite-terminal">cat out/default/$(fx gn outputs out/default <var>package target</var>_manifest)</code> |
| </pre> |
| |
| See also: |
| |
| * [Working with packages][working-with-packages] |
| * [pm] |
| |
| ### Finding paths for built executables {#finding-paths-for-built-executables} |
| |
| Executable programs can be built with various language-specific templates such |
| as `executable()`, `rustc_binary()`, `go_binary()` etc'. These templates are |
| responsible for specifying where in a package their output binaries should be |
| included. The details vary by runtime and toolchain configuration. |
| |
| * Typically the path is `bin/` followed by the target's name. |
| * Typically if an `output_name` or `name` is specified, it overrides the |
| target name. |
| |
| Some rudimentary examples are given below: |
| |
| * {C++} |
| |
| ```gn |
| # Binary is packaged as `bin/rot13_encode` |
| executable("rot13_encode") { |
| sources = [ "main.cc" ] |
| } |
| ``` |
| |
| * {Rust} |
| |
| ```gn |
| # Binary is packaged as `bin/rot13_encode` |
| rustc_binary("rot13_encode") {} |
| ``` |
| |
| * {Go} |
| |
| ```gn |
| # Binary is packaged as `bin/rot13_encode` |
| go_binary("rot13_encode") {} |
| ``` |
| |
| In order to reference an executable in a component manifest, the author needs |
| to know its packaged path. |
| |
| One way to find the packaged path for an executable is to make sure that the |
| target that builds the executable is in a package's `deps`, then follow |
| [listing the contents of a package](#listing-the-contents-of-a-package). |
| The executable is listed among the contents of the package. |
| |
| ### Finding a [component's launch URL][glossary-component-url] |
| |
| Component URLs follow this pattern: |
| |
| ```none |
| fuchsia-pkg://fuchsia.com/<package-name>#meta/<component-name>.<extension> |
| ``` |
| |
| * `<package-name>`: specified as `package_name` on the package target, which |
| defaults to the target name. |
| * `<component-name>`: specified as `component_name` on the component target, |
| which defaults to the target name. |
| * `<extension>`: based on the [component |
| manifest][glossary-component-manifest] - `cmx` for cmx files, `cm` for cml |
| files. |
| |
| ## Migrating from legacy build rules {#legacy-package-migration} |
| |
| The examples below demonstrate migration scenarios from the legacy |
| [`package()`](/build/package.gni) template to the new |
| [`fuchsia_package()`](/src/sys/build/fuchsia_package.gni) & friends. |
| |
| ### Simple `package()` example |
| |
| This example is adapted from |
| [`//src/sys/time/timekeeper/BUILD.gn`](/src/sys/time/timekeeper/BUILD.gn). |
| |
| {# Disable variable substition to avoid {{ being interpreted by the template engine #} |
| {% verbatim %} |
| |
| * {Pre-migration} |
| |
| ```gn |
| import("//build/config.gni") |
| import("//build/package.gni") # <1> |
| import("//build/rust/rustc_binary.gni") |
| import("//build/test/test_package.gni") # <1> |
| |
| rustc_binary("bin") { # <2> |
| output_name = "timekeeper" |
| edition = "2018" |
| # Generate a ":bin_test" target for unit tests |
| with_unit_tests = true |
| deps = [ ... ] |
| } |
| |
| config_data("timekeeper_config") { |
| for_pkg = "sysmgr" |
| outputs = [ "timekeeper.config" ] |
| sources = [ "service.config" ] |
| } |
| |
| package("timekeeper") { |
| meta = [ |
| { |
| path = "meta/service.cmx" # <3> |
| dest = "timekeeper.cmx" # <3> |
| }, |
| ] |
| deps = [ |
| ":bin", |
| ":timekeeper_config", |
| ] |
| binaries = [ |
| { |
| name = "timekeeper" |
| }, |
| ] |
| } |
| |
| test_package("timekeeper_bin_test") { |
| deps = [ ":bin_test" ] |
| tests = [ |
| { |
| name = "timekeeper_bin_test" |
| environments = basic_envs |
| }, |
| ] |
| resources = [ # <4> |
| { |
| path = "test/y2k" |
| dest = "y2k" # <4> |
| }, |
| { |
| path = "test/end-of-unix-time" |
| dest = "end-of-unix-time" # <4> |
| }, |
| ] |
| } |
| ``` |
| |
| * {Post-migration} |
| |
| ```gn |
| import("//build/config.gni") |
| import("//build/rust/rustc_binary.gni") |
| import("//src/sys/build/components.gni") # <1> |
| |
| rustc_binary("bin") { # <2> |
| output_name = "timekeeper" |
| edition = "2018" |
| # Generate a ":bin_test" target for unit tests |
| with_unit_tests = true |
| deps = [ ... ] |
| } |
| |
| config_data("timekeeper_config") { |
| for_pkg = "sysmgr" |
| outputs = [ "timekeeper.config" ] |
| sources = [ "service.config" ] |
| } |
| |
| fuchsia_component("service") { |
| component_name = "timekeeper" # <3> |
| manifest = "meta/service.cmx" # <3> |
| deps = [ ":bin" ] |
| } |
| |
| fuchsia_package("timekeeper") { |
| deps = [ ":service" ] |
| } |
| |
| resource("testdata") { # <4> |
| sources = [ |
| "test/y2k", |
| "test/end-of-unix-time", |
| ] |
| outputs = [ "data/{{source_file_part}}" ] # <4> |
| } |
| |
| fuchsia_unittest_package("timekeeper_unittests") { |
| manifest = "meta/unittests.cmx" |
| deps = [ |
| ":bin_test", |
| ":testdata", # <4> |
| ] |
| } |
| ``` |
| {# Re-enable variable substition #} |
| {% endverbatim %} |
| |
| The following key elements are called out in the code example above: |
| |
| > 1. Necessary imports are replaced by `//src/sys/build/components.gni`. |
| > 2. Targets that generate executables or data files are not expected to change |
| > in a migration. |
| > 3. Previously, `meta/service.cmx` was given the destination `"timekeeper.cmx"` |
| > which placed it in `meta/timekeeper.cmx`. With `fuchsia_component()`, the |
| > given manifest is automatically renamed per the `component_name` field |
| > (`"timekeeper"`) and `meta/` is prepended. As a result, the launch URL for |
| > the timekeeper component remains the same: |
| > `fuchsia-pkg://fuchsia.com/timekeeper#meta/timekeeper.cmx` |
| > |
| > Note: If you do not provide a `component_name`, the manifest is named |
| > according to the `fuchsia_component()` target name. |
| > |
| > 4. Additional resources (in this case, the data asset files used in the test |
| > such as the `test/y2k` file) are included in the unit test. Their |
| > destination path is a full packaged path, whereas before it would have had |
| > `data/` automatically prepended to it. In both cases, the data file can |
| > be read by the test at runtime from the paths `/pkg/data/y2k` and |
| > `/pkg/data/end-of-unix-time`. |
| |
| ### Complex `package()` example |
| |
| This example is adapted from |
| [`//src/fonts/BUILD.gn`](/src/fonts/BUILD.gn). |
| |
| * {Pre-migration} |
| |
| ```gn |
| import("//build/package.gni") # <1> |
| import("//build/rust/rustc_binary.gni") |
| import("//build/test/test_package.gni") # <1> |
| import("//src/fonts/build/fonts.gni") |
| |
| rustc_binary("font_provider") { # <2> |
| name = "font_provider" |
| # Generate a ":bin_test" target for unit tests |
| with_unit_tests = true |
| edition = "2018" |
| |
| deps = [ ... ] |
| sources = [ ... ] |
| } |
| |
| package("pkg") { |
| package_name = "fonts" |
| |
| deps = [ ":font_provider" ] |
| |
| binaries = [ |
| { |
| name = "font_provider" |
| }, |
| ] |
| meta = [ # <3> |
| { |
| path = rebase_path("meta/fonts.cmx") # <3> |
| dest = "fonts.cmx" # <4> |
| }, |
| { |
| path = rebase_path("meta/fonts.cml") # <3> |
| dest = "fonts.cm" # <4> |
| }, |
| ] |
| } |
| |
| test_package("font_provider_unit_tests") { |
| deps = [ ":font_provider_test" ] |
| |
| v2_tests = [ |
| { |
| name = "font_provider_bin_test" # <4> |
| }, |
| ] |
| } |
| ``` |
| |
| * {Post-migration} |
| |
| ```gn |
| import("//build/rust/rustc_binary.gni") |
| import("//src/fonts/build/fonts.gni") |
| import("//src/sys/build/components.gni") # <1> |
| |
| rustc_binary("font_provider") { # <2> |
| name = "font_provider" |
| # Generate a ":bin_test" target for unit tests |
| with_unit_tests = true |
| edition = "2018" |
| |
| deps = [ ... ] |
| sources = [ ... ] |
| } |
| |
| fuchsia_component("font_provider_cm") { # <3> |
| manifest = "meta/fonts.cml" |
| component_name = "fonts" # <4> |
| deps = [ ":font_provider" ] |
| } |
| |
| fuchsia_component("font_provider_cmx") { # <3> |
| manifest = "meta/fonts.cmx" |
| component_name = "fonts" # <4> |
| deps = [ ":font_provider" ] |
| } |
| |
| fuchsia_package("pkg") { |
| package_name = "fonts" |
| deps = [ |
| ":font_provider_cm", # <3> |
| ":font_provider_cmx", # <3> |
| ] |
| } |
| |
| fuchsia_component("font_provider_unit_tests_cmp") { |
| testonly = true |
| manifest = "meta/font_provider_bin_test.cml" |
| component_name = "font_provider_bin_test" # <4> |
| deps = [ ":font_provider_test" ] |
| } |
| |
| fuchsia_test_package("font_provider_unit_tests") { |
| test_components = [ ":font_provider_unit_tests_cmp" ] |
| } |
| ``` |
| |
| The following key elements are called out in the code example above: |
| |
| > 1. Necessary imports are replaced by `//src/sys/build/components.gni`. |
| > 2. Targets that generate executables or data files are not expected to change |
| > in a migration. |
| > 3. If a `package()` includes multiple distinct components using the `meta` |
| > field, each one must be broken out into a separate `fuchsia_component()` |
| > and collected together in the `fuchsia_package()` using `deps`. |
| > 4. Each `fuchsia_component()` uses the `component_name` field to map the |
| > manifest destination in the final package. Without this, they are placed |
| > according to the **target name**, which affects the launch URL of the |
| > component. This is true for both `fuchsia_package()` and `fuchsia_test_package()`. |
| |
| Note: The new build templates allow targets that produce files, such as |
| `executable()`, to decide which files they produce and where the targets place |
| these files. This may affect the packaged path to binaries in your manifest or |
| test definition after migrating. If you encounter build-time errors you are |
| unable to resolve, see [Troubleshooting](#troubleshooting). |
| |
| ### Test package considerations |
| |
| The example below highlights some key differences between the legacy |
| [`test_package()`](/build/test/test_package.gni) template and the new |
| [`fuchsia_test_package()`](/src/sys/build/fuchsia_test_package.gni). |
| |
| * {Pre-migration} |
| |
| ```gn |
| import("//build/package.gni") # <1> |
| import("//build/test/test_package.gni") # <1> |
| |
| executable("foo_bin_test") { ... } |
| |
| test_package("foo_tests") { # <1> |
| deps = [ ":foo_bin_test" ] # <2> |
| |
| tests = [ # <3> |
| { |
| name = "foo_test" # <2> |
| log_settings = { |
| max_severity = "ERROR" |
| } |
| } |
| ] |
| } |
| ``` |
| |
| * {Post-migration} |
| |
| ```gn |
| import("//src/sys/build/components.gni") # <1> |
| |
| executable("foo_bin_test") { ... } |
| |
| fuchsia_component("foo_test") { # <2> |
| testonly = true |
| manifest = "meta/foo_test.cmx" |
| deps = [ ":foo_bin_test" ] |
| } |
| |
| fuchsia_test_package("foo_tests") { # <1> |
| test_components = [ ":foo_test" ] # <2> |
| |
| test_specs = { # <3> |
| log_settings = { |
| max_severity = "ERROR" |
| } |
| } |
| } |
| ``` |
| |
| The following key elements are called out in the code example above: |
| |
| > 1. Replace necessary imports with `//src/sys/build/components.gni` and rename |
| > `test_package()` to `fuchsia_test_package()`. |
| > 2. Create a `fuchsia_component()` to encapsulate the test components previously |
| > added with the `tests` field. Reference the components in the package with |
| > the new `test_components` field. |
| > |
| > Note: A `test_package()` typically sets the packaged path for binaries to |
| > `test/`, while the new build rules let the executables define this and they |
| > typically use `bin/`. This may affect the packaged path to binaries in your |
| > test definition after migrating. If you encounter build-time errors you are |
| > unable to resolve, see [Troubleshooting](#troubleshooting). |
| > |
| > 3. Both template families support test specifications, such as restricting to |
| > specific [test environments][test-environments] or |
| > [restricting log severity][restrict-log-severity]. |
| > |
| > Note: With the new templates, the `test_specs` apply to all tests in the package. |
| > See [test packages](#test-packages) for more examples. |
| |
| ### Remove legacy allowlist |
| |
| The `deprecated_package` group in [`//build/BUILD.gn`](/build/BUILD.gn) contains |
| an allowlist of build files still using the legacy `package()` template. |
| Once you have successfully migrated your build files to the new templates, |
| remove the affected lines from the group. Removing the allowlist entries prevents |
| future changes from re-introducing uses of the legacy templates. |
| |
| For example, if you migrated the files under [`//src/fonts`](/src/fonts) to the |
| new templates, you would find and remove all the related files paths in |
| [`//build/BUILD.gn`](/build/BUILD.gn): |
| |
| ```gn |
| group("deprecated_package") { |
| ... |
| visibility += [ |
| ... |
| "//src/fonts/*", |
| "//src/fonts/char_set/*", |
| "//src/fonts/font_info/*", |
| "//src/fonts/manifest/*", |
| "//src/fonts/offset_string/*", |
| "//src/fonts/tests/integration/*", |
| "//src/fonts/tests/smoke/*", |
| ... |
| ] |
| } |
| ``` |
| |
| |
| ### Legacy features |
| |
| The following special attributes are supported by the legacy `package()` template: |
| |
| * `binaries` |
| * `drivers` |
| * `libraries` |
| * `loadable_modules` |
| |
| These are used with special syntax, which determines how the files that certain |
| targets produce are packaged. |
| For instance the `libraries` attribute installs resources in a special `lib/` directory, |
| `drivers` are installed in `drivers/`, etc'. |
| The legacy syntax looks like this: |
| |
| ``` |
| package("my_driver_package") { |
| deps = [ ":my_driver" ] |
| |
| drivers = [ |
| { |
| name = "my_driver.so" |
| }, |
| ] |
| } |
| ``` |
| |
| This special treatment is not necessary with the new templates. Simply add the |
| necessary target to `deps = [ ... ]` and the packaging is done automatically. |
| |
| ``` |
| fuchsia_component("my_driver_component") { |
| deps = [ ":my_driver" ] |
| ... |
| } |
| |
| fuchsia_package("my_driver_package") { |
| deps = [ ":my_driver_component" ] |
| ... |
| } |
| ``` |
| |
| Additionally, legacy `package()` supports the `resources` attribute. This is |
| replaced by adding a dependency on a `resource()` target. |
| See also: |
| |
| * [Listing the contents of a package](#listing-the-contents-of-a-package). |
| * [Additional packaged resources](#additional-packaged-resources). |
| |
| ### Renaming files |
| |
| The legacy `package()` template allowed developers to rename certain files that |
| are included in their package. For example, below we see an executable being |
| built and then renamed before it's packaged so that it's packaged under the path |
| `bin/foo_bin`. |
| |
| ```gn |
| import("//build/package.gni") |
| |
| executable("bin") { |
| ... |
| } |
| |
| package("foo_pkg") { |
| deps = [ ":bin" ] |
| binaries = [ |
| { |
| name = "bin" |
| dest = "foo_bin" |
| } |
| ] |
| meta = [ |
| { |
| path = "meta/foo_bin.cmx" |
| dest = "foo.cmx" |
| } |
| ] |
| } |
| ``` |
| |
| The new templates allow targets that produce files, such as `executable()` |
| above, to decide which files they produce and where they're placed. This is |
| important because some targets produce multiple files, or might produce |
| different files based on the build configuration (for instance if building |
| for a different target architecture). In order to control the paths of |
| packaged files, developers should work with the templates for the targets |
| that produce those files. For instance: |
| |
| ```gn |
| import("//src/sys/build/components.gni") |
| |
| executable("bin") { |
| output_name = "foo_bin" |
| ... |
| } |
| |
| fuchsia_component("foo_cmp") { |
| deps = [ ":bin" ] |
| manifest = "meta/foo_bin.cmx" |
| } |
| |
| fuchsia_package("foo_pkg") { |
| deps = [ ":foo_cmp" ] |
| } |
| ``` |
| |
| ### Shell binaries |
| |
| The legacy `package()` template allowed developers to make a particular binary |
| in the package available to `fx shell`. |
| |
| ```gn |
| import("//build/package.gni") |
| |
| # `fx shell echo Hello World` prints "Hello World" |
| executable("bin") { |
| output_name = "echo" |
| ... |
| } |
| |
| package("echo") { |
| binaries = [ |
| { |
| name = "echo" |
| dest = "echo" |
| shell = true |
| } |
| ] |
| deps = [ ":bin" ] |
| } |
| ``` |
| |
| The new templates support this feature as follows: |
| |
| ```gn |
| import("//src/sys/build/components.gni") |
| |
| # `fx shell echo Hello World` prints "Hello World" |
| executable("bin") { |
| output_name = "echo" |
| ... |
| } |
| |
| fuchsia_shell_package("echo") { |
| deps = [ ":bin" ] |
| } |
| ``` |
| |
| Note that in the `package()` example the binary is explicitly named "echo", |
| which is the same name that's used for its intrinsic name |
| (`output_name = "echo"`). The new templates don't have this renaming behavior, |
| and instead let the target that produces the binary (`executable()` in this |
| case) decide the file name, as determined by the `output_name` specified (or the |
| executable target's name if `output_name` isn't specified). |
| |
| This feature was left out intentionally. |
| Moving forward the use of legacy shell tools is discouraged. |
| |
| ### Go `grand_unified_binary` |
| |
| "Grand unified binary" (GUB) is a single binary that merges together multiple Go |
| programs. The entry point to the combined program can identify which sub-program |
| the caller intends to run based on the filename of the invocation (`argv[0]`). |
| Therefore in order to include GUB in your package and invoke a sub-program the |
| common practice is to rename the binary. |
| |
| The legacy `package()` template allowed developers to accomplish this as shown |
| below: |
| |
| ```gn |
| import("//build/go/go_library.gni") |
| import("//build/package.gni") |
| |
| go_library("my_tool") { |
| ... |
| } |
| |
| package("tools") { |
| deps = [ |
| "//src/go/grand_unified_binary", |
| ] |
| binaries = [ |
| { |
| name = "my_tool" |
| source = "grand_unified_binary" |
| } |
| ] |
| } |
| ``` |
| |
| The new templates support this feature as follows: |
| |
| ```gn |
| import("//build/go/go_library.gni") |
| import("//src/go/grand_unified_binary/gub.gni") |
| import("//src/sys/build/components.gni") |
| |
| go_library("my_tool") { |
| ... |
| } |
| |
| grand_unified_binary("bin") { |
| output_name = "my_tool" |
| } |
| |
| fuchsia_package("tools") { |
| deps = [ ":bin" ] |
| } |
| ``` |
| |
| ### Legacy component index (aka `fx run my_package`) |
| |
| The legacy `package()` template supported a short-form syntax for launching legacy |
| v1 components in the legacy sys shell. |
| |
| ```gn |
| import("//build/package.gni") |
| |
| executable("bin") { |
| output_name = "echo" |
| sources = [ "echo.cc" ] |
| } |
| |
| package("echo") { |
| deps = [ ":bin" ] |
| binaries = [ |
| { |
| name = "echo" |
| }, |
| ] |
| meta = [ |
| { |
| path = "meta/echo.cmx" |
| dest = "echo.cmx" |
| }, |
| ] |
| } |
| ``` |
| |
| ```posix-terminal |
| fx run echo Hello World |
| ``` |
| |
| This is also known as the [Component Index][component-index]. |
| |
| The new templates don't support this feature out of the box, but you can use the |
| full launch URL. |
| |
| ```posix-terminal |
| fx run fuchsia-pkg://fuchsia.com/echo#meta/echo.cmx Hello World |
| ``` |
| |
| The plan is to deprecate the legacy shell and the legacy component index along |
| with it, but there is currently no concrete timeline for this deprecation. If |
| you'd like to keep the old behavior, you can do so with this special syntax: |
| |
| ```gn |
| import("//src/sys/build/components.gni") |
| import("//src/sys/component_index/component_index.gni") |
| |
| executable("bin") { |
| output_name = "echo" |
| sources = [ "echo.cc" ] |
| } |
| |
| add_to_component_index("component_index") { |
| package_name = "echo" |
| manifest = "meta/echo.cmx" |
| } |
| |
| fuchsia_package_with_single_component("echo") { |
| deps = [ |
| ":bin", |
| ":component_index", |
| ] |
| manifest = "meta/echo.cmx" |
| } |
| ``` |
| |
| ### Other unsupported features |
| |
| Note that some features of `package()` are unsupported moving forward. If your |
| package depends on them then at this time it cannot be migrated to the new |
| templates. These unsupported features include: |
| |
| * Marking a test as disabled. Instead, change the test source code to mark it |
| as disabled, or comment out the disabled test component from the build file. |
| * `__deprecated_system_image`: the legacy approach to including a package in |
| the system image is not supported moving forward. A solution is being |
| prepared and will be available later in 2021. |
| Nearly all existing uses of this legacy feature are done via the |
| `driver_package()` wrapper, which currently cannot be migrated. |
| |
| [cpp-syslog]: /docs/development/languages/c-cpp/logging.md#component_manifest_dependency |
| [cml-format]: /docs/concepts/components/v2/component_manifests.md |
| [cmx-format]: /docs/concepts/components/v1/component_manifests.md |
| [component-index]: /src/sys/component_index/component_index.gni |
| [executable]: https://gn.googlesource.com/gn/+/HEAD/docs/reference.md#func_executable |
| [fx-test]: https://www.fuchsia.dev/reference/tools/fx/cmd/test.md |
| [fxb-55739]: https://bugs.fuchsia.dev/p/fuchsia/issues/detail?id=55739 |
| [glossary-component]: /docs/glossary.md#component |
| [glossary-component-instance]: /docs/glossary.md#component-instance |
| [glossary-component-manifest]: /docs/glossary.md#component-manifest |
| [glossary-component-url]: /docs/glossary.md#component-url |
| [glossary-fuchsia-pkg-url]: /docs/glossary.md#fuchsia-pkg-url |
| [glossary-gn]: /docs/glossary.md#gn |
| [glossary-package]: /docs/glossary.md#fuchsia-package |
| [gn-get-target-outputs]: https://gn.googlesource.com/gn/+/HEAD/docs/reference.md#func_get_target_outputs |
| [pm]: /src/sys/pkg/bin/pm/README.md |
| [restrict-log-severity]: /docs/concepts/testing/logs.md#restricting_log_severity |
| [rustc-binary]: /build/rust/rustc_binary.gni |
| [rustc-test]: /build/rust/rustc_test.gni |
| [source-code-layout]: /docs/concepts/source_code/layout.md |
| [source-expansion-placeholders]: https://gn.googlesource.com/gn/+/HEAD/docs/reference.md#placeholders |
| [test-environments]: /docs/concepts/testing/environments.md |
| [v2-test-component]: /docs/concepts/testing/v2/v2_test_component.md |
| [working-with-packages]: /docs/development/idk/documentation/packages.md |