Diana Picus | 3b2ef82 | 2016-10-13 16:53:18 +0300 | [diff] [blame] | 1 | """This is the main tool for handling llvm builds, bisects etc.""" |
| 2 | |
| 3 | import os |
Diana Picus | adb07c4 | 2017-11-22 16:12:57 +0100 | [diff] [blame] | 4 | from sys import argv |
Diana Picus | 3b2ef82 | 2016-10-13 16:53:18 +0300 | [diff] [blame] | 5 | from sys import exit |
| 6 | |
Diana Picus | 052b7d3 | 2017-11-24 16:19:41 +0100 | [diff] [blame] | 7 | from modules.llvm import LLVMBuildConfig |
Diana Picus | 95226d4 | 2017-11-01 13:16:54 +0100 | [diff] [blame] | 8 | from modules.llvm import LLVMSubproject |
| 9 | from modules.llvm import LLVMSourceConfig |
Diana Picus | b368cb6 | 2018-01-23 16:41:59 +0100 | [diff] [blame] | 10 | from modules.llvm import run_test_suite |
Diana Picus | f73abbf | 2018-01-26 07:06:20 +0100 | [diff] [blame] | 11 | from modules.llvm import setup_test_suite |
Diana Picus | 052b7d3 | 2017-11-24 16:19:41 +0100 | [diff] [blame] | 12 | from modules.utils import CommandPrinter |
| 13 | from modules.utils import CommandRunner |
Diana Picus | 5ad5542 | 2017-12-14 17:57:10 +0100 | [diff] [blame] | 14 | from modules.utils import get_remote_branch |
| 15 | from modules.utils import push_branch |
Diana Picus | 95226d4 | 2017-11-01 13:16:54 +0100 | [diff] [blame] | 16 | |
Diana Picus | 052b7d3 | 2017-11-24 16:19:41 +0100 | [diff] [blame] | 17 | from linaropy.cd import cd |
Diana Picus | 3b2ef82 | 2016-10-13 16:53:18 +0300 | [diff] [blame] | 18 | from linaropy.git.clone import Clone |
| 19 | from linaropy.proj import Proj |
| 20 | |
| 21 | from argparse import Action, ArgumentParser, RawTextHelpFormatter |
Diana Picus | efc7bda | 2017-06-09 19:14:08 +0200 | [diff] [blame] | 22 | from functools import partial |
Diana Picus | 3b2ef82 | 2016-10-13 16:53:18 +0300 | [diff] [blame] | 23 | |
| 24 | |
| 25 | def die(message, config_to_dump=None): |
| 26 | """Print an error message and exit.""" |
Diana Picus | b430760 | 2017-04-05 19:48:39 +0200 | [diff] [blame] | 27 | print(message) |
Diana Picus | 3b2ef82 | 2016-10-13 16:53:18 +0300 | [diff] [blame] | 28 | |
| 29 | if config_to_dump is not None: |
| 30 | dump_config(config_to_dump) |
| 31 | |
| 32 | exit(1) |
| 33 | |
Diana Picus | 3d1a301 | 2017-03-14 17:38:32 +0100 | [diff] [blame] | 34 | |
Diana Picus | 3b2ef82 | 2016-10-13 16:53:18 +0300 | [diff] [blame] | 35 | def dump_config(config): |
| 36 | """Dump the list of projects that are enabled in the given config.""" |
| 37 | |
Diana Picus | b430760 | 2017-04-05 19:48:39 +0200 | [diff] [blame] | 38 | print("Projects linked:") |
Diana Picus | 3b2ef82 | 2016-10-13 16:53:18 +0300 | [diff] [blame] | 39 | enabled = config.get_enabled_subprojects() |
| 40 | if not enabled: |
Diana Picus | b430760 | 2017-04-05 19:48:39 +0200 | [diff] [blame] | 41 | print("none") |
Diana Picus | 3b2ef82 | 2016-10-13 16:53:18 +0300 | [diff] [blame] | 42 | else: |
| 43 | for subproj in sorted(enabled): |
Diana Picus | b430760 | 2017-04-05 19:48:39 +0200 | [diff] [blame] | 44 | print(" + {}".format(subproj)) |
Diana Picus | 3b2ef82 | 2016-10-13 16:53:18 +0300 | [diff] [blame] | 45 | |
| 46 | |
| 47 | def projects(args): |
| 48 | """Add/remove subprojects based on the values in args.""" |
| 49 | |
| 50 | proj = Proj() |
Diana Picus | 3d1a301 | 2017-03-14 17:38:32 +0100 | [diff] [blame] | 51 | |
Diana Picus | 9f75686 | 2017-12-20 10:35:08 +0100 | [diff] [blame] | 52 | llvm_worktree_root = args.sources |
Diana Picus | 81089db | 2017-05-05 22:26:49 +0200 | [diff] [blame] | 53 | llvm_repos_root = args.repos |
Diana Picus | b1dbcba | 2018-02-07 01:33:17 +0100 | [diff] [blame] | 54 | config = LLVMSourceConfig(proj, llvm_worktree_root, dry=False) |
Diana Picus | 3b2ef82 | 2016-10-13 16:53:18 +0300 | [diff] [blame] | 55 | |
| 56 | if not args.add and not args.remove: |
| 57 | # Nothing to change, just print the current configuration |
| 58 | dump_config(config) |
| 59 | exit(0) |
| 60 | |
| 61 | to_add = {} |
| 62 | if args.add: |
| 63 | for subproj in args.add: |
| 64 | repo = Clone(proj, os.path.join(llvm_repos_root, subproj)) |
| 65 | to_add[subproj] = repo |
| 66 | |
| 67 | try: |
| 68 | config.update(to_add, args.remove) |
| 69 | except (EnvironmentError, ValueError) as exc: |
| 70 | die("Failed to update subprojects because:\n{}".format(str(exc))) |
| 71 | |
| 72 | dump_config(config) |
| 73 | |
Diana Picus | efc7bda | 2017-06-09 19:14:08 +0200 | [diff] [blame] | 74 | |
Diana Picus | 95226d4 | 2017-11-01 13:16:54 +0100 | [diff] [blame] | 75 | def push_current_branch(args): |
Diana Picus | efc7bda | 2017-06-09 19:14:08 +0200 | [diff] [blame] | 76 | """Push current branch to origin.""" |
| 77 | |
| 78 | proj = Proj() |
| 79 | |
Diana Picus | 9f75686 | 2017-12-20 10:35:08 +0100 | [diff] [blame] | 80 | llvm_worktree_root = args.sources |
Diana Picus | b1dbcba | 2018-02-07 01:33:17 +0100 | [diff] [blame] | 81 | config = LLVMSourceConfig(proj, llvm_worktree_root, dry=False) |
Diana Picus | efc7bda | 2017-06-09 19:14:08 +0200 | [diff] [blame] | 82 | |
Diana Picus | 95226d4 | 2017-11-01 13:16:54 +0100 | [diff] [blame] | 83 | llvm_worktree = Clone(proj, llvm_worktree_root) |
| 84 | local_branch = llvm_worktree.getbranch() |
Diana Picus | efc7bda | 2017-06-09 19:14:08 +0200 | [diff] [blame] | 85 | |
| 86 | try: |
Diana Picus | 95226d4 | 2017-11-01 13:16:54 +0100 | [diff] [blame] | 87 | remote_branch = get_remote_branch(llvm_worktree, local_branch) |
| 88 | config.for_each_enabled(partial(push_branch, proj, local_branch, |
| 89 | remote_branch)) |
| 90 | print("Pushed to {}".format(remote_branch)) |
| 91 | except (EnvironmentError, RuntimeError) as exc: |
Diana Picus | efc7bda | 2017-06-09 19:14:08 +0200 | [diff] [blame] | 92 | die("Failed to push branch because: " + str(exc) + str(exc.__cause__)) |
| 93 | |
Diana Picus | 95226d4 | 2017-11-01 13:16:54 +0100 | [diff] [blame] | 94 | |
Diana Picus | 052b7d3 | 2017-11-24 16:19:41 +0100 | [diff] [blame] | 95 | def configure_build(args): |
| 96 | """Configure a given build directory.""" |
| 97 | |
| 98 | proj = Proj() |
| 99 | |
Diana Picus | 9f75686 | 2017-12-20 10:35:08 +0100 | [diff] [blame] | 100 | llvm_worktree_root = args.sources |
Diana Picus | b1dbcba | 2018-02-07 01:33:17 +0100 | [diff] [blame] | 101 | sourceConfig = LLVMSourceConfig(proj, llvm_worktree_root, args.dry) |
Diana Picus | 052b7d3 | 2017-11-24 16:19:41 +0100 | [diff] [blame] | 102 | |
Diana Picus | 052b7d3 | 2017-11-24 16:19:41 +0100 | [diff] [blame] | 103 | if args.dry: |
| 104 | consumer = CommandPrinter() |
| 105 | else: |
| 106 | if not os.path.exists(args.build): |
| 107 | os.makedirs(args.build) |
| 108 | consumer = CommandRunner() |
| 109 | |
Diana Picus | 6b1935f | 2018-02-07 16:44:11 +0100 | [diff] [blame^] | 110 | buildConfig = LLVMBuildConfig(sourceConfig, args.build, consumer) |
| 111 | |
| 112 | if args.defs: |
| 113 | args.defs = ["-D{}".format(v) for v in args.defs] |
| 114 | |
Diana Picus | 052b7d3 | 2017-11-24 16:19:41 +0100 | [diff] [blame] | 115 | try: |
Diana Picus | 6b1935f | 2018-02-07 16:44:11 +0100 | [diff] [blame^] | 116 | buildConfig.cmake(args.defs, args.generator) |
Diana Picus | 052b7d3 | 2017-11-24 16:19:41 +0100 | [diff] [blame] | 117 | except RuntimeError as exc: |
| 118 | die("Failed to configure {} because:\n{}".format(args.build, str(exc))) |
| 119 | |
| 120 | |
Diana Picus | 37126b8 | 2018-01-19 16:14:26 +0100 | [diff] [blame] | 121 | def run_build(args): |
| 122 | """Run a build command in a given directory.""" |
| 123 | build_dir = args.build |
| 124 | |
| 125 | if args.dry: |
| 126 | consumer = CommandPrinter() |
| 127 | else: |
| 128 | consumer = CommandRunner() |
| 129 | |
| 130 | try: |
Diana Picus | 6b1935f | 2018-02-07 16:44:11 +0100 | [diff] [blame^] | 131 | LLVMBuildConfig(None, args.build, consumer).build(args.flags) |
Diana Picus | 37126b8 | 2018-01-19 16:14:26 +0100 | [diff] [blame] | 132 | except RuntimeError as exc: |
| 133 | die("Failed to build {} because:\n{}".format(args.build, str(exc))) |
| 134 | |
| 135 | |
Diana Picus | f73abbf | 2018-01-26 07:06:20 +0100 | [diff] [blame] | 136 | def setup_the_test_suite(args): |
| 137 | """Setup a sandbox for the test-suite.""" |
| 138 | if args.dry: |
| 139 | consumer = CommandPrinter() |
| 140 | else: |
| 141 | consumer = CommandRunner() |
| 142 | |
| 143 | try: |
| 144 | setup_test_suite(consumer, args.sandbox, args.lnt) |
| 145 | except RuntimeError as exc: |
| 146 | die("Failed to setup the test-suite because:\n{}".format(str(exc))) |
| 147 | |
| 148 | |
Diana Picus | b368cb6 | 2018-01-23 16:41:59 +0100 | [diff] [blame] | 149 | def run_the_test_suite(args): |
| 150 | """Run the test-suite in a given sandbox.""" |
| 151 | if args.dry: |
| 152 | consumer = CommandPrinter() |
| 153 | else: |
| 154 | consumer = CommandRunner() |
| 155 | |
| 156 | compilers = ["--cc={}".format(args.cc)] |
| 157 | if args.cxx: |
| 158 | compilers.append("--cxx={}".format(args.cxx)) |
| 159 | |
| 160 | try: |
| 161 | run_test_suite(consumer, args.sandbox, args.testsuite, args.lit, |
| 162 | compilers + args.flags) |
| 163 | except RuntimeError as exc: |
| 164 | die("Failed to run the test-suite because:\n{}".format(str(exc))) |
| 165 | |
Diana Picus | 3b2ef82 | 2016-10-13 16:53:18 +0300 | [diff] [blame] | 166 | ########################################################################## |
| 167 | # Command line parsing # |
| 168 | ########################################################################## |
| 169 | |
| 170 | # If we decide we want shorthands for the subprojects, we can append to this |
| 171 | # list |
Diana Picus | b430760 | 2017-04-05 19:48:39 +0200 | [diff] [blame] | 172 | valid_subprojects = list(LLVMSubproject.get_all_subprojects().keys()) |
Diana Picus | 3b2ef82 | 2016-10-13 16:53:18 +0300 | [diff] [blame] | 173 | |
| 174 | options = ArgumentParser(formatter_class=RawTextHelpFormatter) |
Diana Picus | adb07c4 | 2017-11-22 16:12:57 +0100 | [diff] [blame] | 175 | subcommands = options.add_subparsers(dest="subcommand") |
Diana Picus | 3b2ef82 | 2016-10-13 16:53:18 +0300 | [diff] [blame] | 176 | |
| 177 | # Subcommand for adding / removing subprojects |
Diana Picus | 36317e8 | 2017-10-31 15:35:24 +0100 | [diff] [blame] | 178 | projs = subcommands.add_parser( |
| 179 | "projects", help="Add/remove LLVM subprojects.\n" |
| 180 | "Adding a subproject will create a worktree for it " |
| 181 | "somewhere in the LLVM source tree, on the same git " |
| 182 | "branch as LLVM itself.\n" |
| 183 | "Removing a subproject will remove the worktree, but " |
| 184 | "not the underlying git branch.") |
Diana Picus | 3b2ef82 | 2016-10-13 16:53:18 +0300 | [diff] [blame] | 185 | projs.set_defaults(run_command=projects) |
| 186 | |
| 187 | # TODO: Overwriting previous values is not necessarily what users expect (so for |
| 188 | # instance --add S1 S2 --remove S3 --add S4 would lead to adding only S4). We |
| 189 | # can do better by using action='append', which would create a list (of lists? |
| 190 | # or of lists and scalars?) that we can flatten to obtain all the values passed |
| 191 | # by the user. |
| 192 | projs.add_argument( |
| 193 | '-a', '--add', |
| 194 | nargs='+', |
| 195 | choices=valid_subprojects, |
| 196 | metavar='subproject', |
Diana Picus | 36317e8 | 2017-10-31 15:35:24 +0100 | [diff] [blame] | 197 | help="Enable given subprojects. Valid values are:\n\t{}\n".format( |
Diana Picus | 3b2ef82 | 2016-10-13 16:53:18 +0300 | [diff] [blame] | 198 | "\n\t".join(valid_subprojects))) |
| 199 | projs.add_argument( |
| 200 | '-r', '--remove', |
| 201 | nargs='+', |
| 202 | choices=valid_subprojects, |
| 203 | metavar='subproject', |
Diana Picus | 36317e8 | 2017-10-31 15:35:24 +0100 | [diff] [blame] | 204 | help="Disable given subprojects.") |
Diana Picus | adb07c4 | 2017-11-22 16:12:57 +0100 | [diff] [blame] | 205 | projs.add_argument( |
| 206 | '--repos', |
| 207 | help="Path to the directory containing the repositories for all LLVM " |
| 208 | "subprojects.") |
Diana Picus | 9f75686 | 2017-12-20 10:35:08 +0100 | [diff] [blame] | 209 | projs.add_argument( |
| 210 | '--source-dir', |
| 211 | dest='sources', |
| 212 | required=True, |
| 213 | help="Path to the directory containing the LLVM worktree that we're adding " |
| 214 | "or removing subprojects from.") |
Diana Picus | 3b2ef82 | 2016-10-13 16:53:18 +0300 | [diff] [blame] | 215 | |
Diana Picus | efc7bda | 2017-06-09 19:14:08 +0200 | [diff] [blame] | 216 | # Subcommand for pushing the current branch to origin |
| 217 | push = subcommands.add_parser( |
| 218 | "push", |
Diana Picus | 36317e8 | 2017-10-31 15:35:24 +0100 | [diff] [blame] | 219 | help="Push current branch to origin linaro-local/<user>/<branch>, " |
| 220 | "for all enabled subprojects.") |
Diana Picus | 95226d4 | 2017-11-01 13:16:54 +0100 | [diff] [blame] | 221 | push.set_defaults(run_command=push_current_branch) |
Diana Picus | 9f75686 | 2017-12-20 10:35:08 +0100 | [diff] [blame] | 222 | push.add_argument( |
| 223 | '--source-dir', |
| 224 | dest='sources', |
| 225 | required=True, |
| 226 | help="Path to the directory containing the LLVM worktree.") |
Diana Picus | efc7bda | 2017-06-09 19:14:08 +0200 | [diff] [blame] | 227 | |
Diana Picus | 052b7d3 | 2017-11-24 16:19:41 +0100 | [diff] [blame] | 228 | # Subcommand for configuring a build directory |
| 229 | configure = subcommands.add_parser( |
| 230 | 'configure', |
| 231 | help="Run CMake in the given build directory.") |
| 232 | configure.add_argument( |
Diana Picus | 9f75686 | 2017-12-20 10:35:08 +0100 | [diff] [blame] | 233 | '--source-dir', |
| 234 | dest='sources', |
| 235 | required=True, |
| 236 | help="Path to the sources directory. It should contain an LLVM worktree.") |
| 237 | configure.add_argument( |
Diana Picus | 052b7d3 | 2017-11-24 16:19:41 +0100 | [diff] [blame] | 238 | '--build-dir', |
| 239 | dest='build', |
| 240 | required=True, |
| 241 | help="Path to the build directory. It will be created if it does not exist") |
| 242 | configure.add_argument( |
| 243 | '--cmake-generator', |
| 244 | dest='generator', |
| 245 | default='Ninja', |
| 246 | help="CMake generator to use (default is Ninja).") |
| 247 | configure.add_argument( |
| 248 | '--cmake-def', |
| 249 | dest='defs', |
| 250 | metavar='VAR=VALUE', |
| 251 | default=[], |
| 252 | action='append', |
| 253 | # We add the -D in front of the variable ourselves because the argument |
| 254 | # parsing gets confused otherwise (and quoting doesn't help). |
| 255 | help="Additional CMake definitions, e.g. CMAKE_BUILD_TYPE=Release." |
| 256 | "May be passed several times. The -D is added automatically.") |
| 257 | configure.add_argument( |
| 258 | '-n', '--dry-run', |
| 259 | dest='dry', |
| 260 | action='store_true', |
| 261 | default=False, |
| 262 | help="Print the CMake command instead of executing it.") |
| 263 | configure.set_defaults(run_command=configure_build) |
| 264 | |
Diana Picus | 37126b8 | 2018-01-19 16:14:26 +0100 | [diff] [blame] | 265 | # Subcommand for building a target |
| 266 | build = subcommands.add_parser( |
| 267 | 'build', |
| 268 | help="Run a build command in the given directory." |
| 269 | "The build command can be either a 'ninja' or a 'make' command, depending " |
| 270 | "on what the build directory contains. First, we look for a 'build.ninja' " |
| 271 | "file. If that is not found, we look for a 'Makefile'. If that is not " |
| 272 | "found either, the script fails.") |
| 273 | build.add_argument( |
| 274 | '--build-dir', |
| 275 | dest='build', |
| 276 | required=True, |
| 277 | help="Path to the build directory. It must have already been configured.") |
| 278 | build.add_argument( |
| 279 | '-n', '--dry-run', |
| 280 | dest='dry', |
| 281 | action='store_true', |
| 282 | default=False, |
| 283 | help="Print the build command instead of executing it.") |
| 284 | build.add_argument( |
| 285 | '--build-flag', |
| 286 | dest='flags', |
| 287 | metavar='FLAG', |
| 288 | default=[], |
| 289 | action='append', |
| 290 | help="Additional flags for the build command (e.g. targets to build). " |
| 291 | "May be passed several times. If your flag starts with a '-', use " |
| 292 | "'--build-flag=-FLAG' to pass it.") |
| 293 | build.set_defaults(run_command=run_build) |
| 294 | |
Diana Picus | f73abbf | 2018-01-26 07:06:20 +0100 | [diff] [blame] | 295 | # Subcommand for setting up the test-suite |
| 296 | setupTestSuite = subcommands.add_parser( |
| 297 | 'setup-test-suite', |
| 298 | help="Prepare a sandbox for running the test-suite.") |
| 299 | setupTestSuite.add_argument( |
| 300 | '--sandbox', |
| 301 | required=True, |
| 302 | help="Path where we should setup the sandbox.") |
| 303 | setupTestSuite.add_argument( |
| 304 | '--lnt', |
| 305 | required=True, |
| 306 | help="Path to the LNT sources.") |
| 307 | setupTestSuite.add_argument( |
| 308 | '-n', '--dry-run', |
| 309 | dest='dry', |
| 310 | action='store_true', |
| 311 | default=False, |
| 312 | help="Print the commands instead of executing them.") |
| 313 | setupTestSuite.set_defaults(run_command=setup_the_test_suite) |
| 314 | |
Diana Picus | b368cb6 | 2018-01-23 16:41:59 +0100 | [diff] [blame] | 315 | # Subcommand for running the test-suite |
| 316 | runTestSuite = subcommands.add_parser( |
| 317 | 'run-test-suite', |
| 318 | help="Run the test-suite in the given sandbox.") |
| 319 | runTestSuite.add_argument( |
| 320 | '--sandbox', |
| 321 | required=True, |
| 322 | help="Path to the sandbox. It must point to a virtualenv with a LNT setup.") |
| 323 | runTestSuite.add_argument( |
| 324 | '--test-suite', |
| 325 | dest="testsuite", |
| 326 | required=True, |
| 327 | help="Path to the test-suite repo.") |
| 328 | runTestSuite.add_argument( |
| 329 | '--use-lit', |
| 330 | dest="lit", |
| 331 | required=True, |
| 332 | help="Path to llvm-lit.") |
| 333 | runTestSuite.add_argument( |
| 334 | '--lnt-flag', |
| 335 | dest='flags', |
| 336 | metavar='FLAG', |
| 337 | default=[], |
| 338 | action='append', |
| 339 | help="Additional flags to be passed to LNT when running the test-suite." |
| 340 | "May be passed several times. If your flag starts with a '-', use " |
| 341 | "'--lnt-flag=-FLAG' to pass it.") |
| 342 | runTestSuite.add_argument( |
| 343 | # We can pass --cc through the --lnt-flag interface, but we generally won't |
| 344 | # want to test the system compiler, so force the user to be specific. |
| 345 | '--cc', |
| 346 | required=True, |
| 347 | help="The path to the C compiler that we're testing.") |
| 348 | runTestSuite.add_argument( |
| 349 | # For symmetry, we also provide a --cxx argument, but this one isn't |
| 350 | # required since LNT tries to guess it based on the value of --cc. |
| 351 | '--cxx', |
| 352 | required=False, |
| 353 | help="The path to the C++ compiler that we're testing.") |
| 354 | runTestSuite.add_argument( |
| 355 | '-n', '--dry-run', |
| 356 | dest='dry', |
| 357 | action='store_true', |
| 358 | default=False, |
| 359 | help="Print the commands instead of executing them.") |
| 360 | runTestSuite.set_defaults(run_command=run_the_test_suite) |
| 361 | |
Diana Picus | 3b2ef82 | 2016-10-13 16:53:18 +0300 | [diff] [blame] | 362 | args = options.parse_args() |
Diana Picus | adb07c4 | 2017-11-22 16:12:57 +0100 | [diff] [blame] | 363 | if args.subcommand == "projects" and args.add and not args.repos: |
| 364 | projs.error( |
| 365 | "When adding a subproject you must also pass the --repos argument") |
Diana Picus | 3b2ef82 | 2016-10-13 16:53:18 +0300 | [diff] [blame] | 366 | args.run_command(args) |