Skip to content

Commit 541bb18

Browse files
committed
Replace -R tree with cbi-tree utility
This initial commit just moves the existing functionality. Other changes to reflect the new standalone nature of the utility (e.g., improved formatting, additional options) will follow in subsequent commits. Signed-off-by: John Pennycook <john.pennycook@intel.com>
1 parent 180cef8 commit 541bb18

File tree

3 files changed

+177
-5
lines changed

3 files changed

+177
-5
lines changed

codebasin/__main__.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ def _main():
9696
metavar="<report>",
9797
action="append",
9898
default=[],
99-
choices=["all", "summary", "clustering", "duplicates", "files"],
99+
choices=["all", "summary", "clustering", "duplicates"],
100100
help=_help_string(
101101
"Generate a report of the specified type:",
102102
"- summary: code divergence information",
@@ -246,10 +246,6 @@ def report_enabled(name):
246246
if report_enabled("summary"):
247247
report.summary(setmap)
248248

249-
# Print files report
250-
if report_enabled("files"):
251-
report.files(codebase, state)
252-
253249
# Print clustering report
254250
if report_enabled("clustering"):
255251
basename = os.path.basename(args.analysis_file)

codebasin/tree.py

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
#!/usr/bin/env python3
2+
# Copyright (C) 2019-2024 Intel Corporation
3+
# SPDX-License-Identifier: BSD-3-Clause
4+
5+
import argparse
6+
import logging
7+
import os
8+
import sys
9+
10+
from codebasin import CodeBase, config, finder, report, util
11+
12+
# TODO: Refactor to avoid imports from __main__
13+
from codebasin.__main__ import Formatter, _help_string, version
14+
15+
log = logging.getLogger("codebasin")
16+
17+
18+
def _build_parser() -> argparse.ArgumentParser:
19+
"""
20+
Build argument parser.
21+
"""
22+
parser = argparse.ArgumentParser(
23+
description="CBI Tree Tool " + version,
24+
formatter_class=argparse.RawTextHelpFormatter,
25+
add_help=False,
26+
)
27+
parser.add_argument(
28+
"-h",
29+
"--help",
30+
action="help",
31+
help=_help_string("Display help message and exit."),
32+
)
33+
parser.add_argument(
34+
"--version",
35+
action="version",
36+
version=f"CBI Coverage Tool {version}",
37+
help=_help_string("Display version information and exit."),
38+
)
39+
parser.add_argument(
40+
"-x",
41+
"--exclude",
42+
dest="excludes",
43+
metavar="<pattern>",
44+
action="append",
45+
default=[],
46+
help=_help_string(
47+
"Exclude files matching this pattern from the code base.",
48+
"May be specified multiple times.",
49+
is_long=True,
50+
),
51+
)
52+
parser.add_argument(
53+
"-p",
54+
"--platform",
55+
dest="platforms",
56+
metavar="<platform>",
57+
action="append",
58+
default=[],
59+
help=_help_string(
60+
"Include the specified platform in the analysis.",
61+
"May be specified multiple times.",
62+
"If not specified, all platforms will be included.",
63+
is_long=True,
64+
is_last=True,
65+
),
66+
)
67+
68+
parser.add_argument(
69+
"analysis_file",
70+
metavar="<analysis-file>",
71+
help=_help_string(
72+
"TOML file describing the analysis to be performed, "
73+
+ "including the codebase and platform descriptions.",
74+
is_last=True,
75+
),
76+
)
77+
78+
return parser
79+
80+
81+
def _tree(args: argparse.Namespace):
82+
# TODO: Refactor this to avoid duplication in __main__
83+
# Determine the root directory based on where codebasin is run.
84+
rootdir = os.path.abspath(os.getcwd())
85+
86+
# Set up a default configuration object.
87+
configuration = {}
88+
89+
# Load the analysis file if it exists.
90+
if args.analysis_file is not None:
91+
path = os.path.abspath(args.analysis_file)
92+
if os.path.exists(path):
93+
if not os.path.splitext(path)[1] == ".toml":
94+
raise RuntimeError(f"Analysis file {path} must end in .toml.")
95+
96+
with open(path, "rb") as f:
97+
analysis_toml = util._load_toml(f, "analysis")
98+
99+
if "codebase" in analysis_toml:
100+
if "exclude" in analysis_toml["codebase"]:
101+
args.excludes += analysis_toml["codebase"]["exclude"]
102+
103+
for name in args.platforms:
104+
if name not in analysis_toml["platform"].keys():
105+
raise KeyError(
106+
f"Platform {name} requested on the command line "
107+
+ "does not exist in the configuration file.",
108+
)
109+
110+
cmd_platforms = args.platforms.copy()
111+
for name in analysis_toml["platform"].keys():
112+
if cmd_platforms and name not in cmd_platforms:
113+
continue
114+
if "commands" not in analysis_toml["platform"][name]:
115+
raise ValueError(f"Missing 'commands' for platform {name}")
116+
p = analysis_toml["platform"][name]["commands"]
117+
db = config.load_database(p, rootdir)
118+
args.platforms.append(name)
119+
configuration.update({name: db})
120+
121+
# Construct a codebase object associated with the root directory.
122+
codebase = CodeBase(rootdir, exclude_patterns=args.excludes)
123+
124+
# Parse the source tree, and determine source line associations.
125+
# The trees and associations are housed in state.
126+
state = finder.find(
127+
rootdir,
128+
codebase,
129+
configuration,
130+
show_progress=True,
131+
)
132+
133+
# Print the file tree.
134+
report.files(codebase, state)
135+
sys.exit(0)
136+
137+
138+
def cli(argv: list[str]) -> int:
139+
parser = _build_parser()
140+
args = parser.parse_args(argv)
141+
142+
# Configure logging such that:
143+
# - All messages are written to a log file
144+
# - Only errors are written to the terminal
145+
log.setLevel(logging.DEBUG)
146+
147+
file_handler = logging.FileHandler("cbi.log", mode="w")
148+
file_handler.setLevel(logging.INFO)
149+
file_handler.setFormatter(Formatter())
150+
log.addHandler(file_handler)
151+
152+
# Inform the user that a log file has been created.
153+
# 'print' instead of 'log' to ensure the message is visible in the output.
154+
log_path = os.path.abspath("cbi.log")
155+
print(f"Log file created at {log_path}")
156+
157+
stderr_handler = logging.StreamHandler(sys.stderr)
158+
stderr_handler.setLevel(logging.ERROR)
159+
stderr_handler.setFormatter(Formatter(colors=sys.stderr.isatty()))
160+
log.addHandler(stderr_handler)
161+
162+
return _tree(args)
163+
164+
165+
def main():
166+
try:
167+
cli(sys.argv[1:])
168+
except Exception as e:
169+
log.error(str(e))
170+
sys.exit(1)
171+
172+
173+
if __name__ == "__main__":
174+
sys.argv[0] = "codebasin.tree"
175+
main()

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ dependencies = [
3838
[project.scripts]
3939
codebasin = "codebasin:__main__.main"
4040
cbi-cov = "codebasin.coverage:__main__.main"
41+
cbi-tree = "codebasin:tree.main"
4142

4243
[project.urls]
4344
"Github" = "https://www.github.com/intel/code-base-investigator"

0 commit comments

Comments
 (0)