pebble/devsite/source/_posts/2015-05-19-tips-and-tricks-platform-specific-c-file-sets.md

227 lines
7 KiB
Markdown
Raw Normal View History

---
# Copyright 2025 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.
title: Tips and Tricks - Platform-specific C File Set
author: chrislewis
tags:
- Beautiful Code
---
In
[the last Tips and Tricks blog post](/blog/2015/05/13/tips-and-tricks-transparent-images/)
we looked at drawing transparent images on both the Aplite and Basalt platforms.
This time around, we will look at a `wscript` modification that can allow you to
build a Pebble project when you have two completely separate sets of C source
files; one set for Aplite, and another for Basalt.
Note: This technique can be applied only to local SDK projects, where access to
`wscript` is available. This means that it cannot be used in CloudPebble
projects.
> Update 05/20/15: There was an error in the JS code sample given at the end of
> this post, which has now been corrected.
## The Preprocessor Approach
In the [3.0 Migration Guide](/sdk/migration-guide/#backwards-compatibility) we
recommend using preprocessor directives such as `PBL_PLATFORM_APLITE` and
`PBL_PLATFORM_BASALT` to mark code to be compiled only on that particular
platform. This helps avoid the need to maintain two separate projects for one
app, which is especially convenient when migrating a 2.x app to the Basalt
platform.
```c
#ifdef PBL_PLATFORM_APLITE
// Aligns under the status bar
layer_set_frame(s_layer, GRect(0, 0, 144, 68));
#elif PBL_PLATFORM_BASALT
// Preserve alignment to status bar on Aplite
layer_set_frame(s_layer, GRect(0, STATUS_BAR_LAYER_HEIGHT, 144, 68));
#endif
```
This is a good solution for small blocks of conditional code, but some
developers may find that complicated conditional code can soon become more
`#ifdef...#elif...#endif` than actual code itself!
## The Modified Wscript Approach
In these situations you may find it preferable to use a different approach.
Instead of modifying your app code to use preprocessor statements whenever a
platform-specific value is needed, you can modify your project's `wscript` file
to limit each compilation pass to a certain folder of source files.
By default, you will probably have a project with this file structure:
```text
my_project
resources
images
banner~bw.png
banner~color.png
src
main.c
util.h
util.c
appinfo.json
wscript
```
In this scenario, the `wscript` dictates that *any* `.c` files found in `src`
will be compiled for both platforms.
To use a different set of source files for each platform during compilation,
modify the lines with the `**` wildcard (within the `for` loop) to point to a
folder within `src` where the platform- specific files are then located:
```python
for p in ctx.env.TARGET_PLATFORMS:
ctx.set_env(ctx.all_envs[p])
ctx.set_group(ctx.env.PLATFORM_NAME)
app_elf='{}/pebble-app.elf'.format(ctx.env.BUILD_DIR)
# MODIFY THIS LINE!
# E.g.: When 'p' == 'aplite', look in 'src/aplite/'
ctx.pbl_program(source=ctx.path.ant_glob('src/{}/**/*.c'.format(p)), target=app_elf)
if build_worker:
worker_elf='{}/pebble-worker.elf'.format(ctx.env.BUILD_DIR)
binaries.append({'platform': p, 'app_elf': app_elf, 'worker_elf': worker_elf})
# MODIFY THIS LINE!
# Also modify this line to look for platform-specific C files in `worker_src`
ctx.pbl_worker(source=ctx.path.ant_glob('worker_src/{}/**/*.c'.format(p)), target=worker_elf)
else:
binaries.append({'platform': p, 'app_elf': app_elf})
```
With this newly modified `wscript`, we must re-organise our `src` folder to
match the new search pattern. This allows us to maintain two separate sets of
source files, each free of any excessive `#ifdef` pollution.
```text
my_project
resources
images
banner~bw.png
banner~color.png
src
aplite
main.c
util.h
util.c
basalt
main.c
util.h
util.c
appinfo.json
wscript
```
## Sharing Files Between Platforms
Using the modified wscript approach as shown above still requires any files that
are used on both platforms to be included twice: in the respective folder. You
may wish to reduce this clutter by moving any platform-agnostic files that both
platforms use to a `common` folder inside `src`.
You project might now look like this:
```text
my_project
resources
images
banner~bw.png
banner~color.png
src
aplite
main.c
basalt
main.c
common
util.h
util.c
appinfo.json
wscript
```
To tell the SDK to look in this extra folder during compilation of each
platform, further modify the two lines calling `ctx.pbl_program()` to include
the `common` folder in the array of paths passed to
[`ant_glob()`](https://waf.io/book/#_general_usage). This is shown in the code
snipped below, with unchanged lines ommitted for brevity:
```python
# Additionally modified to include the 'common' folder
ctx.pbl_program(source=ctx.path.ant_glob(['src/{}/**/*.c'.format(p), 'src/common/**/*.c']), target=app_elf)
/* Other code */
if build_worker:
# Also modify this line to look for common files in '/worker_src/common/'
ctx.pbl_worker(source=ctx.path.ant_glob(['worker_src/{}/**/*.c'.format(p), 'worker_src/common/**/*.c']), target=worker_elf)
else:
/* Other code */
```
## Important Notes
While this new `wscript` allows us to keep our source files for each platform
separated entirely, there are a couple of important limitations to take into
account when using this method (without any further modification):
* There can still be only *one* JavaScript file, in `src/js/pebble-js-app.js`.
You can simulate two JS files using a platform check:
```js
Pebble.addEventListener('ready', function() {
if(Pebble.getActiveWatchInfo && Pebble.getActiveWatchInfo().platform === 'basalt') {
// This is the Basalt platform
console.log('PebbleKit JS ready on Basalt!');
} else {
// This is the Aplite platform
console.log('PebbleKit JS ready on Aplite!');
}
});
```
* Each binary can be bundled with only the app resources required for that
specific platform. To learn how to package app resources with only a certain
platform, read the
[*Platform-specific Resources*](/guides/app-resources/platform-specific/)
guide.
## Conclusion
With this modification to a Pebble project's `wscript`, developers now have two
options when it comes to diverging their app code for Aplite- and Basalt-
specific features **without** the need to maintain two completely separate
projects.
You can see a simple example project that ses all these techniques over at
[`pebble-examples/multi-platform-wscript`]({{site.links.examples_org}}/multi-platform-wscript).