It's actually just a basic bash script file, along with a text file where you define:
- A number of "targets" (e.g. CIQ1 vs CIQ2 vs CIQ3)
- The annotations to be applied to each target
- The C-style macros to be used with #if, #ifdef, and #ifndef so we can enjoy our archaic line-by-line source code inclusion/exclusion in Connect IQ.
In the example below, assume that the following annotations have already been set up to be excluded by the appropriate targets, in the jungle file:
:round - all round watches
:semiround - all semiround watches
:ciq2 - all watches currently supporting ciq2 (but not ciq3)
:ciq3 - all watches currently supporting ciq3
In the example, the class to be conditionally compiled has a whopping 3 features. There are two build targets:
A) Round CIQ3 watches
B) Semiround CIQ2 watches
Target A supports features 1 and 2.
Target B supports features 2 and 3.
Explanation:
- generate.sh looks for files named *.src (and *.mc-src) in the current folder. It creates a new subfolder called autogen/ and places modified versions of the source files based on the defined targets. All comments and whitespace are preserved
- I recommend using the extension .mc-src for your "unprocessed MC files". That way you can open *.mc-src in the Monkey C Eclipse editor and still have syntax highlighting, etc.
- I recommend using the extension .mc-src for your "unprocessed MC files". That way you can open *.mc-src in the Monkey C Eclipse editor and still have syntax highlighting, etc.
- All files in the subfolder called autogen/ will be deleted when this script runs, so make sure you don't put anything else in there
- generate.sh uses the target definition file (generate.targets) that you provide, to create the modified source files:
generate.targets (example)
targetA|:round,:ciq3|FEATURE_ONE,FEATURE_TWO
targetB|:semiround,:ciq2|FEATURE_TWO,FEATURE_THREE
global||GLOBAL_SYMBOLS
The first target in this example is
"targetA|:round,:ciq3|FEATURE_ONE,FEATURE_TWO"
- targetA is just a descriptive name for your target that will be used to generate the modified source file.
- :round,:ciq3 is the list of annotations that apply to the given target. The C-style define BUILD_TARGET will be set to ":round :ciq3" in this case. All unannotated global symbols and class definitions should have "(BUILD_TARGET)" in front of them.
- Global symbols and class definitions which already have annotations should be wrapped in a #IFDEF/ENDIF block with a macro that is defined once (e.g. GLOBAL_SYMBOLS)
In the above example, the "global" target is defined to handle global symbols and classes that are already annotated. - FEATURE_ONE,FEATURE_TWO is the list of C-style macros which will be applied to given target.
generate.sh
Bash script file. (Tested in Ubuntu for Windows 10. Should work in Linux, and it should work macOS as it doesn't require any features from Bash 4 afaik).
#!/bin/bash
#prereqs:
# bash
# gcc
#stackoverflow.com/.../how-do-i-split-a-string-on-a-delimiter-in-bash
#stackoverflow.com/.../how-can-i-join-elements-of-an-array-in-bash
#stackoverflow.com/.../read-lines-from-a-file-into-a-bash-array
function join_by { local IFS="$1"; shift; echo "$*"; }
# Read targets from file
IFS=$'\r\n' GLOBIGNORE='*' command eval 'TARGETS=($(cat generate.targets))'
mkdir -p autogen
rm -f autogen/*
shopt -s nullglob
# You can add more extensions besides "*.src" here.
# For example, you may wish to use .mc-src as a unique extension
# for MC source template files, so that you can open *.mc-src files
# in the Monkey C editor, in Eclipse.
for srcfile in *.src *.mc-src; do
#for srcfile in *.src ; do
echo Source file: $srcfile
echo ""
for TARGET in ${TARGETS[@]}; do
unset TARGET_ARRAY
unset ANNOTATIONS_ARRAY
unset DEFINES_ARRAY
IFS="|" read -ra TARGET_ARRAY <<< "$TARGET"
echo Target name: ${TARGET_ARRAY[0]}
IFS="," read -ra ANNOTATIONS_ARRAY <<< "${TARGET_ARRAY[1]}"
IFS="," read -ra DEFINES_ARRAY <<< "${TARGET_ARRAY[2]}"
echo Target annotations: ${ANNOTATIONS_ARRAY[@]}
echo Target defines: ${DEFINES_ARRAY[@]}
BUILD_TARGET_DEFINE=$(join_by " " ${ANNOTATIONS_ARRAY[@]})
unset DEFINES_ARGS_ARRAY
count=0;
for DEFINE in ${DEFINES_ARRAY[@]}; do
DEFINES_ARGS_ARRAY[count]="-D$DEFINE"
count=$(( $count + 1 ))
done
# Source file: MYFILE.mc.src
# Output file: autogen/MYFILE.TARGETNAME.mc
filename=$srcfile
extension="${filename##*.}"
filename="${filename%.*}"
extension="${filename##*.}"
filename="${filename%.*}"
DESTFILE=autogen/$filename.${TARGET_ARRAY[0]}.$extension
echo Writing $DESTFILE
set -x
gcc -nostdinc -traditional-cpp -C -x c -P -E "-DBUILD_TARGET=$BUILD_TARGET_DEFINE" ${DEFINES_ARGS_ARRAY[@]} "$srcfile" > "$DESTFILE"
set +x
echo ""
done
echo "==================================="
echo ""
done
Example MC.SRC file that implements 2 of 3 features, depending on your target.
myClass.mc.src
or
myClass.mc.mc-src
#ifdef GLOBAL_SYMBOLS // prevent dupe symbol definitions
(:ciq2) const isCiq3 = false;
(:ciq3) const isCiq3 = true;
#else // ifndef GLOBAL_SYMBOLS
(BUILD_TARGET) class myClass
{
function initialize()
{
#ifdef FEATURE_ONE
System.println("I support feature one."); // comment 1
#endif
#ifdef FEATURE_TWO
System.println("Feature two is kinda cool, I guess."); // comment 2
#endif
#ifdef FEATURE_THREE
System.println("A third feature is nice to have."); // comment 3
#endif
}
}
#endif // ifndef GLOBAL_SYMBOLS
Generated output:
myClass.global.mc
(:ciq2) const isCiq3 = false;
(:ciq3) const isCiq3 = true;
myClass.targetA.mc
(:round :ciq3) class myClass
{
function initialize()
{
System.println("I support feature one."); // comment 1
System.println("Feature two is kinda cool, I guess."); // comment 2
}
}
myClass.targetB.mc
(:semiround :ciq2) class myClass
{
function initialize()
{
System.println("Feature two is kinda cool, I guess."); // comment 2
System.println("A third feature is nice to have."); // comment 3
}
}