Ubuntu 12.04.3 LTS に Katana(OwinHost.exe) をインストールする

OwinHost.exe - Katana

C#+Owinのコード書きたい。でも、IISじゃなくてLinuxでホストしたいよっていうヒネクレな状況で、とりあえず、Katanaが動かないと話にならないので、Katanaを動かしてみたっていうのが趣旨です。

Mono 3.X

とりあえず、新しいバージョンのMonoじゃないとOwinHostが動かないと思うんだけど、Ubuntu 12.04.3には、mono 2.Xのmonoしか入ってない。というか、gmcsな時点でアウト。
apt-cacheでパッケージを確認した感じではやっぱり、C# 3.0までしか対応してない。悲しい。

espresso3389@nu:~/katana$ apt-cache show mono-gmcs
Package: mono-gmcs
Priority: optional
Section: cli-mono
Installed-Size: 1178
Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
Original-Maintainer: Debian Mono Group <pkg-mono-group@lists.alioth.debian.org>
Architecture: all
Source: mono
Version: 2.10.8.1-1ubuntu2.2
Replaces: mono-devel (<< 2.4.2.3), mono-mcs (>= 1.1.10), mono-mcs (<= 1.1.13)
Depends: mono-runtime (>= 1.1.8.1), libc6 (>= 2.15) | libc6.1 (>= 2.15) | libc0.1 (>= 2.15), libmono-corlib2.0-cil (>= 2.10.8.1), libmono-system2.0-cil (>= 2.10.3), libmono-corlib2.0-cil (<< 2.10.8.2)
Recommends: pkg-config
Filename: pool/main/m/mono/mono-gmcs_2.10.8.1-1ubuntu2.2_all.deb
Size: 422312
MD5sum: ef7aed6da981daf02fb910147b4b105e
SHA1: 1de52356da26cb060cebd78c24849f4b356bd8db
SHA256: 3164fdd32843326be71ad8e34f0d780b55c10b77cd74d919fff746dc0d5a412b
Description-en: Mono C# 2.0 and C# 3.0 compiler for CLI 2.0
 This is the Mono C# (C-Sharp) 2.0 and C# 3.0 compiler, a platform-independent
 compiler which produces CIL (Common Intermediate Language) binary executables.
 The gmcs compiler supports two different featuresets (C# versions).
....

directhex/monoxide

ソースからビルドするほど若くないので、そうじゃない方法を探したところ、 directhex さんという人が directhex/monoxide という吸ったら死にそうな感じの危険なパッケージ群をホストしてくれているらしい。

Installing Mono 3.x (3.0.x and/or 3.2.x) - stackoverflow

stackoverflowからそのまま引用すると、やることは簡単で、

espresso3389@nu:~/katana$ sudo add-apt-repository ppa:directhex/monoxide

とやって、このレポジトリを追加するだけ。ただし、この見慣れない、 add-apt-repository というツールは、標準では入ってないらしい。sudoなしでタイプしてみたところ、

espresso3389@nu:~/katana$ add-apt-repository
The program 'add-apt-repository' is currently not installed.  You can install it by typing:
sudo apt-get install python-software-properties

と出てきたので、

espresso3389@nu:~/katana$ sudo apt-get install python-software-properties

してインストールした。もちろん、この後に apt-get update しないと反映されない。ここまでをまとめると、詰まるところ、次の作業をやれと言うこと:

espresso3389@nu:~/katana$ sudo apt-get install python-software-properties
espresso3389@nu:~/katana$ sudo add-apt-repository ppa:directhex/monoxide
espresso3389@nu:~/katana$ sudo apt-get update

ということで、本ちゃんのインストール。フルセットをインストールするために、mono-completeをインストールする。

espresso3389@nu:~/katana$ sudo apt-get install mono-complete

インストールが終わったら、monoのバージョンを確認しておこう。

espresso3389@nu:~/katana$ mcs --version
Mono C# compiler version 3.2.1.0

OK。C# 5.0対応(async/await対応)が入っているバージョンだ。これで好き放題出来る。

NuGet を取得する

NuGetとは、.NET向けのパッケージマネージャ。これを使えば、欲しいモジュールを自由自在にインストールできる。

Installing NuGet にある Direct Downloadから、nuget.exeを入手する。

espresso3389@nu:~/katana$ wget http://nuget.org/nuget.exe

そして、どうやら、chmodで実行権限を付けておけば、普通に実行ファイルとして実行できる模様なので、そうしておく(mono経由で実行しなくて良いみたい)。

espresso3389@nu:~/katana$ chmod +x nuget.exe

バージョンを確認してみたところ、今のところ、

espresso3389@nu:~/katana$ ./nuget.exe
NuGet Version: 2.7.41115.310
usage: NuGet <command> [args] [options]
Type 'NuGet help <command>' for help on a specific command.
...

と表示された。

NuGetのためにSSL証明書をインストールする

How to Use NuGet on Mono, Part I - Half-Blood Programmerにあるように、SSLの証明書をインストールしないと、NuGetがパッケージをダウンロードできないので、そうする。

espresso3389@nu:~/katana$ mozroots --import --sync

Katana (OwinHost)のインストール

ソースからビルドすべきかとも考えたが、やっぱり面倒なので、NuGetでさっくり行きます。-Version 2.0.2は省略しても良いが、その場合、ここの例とはバージョンが変わる可能性がある。

espresso3389@nu:~/katana$ ./nuget.exe install OwinHost -Version 2.0.2 -pre

そうすると、 ./OwinHost.2.0.2/tools/ に

espresso3389@nu:~/katana$ find OwinHost.2.0.2/
OwinHost.2.0.2/
OwinHost.2.0.2/OwinHost.2.0.2.nupkg
OwinHost.2.0.2/tools
OwinHost.2.0.2/tools/Microsoft.Owin.dll
OwinHost.2.0.2/tools/OwinHost.exe.config
OwinHost.2.0.2/tools/install.ps1
OwinHost.2.0.2/tools/OwinHost.exe
OwinHost.2.0.2/tools/Owin.dll
OwinHost.2.0.2/tools/uninstall.ps1
OwinHost.2.0.2/tools/Microsoft.Owin.Host.HttpListener.dll
OwinHost.2.0.2/tools/Microsoft.Owin.Hosting.dll

OwinHost.exe も chmod +xしておいた方がイライラが少なくなるかも知れない。

espresso3389@nu:~/katana$ chmod +x OwinHost.2.0.2/tools/OwinHost.exe

実験

さて、せっかくなので、Linux上で何らかのサンプルをビルドしてみましょう。
@miso_soup3さんのブログにKatana で Hello, worldというエントリがあるので、そのコードを拝借。

下記のコードを Startup.cs として保存:

using System;
using System.Threading.Tasks;
using Microsoft.Owin;
using Owin;

[assembly: OwinStartup(typeof(WebApplication40.Startup1))]

namespace WebApplication40
{
  public class Startup1
  {
    public void Configuration(IAppBuilder app)
    {
      app.Run(context =>
      {
        context.Response.ContentType = "text/plain";
        return context.Response.WriteAsync("Hello World!");
      });
    }
  }
}

web.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <appSettings>
    <add key="owin:AppStartup" value="WebApplication40.Startup1, Startup"/>
  </appSettings>
</configuration>

OwinHost.exeは、 binディレクトリ下のアセンブリしか読み込まないような仕様になっているらしいので、先にbinディレクトリを作成する。

espresso3389@nu:~/katana$ mkdir bin

そして、Startup.csをmcsでDLL(library)としてビルドしてbinに配置するには、次のようにする(デバッグ情報有り/最適化なし):

espresso3389@nu:~/katana$ mcs -debug -optimize- -target:library -lib:./OwinHost.2.0.2/tools/ -r:Microsoft.Owin.dll -r:Owin.dll Startup.cs -out:bin/Startup.dll 

今のディレクトリ構造はこの作業の通りになっていれば、

espresso3389@nu:~/katana$ find .
.
./nuget.exe
./Startup.cs
./web.config
./OwinHost.2.0.2
./OwinHost.2.0.2/tools
./OwinHost.2.0.2/tools/Microsoft.Owin.dll
./OwinHost.2.0.2/tools/OwinHost.exe.config
./OwinHost.2.0.2/tools/install.ps1
./OwinHost.2.0.2/tools/OwinHost.exe
./OwinHost.2.0.2/tools/Owin.dll
./OwinHost.2.0.2/tools/uninstall.ps1
./OwinHost.2.0.2/tools/Microsoft.Owin.Host.HttpListener.dll
./OwinHost.2.0.2/tools/Microsoft.Owin.Hosting.dll
./OwinHost.2.0.2/OwinHost.2.0.2.nupkg
./OwinHost.2.0.2/ReadMe.txt

./bin
./bin/Startup.dll
./bin/Startup.dll.mdb

となっているはず。この状態で、OwinHost.exeを実行する:

espresso3389@nu:~/katana$  ./OwinHost.2.0.2/tools/OwinHost.exe
Starting with the default port: 5000 (http://localhost:5000/)
Started successfully
Press Enter to exit

何度も言うが、このマシンはヘッドレスなので、ブラウザは動かない。従って、面倒だが、lynxなどで http://localhost:5000 を参照するか、あるいは、OwinHost.exeを起動するときに、-uオプションで、明示的に公開URLを指定する必要がある。

次の例は、 http://192.168.0.18:8080/ で公開する場合:

espresso3389@nu:~/katana$  ./OwinHost.2.0.2/tools/OwinHost.exe -u http://192.168.0.18:8080/

ここにアクセスすれば、

Hello World!

と表示されるはず。

[追記] monodoc-browser のインストール処理中にエラーが発生する

処理中に、次のようなエラーが発生する。

...
Setting up monodoc-browser (2.10-2) ...
generating monodoc index...
grep: /etc/gre.d/*.conf: No such file or directory

Unhandled Exception:
System.TypeLoadException: Could not load type 'Monodoc.EditMerger' from assembly 'monodoc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756'.
[ERROR] FATAL UNHANDLED EXCEPTION: System.TypeLoadException: Could not load type 'Monodoc.EditMerger' from assembly 'monodoc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756'.
dpkg: error processing monodoc-browser (--configure):
 subprocess installed post-installation script returned error exit status 1
Errors were encountered while processing:
 monodoc-browser
E: Sub-process /usr/bin/dpkg returned an error code (1)
...

これは、mono-completeをインストールしている性で、monodoc-browserなんていうどうでも良い物がインストールされようとしていて、エラーになっているだけなので、素直に、monodoc-browserだけを消せば良い。

espresso3389@nu:~/katana$ sudo apt-get remove monodoc-browser

[追記] binfmt_misc

monoのバイナリというか、普通のWindowsのexeに実行権限さえ付ければ動作しちゃうのは、binfmt_miscというカーネルのモジュールが有効になっているからです。

日本語のドキュメントだと、「binfmt_misc.txt - いろんな (お好みの) バイナリフォーマットのカーネルサポート v1.1」かな。

要は、ファイルのシグニチャ(ヘッダ)を見て、そのファイルをハンドルするプログラムを特定する仕組みですが、MZっていうシグニチャでPEフォーマットなことは分かるけど、それって、Wineで動かせば良いの?それとも、Mono?みたいな話に関しては、適当なシェルスクリプト等に投げて、そこで、fileコマンドなどを使ってフォーマット判別すると言うことのようです。

とりあえず、/usr/sbin/update-binfmts というコマンドがあって、エントリを見たかったら、--displayというオプションを与えれば良い感じ。

espresso3389@nu:~$ /usr/sbin/update-binfmts --display
llvm-3.0.binfmt (enabled):
     package = llvm
        type = magic
      offset = 0
       magic = BC
        mask =
 interpreter = /usr/bin/lli-3.0
    detector =
python2.7 (enabled):
     package = python2.7
        type = magic
      offset = 0
       magic = \x03\xf3\x0d\x0a
        mask =
 interpreter = /usr/bin/python2.7
    detector =
cli (enabled):
     package = mono-runtime
        type = magic
      offset = 0
       magic = MZ
        mask =
 interpreter = /usr/bin/cli
    detector = /usr/lib/cli/binfmt-detector-cli

おぉ、llvmの中間コードが動くっぽいな。楽しい。