Corentin Wallez | 59382b7 | 2020-04-17 20:43:07 +0000 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
Austin Eng | cc2516a | 2023-10-17 20:57:54 +0000 | [diff] [blame] | 2 | # Copyright 2019 The Dawn & Tint Authors |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 3 | # |
Austin Eng | cc2516a | 2023-10-17 20:57:54 +0000 | [diff] [blame] | 4 | # Redistribution and use in source and binary forms, with or without |
| 5 | # modification, are permitted provided that the following conditions are met: |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 6 | # |
Austin Eng | cc2516a | 2023-10-17 20:57:54 +0000 | [diff] [blame] | 7 | # 1. Redistributions of source code must retain the above copyright notice, this |
| 8 | # list of conditions and the following disclaimer. |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 9 | # |
Austin Eng | cc2516a | 2023-10-17 20:57:54 +0000 | [diff] [blame] | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, |
| 11 | # this list of conditions and the following disclaimer in the documentation |
| 12 | # and/or other materials provided with the distribution. |
| 13 | # |
| 14 | # 3. Neither the name of the copyright holder nor the names of its |
| 15 | # contributors may be used to endorse or promote products derived from |
| 16 | # this software without specific prior written permission. |
| 17 | # |
| 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| 19 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 20 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| 21 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
| 22 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| 23 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| 24 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| 25 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| 26 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
David 'Digit' Turner | 5dee3e8 | 2019-06-24 14:31:06 +0000 | [diff] [blame] | 28 | """Module to create generators that render multiple Jinja2 templates for GN. |
| 29 | |
| 30 | A helper module that can be used to create generator scripts (clients) |
| 31 | that expand one or more Jinja2 templates, without outputs usable from |
| 32 | GN and Ninja build-based systems. See generator_lib.gni as well. |
| 33 | |
| 34 | Clients should create a Generator sub-class, then call run_generator() |
| 35 | with a proper derived class instance. |
| 36 | |
| 37 | Clients specify a list of FileRender operations, each one of them will |
| 38 | output a file into a temporary output directory through Jinja2 expansion. |
| 39 | All temporary output files are then grouped and written to into a single JSON |
| 40 | file, that acts as a convenient single GN output target. Use extract_json.py |
| 41 | to extract the output files from the JSON tarball in another GN action. |
| 42 | |
| 43 | --depfile can be used to specify an output Ninja dependency file for the |
| 44 | JSON tarball, to ensure it is regenerated any time one of its dependencies |
| 45 | changes. |
| 46 | |
| 47 | Finally, --expected-output-files can be used to check the list of generated |
| 48 | output files. |
| 49 | """ |
| 50 | |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 51 | import argparse, json, os, re, sys |
| 52 | from collections import namedtuple |
| 53 | |
David 'Digit' Turner | 5dee3e8 | 2019-06-24 14:31:06 +0000 | [diff] [blame] | 54 | # A FileRender represents a single Jinja2 template render operation: |
| 55 | # |
| 56 | # template: Jinja2 template name, relative to --template-dir path. |
| 57 | # |
| 58 | # output: Output file path, relative to temporary output directory. |
| 59 | # |
| 60 | # params_dicts: iterable of (name:string -> value:string) dictionaries. |
| 61 | # All of them will be merged before being sent as Jinja2 template |
| 62 | # expansion parameters. |
| 63 | # |
| 64 | # Example: |
| 65 | # FileRender('api.c', 'src/project_api.c', [{'PROJECT_VERSION': '1.0.0'}]) |
| 66 | # |
| 67 | FileRender = namedtuple('FileRender', ['template', 'output', 'params_dicts']) |
| 68 | |
Kai Ninomiya | 01aeca2 | 2020-07-15 19:51:17 +0000 | [diff] [blame] | 69 | |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 70 | # The interface that must be implemented by generators. |
| 71 | class Generator: |
| 72 | def get_description(self): |
David 'Digit' Turner | 5dee3e8 | 2019-06-24 14:31:06 +0000 | [diff] [blame] | 73 | """Return generator description for --help.""" |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 74 | return "" |
| 75 | |
| 76 | def add_commandline_arguments(self, parser): |
David 'Digit' Turner | 5dee3e8 | 2019-06-24 14:31:06 +0000 | [diff] [blame] | 77 | """Add generator-specific argparse arguments.""" |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 78 | pass |
| 79 | |
| 80 | def get_file_renders(self, args): |
David 'Digit' Turner | 5dee3e8 | 2019-06-24 14:31:06 +0000 | [diff] [blame] | 81 | """Return the list of FileRender objects to process.""" |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 82 | return [] |
| 83 | |
| 84 | def get_dependencies(self, args): |
David 'Digit' Turner | 5dee3e8 | 2019-06-24 14:31:06 +0000 | [diff] [blame] | 85 | """Return a list of extra input dependencies.""" |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 86 | return [] |
| 87 | |
Kai Ninomiya | 01aeca2 | 2020-07-15 19:51:17 +0000 | [diff] [blame] | 88 | |
David 'Digit' Turner | 5dee3e8 | 2019-06-24 14:31:06 +0000 | [diff] [blame] | 89 | # Allow custom Jinja2 installation path through an additional python |
| 90 | # path from the arguments if present. This isn't done through the regular |
| 91 | # argparse because PreprocessingLoader uses jinja2 in the global scope before |
| 92 | # "main" gets to run. |
| 93 | # |
| 94 | # NOTE: If this argument appears several times, this only uses the first |
| 95 | # value, while argparse would typically keep the last one! |
| 96 | kJinja2Path = '--jinja2-path' |
Corentin Wallez | 45f9185 | 2019-09-18 00:59:40 +0000 | [diff] [blame] | 97 | try: |
| 98 | jinja2_path_argv_index = sys.argv.index(kJinja2Path) |
David 'Digit' Turner | 5dee3e8 | 2019-06-24 14:31:06 +0000 | [diff] [blame] | 99 | # Add parent path for the import to succeed. |
| 100 | path = os.path.join(sys.argv[jinja2_path_argv_index + 1], os.pardir) |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 101 | sys.path.insert(1, path) |
Corentin Wallez | 45f9185 | 2019-09-18 00:59:40 +0000 | [diff] [blame] | 102 | except ValueError: |
| 103 | # --jinja2-path isn't passed, ignore the exception and just import Jinja2 |
| 104 | # assuming it already is in the Python PATH. |
| 105 | pass |
dan sinclair | 6b67a90 | 2023-04-07 07:52:36 +0000 | [diff] [blame] | 106 | kMarkupSafePath = '--markupsafe-path' |
| 107 | try: |
| 108 | markupsafe_path_argv_index = sys.argv.index(kMarkupSafePath) |
| 109 | # Add parent path for the import to succeed. |
| 110 | path = os.path.join(sys.argv[markupsafe_path_argv_index + 1], os.pardir) |
| 111 | sys.path.insert(1, path) |
| 112 | except ValueError: |
| 113 | # --markupsafe-path isn't passed, ignore the exception and just import |
| 114 | # assuming it already is in the Python PATH. |
| 115 | pass |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 116 | |
| 117 | import jinja2 |
| 118 | |
Kai Ninomiya | 01aeca2 | 2020-07-15 19:51:17 +0000 | [diff] [blame] | 119 | |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 120 | # A custom Jinja2 template loader that removes the extra indentation |
| 121 | # of the template blocks so that the output is correctly indented |
Corentin Wallez | df69f24 | 2019-06-13 10:22:32 +0000 | [diff] [blame] | 122 | class _PreprocessingLoader(jinja2.BaseLoader): |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 123 | def __init__(self, path): |
| 124 | self.path = path |
| 125 | |
| 126 | def get_source(self, environment, template): |
| 127 | path = os.path.join(self.path, template) |
| 128 | if not os.path.exists(path): |
| 129 | raise jinja2.TemplateNotFound(template) |
| 130 | mtime = os.path.getmtime(path) |
| 131 | with open(path) as f: |
| 132 | source = self.preprocess(f.read()) |
| 133 | return source, path, lambda: mtime == os.path.getmtime(path) |
| 134 | |
Ho Cheung | 1281bdb | 2023-10-26 18:41:50 +0000 | [diff] [blame] | 135 | blockstart = re.compile(r'{%-?\s*(if|elif|else|for|block|macro)[^}]*%}') |
| 136 | blockend = re.compile(r'{%-?\s*(end(if|for|block|macro)|elif|else)[^}]*%}') |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 137 | |
| 138 | def preprocess(self, source): |
| 139 | lines = source.split('\n') |
| 140 | |
Kai Ninomiya | 01aeca2 | 2020-07-15 19:51:17 +0000 | [diff] [blame] | 141 | # Compute the current indentation level of the template blocks and |
| 142 | # remove their indentation |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 143 | result = [] |
| 144 | indentation_level = 0 |
| 145 | |
Kai Ninomiya | 01aeca2 | 2020-07-15 19:51:17 +0000 | [diff] [blame] | 146 | # Filter lines that are pure comments. line_comment_prefix is not |
| 147 | # enough because it removes the comment but doesn't completely remove |
| 148 | # the line, resulting in more verbose output. |
Corentin Wallez | 1bf3167 | 2020-01-15 15:39:12 +0000 | [diff] [blame] | 149 | lines = filter(lambda line: not line.strip().startswith('//*'), lines) |
| 150 | |
| 151 | # Remove indentation templates have for the Jinja control flow. |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 152 | for line in lines: |
Kai Ninomiya | 01aeca2 | 2020-07-15 19:51:17 +0000 | [diff] [blame] | 153 | # The capture in the regex adds one element per block start or end, |
| 154 | # so we divide by two. There is also an extra line chunk |
| 155 | # corresponding to the line end, so we subtract it. |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 156 | numends = (len(self.blockend.split(line)) - 1) // 2 |
| 157 | indentation_level -= numends |
| 158 | |
| 159 | result.append(self.remove_indentation(line, indentation_level)) |
| 160 | |
| 161 | numstarts = (len(self.blockstart.split(line)) - 1) // 2 |
| 162 | indentation_level += numstarts |
| 163 | |
| 164 | return '\n'.join(result) + '\n' |
| 165 | |
| 166 | def remove_indentation(self, line, n): |
| 167 | for _ in range(n): |
| 168 | if line.startswith(' '): |
| 169 | line = line[4:] |
| 170 | elif line.startswith('\t'): |
| 171 | line = line[1:] |
| 172 | else: |
Kai Ninomiya | 01aeca2 | 2020-07-15 19:51:17 +0000 | [diff] [blame] | 173 | assert line.strip() == '' |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 174 | return line |
| 175 | |
Kai Ninomiya | 01aeca2 | 2020-07-15 19:51:17 +0000 | [diff] [blame] | 176 | |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 177 | _FileOutput = namedtuple('FileOutput', ['name', 'content']) |
| 178 | |
Kai Ninomiya | 01aeca2 | 2020-07-15 19:51:17 +0000 | [diff] [blame] | 179 | |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 180 | def _do_renders(renders, template_dir): |
Corentin Wallez | df69f24 | 2019-06-13 10:22:32 +0000 | [diff] [blame] | 181 | loader = _PreprocessingLoader(template_dir) |
Loko Kung | 7c8dfbc | 2023-06-09 23:55:39 +0000 | [diff] [blame] | 182 | env = jinja2.Environment( |
| 183 | extensions=['jinja2.ext.do', 'jinja2.ext.loopcontrols'], |
| 184 | loader=loader, |
| 185 | lstrip_blocks=True, |
| 186 | trim_blocks=True, |
| 187 | line_comment_prefix='//*') |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 188 | |
Kai Ninomiya | 8d524b2 | 2023-09-02 01:11:30 +0000 | [diff] [blame] | 189 | def do_assert(expr, message=''): |
| 190 | assert expr, message |
Corentin Wallez | 031fbbb | 2019-06-11 18:03:05 +0000 | [diff] [blame] | 191 | return '' |
| 192 | |
| 193 | def debug(text): |
| 194 | print(text) |
| 195 | |
| 196 | base_params = { |
| 197 | 'enumerate': enumerate, |
| 198 | 'format': format, |
| 199 | 'len': len, |
| 200 | 'debug': debug, |
| 201 | 'assert': do_assert, |
| 202 | } |
| 203 | |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 204 | outputs = [] |
| 205 | for render in renders: |
| 206 | params = {} |
Corentin Wallez | 031fbbb | 2019-06-11 18:03:05 +0000 | [diff] [blame] | 207 | params.update(base_params) |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 208 | for param_dict in render.params_dicts: |
| 209 | params.update(param_dict) |
| 210 | content = env.get_template(render.template).render(**params) |
| 211 | outputs.append(_FileOutput(render.output, content)) |
| 212 | |
| 213 | return outputs |
| 214 | |
Kai Ninomiya | 01aeca2 | 2020-07-15 19:51:17 +0000 | [diff] [blame] | 215 | |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 216 | # Compute the list of imported, non-system Python modules. |
David 'Digit' Turner | 5dee3e8 | 2019-06-24 14:31:06 +0000 | [diff] [blame] | 217 | # It assumes that any path outside of the root directory is system. |
Kai Ninomiya | 01aeca2 | 2020-07-15 19:51:17 +0000 | [diff] [blame] | 218 | def _compute_python_dependencies(root_dir=None): |
David 'Digit' Turner | 5dee3e8 | 2019-06-24 14:31:06 +0000 | [diff] [blame] | 219 | if not root_dir: |
| 220 | # Assume this script is under generator/ by default. |
| 221 | root_dir = os.path.join(os.path.dirname(__file__), os.pardir) |
| 222 | root_dir = os.path.abspath(root_dir) |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 223 | |
| 224 | module_paths = (module.__file__ for module in sys.modules.values() |
Kai Ninomiya | 01aeca2 | 2020-07-15 19:51:17 +0000 | [diff] [blame] | 225 | if module and hasattr(module, '__file__')) |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 226 | |
| 227 | paths = set() |
| 228 | for path in module_paths: |
dan sinclair | e8022ea | 2020-10-20 14:46:10 +0000 | [diff] [blame] | 229 | # Builtin/namespaced modules may return None for the file path. |
| 230 | if not path: |
| 231 | continue |
| 232 | |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 233 | path = os.path.abspath(path) |
| 234 | |
David 'Digit' Turner | 5dee3e8 | 2019-06-24 14:31:06 +0000 | [diff] [blame] | 235 | if not path.startswith(root_dir): |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 236 | continue |
| 237 | |
| 238 | if (path.endswith('.pyc') |
| 239 | or (path.endswith('c') and not os.path.splitext(path)[1])): |
| 240 | path = path[:-1] |
| 241 | |
| 242 | paths.add(path) |
| 243 | |
| 244 | return paths |
| 245 | |
Kai Ninomiya | 01aeca2 | 2020-07-15 19:51:17 +0000 | [diff] [blame] | 246 | |
Corentin Wallez | 08e0fd7 | 2023-09-18 16:55:31 +0000 | [diff] [blame] | 247 | # Computes the string representing a cmake list of paths. |
| 248 | def _cmake_path_list(paths): |
| 249 | if os.name == "nt": |
| 250 | # On Windows CMake still expects paths to be separated by forward |
| 251 | # slashes |
| 252 | return (";".join(paths)).replace("\\", "/") |
| 253 | else: |
| 254 | return ";".join(paths) |
| 255 | |
| 256 | |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 257 | def run_generator(generator): |
| 258 | parser = argparse.ArgumentParser( |
Kai Ninomiya | 01aeca2 | 2020-07-15 19:51:17 +0000 | [diff] [blame] | 259 | description=generator.get_description(), |
| 260 | formatter_class=argparse.ArgumentDefaultsHelpFormatter, |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 261 | ) |
| 262 | |
Kai Ninomiya | 01aeca2 | 2020-07-15 19:51:17 +0000 | [diff] [blame] | 263 | generator.add_commandline_arguments(parser) |
| 264 | parser.add_argument('--template-dir', |
| 265 | default='templates', |
| 266 | type=str, |
| 267 | help='Directory with template files.') |
| 268 | parser.add_argument( |
| 269 | kJinja2Path, |
| 270 | default=None, |
| 271 | type=str, |
| 272 | help='Additional python path to set before loading Jinja2') |
| 273 | parser.add_argument( |
dan sinclair | 6b67a90 | 2023-04-07 07:52:36 +0000 | [diff] [blame] | 274 | kMarkupSafePath, |
| 275 | default=None, |
| 276 | type=str, |
| 277 | help='Additional python path to set before loading MarkupSafe') |
| 278 | parser.add_argument( |
Kai Ninomiya | 01aeca2 | 2020-07-15 19:51:17 +0000 | [diff] [blame] | 279 | '--output-json-tarball', |
| 280 | default=None, |
| 281 | type=str, |
| 282 | help=('Name of the "JSON tarball" to create (tar is too annoying ' |
| 283 | 'to use in python).')) |
| 284 | parser.add_argument( |
| 285 | '--depfile', |
| 286 | default=None, |
| 287 | type=str, |
| 288 | help='Name of the Ninja depfile to create for the JSON tarball') |
| 289 | parser.add_argument( |
| 290 | '--expected-outputs-file', |
| 291 | default=None, |
| 292 | type=str, |
| 293 | help="File to compare outputs with and fail if it doesn't match") |
| 294 | parser.add_argument( |
| 295 | '--root-dir', |
| 296 | default=None, |
| 297 | type=str, |
| 298 | help=('Optional source root directory for Python dependency ' |
| 299 | 'computations')) |
| 300 | parser.add_argument( |
Kai Ninomiya | 01aeca2 | 2020-07-15 19:51:17 +0000 | [diff] [blame] | 301 | '--print-cmake-dependencies', |
| 302 | default=False, |
| 303 | action="store_true", |
| 304 | help=("Prints a semi-colon separated list of dependencies to " |
| 305 | "stdout and exits.")) |
| 306 | parser.add_argument( |
| 307 | '--print-cmake-outputs', |
| 308 | default=False, |
| 309 | action="store_true", |
| 310 | help=("Prints a semi-colon separated list of outputs to " |
| 311 | "stdout and exits.")) |
| 312 | parser.add_argument('--output-dir', |
| 313 | default=None, |
| 314 | type=str, |
| 315 | help='Directory where to output generate files.') |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 316 | |
| 317 | args = parser.parse_args() |
| 318 | |
Kai Ninomiya | 01aeca2 | 2020-07-15 19:51:17 +0000 | [diff] [blame] | 319 | renders = generator.get_file_renders(args) |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 320 | |
Corentin Wallez | 7fe6efb | 2020-02-05 17:16:05 +0000 | [diff] [blame] | 321 | # Output a list of all dependencies for CMake or the tarball for GN/Ninja. |
| 322 | if args.depfile != None or args.print_cmake_dependencies: |
| 323 | dependencies = generator.get_dependencies(args) |
Kai Ninomiya | 01aeca2 | 2020-07-15 19:51:17 +0000 | [diff] [blame] | 324 | dependencies += [ |
| 325 | args.template_dir + os.path.sep + render.template |
| 326 | for render in renders |
| 327 | ] |
Corentin Wallez | 7fe6efb | 2020-02-05 17:16:05 +0000 | [diff] [blame] | 328 | dependencies += _compute_python_dependencies(args.root_dir) |
| 329 | |
| 330 | if args.depfile != None: |
| 331 | with open(args.depfile, 'w') as f: |
Kai Ninomiya | 01aeca2 | 2020-07-15 19:51:17 +0000 | [diff] [blame] | 332 | f.write(args.output_json_tarball + ": " + |
| 333 | " ".join(dependencies)) |
Corentin Wallez | 7fe6efb | 2020-02-05 17:16:05 +0000 | [diff] [blame] | 334 | |
| 335 | if args.print_cmake_dependencies: |
Corentin Wallez | 08e0fd7 | 2023-09-18 16:55:31 +0000 | [diff] [blame] | 336 | sys.stdout.write(_cmake_path_list(dependencies)) |
Corentin Wallez | 7fe6efb | 2020-02-05 17:16:05 +0000 | [diff] [blame] | 337 | return 0 |
| 338 | |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 339 | # The caller wants to assert that the outputs are what it expects. |
| 340 | # Load the file and compare with our renders. |
| 341 | if args.expected_outputs_file != None: |
| 342 | with open(args.expected_outputs_file) as f: |
| 343 | expected = set([line.strip() for line in f.readlines()]) |
| 344 | |
David 'Digit' Turner | 5dee3e8 | 2019-06-24 14:31:06 +0000 | [diff] [blame] | 345 | actual = {render.output for render in renders} |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 346 | |
| 347 | if actual != expected: |
Kai Ninomiya | 01aeca2 | 2020-07-15 19:51:17 +0000 | [diff] [blame] | 348 | print("Wrong expected outputs, caller expected:\n " + |
| 349 | repr(sorted(expected))) |
David 'Digit' Turner | 5dee3e8 | 2019-06-24 14:31:06 +0000 | [diff] [blame] | 350 | print("Actual output:\n " + repr(sorted(actual))) |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 351 | return 1 |
| 352 | |
Corentin Wallez | 7fe6efb | 2020-02-05 17:16:05 +0000 | [diff] [blame] | 353 | # Print the list of all the outputs for cmake. |
| 354 | if args.print_cmake_outputs: |
Corentin Wallez | 08e0fd7 | 2023-09-18 16:55:31 +0000 | [diff] [blame] | 355 | sys.stdout.write( |
| 356 | _cmake_path_list([ |
| 357 | os.path.join(args.output_dir, render.output) |
| 358 | for render in renders |
| 359 | ])) |
Corentin Wallez | 7fe6efb | 2020-02-05 17:16:05 +0000 | [diff] [blame] | 360 | return 0 |
| 361 | |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 362 | outputs = _do_renders(renders, args.template_dir) |
| 363 | |
Corentin Wallez | 7fe6efb | 2020-02-05 17:16:05 +0000 | [diff] [blame] | 364 | # Output the JSON tarball |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 365 | if args.output_json_tarball != None: |
| 366 | json_root = {} |
| 367 | for output in outputs: |
| 368 | json_root[output.name] = output.content |
| 369 | |
| 370 | with open(args.output_json_tarball, 'w') as f: |
| 371 | f.write(json.dumps(json_root)) |
| 372 | |
Corentin Wallez | 7fe6efb | 2020-02-05 17:16:05 +0000 | [diff] [blame] | 373 | # Output the files directly. |
| 374 | if args.output_dir != None: |
| 375 | for output in outputs: |
| 376 | output_path = os.path.join(args.output_dir, output.name) |
Corentin Wallez | 0c38e92 | 2019-06-07 08:59:17 +0000 | [diff] [blame] | 377 | |
Corentin Wallez | 7fe6efb | 2020-02-05 17:16:05 +0000 | [diff] [blame] | 378 | directory = os.path.dirname(output_path) |
Corentin Wallez | 20a6ca0 | 2022-12-19 11:14:54 +0000 | [diff] [blame] | 379 | os.makedirs(directory, exist_ok=True) |
Corentin Wallez | 7fe6efb | 2020-02-05 17:16:05 +0000 | [diff] [blame] | 380 | |
| 381 | with open(output_path, 'w') as outfile: |
| 382 | outfile.write(output.content) |