[Files app]: Add scripts for JS modules conversion
Command: ui/file_manager/base/tools/modules.sh [1] [2] [1] build directory (e.g. out/Release) [2] path of JS file to convert (relative to chromium/src) What does the script do: 1. Update BUILD.gn to enable conversion of JS file. 2. Add exports. 3. Run closure compiler. 4. Parse closure compiler output to retrieve unknown types/variables/functions. 6. Parse these dependencies to find relevant exported types/variables/functions. 7. When found, update BUILD.gn and JS file. Made sure that: The script doesn't add any irrelevant import/export. No duplicate lines are added. Steps 3 to 7 are ran until the closure compiler output 'X error(s), Y warning(s), Z% typed' doesn't change. The closure compiler is ran at least 3 times (except if no imports are found), sometimes more. Bug: 1133186 Change-Id: I1528c8187bc1476d6add1bcb14a0d4fd1b64a4fe Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2550328 Commit-Queue: Jeremie Boulic <jboulic@chromium.org> Reviewed-by: Luciano Pacheco <lucmult@chromium.org> Cr-Commit-Position: refs/heads/master@{#834181}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
b8c6670fe3
commit
2ce3bfdf66
ui/file_manager/base/tools
774
ui/file_manager/base/tools/modules.py
Executable file
774
ui/file_manager/base/tools/modules.py
Executable file
@ -0,0 +1,774 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright (c) 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 os
|
||||
import re
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
|
||||
def get_file_lines(file_path):
|
||||
'''Returns list of lines from a file. '\n' at the end of lines are
|
||||
removed.'''
|
||||
with open(file_path, 'r') as f:
|
||||
file_lines = f.readlines()
|
||||
return [l.rstrip() for l in file_lines]
|
||||
|
||||
|
||||
def save_file_lines(file_path, file_lines):
|
||||
'''Saves file_lines in file pointed by `file_path`, '\n' at the end of
|
||||
lines are added back.'''
|
||||
with open(file_path, 'w') as f:
|
||||
for line in file_lines:
|
||||
f.write(line + '\n')
|
||||
|
||||
|
||||
def get_relative_dependency(path, dir_path):
|
||||
'''Given a file path, returns formatted BUILD.gn dependency.
|
||||
|
||||
Parameters:
|
||||
path: file path to format as relative dependency.
|
||||
dir_path: directory from which the relative dependency is calculated.
|
||||
|
||||
Returns:
|
||||
Formatted dependency.
|
||||
The 3 cases illustrated below are handled:
|
||||
- ":file_type.m" if returned if file_type.js is in dir_path.
|
||||
- "metadata:metadata_model.m" is returned if metadata/metadata_model.js
|
||||
is in dir_path.
|
||||
- "//ui/file_manager/externs:volume_manager.m" is returned if
|
||||
ui/file_manager/externs is not included in dir_path.
|
||||
'''
|
||||
split_path = path.split('/')
|
||||
file_name = split_path.pop().replace('.js', '.m')
|
||||
split_dir_path = dir_path.split('/')
|
||||
while (len(split_path) > 0 and len(split_dir_path) > 0
|
||||
and split_path[0] == split_dir_path[0]):
|
||||
del split_path[0]
|
||||
del split_dir_path[0]
|
||||
if len(split_dir_path) == 0:
|
||||
# The dependency is within dir_path.
|
||||
return '/'.join(split_path) + ':' + file_name
|
||||
else:
|
||||
return '//' + re.sub(r"\/[a-zA-Z0-9_]+\.js", ":", path) + file_name
|
||||
|
||||
|
||||
def get_index_substr(file_lines, substr):
|
||||
'''Finds first occurence of `substr` and returns its index in
|
||||
`file_lines`.'''
|
||||
for i, line in enumerate(file_lines):
|
||||
if substr in line:
|
||||
return i
|
||||
return -1
|
||||
|
||||
|
||||
def get_end_of_copyright(file_lines):
|
||||
'''Get index of last line of copyright (after checking that the section
|
||||
exists).'''
|
||||
index = get_index_substr(file_lines, 'Copyright 20')
|
||||
if index < 0:
|
||||
return -1
|
||||
while index < len(file_lines) and file_lines[index].startswith('// '):
|
||||
index += 1
|
||||
return index - 1
|
||||
|
||||
|
||||
def get_end_of_file_overview(file_lines):
|
||||
'''Get index of last line of file_overview (after checking that the section
|
||||
exists).'''
|
||||
index = get_index_substr(file_lines, '@fileoverview')
|
||||
if index < 0:
|
||||
return -1
|
||||
while index < len(file_lines) and file_lines[index] != ' */':
|
||||
index += 1
|
||||
return index
|
||||
|
||||
|
||||
def get_last_index_of_line_starting_with(file_lines, substr):
|
||||
'''Get last line starting with the given `substr`.'''
|
||||
last_index = -1
|
||||
for i, line in enumerate(file_lines):
|
||||
if line.startswith(substr):
|
||||
last_index = i
|
||||
return last_index
|
||||
|
||||
|
||||
def get_end_of_build_imports(file_lines):
|
||||
'''In BUILD.gn, gets index of last 'import' line at the beginning of the
|
||||
file.'''
|
||||
index = get_last_index_of_line_starting_with(file_lines, 'import("//')
|
||||
if index < 0:
|
||||
return get_end_of_copyright(file_lines)
|
||||
return index
|
||||
|
||||
|
||||
def add_js_library(file_lines, file_name, dir_path):
|
||||
'''Adds js_library target in BUILD.gn.'''
|
||||
i = file_lines.index('js_library("%s") {' % (file_name))
|
||||
|
||||
# Check that the library does exist and hasn't already been converted.
|
||||
if i < 0:
|
||||
print 'Unable to find js_library for {}'.format(file_name)
|
||||
return False
|
||||
if 'js_library("%s.m") {' % (file_name) in file_lines:
|
||||
print 'js_library for {}.m already added'.format(file_name)
|
||||
return False
|
||||
|
||||
# Find the end of the library definition.
|
||||
while i < len(file_lines) and file_lines[i] != '}':
|
||||
i += 1
|
||||
i += 1
|
||||
if i == len(file_lines):
|
||||
print 'reached end of file'
|
||||
return False
|
||||
new_lines = '''
|
||||
js_library("%s.m") {
|
||||
sources = [ "$root_gen_dir/%s/%s.m.js" ]
|
||||
|
||||
extra_deps = [ ":modulize" ]
|
||||
}''' % (file_name, dir_path, file_name)
|
||||
file_lines[i:i] = new_lines.split('\n')
|
||||
return True
|
||||
|
||||
|
||||
def add_import_line(file_lines, variable, relative_path, is_unittest):
|
||||
'''Adds import line (import {...} from '...') in JS file.'''
|
||||
# Construct import line.
|
||||
import_line = 'import ' if is_unittest else '// #import '
|
||||
|
||||
# Check: existing relative path.
|
||||
i = get_index_substr(file_lines, relative_path)
|
||||
if i >= 0 and '}' in file_lines[i]:
|
||||
if variable + '}' in file_lines[i] or variable + ',' in file_lines[i]:
|
||||
return
|
||||
split_line = file_lines[i].split('}')
|
||||
file_lines[i] = '%s, %s}%s' % (split_line[0], variable, split_line[1])
|
||||
return
|
||||
import_line += "{%s} from '%s';" % (variable, relative_path)
|
||||
|
||||
if import_line in file_lines:
|
||||
return
|
||||
|
||||
# Add clang-format off/on if necessary.
|
||||
index = 0
|
||||
if '// clang-format off' in file_lines:
|
||||
index = file_lines.index('// clang-format off')
|
||||
else:
|
||||
# Go to the end of copyright and fileoverview.
|
||||
index = get_end_of_file_overview(file_lines)
|
||||
if index < 0:
|
||||
index = get_end_of_copyright(file_lines)
|
||||
if (index < 0):
|
||||
index = 0
|
||||
index += 1
|
||||
if len(import_line) > 80:
|
||||
index += 1
|
||||
file_lines.insert(index, '// clang-format off')
|
||||
|
||||
# Go to the last existing import line.
|
||||
last_import_line_index = get_last_index_of_line_starting_with(
|
||||
file_lines, 'import ')
|
||||
if last_import_line_index >= 0:
|
||||
index = last_import_line_index
|
||||
else:
|
||||
file_lines.insert(index + 1, '')
|
||||
file_lines.insert(index + 1, '// clang-format off')
|
||||
elif 'import ' not in file_lines[index + 1]:
|
||||
file_lines.insert(index, '')
|
||||
|
||||
# Add import line.
|
||||
file_lines.insert(index + 1, import_line)
|
||||
|
||||
|
||||
def add_namespace_rewrite(build_gn_path):
|
||||
build_file_lines = get_file_lines(build_gn_path)
|
||||
|
||||
# Add import("//ui/webui/resources/js/cr.gni").
|
||||
cr_gni = 'import("//ui/webui/resources/js/cr.gni")'
|
||||
modulizer_gni = 'import("//ui/webui/resources/tools/js_modulizer.gni")'
|
||||
if not cr_gni in build_file_lines:
|
||||
if not modulizer_gni in build_file_lines:
|
||||
raise ValueError('"js_modulizer.gni" not found')
|
||||
index = build_file_lines.index(modulizer_gni)
|
||||
build_file_lines.insert(index, cr_gni)
|
||||
|
||||
# Add namespace_rewrites = cr_namespace_rewrites.
|
||||
namespace_rewrite = ' namespace_rewrites = cr_namespace_rewrites'
|
||||
if not namespace_rewrite in build_file_lines:
|
||||
index = get_index_substr(build_file_lines,
|
||||
'js_modulizer("modulize") {')
|
||||
if index < 0:
|
||||
print 'No modulize rule found'
|
||||
return
|
||||
while index < len(build_file_lines) and build_file_lines[index] != '':
|
||||
index += 1
|
||||
index -= 1
|
||||
build_file_lines.insert(index, '')
|
||||
build_file_lines.insert(index + 1, namespace_rewrite)
|
||||
|
||||
save_file_lines(build_gn_path, build_file_lines)
|
||||
|
||||
|
||||
def add_hide_third_party(build_gn_path):
|
||||
build_file_lines = get_file_lines(build_gn_path)
|
||||
|
||||
hide_third_party = ' "hide_warnings_for=third_party/",'
|
||||
prefix_replacement_line = 'browser_resolver_prefix_replacements='
|
||||
if not hide_third_party in build_file_lines:
|
||||
index = get_index_substr(build_file_lines, prefix_replacement_line)
|
||||
if index < 0:
|
||||
print 'prefix replacement not found in "js_test_gen_html_modules"'
|
||||
return
|
||||
build_file_lines.insert(index + 1, hide_third_party)
|
||||
|
||||
save_file_lines(build_gn_path, build_file_lines)
|
||||
|
||||
|
||||
def add_dependency(file_lines, section_first_line, list_name, dependency_line):
|
||||
'''
|
||||
Add dependency in BUILD.gn.
|
||||
Parameters:
|
||||
file_lines: lines of BUILD.gn file.
|
||||
section_first_line: opening line of target to update.
|
||||
list_name: name of the dependency list, deps', 'input_files' etc...
|
||||
dependency_line: line to add to the dependency list to update.
|
||||
'''
|
||||
# Find a line that starts with deps.
|
||||
if not section_first_line in file_lines:
|
||||
print 'Unable to find ' + section_first_line
|
||||
return False
|
||||
|
||||
# Return False if dependency already found.
|
||||
if dependency_line in file_lines:
|
||||
return False
|
||||
|
||||
# Find index of `list_name`. Get index of 'sources = [' in case `list_name`
|
||||
# is not defined.
|
||||
rule_index = file_lines.index(section_first_line)
|
||||
sources_index = -1
|
||||
insertion_index = -1
|
||||
single_line_dependency_list = False
|
||||
|
||||
for i in range(rule_index, len(file_lines)):
|
||||
if 'sources = [' in file_lines[i]:
|
||||
# Jump to the end of the 'sources' list.
|
||||
while not ']' in file_lines[i]:
|
||||
i += 1
|
||||
sources_index = i
|
||||
if ' {} = '.format(list_name) in file_lines[i]:
|
||||
# Dependency line found.
|
||||
if file_lines[i].endswith(']'):
|
||||
single_line_dependency_list = True
|
||||
|
||||
# Jump to the end of the dependency list.
|
||||
while not ']' in file_lines[i]:
|
||||
i += 1
|
||||
insertion_index = i
|
||||
break
|
||||
if file_lines[i] == '}':
|
||||
# End of build rule.
|
||||
break
|
||||
|
||||
if insertion_index == -1:
|
||||
# Add dependency line, after sources if possible.
|
||||
index = sources_index + 1 if sources_index > 0 else rule_index + 1
|
||||
|
||||
# Define new list over 2 lines: 'list_name = [\n]'
|
||||
file_lines.insert(index, ' {} = ['.format(list_name))
|
||||
file_lines.insert(index + 1, ' ]')
|
||||
insertion_index = index + 1
|
||||
|
||||
if single_line_dependency_list:
|
||||
# If one-line dependency is found, rewrite it over 2 lines, as above.
|
||||
existing_dependency = file_lines[insertion_index].split(' ')[-2]
|
||||
file_lines[insertion_index] = ' {} = ['.format(list_name)
|
||||
file_lines[insertion_index + 1] = ' {},'.format(existing_dependency)
|
||||
file_lines[insertion_index + 2] = ' ]'
|
||||
|
||||
# Check for already imported dependency after reformatting.
|
||||
if file_lines[insertion_index + 1] == dependency_line:
|
||||
return False
|
||||
|
||||
insertion_index += 2
|
||||
|
||||
# Insert dependency.
|
||||
file_lines.insert(insertion_index, dependency_line)
|
||||
return True
|
||||
|
||||
|
||||
def update_build_gn_dependencies(dir_path, file_name, build_gn_path):
|
||||
print 'Updating BUILD.gn dependencies for ' + file_name
|
||||
|
||||
# Get file contents.
|
||||
file_lines = get_file_lines(build_gn_path)
|
||||
|
||||
# Edit file with modules-related targets.
|
||||
import_gni = 'import("//ui/webui/resources/tools/js_modulizer.gni")'
|
||||
if not import_gni in file_lines:
|
||||
index = get_end_of_build_imports(file_lines) + 1
|
||||
file_lines.insert(index, import_gni)
|
||||
new_lines = '''
|
||||
js_modulizer("modulize") {
|
||||
input_files = [
|
||||
]
|
||||
}'''
|
||||
file_lines.extend(new_lines.split('\n'))
|
||||
if not add_dependency(file_lines, 'group("closure_compile") {', 'deps',
|
||||
' ":closure_compile_jsmodules",'):
|
||||
return
|
||||
|
||||
# Add closure_compile_jsmodules rule.
|
||||
index = get_index_substr(file_lines,
|
||||
'js_type_check("closure_compile_module") {')
|
||||
if index < 0:
|
||||
print 'js_type_check("closure_compile_module") not found'
|
||||
return
|
||||
new_lines = '''\
|
||||
js_type_check("closure_compile_jsmodules") {
|
||||
uses_js_modules = true
|
||||
deps = [
|
||||
]
|
||||
}
|
||||
'''
|
||||
file_lines[index:index] = new_lines.split('\n')
|
||||
if not add_js_library(file_lines, file_name, dir_path):
|
||||
return
|
||||
|
||||
# Add closure dependency.
|
||||
if not add_dependency(
|
||||
file_lines, 'js_type_check("closure_compile_jsmodules") {', 'deps',
|
||||
' ":{}.m",'.format(file_name)):
|
||||
return
|
||||
|
||||
# Add 'modulize' dependency.
|
||||
if not add_dependency(file_lines, 'js_modulizer("modulize") {',
|
||||
'input_files', ' "{}.js",'.format(file_name)):
|
||||
return
|
||||
|
||||
# Save file contents.
|
||||
save_file_lines(build_gn_path, file_lines)
|
||||
|
||||
|
||||
def update_buid_gn_unittest_dependencies(build_gn_path, js_file_path,
|
||||
file_name):
|
||||
'''
|
||||
Rename _unittest.js to _unittest.m.js.
|
||||
Update test URL in file_manager_jstest.cc.
|
||||
Update BUILD.gn rules.
|
||||
It is assumed that if we're converting [file]_unittest.js, [file].js has
|
||||
been converted already.
|
||||
|
||||
Parameters:
|
||||
build_gn_path: Path of BUILD.gn file used by the file be converted.
|
||||
js_file_path: Path of the file to be converted
|
||||
(ui/file_manager/.../..._unittest.m.js)
|
||||
file_name: Name of the file to be converted (..._unittest)
|
||||
'''
|
||||
print 'Updating BUILD.gn dependencies for ' + file_name
|
||||
|
||||
main_build_path = 'ui/file_manager/BUILD.gn'
|
||||
file_manager_jstest_path = ('chrome/browser/chromeos/file_manager/'
|
||||
'file_manager_jstest.cc')
|
||||
|
||||
# Get file contents.
|
||||
build_file_lines = get_file_lines(build_gn_path)
|
||||
main_build_file_lines = get_file_lines(main_build_path)
|
||||
file_manager_jstest_lines = get_file_lines(file_manager_jstest_path)
|
||||
|
||||
# Rename _unittest.js to _unittest.m.js.
|
||||
original_js_file_path = js_file_path.replace('.m.js', '.js')
|
||||
if not os.path.isfile(js_file_path):
|
||||
if not os.path.isfile(original_js_file_path):
|
||||
e = 'Could not find neither file {} or {}'.format(
|
||||
js_file_path, original_js_file_path)
|
||||
raise ValueError(e)
|
||||
os.rename(original_js_file_path, js_file_path)
|
||||
|
||||
# Update chrome/browser/chromeos/file_manager/file_manager_jstest.cc
|
||||
# by updating RunTestURL for the unittest being converted.
|
||||
# [...]_unittest_gen.html -> [...]_unittest.m_gen.html.
|
||||
index = get_index_substr(file_manager_jstest_lines, file_name + '.m')
|
||||
if index < 0:
|
||||
index = get_index_substr(file_manager_jstest_lines, file_name)
|
||||
if index < 0:
|
||||
print 'file_manager_jstest.cc: {} not found'.format(file_name)
|
||||
file_manager_jstest_lines[index] = file_manager_jstest_lines[
|
||||
index].replace(file_name, file_name + '.m')
|
||||
|
||||
# Add "js_test_gen_html_modules" target.
|
||||
build_rule = 'js_test_gen_html("js_test_gen_html_modules") {'
|
||||
if not build_rule in build_file_lines:
|
||||
if 'js_test_gen_html("js_test_gen_html") {' in build_file_lines:
|
||||
index = build_file_lines.index(
|
||||
'js_test_gen_html("js_test_gen_html") {') - 1
|
||||
new_lines = r'''
|
||||
js_test_gen_html("js_test_gen_html_modules") {
|
||||
deps = [
|
||||
]
|
||||
js_module = true
|
||||
|
||||
closure_flags =
|
||||
strict_error_checking_closure_args + [
|
||||
"js_module_root=./gen/ui",
|
||||
"js_module_root=../../ui",
|
||||
"browser_resolver_prefix_replacements=\"chrome://test/=./\"",
|
||||
]
|
||||
}'''
|
||||
build_file_lines[index:index] = new_lines.split('\n')
|
||||
else:
|
||||
e = '"js_test_gen_html" build target not found on {}'.format(
|
||||
build_gn_path)
|
||||
raise ValueError(e)
|
||||
|
||||
# Add ":js_test_gen_html_modules_type_check_auto", in closure_compile deps.
|
||||
index = get_index_substr(build_file_lines,
|
||||
':js_test_gen_html_modules_type_check_auto')
|
||||
if index < 0:
|
||||
index = get_index_substr(build_file_lines,
|
||||
':js_test_gen_html_type_check_auto')
|
||||
if index < 0:
|
||||
e = '"js_test_gen_html_type_check_auto" dependency not found'
|
||||
raise ValueError(e)
|
||||
else:
|
||||
new_line = ' ":js_test_gen_html_modules_type_check_auto",'
|
||||
build_file_lines.insert(index, new_line)
|
||||
|
||||
# In BUILD.gn, update js_unittest target to .m
|
||||
js_unittest = 'js_unittest("%s") {' % (file_name)
|
||||
js_unittest_m = 'js_unittest("%s.m") {' % (file_name)
|
||||
if js_unittest in build_file_lines:
|
||||
index = build_file_lines.index(js_unittest)
|
||||
build_file_lines[index] = js_unittest_m
|
||||
# Remove dependencies.
|
||||
while index < len(
|
||||
build_file_lines) and not 'deps =' in build_file_lines[index]:
|
||||
index += 1
|
||||
if ']' in build_file_lines[index]:
|
||||
build_file_lines[index] = ' deps = ['
|
||||
build_file_lines.insert(index, ' ]')
|
||||
else:
|
||||
index += 1
|
||||
while index < len(
|
||||
build_file_lines) and not ']' in build_file_lines[index]:
|
||||
del build_file_lines[index]
|
||||
else:
|
||||
if not js_unittest_m in build_file_lines:
|
||||
print 'Unable to find "{}" target'.format(file_name)
|
||||
|
||||
# Move unittest dependency from js_test_gen_html to
|
||||
# js_test_gen_html_modules.
|
||||
index = get_index_substr(build_file_lines, '":{}",'.format(file_name))
|
||||
if index >= 0:
|
||||
del build_file_lines[index]
|
||||
else:
|
||||
single_line_deps = ' deps = [ ":{}" ]'.format(file_name)
|
||||
if single_line_deps in build_file_lines:
|
||||
index = build_file_lines.index(single_line_deps)
|
||||
build_file_lines[index] = ' deps = []'
|
||||
|
||||
add_dependency(build_file_lines,
|
||||
'js_test_gen_html("js_test_gen_html_modules") {', 'deps',
|
||||
' ":{}.m",'.format(file_name))
|
||||
# Add dependency in ui/file_manager/BUILD.gn.
|
||||
dir_path = os.path.dirname(js_file_path)
|
||||
rel_dir_path = '/'.join(dir_path.split('/')[2:])
|
||||
dependency_line = ' "{}:js_test_gen_html_modules",'.format(rel_dir_path)
|
||||
if not dependency_line in main_build_file_lines:
|
||||
if not add_dependency(main_build_file_lines,
|
||||
'group("unit_test_data") {', 'deps',
|
||||
dependency_line):
|
||||
print 'unable to update ui/file_manager/BUILD.gn'
|
||||
|
||||
# Save file contents.
|
||||
save_file_lines(build_gn_path, build_file_lines)
|
||||
save_file_lines(main_build_path, main_build_file_lines)
|
||||
save_file_lines(file_manager_jstest_path, file_manager_jstest_lines)
|
||||
|
||||
|
||||
def parse_compiler_output(compiler_output, build_gn_path, file_name):
|
||||
'''Parse closure compile output and return list of token that are
|
||||
undefined/undeclared (no duplicate).'''
|
||||
print "Parsing closure compiler output"
|
||||
with open(compiler_output, 'r') as f:
|
||||
# Retrieve from the closure compile output the types that need to be
|
||||
# imported.
|
||||
variables_to_import = []
|
||||
for line in f:
|
||||
line = line.rstrip()
|
||||
# Select from the output the lines that contain 'ERROR - []' and
|
||||
# 'Unknown type '.
|
||||
if 'ERROR - [' in line:
|
||||
if not file_name in line:
|
||||
if '/third_party/' in line:
|
||||
add_hide_third_party(build_gn_path)
|
||||
else:
|
||||
print 'UNHANDLED ERROR (Other file): ' + line
|
||||
elif 'Unknown type ' in line:
|
||||
unknown_type = line.split('Unknown type ')[1]
|
||||
# Select the main type by removing what follows the dot:
|
||||
# ThumbnailLoader.LoaderType -> ThumbnailLoader.
|
||||
unknown_type = unknown_type.split('.')[0]
|
||||
if not unknown_type in variables_to_import:
|
||||
variables_to_import.append(unknown_type)
|
||||
elif 'variable ' in line and ' is undeclared' in line:
|
||||
if ' cr ' in line: # namespace rewrite.
|
||||
add_namespace_rewrite(build_gn_path)
|
||||
continue
|
||||
unknown_type = line.split('variable ')[1]
|
||||
unknown_type = unknown_type.split(' is undeclared')[0]
|
||||
if not unknown_type in variables_to_import:
|
||||
variables_to_import.append(unknown_type)
|
||||
else:
|
||||
print 'UNHANDLED ERROR: ' + line
|
||||
return variables_to_import
|
||||
|
||||
|
||||
def find_dependencies(dir_path, build_gn_path, js_file_path,
|
||||
variables_to_import, is_unittest):
|
||||
'''Finds dependencies and updates BUILD.gn and JS file to import these
|
||||
dependencies.'''
|
||||
# Get BUILD.gn and JS file contents.
|
||||
build_file_lines = get_file_lines(build_gn_path)
|
||||
js_file_lines = get_file_lines(js_file_path)
|
||||
|
||||
file_name = js_file_path.split('/').pop().replace('.js', '')
|
||||
|
||||
# Special case: classes that extend "cr.EventTarget".
|
||||
index = get_index_substr(js_file_lines, ' extends cr.EventTarget')
|
||||
if index >= 0:
|
||||
if is_unittest:
|
||||
js_file_lines[index] = js_file_lines[index].replace(
|
||||
' extends cr.EventTarget', ' extends EventTarget')
|
||||
add_dependency(build_file_lines,
|
||||
'js_unittest("%s") {' % (file_name), 'deps',
|
||||
' "//ui/webui/resources/js/cr:event_target.m",')
|
||||
else:
|
||||
add_dependency(build_file_lines,
|
||||
'js_library("%s.m") {' % (file_name), 'deps',
|
||||
' "//ui/webui/resources/js/cr:event_target.m",')
|
||||
add_import_line(js_file_lines, 'NativeEventTarget as EventTarget',
|
||||
'chrome://resources/js/cr/event_target.m.js',
|
||||
is_unittest)
|
||||
|
||||
# General case.
|
||||
for variable in variables_to_import:
|
||||
# First, handle special cases.
|
||||
if is_unittest:
|
||||
# "//chrome/test/data/webui:chai_assert".
|
||||
out, _ = subprocess.Popen([
|
||||
'egrep', '-R', 'export function {}\('.format(variable), '-l',
|
||||
'./chrome/test/data/webui/chai_assert.js'
|
||||
],
|
||||
stdout=subprocess.PIPE).communicate()
|
||||
if out.rstrip() != '':
|
||||
add_dependency(build_file_lines,
|
||||
'js_unittest("%s") {' % (file_name), 'deps',
|
||||
' "//chrome/test/data/webui:chai_assert",')
|
||||
add_import_line(js_file_lines, variable,
|
||||
'chrome://test/chai_assert.js', is_unittest)
|
||||
continue
|
||||
else:
|
||||
# "//ui/webui/resources/js".
|
||||
out, _ = subprocess.Popen([
|
||||
'egrep', '-R', '\/\* #export \*\/ \w+ {}\W'.format(variable),
|
||||
'-l', './ui/webui/resources/js'
|
||||
],
|
||||
stdout=subprocess.PIPE).communicate()
|
||||
path = out.rstrip()
|
||||
if path != '':
|
||||
path = path.replace('./', '//').replace('.js', '.m')
|
||||
dependency = ':'.join(path.rsplit(
|
||||
'/', 1)) # //ui/webui/resources/js:assert.m.
|
||||
add_dependency(build_file_lines,
|
||||
'js_library("%s.m") {' % (file_name), 'deps',
|
||||
' "{}",'.format(dependency))
|
||||
path = path.replace('//ui/webui/', 'chrome://').replace(
|
||||
'.m', '.m.js') # chrome://resources/js/assert.m.js.
|
||||
add_import_line(js_file_lines, variable, path, is_unittest)
|
||||
continue
|
||||
|
||||
# Look for exported variables.
|
||||
out, _ = subprocess.Popen([
|
||||
'egrep', '-R', '\/\* #export \*\/ \w+ {}\W'.format(variable), '-l',
|
||||
'./ui/file_manager/'
|
||||
],
|
||||
stdout=subprocess.PIPE).communicate()
|
||||
path = out.rstrip()
|
||||
if path == '':
|
||||
# Look for variables exported as wrapped objects
|
||||
out, _ = subprocess.Popen([
|
||||
'egrep', '-R',
|
||||
'\/\* #export \*\/ \{%s\}' % variable, '-l',
|
||||
'./ui/file_manager/'
|
||||
],
|
||||
stdout=subprocess.PIPE).communicate()
|
||||
path = out.rstrip()
|
||||
if path == '':
|
||||
continue
|
||||
if '\n' in path:
|
||||
print '{}: export found in more than 1 file'.format(variable)
|
||||
print path
|
||||
continue
|
||||
|
||||
# Remove './' at the start.
|
||||
path = path[2:]
|
||||
relative_path = os.path.relpath(path, start=dir_path).replace(
|
||||
'.js', '.m.js')
|
||||
if not relative_path.startswith('../'):
|
||||
relative_path = './' + relative_path
|
||||
add_import_line(js_file_lines, variable, relative_path, is_unittest)
|
||||
if is_unittest:
|
||||
add_dependency(
|
||||
build_file_lines, 'js_unittest("%s") {' % (file_name), 'deps',
|
||||
' "{}",'.format(get_relative_dependency(path, dir_path)))
|
||||
else:
|
||||
add_dependency(
|
||||
build_file_lines, 'js_library("%s.m") {' % (file_name), 'deps',
|
||||
' "{}",'.format(get_relative_dependency(path, dir_path)))
|
||||
|
||||
# Save BUILD.gn and JS file contents.
|
||||
save_file_lines(build_gn_path, build_file_lines)
|
||||
save_file_lines(js_file_path, js_file_lines)
|
||||
|
||||
|
||||
def convert_js_file(js_file_path):
|
||||
'''Add exports (/* #export */) and ignore 'use strict'.'''
|
||||
file_lines = get_file_lines(js_file_path)
|
||||
|
||||
# Ignore 'use strict'.
|
||||
index = get_index_substr(file_lines, "'use strict'")
|
||||
if index >= 0:
|
||||
file_lines[index] = file_lines[index].replace(
|
||||
"'use strict'", "/* #ignore */ 'use strict'")
|
||||
|
||||
# Add exports.
|
||||
for i, line in enumerate(file_lines):
|
||||
# Export class.
|
||||
if line.startswith('class '):
|
||||
# Extract class name.
|
||||
class_name = line.split(' ')[1]
|
||||
# Make sure this class is not used locally.
|
||||
if get_index_substr(file_lines, 'new ' + class_name) >= 0:
|
||||
# Export only if the class name is the name of the file.
|
||||
snake_case_name = ''.join([
|
||||
'_' + c.lower() if c.isupper() else c for c in class_name
|
||||
]).lstrip('_')
|
||||
if not snake_case_name + '.js' == js_file_path.split('/')[-1]:
|
||||
continue
|
||||
file_lines[i] = '/* #export */ ' + line
|
||||
# Export variable in global namespace.
|
||||
elif line.startswith('const ') or line.startswith(
|
||||
'let ') or line.startswith('var '):
|
||||
# Extract variable name.
|
||||
variable_name = line.split(' ')[1]
|
||||
|
||||
# Check if the variable is further defined/extended in the file. If
|
||||
# so, export it.
|
||||
export_variable = False
|
||||
for l in range(len(file_lines)):
|
||||
if file_lines[l].startswith(variable_name + '.'):
|
||||
export_variable = True
|
||||
break
|
||||
if not export_variable:
|
||||
continue
|
||||
|
||||
# Check if export already added.
|
||||
if get_index_substr(file_lines, '/* #export */ {%s}' %
|
||||
(variable_name)) >= 0:
|
||||
continue
|
||||
|
||||
# Export variable in wrapping object at the end of the file.
|
||||
new_lines = '''
|
||||
// eslint-disable-next-line semi,no-extra-semi
|
||||
/* #export */ {%s};''' % (variable_name)
|
||||
file_lines.extend(new_lines.split('\n'))
|
||||
|
||||
# Check if @suppress already added.
|
||||
if get_index_substr(file_lines, ' @suppress {uselessCode}') >= 0:
|
||||
continue
|
||||
index = get_end_of_file_overview(file_lines)
|
||||
if index < 0:
|
||||
index = get_end_of_copyright(file_lines)
|
||||
if index < 0:
|
||||
index = 0
|
||||
index += 1
|
||||
new_lines = '''
|
||||
/**
|
||||
* @fileoverview
|
||||
* @suppress {uselessCode} Temporary suppress %s.
|
||||
*/''' % ('because of the line exporting')
|
||||
file_lines[index:index] = new_lines.split('\n')
|
||||
else:
|
||||
while ' */' not in file_lines[index]:
|
||||
index += 1
|
||||
new_line = (' * @suppress {uselessCode} Temporary suppress '
|
||||
'because of the line exporting.')
|
||||
file_lines.insert(index, new_line)
|
||||
# Export function.
|
||||
elif line.startswith('function '):
|
||||
if not get_index_substr(file_lines, ' {}('.format(function_name)):
|
||||
# The function has to be used outside the file, so has to be
|
||||
# exported.
|
||||
file_lines[i] = '/* #export */ ' + line
|
||||
|
||||
# Save file contents.
|
||||
save_file_lines(js_file_path, file_lines)
|
||||
|
||||
|
||||
def convert_unittest_file(js_file_path):
|
||||
'''Add exports and remove 'use strict'.'''
|
||||
file_lines = get_file_lines(js_file_path)
|
||||
|
||||
# Remove 'use strict'.
|
||||
index = get_index_substr(file_lines, "'use strict'")
|
||||
if index >= 0:
|
||||
del file_lines[index]
|
||||
|
||||
# Add exports.
|
||||
for i, line in enumerate(file_lines):
|
||||
if line.startswith('function setUp()') or 'function test' in line:
|
||||
file_lines[i] = 'export ' + file_lines[i]
|
||||
|
||||
# Save file contents.
|
||||
save_file_lines(js_file_path, file_lines)
|
||||
|
||||
|
||||
def main():
|
||||
# Get command line arguments.
|
||||
js_file_path = sys.argv[1]
|
||||
action = sys.argv[2]
|
||||
if not js_file_path.endswith('.js'):
|
||||
e = "Argument 1 {} isn't a JS file (expected .js extension)".format(
|
||||
js_file_path)
|
||||
raise ValueError(e)
|
||||
dir_path = os.path.dirname(js_file_path)
|
||||
build_gn_path = dir_path + '/BUILD.gn'
|
||||
file_name = os.path.basename(js_file_path).replace('.js', '')
|
||||
is_unittest = file_name.endswith('_unittest')
|
||||
if is_unittest:
|
||||
js_file_path = js_file_path.replace('.js', '.m.js')
|
||||
|
||||
if (action == 'generate'):
|
||||
if is_unittest:
|
||||
update_buid_gn_unittest_dependencies(build_gn_path, js_file_path,
|
||||
file_name)
|
||||
convert_unittest_file(js_file_path)
|
||||
else:
|
||||
update_build_gn_dependencies(dir_path, file_name, build_gn_path)
|
||||
convert_js_file(js_file_path)
|
||||
elif (action == 'parse'):
|
||||
compiler_output = sys.argv[3]
|
||||
variables_to_import = parse_compiler_output(compiler_output,
|
||||
build_gn_path, file_name)
|
||||
if variables_to_import == []:
|
||||
return
|
||||
find_dependencies(dir_path, build_gn_path, js_file_path,
|
||||
variables_to_import, is_unittest)
|
||||
|
||||
|
||||
print "-----modules.py-----"
|
||||
main()
|
||||
print "--------------------"
|
51
ui/file_manager/base/tools/modules.sh
Executable file
51
ui/file_manager/base/tools/modules.sh
Executable file
@ -0,0 +1,51 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright (c) 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.
|
||||
|
||||
# Command usage:
|
||||
# ui/file_manager/base/tools/modules.sh out/Release ui/file_manager/
|
||||
# file_manager/foreground/js/list_thumbnail_loader.js
|
||||
# ui/file_manager/base/tools/modules.sh out/Release ui/file_manager/
|
||||
# file_manager/common/js/importer_common.js
|
||||
# ui/file_manager/base/tools/modules.sh out/Release ui/file_manager/
|
||||
# file_manager/foreground/js/list_thumbnail_loader_unittest.js
|
||||
|
||||
# Input: js file to convert.
|
||||
build_dir=$1;
|
||||
file_path=$2;
|
||||
dir=`dirname $file_path`;
|
||||
compiler_output="$build_dir/gen/ui/file_manager/base/tools/compiler_output.txt";
|
||||
|
||||
# Create containing directory of `compiler_output` if doesn't exist.
|
||||
mkdir -p `dirname $compiler_output`;
|
||||
|
||||
# Process files with Python.
|
||||
ui/file_manager/base/tools/modules.py $file_path 'generate';
|
||||
|
||||
# Parse closure compiler output and update files until the message 'X error(s),
|
||||
# Y warning(s), Z% typed' doesn't change.
|
||||
prev_output=""
|
||||
new_output="."
|
||||
while [[ $prev_output != $new_output ]]; do
|
||||
prev_output=$new_output;
|
||||
|
||||
# Run closure compiler and save output.
|
||||
ninja -C $build_dir $dir:closure_compile 2>&1 | tee $compiler_output;
|
||||
|
||||
# Parse closure compiler output.
|
||||
ui/file_manager/base/tools/modules.py $file_path 'parse' $compiler_output;
|
||||
|
||||
# Get number of errors from modules.txt.
|
||||
new_output=$(cat $compiler_output | grep 'error(s)' );
|
||||
done;
|
||||
|
||||
# Format files.
|
||||
git cl format --js;
|
||||
|
||||
if [[ $new_output == "" ]]; then
|
||||
echo "No closure compiler error found"
|
||||
else
|
||||
echo "Final state: " $new_output;
|
||||
fi;
|
Reference in New Issue
Block a user