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 | 37126b8 | 2018-01-19 16:14:26 +0100 | [diff] [blame] | 7 | from modules.llvm import build_llvm |
Diana Picus | 052b7d3 | 2017-11-24 16:19:41 +0100 | [diff] [blame] | 8 | from modules.llvm import LLVMBuildConfig |
Diana Picus | 95226d4 | 2017-11-01 13:16:54 +0100 | [diff] [blame] | 9 | from modules.llvm import LLVMSubproject |
| 10 | from modules.llvm import LLVMSourceConfig |
Diana Picus | b368cb6 | 2018-01-23 16:41:59 +0100 | [diff] [blame^] | 11 | from modules.llvm import run_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 | 3b2ef82 | 2016-10-13 16:53:18 +0300 | [diff] [blame] | 54 | config = LLVMSourceConfig(proj, llvm_worktree_root) |
| 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 | efc7bda | 2017-06-09 19:14:08 +0200 | [diff] [blame] | 81 | config = LLVMSourceConfig(proj, llvm_worktree_root) |
| 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 | 052b7d3 | 2017-11-24 16:19:41 +0100 | [diff] [blame] | 101 | sourceConfig = LLVMSourceConfig(proj, llvm_worktree_root) |
| 102 | |
| 103 | buildConfig = LLVMBuildConfig(sourceConfig, args.build) |
| 104 | |
| 105 | if args.defs: |
| 106 | args.defs = ["-D{}".format(v) for v in args.defs] |
| 107 | |
| 108 | if args.dry: |
| 109 | consumer = CommandPrinter() |
| 110 | else: |
| 111 | if not os.path.exists(args.build): |
| 112 | os.makedirs(args.build) |
| 113 | consumer = CommandRunner() |
| 114 | |
| 115 | try: |
| 116 | buildConfig.cmake(consumer, args.defs, args.generator) |
| 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: |
| 131 | build_llvm(consumer, args.build, args.flags) |
| 132 | except RuntimeError as exc: |
| 133 | die("Failed to build {} because:\n{}".format(args.build, str(exc))) |
| 134 | |
| 135 | |
Diana Picus | b368cb6 | 2018-01-23 16:41:59 +0100 | [diff] [blame^] | 136 | def run_the_test_suite(args): |
| 137 | """Run the test-suite in a given sandbox.""" |
| 138 | if args.dry: |
| 139 | consumer = CommandPrinter() |
| 140 | else: |
| 141 | consumer = CommandRunner() |
| 142 | |
| 143 | compilers = ["--cc={}".format(args.cc)] |
| 144 | if args.cxx: |
| 145 | compilers.append("--cxx={}".format(args.cxx)) |
| 146 | |
| 147 | try: |
| 148 | run_test_suite(consumer, args.sandbox, args.testsuite, args.lit, |
| 149 | compilers + args.flags) |
| 150 | except RuntimeError as exc: |
| 151 | die("Failed to run the test-suite because:\n{}".format(str(exc))) |
| 152 | |
Diana Picus | 3b2ef82 | 2016-10-13 16:53:18 +0300 | [diff] [blame] | 153 | ########################################################################## |
| 154 | # Command line parsing # |
| 155 | ########################################################################## |
| 156 | |
| 157 | # If we decide we want shorthands for the subprojects, we can append to this |
| 158 | # list |
Diana Picus | b430760 | 2017-04-05 19:48:39 +0200 | [diff] [blame] | 159 | valid_subprojects = list(LLVMSubproject.get_all_subprojects().keys()) |
Diana Picus | 3b2ef82 | 2016-10-13 16:53:18 +0300 | [diff] [blame] | 160 | |
| 161 | options = ArgumentParser(formatter_class=RawTextHelpFormatter) |
Diana Picus | adb07c4 | 2017-11-22 16:12:57 +0100 | [diff] [blame] | 162 | subcommands = options.add_subparsers(dest="subcommand") |
Diana Picus | 3b2ef82 | 2016-10-13 16:53:18 +0300 | [diff] [blame] | 163 | |
| 164 | # Subcommand for adding / removing subprojects |
Diana Picus | 36317e8 | 2017-10-31 15:35:24 +0100 | [diff] [blame] | 165 | projs = subcommands.add_parser( |
| 166 | "projects", help="Add/remove LLVM subprojects.\n" |
| 167 | "Adding a subproject will create a worktree for it " |
| 168 | "somewhere in the LLVM source tree, on the same git " |
| 169 | "branch as LLVM itself.\n" |
| 170 | "Removing a subproject will remove the worktree, but " |
| 171 | "not the underlying git branch.") |
Diana Picus | 3b2ef82 | 2016-10-13 16:53:18 +0300 | [diff] [blame] | 172 | projs.set_defaults(run_command=projects) |
| 173 | |
| 174 | # TODO: Overwriting previous values is not necessarily what users expect (so for |
| 175 | # instance --add S1 S2 --remove S3 --add S4 would lead to adding only S4). We |
| 176 | # can do better by using action='append', which would create a list (of lists? |
| 177 | # or of lists and scalars?) that we can flatten to obtain all the values passed |
| 178 | # by the user. |
| 179 | projs.add_argument( |
| 180 | '-a', '--add', |
| 181 | nargs='+', |
| 182 | choices=valid_subprojects, |
| 183 | metavar='subproject', |
Diana Picus | 36317e8 | 2017-10-31 15:35:24 +0100 | [diff] [blame] | 184 | help="Enable given subprojects. Valid values are:\n\t{}\n".format( |
Diana Picus | 3b2ef82 | 2016-10-13 16:53:18 +0300 | [diff] [blame] | 185 | "\n\t".join(valid_subprojects))) |
| 186 | projs.add_argument( |
| 187 | '-r', '--remove', |
| 188 | nargs='+', |
| 189 | choices=valid_subprojects, |
| 190 | metavar='subproject', |
Diana Picus | 36317e8 | 2017-10-31 15:35:24 +0100 | [diff] [blame] | 191 | help="Disable given subprojects.") |
Diana Picus | adb07c4 | 2017-11-22 16:12:57 +0100 | [diff] [blame] | 192 | projs.add_argument( |
| 193 | '--repos', |
| 194 | help="Path to the directory containing the repositories for all LLVM " |
| 195 | "subprojects.") |
Diana Picus | 9f75686 | 2017-12-20 10:35:08 +0100 | [diff] [blame] | 196 | projs.add_argument( |
| 197 | '--source-dir', |
| 198 | dest='sources', |
| 199 | required=True, |
| 200 | help="Path to the directory containing the LLVM worktree that we're adding " |
| 201 | "or removing subprojects from.") |
Diana Picus | 3b2ef82 | 2016-10-13 16:53:18 +0300 | [diff] [blame] | 202 | |
Diana Picus | efc7bda | 2017-06-09 19:14:08 +0200 | [diff] [blame] | 203 | # Subcommand for pushing the current branch to origin |
| 204 | push = subcommands.add_parser( |
| 205 | "push", |
Diana Picus | 36317e8 | 2017-10-31 15:35:24 +0100 | [diff] [blame] | 206 | help="Push current branch to origin linaro-local/<user>/<branch>, " |
| 207 | "for all enabled subprojects.") |
Diana Picus | 95226d4 | 2017-11-01 13:16:54 +0100 | [diff] [blame] | 208 | push.set_defaults(run_command=push_current_branch) |
Diana Picus | 9f75686 | 2017-12-20 10:35:08 +0100 | [diff] [blame] | 209 | push.add_argument( |
| 210 | '--source-dir', |
| 211 | dest='sources', |
| 212 | required=True, |
| 213 | help="Path to the directory containing the LLVM worktree.") |
Diana Picus | efc7bda | 2017-06-09 19:14:08 +0200 | [diff] [blame] | 214 | |
Diana Picus | 052b7d3 | 2017-11-24 16:19:41 +0100 | [diff] [blame] | 215 | # Subcommand for configuring a build directory |
| 216 | configure = subcommands.add_parser( |
| 217 | 'configure', |
| 218 | help="Run CMake in the given build directory.") |
| 219 | configure.add_argument( |
Diana Picus | 9f75686 | 2017-12-20 10:35:08 +0100 | [diff] [blame] | 220 | '--source-dir', |
| 221 | dest='sources', |
| 222 | required=True, |
| 223 | help="Path to the sources directory. It should contain an LLVM worktree.") |
| 224 | configure.add_argument( |
Diana Picus | 052b7d3 | 2017-11-24 16:19:41 +0100 | [diff] [blame] | 225 | '--build-dir', |
| 226 | dest='build', |
| 227 | required=True, |
| 228 | help="Path to the build directory. It will be created if it does not exist") |
| 229 | configure.add_argument( |
| 230 | '--cmake-generator', |
| 231 | dest='generator', |
| 232 | default='Ninja', |
| 233 | help="CMake generator to use (default is Ninja).") |
| 234 | configure.add_argument( |
| 235 | '--cmake-def', |
| 236 | dest='defs', |
| 237 | metavar='VAR=VALUE', |
| 238 | default=[], |
| 239 | action='append', |
| 240 | # We add the -D in front of the variable ourselves because the argument |
| 241 | # parsing gets confused otherwise (and quoting doesn't help). |
| 242 | help="Additional CMake definitions, e.g. CMAKE_BUILD_TYPE=Release." |
| 243 | "May be passed several times. The -D is added automatically.") |
| 244 | configure.add_argument( |
| 245 | '-n', '--dry-run', |
| 246 | dest='dry', |
| 247 | action='store_true', |
| 248 | default=False, |
| 249 | help="Print the CMake command instead of executing it.") |
| 250 | configure.set_defaults(run_command=configure_build) |
| 251 | |
Diana Picus | 37126b8 | 2018-01-19 16:14:26 +0100 | [diff] [blame] | 252 | # Subcommand for building a target |
| 253 | build = subcommands.add_parser( |
| 254 | 'build', |
| 255 | help="Run a build command in the given directory." |
| 256 | "The build command can be either a 'ninja' or a 'make' command, depending " |
| 257 | "on what the build directory contains. First, we look for a 'build.ninja' " |
| 258 | "file. If that is not found, we look for a 'Makefile'. If that is not " |
| 259 | "found either, the script fails.") |
| 260 | build.add_argument( |
| 261 | '--build-dir', |
| 262 | dest='build', |
| 263 | required=True, |
| 264 | help="Path to the build directory. It must have already been configured.") |
| 265 | build.add_argument( |
| 266 | '-n', '--dry-run', |
| 267 | dest='dry', |
| 268 | action='store_true', |
| 269 | default=False, |
| 270 | help="Print the build command instead of executing it.") |
| 271 | build.add_argument( |
| 272 | '--build-flag', |
| 273 | dest='flags', |
| 274 | metavar='FLAG', |
| 275 | default=[], |
| 276 | action='append', |
| 277 | help="Additional flags for the build command (e.g. targets to build). " |
| 278 | "May be passed several times. If your flag starts with a '-', use " |
| 279 | "'--build-flag=-FLAG' to pass it.") |
| 280 | build.set_defaults(run_command=run_build) |
| 281 | |
Diana Picus | b368cb6 | 2018-01-23 16:41:59 +0100 | [diff] [blame^] | 282 | # Subcommand for running the test-suite |
| 283 | runTestSuite = subcommands.add_parser( |
| 284 | 'run-test-suite', |
| 285 | help="Run the test-suite in the given sandbox.") |
| 286 | runTestSuite.add_argument( |
| 287 | '--sandbox', |
| 288 | required=True, |
| 289 | help="Path to the sandbox. It must point to a virtualenv with a LNT setup.") |
| 290 | runTestSuite.add_argument( |
| 291 | '--test-suite', |
| 292 | dest="testsuite", |
| 293 | required=True, |
| 294 | help="Path to the test-suite repo.") |
| 295 | runTestSuite.add_argument( |
| 296 | '--use-lit', |
| 297 | dest="lit", |
| 298 | required=True, |
| 299 | help="Path to llvm-lit.") |
| 300 | runTestSuite.add_argument( |
| 301 | '--lnt-flag', |
| 302 | dest='flags', |
| 303 | metavar='FLAG', |
| 304 | default=[], |
| 305 | action='append', |
| 306 | help="Additional flags to be passed to LNT when running the test-suite." |
| 307 | "May be passed several times. If your flag starts with a '-', use " |
| 308 | "'--lnt-flag=-FLAG' to pass it.") |
| 309 | runTestSuite.add_argument( |
| 310 | # We can pass --cc through the --lnt-flag interface, but we generally won't |
| 311 | # want to test the system compiler, so force the user to be specific. |
| 312 | '--cc', |
| 313 | required=True, |
| 314 | help="The path to the C compiler that we're testing.") |
| 315 | runTestSuite.add_argument( |
| 316 | # For symmetry, we also provide a --cxx argument, but this one isn't |
| 317 | # required since LNT tries to guess it based on the value of --cc. |
| 318 | '--cxx', |
| 319 | required=False, |
| 320 | help="The path to the C++ compiler that we're testing.") |
| 321 | runTestSuite.add_argument( |
| 322 | '-n', '--dry-run', |
| 323 | dest='dry', |
| 324 | action='store_true', |
| 325 | default=False, |
| 326 | help="Print the commands instead of executing them.") |
| 327 | runTestSuite.set_defaults(run_command=run_the_test_suite) |
| 328 | |
Diana Picus | 3b2ef82 | 2016-10-13 16:53:18 +0300 | [diff] [blame] | 329 | args = options.parse_args() |
Diana Picus | adb07c4 | 2017-11-22 16:12:57 +0100 | [diff] [blame] | 330 | if args.subcommand == "projects" and args.add and not args.repos: |
| 331 | projs.error( |
| 332 | "When adding a subproject you must also pass the --repos argument") |
Diana Picus | 3b2ef82 | 2016-10-13 16:53:18 +0300 | [diff] [blame] | 333 | args.run_command(args) |