Skip to content

Latest commit

 

History

History
291 lines (217 loc) · 7.43 KB

CONFIG.md

File metadata and controls

291 lines (217 loc) · 7.43 KB

Configuration format

Cackle is configured via a cackle.toml, which by default is located in the package or workspace root.

API definitions

Example:

[api.process]
include = [
    "std::process",
]
exclude = [
    "std::process::abort",
    "std::process::exit",
]

Here we define an API called "process". Any package that references symbols in std::process is considered to use this API except if the symbol referenced is std::process::abort or std::process::exit, which are excluded from the process API.

We can define as many APIs as we like. If an API is declared, then packages need permission in order to use those APIs.

Importing standard library API definitions

Cackle has some built-in API definitions for the Rust standard library that can optionally be used.

import_std = [
    "fs",
    "net",
    "process",
    "env",
    "terminate",
]

Package permissions

We can grant permissions to a package to use APIs or use unsafe. e.g.:

[pkg.crab1]
allow_unsafe = true
allow_apis = [
    "fs",
    "process",
]

Here we declare a package called crab1 and say that it is allowed to use the fs and process APIs. We also say that it's allowed to use unsafe code.

We can also conditionally grant permissions to use APIs only from particular kinds of binaries. For example, if we wanted to allow crab1 to use the fs API, but only in code that is only reachable from test code, we can do that as follows:

[pkg.crab1]
from.test.allow_apis = [
    "fs",
]

Similarly, we can allow the API to be used, but only in code that is reachable from build scripts:

[pkg.crab1]
from.build.allow_apis = [
    "fs",
]

If we want to allow an API to be used specifically by crab1's build script, we can do that as follows:

[pkg.crab1]
build.allow_apis = [
    "fs",
]

Allowed APIs inherit as follows:

  • pkg.N
    • pkg.N.from.build (any build script)
      • pkg.N.build (N's build script)
    • pkg.N.from.test (any test)
      • pkg.N.test (N's tests)

So granting an API usage to pkg.N means it can be used in any kind of binary.

Sandbox

[sandbox]
kind = "Bubblewrap"

Here we declare that we'd like to use Bubblewrap (installed as bwrap) as our sandbox. Bubblewrap is currently the only supported kind of sandbox. The sandbox will be used for running build scripts (build.rs), running tests (with cargo acl test) and optionally for sandboxing rustc.

If for some reason you don't want to sandbox a particular build script, you can disable the sandbox just for that build script.

[pkg.foo]
build.sandbox.kind = "Disabled"

If a build script needs network access, you can relax the sandbox to allow it as follows:

[pkg.foo]
build.sandbox.allow_network = true

Tests can also be run in a sandbox using the test subcommand, for example:

cargo acl test

This builds the tests, checking the built test binaries against the permissions granted in cackle.toml, then runs the tests, with a sandbox if one is configured.

The sandbox used for tests is configured under pkg.{pkg-name}.test. e.g.:

[pkg.foo]
test.sandbox.kind = "Disabled"

Tests and build scripts already have write access to a temporary directory, however, if for some reason they need to write to some directory in your source folder, this can be permitted as follows:

[pkg.foo]
test.sandbox.bind_writable = [
    "test_outputs",
]

This will allow tests to write to the "test_outputs" subdirectory within the directory containing your Cargo.toml. All directories listed in bind_writable must exist.

If you'd like to automatically create a writable directory if it doesn't already exist, then make_writable behaves the same, but will create the directory before starting the sandbox.

[pkg.foo]
test.sandbox.make_writable = [
    "test_outputs",
]

If you need to pass particular environment variables into a sandboxed process, you can list them as follows:

[pkg.foo.test.sandbox]
pass_env = [
    "VAR1",
    "VAR2",
]

This will cause the variables "VAR1" and "VAR2", if set, to be passed to the sandboxed process - in this case the tests for the package foo.

Sandboxing rustc

If you have a sandbox configuration, then from config version 2 onwards, rustc will be run in a sandbox. This means that all proc macros get sandboxed. Controlling the sandbox on a per-proc-macro basis unfortunately isn't supported yet, but hopefully will in future. This means that if you have for example one proc macro that needs network access, you'd need to enable network access for the whole rustc sandbox, which means that all proc macros would have network access.

If you need to enable networking from the rustc sandbox, you can do so as follows:

[rustc.sandbox]
allow_network = true

Or if you need to completely disable the rustc sandbox:

[rustc.sandbox]
kind = "Disable"

Importing API definitions from an external crate

If you depend on a crate that publishes cackle/export.toml, you can import API definitions from this as follows:

[pkg.some-dependency]
import = [
    "fs",
]

API definitions imported like this will be namespaced by prefixing them with the crate that exported them. For example:

[pkg.my-bin]
allow_apis = [
    "some-dependency::fs",
]

If you're the owner of a crate that provides APIs that you'd like classified, you can create cackle/export.toml in your crate.

Build options

Specifying features

Features to be be passed to cargo build can be specified in cackle.toml as follows:

features = ["feature1", "feature2"]

Selecting build targets

Arbitrary build flags can be passed to cargo build using the build_flags option. The default is to pass --all-targets.

[common]
build_flags = ["--all-targets"]

If you'd like to not analyse tests, examples etc, you might override this to just the empty array []. Or if you want to analyse tests, but not examples you might set it to ["--tests"]. For available options run cargo build --help.

Custom build profile

By default, Cackle builds with a custom profile named "cackle" which inherits from the "dev" profile. If you'd like to use a different profile, you can override the profile in the configuration file. e.g.

[common]
profile = "cackle-release"

You can also override with the --profile flag, which takes precedence over the config file.

Cackle supports analysing references even when inlining occurs, so it can work to some extent even with optimisations enabled, however it's more likely that you'll run into false attribution bugs, where an API usage is attributed to the wrong package. So unless you really need optimisation for some reason, it's recommended to set opt-level = 0.

Split debug info is not yet supported, so you should turn it off.

Here's an example of what you might put in your Cargo.toml:

[profile.cackle-release]
inherits = "release"
opt-level = 0
split-debuginfo = "off"
strip = false
debug = 2
lto = "off"

Version number

The field common.version is the only required field in the config file.

[common]
version = 2

If we decide to change the default values for any fields in future, we'll add a new supported version number. In this regard, common.version is a bit like package.edition in Cargo.toml. It's intended as a way to preserve old behaviour while making breaking changes, in particular breaking changes that might otherwise go unnoticed.