123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266 |
- # 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.")
|