#!/usr/bin/env python # # Copyright 2014 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """Renders one or more template files using the Jinja template engine.""" import codecs import optparse import os import sys from util import build_utils sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir)) from pylib.constants import host_paths # Import jinja2 from third_party/jinja2 sys.path.append(os.path.join(host_paths.DIR_SOURCE_ROOT, 'third_party')) import jinja2 # pylint: disable=F0401 class RecordingFileSystemLoader(jinja2.FileSystemLoader): '''A FileSystemLoader that stores a list of loaded templates.''' def __init__(self, searchpath): jinja2.FileSystemLoader.__init__(self, searchpath) self.loaded_templates = set() def get_source(self, environment, template): contents, filename, uptodate = jinja2.FileSystemLoader.get_source( self, environment, template) self.loaded_templates.add(os.path.relpath(filename)) return contents, filename, uptodate def get_loaded_templates(self): return list(self.loaded_templates) def ProcessFile(env, input_filename, loader_base_dir, output_filename, variables): input_rel_path = os.path.relpath(input_filename, loader_base_dir) template = env.get_template(input_rel_path) output = template.render(variables) with codecs.open(output_filename, 'w', 'utf-8') as output_file: output_file.write(output) def ProcessFiles(env, input_filenames, loader_base_dir, inputs_base_dir, outputs_zip, variables): with build_utils.TempDir() as temp_dir: for input_filename in input_filenames: relpath = os.path.relpath(os.path.abspath(input_filename), os.path.abspath(inputs_base_dir)) if relpath.startswith(os.pardir): raise Exception('input file %s is not contained in inputs base dir %s' % (input_filename, inputs_base_dir)) output_filename = os.path.join(temp_dir, relpath) parent_dir = os.path.dirname(output_filename) build_utils.MakeDirectory(parent_dir) ProcessFile(env, input_filename, loader_base_dir, output_filename, variables) build_utils.ZipDir(outputs_zip, temp_dir) def main(): parser = optparse.OptionParser() build_utils.AddDepfileOption(parser) parser.add_option('--inputs', help='The template files to process.') parser.add_option('--output', help='The output file to generate. Valid ' 'only if there is a single input.') parser.add_option('--outputs-zip', help='A zip file containing the processed ' 'templates. Required if there are multiple inputs.') parser.add_option('--inputs-base-dir', help='A common ancestor directory of ' 'the inputs. Each output\'s path in the output zip will ' 'match the relative path from INPUTS_BASE_DIR to the ' 'input. Required if --output-zip is given.') parser.add_option('--loader-base-dir', help='Base path used by the template ' 'loader. Must be a common ancestor directory of ' 'the inputs. Defaults to DIR_SOURCE_ROOT.', default=host_paths.DIR_SOURCE_ROOT) parser.add_option('--variables', help='Variables to be made available in the ' 'template processing environment, as a GYP list (e.g. ' '--variables "channel=beta mstone=39")', default='') options, args = parser.parse_args() build_utils.CheckOptions(options, parser, required=['inputs']) inputs = build_utils.ParseGypList(options.inputs) if (options.output is None) == (options.outputs_zip is None): parser.error('Exactly one of --output and --output-zip must be given') if options.output and len(inputs) != 1: parser.error('--output cannot be used with multiple inputs') if options.outputs_zip and not options.inputs_base_dir: parser.error('--inputs-base-dir must be given when --output-zip is used') if args: parser.error('No positional arguments should be given.') variables = {} for v in build_utils.ParseGypList(options.variables): if '=' not in v: parser.error('--variables argument must contain "=": ' + v) name, _, value = v.partition('=') variables[name] = value loader = RecordingFileSystemLoader(options.loader_base_dir) env = jinja2.Environment(loader=loader, undefined=jinja2.StrictUndefined, line_comment_prefix='##') if options.output: ProcessFile(env, inputs[0], options.loader_base_dir, options.output, variables) else: ProcessFiles(env, inputs, options.loader_base_dir, options.inputs_base_dir, options.outputs_zip, variables) if options.depfile: deps = loader.get_loaded_templates() + build_utils.GetPythonDependencies() build_utils.WriteDepfile(options.depfile, deps) if __name__ == '__main__': main()