2016/09/21


以前から c-archive は作れたけど DLL にするとシンボルが被ったりして上手く DLL が作れなかった。

Big Sky :: golang の Windows 版が buildmode=c-archive をサポートした。

だいぶ時間が掛かった様ですが、ようやく buildmode=c-archive が Windows でも使える様になりました。 cmd/go: -buildmode=c-archive should ...

http://mattn.kaoriya.net/software/lang/go/20160405114638.htm
cmd/go: -buildmode=c-shared should work on windows · Issue #11058 · golang/go · GitHub
https://github.com/golang/go/issues/11058#issuecomment-248330515

-Wl,--allow-multiple-definition を知らなかった。

package main

import "C"

func main() {
}

//export HelloWorld
func HelloWorld() {
    println("Hello golang!")
}

まずこの様なソースコードを c-archive にして libxxx.a を作る。

go build -buildmode=c-archive -o libxxx.a xxx.go

次に HelloWorld をエントリにする def ファイルを作る。

LIBRARY   helloworld
EXPORTS
   HelloWorld

そして def ファイルを指定して DLL を生成する。この時 -Wl,--allow-multiple-definition を指定すること。

gcc -m64 -shared -o xxx.dll xxx.def libxxx.a -Wl,--allow-multiple-definition -static -lstdc++ -lwinmm -lntdll -lWs2_32

後は呼び出し元を作って...

package main

import "syscall"

var (
    lib        = syscall.NewLazyDLL("xxx.dll")
    helloworld = lib.NewProc("HelloWorld")
)

func main() {
    helloworld.Call()
}
Golang で DLL!

C言語からも

#include <windows.h>

typedef void (*pfn)(void);

int
main(int argc, char* argv[]) {
    pfn fn;
    HMODULE h = LoadLibrary("xxx.dll");
    fn = (pfn) GetProcAddress(h, "HelloWorld");
    fn();
    return 0;
}
Golang で DLL!

Golang のランタイムが FreeLibrary される事を想定していないので Vim の libcall からは直では呼び出せないけど libcallex-vim を使って解放なしに呼び出せば実行可能。ただしハンドルは持ちっぱなしにしないとリークする。

let xxx = libcallex#load("xxx.dll")
call xxx.call("HelloWorld", [], "")
"call xxx.free()

やったー!これで勝つる!

みんなのGo言語【現場で使える実践テクニック】 みんなのGo言語【現場で使える実践テクニック】
松木雅幸, mattn, 藤原俊一郎, 中島大一, 牧 大輔, 鈴木健太
技術評論社 / ¥ 2,138 (2016-09-09)
 
発送可能時間:在庫あり。


2016/09/10


「みんなのGo言語」が本日発売となりました。

みんなのGo言語【現場で使える実践テクニック】 みんなのGo言語【現場で使える実践テクニック】
松木雅幸, mattn, 藤原俊一郎, 中島大一, 牧 大輔, 鈴木健太
技術評論社 / ¥ 2,138 (2016-09-09)
 
発送可能時間:在庫あり。

現在、プログラミング言語のカテゴリで1位の様です。めでたい。

みんなのGo言語

僕も執筆に参加させて頂いたので興味ある方はぜひ読んで欲しいです。

さて最近の僕がGo言語で何を書いていたかというと
GitHub - mattn/go-slim: Slim Template Engine for golang

Rslim template engine for golang

https://github.com/mattn/go-slim

slim template の golang 版です。忙しくてまだ完成はしていないんですが、ちょっとした物であれば使えるレベルまで出来ていると思っています。haml を golang で実装している物はいくらかあったのですが、僕は haml よりも slim が好きなので何も迷わず実装しました。

doctype 5
html lang="ja"
  head
    meta charset="UTF-8"
    title
  body
    ul
    - for x in foo
      li = x

こんな slim のテンプレートを用意して

tmpl, err := slim.ParseFile("template.slim")
if err != nil {
    t.Fatal(err)
}
err = tmpl.Execute(os.Stdout, slim.Values{
    "foo": []string{"foo""bar""baz"},
})

テンプレートを読み込んで値を渡しながら Execute を呼び出します。すると

<!doctype html>
<html lang="ja">
  <head>
    <meta charset="UTF-8"/>
    <title>
    </title>
  </head>
  <body>
    <ul>
      <li>foo</li>
      <li>bar</li>
      <li>baz</li>
    </ul>
  </body>
</html>

綺麗な HTML が生成されます。slim 内での式にも対応していて for in 文だけでなく

= foo[0] + foo[1].Bar[0]

式も書けるし以下の様なコードに対して

:= make(map[string]string)
m["baz"= "Baz!"

type Baz struct {
    Fuga string
}
var buf bytes.Buffer
err = tmpl.Execute(&buf, Values{
    "foo"struct {
        Baz []Baz
    }{
        Baz: []Baz{
            {Fuga: "hello"},
            {Fuga: "world"},
            {Fuga: "golang"},
        },
    },
    "bar": m,
})

構造体や map を参照してテンプレートから扱う事も出来ます。

= bar.baz
- for x in foo.Baz
    p = x.Fuga

ruby を内部で実行する代わりに slim 用の専用のインタプリタ言語を作っています。はじめは anko を埋め込もうと思ったのですが高機能すぎるので文法の小さい物にしています。割と綺麗に書けました。おそらくですが他のテンプレートエンジンに使いまわしたり、「プログラミング言語の実装ちょっと興味ある」という方には良い教材になると思います。vm というフォルダだけ持っていけば他のプロダクトにも埋め込められるはずです。

面白いところでは for in 文の右辺に配列だけでなく channel を指定できる様にしてあります。

ch := make(chan string)
go func() {
    for _, a := range []string{"foo""bar""baz"} {
        ch <- a
    }
    close(ch)
}()

var buf bytes.Buffer
err = tmpl.Execute(&buf, Values{
    "foo": ch,
})
こんな風に chan をテンプレートに渡して
ul
  - for x in foo
    li = x
slim から呼び出すと
<ul>
  <li>foo</li>
  <li>bar</li>
  <li>baz</li>
</ul>

ループを回してくれます。実用的かどうかなんて関係ないです。面白ければいいんです。

まだやりかけというだけあって slim から使える関数は trim, to_upper, to_lower, split 程度しかありませんが、適当に時間を見つけて作って行きたいと思います。


2016/08/24


Bash on Ubuntu on Windows (以下 BoW) は Windows コマンドプロンプトを使って実行される。今までであれば Windows のコマンドプロンプトはエスケープシーケンスを認識しなかったので cmd.exe そのものでは vt100 等の端末環境を用意できませんでした。しかし Windows10 の cmd.exe では新しいコンソールモードが用意されています。

Console Virtual Terminal Sequences (Windows)

Virtual terminal sequences are control character sequences that can control cursor movement, color/font mode, and other operations.

https://msdn.microsoft.com/ja-jp/library/windows/desktop/mt638032(v=vs.85).aspx

SetConsoleMode の第二引数に ENABLE_VIRTUAL_TERMINAL_PROCESSING を指定するとエスケープシーケンスを認識する様になります。Windows10 が当たり前の世の中になってきたら、ようやく Windows でエスケープシーケンスを使った CLI を実装できる様になります。それまで我慢できない人は ansicolor-w32.c を使うと良いです。

BoW ではこの新しい API のおかげでコマンドプロンプトを使って Linux の CLI を表現できている訳ですが、いかんせん完全ではありません。日本語を表示すると幅が合わず描画崩れを起こします。

BoW

これを見て「あぁ、msys2 は素晴らしい mintty は素晴らしい」と思う方も多いと思います。もしくはこれを見て BoW を使うのをやめる人もいるかもしれません。ただちょっとまって下さいよ。もし mintty の中で BoW が動いたら、しかも ssh 超しに使ったり Visual Studio Code の端末機能を使うといったナンチャッテハックをやる必要無かったとしたら、BoW 超便利だと思いませんか?以下でその方法をお教えします。まずこれからお伝えする環境を作る為にコンパイラが必要です。msys2 をお持ちならばそれをそのまま使って下さい。次に以下のリポジトリを git clone します。

GitHub - rprichard/wslbridge: Bridge from Cygwin to WSL pty/pipe I/O

wslbridge is a Cygwin program that allows connecting to the WSL command-line environment over TCP sockets, as with ssh, but without the overhead of configuring an SSH server.

https://github.com/rprichard/wslbridge

ビルドは mingw ではなく msys2 側でビルドして下さい。frontend を msys2 でビルドし、backend を BoW の中でビルドします。wslbridge は BoW の bash を起動すると共に backend を起動させてソケットを張り BoW の標準入出力とのパイプを作成します。そのパイプを msys2 の pty と繋げる事で msys2 レイヤと BoW を結び付け、如何にも msys2 の上で BoW が動いている様に見せるプログラムです。この wslbridge を mintty から起動すれば mintty の中で BoW が起動する、という仕組みです。mintty から wslbridge を起動するには以下の様に引数を指定します。

mintty -t "Bash on Ubuntu on Windows" -e c:/dev/wslbridge/out/wslbridge.exe

これをショートカットファイル等に指定すれば

BoW

すばらしいですね。かっこいいですね。画面崩れもありませんね。ちなみに以下の設定を行うと、エクスプローラのフォルダの右クリックから「Bash on Ubuntu on Windows」が選べる様になり、更に便利になります。

まずレジストリエディタを起動し、以下のキーを作成します。

HKEY_CLASSES_ROOT\Folder\shell\bow

右のペーンで「(規定)」の値に「Bash on Ubuntu on Windows」を入力します。

BoW

次にさらに下の階層に以下のキーを作ります。

HKEY_CLASSES_ROOT\Folder\shell\bow\command
「(規定)」に以下を設定します。
c:\msys64\usr\bin\mintty.exe -t "Bash on Ubuntu on Windows" -e c:/dev/wslbridge/out/wslbridge.exe

wslbridge へのパスは環境に合わせて設定して下さい。これでフォルダを右クリックして「Bash on Ubuntu on Windows」が表示される様になり、選択すると mintty が起動する様になります。

BoW