| Matthew Barth | ccb7f85 | 2016-11-23 17:43:02 -0600 | [diff] [blame] | 1 | #!/usr/bin/env python | 
 | 2 |  | 
 | 3 | """ | 
 | 4 | This script determines the given package's openbmc dependencies from its | 
 | 5 | configure.ac file where it downloads, configures, builds, and installs each of | 
 | 6 | these dependencies. Then the given package is configured, built, and installed | 
 | 7 | prior to executing its unit tests. | 
 | 8 | """ | 
 | 9 |  | 
| Matthew Barth | d181037 | 2016-12-19 16:57:21 -0600 | [diff] [blame] | 10 | from git import Repo | 
| Matthew Barth | ccb7f85 | 2016-11-23 17:43:02 -0600 | [diff] [blame] | 11 | from urlparse import urljoin | 
| Andrew Jeffery | a4e31c6 | 2018-03-08 13:45:28 +1030 | [diff] [blame] | 12 | from subprocess import check_call, call, CalledProcessError | 
| Matthew Barth | ccb7f85 | 2016-11-23 17:43:02 -0600 | [diff] [blame] | 13 | import os | 
 | 14 | import sys | 
| Matthew Barth | 33df879 | 2016-12-19 14:30:17 -0600 | [diff] [blame] | 15 | import argparse | 
| William A. Kennington III | a215673 | 2018-06-30 18:38:09 -0700 | [diff] [blame] | 16 | import multiprocessing | 
| Leonel Gonzalez | a62a1a1 | 2017-03-24 11:03:47 -0500 | [diff] [blame] | 17 | import re | 
| William A. Kennington III | 9a32d5e | 2018-12-06 17:38:53 -0800 | [diff] [blame] | 18 | import sets | 
| William A. Kennington III | e67f5fc | 2018-12-06 17:40:30 -0800 | [diff] [blame] | 19 | import subprocess | 
| William A. Kennington III | 3f1d120 | 2018-12-06 18:02:07 -0800 | [diff] [blame] | 20 | import shutil | 
| William A. Kennington III | 4e1d0a1 | 2018-07-16 12:04:03 -0700 | [diff] [blame] | 21 | import platform | 
| Leonel Gonzalez | a62a1a1 | 2017-03-24 11:03:47 -0500 | [diff] [blame] | 22 |  | 
 | 23 |  | 
 | 24 | class DepTree(): | 
 | 25 |     """ | 
 | 26 |     Represents package dependency tree, where each node is a DepTree with a | 
 | 27 |     name and DepTree children. | 
 | 28 |     """ | 
 | 29 |  | 
 | 30 |     def __init__(self, name): | 
 | 31 |         """ | 
 | 32 |         Create new DepTree. | 
 | 33 |  | 
 | 34 |         Parameter descriptions: | 
 | 35 |         name               Name of new tree node. | 
 | 36 |         """ | 
 | 37 |         self.name = name | 
 | 38 |         self.children = list() | 
 | 39 |  | 
 | 40 |     def AddChild(self, name): | 
 | 41 |         """ | 
 | 42 |         Add new child node to current node. | 
 | 43 |  | 
 | 44 |         Parameter descriptions: | 
 | 45 |         name               Name of new child | 
 | 46 |         """ | 
 | 47 |         new_child = DepTree(name) | 
 | 48 |         self.children.append(new_child) | 
 | 49 |         return new_child | 
 | 50 |  | 
 | 51 |     def AddChildNode(self, node): | 
 | 52 |         """ | 
 | 53 |         Add existing child node to current node. | 
 | 54 |  | 
 | 55 |         Parameter descriptions: | 
 | 56 |         node               Tree node to add | 
 | 57 |         """ | 
 | 58 |         self.children.append(node) | 
 | 59 |  | 
 | 60 |     def RemoveChild(self, name): | 
 | 61 |         """ | 
 | 62 |         Remove child node. | 
 | 63 |  | 
 | 64 |         Parameter descriptions: | 
 | 65 |         name               Name of child to remove | 
 | 66 |         """ | 
 | 67 |         for child in self.children: | 
 | 68 |             if child.name == name: | 
 | 69 |                 self.children.remove(child) | 
 | 70 |                 return | 
 | 71 |  | 
 | 72 |     def GetNode(self, name): | 
 | 73 |         """ | 
 | 74 |         Return node with matching name. Return None if not found. | 
 | 75 |  | 
 | 76 |         Parameter descriptions: | 
 | 77 |         name               Name of node to return | 
 | 78 |         """ | 
 | 79 |         if self.name == name: | 
 | 80 |             return self | 
 | 81 |         for child in self.children: | 
 | 82 |             node = child.GetNode(name) | 
 | 83 |             if node: | 
 | 84 |                 return node | 
 | 85 |         return None | 
 | 86 |  | 
 | 87 |     def GetParentNode(self, name, parent_node=None): | 
 | 88 |         """ | 
 | 89 |         Return parent of node with matching name. Return none if not found. | 
 | 90 |  | 
 | 91 |         Parameter descriptions: | 
 | 92 |         name               Name of node to get parent of | 
 | 93 |         parent_node        Parent of current node | 
 | 94 |         """ | 
 | 95 |         if self.name == name: | 
 | 96 |             return parent_node | 
 | 97 |         for child in self.children: | 
 | 98 |             found_node = child.GetParentNode(name, self) | 
 | 99 |             if found_node: | 
 | 100 |                 return found_node | 
 | 101 |         return None | 
 | 102 |  | 
 | 103 |     def GetPath(self, name, path=None): | 
 | 104 |         """ | 
 | 105 |         Return list of node names from head to matching name. | 
 | 106 |         Return None if not found. | 
 | 107 |  | 
 | 108 |         Parameter descriptions: | 
 | 109 |         name               Name of node | 
 | 110 |         path               List of node names from head to current node | 
 | 111 |         """ | 
 | 112 |         if not path: | 
 | 113 |             path = [] | 
 | 114 |         if self.name == name: | 
 | 115 |             path.append(self.name) | 
 | 116 |             return path | 
 | 117 |         for child in self.children: | 
 | 118 |             match = child.GetPath(name, path + [self.name]) | 
 | 119 |             if match: | 
 | 120 |                 return match | 
 | 121 |         return None | 
 | 122 |  | 
 | 123 |     def GetPathRegex(self, name, regex_str, path=None): | 
 | 124 |         """ | 
 | 125 |         Return list of node paths that end in name, or match regex_str. | 
 | 126 |         Return empty list if not found. | 
 | 127 |  | 
 | 128 |         Parameter descriptions: | 
 | 129 |         name               Name of node to search for | 
 | 130 |         regex_str          Regex string to match node names | 
 | 131 |         path               Path of node names from head to current node | 
 | 132 |         """ | 
 | 133 |         new_paths = [] | 
 | 134 |         if not path: | 
 | 135 |             path = [] | 
 | 136 |         match = re.match(regex_str, self.name) | 
 | 137 |         if (self.name == name) or (match): | 
 | 138 |             new_paths.append(path + [self.name]) | 
 | 139 |         for child in self.children: | 
 | 140 |             return_paths = None | 
 | 141 |             full_path = path + [self.name] | 
 | 142 |             return_paths = child.GetPathRegex(name, regex_str, full_path) | 
 | 143 |             for i in return_paths: | 
 | 144 |                 new_paths.append(i) | 
 | 145 |         return new_paths | 
 | 146 |  | 
 | 147 |     def MoveNode(self, from_name, to_name): | 
 | 148 |         """ | 
 | 149 |         Mode existing from_name node to become child of to_name node. | 
 | 150 |  | 
 | 151 |         Parameter descriptions: | 
 | 152 |         from_name          Name of node to make a child of to_name | 
 | 153 |         to_name            Name of node to make parent of from_name | 
 | 154 |         """ | 
 | 155 |         parent_from_node = self.GetParentNode(from_name) | 
 | 156 |         from_node = self.GetNode(from_name) | 
 | 157 |         parent_from_node.RemoveChild(from_name) | 
 | 158 |         to_node = self.GetNode(to_name) | 
 | 159 |         to_node.AddChildNode(from_node) | 
 | 160 |  | 
 | 161 |     def ReorderDeps(self, name, regex_str): | 
 | 162 |         """ | 
 | 163 |         Reorder dependency tree.  If tree contains nodes with names that | 
 | 164 |         match 'name' and 'regex_str', move 'regex_str' nodes that are | 
 | 165 |         to the right of 'name' node, so that they become children of the | 
 | 166 |         'name' node. | 
 | 167 |  | 
 | 168 |         Parameter descriptions: | 
 | 169 |         name               Name of node to look for | 
 | 170 |         regex_str          Regex string to match names to | 
 | 171 |         """ | 
 | 172 |         name_path = self.GetPath(name) | 
 | 173 |         if not name_path: | 
 | 174 |             return | 
 | 175 |         paths = self.GetPathRegex(name, regex_str) | 
 | 176 |         is_name_in_paths = False | 
 | 177 |         name_index = 0 | 
 | 178 |         for i in range(len(paths)): | 
 | 179 |             path = paths[i] | 
 | 180 |             if path[-1] == name: | 
 | 181 |                 is_name_in_paths = True | 
 | 182 |                 name_index = i | 
 | 183 |                 break | 
 | 184 |         if not is_name_in_paths: | 
 | 185 |             return | 
 | 186 |         for i in range(name_index + 1, len(paths)): | 
 | 187 |             path = paths[i] | 
 | 188 |             if name in path: | 
 | 189 |                 continue | 
 | 190 |             from_name = path[-1] | 
 | 191 |             self.MoveNode(from_name, name) | 
 | 192 |  | 
 | 193 |     def GetInstallList(self): | 
 | 194 |         """ | 
 | 195 |         Return post-order list of node names. | 
 | 196 |  | 
 | 197 |         Parameter descriptions: | 
 | 198 |         """ | 
 | 199 |         install_list = [] | 
 | 200 |         for child in self.children: | 
 | 201 |             child_install_list = child.GetInstallList() | 
 | 202 |             install_list.extend(child_install_list) | 
 | 203 |         install_list.append(self.name) | 
 | 204 |         return install_list | 
 | 205 |  | 
 | 206 |     def PrintTree(self, level=0): | 
 | 207 |         """ | 
 | 208 |         Print pre-order node names with indentation denoting node depth level. | 
 | 209 |  | 
 | 210 |         Parameter descriptions: | 
 | 211 |         level              Current depth level | 
 | 212 |         """ | 
 | 213 |         INDENT_PER_LEVEL = 4 | 
 | 214 |         print ' ' * (level * INDENT_PER_LEVEL) + self.name | 
 | 215 |         for child in self.children: | 
 | 216 |             child.PrintTree(level + 1) | 
| Matthew Barth | 33df879 | 2016-12-19 14:30:17 -0600 | [diff] [blame] | 217 |  | 
 | 218 |  | 
 | 219 | def check_call_cmd(dir, *cmd): | 
 | 220 |     """ | 
 | 221 |     Verbose prints the directory location the given command is called from and | 
 | 222 |     the command, then executes the command using check_call. | 
 | 223 |  | 
 | 224 |     Parameter descriptions: | 
 | 225 |     dir                 Directory location command is to be called from | 
 | 226 |     cmd                 List of parameters constructing the complete command | 
 | 227 |     """ | 
 | 228 |     printline(dir, ">", " ".join(cmd)) | 
 | 229 |     check_call(cmd) | 
| Matthew Barth | ccb7f85 | 2016-11-23 17:43:02 -0600 | [diff] [blame] | 230 |  | 
 | 231 |  | 
| Andrew Geissler | a61acb5 | 2019-01-03 16:32:44 -0600 | [diff] [blame] | 232 | def clone_pkg(pkg, branch): | 
| Matthew Barth | 33df879 | 2016-12-19 14:30:17 -0600 | [diff] [blame] | 233 |     """ | 
 | 234 |     Clone the given openbmc package's git repository from gerrit into | 
 | 235 |     the WORKSPACE location | 
 | 236 |  | 
 | 237 |     Parameter descriptions: | 
 | 238 |     pkg                 Name of the package to clone | 
| Andrew Geissler | a61acb5 | 2019-01-03 16:32:44 -0600 | [diff] [blame] | 239 |     branch              Branch to clone from pkg | 
| Matthew Barth | 33df879 | 2016-12-19 14:30:17 -0600 | [diff] [blame] | 240 |     """ | 
| Andrew Jeffery | 7be94ca | 2018-03-08 13:15:33 +1030 | [diff] [blame] | 241 |     pkg_dir = os.path.join(WORKSPACE, pkg) | 
 | 242 |     if os.path.exists(os.path.join(pkg_dir, '.git')): | 
 | 243 |         return pkg_dir | 
| Matthew Barth | ccb7f85 | 2016-11-23 17:43:02 -0600 | [diff] [blame] | 244 |     pkg_repo = urljoin('https://gerrit.openbmc-project.xyz/openbmc/', pkg) | 
| Andrew Jeffery | 7be94ca | 2018-03-08 13:15:33 +1030 | [diff] [blame] | 245 |     os.mkdir(pkg_dir) | 
| Andrew Geissler | a61acb5 | 2019-01-03 16:32:44 -0600 | [diff] [blame] | 246 |     printline(pkg_dir, "> git clone", pkg_repo, branch, "./") | 
 | 247 |     try: | 
 | 248 |         # first try the branch | 
 | 249 |         repo_inst = Repo.clone_from(pkg_repo, pkg_dir, | 
 | 250 |                 branch=branch).working_dir | 
 | 251 |     except: | 
 | 252 |         printline("Input branch not found, default to master") | 
 | 253 |         repo_inst = Repo.clone_from(pkg_repo, pkg_dir, | 
 | 254 |                 branch="master").working_dir | 
 | 255 |     return repo_inst | 
| Matthew Barth | 33df879 | 2016-12-19 14:30:17 -0600 | [diff] [blame] | 256 |  | 
 | 257 |  | 
| William A. Kennington III | c048cc0 | 2018-12-06 15:39:18 -0800 | [diff] [blame] | 258 | def get_autoconf_deps(pkgdir): | 
 | 259 |     """ | 
 | 260 |     Parse the given 'configure.ac' file for package dependencies and return | 
 | 261 |     a list of the dependencies found. If the package is not autoconf it is just | 
 | 262 |     ignored. | 
 | 263 |  | 
 | 264 |     Parameter descriptions: | 
 | 265 |     pkgdir              Directory where package source is located | 
 | 266 |     """ | 
 | 267 |     configure_ac = os.path.join(pkgdir, 'configure.ac') | 
 | 268 |     if not os.path.exists(configure_ac): | 
 | 269 |         return [] | 
 | 270 |  | 
| William A. Kennington III | e67f5fc | 2018-12-06 17:40:30 -0800 | [diff] [blame] | 271 |     configure_ac_contents = '' | 
 | 272 |     # Prepend some special function overrides so we can parse out dependencies | 
 | 273 |     for macro in DEPENDENCIES.iterkeys(): | 
 | 274 |         configure_ac_contents += ('m4_define([' + macro + '], [' + | 
 | 275 |                 macro + '_START$' + str(DEPENDENCIES_OFFSET[macro] + 1) + | 
 | 276 |                 macro + '_END])\n') | 
| William A. Kennington III | c048cc0 | 2018-12-06 15:39:18 -0800 | [diff] [blame] | 277 |     with open(configure_ac, "rt") as f: | 
| William A. Kennington III | e67f5fc | 2018-12-06 17:40:30 -0800 | [diff] [blame] | 278 |         configure_ac_contents += f.read() | 
 | 279 |  | 
 | 280 |     autoconf_process = subprocess.Popen(['autoconf', '-Wno-undefined', '-'], | 
 | 281 |             stdin=subprocess.PIPE, stdout=subprocess.PIPE, | 
 | 282 |             stderr=subprocess.PIPE) | 
 | 283 |     (stdout, stderr) = autoconf_process.communicate(input=configure_ac_contents) | 
 | 284 |     if not stdout: | 
 | 285 |         print(stderr) | 
 | 286 |         raise Exception("Failed to run autoconf for parsing dependencies") | 
 | 287 |  | 
 | 288 |     # Parse out all of the dependency text | 
 | 289 |     matches = [] | 
 | 290 |     for macro in DEPENDENCIES.iterkeys(): | 
 | 291 |         pattern = '(' + macro + ')_START(.*?)' + macro + '_END' | 
 | 292 |         for match in re.compile(pattern).finditer(stdout): | 
 | 293 |             matches.append((match.group(1), match.group(2))) | 
 | 294 |  | 
 | 295 |     # Look up dependencies from the text | 
 | 296 |     found_deps = [] | 
 | 297 |     for macro, deptext in matches: | 
 | 298 |         for potential_dep in deptext.split(' '): | 
 | 299 |             for known_dep in DEPENDENCIES[macro].iterkeys(): | 
 | 300 |                 if potential_dep.startswith(known_dep): | 
 | 301 |                     found_deps.append(DEPENDENCIES[macro][known_dep]) | 
 | 302 |  | 
 | 303 |     return found_deps | 
| Matthew Barth | ccb7f85 | 2016-11-23 17:43:02 -0600 | [diff] [blame] | 304 |  | 
| William A. Kennington III | 3f1d120 | 2018-12-06 18:02:07 -0800 | [diff] [blame] | 305 | def get_meson_deps(pkgdir): | 
 | 306 |     """ | 
 | 307 |     Parse the given 'meson.build' file for package dependencies and return | 
 | 308 |     a list of the dependencies found. If the package is not meson compatible | 
 | 309 |     it is just ignored. | 
 | 310 |  | 
 | 311 |     Parameter descriptions: | 
 | 312 |     pkgdir              Directory where package source is located | 
 | 313 |     """ | 
 | 314 |     meson_build = os.path.join(pkgdir, 'meson.build') | 
 | 315 |     if not os.path.exists(meson_build): | 
 | 316 |         return [] | 
 | 317 |  | 
 | 318 |     found_deps = [] | 
 | 319 |     for root, dirs, files in os.walk(pkgdir): | 
 | 320 |         if 'meson.build' not in files: | 
 | 321 |             continue | 
 | 322 |         with open(os.path.join(root, 'meson.build'), 'rt') as f: | 
 | 323 |             build_contents = f.read() | 
 | 324 |         for match in re.finditer(r"dependency\('([^']*)'.*?\)\n", build_contents): | 
 | 325 |             maybe_dep = DEPENDENCIES['PKG_CHECK_MODULES'].get(match.group(1)) | 
 | 326 |             if maybe_dep is not None: | 
 | 327 |                 found_deps.append(maybe_dep) | 
 | 328 |  | 
 | 329 |     return found_deps | 
 | 330 |  | 
| William A. Kennington III | a215673 | 2018-06-30 18:38:09 -0700 | [diff] [blame] | 331 | make_parallel = [ | 
 | 332 |     'make', | 
 | 333 |     # Run enough jobs to saturate all the cpus | 
 | 334 |     '-j', str(multiprocessing.cpu_count()), | 
 | 335 |     # Don't start more jobs if the load avg is too high | 
 | 336 |     '-l', str(multiprocessing.cpu_count()), | 
 | 337 |     # Synchronize the output so logs aren't intermixed in stdout / stderr | 
 | 338 |     '-O', | 
 | 339 | ] | 
 | 340 |  | 
| William A. Kennington III | a045491 | 2018-12-06 14:47:16 -0800 | [diff] [blame] | 341 | def enFlag(flag, enabled): | 
 | 342 |     """ | 
 | 343 |     Returns an configure flag as a string | 
 | 344 |  | 
 | 345 |     Parameters: | 
 | 346 |     flag                The name of the flag | 
 | 347 |     enabled             Whether the flag is enabled or disabled | 
 | 348 |     """ | 
 | 349 |     return '--' + ('enable' if enabled else 'disable') + '-' + flag | 
 | 350 |  | 
| William A. Kennington III | 3f1d120 | 2018-12-06 18:02:07 -0800 | [diff] [blame] | 351 | def mesonFeature(val): | 
 | 352 |     """ | 
 | 353 |     Returns the meson flag which signifies the value | 
 | 354 |  | 
 | 355 |     True is enabled which requires the feature. | 
 | 356 |     False is disabled which disables the feature. | 
 | 357 |     None is auto which autodetects the feature. | 
 | 358 |  | 
 | 359 |     Parameters: | 
 | 360 |     val                 The value being converted | 
 | 361 |     """ | 
 | 362 |     if val is True: | 
 | 363 |         return "enabled" | 
 | 364 |     elif val is False: | 
 | 365 |         return "disabled" | 
 | 366 |     elif val is None: | 
 | 367 |         return "auto" | 
 | 368 |     else: | 
 | 369 |         raise Exception("Bad meson feature value") | 
 | 370 |  | 
| William A. Kennington III | 6764d5f | 2018-12-13 12:27:03 -0800 | [diff] [blame] | 371 | def parse_meson_options(options_file): | 
 | 372 |     """ | 
 | 373 |     Returns a set of options defined in the provides meson_options.txt file | 
 | 374 |  | 
 | 375 |     Parameters: | 
 | 376 |     options_file        The file containing options | 
 | 377 |     """ | 
 | 378 |     options_contents = '' | 
 | 379 |     with open(options_file, "rt") as f: | 
 | 380 |         options_contents += f.read() | 
 | 381 |     options = sets.Set() | 
 | 382 |     pattern = 'option\\(\\s*\'([^\']*)\'' | 
 | 383 |     for match in re.compile(pattern).finditer(options_contents): | 
 | 384 |         options.add(match.group(1)) | 
 | 385 |     return options | 
| William A. Kennington III | 3f1d120 | 2018-12-06 18:02:07 -0800 | [diff] [blame] | 386 |  | 
| William A. Kennington III | a045491 | 2018-12-06 14:47:16 -0800 | [diff] [blame] | 387 | def build_and_install(pkg, build_for_testing=False): | 
| William A. Kennington III | 780ec09 | 2018-12-06 14:46:50 -0800 | [diff] [blame] | 388 |     """ | 
 | 389 |     Builds and installs the package in the environment. Optionally | 
 | 390 |     builds the examples and test cases for package. | 
 | 391 |  | 
 | 392 |     Parameter description: | 
 | 393 |     pkg                 The package we are building | 
| William A. Kennington III | a045491 | 2018-12-06 14:47:16 -0800 | [diff] [blame] | 394 |     build_for_testing   Enable options related to testing on the package? | 
| William A. Kennington III | 780ec09 | 2018-12-06 14:46:50 -0800 | [diff] [blame] | 395 |     """ | 
 | 396 |     pkgdir = os.path.join(WORKSPACE, pkg) | 
| William A. Kennington III | 54d4faf | 2018-12-06 17:46:24 -0800 | [diff] [blame] | 397 |     os.chdir(pkgdir) | 
 | 398 |  | 
 | 399 |     # Refresh dynamic linker run time bindings for dependencies | 
 | 400 |     check_call_cmd(pkgdir, 'sudo', '-n', '--', 'ldconfig') | 
 | 401 |  | 
| William A. Kennington III | 780ec09 | 2018-12-06 14:46:50 -0800 | [diff] [blame] | 402 |     # Build & install this package | 
| William A. Kennington III | 3f1d120 | 2018-12-06 18:02:07 -0800 | [diff] [blame] | 403 |     # Always try using meson first | 
 | 404 |     if os.path.exists('meson.build'): | 
| William A. Kennington III | 6764d5f | 2018-12-13 12:27:03 -0800 | [diff] [blame] | 405 |         meson_options = parse_meson_options("meson_options.txt") | 
| William A. Kennington III | 3f1d120 | 2018-12-06 18:02:07 -0800 | [diff] [blame] | 406 |         meson_flags = [ | 
 | 407 |             '-Db_colorout=never', | 
| William A. Kennington III | 3f1d120 | 2018-12-06 18:02:07 -0800 | [diff] [blame] | 408 |         ] | 
| William A. Kennington III | 25e5814 | 2018-12-13 14:30:43 -0800 | [diff] [blame] | 409 |         if build_for_testing: | 
 | 410 |             meson_flags.append('--buildtype=debug') | 
 | 411 |         else: | 
 | 412 |             meson_flags.append('--buildtype=debugoptimized') | 
| William A. Kennington III | 6764d5f | 2018-12-13 12:27:03 -0800 | [diff] [blame] | 413 |         if 'tests' in meson_options: | 
 | 414 |             meson_flags.append('-Dtests=' + mesonFeature(build_for_testing)) | 
 | 415 |         if 'examples' in meson_options: | 
 | 416 |             meson_flags.append('-Dexamples=' + str(build_for_testing).lower()) | 
| William A. Kennington III | 3f1d120 | 2018-12-06 18:02:07 -0800 | [diff] [blame] | 417 |         if MESON_FLAGS.get(pkg) is not None: | 
 | 418 |             meson_flags.extend(MESON_FLAGS.get(pkg)) | 
 | 419 |         try: | 
 | 420 |             check_call_cmd(pkgdir, 'meson', 'setup', '--reconfigure', 'build', *meson_flags) | 
 | 421 |         except: | 
 | 422 |             shutil.rmtree('build') | 
 | 423 |             check_call_cmd(pkgdir, 'meson', 'setup', 'build', *meson_flags) | 
 | 424 |         check_call_cmd(pkgdir, 'ninja', '-C', 'build') | 
 | 425 |         check_call_cmd(pkgdir, 'sudo', '-n', '--', 'ninja', '-C', 'build', 'install') | 
 | 426 |     # Assume we are autoconf otherwise | 
 | 427 |     else: | 
 | 428 |         conf_flags = [ | 
 | 429 |             enFlag('silent-rules', False), | 
 | 430 |             enFlag('examples', build_for_testing), | 
 | 431 |             enFlag('tests', build_for_testing), | 
| William A. Kennington III | 3f1d120 | 2018-12-06 18:02:07 -0800 | [diff] [blame] | 432 |         ] | 
| William A. Kennington III | 65b37fa | 2019-01-31 15:15:17 -0800 | [diff] [blame^] | 433 |         if not TEST_ONLY: | 
 | 434 |             conf_flags.extend([ | 
 | 435 |                 enFlag('code-coverage', build_for_testing), | 
 | 436 |                 enFlag('valgrind', build_for_testing), | 
 | 437 |             ]) | 
| William A. Kennington III | 3f1d120 | 2018-12-06 18:02:07 -0800 | [diff] [blame] | 438 |         # Add any necessary configure flags for package | 
 | 439 |         if CONFIGURE_FLAGS.get(pkg) is not None: | 
 | 440 |             conf_flags.extend(CONFIGURE_FLAGS.get(pkg)) | 
 | 441 |         for bootstrap in ['bootstrap.sh', 'bootstrap', 'autogen.sh']: | 
 | 442 |             if os.path.exists(bootstrap): | 
 | 443 |                 check_call_cmd(pkgdir, './' + bootstrap) | 
 | 444 |                 break | 
 | 445 |         check_call_cmd(pkgdir, './configure', *conf_flags) | 
 | 446 |         check_call_cmd(pkgdir, *make_parallel) | 
 | 447 |         check_call_cmd(pkgdir, 'sudo', '-n', '--', *(make_parallel + [ 'install' ])) | 
| William A. Kennington III | 780ec09 | 2018-12-06 14:46:50 -0800 | [diff] [blame] | 448 |  | 
| Andrew Geissler | a61acb5 | 2019-01-03 16:32:44 -0600 | [diff] [blame] | 449 | def build_dep_tree(pkg, pkgdir, dep_added, head, branch, dep_tree=None): | 
| Leonel Gonzalez | a62a1a1 | 2017-03-24 11:03:47 -0500 | [diff] [blame] | 450 |     """ | 
 | 451 |     For each package(pkg), starting with the package to be unit tested, | 
 | 452 |     parse its 'configure.ac' file from within the package's directory(pkgdir) | 
 | 453 |     for each package dependency defined recursively doing the same thing | 
 | 454 |     on each package found as a dependency. | 
 | 455 |  | 
 | 456 |     Parameter descriptions: | 
 | 457 |     pkg                 Name of the package | 
 | 458 |     pkgdir              Directory where package source is located | 
| William A. Kennington III | c048cc0 | 2018-12-06 15:39:18 -0800 | [diff] [blame] | 459 |     dep_added           Current dict of dependencies and added status | 
| Leonel Gonzalez | a62a1a1 | 2017-03-24 11:03:47 -0500 | [diff] [blame] | 460 |     head                Head node of the dependency tree | 
| Andrew Geissler | a61acb5 | 2019-01-03 16:32:44 -0600 | [diff] [blame] | 461 |     branch              Branch to clone from pkg | 
| Leonel Gonzalez | a62a1a1 | 2017-03-24 11:03:47 -0500 | [diff] [blame] | 462 |     dep_tree            Current dependency tree node | 
 | 463 |     """ | 
 | 464 |     if not dep_tree: | 
 | 465 |         dep_tree = head | 
| William A. Kennington III | c048cc0 | 2018-12-06 15:39:18 -0800 | [diff] [blame] | 466 |  | 
| William A. Kennington III | be6aab2 | 2018-12-06 15:01:54 -0800 | [diff] [blame] | 467 |     with open("/tmp/depcache", "r") as depcache: | 
| William A. Kennington III | c048cc0 | 2018-12-06 15:39:18 -0800 | [diff] [blame] | 468 |         cache = depcache.readline() | 
 | 469 |  | 
 | 470 |     # Read out pkg dependencies | 
 | 471 |     pkg_deps = [] | 
 | 472 |     pkg_deps += get_autoconf_deps(pkgdir) | 
| William A. Kennington III | 3f1d120 | 2018-12-06 18:02:07 -0800 | [diff] [blame] | 473 |     pkg_deps += get_meson_deps(pkgdir) | 
| William A. Kennington III | c048cc0 | 2018-12-06 15:39:18 -0800 | [diff] [blame] | 474 |  | 
| William A. Kennington III | 9a32d5e | 2018-12-06 17:38:53 -0800 | [diff] [blame] | 475 |     for dep in sets.Set(pkg_deps): | 
| William A. Kennington III | c048cc0 | 2018-12-06 15:39:18 -0800 | [diff] [blame] | 476 |         if dep in cache: | 
 | 477 |             continue | 
 | 478 |         # Dependency package not already known | 
 | 479 |         if dep_added.get(dep) is None: | 
 | 480 |             # Dependency package not added | 
 | 481 |             new_child = dep_tree.AddChild(dep) | 
 | 482 |             dep_added[dep] = False | 
| Andrew Geissler | a61acb5 | 2019-01-03 16:32:44 -0600 | [diff] [blame] | 483 |             dep_pkgdir = clone_pkg(dep,branch) | 
| William A. Kennington III | c048cc0 | 2018-12-06 15:39:18 -0800 | [diff] [blame] | 484 |             # Determine this dependency package's | 
 | 485 |             # dependencies and add them before | 
 | 486 |             # returning to add this package | 
 | 487 |             dep_added = build_dep_tree(dep, | 
 | 488 |                                        dep_pkgdir, | 
 | 489 |                                        dep_added, | 
 | 490 |                                        head, | 
| Andrew Geissler | a61acb5 | 2019-01-03 16:32:44 -0600 | [diff] [blame] | 491 |                                        branch, | 
| William A. Kennington III | c048cc0 | 2018-12-06 15:39:18 -0800 | [diff] [blame] | 492 |                                        new_child) | 
 | 493 |         else: | 
 | 494 |             # Dependency package known and added | 
 | 495 |             if dep_added[dep]: | 
| Andrew Jeffery | 2cb0c7a | 2018-03-08 13:19:08 +1030 | [diff] [blame] | 496 |                 continue | 
| Leonel Gonzalez | a62a1a1 | 2017-03-24 11:03:47 -0500 | [diff] [blame] | 497 |             else: | 
| William A. Kennington III | c048cc0 | 2018-12-06 15:39:18 -0800 | [diff] [blame] | 498 |                 # Cyclic dependency failure | 
 | 499 |                 raise Exception("Cyclic dependencies found in "+pkg) | 
| Leonel Gonzalez | a62a1a1 | 2017-03-24 11:03:47 -0500 | [diff] [blame] | 500 |  | 
 | 501 |     if not dep_added[pkg]: | 
 | 502 |         dep_added[pkg] = True | 
 | 503 |  | 
 | 504 |     return dep_added | 
| Matthew Barth | ccb7f85 | 2016-11-23 17:43:02 -0600 | [diff] [blame] | 505 |  | 
| William A. Kennington III | 0f0a680 | 2018-07-16 11:52:33 -0700 | [diff] [blame] | 506 | def make_target_exists(target): | 
 | 507 |     """ | 
 | 508 |     Runs a check against the makefile in the current directory to determine | 
 | 509 |     if the target exists so that it can be built. | 
 | 510 |  | 
 | 511 |     Parameter descriptions: | 
 | 512 |     target              The make target we are checking | 
 | 513 |     """ | 
 | 514 |     try: | 
 | 515 |         cmd = [ 'make', '-n', target ] | 
 | 516 |         with open(os.devnull, 'w') as devnull: | 
 | 517 |             check_call(cmd, stdout=devnull, stderr=devnull) | 
 | 518 |         return True | 
 | 519 |     except CalledProcessError: | 
 | 520 |         return False | 
 | 521 |  | 
 | 522 | def run_unit_tests(top_dir): | 
 | 523 |     """ | 
 | 524 |     Runs the unit tests for the package via `make check` | 
 | 525 |  | 
 | 526 |     Parameter descriptions: | 
 | 527 |     top_dir             The root directory of our project | 
 | 528 |     """ | 
 | 529 |     try: | 
 | 530 |         cmd = make_parallel + [ 'check' ] | 
 | 531 |         for i in range(0, args.repeat): | 
 | 532 |             check_call_cmd(top_dir,  *cmd) | 
 | 533 |     except CalledProcessError: | 
 | 534 |         for root, _, files in os.walk(top_dir): | 
 | 535 |             if 'test-suite.log' not in files: | 
 | 536 |                 continue | 
 | 537 |             check_call_cmd(root, 'cat', os.path.join(root, 'test-suite.log')) | 
 | 538 |         raise Exception('Unit tests failed') | 
 | 539 |  | 
| Patrick Venture | ad4354e | 2018-10-12 16:59:54 -0700 | [diff] [blame] | 540 | def run_cppcheck(top_dir): | 
 | 541 |     try: | 
 | 542 |         # http://cppcheck.sourceforge.net/manual.pdf | 
 | 543 |         ignore_list = ['-i%s' % path for path in os.listdir(top_dir) \ | 
 | 544 |                        if path.endswith('-src') or path.endswith('-build')] | 
 | 545 |         ignore_list.extend(('-itest', '-iscripts')) | 
 | 546 |         params = ['cppcheck', '-j', str(multiprocessing.cpu_count()), | 
 | 547 |                   '--enable=all'] | 
 | 548 |         params.extend(ignore_list) | 
 | 549 |         params.append('.') | 
 | 550 |  | 
 | 551 |         check_call_cmd(top_dir, *params) | 
 | 552 |     except CalledProcessError: | 
 | 553 |         raise Exception('Cppcheck failed') | 
| William A. Kennington III | 0f0a680 | 2018-07-16 11:52:33 -0700 | [diff] [blame] | 554 |  | 
| William A. Kennington III | 37a89a2 | 2018-12-13 14:32:02 -0800 | [diff] [blame] | 555 | def is_valgrind_safe(): | 
 | 556 |     """ | 
 | 557 |     Returns whether it is safe to run valgrind on our platform | 
 | 558 |     """ | 
 | 559 |     return re.match('ppc64', platform.machine()) is None | 
 | 560 |  | 
| William A. Kennington III | 0f0a680 | 2018-07-16 11:52:33 -0700 | [diff] [blame] | 561 | def maybe_run_valgrind(top_dir): | 
 | 562 |     """ | 
 | 563 |     Potentially runs the unit tests through valgrind for the package | 
 | 564 |     via `make check-valgrind`. If the package does not have valgrind testing | 
 | 565 |     then it just skips over this. | 
 | 566 |  | 
 | 567 |     Parameter descriptions: | 
 | 568 |     top_dir             The root directory of our project | 
 | 569 |     """ | 
| William A. Kennington III | 4e1d0a1 | 2018-07-16 12:04:03 -0700 | [diff] [blame] | 570 |     # Valgrind testing is currently broken by an aggressive strcmp optimization | 
 | 571 |     # that is inlined into optimized code for POWER by gcc 7+. Until we find | 
 | 572 |     # a workaround, just don't run valgrind tests on POWER. | 
 | 573 |     # https://github.com/openbmc/openbmc/issues/3315 | 
| William A. Kennington III | 37a89a2 | 2018-12-13 14:32:02 -0800 | [diff] [blame] | 574 |     if not is_valgrind_safe(): | 
| William A. Kennington III | 4e1d0a1 | 2018-07-16 12:04:03 -0700 | [diff] [blame] | 575 |         return | 
| William A. Kennington III | 0f0a680 | 2018-07-16 11:52:33 -0700 | [diff] [blame] | 576 |     if not make_target_exists('check-valgrind'): | 
 | 577 |         return | 
 | 578 |  | 
 | 579 |     try: | 
 | 580 |         cmd = make_parallel + [ 'check-valgrind' ] | 
 | 581 |         check_call_cmd(top_dir,  *cmd) | 
 | 582 |     except CalledProcessError: | 
 | 583 |         for root, _, files in os.walk(top_dir): | 
 | 584 |             for f in files: | 
 | 585 |                 if re.search('test-suite-[a-z]+.log', f) is None: | 
 | 586 |                     continue | 
 | 587 |                 check_call_cmd(root, 'cat', os.path.join(root, f)) | 
 | 588 |         raise Exception('Valgrind tests failed') | 
 | 589 |  | 
 | 590 | def maybe_run_coverage(top_dir): | 
 | 591 |     """ | 
 | 592 |     Potentially runs the unit tests through code coverage for the package | 
 | 593 |     via `make check-code-coverage`. If the package does not have code coverage | 
 | 594 |     testing then it just skips over this. | 
 | 595 |  | 
 | 596 |     Parameter descriptions: | 
 | 597 |     top_dir             The root directory of our project | 
 | 598 |     """ | 
 | 599 |     if not make_target_exists('check-code-coverage'): | 
 | 600 |         return | 
 | 601 |  | 
 | 602 |     # Actually run code coverage | 
 | 603 |     try: | 
 | 604 |         cmd = make_parallel + [ 'check-code-coverage' ] | 
 | 605 |         check_call_cmd(top_dir,  *cmd) | 
 | 606 |     except CalledProcessError: | 
 | 607 |         raise Exception('Code coverage failed') | 
| Matthew Barth | ccb7f85 | 2016-11-23 17:43:02 -0600 | [diff] [blame] | 608 |  | 
 | 609 | if __name__ == '__main__': | 
 | 610 |     # CONFIGURE_FLAGS = [GIT REPO]:[CONFIGURE FLAGS] | 
 | 611 |     CONFIGURE_FLAGS = { | 
| Adriana Kobylak | 43c31e8 | 2017-02-13 09:28:35 -0600 | [diff] [blame] | 612 |         'phosphor-objmgr': ['--enable-unpatched-systemd'], | 
| Matthew Barth | 1d1c673 | 2017-03-24 10:00:28 -0500 | [diff] [blame] | 613 |         'sdbusplus': ['--enable-transaction'], | 
 | 614 |         'phosphor-logging': | 
 | 615 |         ['--enable-metadata-processing', | 
| Deepak Kodihalli | 3a4e1b4 | 2017-06-08 09:52:35 -0500 | [diff] [blame] | 616 |          'YAML_DIR=/usr/local/share/phosphor-dbus-yaml/yaml'] | 
| Matthew Barth | ccb7f85 | 2016-11-23 17:43:02 -0600 | [diff] [blame] | 617 |     } | 
 | 618 |  | 
| William A. Kennington III | 3f1d120 | 2018-12-06 18:02:07 -0800 | [diff] [blame] | 619 |     # MESON_FLAGS = [GIT REPO]:[MESON FLAGS] | 
 | 620 |     MESON_FLAGS = { | 
 | 621 |     } | 
 | 622 |  | 
| Matthew Barth | ccb7f85 | 2016-11-23 17:43:02 -0600 | [diff] [blame] | 623 |     # DEPENDENCIES = [MACRO]:[library/header]:[GIT REPO] | 
 | 624 |     DEPENDENCIES = { | 
 | 625 |         'AC_CHECK_LIB': {'mapper': 'phosphor-objmgr'}, | 
| Matthew Barth | 710f3f0 | 2017-01-18 15:20:19 -0600 | [diff] [blame] | 626 |         'AC_CHECK_HEADER': { | 
 | 627 |             'host-ipmid': 'phosphor-host-ipmid', | 
| Patrick Venture | b41a446 | 2018-10-03 17:27:38 -0700 | [diff] [blame] | 628 |             'blobs-ipmid': 'phosphor-ipmi-blobs', | 
| Matthew Barth | 710f3f0 | 2017-01-18 15:20:19 -0600 | [diff] [blame] | 629 |             'sdbusplus': 'sdbusplus', | 
| William A. Kennington III | b4f730a | 2018-09-12 11:21:20 -0700 | [diff] [blame] | 630 |             'sdeventplus': 'sdeventplus', | 
| Patrick Venture | 2232996 | 2018-09-14 10:23:04 -0700 | [diff] [blame] | 631 |             'gpioplus': 'gpioplus', | 
| Saqib Khan | 6614505 | 2017-02-14 12:02:07 -0600 | [diff] [blame] | 632 |             'phosphor-logging/log.hpp': 'phosphor-logging', | 
| Patrick Williams | eab8a37 | 2017-01-30 11:21:32 -0600 | [diff] [blame] | 633 |         }, | 
| Brad Bishop | ebb4911 | 2017-02-13 11:07:26 -0500 | [diff] [blame] | 634 |         'AC_PATH_PROG': {'sdbus++': 'sdbusplus'}, | 
| Patrick Williams | eab8a37 | 2017-01-30 11:21:32 -0600 | [diff] [blame] | 635 |         'PKG_CHECK_MODULES': { | 
| Matthew Barth | 19e261e | 2017-02-01 12:55:22 -0600 | [diff] [blame] | 636 |             'phosphor-dbus-interfaces': 'phosphor-dbus-interfaces', | 
| Patrick Williams | f128b40 | 2017-03-29 06:45:59 -0500 | [diff] [blame] | 637 |             'openpower-dbus-interfaces': 'openpower-dbus-interfaces', | 
| Matt Spinler | 7be1903 | 2018-04-13 09:43:14 -0500 | [diff] [blame] | 638 |             'ibm-dbus-interfaces': 'ibm-dbus-interfaces', | 
| Brad Bishop | ebb4911 | 2017-02-13 11:07:26 -0500 | [diff] [blame] | 639 |             'sdbusplus': 'sdbusplus', | 
| William A. Kennington III | b4f730a | 2018-09-12 11:21:20 -0700 | [diff] [blame] | 640 |             'sdeventplus': 'sdeventplus', | 
| Patrick Venture | 2232996 | 2018-09-14 10:23:04 -0700 | [diff] [blame] | 641 |             'gpioplus': 'gpioplus', | 
| Brad Bishop | ebb4911 | 2017-02-13 11:07:26 -0500 | [diff] [blame] | 642 |             'phosphor-logging': 'phosphor-logging', | 
| Marri Devender Rao | a3eee8a | 2018-08-13 05:34:27 -0500 | [diff] [blame] | 643 |             'phosphor-snmp': 'phosphor-snmp', | 
| Brad Bishop | ebb4911 | 2017-02-13 11:07:26 -0500 | [diff] [blame] | 644 |         }, | 
| Matthew Barth | ccb7f85 | 2016-11-23 17:43:02 -0600 | [diff] [blame] | 645 |     } | 
 | 646 |  | 
| William A. Kennington III | e67f5fc | 2018-12-06 17:40:30 -0800 | [diff] [blame] | 647 |     # Offset into array of macro parameters MACRO(0, 1, ...N) | 
 | 648 |     DEPENDENCIES_OFFSET = { | 
 | 649 |         'AC_CHECK_LIB': 0, | 
 | 650 |         'AC_CHECK_HEADER': 0, | 
 | 651 |         'AC_PATH_PROG': 1, | 
 | 652 |         'PKG_CHECK_MODULES': 1, | 
 | 653 |     } | 
 | 654 |  | 
| Leonel Gonzalez | a62a1a1 | 2017-03-24 11:03:47 -0500 | [diff] [blame] | 655 |     # DEPENDENCIES_REGEX = [GIT REPO]:[REGEX STRING] | 
 | 656 |     DEPENDENCIES_REGEX = { | 
| Patrick Venture | d37b805 | 2018-10-16 16:03:03 -0700 | [diff] [blame] | 657 |         'phosphor-logging': r'\S+-dbus-interfaces$' | 
| Leonel Gonzalez | a62a1a1 | 2017-03-24 11:03:47 -0500 | [diff] [blame] | 658 |     } | 
 | 659 |  | 
| Matthew Barth | 33df879 | 2016-12-19 14:30:17 -0600 | [diff] [blame] | 660 |     # Set command line arguments | 
 | 661 |     parser = argparse.ArgumentParser() | 
 | 662 |     parser.add_argument("-w", "--workspace", dest="WORKSPACE", required=True, | 
 | 663 |                         help="Workspace directory location(i.e. /home)") | 
 | 664 |     parser.add_argument("-p", "--package", dest="PACKAGE", required=True, | 
 | 665 |                         help="OpenBMC package to be unit tested") | 
| William A. Kennington III | 65b37fa | 2019-01-31 15:15:17 -0800 | [diff] [blame^] | 666 |     parser.add_argument("-t", "--test-only", dest="TEST_ONLY", | 
 | 667 |                         action="store_true", required=False, default=False, | 
 | 668 |                         help="Only run test cases, no other validation") | 
| Matthew Barth | 33df879 | 2016-12-19 14:30:17 -0600 | [diff] [blame] | 669 |     parser.add_argument("-v", "--verbose", action="store_true", | 
 | 670 |                         help="Print additional package status messages") | 
| Andrew Jeffery | 468309d | 2018-03-08 13:46:33 +1030 | [diff] [blame] | 671 |     parser.add_argument("-r", "--repeat", help="Repeat tests N times", | 
 | 672 |                         type=int, default=1) | 
| Andrew Geissler | a61acb5 | 2019-01-03 16:32:44 -0600 | [diff] [blame] | 673 |     parser.add_argument("-b", "--branch", dest="BRANCH", required=False, | 
 | 674 |                         help="Branch to target for dependent repositories", | 
 | 675 |                         default="master") | 
| Matthew Barth | 33df879 | 2016-12-19 14:30:17 -0600 | [diff] [blame] | 676 |     args = parser.parse_args(sys.argv[1:]) | 
 | 677 |     WORKSPACE = args.WORKSPACE | 
 | 678 |     UNIT_TEST_PKG = args.PACKAGE | 
| William A. Kennington III | 65b37fa | 2019-01-31 15:15:17 -0800 | [diff] [blame^] | 679 |     TEST_ONLY = args.TEST_ONLY | 
| Andrew Geissler | a61acb5 | 2019-01-03 16:32:44 -0600 | [diff] [blame] | 680 |     BRANCH = args.BRANCH | 
| Matthew Barth | 33df879 | 2016-12-19 14:30:17 -0600 | [diff] [blame] | 681 |     if args.verbose: | 
 | 682 |         def printline(*line): | 
 | 683 |             for arg in line: | 
 | 684 |                 print arg, | 
 | 685 |             print | 
 | 686 |     else: | 
 | 687 |         printline = lambda *l: None | 
| Matthew Barth | ccb7f85 | 2016-11-23 17:43:02 -0600 | [diff] [blame] | 688 |  | 
| James Feist | 878df5c | 2018-07-26 14:54:28 -0700 | [diff] [blame] | 689 |     # First validate code formatting if repo has style formatting files. | 
| Adriana Kobylak | bcee22b | 2018-01-10 16:58:27 -0600 | [diff] [blame] | 690 |     # The format-code.sh checks for these files. | 
| Andrew Geissler | a28286d | 2018-01-10 11:00:00 -0800 | [diff] [blame] | 691 |     CODE_SCAN_DIR = WORKSPACE + "/" + UNIT_TEST_PKG | 
| Adriana Kobylak | bcee22b | 2018-01-10 16:58:27 -0600 | [diff] [blame] | 692 |     check_call_cmd(WORKSPACE, "./format-code.sh", CODE_SCAN_DIR) | 
| Andrew Geissler | a28286d | 2018-01-10 11:00:00 -0800 | [diff] [blame] | 693 |  | 
| William A. Kennington III | 3f1d120 | 2018-12-06 18:02:07 -0800 | [diff] [blame] | 694 |     # Automake and meson | 
 | 695 |     if (os.path.isfile(CODE_SCAN_DIR + "/configure.ac") or | 
 | 696 |         os.path.isfile(CODE_SCAN_DIR + '/meson.build')): | 
| James Feist | 878df5c | 2018-07-26 14:54:28 -0700 | [diff] [blame] | 697 |         prev_umask = os.umask(000) | 
 | 698 |         # Determine dependencies and add them | 
 | 699 |         dep_added = dict() | 
 | 700 |         dep_added[UNIT_TEST_PKG] = False | 
 | 701 |         # Create dependency tree | 
 | 702 |         dep_tree = DepTree(UNIT_TEST_PKG) | 
 | 703 |         build_dep_tree(UNIT_TEST_PKG, | 
 | 704 |                        os.path.join(WORKSPACE, UNIT_TEST_PKG), | 
 | 705 |                        dep_added, | 
| Andrew Geissler | a61acb5 | 2019-01-03 16:32:44 -0600 | [diff] [blame] | 706 |                        dep_tree, | 
 | 707 |                        BRANCH) | 
| James Feist | 878df5c | 2018-07-26 14:54:28 -0700 | [diff] [blame] | 708 |  | 
 | 709 |         # Reorder Dependency Tree | 
 | 710 |         for pkg_name, regex_str in DEPENDENCIES_REGEX.iteritems(): | 
 | 711 |             dep_tree.ReorderDeps(pkg_name, regex_str) | 
 | 712 |         if args.verbose: | 
 | 713 |             dep_tree.PrintTree() | 
 | 714 |         install_list = dep_tree.GetInstallList() | 
| William A. Kennington III | d61316d | 2018-12-06 14:56:12 -0800 | [diff] [blame] | 715 |         # We don't want to treat our package as a dependency | 
 | 716 |         install_list.remove(UNIT_TEST_PKG) | 
| James Feist | 878df5c | 2018-07-26 14:54:28 -0700 | [diff] [blame] | 717 |         # install reordered dependencies | 
| William A. Kennington III | d61316d | 2018-12-06 14:56:12 -0800 | [diff] [blame] | 718 |         for dep in install_list: | 
 | 719 |             build_and_install(dep, False) | 
| James Feist | 878df5c | 2018-07-26 14:54:28 -0700 | [diff] [blame] | 720 |         top_dir = os.path.join(WORKSPACE, UNIT_TEST_PKG) | 
 | 721 |         os.chdir(top_dir) | 
| James Feist | 878df5c | 2018-07-26 14:54:28 -0700 | [diff] [blame] | 722 |         # Run package unit tests | 
| William A. Kennington III | d61316d | 2018-12-06 14:56:12 -0800 | [diff] [blame] | 723 |         build_and_install(UNIT_TEST_PKG, True) | 
| William A. Kennington III | 3f1d120 | 2018-12-06 18:02:07 -0800 | [diff] [blame] | 724 |         if os.path.isfile(CODE_SCAN_DIR + '/meson.build'): | 
| William A. Kennington III | 65b37fa | 2019-01-31 15:15:17 -0800 | [diff] [blame^] | 725 |             if not TEST_ONLY: | 
 | 726 |                 # Run valgrind if it is supported | 
 | 727 |                 if is_valgrind_safe(): | 
 | 728 |                     check_call_cmd(top_dir, 'meson', 'test', '-C', 'build', | 
 | 729 |                                    '--wrap', 'valgrind') | 
| William A. Kennington III | 40d5c7c | 2018-12-13 14:37:59 -0800 | [diff] [blame] | 730 |  | 
| William A. Kennington III | 65b37fa | 2019-01-31 15:15:17 -0800 | [diff] [blame^] | 731 |                 # Run clang-tidy only if the project has a configuration | 
 | 732 |                 if os.path.isfile('.clang-tidy'): | 
 | 733 |                     check_call_cmd(top_dir, 'run-clang-tidy-6.0.py', '-p', | 
 | 734 |                                    'build') | 
 | 735 |                 # Run the basic clang static analyzer otherwise | 
 | 736 |                 else: | 
 | 737 |                     os.environ['SCANBUILD'] = 'scan-build-6.0' | 
| William A. Kennington III | 9fe5832 | 2018-12-13 19:33:39 -0800 | [diff] [blame] | 738 |                     check_call_cmd(top_dir, 'ninja', '-C', 'build', | 
| William A. Kennington III | 65b37fa | 2019-01-31 15:15:17 -0800 | [diff] [blame^] | 739 |                                    'scan-build') | 
 | 740 |  | 
 | 741 |                 # Run tests through sanitizers | 
 | 742 |                 # b_lundef is needed if clang++ is CXX since it resolves the | 
 | 743 |                 # asan symbols at runtime only. We don't want to set it earlier | 
 | 744 |                 # in the build process to ensure we don't have undefined | 
 | 745 |                 # runtime code. | 
 | 746 |                 check_call_cmd(top_dir, 'meson', 'configure', 'build', | 
 | 747 |                                '-Db_sanitize=address,undefined', | 
 | 748 |                                '-Db_lundef=false') | 
 | 749 |                 check_call_cmd(top_dir, 'meson', 'test', '-C', 'build', | 
 | 750 |                                '--logbase', 'testlog-ubasan') | 
 | 751 |                 # TODO: Fix memory sanitizer | 
 | 752 |                 #check_call_cmd(top_dir, 'meson', 'configure', 'build', | 
 | 753 |                 #               '-Db_sanitize=memory') | 
 | 754 |                 #check_call_cmd(top_dir, 'meson', 'test', '-C', 'build' | 
 | 755 |                 #               '--logbase', 'testlog-msan') | 
 | 756 |                 check_call_cmd(top_dir, 'meson', 'configure', 'build', | 
 | 757 |                                '-Db_sanitize=none', '-Db_lundef=true') | 
 | 758 |  | 
 | 759 |                 # Run coverage checks | 
 | 760 |                 check_call_cmd(top_dir, 'meson', 'configure', 'build', | 
 | 761 |                                '-Db_coverage=true') | 
 | 762 |                 check_call_cmd(top_dir, 'meson', 'test', '-C', 'build') | 
 | 763 |                 # Only build coverage HTML if coverage files were produced | 
 | 764 |                 for root, dirs, files in os.walk('build'): | 
 | 765 |                     if any([f.endswith('.gcda') for f in files]): | 
 | 766 |                         check_call_cmd(top_dir, 'ninja', '-C', 'build', | 
 | 767 |                                        'coverage-html') | 
 | 768 |                         break | 
 | 769 |                 check_call_cmd(top_dir, 'meson', 'configure', 'build', | 
 | 770 |                                '-Db_coverage=false') | 
 | 771 |             else: | 
 | 772 |                 check_call_cmd(top_dir, 'meson', 'test', '-C', 'build') | 
| William A. Kennington III | 3f1d120 | 2018-12-06 18:02:07 -0800 | [diff] [blame] | 773 |         else: | 
 | 774 |             run_unit_tests(top_dir) | 
| William A. Kennington III | 65b37fa | 2019-01-31 15:15:17 -0800 | [diff] [blame^] | 775 |             if not TEST_ONLY: | 
 | 776 |                 maybe_run_valgrind(top_dir) | 
 | 777 |                 maybe_run_coverage(top_dir) | 
 | 778 |         if not TEST_ONLY: | 
 | 779 |             run_cppcheck(top_dir) | 
| James Feist | 878df5c | 2018-07-26 14:54:28 -0700 | [diff] [blame] | 780 |  | 
 | 781 |         os.umask(prev_umask) | 
 | 782 |  | 
 | 783 |     # Cmake | 
 | 784 |     elif os.path.isfile(CODE_SCAN_DIR + "/CMakeLists.txt"): | 
 | 785 |         top_dir = os.path.join(WORKSPACE, UNIT_TEST_PKG) | 
 | 786 |         os.chdir(top_dir) | 
| William A. Kennington III | fafef89 | 2018-12-11 16:52:43 -0800 | [diff] [blame] | 787 |         check_call_cmd(top_dir, 'cmake', '-DCMAKE_EXPORT_COMPILE_COMMANDS=ON', '.') | 
| James Feist | 878df5c | 2018-07-26 14:54:28 -0700 | [diff] [blame] | 788 |         check_call_cmd(top_dir, 'cmake', '--build', '.', '--', '-j', | 
 | 789 |                        str(multiprocessing.cpu_count())) | 
 | 790 |         if make_target_exists('test'): | 
 | 791 |             check_call_cmd(top_dir, 'ctest', '.') | 
| William A. Kennington III | 65b37fa | 2019-01-31 15:15:17 -0800 | [diff] [blame^] | 792 |         if not TEST_ONLY: | 
 | 793 |             maybe_run_valgrind(top_dir) | 
 | 794 |             maybe_run_coverage(top_dir) | 
 | 795 |             run_cppcheck(top_dir) | 
 | 796 |             if os.path.isfile('.clang-tidy'): | 
 | 797 |                 check_call_cmd(top_dir, 'run-clang-tidy-6.0.py', '-p', '.') | 
| James Feist | 878df5c | 2018-07-26 14:54:28 -0700 | [diff] [blame] | 798 |  | 
 | 799 |     else: | 
| Andrew Geissler | 71a7cc1 | 2018-01-31 14:18:37 -0800 | [diff] [blame] | 800 |         print "Not a supported repo for CI Tests, exit" | 
 | 801 |         quit() |