Channagoud Kadabi | 1a82c2b | 2016-07-01 12:28:20 -0700 | [diff] [blame] | 1 | #! /usr/bin/env python2 |
| 2 | |
Satya Durga Srinivasu Prabhala | 41259a4 | 2017-05-22 17:00:49 -0700 | [diff] [blame] | 3 | # Copyright (c) 2009-2015, 2017, 2019, The Linux Foundation. All rights reserved. |
Channagoud Kadabi | 1a82c2b | 2016-07-01 12:28:20 -0700 | [diff] [blame] | 4 | # |
| 5 | # Redistribution and use in source and binary forms, with or without |
| 6 | # modification, are permitted provided that the following conditions are met: |
| 7 | # * Redistributions of source code must retain the above copyright |
| 8 | # notice, this list of conditions and the following disclaimer. |
| 9 | # * Redistributions in binary form must reproduce the above copyright |
| 10 | # notice, this list of conditions and the following disclaimer in the |
| 11 | # documentation and/or other materials provided with the distribution. |
| 12 | # * Neither the name of The Linux Foundation nor |
| 13 | # the names of its contributors may be used to endorse or promote |
| 14 | # products derived from this software without specific prior written |
| 15 | # permission. |
| 16 | # |
| 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| 18 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 19 | # IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| 20 | # NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
| 21 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| 22 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| 23 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
| 24 | # OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| 25 | # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
| 26 | # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
| 27 | # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 28 | |
| 29 | # Build the kernel for all targets using the Android build environment. |
| 30 | |
| 31 | from collections import namedtuple |
| 32 | import glob |
| 33 | from optparse import OptionParser |
| 34 | import os |
| 35 | import re |
| 36 | import shutil |
| 37 | import subprocess |
| 38 | import sys |
| 39 | import threading |
| 40 | import Queue |
| 41 | |
| 42 | version = 'build-all.py, version 1.99' |
| 43 | |
| 44 | build_dir = '../all-kernels' |
| 45 | make_command = ["vmlinux", "modules", "dtbs"] |
| 46 | all_options = {} |
| 47 | compile64 = os.environ.get('CROSS_COMPILE64') |
Satya Durga Srinivasu Prabhala | 41259a4 | 2017-05-22 17:00:49 -0700 | [diff] [blame] | 48 | clang_bin = os.environ.get('CLANG_BIN') |
Channagoud Kadabi | 1a82c2b | 2016-07-01 12:28:20 -0700 | [diff] [blame] | 49 | |
| 50 | def error(msg): |
| 51 | sys.stderr.write("error: %s\n" % msg) |
| 52 | |
| 53 | def fail(msg): |
| 54 | """Fail with a user-printed message""" |
| 55 | error(msg) |
| 56 | sys.exit(1) |
| 57 | |
| 58 | if not os.environ.get('CROSS_COMPILE'): |
| 59 | fail("CROSS_COMPILE must be set in the environment") |
| 60 | |
| 61 | def check_kernel(): |
| 62 | """Ensure that PWD is a kernel directory""" |
Bryan Huntsman | 3e408fe | 2018-06-04 15:11:10 -0700 | [diff] [blame] | 63 | if not os.path.isfile('MAINTAINERS'): |
| 64 | fail("This doesn't seem to be a kernel dir") |
Channagoud Kadabi | 1a82c2b | 2016-07-01 12:28:20 -0700 | [diff] [blame] | 65 | |
| 66 | def check_build(): |
| 67 | """Ensure that the build directory is present.""" |
| 68 | if not os.path.isdir(build_dir): |
| 69 | try: |
| 70 | os.makedirs(build_dir) |
| 71 | except OSError as exc: |
| 72 | if exc.errno == errno.EEXIST: |
| 73 | pass |
| 74 | else: |
| 75 | raise |
| 76 | |
| 77 | failed_targets = [] |
| 78 | |
| 79 | BuildResult = namedtuple('BuildResult', ['status', 'messages']) |
| 80 | |
| 81 | class BuildSequence(namedtuple('BuildSequence', ['log_name', 'short_name', 'steps'])): |
| 82 | |
| 83 | def set_width(self, width): |
| 84 | self.width = width |
| 85 | |
| 86 | def __enter__(self): |
| 87 | self.log = open(self.log_name, 'w') |
| 88 | def __exit__(self, type, value, traceback): |
| 89 | self.log.close() |
| 90 | |
| 91 | def run(self): |
| 92 | self.status = None |
| 93 | messages = ["Building: " + self.short_name] |
| 94 | def printer(line): |
| 95 | text = "[%-*s] %s" % (self.width, self.short_name, line) |
| 96 | messages.append(text) |
| 97 | self.log.write(text) |
| 98 | self.log.write('\n') |
| 99 | for step in self.steps: |
| 100 | st = step.run(printer) |
| 101 | if st: |
| 102 | self.status = BuildResult(self.short_name, messages) |
| 103 | break |
| 104 | if not self.status: |
| 105 | self.status = BuildResult(None, messages) |
| 106 | |
| 107 | class BuildTracker: |
| 108 | """Manages all of the steps necessary to perform a build. The |
| 109 | build consists of one or more sequences of steps. The different |
| 110 | sequences can be processed independently, while the steps within a |
| 111 | sequence must be done in order.""" |
| 112 | |
| 113 | def __init__(self, parallel_builds): |
| 114 | self.sequence = [] |
| 115 | self.lock = threading.Lock() |
| 116 | self.parallel_builds = parallel_builds |
| 117 | |
| 118 | def add_sequence(self, log_name, short_name, steps): |
| 119 | self.sequence.append(BuildSequence(log_name, short_name, steps)) |
| 120 | |
| 121 | def longest_name(self): |
| 122 | longest = 0 |
| 123 | for seq in self.sequence: |
| 124 | longest = max(longest, len(seq.short_name)) |
| 125 | return longest |
| 126 | |
| 127 | def __repr__(self): |
| 128 | return "BuildTracker(%s)" % self.sequence |
| 129 | |
| 130 | def run_child(self, seq): |
| 131 | seq.set_width(self.longest) |
| 132 | tok = self.build_tokens.get() |
| 133 | with self.lock: |
| 134 | print "Building:", seq.short_name |
| 135 | with seq: |
| 136 | seq.run() |
| 137 | self.results.put(seq.status) |
| 138 | self.build_tokens.put(tok) |
| 139 | |
| 140 | def run(self): |
| 141 | self.longest = self.longest_name() |
| 142 | self.results = Queue.Queue() |
| 143 | children = [] |
| 144 | errors = [] |
| 145 | self.build_tokens = Queue.Queue() |
| 146 | nthreads = self.parallel_builds |
| 147 | print "Building with", nthreads, "threads" |
| 148 | for i in range(nthreads): |
| 149 | self.build_tokens.put(True) |
| 150 | for seq in self.sequence: |
| 151 | child = threading.Thread(target=self.run_child, args=[seq]) |
| 152 | children.append(child) |
| 153 | child.start() |
| 154 | for child in children: |
| 155 | stats = self.results.get() |
| 156 | if all_options.verbose: |
| 157 | with self.lock: |
| 158 | for line in stats.messages: |
| 159 | print line |
| 160 | sys.stdout.flush() |
| 161 | if stats.status: |
| 162 | errors.append(stats.status) |
| 163 | for child in children: |
| 164 | child.join() |
| 165 | if errors: |
| 166 | fail("\n ".join(["Failed targets:"] + errors)) |
| 167 | |
| 168 | class PrintStep: |
| 169 | """A step that just prints a message""" |
| 170 | def __init__(self, message): |
| 171 | self.message = message |
| 172 | |
| 173 | def run(self, outp): |
| 174 | outp(self.message) |
| 175 | |
| 176 | class MkdirStep: |
| 177 | """A step that makes a directory""" |
| 178 | def __init__(self, direc): |
| 179 | self.direc = direc |
| 180 | |
| 181 | def run(self, outp): |
| 182 | outp("mkdir %s" % self.direc) |
| 183 | os.mkdir(self.direc) |
| 184 | |
| 185 | class RmtreeStep: |
| 186 | def __init__(self, direc): |
| 187 | self.direc = direc |
| 188 | |
| 189 | def run(self, outp): |
| 190 | outp("rmtree %s" % self.direc) |
| 191 | shutil.rmtree(self.direc, ignore_errors=True) |
| 192 | |
| 193 | class CopyfileStep: |
| 194 | def __init__(self, src, dest): |
| 195 | self.src = src |
| 196 | self.dest = dest |
| 197 | |
| 198 | def run(self, outp): |
| 199 | outp("cp %s %s" % (self.src, self.dest)) |
| 200 | shutil.copyfile(self.src, self.dest) |
| 201 | |
| 202 | class ExecStep: |
| 203 | def __init__(self, cmd, **kwargs): |
| 204 | self.cmd = cmd |
| 205 | self.kwargs = kwargs |
| 206 | |
| 207 | def run(self, outp): |
| 208 | outp("exec: %s" % (" ".join(self.cmd),)) |
| 209 | with open('/dev/null', 'r') as devnull: |
| 210 | proc = subprocess.Popen(self.cmd, stdin=devnull, |
| 211 | stdout=subprocess.PIPE, |
| 212 | stderr=subprocess.STDOUT, |
| 213 | **self.kwargs) |
| 214 | stdout = proc.stdout |
| 215 | while True: |
| 216 | line = stdout.readline() |
| 217 | if not line: |
| 218 | break |
| 219 | line = line.rstrip('\n') |
| 220 | outp(line) |
| 221 | result = proc.wait() |
| 222 | if result != 0: |
| 223 | return ('error', result) |
| 224 | else: |
| 225 | return None |
| 226 | |
| 227 | class Builder(): |
| 228 | |
| 229 | def __init__(self, name, defconfig): |
| 230 | self.name = name |
| 231 | self.defconfig = defconfig |
| 232 | |
Bryan Huntsman | 6915ec2 | 2018-06-04 14:18:41 -0700 | [diff] [blame] | 233 | self.confname = re.sub('arch/arm[64]*/configs/', '', self.defconfig) |
Channagoud Kadabi | 1a82c2b | 2016-07-01 12:28:20 -0700 | [diff] [blame] | 234 | |
| 235 | # Determine if this is a 64-bit target based on the location |
| 236 | # of the defconfig. |
| 237 | self.make_env = os.environ.copy() |
| 238 | if "/arm64/" in defconfig: |
| 239 | if compile64: |
| 240 | self.make_env['CROSS_COMPILE'] = compile64 |
| 241 | else: |
| 242 | fail("Attempting to build 64-bit, without setting CROSS_COMPILE64") |
| 243 | self.make_env['ARCH'] = 'arm64' |
| 244 | else: |
| 245 | self.make_env['ARCH'] = 'arm' |
| 246 | self.make_env['KCONFIG_NOTIMESTAMP'] = 'true' |
| 247 | self.log_name = "%s/log-%s.log" % (build_dir, self.name) |
| 248 | |
| 249 | def build(self): |
| 250 | steps = [] |
| 251 | dest_dir = os.path.join(build_dir, self.name) |
| 252 | log_name = "%s/log-%s.log" % (build_dir, self.name) |
| 253 | steps.append(PrintStep('Building %s in %s log %s' % |
| 254 | (self.name, dest_dir, log_name))) |
| 255 | if not os.path.isdir(dest_dir): |
| 256 | steps.append(MkdirStep(dest_dir)) |
| 257 | defconfig = self.defconfig |
| 258 | dotconfig = '%s/.config' % dest_dir |
| 259 | savedefconfig = '%s/defconfig' % dest_dir |
| 260 | |
| 261 | staging_dir = 'install_staging' |
| 262 | modi_dir = '%s' % staging_dir |
| 263 | hdri_dir = '%s/usr' % staging_dir |
| 264 | steps.append(RmtreeStep(os.path.join(dest_dir, staging_dir))) |
| 265 | |
| 266 | steps.append(ExecStep(['make', 'O=%s' % dest_dir, |
| 267 | self.confname], env=self.make_env)) |
| 268 | |
Bryan Huntsman | 895fdcc | 2018-05-25 13:05:57 -0700 | [diff] [blame] | 269 | # Build targets can be dependent upon the completion of |
| 270 | # previous build targets, so build them one at a time. |
Rahul Shahare | 850650f | 2020-01-28 10:39:05 +0530 | [diff] [blame] | 271 | cmd_line = ['make', |
| 272 | 'INSTALL_HDR_PATH=%s' % hdri_dir, |
| 273 | 'INSTALL_MOD_PATH=%s' % modi_dir, |
| 274 | 'O=%s' % dest_dir, |
| 275 | 'REAL_CC=%s' % clang_bin] |
Bryan Huntsman | 895fdcc | 2018-05-25 13:05:57 -0700 | [diff] [blame] | 276 | build_targets = [] |
| 277 | for c in make_command: |
| 278 | if re.match(r'^-{1,2}\w', c): |
| 279 | cmd_line.append(c) |
Satya Durga Srinivasu Prabhala | 41259a4 | 2017-05-22 17:00:49 -0700 | [diff] [blame] | 280 | else: |
Bryan Huntsman | 895fdcc | 2018-05-25 13:05:57 -0700 | [diff] [blame] | 281 | build_targets.append(c) |
| 282 | for t in build_targets: |
| 283 | steps.append(ExecStep(cmd_line + [t], env=self.make_env)) |
Channagoud Kadabi | 1a82c2b | 2016-07-01 12:28:20 -0700 | [diff] [blame] | 284 | |
| 285 | return steps |
| 286 | |
Channagoud Kadabi | 1a82c2b | 2016-07-01 12:28:20 -0700 | [diff] [blame] | 287 | def scan_configs(): |
| 288 | """Get the full list of defconfigs appropriate for this tree.""" |
| 289 | names = [] |
Bryan Huntsman | 6915ec2 | 2018-06-04 14:18:41 -0700 | [diff] [blame] | 290 | for defconfig in glob.glob('arch/arm*/configs/vendor/*_defconfig'): |
| 291 | target = os.path.basename(defconfig)[:-10] |
| 292 | name = target + "-llvm" |
| 293 | if 'arch/arm64' in defconfig: |
| 294 | name = name + "-64" |
| 295 | names.append(Builder(name, defconfig)) |
| 296 | |
Channagoud Kadabi | 1a82c2b | 2016-07-01 12:28:20 -0700 | [diff] [blame] | 297 | return names |
| 298 | |
| 299 | def build_many(targets): |
| 300 | print "Building %d target(s)" % len(targets) |
| 301 | |
| 302 | # To try and make up for the link phase being serial, try to do |
| 303 | # two full builds in parallel. Don't do too many because lots of |
| 304 | # parallel builds tends to use up available memory rather quickly. |
| 305 | parallel = 2 |
| 306 | if all_options.jobs and all_options.jobs > 1: |
| 307 | j = max(all_options.jobs / parallel, 2) |
| 308 | make_command.append("-j" + str(j)) |
| 309 | |
| 310 | tracker = BuildTracker(parallel) |
| 311 | for target in targets: |
Channagoud Kadabi | 1a82c2b | 2016-07-01 12:28:20 -0700 | [diff] [blame] | 312 | steps = target.build() |
| 313 | tracker.add_sequence(target.log_name, target.name, steps) |
| 314 | tracker.run() |
| 315 | |
| 316 | def main(): |
| 317 | global make_command |
| 318 | |
| 319 | check_kernel() |
| 320 | check_build() |
| 321 | |
| 322 | configs = scan_configs() |
| 323 | |
| 324 | usage = (""" |
| 325 | %prog [options] all -- Build all targets |
| 326 | %prog [options] target target ... -- List specific targets |
Bryan Huntsman | 895fdcc | 2018-05-25 13:05:57 -0700 | [diff] [blame] | 327 | """) |
Channagoud Kadabi | 1a82c2b | 2016-07-01 12:28:20 -0700 | [diff] [blame] | 328 | parser = OptionParser(usage=usage, version=version) |
Channagoud Kadabi | 1a82c2b | 2016-07-01 12:28:20 -0700 | [diff] [blame] | 329 | parser.add_option('--list', action='store_true', |
| 330 | dest='list', |
| 331 | help='List available targets') |
| 332 | parser.add_option('-v', '--verbose', action='store_true', |
| 333 | dest='verbose', |
| 334 | help='Output to stdout in addition to log file') |
Channagoud Kadabi | 1a82c2b | 2016-07-01 12:28:20 -0700 | [diff] [blame] | 335 | parser.add_option('-j', '--jobs', type='int', dest="jobs", |
| 336 | help="Number of simultaneous jobs") |
| 337 | parser.add_option('-l', '--load-average', type='int', |
| 338 | dest='load_average', |
| 339 | help="Don't start multiple jobs unless load is below LOAD_AVERAGE") |
| 340 | parser.add_option('-k', '--keep-going', action='store_true', |
| 341 | dest='keep_going', default=False, |
| 342 | help="Keep building other targets if a target fails") |
| 343 | parser.add_option('-m', '--make-target', action='append', |
| 344 | help='Build the indicated make target (default: %s)' % |
| 345 | ' '.join(make_command)) |
| 346 | |
| 347 | (options, args) = parser.parse_args() |
| 348 | global all_options |
| 349 | all_options = options |
| 350 | |
| 351 | if options.list: |
| 352 | print "Available targets:" |
| 353 | for target in configs: |
| 354 | print " %s" % target.name |
| 355 | sys.exit(0) |
| 356 | |
Bryan Huntsman | 895fdcc | 2018-05-25 13:05:57 -0700 | [diff] [blame] | 357 | if options.make_target: |
Channagoud Kadabi | 1a82c2b | 2016-07-01 12:28:20 -0700 | [diff] [blame] | 358 | make_command = options.make_target |
| 359 | |
| 360 | if args == ['all']: |
| 361 | build_many(configs) |
Channagoud Kadabi | 1a82c2b | 2016-07-01 12:28:20 -0700 | [diff] [blame] | 362 | elif len(args) > 0: |
| 363 | all_configs = {} |
| 364 | for t in configs: |
| 365 | all_configs[t.name] = t |
| 366 | targets = [] |
| 367 | for t in args: |
| 368 | if t not in all_configs: |
| 369 | parser.error("Target '%s' not one of %s" % (t, all_configs.keys())) |
| 370 | targets.append(all_configs[t]) |
| 371 | build_many(targets) |
| 372 | else: |
| 373 | parser.error("Must specify a target to build, or 'all'") |
| 374 | |
| 375 | if __name__ == "__main__": |
| 376 | main() |