Last active
December 17, 2015 05:29
-
-
Save jonbinney/5558335 to your computer and use it in GitHub Desktop.
Recursively checks a catkin workspace for missing _gencpp dependencies
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import sys, re, os, os.path | |
def find_files(root_dir, exclude_re, include_re): | |
files = [] | |
for dirpath, dirnames, filenames in os.walk(root_dir, topdown=True): | |
for filename in filenames: | |
if include_re.match(filename): | |
files.append((dirpath, filename)) | |
for dirname in dirnames: | |
# don't recurse into excluded directories | |
if exclude_re.match(dirname): | |
dirnames.remove(dirname) | |
return files | |
# look for all add_executable macros in a CMakeLists.txt | |
add_target_re = re.compile('(add_executable|add_library)\(\s*(\S+)([^\(]*)\)', re.IGNORECASE | re.MULTILINE) | |
# look for all add_dependencies macros in a CMakeLists.txt | |
add_dependencies_re = re.compile('add_dependencies\(\s*(\S+)([^\(]*)\)', re.IGNORECASE | re.MULTILINE) | |
# check whether a filename could be a c++ source file | |
cpp_source_file_re = re.compile('.+(\.cpp|\.h|\.hh|\.cc|\.c|\.hpp)$') | |
# look for includes that look like they are an autogenerated message | |
# header, and find the name of the package the message is in | |
# for now, relies on the fact that most message packages end in _msg | |
# is there a more reliable heuristic for this? | |
msg_include_re = re.compile('\#include\s*\<(\S+_msgs)\S+\>') | |
def get_cpp_message_packages_used(source_file_path): | |
''' | |
Looks through a C++ source or header, and tries to | |
guess what packages it uses messages from. | |
''' | |
f = open(source_file_path) | |
source_str = f.read() | |
f.close() | |
message_packages = set(msg_include_re.findall(source_str)) | |
return message_packages | |
def check_cmakelists(dirpath): | |
''' | |
For each target in the given CMakeLists.txt file, | |
Args: | |
dirpath (str) - absolute path to the directory the cmakelists is in | |
''' | |
f = open(os.path.join(dirpath, 'CMakeLists.txt')) | |
cmakelists_str = f.read() | |
f.close() | |
print os.path.join(dirpath, filename) | |
# find all dependencies explicitly declared for each target | |
target_message_deps = {} | |
matches = add_dependencies_re.findall(cmakelists_str) | |
for target_name, args_str in matches: | |
if target_name not in target_message_deps: | |
target_message_deps[target_name] = set() | |
for dep in args_str.split(): | |
if dep[-7:] == '_gencpp': | |
target_message_deps[target_name].add(dep[:-7]) | |
matches = add_target_re.findall(cmakelists_str) | |
for macro_name, target_name, args_str in matches: | |
args = args_str.split() | |
declared_message_deps = target_message_deps.get(target_name, set()) | |
if verbose: | |
print ' Target: %s' % target_name | |
if verbose: | |
print ' declared:', declared_message_deps | |
for arg in args: | |
if cpp_source_file_re.match(arg): | |
file_path = os.path.join(dirpath, arg) | |
# check what message packages this source file needs | |
try: | |
message_packages_used = get_cpp_message_packages_used(file_path) | |
except: | |
print ' Unable to process file %s' % arg | |
continue | |
if verbose: | |
print ' used in %s:' % (arg,), message_packages_used | |
for pkg in message_packages_used: | |
if pkg not in declared_message_deps: | |
print ' ' + '%s used for target %s but no %s_gencpp dependency declared' % ( | |
pkg, target_name, pkg) | |
if len(sys.argv) > 1: | |
root_dir = sys.argv[1] | |
else: | |
root_dir = os.getcwd() | |
verbose = False | |
print 'Recursively checking %s' % root_dir | |
# don't recurse into directories matching this | |
exclude_re = re.compile(r'.*\.git') | |
include_re = re.compile(r'CMakeLists.txt') | |
cmakelists_files = find_files(root_dir, exclude_re, include_re) | |
for dirpath, filename in cmakelists_files: | |
check_cmakelists(dirpath) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment