Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CompilationDatabase class #103

Merged
merged 4 commits into from
Sep 3, 2024
Merged
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
66 changes: 66 additions & 0 deletions codebasin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import pathspec

import codebasin.source
import codebasin.util
import codebasin.walkers

warnings.warn(
Expand Down Expand Up @@ -87,6 +88,10 @@ def __str__(self):
else:
return self._command

def __eq__(self, other):
props = ["directory", "filename", "arguments", "output"]
return all([getattr(self, p) == getattr(other, p) for p in props])

def is_supported(self):
"""
Returns
Expand Down Expand Up @@ -130,6 +135,67 @@ def from_json(cls, instance: dict):
)


class CompilationDatabase:
"""
A compilation database containing multiple CompileCommands.
"""

def __init__(self, commands: list[CompileCommand]):
self.commands = commands

def __iter__(self):
"""
Iterate over all commands in the compilation database.
"""
yield from self.commands

@classmethod
def from_json(cls, instance: list):
"""
Parameters
----------
instance: list
A JSON representation of a list of compile commands.

Raises
------
ValueError
If the JSON fails validation.

Returns
-------
CompilationDatabase
A CompilationDatabase corresponding to the provided JSON.
"""
codebasin.util._validate_json(instance, "compiledb")
commands = [CompileCommand.from_json(c) for c in instance]
return cls(commands)

@classmethod
def from_file(cls, filename: str | os.PathLike[str]):
"""
Parameters
---------
filename: str | os.PathLike[str]
A JSON file containing a compilation database.

Raises
------
ValueError
If the JSON fails validation.

FileNotFoundError
If the file with the specified name does not exist.

Returns
-------
A CompilationDatbase corresponding to the provided JSON file.
"""
with codebasin.util.safe_open_read_nofollow(filename, "r") as f:
db = codebasin.util._load_json(f, schema_name="compiledb")
return CompilationDatabase.from_json(db)


class CodeBase:
"""
A representation of all source files in the code base.
Expand Down
8 changes: 3 additions & 5 deletions codebasin/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import os
import re

from codebasin import CompileCommand, util
from codebasin import CompilationDatabase, util

log = logging.getLogger("codebasin")

Expand Down Expand Up @@ -304,12 +304,10 @@ def load_database(dbpath, rootdir):
Return a list of compilation commands, where each command is
represented as a compilation database entry.
"""
with util.safe_open_read_nofollow(dbpath, "r") as fi:
db = util._load_json(fi, schema_name="compiledb")
db = CompilationDatabase.from_file(dbpath)

configuration = []
for e in db:
command = CompileCommand.from_json(e)
for command in db:
if not command.is_supported():
continue
argv = command.arguments
Expand Down
2 changes: 2 additions & 0 deletions tests/compilation-database/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Copyright (C) 2019-2024 Intel Corporation
# SPDX-License-Identifier: BSD-3-Clause
73 changes: 73 additions & 0 deletions tests/compilation-database/test_compilation_database.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Copyright (C) 2019-2024 Intel Corporation
# SPDX-License-Identifier: BSD-3-Clause

import json
import tempfile
import unittest

from codebasin import CompilationDatabase, CompileCommand


class TestCompilationDatabase(unittest.TestCase):
"""
Test CompileDatabase class.
"""

def setUp(self):
self.commands = [
CompileCommand("foo.o", command="c++ -o foo.o foo.c"),
CompileCommand("bar.o", command="c++ -o bar.o bar.c"),
]
self.valid_json = [
{
"arguments": ["gcc", "-c", "-o", "output", "test.cpp"],
"directory": "/path/containing/source/files/",
"file": "test.cpp",
},
]
self.invalid_json = [
{
"arguments": ["gcc", "-c", "-o", "output", "test.cpp"],
"directory": ["not", "a", "directory"],
"file": "test.cpp",
},
]

def test_constructor(self):
"""Check commands are stored correctly"""
db = CompilationDatabase(self.commands)
self.assertEqual(self.commands, db.commands)

def test_iterator(self):
"""Check implementation of __iter__"""
db = CompilationDatabase(self.commands)
commands = [c for c in db]
self.assertEqual(self.commands, commands)

def test_from_json(self):
"""Check conversion from JSON"""
db = CompilationDatabase.from_json(self.valid_json)
commands = [CompileCommand.from_json(self.valid_json[0])]
self.assertEqual(commands, db.commands)

with self.assertRaises(ValueError):
_ = CompilationDatabase.from_json(self.invalid_json)

def test_from_file(self):
"""Check conversion from file"""
with tempfile.NamedTemporaryFile(mode="w", delete_on_close=False) as f:
json.dump(self.valid_json, f)
f.close()
db = CompilationDatabase.from_file(f.name)
commands = [CompileCommand.from_json(self.valid_json[0])]
self.assertEqual(commands, db.commands)

with tempfile.NamedTemporaryFile(mode="w", delete_on_close=False) as f:
json.dump(self.invalid_json, f)
f.close()
with self.assertRaises(ValueError):
_ = CompilationDatabase.from_file(f.name)


if __name__ == "__main__":
unittest.main()
Loading