Uploaded the Garmin Dev Tools to: https://github.com/flocsy/garmin-dev-tools
If you're a CIQ developer then you might find interesting aggregation of the data collected from the SDK.
Uploaded the Garmin Dev Tools to: https://github.com/flocsy/garmin-dev-tools
If you're a CIQ developer then you might find interesting aggregation of the data collected from the SDK.
For someone coming in cold, your introduction: “A collection of shell scripts for Garmin ConnectIQ and Monkey C developers.” could do with a bit more detail. What developer issues does it address?
Sure, but who's going to read through that? The GitHub README.md is the place for the description.
Added an example with comments: https://github.com/flocsy/garmin-dev-tools/blob/main/monkey-generator/monkey.template.jungle
Thanks for this!
I have a small nitpick about the following comments:
github.com/.../monkey.template.jungle
# I find the excludeAnnotations to be counter-intuitive, so I use them more like "includeAnnotations":
# For annotations I technically use the excludeAnnotations but with a "positive" (aka include) meaning:
# monkey-generator generates the excludeAnnotations based on the assumption, that they will be used as include annotation,
# so when it determines that a device has the feature `foo` it'll have the opposite excludeAnnotation:
# fr965.excludeAnnotations=...;no_foo;...
# and if a device doesn't have feature `bar` then it'll have:
# fr230.excludeAnnotations=...;bar;...
# So in the code you can use the "positive' or "include" annotation:
# (:no_foo) const HAS_FOO = false;
# (:foo) const HAS_FOO = true;
# (:no_foo) function func1() {do whatever you need to for devices without foo}
# (:foo) function func1() {do whatever you need to for devices with foo}
While I agree the concept of excludeAnnotations in monkey.jungle is somewhat counter-inituitive (you have to exclude the annotations for code you *don't* want in the build *), Garmin's usage (as in naming / semantics) of annotations is exactly the same as yours. Meaning that a Garmin-defined annotation of X has a positive/inclusive meaning when it appears in source code: it means the annotated symbol is associated with X, not that the annotated symbol isn't associated with X.
For example:
- release-only code is annotated with :release [**]
- debug-only code is annotated with :debug [**]
- code available to the background is annotated with :background
etc.
So Garmin actually uses annotations in the same way you do (in a "positive/inclusive sense") - your interpretation/usage of annotations/excludeAnnotations is not at all different from Garmin's intent, as far as I can tell.
To be clear, the fact that annotations are excluded with excludeAnnotations in monkey.jungle in no way suggests that annotations themselves are supposed to "negative" / "exclusive" (or have the opposite meaning to what you'd expect).
[*] having said that, I kind of understand why Garmin did it that way - it's probably because they want the default behaviour to be to include all annotated and non-annotated code, which makes it somewhat natural to ask devs to explicitly exclude annotations they *don't* want.
[**] As a counter-example, Garmin does not ask you to annotate release-only code with :debug and then exclude all :debug code during debug builds. On the contrary, it asks you annotate release-only code with :release, and for debug builds, all code annotated with :release is included (referring back to your concept of "include annotations") while all code annotated with :debug is excluded.
So in the code you can use the "positive' or "include" annotation:
I think a possible misunderstanding arises from thinking of "exclude" in "excludeAnnotations" as an adjective (or noun modifier), meaning that the annotations themselves are somehow associated with the concept of exclusion. I don't think this is true, I think "exclude" is supposed to be a verb. (As in "Exclude the following annotations: ...")
In other words, in Monkey C, there is no such concept as an "exclude annotation" or even an "include annotation" (the "include" modifier is unnecessary). Your source code's symbols can have annotations (no adjective), and monkey.jungle can be used to exclude a set of annotations for a given qualifier.
Note that the Monkey C doc on annotations does not use the word "exclude" or "exclusion": https://developer.garmin.com/connect-iq/monkey-c/annotations/
The CIQ doc on configuration build process says: developer.garmin.com/.../
With Jungles, developers may:
Exclude portions of source code with annotations
Note the use of "exclude" as a verb, not an adjective that modifies "annotations"
Feeling Excluded
We don’t want to include both versions in any executable because one version would just be dead code. Jungles allow us to specify this using exclusions.
Note how "annotations" and "exclusions [of annotations]" are two separate (yet related) terms/concepts.
TL;DR actually you and Garmin use annotations in exactly the same way, and I think the comments which suggest otherwise, as well as the use of the phrase "the...'include' annotation", actually muddy the waters. You are not doing anything different than what Garmin intends, or what it does with its own standard annotations.
It might be clearer if you think of excludeAnnotations as "annotations to exclude".
This (and the previous 2) comment is funny. I think Garmin managed to confuse you in another level than you realized:
Looking at the source code you are right. However both in my and your usage of words we haven't differentiate between annotations and exclude annotations, or at least not consistent enough.
I think there are 4 types of annotations that Garmin use. All of them look the same in the code: (:annotation), but the way they are activated is different:
1. by the compiler flags: debug, release are set by the compiler and they are "mandatory" and mutually exclusive pairs. In every build exactly one of them is included and the other one is excluded. I'd call them include annotation from the developers perspective.
2. by the compiler flags: test. But this unfortunately has no mutually exclusive pair. This was one of the reasons I started to develop monkey-generator, to be able to switch between a pair of annotation (testing, no_testing) by only changing the code in 1 place (arguably you could do that by adding or removing "no_" prefix in monkey.jungle :) Also because test is built-in, you can't really add it's pair by setting an excludeAnnotation: no_test. If you are curious why I need it: I have a function that is called by some tests that is only needed by tests.
3. excludeAnnotations set by the developer in jungle. However to me it's counter intuitive. It has an additional layer of thinking: what should I call it so it makes sense? Where do I use it more? In mc files or in jungle? Because monkey-generator was born (or is just being born) as a personal project I decided to have this convention that my annotations are being called in the positive sense, as the developer would want the annotated code to be included in the mc file, and monkey generator takes care of adding the "opposite" as excludeAnnotation. Also the naming convention to call the annotations foo and no_foo and also that I automatically generate a pair of annotation for every feature, even if it's not used in the mc files.
4. "include" annotations, that less developers use it even know about when they use parts of a bundle.
But I'll change that sentence in the example file.
This (and the previous 2) comment is funny.
I think Garmin managed to confuse you in another level than you realized
I'm not confused at all, I just disagree that there are different "kinds" of annotations based on how they are included/excluded in the build.
I think you have to separate the concepts of:
1) annotations (simply text labels that are associated with source code symbols)
2a) the specific circumstances of when code annotated with a specific symbol is included in the build. (e.g. code with :background is in the background, glance, and main processes. e.g. code :with release is in the release build but not the debug build)
2b) the *mechanisms* by which 2a) is accomplished. (the jungle excludeAnnotations qualifier property is one mechanism. the test compiler flag is another. the layout of the PRG wrt to the background, glance and other sections is yet another). Will you define each annotation with a different include/exclude mechanism as a different "kind"? Maybe you'll need more modifiers than "include" and "exclude", especially since your own comment says there are 4 kinds.
And I disagree that "[an] include annotation" and "[an] exclude annotation" make sense either conceptually or grammatically. I don't think you've defined either of these terms coherently at all. You seem to be defining "an include annotation" in *opposition* to "an exclude annotation", but you haven't properly defined what an "exclude annotation" is. It can't be simply an annotation that's specified in an "excludeAnnotations" directive as obviously "include annotations" can *also* be specified in excludeAnnotations. Is an "exclude annotation" an annotation whose text is defined with a negative meaning? If so, you haven't explained how Garmin ever uses such an "exclude annotation" or forces devs to define annotations with such text.
Again: Garmin does not ask you to annotate debug code with "release" or even "not-release". And you can use the excludeAnnotations property/directive to implement "include annotations" as you call them, so we may as well define "exclude annotations" to be nothing at all (nobody is forced to use them.) And if there are no "exclude annotations", then the concept of "include annotations" (in opposition to the other kind) is not necessary.
It's like looking at IDs on a whitelist and blacklist (or allowlist / denylist), and saying that the IDs themselves are "allow IDs" and "deny IDs". No, that wouldn't make sense - they're just plain IDs that have either been allowed or denied.
And as I've argued, Garmin itself did not intend for the term "an exclude annotation" to be used (where exclude is an adjective/noun modifier). They never use such a term in their own documentation.
excludeAnnotations clearly means "annotations to exclude" (exclude is a verb in this context, not a noun modifier/adjective).
1. by the compiler flags: debug, release are set by the compiler and they are "mandatory" and mutually exclusive pairs. In every build exactly one of them is included and the other one is excluded. I'd call them include annotation from the developers perspective.
Why? Just because :release code is "included" for the "release" build and :debug code is "included" for debug build?
If exactly one of them is included and exactly one is excluded per build, why prefer "include" over "exclude"? The doc says this:
https://developer.garmin.com/connect-iq/monkey-c/annotations/
:debug Code blocks decorated with this annotation will not be included in release builds at compile time.
:release Code blocks decorated with this annotation will not be included in debug builds at compile time.
Based on that wording, should they be "exclude annotations"?
Ofc it could've been worded differently:
:debug Code blocks decorated with this annotation will only be included in debug builds at compile time.
:release Code blocks decorated with this annotation will only be included in release builds at compile time.
Based on that wording, should they be "include annotations"?
What about :glance and :background? Are they "include annotations"? If so, they must be a different type of "include annotation" than :release and :debug.
Guess what, the exact mechanism of how :debug and :release are included/excluded doesn't matter, only the end result matters. That's why it makes no sense to attach "include" and "exclude" to the annotations themselves.
4. "include" annotations, that less developers use it even know about when they use parts of a bundle.
Must be a different kind of "include annotation" than :release and :debug, hmm.
3. excludeAnnotations set by the developer in jungle. However to me it's counter intuitive. It has an additional layer of thinking: what should I call it so it makes sense? Where do I use it more? In mc files or in jungle?
It is counter-intuitive and annoying that you have to specify annotations to exclude, but the fact that you feel the need to ask these questions tells me that you fail to separate the concept of an annotation with how code with that annotation is included/excluded from the build.
Let's consider the annotations :foo and :not_foo. I want to use :foo for code that's on devices that support feature foo, and I want to use :not_foo for code that's on devices that don't support feature foo.
e.g.
(:foo) function hasFoo() { return true; }
(:not_foo) function hasFoo() { return false; }
Let's say fr965 supports foo but fr935 does not.
Under the current annoying excludeAnnotations regime, we have the following monkey.jungle snippet:
fr935.excludeAnnotations = foo
fr965.excludeAnnotations = not_foo
Yes, it's extremely confusing and counter-intuitive on the monkey.jungle side. But it's pretty intuitive on the MC side, no?
Now let's say that Garmin adds a new includeAnnotations property (this means "annotations to include"), where:
- if includeAnnotations is *not* specified, then all user-defined annotations are included
- if includeAnnotations is specified, then no user-defined annotations are included except those that are explicitly specified
- for simplicity, lets say this feature does not work if excludeAnnotations is specified and vice versa (includeAnnotations and excludeAnnotations are mutually exclusive)
Now I can change my jungle file to be more intuitive:
fr935.includeAnnotations = not_foo
fr965.includeAnnotations = foo
Notice how:
- I did not have to change the conceptual definitions of :foo and :not_foo (they still mean the same thing)
- I did not have to change a single line of MC code
The hypothetical includeAnnotations property did not flip the "type" of the annotations from "exclude annotations" to "include annotations". The meaning and usage of the annotations in the source code did not change at all, only the mechanism by which they were included/excluded from the build.
To be clear: absolutely nothing about the annotations themselves changed even though the mechanism of including/excluding them from the build changed from "excludeAnnotations" to "includeAnnotations". The meaning of the source code did not change, and neither did the build output.
Because monkey-generator was born (or is just being born) as a personal project I decided to have this convention that my annotations are being called in the positive sense, as the developer would want the annotated code to be included in the mc file, and monkey generator takes care of adding the "opposite" as excludeAnnotation.
It's funny that you think my comments are funny, yet you persistently fail to understand that:
- Garmin's built-in annotations are ALL named in the positive sense (background is for background code, release is for release code, glance is for glance code, etc)
- even Garmin's example of using excludeAnnotations has annotations named in the positive sense
:roundVersion is for round products, :regularVersion is for "regular" (non-round) products. monkey.jungle excludes roundVersion for regular products, and excludes regularVersion for round products. To make it even closer to your "concept" of "include annotations", just rename regularVersion to nonRoundVersion.
This example is conceptually no different than your monkey-generator example, yet Garmin feels no need to introduce a new concept of "include annotations" and to incoherently assert that "excludeAnnotations are actually being used as include annotations" or something like that.
I challenge you to find a Garmin example where annotations are named in the negative sense, or where such a usage is a necessary consequence of the excludeAnnotations mechanism (how can it be, since your monkey-generator uses excludeAnnotations to implement "include annotations".)
Again, you fail to separate to concept of annotations from the semantics and mechanisms of including/excluding annotated code.
And you don't see that the annotation naming convention you have chosen is the same convention that Garmin uses and expects you to use. You are using excludeAnnotations exactly how it was intended to be used!