読者です 読者をやめる 読者になる 読者になる

M12i.

学術書・マンガ・アニメ・映画の消費活動とプログラミングについて

macOS x NuGet v4.1で*.csprojに基づくパッケージ作成

およそ半年前の記事「maxOS環境でNuGetパッケージをつくる」を投稿した段階では、macOS上でNuGetによるパッケージ作成をしようとしたとき、nuget pack *.csproj -Build ...コマンドを実行した時点でエラーとなる状態でした。*.nuspecファイルによるパッケージングはOKですが、*.csprojファイルに基づくそれはNGだったのです(CLRはMono。Xamarin Studioとともにインストールされたもの)。

しかし数日前に公式サイトから最新版であるv4.1をダウンロードしてパッケージングを試行してみたところこの問題がすでに解消していました。

続く説明は、ターミナルでmonoコマンドが実行でき、かつ、v4.1のnuget.exeをダウンロード済みで、mono (nuget.exeのパス) (NuGetのコマンドライン)という形式で実行できる前提で進めます。

*.nuspectファイルに基づくパッケージング

例えば次のようなディレクトリ構成になっているとき、nuget pack Sample.Project.nuspecコマンドを実行すると、パッケージ(*.nupkgファイル)に封入されるNuGetパッケージ情報は純粋に*.nuspecファイルに記述された情報に基づいたものになります。*.nuspecファイルの内容にはNuGetコマンドを実行する前にプロジェクトの最新の情報を手作業で反映させておく必要があります:

─Sample.Solution
  ├Sample.Solution.sln
  ├...
  └Sample.Project
    ├Sample.Project.csproj
    ├Sample.Project.nuspec
    ├Properties
    │├...
    │└AssemblyInfo.cs
    ├...
    └bin
      ├Debug
      └Release
        ├...
        ├Sample.Project.dll
        └Sample.Project.xml

*.csprojファイルに基づくパッケージング

一方、*.nuspecファイルの内容が次のようなプレースホルダを含むものである場合、nuget pack Sample.Project.csproj -Build ...コマンドを実行すると、まずは*.csprojファイルの情報に基づきデフォルトのビルドが実行されて、成果物が所定のディレクトリに生成されます(コマンドライン...の部分には適宜オプションの引数を指定):

<?xml version="1.0"?>
<package >
  <metadata>
    <id>$id$</id>
    <version>$version$</version>
    <authors>$author$</authors>
    <description>$description$</description>
  </metadata>
</package>

NuGetはこの成果物の中のアセンブリの情報を読み取って、*.nuspecファイルの内容をプレースホルダ部分を補完した上でパッケージに封入します。「アセンブリの情報」は一般にはAssemblyInfo.csファイルに記述する[AssemblyName("Sample.Project")]などの各種の属性(Attribute)により指定されるものです。

これらの属性とプレースホルダの対応関係はMicrosoft社のドキュメント.nuspec referenceに示されています。

*.csprojのビルド設定と*.nuspecのビルド設定の関係

ところでこの*.csprojファイルによるパッケージング──正確に言えば「コマンドラインに*.csprojファイルを指定することで、*.csprojファイルと*.nupkgファイルとアセンブリ情報の定義に基づき行うパッケージング」──では、おおよそ次のような順序で処理が進められているようです:

  1. *.csprojファイルの情報に基づきデフォルトのビルドを実行
  2. *.nuspecファイルをメモリ上にロード
  3. 最前のビルド成果物に含まれるアセンブリからメタ情報を抽出
  4. メモリ上の*.nuspecファイル内容にメタ情報をマージ→パッケージZIPに追加
  5. *.nuspecファイルの<files/>タグの内容に基づきファイル特定→パッケージZIPに追加
  6. 最前のビルド成果物をコピーしパッケージZIPに追加

この最後のビルド成果物のパッケージ封入の際、*.csprojファイルで指定されたビルド・ターゲットの情報に基づき、ZIP内のパスも自動で調整されます。例えば.NET Framework v4.5.2をターゲットとしたビルド成果物の場合、ZIP内のパスはlib/net452/Sample.Project.dllのようになります。

そして*.nuspecファイルの<files/>タグの内容に基づきすでに同名のパスにファイルが存在している場合は、「警告: パッケージには既にファイル 'lib/net452/Sample.Project.dll' が含まれているため、ファイル 'Sample.Solution/Sample.Project/bin/Release/Sample.Project.dll' は追加されません」というような警告ログを出力します。

また*.nuspecファイルに<files/>タグの記述がない場合は「最前のビルド成果物をコピーしパッケージZIPに追加」しか行われないので、その結果生成されるパッケージも*.csprojファイルでターゲット指定したフレームワーク・バージョンにしか対応しないものになります。

ISBNコードのシリアライザ/デシリアライザをつくった

まあ背景事情がないでもないのですが。。思い立ったが吉日とばかり、あれこれの予定をすっぽかしてISBNコードの仕様を調べて、シリアライザ/デシリアライザをつくってみました。ISBN-10・ISBN-13のいずれにも対応しています。単体テストNUnitで、動作確認はWindowsmacOSのいずれでもとっています。

プロジェクトのGitHubリポジトリこちらアセンブリは例によってNuGet Galleryにて公開しています。

以下のようにして使います:

// P・ブルデュー『社会学の社会学』のISBNコード
string isbnString = "ISBN-13: 978-4938661236";
IsbnCode isbnCode = IsbnCode.Parse(isbnString);

// 以下のコードもこれと同義
// var isbnCode = IsbnCode.Parse("ISBN-13: 978-4-938661-23-6");
// var isbnCode = IsbnCode.Parse("ISBN-13 978-4938661236");
// var isbnCode = IsbnCode.Parse("ISBN 978-4-938661-23-6");
// var isbnCode = IsbnCode.Parse("ISBN978-4-938661-23-6");
// var isbnCode = IsbnCode.Parse("978-4-938661-23-6");
// var isbnCode = IsbnCode.Parse("9784938661236");
// var isbnCode = IsbnCode.Parse("ISBN-10: 4-938661-23-6");
// var isbnCode = IsbnCode.Parse("4-938661-23-6");
// var isbnCode = IsbnCode.Parse("4938661236");

// グループ記号は日本国内の出版であることを示す "4"
Digits group = isbnCode.Group; // => Digits("4")
string agencyName = isbnCode.Agency; // => "Japan"

// 出版者記号は藤原書店を表す "938661"
Digits publisher = isbnCode.Publisher; // => Digits("938661")

// 書名記号は 『社会学の社会学』 を示す "23"
Digits title = isbnCode.Title; // => Digits("23")

// 検査数字はモジュラス10で計算された '6'
char checkDigit = isbnCode.CheckDigit; // => '6'

// 引数なしの ToString() はプレフィクスやハイフンを含んだ形式の文字列を返す
string isbnString2 = isbnCode.ToString(); // => "ISBN-13: 978-4-938661-23-6"
string isbnString3 = isbnCode.ToString(IsbnCodeStyles.WithHyphens); // =>  "978-4-938661-23-6"
string isbnString4 = isbnCode.ToString(IsbnCodeStyles.AsIsbn10Code); // => "4-938661-23-6"

イベントログ1530直後に実行中の複数CLRプロセスでエラー発生

直近、仕事で保守しているバッチシステムで、同時に実行されていた複数の.exeがある時間帯─数十秒間にほぼ同時にエラーとなるという事象が発生しました。

標準エラー出力やエラーログに出力された内容は.exeにより様々で「インターフェイスがサポートされていません」や「タイプ初期化子が例外をスローしました」、そして単に「○○でエラーが発生しました」的な素朴で役立たずなものもありました。

一方、後述のようにイベントビューアを確認すると、事象発生直前にイベントログ1530が記録されていました。

コンテキスト

OS Windows Server 2008 R2
ランタイム .NET Framework 4.5.2
実装言語 C#
エラーメッセージ インターフェイスがサポートされていません」、「タイプ初期化子が例外をスローしました」など

インターフェイスがサポートされていません」は.exeが参照していたCOMの1つをロードする過程で発生しているようでした。「タイプ初期化子が例外をスローしました」は(別の).exeが稼働するCLRにODP.NETのクラスがロードされたとき、タイプ初期化子(=静的コンストラクタ)の実行中に意図せぬ例外に見舞われて発生したようです。

しかし前後の時間に実行されていた.exeでは事象は発生しておらず、現に今回異常終了した.exeにしても再実行したところ正常稼動しました。当該時間帯に人為的にシステム変更がなされていた痕跡もありません。

イベントログ

一方、OSのイベントログを参照すると事象発生の直前にこちらの記事で言及されているイベントログ1530が発生していました。

レジストリ ファイルは他のアプリケーションまたはサービスで使用されています。ファイルはすぐにアンロードされます。レジストリ ファイルを保持しているアプリケーションまたはサービスはこれ以降正しく機能しない可能性があります

というメッセージに続いて、この警告内容に該当するプロセスがずらりと一覧されており、前述のエラーを起こした.exeのプロセスはすべてここに含まれていました。

Microsoft TechNetの記事の説明には次のようにありますが:

This event can be caused by apps that do not release their Registry keys before shutting down. This most often occurs when an app runs in the background and does not release its Registry keys when a user signs off, in which case Windows forces the Registry to unload. There is no impact to users, though in rare cases recent configuration changes in the app might not be saved.
このイベントはシャットダウンのまえにレジストリ・キーを解放していないアプリケーションにより引き起こされる可能性があります。バックグラウンドで何かしらアプリケーションが実行されていて、ユーザのサインオフ〔に伴う当該アプリケーションのシャットダウン?〕に際してレジストリー・キーが解放されない場合にはたいがい発生するイベントです。このような場合、Windows OSが強制的にレジストリーをアンロードさせます。ユーザへの影響はありません。稀ではありますが、アプリケーションが行った直近の設定情報〔レジストリーに保存される設定情報?〕の変更が保存されないままとなることがあります。

今回の事象からすればこの説明にあるようなアプリケーション起点の問題ではないように思われます。時系列でいえば、このイベントログが出力された直後から数十秒間に.exeのエラーが発生しています。上記説明にあるサインオフ云々にしても、当該バッチシステムはJP1/AJS2 Agentサービスにより実行されており、ユーザのサインオン/サインオフとは無関係であるはず。

あるいは逆に、というかそもそも、このイベントログと事象とは何の関係もないのでしょうか。それにしてはタイミングがぴったりすぎで、メッセージ内容には直後にエラーになった.exeがずらりと並んでいるのですから、ひとまず関係ありと仮定するのが妥当なように思われます。

とはいえ、Google先生にあれこれ伺ってみても類似事象に関する返答を拝聴することはできませんでした。Microsoft社のサポート問合せで何かわかるとよいのですが。。