Belle II Software  release-06-01-15
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 # grab all of the light release packages
109 # (defined by the .light file for the sparse checkout)
110 light_file = basf2.find_file(".light")
111 with open(light_file) as fi:
112  light_packages = [li.strip().replace("/", "") for li in fi if li.strip().endswith("/")]
113 
114 # we also need all packages (to compare), for this use b2code-package-list.
115 # note that b2code-package-list has to be executed from the top basf2 directory,
116 # since it requires the presence of the .release file: we use cwd for this.
117 all_packages = subprocess.check_output(
118  ["b2code-package-list"], cwd=os.path.dirname(light_file),
119 )
120 all_packages = all_packages.decode("utf-8").strip().split(" ")
121 
122 # the forbidden packages
123 non_light_packages = set(all_packages).difference(set(light_packages))
124 
125 # sum up the return codes
126 return_code_sum = 0
127 
128 # run the check over all packages - just a dumb nested loop
129 start = time()
130 for package in light_packages:
131  sconscript_files = get_sconscripts(package)
132  return_code_sum += check_dependencies(
133  non_light_packages, sconscript_files, "This breaks light releases! Sorry."
134  )
135 
136 # test finished, now report
137 print("Test of light dependencies, the loop took:", time() - start, "seconds to run")
138 print("There were", return_code_sum, "forbidden dependencies")
139 
140 if return_code_sum != 0:
141  sys.exit(1) # fails the test