Go言語におけるSignalのハンドリングについて
golangには、Linuxのシグナルをハンドリングする機能がある。
具体的には、os/signal
パッケージ/syscall
パッケージとchannel機能を使う。
func main() { sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, syscall.SIGTERM, syscall.SIGINT) switch(sigCh) { case syscall.SIGTERM: fmt.Println("SIGTERM") case syscall.SIGINT: fmt.Println("SIGINT") } }
ただし、signalはlinux/unixにおける概念であり、WindowsでのCTRL^C
はキャッチできない。パッケージもsyscall
だし。
Windowsに対応させるには、os.Interrupt
を使う
(参考:signal - The Go Programming Language)
func main() { sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, syscall.SIGTERM, syscall.SIGINT, os.Intterupt) switch(sigCh) { case syscall.SIGTERM: fmt.Println("SIGTERM") case syscall.SIGINT: fmt.Println("SIGINT") case os.Interrupt: fmt.Println("Interrupt") } }
これで対象のシグナルが発生すると、sigChにメッセージが通知されるようになる。 なお、sigChはchannelのため、読み出しを行うと、メッセージングされるまでブロッキングが発生する。
通常は何かを実行中のバックグランドでキャッチしたいはずなので、goroutineにする。ただし、goroutineはGCされないため、終了しないとリークしてしまうらしい(Go言語(Golang) はまりどころと解決策)。
なので、別途チャンネルを使って停止できるようにする。
このとき、ちゃんとgoroutineが終了するように、sync.WaitGroup
を使って同期を行う
package main import ( "fmt" "os" "os/signal" "syscall" "sync" ) func main() { wg := sync.WaitGroup{} doneCh := make(chan struct{}, 1) wg.Add(1) go func() { defer wg.Done() sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, syscall.SIGTERM, syscall.SIGINT, os.Intterupt) defer signal.Stop(sigCh) for { select { case sig := <-sigCh: switch(sig) { case syscall.SIGTERM: fmt.Println("SIGTERM") case syscall.SIGINT: fmt.Println("SIGINT") case os.Interrupt: fmt.Println("Interrupt") } case <-doneCh: fmt.Println("Done") return } } }() // 何かする // 終了 doneCh <- struct{}{} wg.Wait() }
context.Context
と組み合わせれば、CTRL^C
を受けてキャンセルを行ったりできる。
ただその場合、生成されたcontext.CancelFunc()
を、シグナルハンドラのgoroutineで直接呼び出すか、一層channelをはさむべきなのか、今一つ判断がつかない。
msys2のpacmanがupdate-core不要に
MSYS2のバージョンを上げたところ、update-coreコマンドがなくなっていた。
どうやら、5.0.1.6403以降のpacmanはupdate-coreがなくなり、
pacman -Syu
を行うと、coreアップデートがあれば、update-coreと同等の挙動をしてくれるようになったようだ。
Visual Studio Codeはmsys2版gitに対応していない
Visual Studio Codeはgitと連携可能で、 Preferenceで、gitのパスを指定して利用します。
ですが、msys2版のgitのパスを指定しても正常に動作しません。
以下のissueによって知ったのですが、msys2のgitは、
git rev-parse --show-toplevel
を実行すると、/から始まるPOSIXシェル環境のパスを返します。
vscodeは当然Windows空間であるため、正常に動作しない、ということのようです。
$ cd ~/dotfiles $ git rev-parse --show-toplevel /home/User/dotfiles
というわけで、Chocolateyでportable版のgitを導入してvscodeに渡すようにしました。 PATHを通さないかぎり競合はしないでしょう。
choco install git.commandline
chocolateyではportable版は%ChocolateyInstall%\lib\(package name)
にインストロールされるようです。
{ // Path to the git executable "git.path": "%ChocolateyInstall%\\lib\\git.commandline\\tools\\bin\\git.exe" }
Cygwinにgit ver 1.8.3.4を導入する
Cygwinに付属するgitのバージョンは1.7.9。 理由不明ですが、長いことアップデートされていません。
一方、Cygwin Portsでは、現在version .1.8.3.4のgitがサポートされています。
別に古いバージョンでも良いっちゃ良いのですが、git difftoolの--dir-diff
という超絶便利機能が、ver 1.7.11からしかサポートされていないという
悲しい状況のため、これを導入します。
git ver.1.8.3.4の導入
setup.exeを-Kオプション付きで起動します。setup.exeのミラーサイトを追加するオプションの模様
cygstart -- /path/to/setup-x86.exe -K http://cygwinports.org/ports.gpg
いつものようにポチポチと勧め、Choose Download Site(s)まで来たら、User URL欄に、ftp://ftp.cygwinports.org/pub/cygwinports
を入力してAddします。
これで、導入できるパッケージ一覧にCygwin Portsの内容が反映されるようになります。
git-completionの導入
同様に、git-completionも導入できます。
どうも、ver 1.7.9以降で、__git_ps1を表示させるためのシェルスクリプトが、git-prompt.sh
という形式で分割されたようです。
が、何故かこのファイルが含まれていません。仕方がないので、gitのソースから取り出しましょう。git ver1.8.3.4を、sourceにもチェックを入れて導入し直します。
/usr/src/git-1.8.3.4.tar.gz
に、ソースコードがインストールされるため、これを解答し、インストールします。
tar zxvf /usr/src/git-1.8.3.4.tar.gz cp git-1.8.3.4/contrib/completion/git-prompt.sh /etc/bash_completion.d/
contribには、他にも色々な機能が詰まっているみたいなので、導入してみるのもいいかもしれません。
__git_ps1用の修正
.bashrc or .bash_profileを新環境に合わせて修正します。
git difftool --dir-diffをWinmergeに対応させる
git difftoolにWinmergeを利用するようにします。
以下のようなスクリプトを何処か(~/bin/等)にインストールし、.gitconfigで、スクリプトを呼び出すように修正します。
これでgit difftool --dir-diffが使えるようになりました。
参考文献
PlantUML用のマクロ作りました
テキストからUMLを自動生成するPlantUMLというツールが有ります
テキストベースなので、既存コードを解析しながらシーケンス図を起こす時など、コピペで作れるのでとても便利なのですが (開発時の資料が殆ど残っていないファッキンな環境なのですよ)、 PlantUMLのシーケンス図は、ちょっと凝ったことをしようとすると、とても見通しが悪くなってしまい困りものです。
具体的には、LifeLineの活性/非活性をコマメに行おうとすると、「あれ、今何段目だったっけ」みたいなことが頻繁に起こります。
なので、見通しの良くなるような定義ファイルを作りました。
リポジトリ
使い方
上記リポジトリをcloneしてきて、
作成するUMLと同じフォルダ内に定義ファイルを置く
plantumlのinclude pathを通す
java -Dplantuml.include.path="path/to/repo" -jar plantuml.jar test.txt
どちらかの方法で導入し、
上記のように、プログラム言語っぽく記述すると、以下の様なシーケンス図が生成されます。
簡単な解説
するほどのものでもないですが。。。
利用しているのは、以下の2つのファイルです。2つ目はただのskin定義なので、直接は関係ありません。
- sequence_macro.h … マクロの定義
- sequence_skin.h … 見た目を変更するファイル
UMLファイルの行頭で、includeします。
!include "sequence_macro.h"
定義は現状、同期呼び出し用のものだけです。
func(送信元, 送信先, メッセージ)
送信元
から、送信先
にメッセージ
を送信し、一時的に送信先を活性状態にします。
func_begin(送信元, 送信先, メッセージ)
func_end(送信元, 送信先)
送信元
から、送信先
にメッセージ
を送信し、送信先を活性状態にします。
送信先が非活性状態になるまで、別途操作が必要な場合に利用します。
func_end()
で非活性状態になります。
local_func(オブジェクト, メッセージ)
自分自身にメッセージを送ります。
local_func_begin(送信元, 送信先, メッセージ)
local_func_end(送信元, 送信先)
自分自身にメッセージを送ります。非活性状態になるまで、別途操作が必要な場合に利用します。
終わりに
利用する事により、大分スッキリさせることが出来ました。 が、ヤリクチが微妙に頭悪い感じなんで、もっといい方法があれば誰か教えてください
CppUTestを使うGitHub上のリポジトリをTravis CIと連携させる方法
折角テスティングフレームワークを使っているのだから、CIツールも利用すべき、ということで、調べました。
前準備 : CppUTestを利用したリポジトリをGitHubに公開する
基本的には、ここで記載した内容を、make
一発で出来るようにすればOKです。
私の使ってるリポジトリでは、以下のようにしています。
- CppUTestは、リポジトリに含まずsubmodule化する
MakefileWorker.mk
を使いテスト用の実行ファイルを作成するMakefileは専用に切り出す(ex. MakefileCppUTest.mk )- プロジェクト自体のMakefileで、CppUTestのビルドを行ったあと、上記Makefileを呼び出すようにする(ex. Makefile )
1. Travis CIと連携する
ブラウザから、GitHubとTravis CI間で連携するための設定を行います。
私はこちらを参考にしました。非常にわかりやすいです。
2. Travis CI用の設定ファイルを書く
リポジトリ直下に.travis.yml
という名前の設定ファイルを設置します。
…素っ気ない設定ファイルですね。
Travis CIはテストの成否をscript
に書かれたコマンドの終了ステータスで判断しているようです。(0
だと成功、それ以外だと失敗)
CppUTestのMakefileWorker.mk
はビルドと共にテストを実行してくれるので、これで十分のようです。
この状態で、commitをpushすると、Travis CIがhockしてビルドを実行してくれます。
CppUTestをインストールせずに利用する方法
CppUTestがver. 3.4になって、automakeによるインストールを行う用に修正された関係で、ver. 3.3以前とは、インストールせずに使う方法が変わっていました。
前提
ここで、CppUTestをシステムにインストールせず利用する、というのは、 libCppUTest.a
及び libCppUTestExt.a
を自前でビルドし、 MakefileWorker.mk
を利用して、テストプログラムにリンクさせるという方法です。
もしかしたら他にやり方あるのかもしれませんが(automake利用するとか)、浅学にして存じません。何方か知っていたら教えたください。
cpputestライブラリのビルド
Makefile_using_MakefileWorker
というファイルを利用してビルドします。
$ git://github.com/cpputest/cpputest.git $ cd cpputest $ make -f Makefile_using_MakefileWorker
CppUMockを利用する場合は、libCppUTestExt.a
のビルドが必要です
$ make -f Makefile_using_MakefileWorker extensions
ver. 3.3以前は、リポジトリルートでmakeすれば良かったのですが、前述のとおり、automakeの関係で、ビルド方法が変わりました。
プロダクトコード&テストコードとのリンク
CppUTesetは、MakefileWorker.mk
を使うと簡単にテスト用の実行ファイルを作成することができます。
以下の様な感じでMakefileを記述し、MakefileWorker.mk
をインポートしてあげるとビルドから実行までやってくれます。
実際にテストコードを書く方法等は、CppUTestのマニュアルを参照したください。
注意点など
PROJECT_HOME_DIRの注意点
厳密に言うと、SRC_DIRS
TEST_SRC_DIRS
MOCKS_SRC_DIRS
についての注意点です。
Makefileworker.mk
を利用すると、ビルド時に、オブジェクトファイルがobjs
以下に作成されますが、このとき、実際に生成される場所が
objs/$(SRC_DIRS)
objs/$(TEST_SRC_DIRS)
objs/$(MOCKS_SRC_DIRS)
になります。ここで、PROJECT_HOME_DIR
に..
を指定してしまうと、プロダクトコードのオブジェクトが、例えば objs/../src/hoge.o
に展開されてしまいます。
最悪、プロダクトコードと混じってしまうので、.
を利用するのが無難でしょう。
プロダクトコードのライブラリはテスト用
Makefileworker.mk
を利用すると、プロダクトコードを固めて lib(COMPONENT_NAME).a
という静的ライブラリにビルドしてくれますが、これはそのままでは利用できません。
以前書きましたが、CppUTestでは、メモリリークを検出するために、new()
delete()
等をマクロを用いて展開しているため、
CppUTestライブラリを利用しないとリンク時エラーになってしまいます。
リリース用のプロダクトコードは、別途makefileを記載する必要があります。
終わりに
もしかして、Makefileworker.mk
はもうメンテされていない?