check_duplicates.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. #!/usr/bin/env python
  2. # Copyright (c) 2017, Open Source Robotics Foundation
  3. # All rights reserved.
  4. #
  5. # Redistribution and use in source and binary forms, with or without
  6. # modification, are permitted provided that the following conditions are met:
  7. #
  8. # * Redistributions of source code must retain the above copyright
  9. # notice, this list of conditions and the following disclaimer.
  10. # * Redistributions in binary form must reproduce the above copyright
  11. # notice, this list of conditions and the following disclaimer in the
  12. # documentation and/or other materials provided with the distribution.
  13. # * Neither the name of the Willow Garage, Inc. nor the names of its
  14. # contributors may be used to endorse or promote products derived from
  15. # this software without specific prior written permission.
  16. #
  17. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  18. # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  19. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  20. # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  21. # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  22. # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  23. # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  24. # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  25. # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  26. # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  27. # POSSIBILITY OF SUCH DAMAGE.
  28. import argparse
  29. import os
  30. import sys
  31. import yaml
  32. from rosdep2.sources_list import load_cached_sources_list, DataSourceMatcher, SourcesListLoader, CachedDataSource
  33. from rosdep2.lookup import RosdepLookup
  34. from rosdep2.rospkg_loader import DEFAULT_VIEW_KEY
  35. from rosdep2.sources_list import *
  36. def create_default_sources():
  37. sources = []
  38. # get all rosdistro files
  39. basedir = os.path.realpath(os.path.join(os.path.dirname(os.path.realpath(__file__)), '..'))
  40. filepath = os.path.join(basedir, 'index.yaml')
  41. with open(filepath) as f:
  42. content = f.read()
  43. index = yaml.safe_load(content)
  44. for distro in index['distributions']:
  45. distfile = 'file://' + basedir + '/' + distro + '/distribution.yaml'
  46. print('loading %s' % distfile)
  47. try:
  48. rds = RosDistroSource(distro)
  49. except KeyError:
  50. # When first adding a ROS distro to the repository, it won't yet
  51. # exist at the URL that RosDistroSource fetches from github. When
  52. # trying to index it, RosDistroSource will throw a KeyError. If we
  53. # see that KeyError, just ignore it and don't add the distro to the
  54. # list of sources.
  55. continue
  56. rosdep_data = get_gbprepo_as_rosdep_data(distro)
  57. sources.append(CachedDataSource('yaml', distfile, [distro], rosdep_data))
  58. for filename in os.listdir(os.path.join(basedir, 'rosdep')):
  59. if not filename.endswith('yaml'):
  60. continue
  61. filepath = os.path.join(basedir, 'rosdep', filename)
  62. with open(filepath) as f:
  63. content = f.read()
  64. rosdep_data = yaml.safe_load(content)
  65. tag = 'osx' if 'osx-' in filepath else ''
  66. sources.append(CachedDataSource('yaml', 'file://' + filepath, [tag], rosdep_data))
  67. return sources
  68. def check_duplicates(sources, os_name, os_codename):
  69. # output debug info
  70. print('checking sources')
  71. for source in sources:
  72. print('- %s' % source.url)
  73. # create lookup
  74. sources_loader = SourcesListLoader(sources)
  75. lookup = RosdepLookup.create_from_rospkg(sources_loader=sources_loader)
  76. # check if duplicates
  77. print("checking duplicates")
  78. db_name_view = {}
  79. has_duplicates = False
  80. # to avoid merge views
  81. view = lookup._load_view_dependencies(DEFAULT_VIEW_KEY, lookup.loader)
  82. for view_key in lookup.rosdep_db.get_view_dependencies(DEFAULT_VIEW_KEY):
  83. db_entry = lookup.rosdep_db.get_view_data(view_key)
  84. print('* %s' % view_key)
  85. for dep_name, dep_data in db_entry.rosdep_data.items():
  86. # skip unknown os names
  87. if os_name not in dep_data.keys():
  88. continue
  89. # skip unknown os codenames
  90. if (
  91. isinstance(dep_data[os_name], dict) and
  92. 'pip' not in dep_data[os_name].keys() and
  93. os_codename not in dep_data[os_name].keys()
  94. ):
  95. continue
  96. if dep_name in db_name_view:
  97. print('%s (%s, %s) is multiply defined in\n\t%s and \n\t%s\n' %
  98. (dep_name, os_name, os_codename, db_name_view[dep_name], view_key))
  99. has_duplicates = True
  100. db_name_view[dep_name] = view_key
  101. return not has_duplicates
  102. def main(infile):
  103. sources = create_default_sources()
  104. matcher = DataSourceMatcher.create_default()
  105. print('default sources')
  106. for source in sources:
  107. print('- %s' % source.url)
  108. # replace with infile
  109. for filename in infile:
  110. filepath = os.path.join(os.getcwd(), filename)
  111. with open(filepath) as f:
  112. content = f.read()
  113. rosdep_data = yaml.safe_load(content)
  114. # osx-homebrew uses osx tag
  115. tag = 'osx' if 'osx-' in filepath else ''
  116. model = CachedDataSource('yaml', 'file://' + filepath, [tag], rosdep_data)
  117. # add sources if not exists
  118. if not [x for x in sources if os.path.basename(filename) == os.path.basename(x.url)]:
  119. sources.append(model)
  120. else:
  121. # remove files with same filename
  122. sources = [model if os.path.basename(filename) == os.path.basename(x.url) else x for x in sources]
  123. ret = True
  124. for tag in [['indigo', 'ubuntu', 'trusty'],
  125. ['jade', 'ubuntu', 'trusty'],
  126. ['kinetic', 'ubuntu', 'xenial'],
  127. ['lunar', 'ubuntu', 'xenial'],
  128. ['', 'osx', 'homebrew']]:
  129. matcher.tags = tag
  130. print('checking with %s' % matcher.tags)
  131. os_name = tag[1]
  132. os_codename = tag[2]
  133. ret &= check_duplicates([x for x in sources if matcher.matches(x)],
  134. os_name, os_codename)
  135. return ret
  136. if __name__ == '__main__':
  137. parser = argparse.ArgumentParser(description='Checks whether rosdep files contain duplicate ROS rules')
  138. parser.add_argument('infiles', nargs='*', help='input rosdep YAML file')
  139. args = parser.parse_args()
  140. if not main(args.infiles):
  141. sys.exit(1)