appveyorで、.NET Core を試してみた

ご無沙汰しております。
Blog存在半分忘れかけておりました。
書きたくないわけでもなく、書きたい気持ちはありつつもなかなかタイミングがなかったというところで、実は、取り組んでいるものに節目がうまく出ていないのも一つかなというところです。
その場その場のことを書いていけるようにしていけたらいいのかなとか考えているところです。

というところで、本題です。

経緯

実は、.NET Coreでの開発をしていて、ある程度の規模になってきたときテストコードを自分のところで実行したり、dotnet packでnupkgを作るということをやりたいなと。ただ、継続的インテグレーションを構成したいと考えてましたところ、6月よりちょっと前だと、対応したと目立った情報もなく結構厳しいのかなと思ってました。毎回、手動で実行していければいいのですが、忘れたりまた、テストの結果が見えるようにできていないというのも、何か不透明な感じで面白くないなーと。
最近、@jsakamotoさんより、「appveyor」は「dotnetコマンドも使えるかも!?」と教えていただきまして、試してみました。

結論

使えたー!

設定したこと

既定ではslnファイルを読み込んでうまいことやろうとしてくれますが、
これだとテストも自動的に含まれてしまったり、うまく動作しなかったりとしたために、dotnetコマンドに置き換えました。

最初に、github上からAppVeyorのプロジェクトを作成しました。

  1. 左側リストのGeneral.プロジェクト名の設定 – ProjectName
    Bjd.xxxx
  2. 左側リストのGeneral.ビルドバージョンの設定 – Build version format
    1.0.{build} → 1.0.0-{build}
    ※気持ちの問題
  3. 左側リストのEnviromentは特になし
  4. 左側リストのBuildで、MSBUILDとなっているものをCMDに変更
    dotnet restore
    
    dotnet build Bjd.xxxx -c Release

    としました。

  5. 左側リストのTestで、「Script」「CMD」を選び、Buildと同じようにdotnetコマンドをdotnet test Bjd.xxxx -parallel all -maxthreads 3としました。オプション「-parallel all -maxthreads 3」はXunitに対するオプションです。並列化を有効にして最大スレッド数を設定してますが、設定しなくても動きます。BlackJumboDogでは、テスト実行にもSocket操作・サーバー動作を伴うとか、テスト実行時間が長くなりすぎないようにと考えたすえ、最大数を調整しました。
  6. Badgeの設定
    といっても、リンクをどこかに張り付けるだけです。私はGitHubのreadme.mdにはっつけてみました。
 
 なんか、それっぽい!という気持ちと、いまだにBjd.WebServer.CoreCLR.Testがpassingになったことはない!という現状を報告しておきます。

そのほか

これは、おどろいたことです。わかる人にはわかるとは思いますが、実はpathにperl.exeがいるらしく、cgiのテストが動作しました。
テストのタイムアウトは60分。普通のテストではそうそう、問題にならないような気がしますが、開発機のスペックと同等だと思わないほうがよいかと思います。テストを並列実行してる場合、コア数の違いで、実行時間がそのまま倍になったりしました。体感的に手持ちのSurfacePro4と同じくらいでした。
Slackへの通知もできます。ビルドの結果くらい通知しておくと、Commitの重みを感じつつ励みになります。(passingなら)
また、余談ですが、サーバーのスレッドが停止しないためにテストが終わらずに60分のタイムアウトを経験できたことは伏せておきます。
開発する環境以外での動作は思ったより大変な部分もありましたが、GitHubにプッシュするだけで、自動的にビルド、テストと動く環境があることで、リポジトリの状態を見えるようにできたことは、ぱっと見るだけで、状況を把握できて面白いかなーと思った次第です。

感謝

@jsakamotoさん、情報ありがとうございます!

新しいdotnet-core(RC2)を味見したい人に!

おひさしぶりです。気付けば、1か月更新してませんでした。

今日は、de:codeに向けて「.NET Core」を「予習」「味見」したい人に向けて、私なりにVisualStudioで、利用可能になる方法を紹介します。

大まかにこんな感じ

0.事前準備.VisualStudio 2015 Pro程度ものが入った環境を用意する(たぶんcommunityでもいけるとは思う)

1.NuGetのリポジトリを追加します。

2.プロジェクトを用意します。

3.project.jsonを書き換えます

4.随時動かす!

 

NuGetのリポジトリを追加

  1. 開発のビルドリソース – dotnet-core .NET Core “dev” builds
  2. リリース向けのリソース – dotnet-core-rel Feed for the dotnet-core packages that we plan to release and publish to nuget.org

 

名前は適当に、ソースのURLは、上記リンク先から下記の部分を利用します

※VS2015は上。

プロジェクトを用意します

新しいプロジェクトの作成ですね。

何かかきます。とりあえずコンソールなので、、、

グレイトなメッセージを出力!テンションあがるねっ

 

project.jsonを書き換え

dnxのみにして、しまいます。こうすると.NETFrameworkでは動作しなくなる。

Devはちょっと修羅なので、リリース候補のrc2にしてみました。

保存で、パッケージが復元されます。あとは、実行!

れこそグレートなメッセージ!!!・・・化けたよ。

こんなときは、慌てずに中身を覗いてみましょう。

            Console.WriteLine(Console.Out.Encoding.WebName);

というわけで、エンコードがutf-8となっているために、コンソールはShiftjis。

なので化けたのだということですね。

そこでproject.jsonに以下を追加

        "System.Text.Encoding.CodePages": "4.0.1-beta-23516"

さらに、メインも以下のように

        public static void Main(string[] args)
        {
            Console.WriteLine(Console.Out.Encoding.WebName);

            // 標準出力の取得
            var s = Console.OpenStandardOutput();

            // コードページの取得
            var e = System.Text.CodePagesEncodingProvider.Instance.GetEncoding(932);

            // エンコード付きのストリーム
            var w = new System.IO.StreamWriter(s, e);

            // これを忘れると・・・すぐに出力されなくなる
            w.AutoFlush = true;

            // 設定
            Console.SetOut(w);

            Console.WriteLine(Console.Out.Encoding.WebName);
            Console.WriteLine("てすとめっせーじ");
            Console.Read();
        }

実行!

となりました!

とはいえ、この問題は公式なNuGetから落とせるものでも発生しますので、特別RC2がというわけではありません。

試行錯誤しながら、メンバーを眺めてみたりとすることもできるということで

興味のあるかたはぜひぜひ

それでは!

 

CoreCLRなBlackJumboDogにするとき仕組みを変更した理由と言訳 動的読み込みから組み立てに

前日からの続きです。日本各地で雪もすごい中、こんながあったりと。個人的にこれ賛成です。ASP.NET5って名称いつまでたっても、.NetFrameworkが5にならないか、いきなり6にいくかとかそういう偶数と奇数みたいなことでもしないとバージョン明記だけは無理があると思ってました。ただまぁ、次世代をいく、置き換えていくっていう願いが込められていたのではないかと思ったりもします。そこで、.Net Coreと同じようにCoreってつけたのいいと思いましたよ!名前と仕組みも一致した感じで。

.Net Core

以前、BlackJumboDogをはじめたところの記事でも書いていますが、GACとかってないんですね。基本すべて、NuGetからダウンロードなんですね。NuGetの参照って、名前とバージョンのみなんですが、これの実体となるDLLは、プラットフォームによって異なることが許されている。つまりDLLは動く環境によって変わる。言い方を変えると、DLLを参照するんじゃなくて、この名前を与えられているパッケージ(バージョンも指定可能)を必要としますっていう感じなんですよね。そうするとプラットフォーム用の実体が自動的に選ばれますよって。何を言いたいかというと、そもそも、実体のDLLに依存したものっていうのが静的(コンパイル)にはできない。また、参照として追加したいなら、DLLの形では難しいということなんですね。今はプロジェクトを参照する形で実現しているのが最初その違いに気づくのに時間がかかりました。NuGetだったので、ASP.NETみたいに、どっかにDLLあってみてるんだろーくらいに思ってたんですね。実行しているときはそうなるわけですが、コンパイル時には見てないってことをちゃんと理解していなかった。。。DLLとかライブラリ作ったならどこかで組み立てるプロジェクトがいないと話にならないなぁって思ったんですね。そこで、Bjd本体と実行イメージを作るプロジェクトを分けてみた(Bjd.Common.CoreCLRとBjd.CoreCLR)という結論を出してみました。これは、将来、BjdがNuGetに上がったりしたときに、利用する側がASPNETホストであるkestrelを動かすのと同じ感覚になることをイメージしてます。

そして、プラグインとしてDLLをReflectionを用いて拾い上げてプラグインを検索していたものを、ランタイム上に読み込まれているものからプラグインを検索する方法にしたというところでした。これはIssuesを見つけるまでどうしたもんかとひたすらネットサーフィンしてました。ここのServiceProviderっていうのを利用して、LibraryManagerなるものを取得、そこからプラグインを検索しています。できてしまえば、大したことないんですけどね。

こうして記事書きながら改めて振り返っているうちにEntryPointExecutor.csからActivatorUtilities.csを経由してMainは初期化されていく様子だということがわかったり。ServiceProvider.csという実体が見つかったりと、いろいろ面白い発見があったりするため、かなり楽しんでいます!ただ、この記事に書いている内容はソースをベースに話を進めているので、将来に渡っての仕様として明確なことではありません。ご了承ください。

それではまた!

CoreCLRなBlackJumboDogにするとき仕組みを変更した理由と言訳 System.Windows.Forms

こんにちは。BlackJumboDogの置き換えに着手して1か月近くが経過しました。そこで、近況ばかりだったここに、当初大きく変更した点を記していきます。自分のまとめと疑問が含まれていますので少し脱線する要素は多くなります。また、答えを求めていた方にはそぐわないものになるかもしれないことを、あらかじめご了承ください。

本家BlackJumboDog

Bjdというプロジェクトがすべての基本となる機能を持っていました。それはExeでWindowsFormsのGUIを持っていました。GUIは起動すると最初に表示されサーバーの設定もログもWindowsサービスのセットアップもこれ一つできるというオールインワンの仕様です。お手軽さとして非常に大きく価値あるものだと思っています。勝手ながらこの記事では、これをBjd本体と表現することにします。

次に、サーバーの機能は参照する他のプロジェクトで実装されていました。ここに実装された設定は、自動的にGUIを追加する機能をBjd本体が提供していました。また必要に応じてダイアログを追加するような土台もBjd本体が提供していました。サーバーの通信処理(Socket)もBjd本体が多くを提供していました。実装は設定と通信プロトコルに集中したものです。勝手ながらこの記事では、Bjdサービスと表現することにします。

Bjdサービスのプロジェクトは、コンパイルによってDLL(アセンブリ)となりそれらを所定のディレクトリに配置しました。実行時には、その特定名称となるDLLを自動的に読み込んでいました。このときに使っていたのがリフレクション(System.Reflection名前空間にある機能)です。このリフレクションとは、型に厳密であるC# VB.NETなど.NetFrameworkの言語に対して、実行時における動的な処理を追加する機能です。コンパイル時にはないものを実行することができる反面、実行するまで結果はわかりません。

アセンブリは通常ファイル名またはアセンブリ名を指定して読み込まれていきます。GAC(GlobalAssemblyCache)はその一つです。これは署名されたアセンブリでインストールされたものだけが格納されている特別な領域です。.NetFrameworkを使ったことのあるかたはご存知のSystem、System.Data、System.Netなどのアセンブリはここに格納されています。これらに格納されたアセンブリを参照したアセンブリで実行イメージを作った場合。実行イメージである.exeと同じディレクトリにアセンブリファイルが存在する必要はありません。サードパーティ製のアセンブリもここにインストールされていることもあります。サードパーティ製のものを使って実装したものが、違う環境では動かなかった。そんな経験を持っている人はかなり多いんじゃないか?と思います。その場合は、このGACに同じものが入るようにインストールするか、コピーしたアセンブリ(DLL)をExeと同じディレクトリに配置する必要がありました。これは、VisualStudioのプロジェクトの参照設定でいう「ローカルコピー」というやつで実現できました。

さて、少し長々と書きましたが、上記の構成では、CoreCLRで動く.Net Coreではいくつか難問がありました。

System.Windows.Formsは存在しない

実は、、、.Net Coreには存在してません。便利だったこのGUI(実際これ肝だったと思う)は削除することにしました。ただし、こうすることで、構成の変更はどうやってやるのか?という話になってくるかと思います。これは、元のBjd本体で作ったoption.iniをそのまま使えることを前提として、この機能を削除しました。ログはファイルに出力されますので、直接の問題にはならないでしょう。CoreCLRはプラットフォームを選ばないものとして作られているため、将来的にもこれが提供される可能性は低いのではないかと思います。あくまで個人的見解ですが・・・。そのまま動かすというのであれば、Monoという.NetFrameworkのCross platformなものがあります。それを選べばいいわけです。こんなところで、CoreCLRを選ぶからこそのメリットを明確にしていく必要が出てきたわけでした。

(続く)

 

 

Consoleの文字化け対策 CoreCLR

昨日のテストで、文字化けしていましたが、これを対策します。

答えは簡単で、エンコードの問題でした。

まず、コンソールのエンコードを見てみます。

932、Shift-JISとなっています。これに対して、System.Consoleのエンコーディングを見てみます。

UTF-8となっています。つまり、UTF-8として出力されたものをShift-JISとして表示しようとしているので、おかしくなっていると考えられます。あわせてしまえばよいですね。一人でやってる分にはConsoleのエンコードを変えるという選択肢もあるかもしれません。ただ、そうしてしまうと個々の環境で設定が必要になってしまうため、コード側で対策しておきたいところです。なので、System.Consoleのエンコードを変えてみます。

var enc = System.Text.Encoding.GetEncoding(932);
var writer = new System.IO.StreamWriter(Console.OpenStandardOutput(), enc);
Console.SetOut(writer);

としたいところなのですが、実はこれだと動きません。

NotSupportedException
No data is available for encoding 932. For information on defining a custom encoding, see the documentation for the Encoding.RegisterProvider method.

何やら、エラーがSystem.Text.Encoding.RegiterProviderメソッドを見ろといっています。ここの説明では

.NET Framework 4.61 つのエンコード プロバイダーが含まれています。 T:System.Text.CodePagesEncodingProvider、を行う、利用可能なエンコーディングでは使用できませんが、完全な .NET Framework に存在を.NET Framework 4.6です。既定では、.NET Framework 4.6のみ、Unicode エンコーディング、ASCII、およびコード ページ 28591 をサポートします。

この「のみ」にすこし違和感ありますが、日本語は含まれていないようです。28591は西ヨーロッパ言語 (ISO)ですね。というわけで、そもそも、日本語は既定でサポートされていません。デスクトップ同様のサポートを確保するためには、System.Text.CodePagesEncodingProviderを利用してねーという内容ですかね。このクラスには、Instanceという静的なプロパティが一つ。これをエラーの内容に従って追加してみましょう。

System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
var enc = System.Text.Encoding.GetEncoding(932);
var writer = new System.IO.StreamWriter(Console.OpenStandardOutput(), enc);
Console.SetOut(writer);

という感じにしてみました。いざ、実行!

あれ!きれいですね。何も出てこないとなるのですが、これはバッファにFlushがかからないために出てこなくなるようです。なので、AutoFlushを有効にしちゃいましょう。

System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
var enc = System.Text.Encoding.GetEncoding(932);
var writer = new System.IO.StreamWriter(Console.OpenStandardOutput(), enc);
writer.AutoFlush = true;
Console.SetOut(writer);

という感じにプロパティの設定を追加。再び!

無事に日本語を拝めました。注意として、ここの実装だけだと、強制的にShiftJISとしてしまうので、Linuxで動かしたときは、逆に文字化けしてしまう可能性があります。また、日本語ではないWindowsでも同じことが言えます。様々なプラットフォームで動かすためには、以下を考慮しておいてみるとよいのではないかと思います。

  • 実行しているOSが、Windowsか?
  • 日本語で動いているか?
それでは!

 

NUnit dnx – BlackJumboDog CoreCLR

こんにちは!前回は、xUnitどうだろうか?ということで検討していましたが、そもそもNUnitがdnxに対応してました!

NUnit 3.0.0 Release Candidate – November 1, 2015

Framework

  • The portable build now supports ASP.NET 5 and the new Core CLR.NOTE: The nunit3-console runner cannot run tests that reference the portable build. You may run such tests using NUnitLite or a platform-specific runner.

ので、移植をメインとして挑んでいくことにします!(早くデプロイしたいという気持ちが抑えられないわけではないと自分に言い聞かせる)また、注意事項が、Upgradingに書いてありました。

For example, NUnit 3.0 no longer supports ExpectedExceptionAttribute. However, preferred alternatives Assert.Throws and the ThrowsConstraint have been available for several years. If you remove the attribute from your tests and use one of the alternatives instead, you can verify that they work in your present environment and they will continue to work after conversion.

ExpectedExceptionAttributeはサポートされなくなったようです。代替として、Assert.ThrowsNUnit.Framework.Constraints.ThrowsConstraintがあるとのこと。うーん。属性によるサポートはなくなったよーみたいなことかなぁ。Exceptionの挙動は、UIスレッド(クラシックWindowsFormなApplicationException)とかAppDomain(UnhandledExceptionあたり)も絡むので、CoreCLRAppDomainの概念はそもそもないと宣言されてる。System.Windows.Formsは存在しない。どちらもないと思われる。)だとつらいとか、実は制約多いからなんかわからなくもない。VSCodeにも.Net Coreにも対応していくなら当然な流れともいえる気がします。というわけで、そのあたりも置き換えていきます! “NUnit dnx – BlackJumboDog CoreCLR”の続きを読む

xUnit dnx

こんにちは!ツイッターネタです。

BlackJumboDogにテストコードいこうぜ!ということでこんな流れになってきました。タイミングとしても、ある程度動くようにコーディングが落ち着いてきた気がしてきています。(Linux側に少し課題は残っています。)

前記事の感触から、異なるフレームワークで動くようにするということはそれぞれに動作検証やら、テストやら必要になってきそうと思っています。そんなところで、CoreCLRとしてのテストコードがほしいと思う今日この頃でした。

http://xunit.github.io/docs/getting-started-dnx.html

ほぼ、リンク先の手順通りに進めます。

といいながら、最初は、project.jsonに対して、参照の追加

  • xunit
  • xunit.runner.dnx

次に、実行するための、commandを定義します。これはkestrelとかbjdと同じでエントリポイントを指し示すようなものといえば、わかりやすいかと思っています。つまり、このプロジェクトには、MAINはいらないという意味でもあります。

 

次に、唯一のクラスを作りました。サンプル通りのものです。

ビルドすると・・・。なんと!テストエクスプローラに出てくるではないですか!!

そして、そのまま調子にのって、すべて実行!

 

動いた・・・。特に何もいれてないんですが・・・。動いた・・・。不思議だ・・・。ふつう、拡張機能とか入れるもんだと思っていたんですが。

気になって、依存関係をのぞいてみました。何やら、TestHostっていうのがあるようですね。このあたりが提供している機能に乗っかっているだけなのかもしれません。

ちょっと、これは面白くなってきた・・・!ちなみに、なぜこうなるのかは、私自身わかっていません!

それでは!!

Ubuntu上のAsyncで遅延 – BlackJumboDog CoreCLR

ずーっと気になっていたんですが、Ubuntu上でサーバーのTCPソケットをAcceptAsyncかけていたら、しばらくアクセスがない状態だと1秒程度の遅延が発生していました。そして、連続したアクセスだとこれは起きなかったんです。

元々のBjdではWhileとSleepによるポーリングを行っていたので、おそらくそんなことは起きなかっただろうと思っています。(そもそも、Begin/Endが動かなかったので、根拠はない!)

同じことが、ManualResetEventSlimeでも起きました。ある程度時間がたったときに、シグナルセット後のWait解放が1秒くらい。これ、Windowsでは起きなかったんですよね。

正直、そこまでプラットフォームに詳しい人間ではないので、なぜこうなるのか?は理解できませんでした。ただ、どうすれば?に関しては、「深い眠りにつかないようにする」っていう発想で回避しました。

https://github.com/darkcrash/bjd5/commit/a9049f8630d2e788dda19065026c415654efcd05#diff-c042d296432145429c9f5f4d27bfef99

     -            tTcp.Wait(this.Kernel.CancelToken);  
 94   +            while (true)  
 95   +            {  
 96   +                if (tTcp.Wait(2000, this.Kernel.CancelToken))  
 97   +                    break;  
 98   +                if (tTcp.Status == TaskStatus.Canceled)  
 99   +                    break;  
 100  +            }

Waitを2秒までとして、完了時はループを抜け、キャンセル時もループを抜けます。こうすることで2秒に一回このWaitが実行されます。そして、結果として遅延1秒はなくなりました。

SpinWaitの絡みとか、タイムスライスのルールとかありそうなんですが、ブラックボックスです。Darkが作るBlackなだけに・・・

これが、Kernelによるものなら、今後もこういう仕様ということになるでしょうし、.Net Coreのものであれば、改善されていくかもしれません。ので、現時点ではこのコードは、一つの回避策として整理してくださいませ!

それでは!

BlackJumboDog on CoreCLR を.NetFrameworkで動かそうと思ったら・・・

ツイッターで、.NetFramework4.6で動かして、NUnitどうなの?という声をいただきまして、さっそく!project.jsonに以下を追加してみました。

  “frameworks”: {
“dnxcore50”: { },
“dnx461”: { }
}

う・・・。いやな感じに。

というのも、このSystem.Text.Encodingを含めは、23516で統一していたのですが、.netframework4.6では、どうやら違うバージョンになさいとのことです。バージョンを厳密にした思わぬ落とし穴でした。

(というか、、、少しおかしい気がします。機会があったらこの辺調べてみようかとは思います。dnx461でそもそもいいのとか。)

これを、それぞれのフレームワーク用に整えていけばよいのですが、今回の目的は、.NetFrameworkでも動かす!ではなく、テストの簡略化にありましたので、別の案を考えたほうがよさそうなところでもあります。

ただ、テストコードは、アプリケーションさえたたいてしまえばよいわけで・・・テストコードの結果さえ得られればよいわけで・・・という風にも考えられなくもないですね。

「NUnit dnx」とかぐぐっていたら、こんなものもありました。

https://github.com/xunit/dnx.xunit

Getting Started with xUnit.net (DNX / ASP.NET 5)

ちょっと興味が出てきます!これをNUnitのコードから移植してとかっていうのも面白そうですね!

(つづく

BlackJumboDog CoreCLR Ubuntu – Socketの置き換え APMからTAPへ

こんにちは!いきなり動いてるスクリーンショットから!!(Azure上のUbuntu14.04で動かしている様子です。)

というのも、動いてるだけでもすごいっていう気持ちを伝えたかったわけですが、今回に至るまでに行った変更点

  1. System.Net.Socketsのバージョンを他と統一し「4.1.0-beta-23516」としました。
  2. それに伴って、APMの実装は捨てられたため、TAPに書き直しました。
  3. パスの置換(\→.Netが提供するCharフィールド)を進めて動かしてみたところ、パスの置換が空回りして無限ループ!→修正済
  4. CPU利用率が起動直後で安定していても、10%以上という状況で、Thread.SleepをManualResetEventSlimに置換→アイドル中のレスポンスに1秒の遅延が発生。→検討中
  5. ConsoleTraceListenerの負荷がかかりそうなので、専用のタスクスケジューラ実装
  6. ログをそれっぽくフォーマットしました。

前回記事の、Begin/EndなどのAPM(非同期プログラミングモデル)なソケット実装がないとエラーになってました。これは、上のリンク先の通り、廃止されていくようです。このIssues(上と同じリンク) でその理由が書かれていました。Windowsに依存したようなものは排除する!っていう面白い話ですね。

ただ、これ変えてしまうと、そもそもポーリングしてるロジックとか全部作りなおしなわけで、結構時間かかってました&まだ懸念点が残ってます。行き詰っていろいろ試したのもあって、ソースの履歴が激しいことになっていてごめんなさいというところです。

また、ぜんぜん気づいていなかったのですが、Traceで出力したものは、Defaultのlistenerか何かによってだとは思いますが、しっかりとSyslogに出力されていました。(これはちょっと驚いた)

大物のWebServerが動いたので、懸念点を解消しつつデプロイ方向にシフトしていきます。

それではまた!