その手の平は尻もつかめるさ

ギジュツ的な事をメーンで書く予定です

builderscon tokyo 2018に登壇します

表題の通り,builderscon tokyo 2018に登壇します.JavaCardという環境をテーマにお話する予定です.

builderscon.io

今回はかなりマニアックな話になることが現時点で予想されていますが,内容はかなり面白いと思います (先日社内の専門家にディープな講習を受けました). いつものJavaとは一味違うJavaを目の当たりにすることになるでしょう……

9/7 (金) の15:30からセッション開始ですから,お昼から会場内のHUBで一杯引っ掛けてから来てもちょうど良い感じの時間ですね! 是非会場でお会いしましょう!!!!

pptxファイルから発表者ノートを一括削除する方法

カンペとしてスライドに発表者ノートを書くことはままあるとおもうんですが,そのスライドを共有する際には発表者ノートを削除したくなるのが人情というものです (カンペを見られるのは恥ずかしい).
Windows版のPowerPointであれば発表者ノートを一括削除するメニューがあるのですが,一方macOS版のPowerPointにはなぜかそのメニューが無いため手でポチポチやるしか方法は無いのか……と途方に暮れていたところ,pptxはそもそもzipでアーカイブされたxmlの集合体ということを思い出し,unzipしてxmlを書き換えてzipし直すという手法を思いつきました.

以下スクリプト:

#!/bin/bash

set -eu

PPTX_FILE_PATH="$1"
PPTX_FILE="$(basename "$PPTX_FILE_PATH")"

TMP_DIR="$(mktemp -d)"
trap 'rm -rf $TMP_DIR' EXIT

cp "$PPTX_FILE_PATH" "$TMP_DIR"

pushd "$TMP_DIR"

unzip "$PPTX_FILE"
rm "$PPTX_FILE"
gfind ./ppt/notesSlides/*.xml | xargs gsed -i'' -e's/<a:t>[^<]*<\/a:t>/<a:t><\/a:t>/g' # remove slide note contents
zip -0 -r "$PPTX_FILE" ./* # do not compress (-0)

popd

cp "$TMP_DIR/$PPTX_FILE" "note-removed-$PPTX_FILE"

これで発表者ノートを消すべきpptxファイルがたくさんあっても安心! よかったですね.

ESP32でZephyrを開発する環境を整える - macOS編

ZephyrはApache Lisence, Version 2.0で公開されているRTOSです.多分「ゼファー」と読むはず.
https://www.zephyrproject.org/

このZephyrをESP32のボードで開発する環境をmacOS上に整えるというのがこの記事の目的です.
なおESP32開発ボードは今回以下を使いました: http://akizukidenshi.com/catalog/g/gM-11819/

基本的にZephyrの以下のドキュメントに従うとできます:

http://docs.zephyrproject.org/getting_started/installation_mac.html
http://docs.zephyrproject.org/boards/xtensa/esp32/doc/esp32.html

事前準備
$ brew install direnv cmake ninja dfu-util doxygen qemu dtc python3 gperf

開発環境が問答無用でSystemのPython3を見てくるのでインストールする必要があります.direnvはあるとなにかと便利なので入れています.

(上記開発ボードを使っている場合) CP210x USB to UART Bridge VCP driversを入れる

https://www.silabs.com/products/development-tools/software/usb-to-uart-bridge-vcp-drivers

もしもmacOS High Sierra以降を使っている場合は Security & Privacy でインストール時にBlockしているソフトウェアベンダーをAllowする必要があります (Silicon Laboratoriesとか).
これがないと開発ボードがシリアルデバイスとして認識されない.

Espressif Toolchainを入れる
$ mkdir -p ~/opt/esp
$ cd ~/opt/esp
$ curl -O https://dl.espressif.com/dl/xtensa-esp32-elf-osx-1.22.0-80-g6c4433a-5.2.0.tar.gz
$ tar zxf xtensa-esp32-elf-osx-1.22.0-80-g6c4433a-5.2.0.tar.gz

xtensa-esp32-elf-osxのバージョンについては実際どこを見れば最新なのかがわからなかった (ダウンロード可能なバージョン一覧が取れるページを見つけられなかった) ので,雑に調べて出てきたものを使っています.
確証はありませんが,
arduino-esp32/package_esp32_index.template.json at master · espressif/arduino-esp32 · GitHub
の中身に書いているやつを使っておけば良いような気がしています (espressifの公式リポジトリだし).

ESP32 IDFを入れる

要はRTOS SDKです.

$ git clone --recursive https://github.com/espressif/esp-idf.git
$ cd esp-idf
$ git checkout dc8c33892e0

espressifのリポジトリからESP32 IDFをcloneしてきます.
加えてZephyrのドキュメントにあったので,IDFのリビジョンを過去のものに固定しています.

Since ESP-IDF is an external project in constant development, it’s possible that files that Zephyr depends on will be moved, removed, or renamed. Those files are mostly header files containing hardware definitions, which are unlikely to change and require fixes from the vendor. In addition to setting the environment variables above, also check out an earlier version of ESP-IDF
http://docs.zephyrproject.org/boards/xtensa/esp32/doc/esp32.html

Zephyrをダウンロードして諸々の開発環境を整える
$ git clone git@github.com:zephyrproject-rtos/zephyr.git /path/to/zephyr
$ cd /path/to/zephyr
$ pip3 install --user -r scripts/requirements.txt
$ cat <<EOS >> .envrc

export PATH="$PATH:$HOME/opt/esp/xtensa-esp32-elf/bin
export ZEPHYR_TOOLCHAIN_VARIANT="espressif"
export ESP_IDF_PATH="$HOME/opt/esp/esp-idf"
export ESPRESSIF_TOOLCHAIN_PATH="$HOME/opt/esp/xtensa-esp32-elf/"

source ./zephyr-env.sh
export ESP_DEVICE="/dev/cu.blahblah" # ここは開発ボードによって変わるでしょう
export ESP_BAUD_RATE=115200 # ここは開発ボードによって変わるでしょう
export ESP_FLASH_SIZE="detect"
export ESP_FLASH_FREQ="40m"
export ESP_FLASH_MODE="dio"

EOS
$ direnv allow

ZephyrはGitHubで公開されているので普通にgit cloneしてくれば良いです.
次にpythonで書かれたツールを動かすための依存ライブラリをインストールします.このコマンドではSystem python以下に依存ライブラリが入ります (system pythonを使わずに済ます方法は無いものか……).
あとは諸々の環境変数を.envrcに書き込んでdirenvでそれらを有効にしてやる.

Hello-Worldを動かしてみる
$ cd "$ZEPHYR_BASE/samples/hello_world"
$ mkdir build && cd build
$ cmake -DESP_IDF_PATH="$ESP_IDF_PATH" -GNinja -DBOARD=esp32 ..
$ west flash \
   --esp-device="$ESP_DEVICE" \
   --esp-baud-rate="$ESP_BAUD_RATE" \
   --esp-flash-size="$ESP_FLASH_SIZE" \
   --esp-flash-freq="$ESP_FLASH_FREQ" \
   --esp-flash-mode="$ESP_FLASH_MODE"
$ screen $ESP_DEVICE $ESP_BAUD_RATE # and push reset button

以上のようにコマンドを叩くとプログラムのビルドとボードへの書き込みを行うことができます.
公式ドキュメントでは ESP_DEVICEESP_BAUD_RATE といった環境変数を設定しておくと ninja flash コマンドはそれらをリスペクトするように書いてあるが,実際にそのような処理が行われている気配はない (ざっとコードを読んだ) ので, ninja コマンドの代わりに west を利用し,そのコマンドラインオプションで各種設定を与えるようにしています.
あとはscreenでTTYにつないでボードのリセットボタンを押すと "Hello World: esp32" が表示されるはずです!


以上です.これでmacOS上でESP32向けのZephyrプログラムが開発できます.やりましたね.

Scrapboxのコンテンツエリアが広いと嬉しい!!!!

scrapbox.io
ユーザースクリプト機能で以下のCSSを読み込ませると……

.page-wrapper{
  width:100%;
  position: absolute;
  left: 0px;
}
.col-page-side{
  position: absolute;
  right: 0px;
}

f:id:moznion:20180601134210p:plain

嬉しい!!!!

expectコマンドを使ってシリアル接続で非対話的にコマンドを流し込む

シリアル接続でコマンドを実行する際は cu コマンドや screen コマンドなどでシリアルコンソールに接続して対話的にコマンドを実行することになると思うんですが,しかし「一定周期ごとにシリアルでコマンドを実行して結果を取得したい」みたいなときに対話モードだとだるいわけです.
というわけで expect の出番です.以下のように expect コマンドを利用すると非対話的にシリアルにコマンドを流し込むことができます. expect コマンドについてはこちらを参照されたい:
expect(1) - Linux man page

expect -c "
  spawn cu -l ${TTY} -s 9600
  send \"AT+CGREG=2\\n\"
  expect \"OK\"
  send \"AT+CGREG?\\n\"
  expect \"+CGREG:\"
  exit 0
"

これは

soracom.zendesk.com

にあるLACとCIDを取得するATコマンドの例ですが,こうしてやることで非対話的にATコマンドを流し込むことができます.

while :
do
  RESULT="$(expect -c "
    spawn cu -l ${TTY} -s 9600
    send \"AT+CGREG=2\\n\"
    expect \"OK\"
    send \"AT+CGREG?\\n\"
    expect \"+CGREG:\"
    exit 0
  " | grep '^+CGREG:')"

  PCRE_LAC_CID_PATTERN='"(.+?)",\s*"(.+?)"'
  LAC="$(echo "$RESULT" | perl -ale "/${PCRE_LAC_CID_PATTERN}/; print \$1")"
  CID="$(echo "$RESULT" | perl -ale "/${PCRE_LAC_CID_PATTERN}/; print \$2")"

  echo "{\"LAC\":$LAC, \"CID\":$CID}"

  sleep 60
done

例えばこうしてやると1分ごとにcell locationが取れて便利.expect コマンド最高!!!! (しかし難しい)

C言語のジェネリクスサポート

全く知らなかったのだけれど,C11の新機能として_Genericという組み込み関数が提供されていた.
Generic selection - cppreference.com


jameshfisher.com

というのをこのブログを見て気づいたんですが,

#include <stdio.h>
int main() {
  char* x = "foo";
  printf("Type of x is: %s\n", _Generic(x, char*: "string", int: "int"));
  return 0;
}

これは完全に動くコード (最初よく文章を読んでなくて,大方擬似コードだろうと侮っていたら本当に動いて驚いた).
だいたい分かると思うんですが,上記のコードは char*の引数が_Genericに渡されるとstringという文字列を出力し,intの引数が与えられるとintという文字列が出力されるという挙動をします.

しかしこれが「リッチなgenericsか」というとそうではなくて,_Genericの引数に延々と型とそれに対応するvalueを書いていかなければならないので,どちらかと言うとType Assertionに近いイメージ.JavagenericsC++のTemplateのようにコンパイル時に良い感じに自動解決してくれるような類のものではない.惜しいですね.とはいえ便利ではある.

問題としてはC11なんてモダンなものを使う環境ってあるの……? という部分であるが……

Code sampleとしては以下がわかりやすかった: http://www.robertgamble.net/2012/01/c11-generic-selections.html

CircleCIでgradleのテストを走らせているとプロセスが突然死するんですけどって時

CircleCI上でgradleでテストを走らせていると,プロセスが突然死してテストがコケるという事態に見舞われていました.
運が悪いと10連続でfailしたりするなどかなり厳しい状況だったのでCircleCI Supportに泣きついたところ,

  • 症状としてはOOMで死んでるっぽい (なぜかOOM debug logは出ていなかった)
  • gradleは環境変数 JVM_OPTS を尊重しない
  • 一方で _JAVA_OPTIONS 環境変数は優先するので,制限をかけたいときはこれを使うと良い

という術を教えてもらったので,それに従ったところテストが完全に安定しました.
diffとしてはたったのこれだけ!

@@ -7,7 +7,7 @@ 
     working_directory: ~/repo

     environment:
-      JVM_OPTS: "-Xms2048m -Xmx2048m"
+      _JAVA_OPTIONS: "-Xms2048m -Xmx2048m"
       TERM: dumb

     steps:

事象としてはgradleは JVM_OPTS を尊重しないため,JVM_OPTS にいくらヒープ容量の制限を書いてもそれが反映されず,結果的にgradleがメモリを無尽蔵のバカ食いして死んでしまったということのようです.
_JAVA_OPTIONSJVMに直接的にパラメータを食わせることができるので,それを用いるとgradleにもヒープ容量の制限を強く課すことができるようです.

本件についてはCircleCIのこのブログが詳しくて良いです: How to Handle Java OOM Errors - CircleCI
しかし効く環境変数と効かない環境変数があるのはなかなか難しいですね……