Skip to content

Commit

Permalink
Merge pull request #103 from Pennycook/compilation-database
Browse files Browse the repository at this point in the history
Add CompilationDatabase class
  • Loading branch information
Pennycook authored Sep 3, 2024
2 parents 356b121 + 52d9940 commit 85942ea
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 5 deletions.
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()

0 comments on commit 85942ea

Please sign in to comment.