Skip to content

Commit

Permalink
Allow to narrow output of workspace to subset referenced by pattern.
Browse files Browse the repository at this point in the history
  • Loading branch information
hzeller committed Jun 27, 2024
1 parent cd48195 commit d8a4258
Show file tree
Hide file tree
Showing 10 changed files with 223 additions and 29 deletions.
27 changes: 23 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,31 @@ of these and print the final form:
bant print -b @googletest//:gtest
```

#### Workspace

**`workspace`** prints all the external projects found in the workspace.

```
bant workspace # All external projects referenced in this workspace
```

If you give a pattern, it will only print external projects used
by targets matching the pattern:

```
bant workspace ... # Print projects referenced in your project
bant workspace @re2//... # Print projects referenced by re2
```

#### list-targets

If you want to find the file quickly, `bant list-target //foo/bar:baz`
will output the filename and exact line/column range where the target
resides.
resides.

```bash
bant list-targets //... # list all targets of current project
bant list-targets -r //... # also following all dependencies
bant list-targets -r //... # also recursively following all dependencies
```

#### lib-headers
Expand Down Expand Up @@ -194,7 +210,7 @@ need for it).
$ bazel-bin/bant/bant -h
bant v0.1.5 <http://bant.build/>
Copyright (c) 2024 Henner Zeller. This program is free software; license GPL 2.0.
Usage: bazel-bin/bant/bant [options] <command> [bazel-target-pattern]
Usage: bant [options] <command> [bazel-target-pattern]
Options
-C <directory> : Change to this project directory first (default = '.')
-q : Quiet: don't print info messages to stderr.
Expand All @@ -213,12 +229,15 @@ Options
Commands (unique prefix sufficient):
== Parsing ==
print : Print AST matching pattern. -e : only files w/ parse errors
-b : elaBorate
-b : elaBorate; light eval: expand variables, concat etc.
parse : Parse all BUILD files from pattern. Follow deps with -r
Emit parse errors. Silent otherwise: No news are good news.
-v : some stats.
== Extract facts == (Use -f to choose output format) ==
workspace : Print external projects found in WORKSPACE.
Without pattern: All external projects.
With pattern : Subset referenced by matching targets.
→ 3 column table: (project, version, path)
-- Given '-r', the following also follow dependencies recursively --
Expand Down
1 change: 1 addition & 0 deletions bant/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ cc_binary(
"//bant/tool:compilation-db",
"//bant/tool:dwyu",
"//bant/tool:edit-callback",
"//bant/tool:workspace",
"//bant/util:file-utils",
"//bant/util:table-printer",
"@abseil-cpp//absl/strings",
Expand Down
5 changes: 4 additions & 1 deletion bant/bant.cc
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,15 @@ static int usage(const char *prog, const char *message, int exit_code) {
Commands (unique prefix sufficient):
%s== Parsing ==%s
print : Print AST matching pattern. -e : only files w/ parse errors
-b : elaBorate
-b : elaBorate; light eval: expand variables, concat etc.
parse : Parse all BUILD files from pattern. Follow deps with -r
Emit parse errors. Silent otherwise: No news are good news.
-v : some stats.
%s== Extract facts ==%s (Use -f to choose output format) ==
workspace : Print external projects found in WORKSPACE.
Without pattern: All external projects.
With pattern : Subset referenced by matching targets.
→ 3 column table: (project, version, path)
-- Given '-r', the following also follow dependencies recursively --
Expand Down
37 changes: 19 additions & 18 deletions bant/cli-commands.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,18 @@
#include "bant/frontend/elaboration.h"
#include "bant/frontend/parsed-project.h"
#include "bant/session.h"
#include "bant/tool/canon-targets.h"
#include "bant/tool/compilation-db.h"
#include "bant/tool/dwyu.h"
#include "bant/tool/edit-callback.h"
#include "bant/types-bazel.h"
#include "bant/types.h"
#include "bant/util/table-printer.h"
#include "bant/workspace.h"

// Tools accessible by these commands
#include "bant/tool/canon-targets.h"
#include "bant/tool/compilation-db.h"
#include "bant/tool/dwyu.h"
#include "bant/tool/edit-callback.h"
#include "bant/tool/workspace.h"

namespace bant {
using ::bant::query::FindTargets;
using ::bant::query::Result;
Expand Down Expand Up @@ -87,6 +90,14 @@ void PrintOneToN(bant::Session &session, const BazelPattern &pattern,
printer->Finish();
}

static bool NeedsProjectPopulated(Command cmd, const BazelPattern &pattern) {
// No need to even parse the project if we just print the full workspace
if (cmd == Command::kListWorkkspace && pattern.is_matchall()) {
return false; // NOLINT(readability-simplify-boolean-expr)
}
return true;
}

CliStatus RunCommand(Session &session, Command cmd,
const BazelPattern &pattern) {
// -- TODO: a lot of the following functionality including choosing what
Expand All @@ -111,7 +122,7 @@ CliStatus RunCommand(Session &session, Command cmd,
CommandlineFlags flags = session.flags();

bant::ParsedProject project(workspace, flags.verbose);
if (cmd != Command::kListWorkkspace) {
if (NeedsProjectPopulated(cmd, pattern)) {
if (project.FillFromPattern(session, dep_pattern) == 0) {
session.error() << "Pattern did not match any dir with BUILD file.\n";
}
Expand Down Expand Up @@ -233,19 +244,9 @@ CliStatus RunCommand(Session &session, Command cmd,
printer->Finish();
} break;

case Command::kListWorkkspace: {
// For now, we just load the workspace file in this command. We might need
// it later also to resolve dependencies.
auto printer =
TablePrinter::Create(session.out(), session.flags().output_format,
{"project", "version", "directory"});
for (const auto &[project, file] : workspace_or->project_location) {
printer->AddRow({project.project,
project.version.empty() ? "-" : project.version,
file.path()});
}
printer->Finish();
} break;
case Command::kListWorkkspace:
PrintMatchingWorkspaceExternalRepos(session, project, pattern);
break;

case Command::kAliasedBy:
PrintOneToN(session, print_pattern, bant::ExtractAliasedBy(project), //
Expand Down
1 change: 0 additions & 1 deletion bant/explore/query-utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,6 @@ class TargetFinder : public BaseVoidVisitor {
}

void InformCaller() {
if (current_.name.empty()) return;
// If we never got a hdrs list (or couldn't read it because
// it was a glob), assume this is an alwayslink library, so it wouldn't be
// considered for removal by DWYU (e.g. :gtest_main)
Expand Down
17 changes: 17 additions & 0 deletions bant/tool/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,20 @@ cc_library(
"@abseil-cpp//absl/strings",
],
)

cc_library(
name = "workspace",
srcs = ["workspace.cc"],
hdrs = ["workspace.h"],
deps = [
"//bant:session",
"//bant:types",
"//bant:types-bazel",
"//bant:workspace",
"//bant/explore:query-utils",
"//bant/frontend:parsed-project",
"//bant/util:file-utils",
"//bant/util:table-printer",
"@abseil-cpp//absl/strings",
],
)
106 changes: 106 additions & 0 deletions bant/tool/workspace.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// bant - Bazel Navigation Tool
// Copyright (C) 2024 Henner Zeller <[email protected]>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

#include "bant/tool/workspace.h"

#include <string_view>
#include <vector>

#include "absl/strings/str_cat.h"
#include "bant/explore/query-utils.h"
#include "bant/frontend/parsed-project.h"
#include "bant/session.h"
#include "bant/types-bazel.h"
#include "bant/types.h"
#include "bant/util/file-utils.h"
#include "bant/util/table-printer.h"
#include "bant/workspace.h"

// TODO: output that shows project dependencies, possibly as graphviz.
// (though maybe better as separate command)

namespace bant {
static void PrintExternalRepos(
Session &session,
const OneToOne<VersionedProject, FilesystemPath> &external_repos) {
auto printer =
TablePrinter::Create(session.out(), session.flags().output_format,
{"project", "version", "directory"});
for (const auto &[project, file] : external_repos) {
printer->AddRow({project.project,
project.version.empty() ? "-" : project.version,
file.path()});
}
printer->Finish();
}

void PrintMatchingWorkspaceExternalRepos(Session &session,
const ParsedProject &project,
const BazelPattern &pattern) {
const BazelWorkspace &global_workspace = project.workspace();
if (pattern.is_matchall()) {
// The whole workspace.
PrintExternalRepos(session, global_workspace.project_location);
return;
}

BazelWorkspace matching_workspace_subset;
// If we have a pattern, look through the project and fish out all the
// different projects we see.
for (const auto &[_, parsed_package] : project.ParsedFiles()) {
const BazelPackage &current_package = parsed_package->package;
if (!pattern.Match(current_package)) {
continue;
}
query::FindTargets(
parsed_package->ast, {}, [&](const query::Result &details) {
std::vector<std::string_view> potential_external_refs;
if (details.rule == "load") { // load() calls at package level.
// load() has positional arguments.
potential_external_refs =
query::ExtractStringList(details.node->argument());
} else {
// Classical cc_library() etc that has dependencies.
auto target = BazelTarget::ParseFrom(absl::StrCat(":", details.name),
current_package);
if (!target.has_value() || !pattern.Match(*target)) {
return;
}
potential_external_refs = query::ExtractStringList(details.deps_list);
}

// Alright, now let's check these if they reference external projects.
for (const std::string_view ref : potential_external_refs) {
const auto ref_target = BazelTarget::ParseFrom(ref, current_package);
if (!ref_target.has_value()) continue; // could not parse.

// We're only interested in printing projects other than our own.
const std::string &project = ref_target->package.project;
if (project.empty() || project == current_package.project) continue;

// If in global workspace, transfer to our selected subset.
const auto found = global_workspace.FindEntryByProject(project);
if (found == global_workspace.project_location.end()) continue;

// TODO: maybe actually report where this was ? We have all the info.
matching_workspace_subset.project_location.insert(*found);
}
});
}
PrintExternalRepos(session, matching_workspace_subset.project_location);
}
} // namespace bant
34 changes: 34 additions & 0 deletions bant/tool/workspace.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// bant - Bazel Navigation Tool
// Copyright (C) 2024 Henner Zeller <[email protected]>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

#ifndef BANT_TOOL_WORKSPACE_H
#define BANT_TOOL_WORKSPACE_H

#include "bant/frontend/parsed-project.h"
#include "bant/session.h"
#include "bant/types-bazel.h"

namespace bant {
// Print versions and paths for external projects mentioned in the workspace.
// If "pattern" is not matchall, it only prints external projects mentioned
// in dependencies that match.
void PrintMatchingWorkspaceExternalRepos(Session &session,
const ParsedProject &project,
const BazelPattern &pattern);

} // namespace bant
#endif // BANT_TOOL_WORKSPACE_H
13 changes: 10 additions & 3 deletions bant/workspace.cc
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,21 @@ static constexpr std::string_view kExternalBaseDir =
return result;
}

std::optional<FilesystemPath> BazelWorkspace::FindPathByProject(
BazelWorkspace::Map::const_iterator BazelWorkspace::FindEntryByProject(
std::string_view name) const {
if (name.empty()) return std::nullopt;
if (name.empty()) return project_location.end();
if (name[0] == '@') name.remove_prefix(1);
const VersionedProject query{.project = std::string(name), .version = ""};
auto found = project_location.lower_bound(query);
if (found == project_location.end()) return found;
if (found->first.project != name) return project_location.end();
return found;
}

std::optional<FilesystemPath> BazelWorkspace::FindPathByProject(
std::string_view name) const {
auto found = FindEntryByProject(name);
if (found == project_location.end()) return std::nullopt;
if (found->first.project != name) return std::nullopt;
return found->second;
}

Expand Down
11 changes: 9 additions & 2 deletions bant/workspace.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,18 @@ struct VersionedProject {
};

struct BazelWorkspace {
// Returns the first Version that matches project name.
using Map = OneToOne<VersionedProject, FilesystemPath>;

// Returns the first Version that matches project name. Query can be with
// or without leading '@".
std::optional<FilesystemPath> FindPathByProject(std::string_view name) const;

// Lower-level functionality returning the full map-entry. Same look-up
// semantics.
Map::const_iterator FindEntryByProject(std::string_view name) const;

// Project to directory.
OneToOne<VersionedProject, FilesystemPath> project_location;
Map project_location;
};

// Scan current directory for workspace files and create an index of all
Expand Down

0 comments on commit d8a4258

Please sign in to comment.