1 # SPDX-License-Identifier: GPL-2.0+
2 # Copyright (c) 2014 Google, Inc
21 # Buildman settings file
29 chroot=/home/sjg/c/chroot
30 vboot=VBOOT_DEBUG=1 MAKEFLAGS_VBOOT=DEBUG=1 CFLAGS_EXTRA_VBOOT=-DUNROLL_LOOPS VBOOT_SOURCE=${src}/platform/vboot_reference
31 chromeos_coreboot=VBOOT=${chroot}/build/link/usr ${vboot}
32 chromeos_daisy=VBOOT=${chroot}/build/daisy/usr ${vboot}
33 chromeos_peach=VBOOT=${chroot}/build/peach_pit/usr ${vboot}
37 ['Active', 'arm', 'armv7', '', 'Tester', 'ARM Board 1', 'board0', ''],
38 ['Active', 'arm', 'armv7', '', 'Tester', 'ARM Board 2', 'board1', ''],
39 ['Active', 'powerpc', 'powerpc', '', 'Tester', 'PowerPC board 1', 'board2', ''],
40 ['Active', 'sandbox', 'sandbox', '', 'Tester', 'Sandbox board', 'board4', ''],
43 commit_shortlog = """4aca821 patman: Avoid changing the order of tags
44 39403bb patman: Use --no-pager' to stop git from forking a pager
45 db6e6f2 patman: Remove the -a option
46 f2ccf03 patman: Correct unit tests to run correctly
47 1d097f9 patman: Fix indentation in terminal.py
48 d073747 patman: Support the 'reverse' option for 'git log
51 commit_log = ["""commit 7f6b8315d18f683c5181d0c3694818c1b2a20dcd
52 Author: Masahiro Yamada <yamada.m@jp.panasonic.com>
53 Date: Fri Aug 22 19:12:41 2014 +0900
55 buildman: refactor help message
57 "buildman [options]" is displayed by default.
59 Append the rest of help messages to parser.usage
60 instead of replacing it.
62 Besides, "-b <branch>" is not mandatory since commit fea5858e.
63 Drop it from the usage.
65 Signed-off-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
67 """commit d0737479be6baf4db5e2cdbee123e96bc5ed0ba8
68 Author: Simon Glass <sjg@chromium.org>
69 Date: Thu Aug 14 16:48:25 2014 -0600
71 patman: Support the 'reverse' option for 'git log'
73 This option is currently not supported, but needs to be, for buildman to
77 - Add new patch to fix the 'reverse' bug
81 Change-Id: I79078f792e8b390b8a1272a8023537821d45feda
82 Reported-by: York Sun <yorksun@freescale.com>
83 Signed-off-by: Simon Glass <sjg@chromium.org>
86 """commit 1d097f9ab487c5019152fd47bda126839f3bf9fc
87 Author: Simon Glass <sjg@chromium.org>
88 Date: Sat Aug 9 11:44:32 2014 -0600
90 patman: Fix indentation in terminal.py
92 This code came from a different project with 2-character indentation. Fix
96 - Add new patch to fix indentation in teminal.py
98 Change-Id: I5a74d2ebbb3cc12a665f5c725064009ac96e8a34
99 Signed-off-by: Simon Glass <sjg@chromium.org>
102 """commit f2ccf03869d1e152c836515a3ceb83cdfe04a105
103 Author: Simon Glass <sjg@chromium.org>
104 Date: Sat Aug 9 11:08:24 2014 -0600
106 patman: Correct unit tests to run correctly
108 It seems that doctest behaves differently now, and some of the unit tests
109 do not run. Adjust the tests to work correctly.
111 ./tools/patman/patman --test
112 <unittest.result.TestResult run=10 errors=0 failures=0>
115 - Add new patch to fix patman unit tests
117 Change-Id: I3d2ca588f4933e1f9d6b1665a00e4ae58269ff3b
120 """commit db6e6f2f9331c5a37647d6668768d4a40b8b0d1c
121 Author: Simon Glass <sjg@chromium.org>
122 Date: Sat Aug 9 12:06:02 2014 -0600
124 patman: Remove the -a option
126 It seems that this is no longer needed, since checkpatch.pl will catch
127 whitespace problems in patches. Also the option is not widely used, so
128 it seems safe to just remove it.
131 - Add new patch to remove patman's -a option
133 Suggested-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
134 Change-Id: I5821a1c75154e532c46513486ca40b808de7e2cc
137 """commit 39403bb4f838153028a6f21ca30bf100f3791133
138 Author: Simon Glass <sjg@chromium.org>
139 Date: Thu Aug 14 21:50:52 2014 -0600
141 patman: Use --no-pager' to stop git from forking a pager
144 """commit 4aca821e27e97925c039e69fd37375b09c6f129c
145 Author: Simon Glass <sjg@chromium.org>
146 Date: Fri Aug 22 15:57:39 2014 -0600
148 patman: Avoid changing the order of tags
150 patman collects tags that it sees in the commit and places them nicely
151 sorted at the end of the patch. However, this is not really necessary and
152 in fact is apparently not desirable.
155 - Add new patch to avoid changing the order of tags
159 Suggested-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
160 Change-Id: Ib1518588c1a189ad5c3198aae76f8654aed8d0db
163 TEST_BRANCH = '__testbranch'
165 class TestFunctional(unittest.TestCase):
166 """Functional test for buildman.
168 This aims to test from just below the invocation of buildman (parsing
169 of arguments) to 'make' and 'git' invocation. It is not a true
170 emd-to-end test, as it mocks git, make and the tool chain. But this
171 makes it easier to detect when the builder is doing the wrong thing,
172 since in many cases this test code will fail. For example, only a
173 very limited subset of 'git' arguments is supported - anything
174 unexpected will fail.
177 self._base_dir = tempfile.mkdtemp()
178 self._output_dir = tempfile.mkdtemp()
179 self._git_dir = os.path.join(self._base_dir, 'src')
180 self._buildman_pathname = sys.argv[0]
181 self._buildman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
182 command.test_result = self._HandleCommand
183 self.setupToolchains()
184 self._toolchains.Add('arm-gcc', test=False)
185 self._toolchains.Add('powerpc-gcc', test=False)
186 bsettings.Setup(None)
187 bsettings.AddFile(settings_data)
188 self._boards = board.Boards()
190 self._boards.AddBoard(board.Board(*brd))
192 # Directories where the source been cloned
193 self._clone_dirs = []
194 self._commits = len(commit_shortlog.splitlines()) + 1
195 self._total_builds = self._commits * len(boards)
197 # Number of calls to make
200 # Map of [board, commit] to error messages
203 self._test_branch = TEST_BRANCH
205 # Avoid sending any output and clear all terminal output
206 terminal.SetPrintTestMode()
207 terminal.GetPrintTestLines()
210 shutil.rmtree(self._base_dir)
211 shutil.rmtree(self._output_dir)
213 def setupToolchains(self):
214 self._toolchains = toolchain.Toolchains()
215 self._toolchains.Add('gcc', test=False)
217 def _RunBuildman(self, *args):
218 return command.RunPipe([[self._buildman_pathname] + list(args)],
219 capture=True, capture_stderr=True)
221 def _RunControl(self, *args, **kwargs):
222 sys.argv = [sys.argv[0]] + list(args)
223 options, args = cmdline.ParseArgs()
224 result = control.DoBuildman(options, args, toolchains=self._toolchains,
225 make_func=self._HandleMake, boards=self._boards,
226 clean_dir=kwargs.get('clean_dir', True))
227 self._builder = control.builder
230 def testFullHelp(self):
231 command.test_result = None
232 result = self._RunBuildman('-H')
233 help_file = os.path.join(self._buildman_dir, 'README')
234 # Remove possible extraneous strings
235 extra = '::::::::::::::\n' + help_file + '\n::::::::::::::\n'
236 gothelp = result.stdout.replace(extra, '')
237 self.assertEqual(len(gothelp), os.path.getsize(help_file))
238 self.assertEqual(0, len(result.stderr))
239 self.assertEqual(0, result.return_code)
242 command.test_result = None
243 result = self._RunBuildman('-h')
244 help_file = os.path.join(self._buildman_dir, 'README')
245 self.assertTrue(len(result.stdout) > 1000)
246 self.assertEqual(0, len(result.stderr))
247 self.assertEqual(0, result.return_code)
249 def testGitSetup(self):
250 """Test gitutils.Setup(), from outside the module itself"""
251 command.test_result = command.CommandResult(return_code=1)
253 self.assertEqual(gitutil.use_no_decorate, False)
255 command.test_result = command.CommandResult(return_code=0)
257 self.assertEqual(gitutil.use_no_decorate, True)
259 def _HandleCommandGitLog(self, args):
263 return command.CommandResult(return_code=0)
264 elif args[-1] == 'upstream/master..%s' % self._test_branch:
265 return command.CommandResult(return_code=0, stdout=commit_shortlog)
266 elif args[:3] == ['--no-color', '--no-decorate', '--reverse']:
267 if args[-1] == self._test_branch:
268 count = int(args[3][2:])
269 return command.CommandResult(return_code=0,
270 stdout=''.join(commit_log[:count]))
272 # Not handled, so abort
273 print('git log', args)
276 def _HandleCommandGitConfig(self, args):
278 if config == 'sendemail.aliasesfile':
279 return command.CommandResult(return_code=0)
280 elif config.startswith('branch.badbranch'):
281 return command.CommandResult(return_code=1)
282 elif config == 'branch.%s.remote' % self._test_branch:
283 return command.CommandResult(return_code=0, stdout='upstream\n')
284 elif config == 'branch.%s.merge' % self._test_branch:
285 return command.CommandResult(return_code=0,
286 stdout='refs/heads/master\n')
288 # Not handled, so abort
289 print('git config', args)
292 def _HandleCommandGit(self, in_args):
293 """Handle execution of a git command
295 This uses a hacked-up parser.
298 in_args: Arguments after 'git' from the command line
300 git_args = [] # Top-level arguments to git itself
301 sub_cmd = None # Git sub-command selected
302 args = [] # Arguments to the git sub-command
309 if git_args and git_args[-1] in ['--git-dir', '--work-tree']:
313 if sub_cmd == 'config':
314 return self._HandleCommandGitConfig(args)
315 elif sub_cmd == 'log':
316 return self._HandleCommandGitLog(args)
317 elif sub_cmd == 'clone':
318 return command.CommandResult(return_code=0)
319 elif sub_cmd == 'checkout':
320 return command.CommandResult(return_code=0)
322 # Not handled, so abort
323 print('git', git_args, sub_cmd, args)
326 def _HandleCommandNm(self, args):
327 return command.CommandResult(return_code=0)
329 def _HandleCommandObjdump(self, args):
330 return command.CommandResult(return_code=0)
332 def _HandleCommandObjcopy(self, args):
333 return command.CommandResult(return_code=0)
335 def _HandleCommandSize(self, args):
336 return command.CommandResult(return_code=0)
338 def _HandleCommand(self, **kwargs):
339 """Handle a command execution.
341 The command is in kwargs['pipe-list'], as a list of pipes, each a
342 list of commands. The command should be emulated as required for
346 A CommandResult object
348 pipe_list = kwargs['pipe_list']
350 if len(pipe_list) != 1:
351 if pipe_list[1] == ['wc', '-l']:
354 print('invalid pipe', kwargs)
356 cmd = pipe_list[0][0]
357 args = pipe_list[0][1:]
360 result = self._HandleCommandGit(args)
361 elif cmd == './scripts/show-gnu-make':
362 return command.CommandResult(return_code=0, stdout='make')
363 elif cmd.endswith('nm'):
364 return self._HandleCommandNm(args)
365 elif cmd.endswith('objdump'):
366 return self._HandleCommandObjdump(args)
367 elif cmd.endswith('objcopy'):
368 return self._HandleCommandObjcopy(args)
369 elif cmd.endswith( 'size'):
370 return self._HandleCommandSize(args)
373 # Not handled, so abort
374 print('unknown command', kwargs)
378 result.stdout = len(result.stdout.splitlines())
381 def _HandleMake(self, commit, brd, stage, cwd, *args, **kwargs):
382 """Handle execution of 'make'
385 commit: Commit object that is being built
386 brd: Board object that is being built
387 stage: Stage that we are at (mrproper, config, build)
388 cwd: Directory where make should be run
389 args: Arguments to pass to make
390 kwargs: Arguments to pass to command.RunPipe()
392 self._make_calls += 1
393 if stage == 'mrproper':
394 return command.CommandResult(return_code=0)
395 elif stage == 'config':
396 return command.CommandResult(return_code=0,
397 combined='Test configuration complete')
398 elif stage == 'build':
400 if type(commit) is not str:
401 stderr = self._error.get((brd.target, commit.sequence))
403 return command.CommandResult(return_code=1, stderr=stderr)
404 return command.CommandResult(return_code=0)
406 # Not handled, so abort
410 # Example function to print output lines
411 def print_lines(self, lines):
415 #self.print_lines(terminal.GetPrintTestLines())
417 def testNoBoards(self):
418 """Test that buildman aborts when there are no boards"""
419 self._boards = board.Boards()
420 with self.assertRaises(SystemExit):
423 def testCurrentSource(self):
424 """Very simple test to invoke buildman on the current source"""
425 self.setupToolchains();
426 self._RunControl('-o', self._output_dir)
427 lines = terminal.GetPrintTestLines()
428 self.assertIn('Building current source for %d boards' % len(boards),
431 def testBadBranch(self):
432 """Test that we can detect an invalid branch"""
433 with self.assertRaises(ValueError):
434 self._RunControl('-b', 'badbranch')
436 def testBadToolchain(self):
437 """Test that missing toolchains are detected"""
438 self.setupToolchains();
439 ret_code = self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir)
440 lines = terminal.GetPrintTestLines()
442 # Buildman always builds the upstream commit as well
443 self.assertIn('Building %d commits for %d boards' %
444 (self._commits, len(boards)), lines[0].text)
445 self.assertEqual(self._builder.count, self._total_builds)
447 # Only sandbox should succeed, the others don't have toolchains
448 self.assertEqual(self._builder.fail,
449 self._total_builds - self._commits)
450 self.assertEqual(ret_code, 128)
452 for commit in range(self._commits):
453 for board in self._boards.GetList():
454 if board.arch != 'sandbox':
455 errfile = self._builder.GetErrFile(commit, board.target)
457 self.assertEqual(fd.readlines(),
458 ['No tool chain for %s\n' % board.arch])
461 def testBranch(self):
462 """Test building a branch with all toolchains present"""
463 self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir)
464 self.assertEqual(self._builder.count, self._total_builds)
465 self.assertEqual(self._builder.fail, 0)
468 """Test building a specific number of commitst"""
469 self._RunControl('-b', TEST_BRANCH, '-c2', '-o', self._output_dir)
470 self.assertEqual(self._builder.count, 2 * len(boards))
471 self.assertEqual(self._builder.fail, 0)
472 # Each board has a mrproper, config, and then one make per commit
473 self.assertEqual(self._make_calls, len(boards) * (2 + 2))
475 def testIncremental(self):
476 """Test building a branch twice - the second time should do nothing"""
477 self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir)
479 # Each board has a mrproper, config, and then one make per commit
480 self.assertEqual(self._make_calls, len(boards) * (self._commits + 2))
482 self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir, clean_dir=False)
483 self.assertEqual(self._make_calls, 0)
484 self.assertEqual(self._builder.count, self._total_builds)
485 self.assertEqual(self._builder.fail, 0)
487 def testForceBuild(self):
488 """The -f flag should force a rebuild"""
489 self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir)
491 self._RunControl('-b', TEST_BRANCH, '-f', '-o', self._output_dir, clean_dir=False)
492 # Each board has a mrproper, config, and then one make per commit
493 self.assertEqual(self._make_calls, len(boards) * (self._commits + 2))
495 def testForceReconfigure(self):
496 """The -f flag should force a rebuild"""
497 self._RunControl('-b', TEST_BRANCH, '-C', '-o', self._output_dir)
498 # Each commit has a mrproper, config and make
499 self.assertEqual(self._make_calls, len(boards) * self._commits * 3)
501 def testErrors(self):
502 """Test handling of build errors"""
503 self._error['board2', 1] = 'fred\n'
504 self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir)
505 self.assertEqual(self._builder.count, self._total_builds)
506 self.assertEqual(self._builder.fail, 1)
508 # Remove the error. This should have no effect since the commit will
510 del self._error['board2', 1]
512 self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir, clean_dir=False)
513 self.assertEqual(self._builder.count, self._total_builds)
514 self.assertEqual(self._make_calls, 0)
515 self.assertEqual(self._builder.fail, 1)
517 # Now use the -F flag to force rebuild of the bad commit
518 self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir, '-F', clean_dir=False)
519 self.assertEqual(self._builder.count, self._total_builds)
520 self.assertEqual(self._builder.fail, 0)
521 self.assertEqual(self._make_calls, 3)
523 def testBranchWithSlash(self):
524 """Test building a branch with a '/' in the name"""
525 self._test_branch = '/__dev/__testbranch'
526 self._RunControl('-b', self._test_branch, clean_dir=False)
527 self.assertEqual(self._builder.count, self._total_builds)
528 self.assertEqual(self._builder.fail, 0)
530 def testBadOutputDir(self):
531 """Test building with an output dir the same as out current dir"""
532 self._test_branch = '/__dev/__testbranch'
533 with self.assertRaises(SystemExit):
534 self._RunControl('-b', self._test_branch, '-o', os.getcwd())
535 with self.assertRaises(SystemExit):
536 self._RunControl('-b', self._test_branch, '-o',
537 os.path.join(os.getcwd(), 'test'))