| #!/usr/bin/python |
| # Copyright 2018 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. |
| |
| """Performs pull of google-input-tools from local clone of GitHub repository.""" |
| |
| import json |
| import logging |
| import optparse |
| import os |
| import re |
| import shutil |
| import subprocess |
| |
| _BASE_REGEX_STRING = r'^\s*goog\.%s\(\s*[\'"](.+)[\'"]\s*\)' |
| require_regex = re.compile(_BASE_REGEX_STRING % 'require') |
| provide_regex = re.compile(_BASE_REGEX_STRING % 'provide') |
| |
| # Entry-points required to build a virtual keyboard. |
| namespaces = [ |
| 'i18n.input.chrome.inputview.Controller', |
| 'i18n.input.chrome.inputview.content.compact.letter', |
| 'i18n.input.chrome.inputview.content.compact.util', |
| 'i18n.input.chrome.inputview.content.compact.symbol', |
| 'i18n.input.chrome.inputview.content.compact.more', |
| 'i18n.input.chrome.inputview.content.compact.numberpad', |
| 'i18n.input.chrome.inputview.content.ContextlayoutUtil', |
| 'i18n.input.chrome.inputview.content.util', |
| 'i18n.input.chrome.inputview.EmojiType', |
| 'i18n.input.chrome.inputview.layouts.CompactSpaceRow', |
| 'i18n.input.chrome.inputview.layouts.RowsOf101', |
| 'i18n.input.chrome.inputview.layouts.RowsOf102', |
| 'i18n.input.chrome.inputview.layouts.RowsOfCompact', |
| 'i18n.input.chrome.inputview.layouts.RowsOfJP', |
| 'i18n.input.chrome.inputview.layouts.RowsOfNumberpad', |
| 'i18n.input.chrome.inputview.layouts.SpaceRow', |
| 'i18n.input.chrome.inputview.layouts.util', |
| 'i18n.input.chrome.inputview.layouts.material.CompactSpaceRow', |
| 'i18n.input.chrome.inputview.layouts.material.RowsOf101', |
| 'i18n.input.chrome.inputview.layouts.material.RowsOf102', |
| 'i18n.input.chrome.inputview.layouts.material.RowsOfCompact', |
| 'i18n.input.chrome.inputview.layouts.material.RowsOfJP', |
| 'i18n.input.chrome.inputview.layouts.material.RowsOfNumberpad', |
| 'i18n.input.chrome.inputview.layouts.material.SpaceRow', |
| 'i18n.input.chrome.inputview.layouts.material.util', |
| 'i18n.input.hwt.util' |
| ] |
| |
| # Any additional required files. |
| extras = [ |
| 'common.css', |
| 'emoji.css' |
| ] |
| |
| |
| def process_file(filename): |
| """Extracts provided and required namespaces. |
| |
| Description: |
| Scans Javascript file for provied and required namespaces. |
| |
| Args: |
| filename: name of the file to process. |
| |
| Returns: |
| Pair of lists, where the first list contains namespaces provided by the file |
| and the second contains a list of requirements. |
| """ |
| provides = [] |
| requires = [] |
| file_handle = open(filename, 'r') |
| try: |
| for line in file_handle: |
| if re.match(require_regex, line): |
| requires.append(re.search(require_regex, line).group(1)) |
| if re.match(provide_regex, line): |
| provides.append(re.search(provide_regex, line).group(1)) |
| finally: |
| file_handle.close() |
| return provides, requires |
| |
| |
| def expand_directories(refs): |
| """Expands any directory references into inputs. |
| |
| Description: |
| Looks for any directories in the provided references. Found directories |
| are recursively searched for .js files. |
| |
| Args: |
| refs: a list of directories. |
| |
| Returns: |
| Pair of maps, where the first maps each namepace to the filename that |
| provides the namespace, and the second maps a filename to prerequisite |
| namespaces. |
| """ |
| providers = {} |
| requirements = {} |
| for ref in refs: |
| if os.path.isdir(ref): |
| for (root, _, files) in os.walk(ref): |
| for name in files: |
| if name.endswith('js'): |
| filename = os.path.join(root, name) |
| provides, requires = process_file(filename) |
| for p in provides: |
| providers[p] = filename |
| requirements[filename] = [] |
| for r in requires: |
| requirements[filename].append(r) |
| return providers, requirements |
| |
| |
| def extract_dependencies(namespace, providers, requirements, dependencies): |
| """Recursively extracts all dependencies for a namespace. |
| |
| Description: |
| Recursively extracts all dependencies for a namespace. |
| |
| Args: |
| namespace: The namespace to process. |
| providers: Mapping of namespace to filename that provides the namespace. |
| requirements: Mapping of filename to a list of prerequisite namespaces. |
| dependencies: List of files required to build inputview. |
| Returns: |
| """ |
| |
| if namespace in providers: |
| filename = providers[namespace] |
| if filename not in dependencies: |
| for ns in requirements[filename]: |
| extract_dependencies(ns, providers, requirements, dependencies) |
| dependencies.add(filename) |
| |
| |
| def home_dir(): |
| """Resolves the user's home directory.""" |
| |
| return os.path.expanduser('~') |
| |
| |
| def expand_path_relative_to_home(path): |
| """Resolves a path that is relative to the home directory. |
| |
| Args: |
| path: Relative path. |
| |
| Returns: |
| Resolved path. |
| """ |
| |
| return os.path.join(os.path.expanduser('~'), path) |
| |
| |
| def get_google_input_tools_sandbox_from_options(options): |
| """Generate the input-input-tools path from the --input flag. |
| |
| Args: |
| options: Flags to update.py. |
| Returns: |
| Path to the google-input-tools sandbox. |
| """ |
| |
| path = options.input |
| if not path: |
| path = expand_path_relative_to_home('google-input-tools') |
| print 'Unspecified path for google-input-tools. Defaulting to %s' % path |
| return path |
| |
| |
| def get_closure_library_sandbox_from_options(options): |
| """Generate the closure-library path from the --input flag. |
| |
| Args: |
| options: Flags to update.py. |
| Returns: |
| Path to the closure-library sandbox. |
| """ |
| |
| path = options.lib |
| if not path: |
| path = expand_path_relative_to_home('closure-library') |
| print 'Unspecified path for closure-library. Defaulting to %s' % path |
| return path |
| |
| |
| def copy_file(source, target): |
| """Copies a file from the source to the target location. |
| |
| Args: |
| source: Path to the source file to copy. |
| target: Path to the target location to copy the file. |
| """ |
| |
| if not os.path.exists(os.path.dirname(target)): |
| os.makedirs(os.path.dirname(target)) |
| shutil.copy(source, target) |
| # Ensure correct file permissions. |
| if target.endswith('py'): |
| subprocess.call(['chmod', '+x', target]) |
| else: |
| subprocess.call(['chmod', '-x', target]) |
| |
| |
| def update_file(filename, input_source, closure_source, target_files): |
| """Updates files in third_party/google_input_tools. |
| |
| Args: |
| filename: The file to update. |
| input_source: Root of the google_input_tools sandbox. |
| closure_source: Root of the closure_library sandbox. |
| target_files: List of relative paths to target files. |
| """ |
| |
| target = '' |
| if filename.startswith(input_source): |
| target = os.path.join('src', filename[len(input_source)+1:]) |
| elif filename.startswith(closure_source): |
| target = os.path.join('third_party/closure_library', |
| filename[len(closure_source)+1:]) |
| if target: |
| copy_file(filename, target) |
| target_files.append(os.path.relpath(target, os.getcwd())) |
| |
| |
| def generate_build_file(target_files): |
| """Updates inputview.json. |
| |
| Args: |
| target_files: List of files required to build inputview.js. |
| """ |
| |
| sorted_files = sorted(target_files) |
| with open('inputview.json', 'w') as file_handle: |
| json_data = {'inputview_sources': sorted_files} |
| json_str = json.dumps(json_data, indent=2, separators=(',', ': ')) |
| file_handle.write(json_str.replace('\"', '\'')) |
| |
| |
| def copy_dir(input_path, sub_dir): |
| """Copies all files in a subdirectory of google-input-tools. |
| |
| Description: |
| Recursive copy of a directory under google-input-tools. Used to copy |
| localization and resource files. |
| |
| Args: |
| input_path: Path to the google-input-tools-sandbox. |
| sub_dir: Subdirectory to copy within google-input-tools sandbox. |
| """ |
| source_dir = os.path.join(input_path, 'chrome', 'os', sub_dir) |
| for (root, _, files) in os.walk(source_dir): |
| for name in files: |
| filename = os.path.join(root, name) |
| relative_path = filename[len(source_dir) + 1:] |
| target = os.path.join('src', 'chrome', 'os', sub_dir, |
| relative_path) |
| copy_file(filename, target) |
| |
| |
| def main(): |
| """The entrypoint for this script.""" |
| |
| logging.basicConfig(format='update.py: %(message)s', level=logging.INFO) |
| |
| usage = 'usage: %prog [options] arg' |
| parser = optparse.OptionParser(usage) |
| parser.add_option('-i', |
| '--input', |
| dest='input', |
| action='append', |
| help='Path to the google-input-tools sandbox.') |
| parser.add_option('-l', |
| '--lib', |
| dest='lib', |
| action='store', |
| help='Path to the closure-library sandbox.') |
| |
| (options, _) = parser.parse_args() |
| |
| input_path = get_google_input_tools_sandbox_from_options(options)[0] |
| closure_library_path = get_closure_library_sandbox_from_options(options)[0] |
| |
| if not os.path.isdir(input_path): |
| print 'Could not find google-input-tools sandbox.' |
| exit(1) |
| if not os.path.isdir(closure_library_path): |
| print 'Could not find closure-library sandbox.' |
| exit(1) |
| |
| (providers, requirements) = expand_directories([ |
| os.path.join(input_path, 'chrome'), |
| closure_library_path]) |
| |
| dependencies = set() |
| for name in namespaces: |
| extract_dependencies(name, providers, requirements, dependencies) |
| |
| target_files = [] |
| for name in dependencies: |
| update_file(name, input_path, closure_library_path, target_files) |
| |
| generate_build_file(target_files) |
| |
| # Copy resources |
| copy_dir(input_path, 'inputview/_locales') |
| copy_dir(input_path, 'inputview/images') |
| copy_dir(input_path, 'inputview/config') |
| copy_dir(input_path, 'inputview/layouts') |
| copy_dir(input_path, 'sounds') |
| |
| # Copy extra support files. |
| for name in extras: |
| source = os.path.join(input_path, 'chrome', 'os', 'inputview', name) |
| target = os.path.join('src', 'chrome', 'os', 'inputview', name) |
| copy_file(source, target) |
| |
| |
| if __name__ == '__main__': |
| main() |