blob: 29e550dc27f31fd3c4650944fa205a4522ebd480 [file] [log] [blame] [edit]
<!DOCTYPE HTML>
<html lang="en" class="light sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>rust_analyzer - rules_rust</title>
<!-- Custom HTML head -->
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
<link rel="icon" href="favicon.svg">
<link rel="shortcut icon" href="favicon.png">
<link rel="stylesheet" href="css/variables.css">
<link rel="stylesheet" href="css/general.css">
<link rel="stylesheet" href="css/chrome.css">
<link rel="stylesheet" href="css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" href="highlight.css">
<link rel="stylesheet" href="tomorrow-night.css">
<link rel="stylesheet" href="ayu-highlight.css">
<!-- Custom theme stylesheets -->
<!-- Provide site root to javascript -->
<script>
var path_to_root = "";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
</script>
<!-- Start loading toc.js asap -->
<script src="toc.js"></script>
</head>
<body>
<div id="body-container">
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
try {
var theme = localStorage.getItem('mdbook-theme');
var sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script>
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
const html = document.documentElement;
html.classList.remove('light')
html.classList.add(theme);
html.classList.add("js");
</script>
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
var sidebar = null;
var sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
} else {
sidebar = 'hidden';
}
sidebar_toggle.checked = sidebar === 'visible';
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<!-- populated by js -->
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
<noscript>
<iframe class="sidebar-iframe-outer" src="toc.html"></iframe>
</noscript>
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
<div class="sidebar-resize-indicator"></div>
</div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky">
<div class="left-buttons">
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</label>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">rules_rust</h1>
<div class="right-buttons">
<a href="print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
<a href="https://github.com/bazelbuild/rules_rust" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa fa-github"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script>
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h2 id="overview"><a class="header" href="#overview">Overview</a></h2>
<p>For <a href="https://rust-analyzer.github.io/manual.html#non-cargo-based-projects">non-Cargo projects</a>,
<a href="https://rust-analyzer.github.io/">rust-analyzer</a> depends on either a <code>rust-project.json</code> file
at the root of the project that describes its structure or on build system specific
<a href="https://rust-analyzer.github.io/manual.html#rust-analyzer.workspace.discoverConfig">project auto-discovery</a>.
The <code>rust_analyzer</code> rules facilitate both approaches.</p>
<h2 id="rust-projectjson-approach"><a class="header" href="#rust-projectjson-approach">rust-project.json approach</a></h2>
<h3 id="setup"><a class="header" href="#setup">Setup</a></h3>
<h4 id="bzlmod"><a class="header" href="#bzlmod">Bzlmod</a></h4>
<p>First, ensure <code>rules_rust</code> is setup in your workspace:</p>
<pre><code class="language-python"># MODULE.bazel
# See releases page for available versions:
# https://github.com/bazelbuild/rules_rust/releases
bazel_dep(name = "rules_rust", version = "{SEE_RELEASES}")
</code></pre>
<p>Bazel will create the target <code>@rules_rust//tools/rust_analyzer:gen_rust_project</code>, which you can build
with</p>
<pre><code>bazel run @rules_rust//tools/rust_analyzer:gen_rust_project
</code></pre>
<p>whenever dependencies change to regenerate the <code>rust-project.json</code> file. It
should be added to <code>.gitignore</code> because it is effectively a build artifact.
Once the <code>rust-project.json</code> has been generated in the project root,
rust-analyzer can pick it up upon restart.</p>
<h4 id="workspace"><a class="header" href="#workspace">WORKSPACE</a></h4>
<p>Alternatively, you can use the legacy WORKSPACE approach. As with Bzlmod, ensure <code>rules_rust</code> is
setup in your workspace.</p>
<p>Moreover, when loading the dependencies for the tool, you should call the function <code>rust_analyzer_dependencies()</code>:</p>
<pre><code class="language-python">load("@rules_rust//tools/rust_analyzer:deps.bzl", "rust_analyzer_dependencies")
rust_analyzer_dependencies()
</code></pre>
<p>Again, you can now run <code>bazel run @rules_rust//tools/rust_analyzer:gen_rust_project</code>
whenever dependencies change to regenerate the <code>rust-project.json</code> file.</p>
<p>For users who do not use <code>rust_register_toolchains</code> to register toolchains, the following can be added
to their WORKSPACE to register a <code>rust_analyzer_toolchain</code>. Please make sure the Rust version used in
this toolchain matches the version used by the currently registered toolchain or the sources/documentation
will not match what's being compiled with and can lead to confusing results.</p>
<pre><code class="language-python">load("@rules_rust//rust:repositories.bzl", "rust_analyzer_toolchain_repository")
register_toolchains(rust_analyzer_toolchain_repository(
name = "rust_analyzer_toolchain",
# This should match the currently registered toolchain.
version = "1.63.0",
))
</code></pre>
<h4 id="vscode"><a class="header" href="#vscode">VSCode</a></h4>
<p>To set this up using <a href="https://code.visualstudio.com/">VSCode</a>, users should first install the
<a href="https://marketplace.visualstudio.com/items?itemName=matklad.rust-analyzer">rust_analyzer plugin</a>.
With that in place, the following task can be added to the <code>.vscode/tasks.json</code> file of the workspace
to ensure a <code>rust-project.json</code> file is created and up to date when the editor is opened.</p>
<pre><code class="language-json">{
"version": "2.0.0",
"tasks": [
{
"label": "Generate rust-project.json",
"command": "bazel",
"args": [
"run",
"@rules_rust//tools/rust_analyzer:gen_rust_project"
],
"options": {
"cwd": "${workspaceFolder}"
},
"group": "build",
"problemMatcher": [],
"presentation": {
"reveal": "never",
"panel": "dedicated"
},
"runOptions": {
"runOn": "folderOpen"
}
},
]
}
</code></pre>
<h4 id="alternative-vscode-option-prototype"><a class="header" href="#alternative-vscode-option-prototype">Alternative vscode option (prototype)</a></h4>
<p>Add the following to your bazelrc:</p>
<pre><code>build --@rules_rust//rust/settings:rustc_output_diagnostics=true --output_groups=+rust_lib_rustc_output,+rust_metadata_rustc_output
</code></pre>
<p>Then you can use a prototype <a href="https://marketplace.visualstudio.com/items?itemName=MattStark.bazel-rust-analyzer">rust-analyzer plugin</a> that automatically collects the outputs whenever you recompile.</p>
<h2 id="project-auto-discovery"><a class="header" href="#project-auto-discovery">Project auto-discovery</a></h2>
<h3 id="setup-1"><a class="header" href="#setup-1">Setup</a></h3>
<p>Auto-discovery makes <code>rust-analyzer</code> behave in a Bazel project in a similar fashion to how it does
in a Cargo project. This is achieved by generating a structure similar to what <code>rust-project.json</code>
contains but, instead of writing that to a file, the data gets piped to <code>rust-analyzer</code> directly
through <code>stdout</code>. To use auto-discovery the <code>rust-analyzer</code> IDE settings must be configured similar to:</p>
<pre><code class="language-json">"rust-analyzer": {
"workspace": {
"discoverConfig": {
"command": ["discover_bazel_rust_project.sh"],
"progressLabel": "rust_analyzer",
"filesToWatch": ["BUILD", "BUILD.bazel", "MODULE.bazel"]
}
}
}
</code></pre>
<p>The shell script passed to <code>discoverConfig.command</code> is typically meant to wrap the bazel rule invocation,
primarily for muting <code>stderr</code> (because <code>rust-analyzer</code> will consider that an error has occurred if anything
is passed through <code>stderr</code>) and, additionally, for specifying rule arguments. E.g:</p>
<pre><code class="language-shell">#!/usr/bin/bash
bazel \
run \
@rules_rust//tools/rust_analyzer:discover_bazel_rust_project -- \
--bazel_startup_option=--output_base=~/ide_bazel \
--bazel_arg=--watchfs \
${1:+"$1"} 2&gt;/dev/null
</code></pre>
<p>The script above also handles an optional CLI argument which gets passed when workspace splitting is
enabled. The script path should be either absolute or relative to the project root.</p>
<h3 id="workspace-splitting"><a class="header" href="#workspace-splitting">Workspace splitting</a></h3>
<p>The above configuration treats the entire project as a single workspace. However, large codebases might be
too much to handle for <code>rust-analyzer</code> all at once. This can be addressed by splitting the codebase in
multiple workspaces by extending the <code>discoverConfig.command</code> setting:</p>
<pre><code class="language-json">"rust-analyzer": {
"workspace": {
"discoverConfig": {
"command": ["discover_bazel_rust_project.sh", "{arg}"],
"progressLabel": "rust_analyzer",
"filesToWatch": ["BUILD", "BUILD.bazel", "MODULE.bazel"]
}
}
}
</code></pre>
<p><code>{arg}</code> acts as a placeholder that <code>rust-analyzer</code> replaces with the path of the source / build file
that gets opened.</p>
<p>The root of the workspace will, in this configuration, be the package the crate currently being worked on
belongs to. This means that only that package and its dependencies get built and indexed by <code>rust-analyzer</code>,
thus allowing for a smaller footprint.</p>
<p><code>rust-analyzer</code> will switch workspaces whenever an out-of-tree file gets opened, essentially indexing that
crate and its dependencies separately. A caveat of this is that <em>dependents</em> of the crate currently being
worked on are not indexed and won't be tracked by <code>rust-analyzer</code>.</p>
<h2 id="aspects"><a class="header" href="#aspects">Aspects</a></h2>
<ul>
<li><a href="#rust_analyzer_aspect">rust_analyzer_aspect</a></li>
</ul>
<p><a id="rust_analyzer_aspect"></a></p>
<h2 id="rust_analyzer_aspect"><a class="header" href="#rust_analyzer_aspect">rust_analyzer_aspect</a></h2>
<pre>
load("@rules_rust//rust:defs.bzl", "rust_analyzer_aspect")
rust_analyzer_aspect()
</pre>
<p>Annotates rust rules with RustAnalyzerInfo later used to build a rust-project.json</p>
<p><strong>ASPECT ATTRIBUTES</strong></p>
<div class="table-wrapper"><table><thead><tr><th style="text-align: left">Name</th><th style="text-align: left">Type</th></tr></thead><tbody>
<tr><td style="text-align: left">deps</td><td style="text-align: left">String</td></tr>
<tr><td style="text-align: left">proc_macro_deps</td><td style="text-align: left">String</td></tr>
<tr><td style="text-align: left">crate</td><td style="text-align: left">String</td></tr>
<tr><td style="text-align: left">actual</td><td style="text-align: left">String</td></tr>
<tr><td style="text-align: left">proto</td><td style="text-align: left">String</td></tr>
</tbody></table>
</div>
<p><strong>ATTRIBUTES</strong></p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="cargo.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="rust_unpretty.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="cargo.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="rust_unpretty.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<script>
window.playground_copyable = true;
</script>
<script src="elasticlunr.min.js"></script>
<script src="mark.min.js"></script>
<script src="searcher.js"></script>
<script src="clipboard.min.js"></script>
<script src="highlight.js"></script>
<script src="book.js"></script>
<!-- Custom JS scripts -->
</div>
</body>
</html>