barebones-watchface 2.1.1

Barebones Watch Face for Mynewt on PineTime Smart Watch
# GitHub Actions Workflow to build WebAssembly Simulator for Rust on Mynewt
# See https://github.com/AppKaki/lvgl-wasm/blob/mynewt/README.md
# TODO: Sync with https://github.com/AppKaki/lvgl-wasm/blob/mynewt/.github/workflows/simulator.yml

# Name of this Workflow
name: Simulate PineTime Firmware

# When to run this Workflow...
on:

  # Run this Workflow when files are updated (Pushed) in the "master" Branch
  push:
    branches: [ master, mynewt ]
    
  # Also run this Workflow when a Pull Request is created or updated in the "master" Branch
  pull_request:
    branches: [ master, mynewt ]

# Steps to run for the Workflow
jobs:
  build:

    # Run these steps on Ubuntu
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2

    # - name: Fetch cache for Rust Toolchain
    #   id:   cache-rust
    #   uses: actions/cache@v2
    #   with:
    #     path: |
    #       ~/.cargo/registry
    #       ~/.cargo/git
    #       target
    #     key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}

    - name: Install Rust Toolchain for emscripten
      run:  |
        rustup default nightly
        rustup target add wasm32-unknown-emscripten

    - name: Check cache for emscripten
      id:   cache-emsdk
      uses: actions/cache@v2
      env:
        cache-name: cache-emsdk
      with:
        path: /tmp/emsdk
        key:  ${{ runner.os }}-build-${{ env.cache-name }}
        restore-keys: ${{ runner.os }}-build-${{ env.cache-name }}

    # We download emscripten instead of building from source because it changes often and causes emcc build errors
    - name: Download emscripten
      if:   steps.cache-emsdk.outputs.cache-hit != 'true'  # Download emscripten if not found in cache
      run:  |
        # Download the emscripten binary from lvgl-wasm
        cd /tmp
        curl -L https://github.com/AppKaki/lvgl-wasm/releases/download/v1.0.0/emsdk.tgz -o emsdk.tgz

        # Unzip to /tmp/emsdk
        tar xzf emsdk.tgz
        rm emsdk.tgz

        # Activate PATH and other environment variables in the current terminal
        source /tmp/emsdk/emsdk_env.sh

        # Show version
        emcc --version
        emcc --version        

    # - name: Install emscripten from source
    #   if:   steps.cache-emsdk.outputs.cache-hit != 'true'  # Install emscripten if not found in cache
    #   run:  |
    #     # Based on https://emscripten.org/docs/getting_started/downloads.html
    #     cd /tmp

    #     # Get the emsdk repo
    #     git clone https://github.com/emscripten-core/emsdk.git

    #     # Enter that directory
    #     cd emsdk

    #     # Download and install the latest SDK tools.
    #     ./emsdk install latest

    #     # Make the "latest" SDK "active" for the current user. (writes .emscripten file)
    #     ./emsdk activate latest

    #     # Activate PATH and other environment variables in the current terminal
    #     source ./emsdk_env.sh

    #     # Show version
    #     emcc --version
    #     emcc --version      
       
    - name: Check cache for wabt
      id:   cache-wabt
      uses: actions/cache@v2
      env:
        cache-name: cache-wabt
      with:
        path: /tmp/wabt
        key:  ${{ runner.os }}-build-${{ env.cache-name }}
        restore-keys: ${{ runner.os }}-build-${{ env.cache-name }}

    # We download wabt instead of building from source because building wabt is slow
    - name: Download wabt
      if:   steps.cache-wabt.outputs.cache-hit != 'true'  # Download wabt if not found in cache
      run:  |
        # Download the wabt binary from lvgl-wasm
        cd /tmp
        curl -L https://github.com/AppKaki/lvgl-wasm/releases/download/v1.0.0/wabt.tgz -o wabt.tgz
        
        # Unzip to /tmp/wabt
        tar xzf wabt.tgz
        rm wabt.tgz
                
    # - name: Install wabt from source
    #   if:   steps.cache-wabt.outputs.cache-hit != 'true'  # Install wabt if not found in cache
    #   run:  |
    #     cd /tmp
    #     git clone --recursive https://github.com/WebAssembly/wabt
    #     cd wabt
    #     mkdir build
    #     cd build
    #     cmake ..
    #     cmake --build .

    - name: Checkout Rust+LVGL for WebAssembly
      run:  |
        cd /tmp
        git clone --branch mynewt https://github.com/AppKaki/lvgl-wasm

    - name: Prebuild Rust+LVGL for WebAssembly
      run:  |
        # Add emscripten and wabt to the PATH
        source /tmp/emsdk/emsdk_env.sh
        export PATH=$PATH:/tmp/wabt/build
        cd /tmp/lvgl-wasm

        # Build Rust app, ignore the error:
        # error[E0432]: unresolved import `ad`
        # --> /home/runner/.cargo/registry/src/github.com-1ecc6299db9ec823/cty-0.1.5/src/lib.rs:8:9
        # pub use ad::*;
        # ^^ maybe a missing crate `ad`?
        # TODO: Fix this workaround and the next step

        cargo build || ls -l $HOME/.cargo/registry/src/github.com-*/cty-0.1.5/src/lib.rs

        # Will be patched in the next step

    - name: Patch Rust cty crate for WebAssembly
      run:  |
        # Patch the previous step: Change $HOME/.cargo/registry/src/github.com-*/cty-0.1.5/src/lib.rs
        # From
        #   target_arch = "arm"
        # To
        #   target_arch = "arm", target_arch = "wasm32"
        # TODO: Fix this workaround and the previous step

        cat $HOME/.cargo/registry/src/github.com-*/cty-0.1.5/src/lib.rs \
            | sed 's/target_arch = "arm"/target_arch = "arm", target_arch = "wasm32"/' \
            >/tmp/lib.rs
        cp /tmp/lib.rs $HOME/.cargo/registry/src/github.com-*/cty-0.1.5/src/lib.rs
        rm /tmp/lib.rs
        cat $HOME/.cargo/registry/src/github.com-*/cty-0.1.5/src/lib.rs
        
    - name: Inject Rust Watch Face into Rust+LVGL for WebAssembly
      run:  |
        # Add emscripten and wabt to the PATH
        source /tmp/emsdk/emsdk_env.sh
        export PATH=$PATH:/tmp/wabt/build

        # Generate the Rust Metadata
        cargo metadata --format-version=1 >/tmp/metadata.json

        # Get the watchface crate name, version and path from the crate metadata
        sudo apt install jq
        crate_root=`jq '.resolve.root' /tmp/metadata.json`
        echo crate_root=$crate_root
        # Returns: "barebones-watchface 1.0.2 (path+file:///home/runner/work/barebones-watchface/barebones-watchface)"

        # Extract watchface crate name e.g. my-watchface
        crate_name=`expr match "$crate_root" '\"\([^ ]*\) '`
        echo crate_name=$crate_name

        # Replace all "-" in crate name to "_" e.g. my_watchface
        crate_name_underscore=${crate_name//-/_}
        echo crate_name_underscore=$crate_name_underscore

        # Extract watchface crate version e.g. 1.0.2
        crate_version=`expr match "$crate_root" '[^ ]* \([^ ]*\) '`
        echo crate_version=$crate_version

        # Extract watchface crate path e.g. /home/runner/work/my-watchface/my-watchface
        crate_path=`expr match "$crate_root" '.*path+file://\([^ ]*\))'`
        echo crate_path=$crate_path

        # Escape the crate path e.g. \/home\/runner\/work\/my-watchface\/my-watchface
        crate_path_escaped=${crate_path//\//\\/}
        echo crate_path_escaped=$crate_path_escaped

        # Search all files *.rs in crate path src for
        #   impl WatchFace for MyWatchface {
        shopt -s globstar
        ls $crate_path/src/**/*.rs
        matches=`grep -r "impl WatchFace" $crate_path/src/**/*.rs`
        echo matches=$matches
        # Returns: impl WatchFace for MyWatchface {

        # Extract the watchface struct name e.g. MyWatchface
        struct_name=`expr match "$matches" '.*impl[ ]*WatchFace[ ]*for[ ]*\([^ {]*\)'`
        echo struct_name=$struct_name

        # Go to the LVGL folder
        cd /tmp/lvgl-wasm

        # Change mynewt/wasm/Cargo.toml from
        #   barebones-watchface = "1.0.2"
        # to
        #   my-watchface = { path = "/home/runner/work/my-watchface/my-watchface" }

        cat mynewt/wasm/Cargo.toml \
            | sed "s/^barebones-watchface.*$/$crate_name = { path = \"$crate_path_escaped\" }/" \
            >mynewt/wasm/Cargo.toml.new
        cp mynewt/wasm/Cargo.toml.new mynewt/wasm/Cargo.toml
        rm mynewt/wasm/Cargo.toml.new
        echo Patched mynewt/wasm/Cargo.toml...
        cat mynewt/wasm/Cargo.toml

        # Change mynewt/wasm/src/lib.rs from
        #   use barebones_watchface::watchface::lvgl::mynewt::fill_zero;
        #   use barebones_watchface::watchface::{self, WatchFace};
        #   type WatchFaceType = barebones_watchface::BarebonesWatchFace;
        # to
        #   use my_watchface::watchface::lvgl::mynewt::fill_zero;
        #   use my_watchface::watchface::{self, WatchFace};
        #   type WatchFaceType = my_watchface::MyWatchFace;
        # Change crate name from "-" to "_"

        cat mynewt/wasm/src/lib.rs \
            | sed "s/barebones_watchface/$crate_name_underscore/" \
            | sed "s/BarebonesWatchFace/$struct_name/" \
             >mynewt/wasm/src/lib.rs.new
        cp mynewt/wasm/src/lib.rs.new mynewt/wasm/src/lib.rs
        rm mynewt/wasm/src/lib.rs.new
        echo Patched mynewt/wasm/src/lib.rs...
        cat mynewt/wasm/src/lib.rs

    - name: Build Rust+LVGL for WebAssembly
      run:  |
        # Add emscripten and wabt to the PATH
        source /tmp/emsdk/emsdk_env.sh
        export PATH=$PATH:/tmp/wabt/build
        cd /tmp/lvgl-wasm

        # Build Rust+LVGL app: wasm/lvgl.html, lvgl.js, lvgl.wasm
        cargo build
        make -j -f mynewt/Makefile

        # Dump the WebAssembly modules
        wasm-objdump -x wasm/lvgl.wasm >wasm/lvgl.txt

        # Rename the HTML files so we don't overwrite the updates
        mv wasm/lvgl.html wasm/lvgl.old.html

    - name: Show files
      run:  set ; pwd ; ls -l /tmp/lvgl-wasm

    - name: Copy WebAssembly to GitHub Pages
      run:  |
        if [ ! -d docs ]; then
          mkdir docs
        fi
        export src=/tmp/lvgl-wasm
        export docs=$src/docs
        export wasm=$src/wasm
        cp \
          $docs/lvgl.html \
          $wasm/*.html \
          $wasm/*.js \
          $wasm/*.wasm \
          $wasm/*.txt \
          docs

    - name: Commit GitHub Pages
      uses: EndBug/add-and-commit@v4.4.0
      with:
        # Arguments for the git add command
        add: docs
        # The name of the user that will be displayed as the author of the commit
        # author_name: # optional
        # The email of the user that will be displayed as the author of the commit
        # author_email: # optional
        # The directory where your repository is located. You should use actions/checkout first to set it up
        # cwd: # optional, default is .
        # Whether to use the force option on git add, in order to bypass eventual gitignores
        # force: # optional, default is false
        # Whether to use the signoff option on git commit
        # signoff: # optional, default is false
        # The message for the commit
        # message: # optional, default is Commit from GitHub Actions
        # Name of the branch to use, if different from the one that triggered the workflow
        # ref: # optional
        # Arguments for the git rm command
        # remove: # optional, default is 
        # The name of the tag to add to the new commit
        # tag: # optional, default is 

    - name: Upload Outputs
      uses: actions/upload-artifact@v2
      with:
        name: wasm
        path: |
          /tmp/lvgl-wasm/wasm/*.html
          /tmp/lvgl-wasm/wasm/*.js
          /tmp/lvgl-wasm/wasm/*.wasm
          /tmp/lvgl-wasm/wasm/*.txt
          /tmp/metadata.json