mirror of
https://github.com/google/pebble.git
synced 2025-05-18 01:14:55 +00:00
Import of the watch repository from Pebble
This commit is contained in:
commit
3b92768480
10334 changed files with 2564465 additions and 0 deletions
374
sdk/waftools/pebble_sdk_common.py
Normal file
374
sdk/waftools/pebble_sdk_common.py
Normal file
|
@ -0,0 +1,374 @@
|
|||
# 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 os
|
||||
import time
|
||||
import types
|
||||
|
||||
from waflib import Logs
|
||||
from waflib.Configure import conf
|
||||
from waflib.Task import Task
|
||||
from waflib.TaskGen import after_method, before_method, feature
|
||||
from waflib.Tools import c, c_preproc
|
||||
|
||||
import ldscript, process_bundle, process_headers, process_js, report_memory_usage, xcode_pebble
|
||||
from pebble_sdk_platform import maybe_import_internal
|
||||
from sdk_helpers import (append_to_attr, find_sdk_component, get_node_from_abspath,
|
||||
wrap_task_name_with_platform)
|
||||
|
||||
|
||||
# Override the default waf task __str__ method to include display of the HW platform being targeted
|
||||
Task.__str__ = wrap_task_name_with_platform
|
||||
|
||||
|
||||
def options(opt):
|
||||
"""
|
||||
Specify the options available when invoking waf; uses OptParse. This method is called from
|
||||
app and lib waftools by `opt.load('pebble_sdk_common')`
|
||||
:param opt: the OptionContext object
|
||||
:return: N/A
|
||||
"""
|
||||
opt.load('gcc')
|
||||
opt.add_option('-d', '--debug', action='store_true', default=False, dest='debug',
|
||||
help='Build in debug mode')
|
||||
opt.add_option('--no-groups', action='store_true', default=False, dest='no_groups')
|
||||
opt.add_option('--sandboxed-build', action='store_true', default=False, dest='sandbox')
|
||||
|
||||
|
||||
def configure(conf):
|
||||
"""
|
||||
Configure the tools for the build by locating SDK prerequisites on the filesystem
|
||||
:param conf: the ConfigureContext
|
||||
:return: N/A
|
||||
"""
|
||||
if not conf.options.debug:
|
||||
conf.env.append_value('DEFINES', 'RELEASE')
|
||||
else:
|
||||
Logs.pprint("CYAN", "Debug enabled")
|
||||
if conf.options.no_groups:
|
||||
conf.env.USE_GROUPS = False
|
||||
else:
|
||||
conf.env.USE_GROUPS = True
|
||||
conf.env.SANDBOX = conf.options.sandbox
|
||||
conf.env.VERBOSE = conf.options.verbose
|
||||
conf.env.TIMESTAMP = int(time.time())
|
||||
|
||||
# If waf is in ~/pebble-dev/PebbleSDK-X.XX/waf
|
||||
# Then this file is in ~/pebble-dev/PebbleSDK-X.XX/.waflib-xxxx/waflib/extras/
|
||||
# => we need to go up 3 directories to find the folder containing waf
|
||||
pebble_sdk = conf.root.find_dir(os.path.dirname(__file__)).parent.parent.parent
|
||||
if pebble_sdk is None:
|
||||
conf.fatal("Unable to find Pebble SDK!\n"
|
||||
"Please make sure you are running waf directly from your SDK.")
|
||||
conf.env.PEBBLE_SDK_ROOT = pebble_sdk.abspath()
|
||||
|
||||
# Set location of Pebble SDK common folder
|
||||
pebble_sdk_common = pebble_sdk.find_node('common')
|
||||
conf.env.PEBBLE_SDK_COMMON = pebble_sdk_common.abspath()
|
||||
|
||||
if 'NODE_PATH' in os.environ:
|
||||
conf.env.NODE_PATH = conf.root.find_node(os.environ['NODE_PATH']).abspath()
|
||||
webpack_path = conf.root.find_node(conf.env.NODE_PATH).find_node('.bin').abspath()
|
||||
|
||||
try:
|
||||
conf.find_program('webpack', path_list=[webpack_path])
|
||||
except conf.errors.ConfigurationError:
|
||||
pass # Error will be caught after checking for enableMultiJS setting
|
||||
else:
|
||||
Logs.pprint('YELLOW', "WARNING: Unable to find $NODE_PATH variable required for SDK "
|
||||
"build. Please verify this build was initiated with a recent "
|
||||
"pebble-tool.")
|
||||
|
||||
maybe_import_internal(conf.env)
|
||||
|
||||
|
||||
def build(bld):
|
||||
"""
|
||||
This method is invoked from the app or lib waftool with the `bld.load('pebble_sdk_common')`
|
||||
call and sets up additional task generators for the SDK.
|
||||
:param bld: the BuildContext object
|
||||
:return: N/A
|
||||
"""
|
||||
|
||||
# cached_env is set to a shallow copy of the current ConfigSet for this BuildContext
|
||||
bld.env = bld.all_envs['']
|
||||
bld.load('file_name_c_define')
|
||||
|
||||
# Process message keys
|
||||
bld(features='message_keys')
|
||||
|
||||
cached_env = bld.env
|
||||
for platform in bld.env.TARGET_PLATFORMS:
|
||||
# bld.env is set to a shallow copy of the ConfigSet labeled <platform>
|
||||
bld.env = bld.all_envs[platform]
|
||||
|
||||
# Create a build group (set of TaskGens) for <platform>
|
||||
if bld.env.USE_GROUPS:
|
||||
bld.add_group(bld.env.PLATFORM_NAME)
|
||||
|
||||
# Generate a linker script specific to the current platform
|
||||
build_node = bld.path.get_bld().find_or_declare(bld.env.BUILD_DIR)
|
||||
bld(features='subst',
|
||||
source=find_sdk_component(bld, bld.env, 'pebble_app.ld.template'),
|
||||
target=build_node.make_node('pebble_app.ld.auto'),
|
||||
**bld.env.PLATFORM)
|
||||
|
||||
# Locate Rocky JS tooling script
|
||||
js_tooling_script = find_sdk_component(bld, bld.env, 'tools/generate_snapshot.js')
|
||||
bld.env.JS_TOOLING_SCRIPT = js_tooling_script if js_tooling_script else None
|
||||
|
||||
# bld.env is set back to a shallow copy of the original ConfigSet that was set when this
|
||||
# `build` method was invoked
|
||||
bld.env = cached_env
|
||||
|
||||
# Create a build group for bundling (should run after the build groups for each platform)
|
||||
if bld.env.USE_GROUPS:
|
||||
bld.add_group('bundle')
|
||||
|
||||
|
||||
def _wrap_c_preproc_scan(task):
|
||||
"""
|
||||
This function is a scanner function that wraps c_preproc.scan to fix up pebble.h dependencies.
|
||||
pebble.h is outside out the bld/src trees so therefore it's not considered a valid dependency
|
||||
and isn't scanned for further dependencies. Normally this would be fine but pebble.h includes
|
||||
an auto-generated resource id header which is really a dependency. We detect this include and
|
||||
add the resource id header file to the nodes being scanned by c_preproc.
|
||||
:param task: the task instance
|
||||
:return: N/A
|
||||
"""
|
||||
(nodes, names) = c_preproc.scan(task)
|
||||
if 'pebble.h' in names:
|
||||
nodes.append(get_node_from_abspath(task.generator.bld, task.env.RESOURCE_ID_HEADER))
|
||||
nodes.append(get_node_from_abspath(task.generator.bld, task.env.MESSAGE_KEYS_HEADER))
|
||||
return nodes, names
|
||||
|
||||
|
||||
@feature('c')
|
||||
@before_method('process_source')
|
||||
def setup_pebble_c(task_gen):
|
||||
"""
|
||||
This method is called before all of the c aliases (objects, shlib, stlib, program, etc) and
|
||||
ensures that the SDK `include` path for the current platform, as well as the project root
|
||||
directory and the project src directory are included as header search paths (includes) for the
|
||||
build.
|
||||
:param task_gen: the task generator instance
|
||||
:return: N/A
|
||||
"""
|
||||
platform = task_gen.env.PLATFORM_NAME
|
||||
append_to_attr(task_gen, 'includes',
|
||||
[find_sdk_component(task_gen.bld, task_gen.env, 'include'),
|
||||
'.', 'include', 'src'])
|
||||
append_to_attr(task_gen, 'includes', platform)
|
||||
for lib in task_gen.bld.env.LIB_JSON:
|
||||
if 'pebble' in lib:
|
||||
lib_include_node = task_gen.bld.path.find_node(lib['path']).find_node('include')
|
||||
append_to_attr(task_gen, 'includes',
|
||||
[lib_include_node,
|
||||
lib_include_node.find_node(str(lib['name'])).find_node(platform)])
|
||||
|
||||
|
||||
@feature('c')
|
||||
@after_method('process_source')
|
||||
def fix_pebble_h_dependencies(task_gen):
|
||||
"""
|
||||
This method is called before all of the c aliases (objects, shlib, stlib, program, etc) and
|
||||
ensures that the _wrap_c_preproc_scan method is run for all c tasks.
|
||||
:param task_gen: the task generator instance
|
||||
:return: N/A
|
||||
"""
|
||||
for task in task_gen.tasks:
|
||||
if type(task) == c.c:
|
||||
# Swap out the bound member function for our own
|
||||
task.scan = types.MethodType(_wrap_c_preproc_scan, task, c.c)
|
||||
|
||||
|
||||
@feature('pebble_cprogram')
|
||||
@before_method('process_source')
|
||||
def setup_pebble_cprogram(task_gen):
|
||||
"""
|
||||
This method is called before all of the c aliases (objects, shlib, stlib, program, etc) and
|
||||
adds the appinfo.auto.c file to the source file list, adds the SDK pebble library to the lib
|
||||
path for the build, sets the linkflags for the build, and specifies the linker script to
|
||||
use during the linking step.
|
||||
:param task_gen: the task generator instance
|
||||
:return: None
|
||||
"""
|
||||
build_node = task_gen.path.get_bld().make_node(task_gen.env.BUILD_DIR)
|
||||
platform = task_gen.env.PLATFORM_NAME
|
||||
if not hasattr(task_gen, 'bin_type') or getattr(task_gen, 'bin_type') != 'lib':
|
||||
append_to_attr(task_gen, 'source', build_node.make_node('appinfo.auto.c'))
|
||||
append_to_attr(task_gen, 'source', build_node.make_node('src/resource_ids.auto.c'))
|
||||
|
||||
if task_gen.env.MESSAGE_KEYS:
|
||||
append_to_attr(task_gen,
|
||||
'source',
|
||||
get_node_from_abspath(task_gen.bld,
|
||||
task_gen.env.MESSAGE_KEYS_DEFINITION))
|
||||
|
||||
append_to_attr(task_gen, 'stlibpath',
|
||||
find_sdk_component(task_gen.bld, task_gen.env, 'lib').abspath())
|
||||
append_to_attr(task_gen, 'stlib', 'pebble')
|
||||
|
||||
for lib in task_gen.bld.env.LIB_JSON:
|
||||
# Skip binary check for non-Pebble libs
|
||||
if not 'pebble' in lib:
|
||||
continue
|
||||
|
||||
binaries_path = task_gen.bld.path.find_node(lib['path']).find_node('binaries')
|
||||
if binaries_path:
|
||||
# Check for existence of platform folders inside binaries folder
|
||||
platform_binary_path = binaries_path.find_node(platform)
|
||||
if not platform_binary_path:
|
||||
task_gen.bld.fatal("Library {} is missing the {} platform folder in {}".
|
||||
format(lib['name'], platform, binaries_path))
|
||||
|
||||
# Check for existence of binary for each platform
|
||||
if lib['name'].startswith('@'):
|
||||
scoped_name = lib['name'].rsplit('/', 1)
|
||||
lib_binary = (platform_binary_path.find_node(str(scoped_name[0])).
|
||||
find_node("lib{}.a".format(scoped_name[1])))
|
||||
else:
|
||||
lib_binary = platform_binary_path.find_node("lib{}.a".format(lib['name']))
|
||||
|
||||
if not lib_binary:
|
||||
task_gen.bld.fatal("Library {} is missing a binary for the {} platform".
|
||||
format(lib['name'], platform))
|
||||
|
||||
# Link library binary (supports scoped names)
|
||||
if lib['name'].startswith('@'):
|
||||
append_to_attr(task_gen, 'stlibpath',
|
||||
platform_binary_path.find_node(str(scoped_name[0])).abspath())
|
||||
append_to_attr(task_gen, 'stlib', scoped_name[1])
|
||||
else:
|
||||
append_to_attr(task_gen, 'stlibpath', platform_binary_path.abspath())
|
||||
append_to_attr(task_gen, 'stlib', lib['name'])
|
||||
|
||||
append_to_attr(task_gen, 'linkflags',
|
||||
['-Wl,--build-id=sha1',
|
||||
'-Wl,-Map,pebble-{}.map,--emit-relocs'.format(getattr(task_gen,
|
||||
'bin_type',
|
||||
'app'))])
|
||||
if not hasattr(task_gen, 'ldscript'):
|
||||
task_gen.ldscript = (
|
||||
build_node.find_or_declare('pebble_app.ld.auto').path_from(task_gen.path))
|
||||
|
||||
|
||||
def _get_entry_point(ctx, js_type, waf_js_entry_point):
|
||||
"""
|
||||
Returns the appropriate JS entry point, extracted from a project's package.json file,
|
||||
wscript or common SDK default
|
||||
:param ctx: the BuildContext
|
||||
:param js_type: type of JS build, pkjs or rockyjs
|
||||
:param waf_js_entry_point: the JS entry point specified by waftools
|
||||
:return: the JS entry point for the bundled JS file
|
||||
"""
|
||||
fallback_entry_point = waf_js_entry_point
|
||||
if not fallback_entry_point:
|
||||
if js_type == 'pkjs':
|
||||
if ctx.path.find_node('src/pkjs/index.js'):
|
||||
fallback_entry_point = 'src/pkjs/index.js'
|
||||
else:
|
||||
fallback_entry_point = 'src/js/app.js'
|
||||
if js_type == 'rockyjs':
|
||||
fallback_entry_point = 'src/rocky/index.js'
|
||||
|
||||
project_info = ctx.env.PROJECT_INFO
|
||||
|
||||
if not project_info.get('main'):
|
||||
return fallback_entry_point
|
||||
if project_info['main'].get(js_type):
|
||||
return str(project_info['main'][js_type])
|
||||
return fallback_entry_point
|
||||
|
||||
|
||||
@conf
|
||||
def pbl_bundle(self, *k, **kw):
|
||||
"""
|
||||
This method is bound to the build context and is called by specifying `bld.pbl_bundle`. We
|
||||
set the custome features `js` and `bundle` to run when this method is invoked.
|
||||
:param self: the BuildContext object
|
||||
:param k: none expected
|
||||
:param kw:
|
||||
binaries - a list containing dictionaries specifying the HW platform targeted by the
|
||||
binary built, the app binary, and an optional worker binary
|
||||
js - the source JS files to be bundled
|
||||
js_entry_file - an optional parameter to specify the entry JS file when
|
||||
enableMultiJS is set to 'true'
|
||||
:return: a task generator instance with keyword arguments specified
|
||||
"""
|
||||
if kw.get('bin_type', 'app') == 'lib':
|
||||
kw['features'] = 'headers js package'
|
||||
else:
|
||||
if self.env.BUILD_TYPE == 'rocky':
|
||||
kw['js_entry_file'] = _get_entry_point(self, 'pkjs', kw.get('js_entry_file'))
|
||||
kw['features'] = 'js bundle'
|
||||
return self(*k, **kw)
|
||||
|
||||
|
||||
@conf
|
||||
def pbl_build(self, *k, **kw):
|
||||
"""
|
||||
This method is bound to the build context and is called by specifying `bld.pbl_build()`. We
|
||||
set the custom features `c`, `cprogram` and `pebble_cprogram` to run when this method is
|
||||
invoked. This method is intended to someday replace `pbl_program` and `pbl_worker` so that
|
||||
all apps, workers, and libs will run through this method.
|
||||
:param self: the BuildContext object
|
||||
:param k: none expected
|
||||
:param kw:
|
||||
source - the source C files to be built and linked
|
||||
target - the destination binary file for the compiled source
|
||||
:return: a task generator instance with keyword arguments specified
|
||||
"""
|
||||
valid_bin_types = ('app', 'worker', 'lib', 'rocky')
|
||||
bin_type = kw.get('bin_type', None)
|
||||
if bin_type not in valid_bin_types:
|
||||
self.fatal("The pbl_build method requires that a valid bin_type attribute be specified. "
|
||||
"Valid options are {}".format(valid_bin_types))
|
||||
|
||||
if bin_type == 'rocky':
|
||||
kw['features'] = 'c cprogram pebble_cprogram memory_usage'
|
||||
elif bin_type in ('app', 'worker'):
|
||||
kw['features'] = 'c cprogram pebble_cprogram memory_usage'
|
||||
kw[bin_type] = kw['target']
|
||||
elif bin_type == 'lib':
|
||||
kw['features'] = 'c cstlib memory_usage'
|
||||
path, name = kw['target'].rsplit('/', 1)
|
||||
kw['lib'] = self.path.find_or_declare(path).make_node("lib{}.a".format(name))
|
||||
|
||||
# Pass values needed for memory usage report
|
||||
if bin_type != 'worker':
|
||||
kw['resources'] = (
|
||||
self.env.PROJECT_RESBALL if bin_type == 'lib' else
|
||||
self.path.find_or_declare(self.env.BUILD_DIR).make_node('app_resources.pbpack'))
|
||||
return self(*k, **kw)
|
||||
|
||||
|
||||
@conf
|
||||
def pbl_js_build(self, *k, **kw):
|
||||
"""
|
||||
This method is bound to the build context and is called by specifying `bld.pbl_cross_compile()`.
|
||||
When this method is invoked, we set the custom feature `rockyjs` to run, which handles
|
||||
processing of JS files in preparation for Rocky.js bytecode compilation (this actually
|
||||
happens during resource generation)
|
||||
:param self: the BuildContext object
|
||||
:param k: none expected
|
||||
:param kw:
|
||||
source - the source JS files that will eventually be compiled into bytecode
|
||||
target - the destination JS file that will be specified as the source file for the
|
||||
bytecode compilation process
|
||||
:return: a task generator instance with keyword arguments specified
|
||||
"""
|
||||
kw['js_entry_file'] = _get_entry_point(self, 'rockyjs', kw.get('js_entry_file'))
|
||||
kw['features'] = 'rockyjs'
|
||||
return self(*k, **kw)
|
Loading…
Add table
Add a link
Reference in a new issue