mirror of
https://github.com/google/pebble.git
synced 2025-03-15 08:41:21 +00:00
266 lines
11 KiB
Python
266 lines
11 KiB
Python
# Copyright 2024 Google LLC
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
import json
|
|
import os
|
|
import subprocess
|
|
from string import Template
|
|
from waflib.Errors import WafError
|
|
from waflib.TaskGen import before_method, feature
|
|
from waflib import Context, Logs, Node, Task
|
|
|
|
from sdk_helpers import find_sdk_component, get_node_from_abspath
|
|
from sdk_helpers import process_package
|
|
|
|
|
|
@feature('rockyjs')
|
|
@before_method('process_sdk_resources')
|
|
def process_rocky_js(task_gen):
|
|
"""
|
|
Lint the JS source files using a Rocky-specific linter
|
|
|
|
Keyword arguments:
|
|
js -- a list of JS files to process for the build
|
|
|
|
:param task_gen: the task generator instance
|
|
:return: N/A
|
|
"""
|
|
bld = task_gen.bld
|
|
task_gen.mappings = {'': (lambda task_gen, node: None)}
|
|
js_nodes = task_gen.to_nodes(task_gen.source)
|
|
target = task_gen.to_nodes(task_gen.target)
|
|
if not js_nodes:
|
|
task_gen.bld.fatal("Project does not contain any source code.")
|
|
js_nodes.append(find_sdk_component(bld, task_gen.env, 'include/rocky.js'))
|
|
|
|
# This locates the available node_modules folders and performs a search for the rocky-lint
|
|
# module. This code remains in this file un-abstracted because similar functionality is not yet
|
|
# needed elsewhere.
|
|
node_modules = []
|
|
rocky_linter = None
|
|
if bld.path.find_node('node_modules'):
|
|
node_modules.append(bld.path.find_node('node_modules'))
|
|
if bld.env.NODE_PATH:
|
|
node_modules.append(bld.root.find_node(bld.env.NODE_PATH))
|
|
for node_modules_node in node_modules:
|
|
rocky_linter = node_modules_node.ant_glob('rocky-lint/**/rocky-lint.js')
|
|
if rocky_linter:
|
|
rocky_linter = rocky_linter[0]
|
|
break
|
|
|
|
rocky_definitions = find_sdk_component(bld, task_gen.env, 'tools/rocky-lint/rocky.d.ts')
|
|
if rocky_linter and rocky_definitions:
|
|
lintable_nodes = [node for node in js_nodes if node.is_child_of(bld.path)]
|
|
lint_task = task_gen.create_task('lint_js', src=lintable_nodes)
|
|
lint_task.linter = [task_gen.env.NODE,
|
|
rocky_linter.path_from(bld.path),
|
|
'-d',
|
|
rocky_definitions.path_from(bld.path)]
|
|
else:
|
|
Logs.pprint('YELLOW', "Rocky JS linter not present - skipping lint task")
|
|
|
|
# Create JS merge task for Rocky.js files
|
|
merge_task = task_gen.create_task('merge_js', src=js_nodes, tgt=target)
|
|
merge_task.js_entry_file = task_gen.js_entry_file
|
|
merge_task.js_build_type = 'rocky'
|
|
|
|
|
|
@feature('js')
|
|
@before_method('make_pbl_bundle', 'make_lib_bundle')
|
|
def process_js(task_gen):
|
|
"""
|
|
Merge the JS source files into a single JS file if enableMultiJS is set to 'true', otherwise,
|
|
skip JS processing
|
|
|
|
Keyword arguments:
|
|
js -- A list of JS files to process for the build
|
|
|
|
:param task_gen: the task generator instance
|
|
:return: N/A
|
|
"""
|
|
# Skip JS handling if there are no JS files
|
|
js_nodes = task_gen.to_nodes(getattr(task_gen, 'js', []))
|
|
if not js_nodes:
|
|
return
|
|
|
|
# Create JS merge task if the project specifies "enableMultiJS: true"
|
|
if task_gen.env.PROJECT_INFO.get('enableMultiJS', False):
|
|
target_js = task_gen.bld.path.get_bld().make_node('pebble-js-app.js')
|
|
target_js_map = target_js.change_ext('.js.map')
|
|
task_gen.js = [target_js, target_js_map]
|
|
|
|
merge_task = task_gen.create_task('merge_js', src=js_nodes, tgt=[target_js, target_js_map])
|
|
merge_task.js_entry_file = task_gen.js_entry_file
|
|
merge_task.js_build_type = 'pkjs'
|
|
merge_task.js_source_map_config = {
|
|
'sourceMapFilename': target_js_map.name
|
|
}
|
|
return
|
|
|
|
# Check for pebble-js-app.js if developer does not specify "enableMultiJS: true" in
|
|
# the project
|
|
if task_gen.env.BUILD_TYPE != 'lib':
|
|
for node in js_nodes:
|
|
if 'pebble-js-app.js' in node.abspath():
|
|
break
|
|
else:
|
|
Logs.pprint("CYAN",
|
|
"WARNING: enableMultiJS is not enabled for this project and "
|
|
"pebble-js-app.js does not exist")
|
|
|
|
# For apps without multiJS enabled and libs, copy JS files from src folder to build folder,
|
|
# skipping any files already in the build folder
|
|
js_nodes_to_copy = [js_node for js_node in js_nodes if not js_node.is_bld()]
|
|
if not js_nodes_to_copy:
|
|
task_gen.js = js_nodes
|
|
return
|
|
|
|
target_nodes = []
|
|
for js in js_nodes_to_copy:
|
|
if js.is_child_of(task_gen.bld.path.find_node('src')):
|
|
js_path = js.path_from(task_gen.bld.path.find_node('src'))
|
|
else:
|
|
js_path = os.path.abspath(js.path_from(task_gen.bld.path))
|
|
target_node = task_gen.bld.path.get_bld().make_node(js_path)
|
|
target_node.parent.mkdir()
|
|
target_nodes.append(target_node)
|
|
task_gen.js = target_nodes + list(set(js_nodes) - set(js_nodes_to_copy))
|
|
task_gen.create_task('copy_js', src=js_nodes_to_copy, tgt=target_nodes)
|
|
|
|
|
|
class copy_js(Task.Task):
|
|
"""
|
|
Task class for copying source JS files to a target location
|
|
"""
|
|
def run(self):
|
|
"""
|
|
This method executes when the JS copy task runs
|
|
:return: N/A
|
|
"""
|
|
bld = self.generator.bld
|
|
|
|
if len(self.inputs) != len(self.outputs):
|
|
bld.fatal("Number of input JS files ({}) does not match number of target JS files ({})".
|
|
format(len(self.inputs), len(self.outputs)))
|
|
|
|
for i in range(len(self.inputs)):
|
|
bld.cmd_and_log('cp "{src}" "{tgt}"'.
|
|
format(src=self.inputs[i].abspath(), tgt=self.outputs[i].abspath()),
|
|
quiet=Context.BOTH)
|
|
|
|
|
|
class merge_js(Task.Task):
|
|
"""
|
|
Task class for merging all specified JS files into one `pebble-js-app.js` file
|
|
"""
|
|
def run(self):
|
|
"""
|
|
This method executes when the JS merge task runs
|
|
:return: N/A
|
|
"""
|
|
bld = self.generator.bld
|
|
js_build_type = getattr(self, 'js_build_type')
|
|
|
|
# Check for a valid JS entry point among JS files
|
|
js_nodes = self.inputs
|
|
entry_point = bld.path.find_resource(self.js_entry_file)
|
|
if entry_point not in js_nodes:
|
|
bld.fatal("\n\nJS entry file '{}' not found in JS source files '{}'. We expect to find "
|
|
"a javascript file here that we will execute directly when your app launches."
|
|
"\n\nIf you are an advanced user, you can supply the 'js_entry_file' "
|
|
"parameter to 'pbl_bundle' in your wscript to change the default entry point."
|
|
" Note that doing this will break CloudPebble compatibility.".
|
|
format(self.js_entry_file, js_nodes))
|
|
target_js = self.outputs[0]
|
|
|
|
entry = [
|
|
entry_point.abspath()
|
|
]
|
|
|
|
if js_build_type == 'pkjs':
|
|
# NOTE: The order is critical here.
|
|
# _pkjs_shared_additions.js MUST be the first in the `entry` array!
|
|
entry.insert(0, "_pkjs_shared_additions.js")
|
|
if self.env.BUILD_TYPE == 'rocky':
|
|
entry.insert(1, "_pkjs_message_wrapper.js")
|
|
|
|
common_node = bld.root.find_node(self.generator.env.PEBBLE_SDK_COMMON)
|
|
tools_webpack_node = common_node.find_node('tools').find_node('webpack')
|
|
webpack_config_template_node = tools_webpack_node.find_node('webpack-config.js.pytemplate')
|
|
with open(webpack_config_template_node.abspath()) as f:
|
|
webpack_config_template_content = f.read()
|
|
|
|
search_paths = [
|
|
common_node.find_node('include').abspath(),
|
|
tools_webpack_node.abspath(),
|
|
bld.root.find_node(self.generator.env.NODE_PATH).abspath(),
|
|
bld.path.get_bld().make_node('js').abspath()
|
|
]
|
|
|
|
pebble_packages = [str(lib['name']) for lib in bld.env.LIB_JSON if 'pebble' in lib]
|
|
aliases = {lib: "{}/dist/js".format(lib) for lib in pebble_packages}
|
|
|
|
info_json_file = bld.path.find_node('package.json') or bld.path.find_node('appinfo.json')
|
|
if info_json_file:
|
|
aliases.update({'app_package.json': info_json_file.abspath()})
|
|
|
|
config_file = (
|
|
bld.path.get_bld().make_node("webpack/{}/webpack.config.js".format(js_build_type)))
|
|
config_file.parent.mkdir()
|
|
with open(config_file.abspath(), 'w') as f:
|
|
m = {
|
|
'IS_SANDBOX': bool(self.env.SANDBOX),
|
|
'ENTRY_FILENAMES': entry,
|
|
'OUTPUT_PATH': target_js.parent.path_from(bld.path),
|
|
'OUTPUT_FILENAME': target_js.name,
|
|
'RESOLVE_ROOTS': search_paths,
|
|
'RESOLVE_ALIASES': aliases,
|
|
'SOURCE_MAP_CONFIG': getattr(self, 'js_source_map_config', None)
|
|
}
|
|
f.write(Template(webpack_config_template_content).substitute(
|
|
{k: json.dumps(m[k], separators=(',\n',': ')) for k in m }))
|
|
|
|
cmd = (
|
|
"'{webpack}' --config {config} --display-modules".
|
|
format(webpack=self.generator.env.WEBPACK, config=config_file.path_from(bld.path)))
|
|
try:
|
|
out = bld.cmd_and_log(cmd, quiet=Context.BOTH, output=Context.STDOUT)
|
|
except WafError as e:
|
|
bld.fatal("JS bundling failed\n{}\n{}".format(e.stdout, e.stderr))
|
|
else:
|
|
if self.env.VERBOSE > 0:
|
|
Logs.pprint('WHITE', out)
|
|
|
|
|
|
class lint_js(Task.Task):
|
|
"""
|
|
Task class for linting JS source files with a specified linter script.
|
|
"""
|
|
def run(self):
|
|
"""
|
|
This method executes when the JS lint task runs
|
|
:return: N/A
|
|
"""
|
|
self.name = 'lint_js'
|
|
js_nodes = self.inputs
|
|
for js_node in js_nodes:
|
|
cmd = self.linter + [js_node.path_from(self.generator.bld.path)]
|
|
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
out, err = proc.communicate()
|
|
if err:
|
|
Logs.pprint('CYAN', "\n========== Lint Results: {} ==========\n".format(js_node))
|
|
Logs.pprint('WHITE', "{}\n{}\n".format(out, err))
|
|
|
|
if proc.returncode != 0:
|
|
self.generator.bld.fatal("Project failed linting.")
|