3 # Copyright 2014 The Chromium Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file.
7 """Renders one or more template files using the Jinja template engine."""
14 from util
import build_utils
16 # Import jinja2 from third_party/jinja2
17 sys
.path
.append(os
.path
.join(os
.path
.dirname(__file__
), '../../../third_party'))
18 import jinja2
# pylint: disable=F0401
21 class RecordingFileSystemLoader(jinja2
.FileSystemLoader
):
22 '''A FileSystemLoader that stores a list of loaded templates.'''
23 def __init__(self
, searchpath
):
24 jinja2
.FileSystemLoader
.__init
__(self
, searchpath
)
25 self
.loaded_templates
= set()
27 def get_source(self
, environment
, template
):
28 contents
, filename
, uptodate
= jinja2
.FileSystemLoader
.get_source(
29 self
, environment
, template
)
30 self
.loaded_templates
.add(os
.path
.relpath(filename
))
31 return contents
, filename
, uptodate
33 def get_loaded_templates(self
):
34 return list(self
.loaded_templates
)
37 def ProcessFile(env
, input_filename
, output_filename
, variables
):
38 input_rel_path
= os
.path
.relpath(input_filename
, build_utils
.CHROMIUM_SRC
)
39 template
= env
.get_template(input_rel_path
)
40 output
= template
.render(variables
)
41 with codecs
.open(output_filename
, 'w', 'utf-8') as output_file
:
42 output_file
.write(output
)
45 def ProcessFiles(env
, input_filenames
, inputs_base_dir
, outputs_zip
, variables
):
46 with build_utils
.TempDir() as temp_dir
:
47 for input_filename
in input_filenames
:
48 relpath
= os
.path
.relpath(os
.path
.abspath(input_filename
),
49 os
.path
.abspath(inputs_base_dir
))
50 if relpath
.startswith(os
.pardir
):
51 raise Exception('input file %s is not contained in inputs base dir %s'
52 % input_filename
, inputs_base_dir
)
54 output_filename
= os
.path
.join(temp_dir
, relpath
)
55 parent_dir
= os
.path
.dirname(output_filename
)
56 build_utils
.MakeDirectory(parent_dir
)
57 ProcessFile(env
, input_filename
, output_filename
, variables
)
59 build_utils
.ZipDir(outputs_zip
, temp_dir
)
63 parser
= optparse
.OptionParser()
64 build_utils
.AddDepfileOption(parser
)
65 parser
.add_option('--inputs', help='The template files to process.')
66 parser
.add_option('--output', help='The output file to generate. Valid '
67 'only if there is a single input.')
68 parser
.add_option('--outputs-zip', help='A zip file containing the processed '
69 'templates. Required if there are multiple inputs.')
70 parser
.add_option('--inputs-base-dir', help='A common ancestor directory of '
71 'the inputs. Each output\'s path in the output zip will '
72 'match the relative path from INPUTS_BASE_DIR to the '
73 'input. Required if --output-zip is given.')
74 parser
.add_option('--variables', help='Variables to be made available in the '
75 'template processing environment, as a GYP list (e.g. '
76 '--variables "channel=beta mstone=39")', default
='')
77 options
, args
= parser
.parse_args()
79 build_utils
.CheckOptions(options
, parser
, required
=['inputs'])
80 inputs
= build_utils
.ParseGypList(options
.inputs
)
82 if (options
.output
is None) == (options
.outputs_zip
is None):
83 parser
.error('Exactly one of --output and --output-zip must be given')
84 if options
.output
and len(inputs
) != 1:
85 parser
.error('--output cannot be used with multiple inputs')
86 if options
.outputs_zip
and not options
.inputs_base_dir
:
87 parser
.error('--inputs-base-dir must be given when --output-zip is used')
89 parser
.error('No positional arguments should be given.')
92 for v
in build_utils
.ParseGypList(options
.variables
):
94 parser
.error('--variables argument must contain "=": ' + v
)
95 name
, _
, value
= v
.partition('=')
96 variables
[name
] = value
98 loader
= RecordingFileSystemLoader(build_utils
.CHROMIUM_SRC
)
99 env
= jinja2
.Environment(loader
=loader
, undefined
=jinja2
.StrictUndefined
,
100 line_comment_prefix
='##')
102 ProcessFile(env
, inputs
[0], options
.output
, variables
)
104 ProcessFiles(env
, inputs
, options
.inputs_base_dir
, options
.outputs_zip
,
108 deps
= loader
.get_loaded_templates() + build_utils
.GetPythonDependencies()
109 build_utils
.WriteDepfile(options
.depfile
, deps
)
112 if __name__
== '__main__':