blob: be4628f7d8d190ee5be68d571889c944a8677227 [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 Picus37126b82018-01-19 16:14:26 +01007from modules.llvm import build_llvm
Diana Picus052b7d32017-11-24 16:19:41 +01008from modules.llvm import LLVMBuildConfig
Diana Picus95226d42017-11-01 13:16:54 +01009from modules.llvm import LLVMSubproject
10from modules.llvm import LLVMSourceConfig
Diana Picusb368cb62018-01-23 16:41:59 +010011from modules.llvm import run_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 Picus3b2ef822016-10-13 16:53:18 +030054 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 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 Picusefc7bda2017-06-09 19:14:08 +020081 config = LLVMSourceConfig(proj, llvm_worktree_root)
82
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 Picus052b7d32017-11-24 16:19:41 +0100101 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 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:
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 Picusb368cb62018-01-23 16:41:59 +0100136def 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 Picus3b2ef822016-10-13 16:53:18 +0300153##########################################################################
154# Command line parsing #
155##########################################################################
156
157# If we decide we want shorthands for the subprojects, we can append to this
158# list
Diana Picusb4307602017-04-05 19:48:39 +0200159valid_subprojects = list(LLVMSubproject.get_all_subprojects().keys())
Diana Picus3b2ef822016-10-13 16:53:18 +0300160
161options = ArgumentParser(formatter_class=RawTextHelpFormatter)
Diana Picusadb07c42017-11-22 16:12:57 +0100162subcommands = options.add_subparsers(dest="subcommand")
Diana Picus3b2ef822016-10-13 16:53:18 +0300163
164# Subcommand for adding / removing subprojects
Diana Picus36317e82017-10-31 15:35:24 +0100165projs = 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 Picus3b2ef822016-10-13 16:53:18 +0300172projs.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.
179projs.add_argument(
180 '-a', '--add',
181 nargs='+',
182 choices=valid_subprojects,
183 metavar='subproject',
Diana Picus36317e82017-10-31 15:35:24 +0100184 help="Enable given subprojects. Valid values are:\n\t{}\n".format(
Diana Picus3b2ef822016-10-13 16:53:18 +0300185 "\n\t".join(valid_subprojects)))
186projs.add_argument(
187 '-r', '--remove',
188 nargs='+',
189 choices=valid_subprojects,
190 metavar='subproject',
Diana Picus36317e82017-10-31 15:35:24 +0100191 help="Disable given subprojects.")
Diana Picusadb07c42017-11-22 16:12:57 +0100192projs.add_argument(
193 '--repos',
194 help="Path to the directory containing the repositories for all LLVM "
195 "subprojects.")
Diana Picus9f756862017-12-20 10:35:08 +0100196projs.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 Picus3b2ef822016-10-13 16:53:18 +0300202
Diana Picusefc7bda2017-06-09 19:14:08 +0200203# Subcommand for pushing the current branch to origin
204push = subcommands.add_parser(
205 "push",
Diana Picus36317e82017-10-31 15:35:24 +0100206 help="Push current branch to origin linaro-local/<user>/<branch>, "
207 "for all enabled subprojects.")
Diana Picus95226d42017-11-01 13:16:54 +0100208push.set_defaults(run_command=push_current_branch)
Diana Picus9f756862017-12-20 10:35:08 +0100209push.add_argument(
210 '--source-dir',
211 dest='sources',
212 required=True,
213 help="Path to the directory containing the LLVM worktree.")
Diana Picusefc7bda2017-06-09 19:14:08 +0200214
Diana Picus052b7d32017-11-24 16:19:41 +0100215# Subcommand for configuring a build directory
216configure = subcommands.add_parser(
217 'configure',
218 help="Run CMake in the given build directory.")
219configure.add_argument(
Diana Picus9f756862017-12-20 10:35:08 +0100220 '--source-dir',
221 dest='sources',
222 required=True,
223 help="Path to the sources directory. It should contain an LLVM worktree.")
224configure.add_argument(
Diana Picus052b7d32017-11-24 16:19:41 +0100225 '--build-dir',
226 dest='build',
227 required=True,
228 help="Path to the build directory. It will be created if it does not exist")
229configure.add_argument(
230 '--cmake-generator',
231 dest='generator',
232 default='Ninja',
233 help="CMake generator to use (default is Ninja).")
234configure.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.")
244configure.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.")
250configure.set_defaults(run_command=configure_build)
251
Diana Picus37126b82018-01-19 16:14:26 +0100252# Subcommand for building a target
253build = 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.")
260build.add_argument(
261 '--build-dir',
262 dest='build',
263 required=True,
264 help="Path to the build directory. It must have already been configured.")
265build.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.")
271build.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.")
280build.set_defaults(run_command=run_build)
281
Diana Picusb368cb62018-01-23 16:41:59 +0100282# Subcommand for running the test-suite
283runTestSuite = subcommands.add_parser(
284 'run-test-suite',
285 help="Run the test-suite in the given sandbox.")
286runTestSuite.add_argument(
287 '--sandbox',
288 required=True,
289 help="Path to the sandbox. It must point to a virtualenv with a LNT setup.")
290runTestSuite.add_argument(
291 '--test-suite',
292 dest="testsuite",
293 required=True,
294 help="Path to the test-suite repo.")
295runTestSuite.add_argument(
296 '--use-lit',
297 dest="lit",
298 required=True,
299 help="Path to llvm-lit.")
300runTestSuite.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.")
309runTestSuite.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.")
315runTestSuite.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.")
321runTestSuite.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.")
327runTestSuite.set_defaults(run_command=run_the_test_suite)
328
Diana Picus3b2ef822016-10-13 16:53:18 +0300329args = options.parse_args()
Diana Picusadb07c42017-11-22 16:12:57 +0100330if 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 Picus3b2ef822016-10-13 16:53:18 +0300333args.run_command(args)