diff --git a/HISTORY.rst b/HISTORY.rst index e2e7c71f..96fc3fea 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -18,6 +18,7 @@ PlatformIO Core 6 * Show "TimeoutError" only in the verbose mode when can not find a serial port * Fixed an issue when a serial port was not automatically detected if the board has predefined HWIDs +* Fixed an issue with endless scanning of project dependencies (`issue #4349 `_) 6.1.0 (2022-07-06) ~~~~~~~~~~~~~~~~~~ diff --git a/platformio/builder/tools/piolib.py b/platformio/builder/tools/piolib.py index 387d891e..ee2fc484 100644 --- a/platformio/builder/tools/piolib.py +++ b/platformio/builder/tools/piolib.py @@ -331,7 +331,7 @@ class LibBuilderBase: ) ] - def _get_found_includes( # pylint: disable=too-many-branches + def get_implicit_includes( # pylint: disable=too-many-branches self, search_files=None ): # all include directories @@ -352,15 +352,17 @@ class LibBuilderBase: include_dirs.extend(LibBuilderBase._INCLUDE_DIRS_CACHE) result = [] - for path in search_files or []: - if path in self._processed_files: + search_files = search_files or [] + while search_files: + node = self.env.File(search_files.pop(0)) + if node.get_abspath() in self._processed_search_files: continue - self._processed_files.append(path) + self._processed_search_files.append(node.get_abspath()) try: assert "+" in self.lib_ldf_mode candidates = LibBuilderBase.CCONDITIONAL_SCANNER( - self.env.File(path), + node, self.env, tuple(include_dirs), depth=self.CCONDITIONAL_SCANNER_DEPTH, @@ -370,39 +372,35 @@ class LibBuilderBase: if self.verbose and "+" in self.lib_ldf_mode: sys.stderr.write( "Warning! Classic Pre Processor is used for `%s`, " - "advanced has failed with `%s`\n" % (path, exc) + "advanced has failed with `%s`\n" % (node.get_abspath(), exc) ) - candidates = self.env.File(path).get_implicit_deps( - self.env, - LibBuilderBase.CLASSIC_SCANNER, - lambda _: tuple(include_dirs), + candidates = LibBuilderBase.CLASSIC_SCANNER( + node, self.env, tuple(include_dirs) ) - # mark candidates already processed - self._processed_files.extend( - [ - c.get_abspath() - for c in candidates - if c.get_abspath() not in self._processed_files - ] - ) - - # print(path, [c.get_abspath() for c in candidates]) + # print(node.get_abspath(), [c.get_abspath() for c in candidates]) for item in candidates: + item_path = item.get_abspath() + # process internal files recursively + if ( + item_path not in self._processed_search_files + and item_path not in search_files + and item_path in self + ): + search_files.append(item_path) if item not in result: result.append(item) if not self.PARSE_SRC_BY_H_NAME: continue - _h_path = item.get_abspath() - if not fs.path_endswith_ext(_h_path, piotool.SRC_HEADER_EXT): + if not fs.path_endswith_ext(item_path, piotool.SRC_HEADER_EXT): continue - _f_part = _h_path[: _h_path.rindex(".")] + item_fname = item_path[: item_path.rindex(".")] for ext in piotool.SRC_C_EXT + piotool.SRC_CXX_EXT: - if not os.path.isfile("%s.%s" % (_f_part, ext)): + if not os.path.isfile("%s.%s" % (item_fname, ext)): continue - _c_path = self.env.File("%s.%s" % (_f_part, ext)) - if _c_path not in result: - result.append(_c_path) + item_c_node = self.env.File("%s.%s" % (item_fname, ext)) + if item_c_node not in result: + result.append(item_c_node) return result @@ -417,7 +415,7 @@ class LibBuilderBase: search_files = self.get_search_files() lib_inc_map = {} - for inc in self._get_found_includes(search_files): + for inc in self.get_implicit_includes(search_files): inc_path = inc.get_abspath() for lb in self.env.GetLibBuilders(): if inc_path in lb: diff --git a/tests/commands/test_run.py b/tests/commands/test_run.py index 23d1e096..009f33c9 100644 --- a/tests/commands/test_run.py +++ b/tests/commands/test_run.py @@ -335,3 +335,73 @@ projenv.Append(CPPDEFINES=[ assert 'MACRO_2=' in result.output assert 'MACRO_3=' in result.output assert "MACRO_4=" in result.output + + +def test_ldf(clirunner, validate_cliresult, tmp_path: Path): + project_dir = tmp_path / "project" + + # libs + lib_dir = project_dir / "lib" + a_lib_dir = lib_dir / "a" + a_lib_dir.mkdir(parents=True) + (a_lib_dir / "a.h").write_text( + """ +#include +""" + ) + # b + b_lib_dir = lib_dir / "b" + b_lib_dir.mkdir(parents=True) + (b_lib_dir / "some_from_b.h").write_text("") + # c + c_lib_dir = lib_dir / "c" + c_lib_dir.mkdir(parents=True) + (c_lib_dir / "parse_c_by_name.h").write_text( + """ +void some_func(); + """ + ) + (c_lib_dir / "parse_c_by_name.c").write_text( + """ +#include +#include + +void some_func() { +} + """ + ) + (c_lib_dir / "some.c").write_text( + """ +#include + """ + ) + # d + d_lib_dir = lib_dir / "d" + d_lib_dir.mkdir(parents=True) + (d_lib_dir / "d.h").write_text("") + + # project + src_dir = project_dir / "src" + src_dir.mkdir(parents=True) + (src_dir / "main.h").write_text( + """ +#include +#include +""" + ) + (src_dir / "main.c").write_text( + """ +#include + +int main() { +} +""" + ) + (project_dir / "platformio.ini").write_text( + """ +[env:native] +platform = native + """ + ) + result = clirunner.invoke(cmd_run, ["--project-dir", str(project_dir)]) + validate_cliresult(result)