Dart

Overview

Dart artifacts are not built the same way in Fuchsia as they are on other platforms.

Instead of relying on pub to manage dependencies, sources of third-party packages we depend on are checked into the tree under //third_party/dart-pkg. This is to ensure we use consistent versions of our dependencies across multiple builds.

Likewise, no build output is placed in the source tree as everything goes under out/. That includes .packages files which are generated as part of the build based on a target's dependency.

Targets

There are five gn targets for building Dart:

See the definitions of each of these targets for how to use them.

Package layout

We use a layout very similar to the standard layout.

my_package/
  |
  |-- pubspec.yaml           # Empty, used as a marker [mandatory]
  |-- BUILD.gn               # Contains all targets
  |-- analysis_options.yaml  # Analysis configuration [mandatory]
  |-- lib/                   # dart_package contents
  |-- bin/                   # dart_binary's (target) or dart_tool's (host)
  |-- test/                  # dart_test contents

Managing third-party dependencies

//third-party/dart-pkg is kept up-to-date with a script that relies on pub to resolve versions and fetch sources for the packages that are used in the tree. This script uses a set of canonical local packages which are assumed to be providing the necessary package coverage for the entire tree.

Additionally, projects may request third-party dependencies to be imported through the following procedure:

  1. create a dart_dependencies.yaml file in the project
  2. add the desired dependencies in that file:
name: my_project
dependencies:
  foo: ^4.0.0
  bar: >=0.1.0
  1. add a reference to the file in //scripts/dart/update_3p_packages.py
  2. run that script

Analysis

Analysis is run as part of the Fuchsia build.

For each dart_package and flutter_app target, an analysis script gets also generated in the output directory under:

out/<build-type>/gen/path/to/package/package.analyzer.sh

Running this script will perform an analysis of the target's sources.

As with standard Dart packages, analysis options are defined in an analysis_options.yaml file, which must be placed at the package root. This file may refer to a common set of options by way of an include directive:

include: relative/path/to/options.file

A canonical set is available at //topaz/tools/analysis_options.yaml. It is customary to merely include that set from a local options file:

include: path/to/topaz/tools/analysis_options.yaml

Analysis may be disabled altogether for a given target with:

dart_package("foo") {
  disable_analysis = true
}

The //scripts/run-dart-action.py script makes it easy to run the analysis over multiple targets:

scripts/run-dart-action.py analyze --out out/<build-type> --tree //topaz/shell/*

Regular analyzer flags may also be passed:

scripts/run-dart-action.py analyze --out out/<build-type> --fatal-warnings --lints

This holds true for the individual analysis scripts.

Testing

The dart_test target is appropriate for unit tests. Each target yields a test script in the output directory under:

out/<build-type>/gen/path/to/package/target_name.sh

This script simply runs the given tests in the Flutter shell on the host.

The //scripts/run-dart-action.py script may be used to run multiple test suites at once:

scripts/run-dart-action.py test --out out/<build-type> --tree //topaz/shell/*

It also works with a single suite:

scripts/run-dart-action.py test --out out/<build-type> --tree //topaz/shell/armadillo:test

FIDL

FIDL targets generate implicit Dart bindings targets. To use the bindings generated for:

//foo/bar
//foo/bar:blah

add a dependency on:

//foo/bar:bar_dart
//foo/bar:blah_dart

and import the resulting Dart sources with:

import "package:foo.bar/baz.dart";
import "package:foo.bar..blah/baz.dart";

Logging

It is highly recommended that you use lib.logging package when you want to add logging statements to your Dart package.

Include the lib.logging package in your BUILD.gn target as a dependency:

deps = [
  ...
  "//topaz/public/dart/logging:lib.logging",
  ...
]

In the main function of your Dart / Flutter app, call the setupLogger() function to make sure logs appear in the Fuchsia console in the desired format.

import 'package:lib.logging/logging.dart';

main() {
  setupLogger();
}

After setting this up, you can call one of the following log methods to add log statements to your code:

import 'package:lib.logging/logging.dart';

// add logging statements somewhere in your code as follows:
log.info('hello world!');

The log object is a Logger instance as documented here.

Log Levels

The log methods are named after the supported log levels. To list the log methods in descending order of severity:

log.shout()    // maps to LOG_FATAL in FXL.
log.severe()   // maps to LOG_ERROR in FXL.
log.warning()  // maps to LOG_WARNING in FXL.
log.info()     // maps to LOG_INFO in FXL.
log.fine()     // maps to VLOG(1) in FXL.
log.finer()    // maps to VLOG(2) in FXL.
log.finest()   // maps to VLOG(3) in FXL.

By default, all the logs of which level is INFO or higher will be shown in the console. Because of this, Dart / Flutter app developers are highly encouraged to use log.fine() for their typical logging statements for development purposes.

Currently, the log level should be adjusted in individual Dart apps by providing the level parameter in the setupLogger() call. For example:

setupLogger(level: Level.ALL);

will make all log statements appear in the console.

IDEs

A prebuilt Dart SDK is available for IDE consumption at: //third_party/dart/tools/sdks/<linux|mac>/dart-sdk. Note that this SDK is sometimes a few days behind the version of //third_party/dart. If you require an up-to-date SDK, one gets built with Fuchsia at: //out/<build-type>/dart_host/dart-sdk.

Troubleshooting

When you find the Dart analysis is not working properly in your IDE, try the following:

  • Delete //out and rebuild. Specifically, a release build overrides a debug build. This means that if you have a broken release build, any release build overrides a debug build. With a broken release build, no amount of correct rebuilding on debug will solve the issue until you delete //out/release-x86-64.
  • Delete the .packages file in your project and rebuild.

Known issues

Multiple FIDL targets in a single BUILD file

If two FIDL targets coexist in a single BUILD file, their respective, generated files will currently be placed in the same subdirectory of the output directory. This means that files belonging to one target will be available to clients of the other target, and this will likely confuse the analyzer. This should not be a build issue now but could become one once the generated Dart files are placed in separate directories if clients do not correctly set up their dependencies.