Belle II Software  release-08-01-10
test_light_dependencies.py
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
3 
4 
11 
12 import os
13 import glob
14 import re
15 import sys
16 import subprocess
17 import basf2
18 from time import time
19 
20 """
21 Check that no light-release-breaking dependencies have been added.
22 
23 If you are failing this test you have managed to break light builds, please
24 check with the light release manager for more information.
25 """
26 
27 
28 def get_sconscripts(package):
29  """glob to get the path to all sconscript files
30 
31  Parameters:
32  package (str): the name of the package directory
33 
34  Returns:
35  list(str): a list of all SConscript files
36  """
37  path_to_package = basf2.find_file(package)
38  return glob.glob(f"{path_to_package}/**/SConscript", recursive=True)
39 
40 
41 def get_dependencies(sconscript_filename):
42  """grab all of the LIBS in the sconscript file
43 
44  Parameters:
45  sconscript_filename (str): the path to the sconscript file
46 
47  Returns:
48  set(str): a set of packages in the env['LIBS'] in this file
49  """
50  # get the " env['LIBS'] " line in the file
51  # (if no LIBS are in the SConscript file then ignore)
52  with open(sconscript_filename) as fi:
53  file_data = fi.read()
54  dependencies = re.search(
55  r"env\['LIBS'\] *= *\[\n*((?:.*?|\n)*?)\n*\]", file_data
56  )
57  if dependencies:
58  dependencies = dependencies.group(1)
59  else:
60  dependencies = ""
61 
62  # clean up the list returned from the regex: only want the names of the
63  # basf2 libs (i.e. strings of the form: packagename_module)
64  dependencies = [
65  lb.replace("'", "").replace('"', "").strip()
66  for lb in re.split(",|\n", dependencies)
67  if not lb.count("#") and not lb == ''
68  ]
69  # now just trim only the packagename's from the library names
70  # and make a python set of them
71  package_dependencies = set([lb.split("_")[0] for lb in dependencies])
72 
73  return package_dependencies
74 
75 
76 def check_dependencies(forbidden, sconscript_files, error=""):
77  """Check that there are no dependencies that are forbidden.
78 
79  Parameters:
80  forbidden set(str): a set of packages we don't want to depend on
81  sconscript_files list(str): a list of paths to sconscript files
82  error str: optionally specify the error message
83 
84  Returns:
85  int: count of the number of forbidden dependencies found
86  """
87  n_forbidden = 0
88  for sconscript_filename in sconscript_files:
89 
90  # get the dependencies from this SConscript file
91  this_dependencies = get_dependencies(sconscript_filename)
92 
93  # using the nice and cool and mathsy python set syntax: figure out if
94  # we depend on anything in the forbidden packages list
95  forbidden_packages_dependencies = forbidden.intersection(this_dependencies)
96 
97  # if so then throw the error
98  if len(forbidden_packages_dependencies) != 0:
99  print(
100  f"The sconscript file {sconscript_filename} depends on",
101  forbidden_packages_dependencies,
102  error,
103  )
104  n_forbidden += 1
105  return n_forbidden
106 
107 
108 if __name__ == "__main__":
109 
110  # grab all of the light release packages
111  # (defined by the .light file for the sparse checkout)
112  light_file = basf2.find_file(".light")
113  with open(light_file) as fi:
114  light_packages = [li.strip().replace("/", "") for li in fi if li.strip().endswith("/")]
115 
116  # we also need all packages (to compare), for this use b2code-package-list.
117  # note that b2code-package-list has to be executed from the top basf2 directory,
118  # since it requires the presence of the .release file: we use cwd for this.
119  all_packages = subprocess.check_output(
120  ["b2code-package-list"], cwd=os.path.dirname(light_file),
121  )
122  all_packages = all_packages.decode("utf-8").strip().split(" ")
123 
124  # the forbidden packages
125  non_light_packages = set(all_packages).difference(set(light_packages))
126 
127  # sum up the return codes
128  return_code_sum = 0
129 
130  # run the check over all packages - just a dumb nested loop
131  start = time()
132  for package in light_packages:
133  sconscript_files = get_sconscripts(package)
134  return_code_sum += check_dependencies(
135  non_light_packages, sconscript_files, "This breaks light releases! Sorry."
136  )
137 
138  # test finished, now report
139  print("Test of light dependencies, the loop took:", time() - start, "seconds to run")
140  print("There were", return_code_sum, "forbidden dependencies")
141 
142  sys.exit(return_code_sum)