blob: 4d083ae9d6d594100f02e5b3fb19c7c8d9d95c51 [file] [log] [blame] [view] [edit]
# FIDL versioning
This document describes how to use FIDL's API versioning features. For a more
technical specification, see [RFC-0083: FIDL versioning][rfc-0083].
## Motivation
FIDL versioning lets you change a FIDL library over time while keeping the
ability to generate bindings for older versions of the library. There are a
number of ways you could do this manually:
* Store `.fidl` files in a `v1/` directory. To make a change, copy `v1/` to
`v2/` and change files there. To generate bindings for the older version,
use the `v1/` library instead of `v2/`.
* Store `.fidl` files in a git repository and make changes in commits. To
generate bindings for an older version, check out an older revision of the
repository.
The first solution is tedious and creates a lot of duplication. The second
solution doesn't work well in a large repository that contains many things
besides that specific FIDL library, such as the main Fuchsia repository.
FIDL versioning accomplishes the same thing, but without these shortcomings.
When making a change, you use the `@available` attribute to describe when (i.e.
at which version) the change occurs. To generate bindings for an older version,
you pass the `--available` flag to fidlc and specify an older version.
There are two important things to keep in mind with FIDL versioning:
* It affects **API only**. Versions exist only at compile time, and have no
impact on runtime behavior.
* It can represent **any syntactically valid change**. Just because you can
represent a change with versioning doesn't mean that change is safe to make.
## Concepts
The unit of versioning is a group of libraries, called a **platform**. By
convention, libraries are named starting with the platform name. For example,
the libraries `fuchsia.mem` and `fuchsia.web` belong to the `fuchsia` platform.
Each platform has a linear **version** history. A version is an integer from 1
to 2^63-1 (inclusive), or the special version `HEAD`. By convention, the `HEAD`
version is used for the latest unstable changes.
All FIDL libraries are versioned. There is no need to support "unversioned
libraries" because they would behave identically to libraries added at `HEAD`.
The defaults are designed so that you can ignore versioning if you aren't using
it: a library with no `@available` attribute is implicitly added at `HEAD`, and
fidlc compiles libraries at `HEAD` by default.
## Command line
The FIDL compiler accepts the `--available` flag to specify platform versions.
For example, assuming `example.fidl` defines a library in the `fuchsia` platform
with no dependencies, you can compile it at version 8 as follows:
```posix-terminal
fidlc --available fuchsia:8 --out example.json --files example.fidl
```
No matter what version you select, fidlc always validates all possible versions.
For example, the above command can report an error even if the error only occurs
in version 5.
If a library `A` has a dependency on a library `B` from a different platform,
you can specify versions for both platforms using the `--available` flag twice.
However, `A` must be compatible across its entire version history with the fixed
version chosen for `B`.
## Syntax
The `@available` attribute is allowed on any [FIDL element][element]. It takes
the following arguments:
Argument | Type | Note
------------ | -------- | -------------------------
`platform` | `string` | Only allowed on `library`
`added` | `uint64` | Integer or `HEAD`
`deprecated` | `uint64` | Integer or `HEAD`
`removed` | `uint64` | Integer or `HEAD`
`note` | `string` | Goes with `deprecated`
All arguments are optional, but at least one must be provided. Argument values
must be literals, not references to `const` declarations. The `added`,
`deprecated`, and `removed` arguments [inherit](#inheritance) from the parent
element by default, and they must respect `added <= deprecated < removed`. For
example:
```fidl
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/versioning.test.fidl" region_tag="arguments" %}
```
If `@available` is used anywhere in a library, it must also appear on the
library declaration. For single-file libraries, this is straightforward. For
libraries with two or more `.fidl` files, only one file can have its library
declaration annotated. (The library is logically considered a single [element]
with attributes merged from each file, so annotating more than one file results
in a duplicate attribute error.) The FIDL style guide [recommends][overview]
creating a file named `overview.fidl` for this purpose.
On the library declaration, the `@available` attribute requires the `added`
argument and allows the `platform` argument. If the `platform` is omitted, it
defaults to the first component of the library name. For example:
```fidl
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/versioning.test.fidl" region_tag="library" %}
```
## Inheritance {#inheritance}
The arguments to `@available` flow from the library declaration to top-level
declarations, and from each top-level declaration to its members. For example,
if a table is added at version 5, there is no need to repeat this annotation on
its members because they could not exist prior to the table itself. Here is a
more complicated example of inheritance:
```fidl
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/versioning.test.fidl" region_tag="inheritance" %}
```
## Deprecation
Deprecation is used to indicate that an element will be removed in the future.
When you deprecate an element, you should add a `# Deprecation` section to the
doc comment with a detailed explanation, and a `note` argument to the
`@available` attribute with a brief instruction. For example:
```fidl
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/versioning.test.fidl" region_tag="deprecation" %}
```
As of May 2022 deprecation has no impact in bindings. However, the FIDL team
[plans][deprecation-bug] to make it emit deprecation annotations in target
languages. For instance, the example above could produce `#[deprecated = "use
Replacement"]` in the Rust bindings.
## References
There are a variety of ways one FIDL element can reference another. For example:
```fidl
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/versioning.test.fidl" region_tag="references" %}
```
When referencing elements, you must respect the `@available` attributes. For
example, the following code is invalid because `A` exists from version 1 onward,
but it tries to reference `B` which only exists at version 2:
```fidl
// Does not compile!
@available(added=1)
const A bool = B;
@available(added=2, removed=3)
const B bool = true;
```
Similarly, it is invalid for a non-deprecated element to reference a deprecated
element. For example, the following code is invalid at version 1 because `A`
references `B`, but `B` is deprecated while `A` is not.
```fidl
// Does not compile!
@available(deprecated=2)
const A bool = B;
@available(deprecated=1)
const B bool = true;
```
## Swapping
Some parts of the FIDL language do not support attributes. For example, you
cannot place the `@available` attribute directly on an enum's `strict` modifier.
However, FIDL versioning can still represent this kind of change using a
technique called _swapping_. Instead of changing the enum, you duplicate it,
simultaneously removing the old copy and adding the new one. For example:
```fidl
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/versioning.test.fidl" region_tag="swapping" %}
```
Taken to the extreme, swapping makes it possible to decompose a versioned
library into a series of snapshots for each version. For example, given the
protocol shown [earlier](#inheritance):
```fidl
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/versioning.test.fidl" region_tag="inheritance" %}
```
We can decompose it by swapping the protocol at every version:
```fidl
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/versioning_decomposed.test.fidl" region_tag="decomposed" %}
```
[rfc-0083]: /docs/contribute/governance/rfcs/0083_fidl_versioning.md
[element]: /docs/contribute/governance/rfcs/0083_fidl_versioning.md#terminology
[overview]: /docs/development/languages/fidl/guides/style.md#library-overview
[deprecation-bug]: https://bugs.fuchsia.dev/p/fuchsia/issues/detail?id=7692