diff --git a/Makefile b/Makefile index d9a16815..5f8716e7 100644 --- a/Makefile +++ b/Makefile @@ -98,7 +98,7 @@ build-gtest-gmock: $(MAKE) -C testing/googletest find-filename-conflicts: - bin/find-filename-conflicts + bin/find-filename-conflicts.py src plugins/* .PHONY: format check-formatting cpplint cpplint-noisy shellcheck smoke-examples find-filename-conflicts prepare-virtual checkout-platform adjust-git-timestamps docker-bash docker-simulator-tests run-tests simulator-tests setup diff --git a/bin/find-filename-conflicts b/bin/find-filename-conflicts deleted file mode 100755 index 8d31ecc2..00000000 --- a/bin/find-filename-conflicts +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env bash -# find-filename-conflicts - Finds cpp files with conflicting filenames -# Copyright (C) 2020 Keyboard.io, Inc. -# -# This program is free software: you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free Software -# Foundation, version 3. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with -# this program. If not, see . - -## When building Kaleidoscope, the compiled object files are linked together -## into a static archive. This static archive has a very simple structure, and -## only stores filenames, not paths, not even relative ones. As such, we can't -## have files with the same name, because they will conflict, and one will -## override the other. -## -## To avoid this situation, this script will find all cpp source files (we don't -## need to care about header-only things, those do not result in an object -## file), and will comb through them to find conflicting filenames. -## -## If a conflict is found, it will print all files that share the name, and will -## exit with an error at the end. It does not exit at the first duplicate, but -## will find and print all of them. -## -## If no conflict is found, the script just prints its status message and exits -## with zero. - -set -e - -FILE_LIST="$(find src -name '*.cpp' | sed -e 's,\(\(.*\)/\([^/]*\)\),\3 \1,')" - -exit_code=0 - -echo -n "Looking for conflicting filenames... " - -for f in $(echo "${FILE_LIST}" | cut -f1 -d" "); do - count=$(echo "${FILE_LIST}" | grep -c "^${f}") - if [ "$count" -gt 1 ]; then - echo >&2 - echo " Conflict found for ${f}: " >&2 - echo "${FILE_LIST}" | grep "${f}" | cut -d" " -f2 | sed -e 's,^, ,' >&2 - exit_code=1 - fi -done - -if [ "${exit_code}" -eq 0 ]; then - echo "done." -fi - -exit ${exit_code} diff --git a/bin/find-filename-conflicts.py b/bin/find-filename-conflicts.py new file mode 100755 index 00000000..558b3d42 --- /dev/null +++ b/bin/find-filename-conflicts.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# ------------------------------------------------------------------------------ +# Copyright (c) 2022 Michael Richters + +# This is free and unencumbered software released into the public domain. + +# Anyone is free to copy, modify, publish, use, compile, sell, or +# distribute this software, either in source code form or as a compiled +# binary, for any purpose, commercial or non-commercial, and by any +# means. + +# In jurisdictions that recognize copyright laws, the author or authors +# of this software dedicate any and all copyright interest in the +# software to the public domain. We make this dedication for the benefit +# of the public at large and to the detriment of our heirs and +# successors. We intend this dedication to be an overt act of +# relinquishment in perpetuity of all present and future rights to this +# software under copyright law. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. + +# For more information, please refer to +# ------------------------------------------------------------------------------ +"""When building Kaleidoscope, the compiled object files are linked together +into a static archive. This static archive has a very simple structure, and only +stores filenames, not paths, not even relative ones. As such, we can't have +files with the same name, because they will conflict, and one will override the +other. + +To avoid this situation, this script will find all cpp source files (we don't +need to care about header-only things, those do not result in an object file), +and will comb through them to find conflicting filenames. + +If a conflict is found, it will print all files that share the name, and will +exit with an error at the end. It does not exit at the first duplicate, but will +find and print all of them. + +If no conflict is found, the script just prints its status message and exits +with zero.""" + +import os +import re +import sys + +cpp_regex = re.compile('.*\.cpp') + + +def find_duplicates(root): + """Search for files with the same basename, but in different directories in + the tree under . Prints a message for each conflict found, and + returns a count of the number of non-unique basenames.""" + + # Search the specified tree for matching basenames: + basenames = {} + for dir_path, dirs, files in os.walk(root): + for file_name in files: + if cpp_regex.match(file_name): + if file_name not in basenames: + basenames[file_name] = [] + basenames[file_name].append(dir_path) + + conflict_count = 0 + for file_name, dirs in basenames.items(): + # Prune unique basenames from the dict: + if len(dirs) <= 1: + continue + + conflict_count += 1 + # Print info about basenames with conflicts: + print(f"Conflict found for file name '{file_name}':") + for root in dirs: + path = os.path.join(root, file_name) + print(f' -> {path}') + + return conflict_count + + +def main(args): + print('Searching for conflicting filenames...') + exit_code = 0 + for path in args: + exit_code += find_duplicates(path) + if exit_code != 0: + sys.exit(exit_code) + print('No filename conflicts found.') + + +if __name__ == "__main__": + main(sys.argv[1:])