diff --git a/tools/build_api.py b/tools/build_api.py index c48b442da4..1e5048c26a 100644 --- a/tools/build_api.py +++ b/tools/build_api.py @@ -452,6 +452,8 @@ def build_project(src_paths, build_path, target, toolchain_name, # Link Program res, _ = toolchain.link_program(resources, build_path, name) + resources.detect_duplicates(toolchain) + if report != None: end = time() cur_result["elapsed_time"] = end - start diff --git a/tools/git_hooks/__init__.py b/tools/git_hooks/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/git_hooks/find_duplicates.py b/tools/git_hooks/find_duplicates.py new file mode 100755 index 0000000000..40531994e2 --- /dev/null +++ b/tools/git_hooks/find_duplicates.py @@ -0,0 +1,26 @@ +from os import walk +from os.path import join, abspath, dirname, basename, splitext +import sys + +ROOT = abspath(join(dirname(__file__), "..", "..")) +sys.path.insert(0, ROOT) + +from tools.toolchains.gcc import GCC_ARM +from tools.targets import TARGET_MAP +from argparse import ArgumentParser + +if __name__ == "__main__": + parser = ArgumentParser("Find duplicate file names within a directory structure") + parser.add_argument("dirs", help="Directories to search for duplicate file names" + , nargs="*") + parser.add_argument("--silent", help="Supress printing of filenames, just return number of duplicates", action="store_true") + args = parser.parse_args() + + toolchain = GCC_ARM(TARGET_MAP["K64F"]) + + resources = sum([toolchain.scan_resources(d) for d in args.dirs], None) + + scanned_files = {} + + exit(resources.detect_duplicates(toolchain)) + diff --git a/tools/test/toolchains/api.py b/tools/test/toolchains/api.py index ae9a5ea071..100d02d11d 100644 --- a/tools/test/toolchains/api.py +++ b/tools/test/toolchains/api.py @@ -3,6 +3,7 @@ import sys import os from string import printable from copy import deepcopy +from mock import MagicMock from hypothesis import given from hypothesis.strategies import text, lists, fixed_dictionaries @@ -10,7 +11,8 @@ ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..")) sys.path.insert(0, ROOT) -from tools.toolchains import TOOLCHAIN_CLASSES, LEGACY_TOOLCHAIN_NAMES +from tools.toolchains import TOOLCHAIN_CLASSES, LEGACY_TOOLCHAIN_NAMES,\ + Resources from tools.targets import TARGET_MAP def test_instantiation(): @@ -96,3 +98,27 @@ def test_toolchain_profile_asm(profile, source_file): "Toolchain %s did not propigate arg %s" % (toolchain.name, parameter) + for name, Class in TOOLCHAIN_CLASSES.items(): + CLS = Class(TARGET_MAP["K64F"]) + assert name == CLS.name or name == LEGACY_TOOLCHAIN_NAMES[CLS.name] + + +@given(lists(text(alphabet=ALPHABET, min_size=1), min_size=1)) +def test_detect_duplicates(filenames): + c_sources = [os.path.join(name, "dupe.c") for name in filenames] + s_sources = [os.path.join(name, "dupe.s") for name in filenames] + cpp_sources = [os.path.join(name, "dupe.cpp") for name in filenames] + with MagicMock() as notify: + toolchain = TOOLCHAIN_CLASSES["ARM"](TARGET_MAP["K64F"], notify=notify) + res = Resources() + res.c_sources = c_sources + res.s_sources = s_sources + res.cpp_sources = cpp_sources + assert res.detect_duplicates(toolchain) == 1,\ + "Not Enough duplicates found" + + _, (notification, _), _ = notify.mock_calls[1] + assert "dupe.o" in notification["message"] + assert "dupe.s" in notification["message"] + assert "dupe.c" in notification["message"] + assert "dupe.cpp" in notification["message"] diff --git a/tools/toolchains/__init__.py b/tools/toolchains/__init__.py index 6438a76221..98def03191 100644 --- a/tools/toolchains/__init__.py +++ b/tools/toolchains/__init__.py @@ -120,6 +120,43 @@ class Resources: return self + def _collect_duplicates(self, dupe_dict, dupe_headers): + for filename in self.s_sources + self.c_sources + self.cpp_sources: + objname, _ = splitext(basename(filename)) + dupe_dict.setdefault(objname, set()) + dupe_dict[objname] |= set([filename]) + for filename in self.headers: + headername = basename(filename) + dupe_headers.setdefault(headername, set()) + dupe_headers[headername] |= set([headername]) + for res in self.features.values(): + res._collect_duplicates(dupe_dict, dupe_headers) + return dupe_dict, dupe_headers + + def detect_duplicates(self, toolchain): + """Detect all potential ambiguities in filenames and report them with + a toolchain notification + + Positional Arguments: + toolchain - used for notifications + """ + count = 0 + dupe_dict, dupe_headers = self._collect_duplicates(dict(), dict()) + for objname, filenames in dupe_dict.iteritems(): + if len(filenames) > 1: + count+=1 + toolchain.tool_error( + "Object file %s.o is not unique! It could be made from: %s"\ + % (objname, " ".join(filenames))) + for headername, locations in dupe_headers.iteritems(): + if len(locations) > 1: + count+=1 + toolchain.tool_error( + "Header file %s is not unique! It could be: %s" %\ + (headername, " ".join(locations))) + return count + + def relative_to(self, base, dot=False): for field in ['inc_dirs', 'headers', 's_sources', 'c_sources', 'cpp_sources', 'lib_dirs', 'objects', 'libraries',