1#!/usr/bin/env python3
2# SPDX-License-Identifier: GPL-2.0+
3
4# Copyright (c) 2016 Google, Inc
5# Written by Simon Glass <sjg@chromium.org>
6#
7# Creates binary images from input files controlled by a description
8#
9
10"""See README for more information"""
11
12import os
13import site
14import sys
15import traceback
16
17# Get the absolute path to this file at run-time
18our_path = os.path.dirname(os.path.realpath(__file__))
19our1_path = os.path.dirname(our_path)
20our2_path = os.path.dirname(our1_path)
21
22# Extract $(srctree) from Kbuild environment, or use relative paths below
23srctree = os.environ.get('srctree', our2_path)
24
25#
26# Do not pollute source tree with cache files:
27# https://stackoverflow.com/a/60024195/2511795
28# https://bugs.python.org/issue33499
29#
30sys.pycache_prefix = os.path.relpath(our_path, srctree)
31
32# Bring in the patman and dtoc libraries (but don't override the first path
33# in PYTHONPATH)
34sys.path.insert(2, our1_path)
35
36from binman import bintool
37from u_boot_pylib import test_util
38
39# Bring in the libfdt module
40sys.path.insert(2, 'scripts/dtc/pylibfdt')
41sys.path.insert(2, os.path.join(srctree, 'scripts/dtc/pylibfdt'))
42sys.path.insert(2, os.path.join(srctree, 'build-sandbox/scripts/dtc/pylibfdt'))
43sys.path.insert(2, os.path.join(srctree, 'build-sandbox_spl/scripts/dtc/pylibfdt'))
44
45from binman import cmdline
46from binman import control
47from u_boot_pylib import test_util
48
49def RunTests(debug, verbosity, processes, test_preserve_dirs, args, toolpath):
50    """Run the functional tests and any embedded doctests
51
52    Args:
53        debug: True to enable debugging, which shows a full stack trace on error
54        verbosity: Verbosity level to use
55        test_preserve_dirs: True to preserve the input directory used by tests
56            so that it can be examined afterwards (only useful for debugging
57            tests). If a single test is selected (in args[0]) it also preserves
58            the output directory for this test. Both directories are displayed
59            on the command line.
60        processes: Number of processes to use to run tests (None=same as #CPUs)
61        args: List of positional args provided to binman. This can hold a test
62            name to execute (as in 'binman test testSections', for example)
63        toolpath: List of paths to use for tools
64    """
65    from binman import bintool_test
66    from binman import cbfs_util_test
67    from binman import elf_test
68    from binman import entry_test
69    from binman import fdt_test
70    from binman import fip_util_test
71    from binman import ftest
72    from binman import image_test
73    import doctest
74
75    test_name = args and args[0] or None
76
77    # Run the entry tests first ,since these need to be the first to import the
78    # 'entry' module.
79    result = test_util.run_test_suites(
80        'binman', debug, verbosity, test_preserve_dirs, processes, test_name,
81        toolpath,
82        [bintool_test.TestBintool, entry_test.TestEntry, ftest.TestFunctional,
83         fdt_test.TestFdt, elf_test.TestElf, image_test.TestImage,
84         cbfs_util_test.TestCbfs, fip_util_test.TestFip])
85
86    return (0 if result.wasSuccessful() else 1)
87
88def RunTestCoverage(toolpath, build_dir):
89    """Run the tests and check that we get 100% coverage"""
90    glob_list = control.GetEntryModules(False)
91    all_set = set([os.path.splitext(os.path.basename(item))[0]
92                   for item in glob_list if '_testing' not in item])
93    extra_args = ''
94    if toolpath:
95        for path in toolpath:
96            extra_args += ' --toolpath %s' % path
97    test_util.run_test_coverage('tools/binman/binman', None,
98            ['*test*', '*main.py', 'tools/patman/*', 'tools/dtoc/*',
99             'tools/u_boot_pylib/*'],
100            build_dir, all_set, extra_args or None)
101
102def RunBinman(args):
103    """Main entry point to binman once arguments are parsed
104
105    Args:
106        args: Command line arguments Namespace object
107    """
108    ret_code = 0
109
110    if not args.debug:
111        sys.tracebacklimit = 0
112
113    # Provide a default toolpath in the hope of finding a mkimage built from
114    # current source
115    if not args.toolpath:
116        args.toolpath = ['./tools', 'build-sandbox/tools']
117
118    if args.cmd == 'test':
119        if args.test_coverage:
120            RunTestCoverage(args.toolpath, args.build_dir)
121        else:
122            ret_code = RunTests(args.debug, args.verbosity, args.processes,
123                                args.test_preserve_dirs, args.tests,
124                                args.toolpath)
125
126    elif args.cmd == 'bintool-docs':
127        control.write_bintool_docs(bintool.Bintool.get_tool_list())
128
129    elif args.cmd == 'entry-docs':
130        control.WriteEntryDocs(control.GetEntryModules())
131
132    else:
133        try:
134            ret_code = control.Binman(args)
135        except Exception as e:
136            print('binman: %s' % e, file=sys.stderr)
137            if args.debug:
138                print()
139                traceback.print_exc()
140            ret_code = 1
141    return ret_code
142
143
144def start_binman():
145    args = cmdline.ParseArgs(sys.argv[1:])
146
147    ret_code = RunBinman(args)
148    sys.exit(ret_code)
149
150
151if __name__ == "__main__":
152    start_binman()
153