From c6ca138b583b401d9d6e6b51f66cf16dc50eb3a4 Mon Sep 17 00:00:00 2001 From: Chad Norvell Date: Tue, 18 Jul 2023 20:40:56 +0000 Subject: [PATCH] pw_ide: Support multiple comp DB search paths Bug: 280363633 Change-Id: I3f5d46a3153e083c4640e3fc66d5c1a19b0cd34a Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/144210 Presubmit-Verified: CQ Bot Account Reviewed-by: Anthony DiGirolamo Commit-Queue: Chad Norvell --- pw_ide/docs.rst | 3 +- pw_ide/py/cpp_test.py | 34 ++++++++-------- pw_ide/py/pw_ide/cli.py | 6 --- pw_ide/py/pw_ide/commands.py | 47 ++++++++++++++++------ pw_ide/py/pw_ide/cpp.py | 60 +++++++++++++++++++++++---- pw_ide/py/pw_ide/settings.py | 78 +++++++++++++++++++++--------------- 6 files changed, 150 insertions(+), 78 deletions(-) diff --git a/pw_ide/docs.rst b/pw_ide/docs.rst index f69bb8ef4d..a5809800f2 100644 --- a/pw_ide/docs.rst +++ b/pw_ide/docs.rst @@ -26,8 +26,7 @@ source control, and applies only to that checkout of the project. All of these files have the same schema, in which these options can be configured: .. autoproperty:: pw_ide.settings.PigweedIdeSettings.working_dir -.. autoproperty:: pw_ide.settings.PigweedIdeSettings.build_dir -.. autoproperty:: pw_ide.settings.PigweedIdeSettings.compdb_paths +.. autoproperty:: pw_ide.settings.PigweedIdeSettings.compdb_search_paths .. autoproperty:: pw_ide.settings.PigweedIdeSettings.targets .. autoproperty:: pw_ide.settings.PigweedIdeSettings.target_inference .. autoproperty:: pw_ide.settings.PigweedIdeSettings.default_target diff --git a/pw_ide/py/cpp_test.py b/pw_ide/py/cpp_test.py index 42ab52aa58..ee5abc423b 100644 --- a/pw_ide/py/cpp_test.py +++ b/pw_ide/py/cpp_test.py @@ -970,7 +970,7 @@ class TestCppCompilationDatabase(PwIdeTestCase): """Tests CppCompilationDatabase""" def setUp(self): - self.build_dir = Path('/pigweed/pigweed/out') + self.root_dir = Path('/pigweed/pigweed/out') self.fixture: List[CppCompileCommandDict] = [ { @@ -1046,10 +1046,10 @@ def setUp(self): def test_merge(self): compdb1 = CppCompilationDatabase.load( - self.fixture_merge_1, self.build_dir + self.fixture_merge_1, self.root_dir ) compdb2 = CppCompilationDatabase.load( - self.fixture_merge_2, self.build_dir + self.fixture_merge_2, self.root_dir ) compdb1.merge(compdb2) result = [compile_command.as_dict() for compile_command in compdb1] @@ -1058,22 +1058,22 @@ def test_merge(self): def test_merge_no_dupes(self): compdb1 = CppCompilationDatabase.load( - self.fixture_merge_1, self.build_dir + self.fixture_merge_1, self.root_dir ) fixture_combo = [*self.fixture_merge_1, *self.fixture_merge_2] - compdb2 = CppCompilationDatabase.load(fixture_combo, self.build_dir) + compdb2 = CppCompilationDatabase.load(fixture_combo, self.root_dir) compdb1.merge(compdb2) result = [compile_command.as_dict() for compile_command in compdb1] expected = [*self.fixture_merge_1, *self.fixture_merge_2] self.assertCountEqual(result, expected) def test_load_from_dicts(self): - compdb = CppCompilationDatabase.load(self.fixture, self.build_dir) + compdb = CppCompilationDatabase.load(self.fixture, self.root_dir) self.assertCountEqual(compdb.as_dicts(), self.expected) def test_load_from_json(self): compdb = CppCompilationDatabase.load( - json.dumps(self.fixture), self.build_dir + json.dumps(self.fixture), self.root_dir ) self.assertCountEqual(compdb.as_dicts(), self.expected) @@ -1084,7 +1084,7 @@ def test_load_from_path(self): ) as (_, file_path): path = file_path - compdb = CppCompilationDatabase.load(path, self.build_dir) + compdb = CppCompilationDatabase.load(path, self.root_dir) self.assertCountEqual(compdb.as_dicts(), self.expected) def test_load_from_file_handle(self): @@ -1092,7 +1092,7 @@ def test_load_from_file_handle(self): COMPDB_FILE_NAME, json.dumps(self.fixture), ) as (file, _): - compdb = CppCompilationDatabase.load(file, self.build_dir) + compdb = CppCompilationDatabase.load(file, self.root_dir) self.assertCountEqual(compdb.as_dicts(), self.expected) @@ -1111,22 +1111,22 @@ def test_process(self): raw_db: List[CppCompileCommandDict] = [ { 'command': 'arm-none-eabi-g++ -MMD -MF stm32f429i_disc1_debug/obj/pw_allocator/block.block.cc.o.d -Wno-psabi -mabi=aapcs -mthumb --sysroot=../environment/cipd/packages/arm -specs=nano.specs -specs=nosys.specs -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -Og -Wshadow -Wredundant-decls -u_printf_float -fdiagnostics-color -g -fno-common -fno-exceptions -ffunction-sections -fdata-sections -Wall -Wextra -Wimplicit-fallthrough -Wcast-qual -Wundef -Wpointer-arith -Werror -Wno-error=cpp -Wno-error=deprecated-declarations -ffile-prefix-map=/pigweed/pigweed/out=out -ffile-prefix-map=/pigweed/pigweed/= -ffile-prefix-map=../= -ffile-prefix-map=/pigweed/pigweed/out=out -fno-rtti -Wnon-virtual-dtor -std=c++17 -Wno-register -DPW_ARMV7M_ENABLE_FPU=1 -I../pw_allocator/public -I../pw_assert/public -I../pw_assert/assert_compatibility_public_overrides -I../pw_preprocessor/public -I../pw_assert_basic/public_overrides -I../pw_assert_basic/public -I../pw_span/public -I../pw_polyfill/public -I../pw_polyfill/standard_library_public -I../pw_status/public -c ../pw_allocator/block.cc -o stm32f429i_disc1_debug/obj/pw_allocator/block.block.cc.o', - 'directory': str(self.build_dir), + 'directory': str(self.root_dir), 'file': '../pw_allocator/block.cc', }, { 'command': '../environment/cipd/packages/pigweed/bin/isosceles-clang++ -MMD -MF isosceles_debug/obj/pw_allocator/block.block.cc.o.d -g3 --sysroot=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk -Og -Wshadow -Wredundant-decls -Wthread-safety -Wswitch-enum -fdiagnostics-color -g -fno-common -fno-exceptions -ffunction-sections -fdata-sections -Wall -Wextra -Wimplicit-fallthrough -Wcast-qual -Wundef -Wpointer-arith -Werror -Wno-error=cpp -Wno-error=deprecated-declarations -ffile-prefix-map=/pigweed/pigweed/out=out -ffile-prefix-map=/pigweed/pigweed/= -ffile-prefix-map=../= -ffile-prefix-map=/pigweed/pigweed/out=out -Wextra-semi -fno-rtti -Wnon-virtual-dtor -std=c++17 -Wno-register -D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS=1 -DPW_STATUS_CFG_CHECK_IF_USED=1 -I../pw_allocator/public -I../pw_assert/public -I../pw_assert/print_and_abort_assert_public_overrides -I../pw_preprocessor/public -I../pw_assert_basic/public_overrides -I../pw_assert_basic/public -I../pw_span/public -I../pw_polyfill/public -I../pw_polyfill/standard_library_public -I../pw_status/public -c ../pw_allocator/block.cc -o isosceles_debug/obj/pw_allocator/block.block.cc.o', - 'directory': str(self.build_dir), + 'directory': str(self.root_dir), 'file': '../pw_allocator/block.cc', }, { 'command': 'ccache ../environment/cipd/packages/pigweed/bin/clang++ -MMD -MF pw_strict_host_clang_debug/obj/pw_allocator/block.block.cc.o.d -g3 --sysroot=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk -Og -Wshadow -Wredundant-decls -Wthread-safety -Wswitch-enum -fdiagnostics-color -g -fno-common -fno-exceptions -ffunction-sections -fdata-sections -Wall -Wextra -Wimplicit-fallthrough -Wcast-qual -Wundef -Wpointer-arith -Werror -Wno-error=cpp -Wno-error=deprecated-declarations -ffile-prefix-map=/pigweed/pigweed/out=out -ffile-prefix-map=/pigweed/pigweed/= -ffile-prefix-map=../= -ffile-prefix-map=/pigweed/pigweed/out=out -Wextra-semi -fno-rtti -Wnon-virtual-dtor -std=c++17 -Wno-register -D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS=1 -DPW_STATUS_CFG_CHECK_IF_USED=1 -I../pw_allocator/public -I../pw_assert/public -I../pw_assert/print_and_abort_assert_public_overrides -I../pw_preprocessor/public -I../pw_assert_basic/public_overrides -I../pw_assert_basic/public -I../pw_span/public -I../pw_polyfill/public -I../pw_polyfill/standard_library_public -I../pw_status/public -c ../pw_allocator/block.cc -o pw_strict_host_clang_debug/obj/pw_allocator/block.block.cc.o', - 'directory': str(self.build_dir), + 'directory': str(self.root_dir), 'file': '../pw_allocator/block.cc', }, { 'command': "python ../pw_toolchain/py/pw_toolchain/clang_tidy.py --source-exclude 'third_party/.*' --source-exclude '.*packages/mbedtls.*' --source-exclude '.*packages/boringssl.*' --skip-include-path 'mbedtls/include' --skip-include-path 'mbedtls' --skip-include-path 'boringssl/src/include' --skip-include-path 'boringssl' --skip-include-path 'pw_tls_client/generate_test_data' --source-file ../pw_allocator/block.cc --source-root '../' --export-fixes pw_strict_host_clang_debug.static_analysis/obj/pw_allocator/block.block.cc.o.yaml -- ../environment/cipd/packages/pigweed/bin/clang++ END_OF_INVOKER -MMD -MF pw_strict_host_clang_debug.static_analysis/obj/pw_allocator/block.block.cc.o.d -g3 --sysroot=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk -Og -Wshadow -Wredundant-decls -Wthread-safety -Wswitch-enum -fdiagnostics-color -g -fno-common -fno-exceptions -ffunction-sections -fdata-sections -Wall -Wextra -Wimplicit-fallthrough -Wcast-qual -Wundef -Wpointer-arith -Werror -Wno-error=cpp -Wno-error=deprecated-declarations -ffile-prefix-map=/pigweed/pigweed/out=out -ffile-prefix-map=/pigweed/pigweed/= -ffile-prefix-map=../= -ffile-prefix-map=/pigweed/pigweed/out=out -Wextra-semi -fno-rtti -Wnon-virtual-dtor -std=c++17 -Wno-register -D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS=1 -DPW_STATUS_CFG_CHECK_IF_USED=1 -I../pw_allocator/public -I../pw_assert/public -I../pw_assert/print_and_abort_assert_public_overrides -I../pw_preprocessor/public -I../pw_assert_basic/public_overrides -I../pw_assert_basic/public -I../pw_span/public -I../pw_polyfill/public -I../pw_polyfill/standard_library_public -I../pw_status/public -c ../pw_allocator/block.cc -o pw_strict_host_clang_debug.static_analysis/obj/pw_allocator/block.block.cc.o && touch pw_strict_host_clang_debug.static_analysis/obj/pw_allocator/block.block.cc.o", - 'directory': str(self.build_dir), + 'directory': str(self.root_dir), 'file': '../pw_allocator/block.cc', }, ] @@ -1137,7 +1137,7 @@ def test_process(self): 'command': # Ensures path format matches OS (e.g. Windows) f'{Path("../environment/cipd/packages/pigweed/bin/isosceles-clang++")} -MMD -MF isosceles_debug/obj/pw_allocator/block.block.cc.o.d -g3 --sysroot=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk -Og -Wshadow -Wredundant-decls -Wthread-safety -Wswitch-enum -fdiagnostics-color -g -fno-common -fno-exceptions -ffunction-sections -fdata-sections -Wall -Wextra -Wimplicit-fallthrough -Wcast-qual -Wundef -Wpointer-arith -Werror -Wno-error=cpp -Wno-error=deprecated-declarations -ffile-prefix-map=/pigweed/pigweed/out=out -ffile-prefix-map=/pigweed/pigweed/= -ffile-prefix-map=../= -ffile-prefix-map=/pigweed/pigweed/out=out -Wextra-semi -fno-rtti -Wnon-virtual-dtor -std=c++17 -Wno-register -D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS=1 -DPW_STATUS_CFG_CHECK_IF_USED=1 -I../pw_allocator/public -I../pw_assert/public -I../pw_assert/print_and_abort_assert_public_overrides -I../pw_preprocessor/public -I../pw_assert_basic/public_overrides -I../pw_assert_basic/public -I../pw_span/public -I../pw_polyfill/public -I../pw_polyfill/standard_library_public -I../pw_status/public -c ../pw_allocator/block.cc -o isosceles_debug/obj/pw_allocator/block.block.cc.o', - 'directory': str(self.build_dir), + 'directory': str(self.root_dir), 'file': '../pw_allocator/block.cc', 'output': 'isosceles_debug/obj/pw_allocator/block.block.cc.o', }, @@ -1147,7 +1147,7 @@ def test_process(self): 'command': # Ensures path format matches OS (e.g. Windows) f'ccache {Path("../environment/cipd/packages/pigweed/bin/clang++")} -MMD -MF pw_strict_host_clang_debug/obj/pw_allocator/block.block.cc.o.d -g3 --sysroot=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk -Og -Wshadow -Wredundant-decls -Wthread-safety -Wswitch-enum -fdiagnostics-color -g -fno-common -fno-exceptions -ffunction-sections -fdata-sections -Wall -Wextra -Wimplicit-fallthrough -Wcast-qual -Wundef -Wpointer-arith -Werror -Wno-error=cpp -Wno-error=deprecated-declarations -ffile-prefix-map=/pigweed/pigweed/out=out -ffile-prefix-map=/pigweed/pigweed/= -ffile-prefix-map=../= -ffile-prefix-map=/pigweed/pigweed/out=out -Wextra-semi -fno-rtti -Wnon-virtual-dtor -std=c++17 -Wno-register -D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS=1 -DPW_STATUS_CFG_CHECK_IF_USED=1 -I../pw_allocator/public -I../pw_assert/public -I../pw_assert/print_and_abort_assert_public_overrides -I../pw_preprocessor/public -I../pw_assert_basic/public_overrides -I../pw_assert_basic/public -I../pw_span/public -I../pw_polyfill/public -I../pw_polyfill/standard_library_public -I../pw_status/public -c ../pw_allocator/block.cc -o pw_strict_host_clang_debug/obj/pw_allocator/block.block.cc.o', - 'directory': str(self.build_dir), + 'directory': str(self.root_dir), 'file': '../pw_allocator/block.cc', 'output': 'pw_strict_host_clang_debug/obj/pw_allocator/block.block.cc.o', }, @@ -1157,7 +1157,7 @@ def test_process(self): 'command': # Ensures this test avoids the unpathed compiler search f'{self.temp_dir_path / "arm-none-eabi-g++"} -MMD -MF stm32f429i_disc1_debug/obj/pw_allocator/block.block.cc.o.d -Wno-psabi -mabi=aapcs -mthumb --sysroot=../environment/cipd/packages/arm -specs=nano.specs -specs=nosys.specs -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -Og -Wshadow -Wredundant-decls -u_printf_float -fdiagnostics-color -g -fno-common -fno-exceptions -ffunction-sections -fdata-sections -Wall -Wextra -Wimplicit-fallthrough -Wcast-qual -Wundef -Wpointer-arith -Werror -Wno-error=cpp -Wno-error=deprecated-declarations -ffile-prefix-map=/pigweed/pigweed/out=out -ffile-prefix-map=/pigweed/pigweed/= -ffile-prefix-map=../= -ffile-prefix-map=/pigweed/pigweed/out=out -fno-rtti -Wnon-virtual-dtor -std=c++17 -Wno-register -DPW_ARMV7M_ENABLE_FPU=1 -I../pw_allocator/public -I../pw_assert/public -I../pw_assert/assert_compatibility_public_overrides -I../pw_preprocessor/public -I../pw_assert_basic/public_overrides -I../pw_assert_basic/public -I../pw_span/public -I../pw_polyfill/public -I../pw_polyfill/standard_library_public -I../pw_status/public -c ../pw_allocator/block.cc -o stm32f429i_disc1_debug/obj/pw_allocator/block.block.cc.o', - 'directory': str(self.build_dir), + 'directory': str(self.root_dir), 'file': '../pw_allocator/block.cc', 'output': 'stm32f429i_disc1_debug/obj/pw_allocator/block.block.cc.o', }, @@ -1166,7 +1166,7 @@ def test_process(self): # pylint: enable=line-too-long compdbs = CppCompilationDatabase.load( - raw_db, build_dir=self.build_dir + raw_db, root_dir=self.root_dir ).process(settings, default_path=self.temp_dir_path) compdbs_as_dicts = { target: compdb.as_dicts() for target, compdb in compdbs.items() diff --git a/pw_ide/py/pw_ide/cli.py b/pw_ide/py/pw_ide/cli.py index 1b9d3a6754..8e0c216550 100644 --- a/pw_ide/py/pw_ide/cli.py +++ b/pw_ide/py/pw_ide/cli.py @@ -16,7 +16,6 @@ import argparse import enum from inspect import cleandoc -from pathlib import Path import re from typing import Any, Callable, Dict, List, Optional, Protocol @@ -313,11 +312,6 @@ def _build_argument_parser() -> argparse.ArgumentParser: help='process a file or several files matching ' 'the clang compilation database format', ) - parser_cpp.add_argument( - '--build-dir', - type=Path, - help='override the build directory defined in pw_ide settings', - ) parser_cpp.add_argument( '--clangd-command', action='store_true', diff --git a/pw_ide/py/pw_ide/commands.py b/pw_ide/py/pw_ide/commands.py index 185df5b156..6263f022a4 100644 --- a/pw_ide/py/pw_ide/commands.py +++ b/pw_ide/py/pw_ide/commands.py @@ -18,7 +18,7 @@ import shlex import subprocess import sys -from typing import cast, Dict, List, Optional, Set +from typing import cast, Dict, List, Optional, Set, Tuple from pw_cli.env import pigweed_environment @@ -280,13 +280,37 @@ def _process_compdbs( # pylint: disable=too-many-locals # Associate processed compilation databases with their original sources all_processed_compdbs: Dict[Path, CppCompilationDatabasesMap] = {} - build_dir: Path = pw_ide_settings.build_dir - compdb_file_paths = list(build_dir.rglob(str(COMPDB_FILE_NAME))) - - for compdb_file_path in compdb_file_paths: + # Get a list of paths to search for compilation databases. + compdb_search_paths: List[ + Tuple[Path, str] + ] = pw_ide_settings.compdb_search_paths + # Get the list of files for each search path, tupled with the search path. + compdb_file_path_groups = [ + (search_path, list(search_path[0].rglob(str(COMPDB_FILE_NAME)))) + for search_path in compdb_search_paths + ] + # Flatten that list. + compdb_file_paths: List[Tuple[Path, Path, str]] = [ + (search_path, file_path, target_inference) + for ( + (search_path, target_inference), + file_path_group, + ) in compdb_file_path_groups + for file_path in file_path_group + ] + + for ( + compdb_root_dir, + compdb_file_path, + target_inference, + ) in compdb_file_paths: # Load the compilation database try: - compdb = CppCompilationDatabase.load(compdb_file_path, build_dir) + compdb = CppCompilationDatabase.load( + compdb_to_load=compdb_file_path, + root_dir=compdb_root_dir, + target_inference=target_inference, + ) except MissingCompDbException: reporter.err(f'File not found: {str(compdb_file_path)}') sys.exit(1) @@ -330,14 +354,16 @@ def _process_compdbs( # pylint: disable=too-many-locals if processed_compdbs is None: # Infer the name of the target from the path name = '_'.join( - compdb_file_path.relative_to(build_dir).parent.parts + compdb_file_path.relative_to(compdb_root_dir).parent.parts ) target = CppIdeFeaturesTarget( name=name, compdb_file_path=compdb_file_path, num_commands=len( - CppCompilationDatabase.load(compdb_file_path, build_dir) + CppCompilationDatabase.load( + compdb_file_path, compdb_root_dir + ) ), ) @@ -456,7 +482,6 @@ def cmd_cpp( # pylint: disable=too-many-arguments, too-many-locals, too-many-br should_list_targets: bool, should_get_target: bool, target_to_set: Optional[str], - build_dir: Optional[Path], process: bool = True, use_default_target: bool = False, clangd_command: bool = False, @@ -556,10 +581,6 @@ def cmd_cpp( # pylint: disable=too-many-arguments, too-many-locals, too-many-br state = CppIdeFeaturesState(pw_ide_settings) should_update_ides = False - build_dir = ( - build_dir if build_dir is not None else pw_ide_settings.build_dir - ) - if process: default = False should_update_ides = _process_compdbs(reporter, pw_ide_settings) diff --git a/pw_ide/py/pw_ide/cpp.py b/pw_ide/py/pw_ide/cpp.py index 2facbd2970..03dff5b927 100644 --- a/pw_ide/py/pw_ide/cpp.py +++ b/pw_ide/py/pw_ide/cpp.py @@ -666,6 +666,27 @@ def as_dict(self) -> CppCompileCommandDict: return compile_command_dict +def _path_nearest_parent(path1: Path, path2: Path) -> Path: + """Get the closest common parent of two paths.""" + # This is the Python < 3.9 version of: if path2.is_relative_to(path1) + try: + path2.relative_to(path1) + return path1 + except ValueError: + pass + + if path1 == path2: + return path1 + + if len(path1.parts) > len(path2.parts): + return _path_nearest_parent(path1.parent, path2) + + if len(path1.parts) < len(path2.parts): + return _path_nearest_parent(path1, path2.parent) + + return _path_nearest_parent(path1.parent, path2.parent) + + def _infer_target_pos(target_glob: str) -> List[int]: """Infer the position of the target in a compilation unit artifact path.""" tokens = Path(target_glob).parts @@ -697,7 +718,17 @@ def infer_target( # may be in the "directory" or the "output" of the compile command. So we # need to construct the full path that combines both and use that to search # for the target. - subpath = output_path.relative_to(root) + try: + # The path used for target inference is the path relative to the root + # dir. If this artifact is a direct child of the root, this just + # truncates the root off of its path. + subpath = output_path.relative_to(root) + except ValueError: + # If the output path isn't a child path of the root dir, find the + # closest shared parent dir and use that as the root for truncation. + common_parent = _path_nearest_parent(root, output_path) + subpath = output_path.relative_to(common_parent) + return '_'.join([subpath.parts[pos] for pos in target_pos]) @@ -714,20 +745,26 @@ class CppCompilationDatabase: def __init__( self, - build_dir: Optional[Path] = None, + root_dir: Optional[Path] = None, file_path: Optional[Path] = None, source_file_path: Optional[Path] = None, + target_inference: Optional[str] = None, ) -> None: self._db: List[CppCompileCommand] = [] self.file_path: Optional[Path] = file_path self.source_file_path: Optional[Path] = source_file_path self.source_file_hash: Optional[str] = None + if target_inference is None: + self.target_inference = PigweedIdeSettings().target_inference + else: + self.target_inference = target_inference + # Only compilation databases that are loaded will have this, and it # contains the root directory of the build that the compilation # database is based on. Processed compilation databases will not have # a value here. - self._build_dir = build_dir + self._root_dir = root_dir def __len__(self) -> int: return len(self._db) @@ -784,7 +821,10 @@ def to_file(self, path: Path): @classmethod def load( - cls, compdb_to_load: LoadableToCppCompilationDatabase, build_dir: Path + cls, + compdb_to_load: LoadableToCppCompilationDatabase, + root_dir: Path, + target_inference: Optional[str] = None, ) -> 'CppCompilationDatabase': """Load a compilation database. @@ -817,7 +857,11 @@ def load( db_as_dicts = json.loads(compdb_data) - compdb = cls(build_dir=build_dir, file_path=file_path) + compdb = cls( + root_dir=root_dir, + file_path=file_path, + target_inference=target_inference, + ) try: compdb.add( @@ -862,7 +906,7 @@ def process( original compilation databases outside the working directory are never made available for code intelligence. """ - if self._build_dir is None: + if self._root_dir is None: raise ValueError( 'Can only process a compilation database that ' 'contains a root build directory, usually ' @@ -885,8 +929,8 @@ def process( and processed_command.output_path is not None ): target = infer_target( - settings.target_inference, - self._build_dir, + self.target_inference, + self._root_dir, processed_command.output_path, ) diff --git a/pw_ide/py/pw_ide/settings.py b/pw_ide/py/pw_ide/settings.py index 537d6c2722..fbb63babba 100644 --- a/pw_ide/py/pw_ide/settings.py +++ b/pw_ide/py/pw_ide/settings.py @@ -15,10 +15,9 @@ import enum from inspect import cleandoc -import glob import os from pathlib import Path -from typing import Any, cast, Dict, List, Literal, Optional, Union +from typing import Any, cast, Dict, List, Literal, Optional, Tuple, Union import yaml from pw_cli.env import pigweed_environment @@ -49,8 +48,7 @@ class SupportedEditor(enum.Enum): _DEFAULT_CONFIG: Dict[str, Any] = { 'cascade_targets': False, 'clangd_additional_query_drivers': [], - 'build_dir': _DEFAULT_BUILD_DIR, - 'compdb_paths': _DEFAULT_BUILD_DIR_NAME, + 'compdb_search_paths': [_DEFAULT_BUILD_DIR_NAME], 'default_target': None, 'editors': _DEFAULT_SUPPORTED_EDITORS, 'sync': ['pw --no-banner ide cpp --process'], @@ -91,6 +89,22 @@ def _expand_any_vars_str(input_path: str) -> str: return str(_expand_any_vars(Path(input_path))) +def _parse_dir_path(input_path_str: str) -> Path: + if (path := Path(input_path_str)).is_absolute(): + return path + + return Path.cwd() / path + + +def _parse_compdb_search_path( + input_data: Union[str, Tuple[str, str]], default_inference: str +) -> Tuple[Path, str]: + if isinstance(input_data, (tuple, list)): + return _parse_dir_path(input_data[0]), input_data[1] + + return _parse_dir_path(input_data), default_inference + + class PigweedIdeSettings(YamlConfigLoaderMixin): """Pigweed IDE features settings storage class.""" @@ -124,30 +138,25 @@ def working_dir(self) -> Path: return Path(self._config.get('working_dir', PW_IDE_DEFAULT_DIR)) @property - def build_dir(self) -> Path: - """The build system's root output directory. - - We will use this as the output directory when automatically running - build system commands, and will use it to resolve target names using - target name inference when processing compilation databases. This can - be the same build directory used for general-purpose builds, but it - does not have to be. - """ - return Path(self._config.get('build_dir', _DEFAULT_BUILD_DIR)) - - @property - def compdb_paths(self) -> str: - """A path glob to search for compilation databases. - - These paths can be to files or to directories. Paths that are - directories will be appended with the default file name for - ``clangd`` compilation databases, ``compile_commands.json``. + def compdb_search_paths(self) -> List[Tuple[Path, str]]: + """Paths to directories to search for compilation databases. + + If you're using a build system to generate compilation databases, this + may simply be your build output directory. However, you can add + additional directories to accommodate compilation databases from other + sources. + + Entries can be just directories, in which case the default target + inference pattern will be used. Or entries can be tuples of a directory + and a target inference pattern. See the documentation for + ``target_inference`` for more information. """ - return self._config.get('compdb_paths', _DEFAULT_BUILD_DIR_NAME) - - @property - def compdb_paths_expanded(self) -> List[Path]: - return [Path(node) for node in glob.iglob(self.compdb_paths)] + return [ + _parse_compdb_search_path(search_path, self.target_inference) + for search_path in self._config.get( + 'compdb_search_paths', _DEFAULT_BUILD_DIR + ) + ] @property def targets(self) -> List[str]: @@ -205,6 +214,14 @@ def target_inference(self) -> str: ignored. For example, a glob indicating that the directory two levels down from the build directory root has the target name would be expressed with ``*/*/?``. + + Note that the build artifact path is relative to the compilation + database search path that found the file. For example, for a compilation + database search path of ``{project dir}/out``, for the purposes of + target inference, the build artifact path is relative to the ``{project + dir}/out`` directory. Target inference patterns can be defined for each + compilation database search path. See the documentation for + ``compdb_search_paths`` for more information. """ return self._config.get('target_inference', _DEFAULT_TARGET_INFERENCE) @@ -349,11 +366,8 @@ def _docstring_set_default( PigweedIdeSettings.working_dir, PW_IDE_DIR_NAME, literal=True ) _docstring_set_default( - PigweedIdeSettings.build_dir, _DEFAULT_BUILD_DIR_NAME, literal=True -) -_docstring_set_default( - PigweedIdeSettings.compdb_paths, - _DEFAULT_CONFIG['compdb_paths'], + PigweedIdeSettings.compdb_search_paths, + [_DEFAULT_BUILD_DIR_NAME], literal=True, ) _docstring_set_default(