Skip to content

Commit

Permalink
Merge pull request #49 from Pennycook/platform-option
Browse files Browse the repository at this point in the history
Add option to define platforms on the command line
  • Loading branch information
Pennycook authored Feb 23, 2024
2 parents 5bf1d72 + fee9460 commit 0ebef0f
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 21 deletions.
95 changes: 80 additions & 15 deletions codebasin.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,19 @@ def main():
help="Exclude files matching this pattern from the code base. "
+ "May be specified multiple times.",
)
parser.add_argument(
"-p",
"--platform",
dest="platforms",
metavar="<platform>",
action="append",
default=[],
help="Add the specified platform to the analysis. "
+ "May be a name or a path to a compilation database. "
+ "May be specified multiple times. "
+ "If not specified, all known platforms will be included.",
)

args = parser.parse_args()

stdout_log = logging.StreamHandler(sys.stdout)
Expand All @@ -120,25 +133,73 @@ def main():
)
rootdir = os.path.realpath(args.rootdir)

if args.config_file is None:
# Process the -p flag first to infer wider context.
filtered_platforms = []
additional_platforms = []
for p in args.platforms:
# If it's a path, it has to be a compilation database.
if os.path.exists(p):
if not os.path.splitext(p)[1] == ".json":
raise RuntimeError(f"Platform file {p} must end in .json.")
additional_platforms.append(p)
continue

# Otherwise, treat it as a name in the configuration file.
# Explain the logic above in cases that look suspiciously like paths.
if "/" in p or os.path.splitext(p)[1] == ".json":
logging.getLogger("codebasin").warning(
f"{p} doesn't exist, so will be treated as a name.",
)
filtered_platforms.append(p)

# If no additional platforms are specified, a config file is required.
config_file = args.config_file
if len(additional_platforms) == 0 and config_file is None:
config_file = os.path.join(rootdir, "config.yaml")
else:
config_file = args.config_file
# Load the configuration file into a dict
if not util.ensure_yaml(config_file):
logging.getLogger("codebasin").error(
"Configuration file does not have YAML file extension.",
if not os.path.exists(config_file):
raise RuntimeError(f"Could not find config file {config_file}")

# Set up a default codebase and configuration object.
codebase = {
"files": [],
"platforms": [],
"exclude_files": set(),
"exclude_patterns": args.excludes,
"rootdir": rootdir,
}
configuration = {}

# Load the configuration file if it exists, obeying any platform filter.
if config_file is not None:
if not util.ensure_yaml(config_file):
logging.getLogger("codebasin").error(
"Configuration file does not have YAML file extension.",
)
sys.exit(1)
codebase, configuration = config.load(
config_file,
rootdir,
exclude_patterns=args.excludes,
filtered_platforms=filtered_platforms,
)
sys.exit(1)
codebase, configuration = config.load(
config_file,
rootdir,
exclude_patterns=args.excludes,
)

# Extend configuration with any additional platforms.
for p in additional_platforms:
name = os.path.splitext(os.path.basename(p))[0]
if name in codebase["platforms"]:
raise RuntimeError(f"Platform name {p} conflicts with {name}.")
db = config.load_database(p, rootdir)
configuration.update({name: db})

# Parse the source tree, and determine source line associations.
# The trees and associations are housed in state.
state = finder.find(rootdir, codebase, configuration)
legacy_warnings = True if config_file else False
state = finder.find(
rootdir,
codebase,
configuration,
legacy_warnings=legacy_warnings,
)

# Count lines for platforms
platform_mapper = PlatformMapper(codebase)
Expand Down Expand Up @@ -172,7 +233,11 @@ def report_enabled(name):

# Print clustering report
if report_enabled("clustering"):
output_prefix = os.path.realpath(guess_project_name(config_file))
if config_file is None:
platform_names = [p[0] for p in args.platforms]
output_prefix = "-".join(platform_names)
else:
output_prefix = os.path.realpath(guess_project_name(config_file))
clustering_output_name = output_prefix + "-dendrogram.png"
clustering = report.clustering(clustering_output_name, setmap)
if clustering is not None:
Expand Down
18 changes: 17 additions & 1 deletion codebasin/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,13 @@ def load_platform(config, rootdir, platform_name):
return configuration


def load(config_file, rootdir, *, exclude_patterns=None):
def load(
config_file,
rootdir,
*,
exclude_patterns=None,
filtered_platforms=None,
):
"""
Load the configuration file into Python objects.
Return a (codebase, platform configuration) tuple of dicts.
Expand All @@ -597,6 +603,16 @@ def load(config_file, rootdir, *, exclude_patterns=None):

log.info("Platforms: %s", ", ".join(codebase["platforms"]))

# Limit the set of platforms in the codebase if requested.
if filtered_platforms:
for p in filtered_platforms:
if p not in codebase["platforms"]:
raise RuntimeError(
f"Platform {p} requested on the command line "
+ "does not exist in the configuration file.",
)
codebase["platforms"] = filtered_platforms

# Read each platform definition and populate platform configuration
# If files was empty, populate it with the files we find here
populate_files = not codebase["files"]
Expand Down
18 changes: 13 additions & 5 deletions codebasin/finder.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,14 @@ def get_map(self, fn):
return self.maps[fn]


def find(rootdir, codebase, configuration, *, summarize_only=True):
def find(
rootdir,
codebase,
configuration,
*,
summarize_only=True,
legacy_warnings=True,
):
"""
Find codepaths in the files provided and return a mapping of source
lines to platforms.
Expand All @@ -141,10 +148,11 @@ def find(rootdir, codebase, configuration, *, summarize_only=True):
for e in configuration[p]:
if e["file"] not in codebase["files"]:
filename = e["file"]
log.warning(
f"{filename} found in definition of platform {p} "
+ "but missing from codebase",
)
if legacy_warnings:
log.warning(
f"{filename} found in definition of platform {p} "
+ "but missing from codebase",
)
state.insert_file(e["file"])

# Process each tree, by associating nodes with platforms
Expand Down

0 comments on commit 0ebef0f

Please sign in to comment.