Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
load(
"@rules_rust//rust:defs.bzl",
"rust_library",
"rust_shared_library",
"rust_static_library",
"rust_test",
Expand All @@ -25,6 +26,13 @@ rust_static_library(
edition = "2018",
)

rust_library(
name = "manual_root",
srcs = ["manual_root.rs"],
edition = "2018",
tags = ["manual"],
)

rust_test(
name = "auto_discovery_json_test",
srcs = ["auto_discovery_json_test.rs"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ mod tests {
.unwrap_or_else(|_| panic!("couldn't open {:?}", &rust_project_path));
println!("{}", content);

let mut found_manual_root = false;

for line in content.lines() {
let discovery: DiscoverProject =
serde_json::from_str(line).expect("Failed to deserialize discovery JSON");
Expand All @@ -52,6 +54,20 @@ mod tests {
.find(|c| &c.display_name == "greeter_staticlib")
.unwrap();
assert!(staticlib.root_module.ends_with("/static_lib.rs"));

found_manual_root = project.crates.iter().any(|crate_spec| {
crate_spec.display_name == "manual_root"
&& crate_spec.root_module.ends_with("/manual_root.rs")
});

if found_manual_root {
break;
}
}

assert!(
found_manual_root,
"manual root-package crate was not discovered"
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
manual_root.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub fn meaning() -> i32 {
42
}
35 changes: 23 additions & 12 deletions test/rust_analyzer/rust_analyzer_test_runner.sh
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ EOF
cat <<EOF >"${new_workspace}/.bazelrc"
build --keep_going
test --test_output=errors
# The 'strict' config is used to ensure extra checks are run on the test
# targets that would otherwise not run due to them being tagged as "manual".
# Note that that tag is stripped for this test.
# The 'strict' config is used to ensure extra checks are run on the fixture
# test targets, including ones that are explicitly invoked despite being tagged
# as "manual".
build:strict --aspects=@rules_rust//rust:defs.bzl%rustfmt_aspect
build:strict --output_groups=+rustfmt_checks
build:strict --aspects=@rules_rust//rust:defs.bzl%rust_clippy_aspect
Expand All @@ -78,6 +78,8 @@ function rust_analyzer_test() {
local workspace="$2"
local generator_arg="$3"
local rust_log="info"
local discover_arg=""
local -a test_targets=()
if [[ -n "${RUST_ANALYZER_TEST_DEBUG:-}" ]]; then
rust_log="debug"
fi
Expand All @@ -86,15 +88,12 @@ function rust_analyzer_test() {
rm -f "${workspace}"/*.rs "${workspace}"/*.json "${workspace}"/*.bzl "${workspace}/BUILD.bazel" "${workspace}/BUILD.bazel-e"
cp -r "${source_dir}"/* "${workspace}"

# Drop the 'manual' tags
if [ "$(uname)" == "Darwin" ]; then
SEDOPTS=(-i '' -e)
else
SEDOPTS=(-i)
if [[ -f "${source_dir}/discover_path" ]]; then
discover_arg='{"path":"'"${workspace}/$(<"${source_dir}/discover_path")"'"}'
fi
sed ${SEDOPTS[@]} 's/"manual"//' "${workspace}/BUILD.bazel"

pushd "${workspace}" &>/dev/null

echo "Generating rust-project.json..."

if [[ -n "${generator_arg}" ]]; then
Expand All @@ -107,14 +106,26 @@ function rust_analyzer_test() {
bazel run "@rules_rust//tools/rust_analyzer:validate" -- rust-project.json

echo "Generating auto-discovery.json..."
RUST_LOG="${rust_log}" bazel run "@rules_rust//tools/rust_analyzer:discover_bazel_rust_project" > auto-discovery.json
if [[ -n "${discover_arg}" ]]; then
RUST_LOG="${rust_log}" bazel run "@rules_rust//tools/rust_analyzer:discover_bazel_rust_project" -- "${discover_arg}" > auto-discovery.json
else
RUST_LOG="${rust_log}" bazel run "@rules_rust//tools/rust_analyzer:discover_bazel_rust_project" > auto-discovery.json
fi

mapfile -t test_targets < <(bazel query 'kind(".*_test rule", //...)')

echo "Building..."
bazel build //...
echo "Testing..."
bazel test //...
if (( ${#test_targets[@]} > 0 )); then
bazel test "${test_targets[@]}"
fi
echo "Building with Aspects..."
bazel build //... --config=strict
if (( ${#test_targets[@]} > 0 )); then
bazel build //... "${test_targets[@]}" --config=strict
else
bazel build //... --config=strict
fi
popd &>/dev/null
}

Expand Down
12 changes: 9 additions & 3 deletions tools/rust_analyzer/bin/discover_rust_project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,16 @@ fn project_discovery() -> anyhow::Result<DiscoverProject<'static>> {

log::info!("resolved rust-analyzer argument: {ra_arg:?}");

let (buildfile, targets) = ra_arg.into_target_details(&workspace)?;
let (buildfile, targets) = ra_arg.into_target_details(
&bazel,
&output_base,
&workspace,
&bazel_startup_options,
&bazel_args,
)?;

log::debug!("got buildfile: {buildfile}");
log::debug!("got targets: {targets}");
log::debug!("got targets: {:?}", targets);

// Use the generated files to print the rust-project.json.
let project = generate_rust_project(
Expand All @@ -67,7 +73,7 @@ fn project_discovery() -> anyhow::Result<DiscoverProject<'static>> {
&bazel_startup_options,
&bazel_args,
rules_rust_name,
&[targets],
&targets,
)?;

Ok(DiscoverProject::Finished { buildfile, project })
Expand Down
45 changes: 43 additions & 2 deletions tools/rust_analyzer/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,10 @@ fn source_file_to_buildfile(file: &Utf8Path) -> anyhow::Result<Utf8PathBuf> {
.with_context(|| format!("no buildfile found for {file}"))
}

fn buildfile_to_targets(workspace: &Utf8Path, buildfile: &Utf8Path) -> anyhow::Result<String> {
fn buildfile_to_package_target_pattern(
workspace: &Utf8Path,
buildfile: &Utf8Path,
) -> anyhow::Result<String> {
log::info!("getting targets for buildfile: {buildfile}");

let parent_dir = buildfile
Expand All @@ -187,12 +190,50 @@ fn buildfile_to_targets(workspace: &Utf8Path, buildfile: &Utf8Path) -> anyhow::R

let targets = match parent_dir {
Some(p) if !p.as_str().is_empty() => format!("//{p}:all"),
_ => "//...".to_string(),
_ => "//:all".to_string(),
};

Ok(targets)
}

fn buildfile_to_targets(
bazel: &Utf8Path,
output_base: &Utf8Path,
workspace: &Utf8Path,
bazel_startup_options: &[String],
bazel_args: &[String],
buildfile: &Utf8Path,
) -> anyhow::Result<Vec<String>> {
let target_pattern = buildfile_to_package_target_pattern(workspace, buildfile)?;
let query = format!("kind(rule, {target_pattern})");

let output = bazel_command(bazel, Some(workspace), Some(output_base))
.args(bazel_startup_options)
.arg("query")
.args(bazel_args)
.arg(query)
.output()?;

if !output.status.success() {
let status = output.status;
let stderr = String::from_utf8_lossy(&output.stderr);
bail!("bazel query failed: ({status:?})\n{stderr}");
}

let targets = String::from_utf8(output.stdout)?
.lines()
.map(str::trim)
.filter(|line| !line.is_empty())
.map(ToOwned::to_owned)
.collect::<Vec<_>>();

if targets.is_empty() {
bail!("no rule targets found for buildfile: {buildfile}");
}

Ok(targets)
}

#[derive(Debug, Deserialize)]
struct ToolchainInfo {
sysroot: Utf8PathBuf,
Expand Down
26 changes: 23 additions & 3 deletions tools/rust_analyzer/rust_project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,35 @@ impl RustAnalyzerArg {
/// Consumes itself to return a build file and the targets to build.
pub fn into_target_details(
self,
bazel: &Utf8Path,
output_base: &Utf8Path,
workspace: &Utf8Path,
) -> anyhow::Result<(Utf8PathBuf, String)> {
bazel_startup_options: &[String],
bazel_args: &[String],
) -> anyhow::Result<(Utf8PathBuf, Vec<String>)> {
match self {
Self::Path(file) => {
let buildfile = source_file_to_buildfile(&file)?;
buildfile_to_targets(workspace, &buildfile).map(|t| (buildfile, t))
buildfile_to_targets(
bazel,
output_base,
workspace,
bazel_startup_options,
bazel_args,
&buildfile,
)
.map(|t| (buildfile, t))
}
Self::Buildfile(buildfile) => {
buildfile_to_targets(workspace, &buildfile).map(|t| (buildfile, t))
buildfile_to_targets(
bazel,
output_base,
workspace,
bazel_startup_options,
bazel_args,
&buildfile,
)
.map(|t| (buildfile, t))
}
}
}
Expand Down