123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- # Copyright 2020 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.
- import argparse
- import errno
- import io
- import json
- import os
- import sys
- # For Node, EvaluateExpression
- import grit.node.base
- # For CheckConditionalElements
- import grit.format.html_inline
- _CWD = os.getcwd()
- class PreprocessIfExprNode(grit.node.base.Node):
- def __init__(self):
- super(PreprocessIfExprNode, self).__init__()
- def PreprocessIfExpr(self, content, removal_comments_extension):
- return grit.format.html_inline.CheckConditionalElements(
- self, content, removal_comments_extension)
- def EvaluateCondition(self, expr):
- return grit.node.base.Node.EvaluateExpression(expr, self.defines,
- self.target_platform, {})
- def SetDefines(self, defines):
- self.defines = defines
- def SetTargetPlatform(self, target_platform):
- self.target_platform = target_platform
- @staticmethod
- def Construct(defines, target_platform):
- node = PreprocessIfExprNode()
- node.SetDefines(defines)
- node.SetTargetPlatform(target_platform or sys.platform)
- return node
- def ParseDefinesArg(definesArg):
- defines = {}
- for define in definesArg:
- parts = [part.strip() for part in define.split('=', 1)]
- name = parts[0]
- val = True if len(parts) == 1 else parts[1]
- if (val == "1" or val == "true"):
- val = True
- elif (val == "0" or val == "false"):
- val = False
- defines[name] = val
- return defines
- def ExtensionForComments(input_file):
- """Get the file extension that determines the comment style.
- Returns the file extension that determines the format of the
- 'grit-removed-lines' comments. '.ts' or '.js' will produce '/*...*/'-style
- comments, '.html; will produce '<!-- -->'-style comments.
- """
- split = os.path.splitext(input_file)
- extension = split[1]
- # .html.ts and .html.js files should still use HTML comments.
- if os.path.splitext(split[0])[1] == '.html':
- extension = '.html'
- return extension
- def main(argv):
- parser = argparse.ArgumentParser()
- parser.add_argument('--in-folder', required=True)
- parser.add_argument('--out-folder', required=True)
- parser.add_argument('--out-manifest')
- parser.add_argument('--in-files', required=True, nargs="*")
- parser.add_argument('-D', '--defines', action='append')
- parser.add_argument('-E', '--environment')
- parser.add_argument('-t', '--target')
- parser.add_argument('--enable_removal_comments', action='store_true')
- args = parser.parse_args(argv)
- in_folder = os.path.normpath(os.path.join(_CWD, args.in_folder))
- out_folder = os.path.normpath(os.path.join(_CWD, args.out_folder))
- defines = ParseDefinesArg(args.defines)
- node = PreprocessIfExprNode.Construct(defines, args.target)
- for input_file in args.in_files:
- in_path = os.path.join(in_folder, input_file)
- content = ""
- with io.open(in_path, encoding='utf-8', mode='r') as f:
- content = f.read()
- removal_comments_extension = None # None means no removal comments
- if args.enable_removal_comments:
- removal_comments_extension = ExtensionForComments(input_file)
- try:
- preprocessed = node.PreprocessIfExpr(content, removal_comments_extension)
- except:
- raise Exception('Error processing %s' % in_path)
- out_path = os.path.join(out_folder, input_file)
- out_dir = os.path.dirname(out_path)
- assert out_dir.startswith(out_folder), \
- 'Cannot preprocess files to locations not under %s.' % out_dir
- try:
- os.makedirs(out_dir)
- except OSError as e:
- # Ignore directory exists errors. This can happen if two build rules
- # for overlapping directories hit the makedirs line at the same time.
- if e.errno != errno.EEXIST:
- raise
- # Delete the target file before witing it, as it may be hardlinked to other
- # files, which can break the build. This is the case in particular if the
- # file was "copied" to different locations with GN (as GN's copy is actually
- # a hard link under the hood). See https://crbug.com/1332497
- if os.path.exists(out_path):
- os.remove(out_path)
- # Detect and delete any stale TypeScript files present in the output folder,
- # corresponding to input .js files, since they can get picked up by
- # subsequent ts_library() invocations and cause transient build failures.
- # This can happen when a file is migrated from JS to TS and a bot is
- # switched from building a later CL to building an earlier CL.
- [pathname, extension] = os.path.splitext(out_path)
- if extension == '.js':
- to_check = pathname + '.ts'
- if os.path.exists(to_check):
- os.remove(to_check)
- with io.open(out_path, mode='wb') as f:
- f.write(preprocessed.encode('utf-8'))
- if args.out_manifest:
- manifest_data = {}
- manifest_data['base_dir'] = '%s' % args.out_folder
- manifest_data['files'] = args.in_files
- manifest_file = io.open(
- os.path.normpath(os.path.join(_CWD, args.out_manifest)), 'w',
- encoding='utf-8', newline='\n')
- json.dump(manifest_data, manifest_file)
- return
- if __name__ == '__main__':
- main(sys.argv[1:])
|