From: Simon Glass Date: Tue, 29 Dec 2020 03:34:51 +0000 (-0700) Subject: dtoc: Allow outputing to multiple files X-Git-Url: http://git.dujemihanovic.xyz/?a=commitdiff_plain;h=be44f27156bf46807049a0e1c303626d05f781f8;p=u-boot.git dtoc: Allow outputing to multiple files Implement the 'output directory' feature, allowing dtoc to write the output files separately to the supplied directories. This allows us to handle the struct and platdata output in one run of dtoc. Signed-off-by: Simon Glass --- diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index 62a65d6214..e2fddfd301 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -15,6 +15,7 @@ See doc/driver-model/of-plat.rst for more informaiton import collections import copy +from enum import IntEnum import os import re import sys @@ -49,6 +50,15 @@ TYPE_NAMES = { STRUCT_PREFIX = 'dtd_' VAL_PREFIX = 'dtv_' +class Ftype(IntEnum): + SOURCE, HEADER = range(2) + + +# This holds information about each type of output file dtoc can create +# type: Type of file (Ftype) +# fname: Filename excluding directory, e.g. 'dt-platdata.c' +OutputFile = collections.namedtuple('OutputFile', ['ftype', 'fname']) + # This holds information about a property which includes phandles. # # max_args: integer: Maximum number or arguments that any phandle uses (int). @@ -180,6 +190,8 @@ class DtbPlatdata(): U_BOOT_DRIVER_ALIAS(driver_alias, driver_name) value: Driver name declared with U_BOOT_DRIVER(driver_name) _drivers_additional: List of additional drivers to use during scanning + _dirname: Directory to hold output files, or None for none (all files + go to stdout) """ def __init__(self, dtb_fname, include_disabled, warning_disabled, drivers_additional=None): @@ -193,6 +205,7 @@ class DtbPlatdata(): self._drivers = {} self._driver_aliases = {} self._drivers_additional = drivers_additional or [] + self._dirnames = [None] * len(Ftype) def get_normalized_compat_name(self, node): """Get a node's normalized compat name @@ -230,20 +243,68 @@ class DtbPlatdata(): return compat_list_c[0], compat_list_c[1:] - def setup_output(self, fname): + def setup_output_dirs(self, output_dirs): + """Set up the output directories + + This should be done before setup_output() is called + + Args: + output_dirs (tuple of str): + Directory to use for C output files. + Use None to write files relative current directory + Directory to use for H output files. + Defaults to the C output dir + """ + def process_dir(ftype, dirname): + if dirname: + os.makedirs(dirname, exist_ok=True) + self._dirnames[ftype] = dirname + + if output_dirs: + c_dirname = output_dirs[0] + h_dirname = output_dirs[1] if len(output_dirs) > 1 else c_dirname + process_dir(Ftype.SOURCE, c_dirname) + process_dir(Ftype.HEADER, h_dirname) + + def setup_output(self, ftype, fname): """Set up the output destination Once this is done, future calls to self.out() will output to this - file. + file. The file used is as follows: + + self._dirnames[ftype] is None: output to fname, or stdout if None + self._dirnames[ftype] is not None: output to fname in that directory + + Calling this function multiple times will close the old file and open + the new one. If they are the same file, nothing happens and output will + continue to the same file. Args: - fname (str): Filename to send output to, or None for stdout + ftype (str): Type of file to create ('c' or 'h') + fname (str): Filename to send output to. If there is a directory in + self._dirnames for this file type, it will be put in that + directory """ - if fname: - self._outfile = open(fname, 'w') + dirname = self._dirnames[ftype] + if dirname: + pathname = os.path.join(dirname, fname) + if self._outfile: + self._outfile.close() + self._outfile = open(pathname, 'w') + elif fname: + if not self._outfile: + self._outfile = open(fname, 'w') else: self._outfile = sys.stdout + def finish_output(self): + """Finish outputing to a file + + This closes the output file, if one is in use + """ + if self._outfile != sys.stdout: + self._outfile.close() + def out(self, line): """Output a string to the output file @@ -758,6 +819,15 @@ class DtbPlatdata(): self.out(''.join(self.get_buf())) +# Types of output file we understand +# key: Command used to generate this file +# value: OutputFile for this command +OUTPUT_FILES = { + 'struct': OutputFile(Ftype.HEADER, 'dt-structs-gen.h'), + 'platdata': OutputFile(Ftype.SOURCE, 'dt-platdata.c'), + } + + def run_steps(args, dtb_file, include_disabled, output, output_dirs, warning_disabled=False, drivers_additional=None): """Run all the steps of the dtoc tool @@ -778,7 +848,9 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, ValueError: if args has no command, or an unknown command """ if not args: - raise ValueError('Please specify a command: struct, platdata') + raise ValueError('Please specify a command: struct, platdata, all') + if output and output_dirs and any(output_dirs): + raise ValueError('Must specify either output or output_dirs, not both') plat = DtbPlatdata(dtb_file, include_disabled, warning_disabled, drivers_additional) @@ -786,15 +858,19 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, plat.scan_dtb() plat.scan_tree() plat.scan_reg_sizes() - plat.setup_output(output) + plat.setup_output_dirs(output_dirs) structs = plat.scan_structs() plat.scan_phandles() for cmd in args[0].split(','): + outfile = OUTPUT_FILES.get(cmd) + if not outfile: + raise ValueError("Unknown command '%s': (use: %s)" % + (cmd, ', '.join(OUTPUT_FILES.keys()))) + plat.setup_output(outfile.ftype, + outfile.fname if output_dirs else output) if cmd == 'struct': plat.generate_structs(structs) elif cmd == 'platdata': plat.generate_tables() - else: - raise ValueError("Unknown command '%s': (use: struct, platdata)" % - cmd) + plat.finish_output() diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index b023a1e14a..6f9af905e8 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -884,6 +884,14 @@ U_BOOT_DEVICE(spl_test2) = { self.run_test(['struct'], dtb_file, None) self._check_strings(self.struct_text, stdout.getvalue()) + def test_multi_to_file(self): + """Test output of multiple pieces to a single file""" + dtb_file = get_dtb_file('dtoc_test_simple.dts') + output = tools.GetOutputFilename('output') + self.run_test(['struct,platdata'], dtb_file, output) + data = tools.ReadFile(output, binary=False) + self._check_strings(self.struct_text + self.platdata_text, data) + def test_no_command(self): """Test running dtoc without a command""" with self.assertRaises(ValueError) as exc: