gecko-dev/build/docs/rust.rst
Nicholas Nethercote 977d5b8a57 Bug 1648348 - Create "Writing Rust Code" docs. r=froydnj,zbraniecki,lina.
This patch includes content from the following places.

- Lina's "Getting Rusty: How to ship an XPCOM component in Firefox" slide deck.
- Zibi's "Rust <--> C/C++ FFI for newbies" gist.

It also links to Emilio's "FFI patterns #1 - Complex Rust data structures
exposed seamlessly to C++" blog post. I was going to include that content, but
it's very long, so I have omitted it for now.

Differential Revision: https://phabricator.services.mozilla.com/D81963
2020-07-07 07:38:27 +00:00

181 lines
8.4 KiB
ReStructuredText

.. _rust:
==============================
Including Rust Code in Firefox
==============================
This page explains how to add, build, link, and vendor Rust crates.
The `code documentation <../../writing-rust-code>`_ explains how to write and
work with Rust code in Firefox. The
`test documentation <../../testing-rust-code>`_ explains how to test and debug
Rust code in Firefox.
Linking Rust crates into libxul
===============================
Rust crates that you want to link into libxul should be listed in the
``dependencies`` section of
`toolkit/library/rust/shared/Cargo.toml <https://searchfox.org/mozilla-central/source/toolkit/library/rust/shared/Cargo.toml>`_.
You must also add an ``extern crate`` reference to
`toolkit/library/rust/shared/lib.rs <https://searchfox.org/mozilla-central/source/toolkit/library/rust/shared/lib.rs>`_.
This ensures that the Rust code will be linked properly into libxul as well
as the copy of libxul used for gtests. (Even though Rust 2018 mostly doesn't
require ``extern crate`` declarations, these ones are necessary because the
gkrust setup is non-typical.)
After adding your crate, execute ``cargo update -p gkrust-shared`` to update
the ``Cargo.lock`` file. You will also need to do this any time you change the
dependencies in a ``Cargo.toml`` file. If you don't, you will get a build error
saying **"error: the lock file /home/njn/moz/mc3/Cargo.lock needs to be updated
but --frozen was passed to prevent this"**.
By default, all Cargo packages in the mozilla-central repository are part of
the same
`workspace <https://searchfox.org/mozilla-central/source/toolkit/library/rust/shared/lib.rs>`_
and will share the ``Cargo.lock`` file and ``target`` directory in the root of
the repository. You can change this behavior by adding a path to the
``exclude`` list in the top-level ``Cargo.toml`` file. You may want to do
this if your package's development workflow includes dev-dependencies that
aren't needed by general Firefox developers or test infrastructure.
The actual build mechanism is as follows. The build system generates a special
'Rust unified library' crate, compiles that to a static library
(``libgkrust.a``), and links that into libxul, so all public symbols will be
available to C++ code. Building a static library that is linked into a dynamic
library is easier than building dynamic libraries directly, and it also avoids
some subtle issues around how mozalloc works that make the Rust dynamic library
path a little wonky.
Linking Rust crates into something else
=======================================
To link Rust code into libraries other than libxul, create a directory with a
``Cargo.toml`` file for your crate, and a ``moz.build`` file that contains:
.. code-block:: python
RustLibrary('crate_name')
where ``crate_name`` matches the name from the ``[package]`` section of your
``Cargo.toml``. You can refer to `the moz.build file <https://searchfox.org/mozilla-central/rev/3f4c3a3cabaf94958834d3a8935adfb4a887942d/toolkit/library/rust/moz.build#7>`_ and `the Cargo.toml file <https://searchfox.org/mozilla-central/rev/3f4c3a3cabaf94958834d3a8935adfb4a887942d/toolkit/library/rust/Cargo.toml>`_ that are used for libxul.
You can then add ``USE_LIBS += ['crate_name']`` to the ``moz.build`` file
that defines the binary as you would with any other library in the tree.
.. important::
You cannot link a Rust crate into an intermediate library that will be
eventually linked into libxul. The build system enforces that only a single
``RustLibrary`` may be linked into a binary. If you need to do this, you
will have to add a ``RustLibrary`` to link to any standalone binaries that
link the intermediate library, and also add the Rust crate to the libxul
dependencies as in `linking Rust Crates into libxul`_.
Conditional compilation
========================
Edit `tool/library/rust/gkrust-features.mozbuild
<https://searchfox.org/mozilla-central/source/toolkit/library/rust/gkrust-features.mozbuild>`_
to expose build flags as Cargo features.
Standalone Rust programs
========================
It is also possible to build standalone Rust programs. First, put the Rust
program (including the ``Cargo.toml`` file and the ``src`` directory) in its
own directory, and add an empty ``moz.build`` file to the same directory.
Then, if the standalone Rust program must run on the compile target (e.g.
because it's shipped with Firefox) then add this rule to the ``moz.build``
file:
.. code-block:: python
RUST_PROGRAMS = ['prog_name']
where *prog_name* is the name of the executable as specified in the
``Cargo.toml`` (and probably also matches the name of the directory).
Otherwise, if the standalone Rust program must run on the compile host (e.g.
because it's used to build Firefox but not shipped with Firefox) then do the
same thing, but use ``HOST_RUST_PROGRAMS`` instead of ``RUST_PROGRAMS``.
Where should I put my crate?
============================
If your crate's canonical home is mozilla-central, you can put it next to the
related code in the appropriate directory.
If your crate is mirrored into mozilla-central from another repository, and
will not be actively developed in mozilla-central, you can simply list it
as a ``crates.io``-style dependency with a version number, and let it be
vendored into the ``third_party/rust`` directory.
If your crate is mirrored into mozilla-central from another repository, but
will be actively developed in both locations, you should send mail to the
dev-builds mailing list to start a discussion on how to meet your needs.
Third-party crate dependencies
==============================
Third-party dependencies for in-tree Rust crates are *vendored* into the
``third_party/rust`` directory of mozilla-central. This means that a copy of
each third-party crate's code is committed into mozilla-central. As a result,
building Firefox does not involve downloading any third-party crates.
If you add a dependency on a new crate you must run ``mach vendor rust`` to
vendor the dependencies into that directory. (Note that ``mach vendor rust``
`may not work as well on Windows <https://bugzilla.mozilla.org/show_bug.cgi?id=1647582>`_
as on other platforms.)
When it comes to checking the suitability of third-party code for inclusion
into mozilla-central, keep the following in mind.
- ``mach vendor rust`` will check that the licenses of all crates are suitable.
- You should review the crate code to some degree to check that it looks
reasonable (especially for unsafe code) and that it has reasonable tests.
- Third-party crate tests aren't run, which means that large test fixtures will
bloat mozilla-central. Consider working with upstream to mark those test
fixtures with ``[package] exclude = ...`` as described
`here <https://doc.rust-lang.org/cargo/reference/manifest.html#the-exclude-and-include-fields>`_.
- If you specify a dependency on a branch, pin it to a specific revision,
otherwise other people will get unexpected changes when they run ``./mach
vendor rust`` any time the branch gets updated. See `bug 1612619
<https://bugzil.la/1612619>`_ for a case where such a problem was fixed.
- Other than that, there is no formal sign-off procedure, but one may be added
in the future.
Note that all dependencies will be vendored, even ones that aren't used due to
disabled features. It's possible that multiple versions of a crate will end up
vendored into mozilla-central.
Patching third-party crates
===========================
Sometimes you might want to temporarily patch a third-party crate, for local
builds or for a try push.
To do this, first add an entry to the ``[patch.crates-io]`` section of the
top-level ``Cargo.toml`` that points to the crate within ``third_party``. For
example
.. code-block:: toml
bitflags = { path = "third_party/rust/bitflags" }
Next, run ``cargo update -p $CRATE_NAME --precise $VERSION``, where
``$CRATE_NAME`` is the name of the patched crate, and ``$VERSION`` is its
version number. This will update the ``Cargo.lock`` file.
Then, make the local changes to the crate.
Finally, make sure you don't accidentally land the changes to the crate or the
``Cargo.lock`` file.
For an example of a more complex workflow involving a third-party crate, see
`mp4parse-rust/README.md <https://searchfox.org/mozilla-central/source/media/mp4parse-rust/README.md>`_.
It describes the workflow for a crate that is hosted on GitHub, and for which
changes are made via GitHub pull requests, but all pull requests must also be
tested within mozilla-central before being merged.