blob: 6184bcad18bf86d9e30938a44b3ac07df4de4c93 [file] [log] [blame]
Diana Picus3b2ef822016-10-13 16:53:18 +03001"""This is the main tool for handling llvm builds, bisects etc."""
2
3import os
Diana Picusadb07c42017-11-22 16:12:57 +01004from sys import argv
Diana Picus3b2ef822016-10-13 16:53:18 +03005from sys import exit
6
Diana Picus052b7d32017-11-24 16:19:41 +01007from modules.llvm import LLVMBuildConfig
Diana Picus95226d42017-11-01 13:16:54 +01008from modules.llvm import LLVMSubproject
9from modules.llvm import LLVMSourceConfig
Diana Picusb368cb62018-01-23 16:41:59 +010010from modules.llvm import run_test_suite
Diana Picusf73abbf2018-01-26 07:06:20 +010011from modules.llvm import setup_test_suite
Diana Picus052b7d32017-11-24 16:19:41 +010012from modules.utils import CommandPrinter
13from modules.utils import CommandRunner
Diana Picus5ad55422017-12-14 17:57:10 +010014from modules.utils import get_remote_branch
15from modules.utils import push_branch
Diana Picus95226d42017-11-01 13:16:54 +010016
Diana Picus052b7d32017-11-24 16:19:41 +010017from linaropy.cd import cd
Diana Picus3b2ef822016-10-13 16:53:18 +030018from linaropy.git.clone import Clone
19from linaropy.proj import Proj
20
21from argparse import Action, ArgumentParser, RawTextHelpFormatter
Diana Picusefc7bda2017-06-09 19:14:08 +020022from functools import partial
Diana Picus3b2ef822016-10-13 16:53:18 +030023
24
25def die(message, config_to_dump=None):
26 """Print an error message and exit."""
Diana Picusb4307602017-04-05 19:48:39 +020027 print(message)
Diana Picus3b2ef822016-10-13 16:53:18 +030028
29 if config_to_dump is not None:
30 dump_config(config_to_dump)
31
32 exit(1)
33
Diana Picus3d1a3012017-03-14 17:38:32 +010034
Diana Picus3b2ef822016-10-13 16:53:18 +030035def dump_config(config):
36 """Dump the list of projects that are enabled in the given config."""
37
Diana Picusb4307602017-04-05 19:48:39 +020038 print("Projects linked:")
Diana Picus3b2ef822016-10-13 16:53:18 +030039 enabled = config.get_enabled_subprojects()
40 if not enabled:
Diana Picusb4307602017-04-05 19:48:39 +020041 print("none")
Diana Picus3b2ef822016-10-13 16:53:18 +030042 else:
43 for subproj in sorted(enabled):
Diana Picusb4307602017-04-05 19:48:39 +020044 print(" + {}".format(subproj))
Diana Picus3b2ef822016-10-13 16:53:18 +030045
46
47def projects(args):
48 """Add/remove subprojects based on the values in args."""
49
50 proj = Proj()
Diana Picus3d1a3012017-03-14 17:38:32 +010051
Diana Picus9f756862017-12-20 10:35:08 +010052 llvm_worktree_root = args.sources
Diana Picus81089db2017-05-05 22:26:49 +020053 llvm_repos_root = args.repos
Diana Picusb1dbcba2018-02-07 01:33:17 +010054 config = LLVMSourceConfig(proj, llvm_worktree_root, dry=False)
Diana Picus3b2ef822016-10-13 16:53:18 +030055
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 Picusefc7bda2017-06-09 19:14:08 +020074
Diana Picus95226d42017-11-01 13:16:54 +010075def push_current_branch(args):
Diana Picusefc7bda2017-06-09 19:14:08 +020076 """Push current branch to origin."""
77
78 proj = Proj()
79
Diana Picus9f756862017-12-20 10:35:08 +010080 llvm_worktree_root = args.sources
Diana Picusb1dbcba2018-02-07 01:33:17 +010081 config = LLVMSourceConfig(proj, llvm_worktree_root, dry=False)
Diana Picusefc7bda2017-06-09 19:14:08 +020082
Diana Picus95226d42017-11-01 13:16:54 +010083 llvm_worktree = Clone(proj, llvm_worktree_root)
84 local_branch = llvm_worktree.getbranch()
Diana Picusefc7bda2017-06-09 19:14:08 +020085
86 try:
Diana Picus95226d42017-11-01 13:16:54 +010087 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 Picusefc7bda2017-06-09 19:14:08 +020092 die("Failed to push branch because: " + str(exc) + str(exc.__cause__))
93
Diana Picus95226d42017-11-01 13:16:54 +010094
Diana Picus052b7d32017-11-24 16:19:41 +010095def configure_build(args):
96 """Configure a given build directory."""
97
98 proj = Proj()
99
Diana Picus9f756862017-12-20 10:35:08 +0100100 llvm_worktree_root = args.sources
Diana Picusb1dbcba2018-02-07 01:33:17 +0100101 sourceConfig = LLVMSourceConfig(proj, llvm_worktree_root, args.dry)
Diana Picus052b7d32017-11-24 16:19:41 +0100102
Diana Picus052b7d32017-11-24 16:19:41 +0100103 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 Picus6b1935f2018-02-07 16:44:11 +0100110 buildConfig = LLVMBuildConfig(sourceConfig, args.build, consumer)
111
112 if args.defs:
113 args.defs = ["-D{}".format(v) for v in args.defs]
114
Diana Picus052b7d32017-11-24 16:19:41 +0100115 try:
Diana Picus6b1935f2018-02-07 16:44:11 +0100116 buildConfig.cmake(args.defs, args.generator)
Diana Picus052b7d32017-11-24 16:19:41 +0100117 except RuntimeError as exc:
118 die("Failed to configure {} because:\n{}".format(args.build, str(exc)))
119
120
Diana Picus37126b82018-01-19 16:14:26 +0100121def 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 Picus6b1935f2018-02-07 16:44:11 +0100131 LLVMBuildConfig(None, args.build, consumer).build(args.flags)
Diana Picus37126b82018-01-19 16:14:26 +0100132 except RuntimeError as exc:
133 die("Failed to build {} because:\n{}".format(args.build, str(exc)))
134
135
Diana Picusf73abbf2018-01-26 07:06:20 +0100136def 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 Picusb368cb62018-01-23 16:41:59 +0100149def 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 Picusb03e5082018-02-05 12:36:49 +0100166
167def build_and_test(args):
168 """
169 Build a set of LLVM subprojects, in one or two stages, with or without a
170 test-suite run.
171 """
172
173 proj = Proj()
174
175 dryRun = args.dry
Diana Picusfcfc6282018-02-14 18:50:24 +0100176 llvmRepos = args.repos
Diana Picusb03e5082018-02-05 12:36:49 +0100177 llvmWorktreeRoot = args.sources
178
179 stage1BuildDir = args.stage1
180 stage2BuildDir = args.stage2
Diana Picusfcfc6282018-02-14 18:50:24 +0100181
182 enableTestSuite = args.enableTestSuite
Diana Picusb03e5082018-02-05 12:36:49 +0100183 sandboxDir = args.sandbox
Diana Picusb03e5082018-02-05 12:36:49 +0100184
185 if dryRun:
186 consumer = CommandPrinter()
187 else:
188 consumer = CommandRunner()
189
190 try:
191 sourceConfig = LLVMSourceConfig(proj, llvmWorktreeRoot, args.dry)
192
193 if not dryRun and not os.path.exists(stage1BuildDir):
194 os.makedirs(stage1BuildDir)
195
196 buildConfig1 = LLVMBuildConfig(sourceConfig, stage1BuildDir, consumer)
197 buildConfig1.cmake([], "Ninja")
198 buildConfig1.build()
199 testedBuildDir = stage1BuildDir
200
201 if stage2BuildDir is not None:
202 if not dryRun and not os.path.exists(stage2BuildDir):
203 os.makedirs(stage2BuildDir)
204
205 buildConfig2 = LLVMBuildConfig(sourceConfig, stage2BuildDir,
206 consumer)
207
208 # TODO: Make sure clang is actually built in this config (preferably
209 # before reaching this point)
210 buildConfig2.cmake(
211 [
212 "-DCMAKE_C_COMPILER={}/bin/clang".format(stage1BuildDir),
213 "-DCMAKE_CXX_COMPILER={}/bin/clang++".format(stage1BuildDir)],
214 "Ninja")
215 buildConfig2.build()
216 testedBuildDir = stage2BuildDir
217
Diana Picusfcfc6282018-02-14 18:50:24 +0100218 if enableTestSuite:
219 testSuiteDir = os.path.join(llvmRepos, "test-suite")
220 lntDir = os.path.join(llvmRepos, "lnt")
221
Diana Picusb03e5082018-02-05 12:36:49 +0100222 setup_test_suite(consumer, sandboxDir, lntDir)
223
224 # TODO: Make sure clang is actually built in this config (preferably
225 # before reaching this point)
226 lit = os.path.join(testedBuildDir, "bin", "llvm-lit")
227 clang = os.path.join(testedBuildDir, "bin", "clang")
228 run_test_suite(consumer, sandboxDir, testSuiteDir, lit,
229 ["--cc={}".format(clang)])
230
231 except RuntimeError as exc:
232 die("Failed because:\n{}".format(str(exc)))
233
234
Diana Picus3b2ef822016-10-13 16:53:18 +0300235##########################################################################
236# Command line parsing #
237##########################################################################
238
239# If we decide we want shorthands for the subprojects, we can append to this
240# list
Diana Picusb4307602017-04-05 19:48:39 +0200241valid_subprojects = list(LLVMSubproject.get_all_subprojects().keys())
Diana Picus3b2ef822016-10-13 16:53:18 +0300242
243options = ArgumentParser(formatter_class=RawTextHelpFormatter)
Diana Picusadb07c42017-11-22 16:12:57 +0100244subcommands = options.add_subparsers(dest="subcommand")
Diana Picus3b2ef822016-10-13 16:53:18 +0300245
246# Subcommand for adding / removing subprojects
Diana Picus36317e82017-10-31 15:35:24 +0100247projs = subcommands.add_parser(
248 "projects", help="Add/remove LLVM subprojects.\n"
249 "Adding a subproject will create a worktree for it "
250 "somewhere in the LLVM source tree, on the same git "
251 "branch as LLVM itself.\n"
252 "Removing a subproject will remove the worktree, but "
253 "not the underlying git branch.")
Diana Picus3b2ef822016-10-13 16:53:18 +0300254projs.set_defaults(run_command=projects)
255
256# TODO: Overwriting previous values is not necessarily what users expect (so for
257# instance --add S1 S2 --remove S3 --add S4 would lead to adding only S4). We
258# can do better by using action='append', which would create a list (of lists?
259# or of lists and scalars?) that we can flatten to obtain all the values passed
260# by the user.
261projs.add_argument(
262 '-a', '--add',
263 nargs='+',
264 choices=valid_subprojects,
265 metavar='subproject',
Diana Picus36317e82017-10-31 15:35:24 +0100266 help="Enable given subprojects. Valid values are:\n\t{}\n".format(
Diana Picus3b2ef822016-10-13 16:53:18 +0300267 "\n\t".join(valid_subprojects)))
268projs.add_argument(
269 '-r', '--remove',
270 nargs='+',
271 choices=valid_subprojects,
272 metavar='subproject',
Diana Picus36317e82017-10-31 15:35:24 +0100273 help="Disable given subprojects.")
Diana Picusadb07c42017-11-22 16:12:57 +0100274projs.add_argument(
275 '--repos',
276 help="Path to the directory containing the repositories for all LLVM "
277 "subprojects.")
Diana Picus9f756862017-12-20 10:35:08 +0100278projs.add_argument(
279 '--source-dir',
280 dest='sources',
281 required=True,
282 help="Path to the directory containing the LLVM worktree that we're adding "
283 "or removing subprojects from.")
Diana Picus3b2ef822016-10-13 16:53:18 +0300284
Diana Picusefc7bda2017-06-09 19:14:08 +0200285# Subcommand for pushing the current branch to origin
286push = subcommands.add_parser(
287 "push",
Diana Picus36317e82017-10-31 15:35:24 +0100288 help="Push current branch to origin linaro-local/<user>/<branch>, "
289 "for all enabled subprojects.")
Diana Picus95226d42017-11-01 13:16:54 +0100290push.set_defaults(run_command=push_current_branch)
Diana Picus9f756862017-12-20 10:35:08 +0100291push.add_argument(
292 '--source-dir',
293 dest='sources',
294 required=True,
295 help="Path to the directory containing the LLVM worktree.")
Diana Picusefc7bda2017-06-09 19:14:08 +0200296
Diana Picus052b7d32017-11-24 16:19:41 +0100297# Subcommand for configuring a build directory
298configure = subcommands.add_parser(
299 'configure',
300 help="Run CMake in the given build directory.")
301configure.add_argument(
Diana Picus9f756862017-12-20 10:35:08 +0100302 '--source-dir',
303 dest='sources',
304 required=True,
305 help="Path to the sources directory. It should contain an LLVM worktree.")
306configure.add_argument(
Diana Picus052b7d32017-11-24 16:19:41 +0100307 '--build-dir',
308 dest='build',
309 required=True,
310 help="Path to the build directory. It will be created if it does not exist")
311configure.add_argument(
312 '--cmake-generator',
313 dest='generator',
314 default='Ninja',
315 help="CMake generator to use (default is Ninja).")
316configure.add_argument(
317 '--cmake-def',
318 dest='defs',
319 metavar='VAR=VALUE',
320 default=[],
321 action='append',
322 # We add the -D in front of the variable ourselves because the argument
323 # parsing gets confused otherwise (and quoting doesn't help).
324 help="Additional CMake definitions, e.g. CMAKE_BUILD_TYPE=Release."
325 "May be passed several times. The -D is added automatically.")
326configure.add_argument(
327 '-n', '--dry-run',
328 dest='dry',
329 action='store_true',
330 default=False,
331 help="Print the CMake command instead of executing it.")
332configure.set_defaults(run_command=configure_build)
333
Diana Picus37126b82018-01-19 16:14:26 +0100334# Subcommand for building a target
335build = subcommands.add_parser(
336 'build',
337 help="Run a build command in the given directory."
338 "The build command can be either a 'ninja' or a 'make' command, depending "
339 "on what the build directory contains. First, we look for a 'build.ninja' "
340 "file. If that is not found, we look for a 'Makefile'. If that is not "
341 "found either, the script fails.")
342build.add_argument(
343 '--build-dir',
344 dest='build',
345 required=True,
346 help="Path to the build directory. It must have already been configured.")
347build.add_argument(
348 '-n', '--dry-run',
349 dest='dry',
350 action='store_true',
351 default=False,
352 help="Print the build command instead of executing it.")
353build.add_argument(
354 '--build-flag',
355 dest='flags',
356 metavar='FLAG',
357 default=[],
358 action='append',
359 help="Additional flags for the build command (e.g. targets to build). "
360 "May be passed several times. If your flag starts with a '-', use "
361 "'--build-flag=-FLAG' to pass it.")
362build.set_defaults(run_command=run_build)
363
Diana Picusf73abbf2018-01-26 07:06:20 +0100364# Subcommand for setting up the test-suite
365setupTestSuite = subcommands.add_parser(
366 'setup-test-suite',
367 help="Prepare a sandbox for running the test-suite.")
368setupTestSuite.add_argument(
369 '--sandbox',
370 required=True,
371 help="Path where we should setup the sandbox.")
372setupTestSuite.add_argument(
373 '--lnt',
374 required=True,
375 help="Path to the LNT sources.")
376setupTestSuite.add_argument(
377 '-n', '--dry-run',
378 dest='dry',
379 action='store_true',
380 default=False,
381 help="Print the commands instead of executing them.")
382setupTestSuite.set_defaults(run_command=setup_the_test_suite)
383
Diana Picusb368cb62018-01-23 16:41:59 +0100384# Subcommand for running the test-suite
385runTestSuite = subcommands.add_parser(
386 'run-test-suite',
387 help="Run the test-suite in the given sandbox.")
388runTestSuite.add_argument(
389 '--sandbox',
390 required=True,
391 help="Path to the sandbox. It must point to a virtualenv with a LNT setup.")
392runTestSuite.add_argument(
393 '--test-suite',
394 dest="testsuite",
395 required=True,
396 help="Path to the test-suite repo.")
397runTestSuite.add_argument(
398 '--use-lit',
399 dest="lit",
400 required=True,
401 help="Path to llvm-lit.")
402runTestSuite.add_argument(
403 '--lnt-flag',
404 dest='flags',
405 metavar='FLAG',
406 default=[],
407 action='append',
408 help="Additional flags to be passed to LNT when running the test-suite."
409 "May be passed several times. If your flag starts with a '-', use "
410 "'--lnt-flag=-FLAG' to pass it.")
411runTestSuite.add_argument(
412 # We can pass --cc through the --lnt-flag interface, but we generally won't
413 # want to test the system compiler, so force the user to be specific.
414 '--cc',
415 required=True,
416 help="The path to the C compiler that we're testing.")
417runTestSuite.add_argument(
418 # For symmetry, we also provide a --cxx argument, but this one isn't
419 # required since LNT tries to guess it based on the value of --cc.
420 '--cxx',
421 required=False,
422 help="The path to the C++ compiler that we're testing.")
423runTestSuite.add_argument(
424 '-n', '--dry-run',
425 dest='dry',
426 action='store_true',
427 default=False,
428 help="Print the commands instead of executing them.")
429runTestSuite.set_defaults(run_command=run_the_test_suite)
430
Diana Picusb03e5082018-02-05 12:36:49 +0100431buildAndTest = subcommands.add_parser(
432 'build-and-test', # TODO: This really needs a better name...
433 help="Run complex build scenarios with one or two stages of clang and "
434 "optionally a test-suite run. This should be flexible enough to allow "
435 "us to reproduce any buildbot configuration, but it can obviously be "
436 "used for other purposes as well.")
437buildAndTest.set_defaults(run_command=build_and_test)
438buildAndTest.add_argument(
Diana Picusfcfc6282018-02-14 18:50:24 +0100439 '--repos-dir',
440 dest='repos',
441 required=True,
442 help="Path to the root directory containing the repositories for LLVM and "
443 "the other subprojects.")
444buildAndTest.add_argument(
Diana Picusb03e5082018-02-05 12:36:49 +0100445 '--source-dir',
446 dest='sources',
447 required=True,
448 help="Path to the directory containing the LLVM worktree that we're going "
449 "to build from.")
450buildAndTest.add_argument(
451 '--stage1-build-dir',
452 dest='stage1',
453 required=True,
454 help="Path to the build directory for stage 1.")
455buildAndTest.add_argument(
456 '--stage2-build-dir',
457 dest='stage2',
458 help="Path to the build directory for stage 2.")
459buildAndTest.add_argument(
Diana Picusfcfc6282018-02-14 18:50:24 +0100460 "--enable-test-suite",
461 dest='enableTestSuite',
462 action='store_true',
463 default=False,
464 help="Whether or not to run the test-suite with the last compiler built.")
Diana Picusb03e5082018-02-05 12:36:49 +0100465buildAndTest.add_argument(
466 "--sandbox",
467 help="Path to the sandbox where the test-suite should be setup.")
468buildAndTest.add_argument(
Diana Picusb03e5082018-02-05 12:36:49 +0100469 '-n', '--dry-run',
470 dest='dry',
471 action='store_true',
472 default=False,
473 help="Print the commands instead of executing them.")
474
Diana Picus3b2ef822016-10-13 16:53:18 +0300475args = options.parse_args()
Diana Picusadb07c42017-11-22 16:12:57 +0100476if args.subcommand == "projects" and args.add and not args.repos:
477 projs.error(
478 "When adding a subproject you must also pass the --repos argument")
Diana Picus3b2ef822016-10-13 16:53:18 +0300479args.run_command(args)