Best practices for build exclusions & exclusion based on API level

I'm sorry if I missed a Jungle tutorial somewhere, and I'm glad if I'm pointed to the right direction. (I've read this, this, and a few relevant posts here.)

tl;dr: I have two questions:

  1. when is it reasonable to use build exclusions for code over runtime checks?
  2. is there a qualifier for API levels, or should one "build" it manually in Jungle?

Thought process:

So the scenario I want to address currently is a simple and common one: use a certain thing in older API versions, and a newer in others. My exact case (and current version of the code):

        if (Application.AppBase has :Properties) {
            clubId = self.Properties.getValue("club");
        } else {
            clubId = self.getProperty("club");
        }
        self.clubData = new ClubData(clubId);

This works fine, and I understand that this has the advantage of "switching" to the newer version under the hood if the API support is (FW) updated for a watch without reinstalling the app itself, as pointed out in this topic. (I'm not sure though, how often that happens.)

If I understand correctly, the other option is to use build exclusion feature of Jungles (that behaves like C macros if I understand correctly), so I can exclude the unnecessary code parts in compile time. However, I struggle to find, if there is a built-in annotation for API level, or one should manually build it. I have seen a (:newer) and (:older) in one of the examples, but I guess, these are custom things, which I have to "construct" myself. What is the proper way to do this? (Question 2) Also, if I understand this correctly, the "built" API version and the "real" API version can differ after a FW update, so this might even cause issues (if backward compatibility is not adhered).

A related question: can higher level of optimizations "optimize out" branches like the above? I'd assume not, as `AppBase has` can only be evaluated at runtime, and it would invalidate the mentioned benefit above.

I completely understand that in my current case the exclusion of this one unnecessary instruction call would be a way overkill in terms of optimization, and I'd even lose the aforementioned benefit.

So my main question (numero zwei) could be probably best phrased as this: what are the best practices for choosing between a dynamic has check, and a compile time Jungles build exclusion. (I assume, that similar compile time checks can be made for families as well.) What is the complexity/size of the code, when the latter becomes preferred over the former? Is there even a practical reason for this? I could argue, that in terms of size, the code itself is negligible compared to resources; and in terms of computation, a single conditional is probably also negligible compared to any background calculation or UI update. So should one use build exclusions at all for code?  (I understand its role and importance for resources.)

These are my thoughts, but I barely spent a few dozen hours with MonkeyC so far, so I'd be glad to hear the experience and recommendation of experienced developers.

  • You can't do anything in Jungles based on an API level.  It would get even tickier of you think of Word Wide versions of a device and APAC versions, which might have different Api levels.  You can excluse devices from an app if they don't have what you need, or set a ApiLevel in the manifest so only allow devices with an ApiLevel of that or above.

    Or use runtime "has", which also has the benefit of adapting if a device gets new and updated firm ware.  That could be really helpful with the upcoming roll out of System 7/CIQ 5

  • Thanks, so question 2 is completely answered. (Also, I've read a response from you in a different topic with this APAC complication, tbh, I'm not ready for that yet :-D)

    Yeah, I know the manifest options, but the point is that I wish to support the old devices. (In the current situation a FR230).

    Regarding question 1, so where have you used / where do you suggest/recommend to use build exclusions (for code) instead of `has` or other runtime checks?

  • I can't recall the last time I used build exclusions.  It's been a few years.  "has" is my go-to.

    In a case like where a device has on-device maps and I support devices that don't, I build with different mc files - one set for maps, one for no maps. 

    In that case, it's an app I actually started on for the fr230 when it came out.  I've been doing apps since CIQ first came out and pretty much support every CIQ device in many of my apps.

  • A related question: can higher level of optimizations "optimize out" branches like the above? I'd assume not, as `AppBase has` can only be evaluated at runtime, and it would invalidate the mentioned benefit above.

    The compiler actually can optimize away "has" checks.

    As some devs have expressed concern that this may break certain use cases (such the case where a device currently lacks support for a certain feature, but will gain support in the future), Garmin plans to introduce a compiler flag to disable "has check optimizations related to API functionality."

    Whether this optimization affects your specific example is unclear to me (in a perfect world I think your case would be optimized.) It's also unclear to me whether the new compiler flag would disable optimization in your example.

    Prior to System 7 the compiler could optimize some has checks based on the API level. With System 7 the compiler can now optimize based on if the device supports a feature, even if it meets the API level required for the feature. An example of this would be Attention.setFlashlightMode(). With System 6, an app built with if (Attention has :setFlashlightMode) would be evaluated at run-time. With System 7 this is now optimized out at compile time.
    We'll be adding a command line option to the compiler that will allow disabling has check optimizations related to API functionality.

    [https://forums.garmin.com/developer/connect-iq/f/discussion/355069/doesn-t-system-6-has-already-compile-out-unused-branches-code]

    [https://forums.garmin.com/developer/connect-iq/f/discussion/347053/instinct-2x-has-api-lvl-3-4-0-instead-of-4-2-0/1713731#1713731]

  • Wow, thank  you, super interesting info. Though the quoted message is a bit ambiguous for me too. Thanks!

  • That was a long read :-D (even with skipping the middle part for another day, though this experience proves that threading, and linking exact comments would be welcome in the forums.) I learned something new again with this System level things. If I understood correctly, System levels are kind of "diagonal" API levels. So it can happen that something is said in the references to be available since 3.3, but that doesn't run on 4.1, so if there is a device which didn't get a 4.2 firmware update, than that's it. Am I understanding this correctly? If so, this is seriously unintuitive (and misleading, at least for me). Is there a difference in the feature sets of 4.0 vs 3.2, or between 4.2 vs 3.4?

  • though this experience proves that threading, and linking exact comments would be welcome in the forums.)

    There used to be threading and infinite scroll in the Connect IQ part of the forums only, and the user experience failed (especially with very long threads) so they switched back to a linear/non-threaded, paginated view. Personally I wish they had gone with a modern infinite scrolling experience like NodeBB or Discourse which combines the best of paging and infinite scroll - you can scroll to your heart's content, but you can also jump to any part of the discussion instantly (with a custom scrubber UI element which also allows you to enter a comment number to jump to). (To be fair, those implementations aren't really threaded - at best they're a hybrid between a linear and threaded view.) The other advantage of those implementations is they aren't terrible on mobile.

    As much as I like threading in reddit and modern comment sections, I do think a mostly linear view does lend itself well to old-style discussion forums where topics can go on for years. The downsides with that:

    - most topics shouldn't go on for years, but some get resurrected after years anyway (compare with reddit where most posts are locked after a few months)

    - it's hard to have a side discussion in a thread without annoying everyone else

    linking exact comments would be welcome in the forums.

    You can link to individual comments as above: [https://forums.garmin.com/developer/connect-iq/f/discussion/347053/instinct-2x-has-api-lvl-3-4-0-instead-of-4-2-0/1713731#1713731]

    Right click on the date/time of of a comment to get a direct link. e.g. "9 hours ago" in the following example:

    [https://forums.garmin.com/developer/connect-iq/f/discussion/356290/best-practices-for-build-exclusions-exclusion-based-on-api-level/1714330#1714330]

    Also, if you quote a comment by selecting text and clicking "Quote", the quote block will also be a link to the comment. You can even use this trick to quote text from another thread, as long as that thread still allows replies. Create a draft reply in the other thread, quote the text you want, and copy the quote block (don't save the draft reply.)

    If you're wondering why I put square brackets around links, it's because the forums have an annoying habit of abbreviating the text of certain links, and I prefer to show the entire URL in the link text in most cases.

    I noticed that there does seem to be a new bug/quirk in the forums which removes text following "[" unless you re-edit your post, though.

  • Is there a difference in the feature sets of 4.0 vs 3.2, or between 4.2 vs 3.4?

    4.x devices have hardware differences compared to 3.x. e.g. 4.x devices have a GPU, 3.x devices do not, so 4.x devices have support for anti-aliasing and affine transformations, and 3.x devices do not. I also noticed that there's translucency in the native UI for CIQ 4 devices, but not in CIQ 3 devices (which is kind of an issue when CIQ 3 and 4 devices share the same code for things like maps, but that's a whole other tangent.)

    There are also differences that are purely on the software side. For example, "super apps" are only supported in 4.x devices - a "super app" is the merging of the old "device app" (aka "watch app") and "widget" app types.. Pre CIQ 4.0, if you wanted an app to show up in the glance/widget loop (accessible by scrolling from the watch face), it had to be a widget. If you wanted an app to show up in the app/activity list, it had to be a device app. In CIQ 4.0, CIQ widgets technically don't exist - as before, device apps show up in the app/activity list, but they can also optionally show up in the glance loop if the dev codes the app in a certain way (by providing a glance view). (You'll note the subtle loss of functionality: it isn't possible to create an app for CIQ 4 devices which *only* shows up in the glance loop.) (If you're curious how this works when you create an app with type "widget" that supports multiple devices, under the covers it's built as a device app for CIQ 4 devices.)

  • I know of 2 goals: optimize for speed or code size.
    As for speed, as Jim will tell you: put your "has" code in the constructor and save it in a boolean, so you won't need to do it all over again when you use it.
    If you don't have code size problems, then I wouldn't worry about that. If you do (as I do in my datafield) then I would do as much as possible in jungle. At the beginning I too started by creating labels like api_x_y_z and no_api_x_y_z. But you need to have all the relevant devices in the jungle file. Later I rather went for labels based on features. ie: has_fit vs no_fit or has_foo vs no_foo. This gave me the additional feature to be able to turn of/off certain functionality by changing the default.
    Either way I use a python script I wrote to generate the jungle file from a template + the data from the devices' compiler.json and simulator.json: https://forums.garmin.com/developer/connect-iq/f/showcase/298405/garmin-dev-tools It's still work in progress, but useful nevertheless.

    And as other wrote all the above will be a bit less important soon, when the 7.0.X compiler (non Beta) will come out in a few weeks