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
, loader_base_dir
, output_filename
,
39 input_rel_path
= os
.path
.relpath(input_filename
, loader_base_dir
)
40 template
= env
.get_template(input_rel_path
)
41 output
= template
.render(variables
)
42 with codecs
.open(output_filename
, 'w', 'utf-8') as output_file
:
43 output_file
.write(output
)
46 def ProcessFiles(env
, input_filenames
, loader_base_dir
, inputs_base_dir
,
47 outputs_zip
, variables
):
48 with build_utils
.TempDir() as temp_dir
:
49 for input_filename
in input_filenames
:
50 relpath
= os
.path
.relpath(os
.path
.abspath(input_filename
),
51 os
.path
.abspath(inputs_base_dir
))
52 if relpath
.startswith(os
.pardir
):
53 raise Exception('input file %s is not contained in inputs base dir %s'
54 % (input_filename
, inputs_base_dir
))
56 output_filename
= os
.path
.join(temp_dir
, relpath
)
57 parent_dir
= os
.path
.dirname(output_filename
)
58 build_utils
.MakeDirectory(parent_dir
)
59 ProcessFile(env
, input_filename
, loader_base_dir
, output_filename
,
62 build_utils
.ZipDir(outputs_zip
, temp_dir
)
66 parser
= optparse
.OptionParser()
67 build_utils
.AddDepfileOption(parser
)
68 parser
.add_option('--inputs', help='The template files to process.')
69 parser
.add_option('--output', help='The output file to generate. Valid '
70 'only if there is a single input.')
71 parser
.add_option('--outputs-zip', help='A zip file containing the processed '
72 'templates. Required if there are multiple inputs.')
73 parser
.add_option('--inputs-base-dir', help='A common ancestor directory of '
74 'the inputs. Each output\'s path in the output zip will '
75 'match the relative path from INPUTS_BASE_DIR to the '
76 'input. Required if --output-zip is given.')
77 parser
.add_option('--loader-base-dir', help='Base path used by the template '
78 'loader. Must be a common ancestor directory of '
79 'the inputs. Defaults to CHROMIUM_SRC.',
80 default
=build_utils
.CHROMIUM_SRC
)
81 parser
.add_option('--variables', help='Variables to be made available in the '
82 'template processing environment, as a GYP list (e.g. '
83 '--variables "channel=beta mstone=39")', default
='')
84 options
, args
= parser
.parse_args()
86 build_utils
.CheckOptions(options
, parser
, required
=['inputs'])
87 inputs
= build_utils
.ParseGypList(options
.inputs
)
89 if (options
.output
is None) == (options
.outputs_zip
is None):
90 parser
.error('Exactly one of --output and --output-zip must be given')
91 if options
.output
and len(inputs
) != 1:
92 parser
.error('--output cannot be used with multiple inputs')
93 if options
.outputs_zip
and not options
.inputs_base_dir
:
94 parser
.error('--inputs-base-dir must be given when --output-zip is used')
96 parser
.error('No positional arguments should be given.')
99 for v
in build_utils
.ParseGypList(options
.variables
):
101 parser
.error('--variables argument must contain "=": ' + v
)
102 name
, _
, value
= v
.partition('=')
103 variables
[name
] = value
105 loader
= RecordingFileSystemLoader(options
.loader_base_dir
)
106 env
= jinja2
.Environment(loader
=loader
, undefined
=jinja2
.StrictUndefined
,
107 line_comment_prefix
='##')
109 ProcessFile(env
, inputs
[0], options
.loader_base_dir
, options
.output
,
112 ProcessFiles(env
, inputs
, options
.loader_base_dir
, options
.inputs_base_dir
,
113 options
.outputs_zip
, variables
)
116 deps
= loader
.get_loaded_templates() + build_utils
.GetPythonDependencies()
117 build_utils
.WriteDepfile(options
.depfile
, deps
)
120 if __name__
== '__main__':