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

Visual Studio 2015にしたらNuget restoreで参照設定が復元されなくなった

.NET NuGet

Jenkinsで流すと、nuget restoreは一見正常に終了しているが次のMSBuildでエラーになります。

エラーは次の通り。(ちょっと長いけど)

ResolveAssemblyReferences:
  プライマリ参照 "log4net" です。
C:\Program Files\MSBuild\14.0\bin\Microsoft.Common.CurrentVersion.targets(1820,5): warning MSB3245: この参照を解決できませんでした。アセンブリ "log4net" が見つかりませんでした。アセンブリが間違いなくディスクに存在することを確認してください。 コードにこの参照が必要な場合、コンパイル エラーが発生する可能性があります。 [c:\jenkins\workspace\Foo_bar_develop-CTIV4IL2JPHXPYVJDTYNNVX42T665ZR7U5MNMTNIGCKMRODFHMAA\src\bar\bar.csproj]
          検索パスは "{TargetFrameworkDirectory}" です。
          "C:\Program Files\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\log4net.winmd" を考慮しましたが、存在しませんでした。
          "C:\Program Files\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\log4net.dll" を考慮しましたが、存在しませんでした。
          "C:\Program Files\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\log4net.exe" を考慮しましたが、存在しませんでした。
          検索パスは "{Registry:Software\Microsoft\.NETFramework,v4.0,AssemblyFoldersEx}" です。
          AssemblyFoldersEx の場所が考慮されました。
          検索パスは "{AssemblyFolders}" です。
          "C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\log4net.winmd" を考慮しましたが、存在しませんでした。
          "C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\log4net.dll" を考慮しましたが、存在しませんでした。
          "C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\log4net.exe" を考慮しましたが、存在しませんでした。
          "C:\Program Files\Microsoft SQL Server\120\SDK\Assemblies\log4net.winmd" を考慮しましたが、存在しませんでした。
          "C:\Program Files\Microsoft SQL Server\120\SDK\Assemblies\log4net.dll" を考慮しましたが、存在しませんでした。
          "C:\Program Files\Microsoft SQL Server\120\SDK\Assemblies\log4net.exe" を考慮しましたが、存在しませんでした。
          "C:\Program Files\IIS\Microsoft Web Deploy V3\log4net.winmd" を考慮しましたが、存在しませんでした。
          "C:\Program Files\IIS\Microsoft Web Deploy V3\log4net.dll" を考慮しましたが、存在しませんでした。
          "C:\Program Files\IIS\Microsoft Web Deploy V3\log4net.exe" を考慮しましたが、存在しませんでした。
          "C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5\log4net.winmd" を考慮しましたが、存在しませんでした。
          "C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5\log4net.dll" を考慮しましたが、存在しませんでした。
          "C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5\log4net.exe" を考慮しましたが、存在しませんでした。
          検索パスは "{GAC}" です。
          GAC で見つからなかった "log4net" を考慮しました。
          検索パスは "{RawFileName}" です。
          "log4net" をファイル名として扱いましたが、存在しませんでした。
          検索パスは "bin\x86\Release\" です。
          "bin\x86\Release\log4net.winmd" を考慮しましたが、存在しませんでした。
          "bin\x86\Release\log4net.dll" を考慮しましたが、存在しませんでした。
          "bin\x86\Release\log4net.exe" を考慮しましたが、存在しませんでした。
  AssemblyFoldersEx の場所:"{Registry:Software\Microsoft\.NETFramework,v4.0,AssemblyFoldersEx}"
          C:\Program Files\NUnit 2.6.3\bin\framework\
          C:\Program Files\Microsoft Visual Studio 14.0\Common7\IDE\ReferenceAssemblies\v4.0\
          C:\Program Files\Microsoft Visual Studio 11.0\Common7\IDE\ReferenceAssemblies\v4.0\
          
          C:\Program Files\Reference Assemblies\Microsoft\VBPowerPacks\v10.0\
          C:\Program Files\Microsoft SDKs\Expression\Blend\.NETFramework\v4.0\Prototyping\Libraries\
          C:\Program Files\Microsoft SDKs\Expression\Extensions\FXG\Libraries\.NETFramework\v4.0\
          C:\Program Files\Microsoft SDKs\Expression\Blend\.NETFramework\v4.0\Libraries\
          C:\Program Files\Microsoft Visual Studio 14.0\Common7\IDE\ReferenceAssemblies\v2.0\
          C:\Program Files\Microsoft Visual Studio 11.0\Common7\IDE\ReferenceAssemblies\v2.0\
          C:\Program Files\Reference Assemblies\Microsoft\VBPowerPacks\v10.0\
          C:\Program Files\Microsoft SQL Server Compact Edition\v4.0\Desktop
          C:\Program Files\Common Files\Microsoft Shared\MSEnv\PublicAssemblies\
          C:\Program Files\Microsoft Chart Controls\Assemblies
GenerateTargetFrameworkMonikerAttribute:
すべての出力ファイルが入力ファイルに対して最新なので、ターゲット "GenerateTargetFrameworkMonikerAttribute" を省略します。
CoreCompile:
  C:\Program Files\MSBuild\14.0\bin\csc.exe /noconfig /nowarn:1701,1702 /nostdlib+ /platform:x86 /errorreport:prompt /define:TRACE /highentropyva- /reference:"C:\Program Files\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\mscorlib.dll" /reference:"C:\Program Files\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.Configuration.dll" /reference:"C:\Program Files\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.Core.dll" /reference:"C:\Program Files\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.dll" /reference:"C:\Program Files\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.Xml.dll" /reference:"C:\Program Files\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.Xml.Linq.dll" /filealign:512 /optimize+ /out:obj\x86\Release\bar.dll /ruleset:"C:\Program Files\Microsoft Visual Studio 14.0\Team Tools\Static Analysis Tools\\Rule Sets\ManagedMinimumRules.ruleset" /target:library /utf8output Properties\AssemblyInfo.cs src\Ibar.cs src\bar4net.cs src\barBase.cs src\barManager.cs "C:\Users\jenkins\AppData\Local\Temp\.NETFramework,Version=v4.0.AssemblyAttributes.cs"
  Using shared compilation with compiler from directory: C:\Program Files\MSBuild\14.0\bin
src\bar4net.cs(11,17): error CS0246: 型または名前空間の名前 'log4net' が見つかりませんでした (using ディレクティブまたはアセンブリ参照が指定されていることを確認してください)。 [c:\jenkins\workspace\Foo_bar_develop-CTIV4IL2JPHXPYVJDTYNNVX42T665ZR7U5MNMTNIGCKMRODFHMAA\src\bar\bar.csproj]
プロジェクト "c:\jenkins\workspace\Foo_bar_develop-CTIV4IL2JPHXPYVJDTYNNVX42T665ZR7U5MNMTNIGCKMRODFHMAA\src\bar\bar.csproj" (Rebuild ターゲット) のビルドが終了しました -- 失敗。
プロジェクト "c:\jenkins\workspace\Foo_bar_develop-CTIV4IL2JPHXPYVJDTYNNVX42T665ZR7U5MNMTNIGCKMRODFHMAA\src\bar.sln" (Rebuild ターゲット) のビルドが終了しました -- 失敗。

ビルドに失敗しました。

Jenkinsのビルド用スレーブでVisual Studioを起動してからUpdate-Package -reinstallすれば直った。
が、CI的にはVS開いてゴニョゴニョしたくないのでスレーブのcsprojファイルを参考に以下のように元ファイルを修正したら、nuget restoreで参照設定が壊れなくなった!

  <ItemGroup>
    <Reference Include="log4net">
      <HintPath>..\packages\log4net.2.0.7\lib\net40-full\log4net.dll</HintPath>
      <Private>True</Private>
    </Reference>
  </ItemGroup>

でもこれってファイルの数が増えたらたまんないな。。。
もっといいやり方がありそうだけど。いや、あるべきだ!

Visual Studio 2015 で古いプロジェクトを開いた時に「参照」が壊れた

.NET

現象

Visual Studio 2012のプロジェクトを2015で開いたら、参照が全部壊れているプロジェクトがあった。(全部ではない)

  • SystemとかSystem.Coreとか基本的なやつやlog4netといったnuget管理の物も黄色の!がでて参照できなくなっている
    • 参照からランタイムを選んでプロパティを表示するとバージョンが0.0.0.0になっている
  • 参照の追加で選び直しても駄目
  • 対象のランタイムを切り替えても駄目

解消方法

結局、csprojファイルを直接エディタで開いて直しました。
以下の部分を削除します。

  <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
    <PropertyGroup>
      <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
    </PropertyGroup>
    <Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
  </Target>

ちなみにソリューションファイル(sln)の変換は上手くいってました。

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Express 2012 for Windows Desktop
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Express 14 for Windows Desktop
VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1

C#でWindowsのシステム環境変数を取得するときに値として埋め込まれている環境変数を展開しないで取得する

.NET C# Windows

題名長い (;´д`)

環境変数を取得して改変してセットしたいときに値として埋め込まれた環境変数が展開されてしまうと不都合があると思います。

システム環境変数Pathを取得して変更する例

システム環境変数(例えばPath)を取得したい場合、いくつかやり方があるけど以下のようにすると簡単ですよね。

string env = "Path";
string value = Environment.GetEnvironmentVariable(env, EnvironmentVariableTarget.Machine);
Console.WriteLine(string.Format("{0}={1}", env, value);

以下のような値が取れたとします。

Path=C:\Program Files\Java\jre1.8.0_111\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\ProgramData\chocolatey\bin

ただ環境変数GUIから確認すると、以下のようになっています。

Path=%JAVA_HOME%\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\ProgramData\chocolatey\bin

取得した値に追加のパスを入れて保存したら%JAVA_HOME%\binC:\Program Files\Java\jre1.8.0_111\binに変わってしまいますよね。これは嬉しくありません。

というわけで、レジストリから取得すれば展開前の値が取れました。

using Microsoft.Win32;

string env = "Path";
RegistryKey reg = Registry.LocalMachine.OpenSubKey(@"System\CurrentControlSet\Control\Session Manager\Environment");
string value = (String)reg.GetValue(env, "", RegistryValueOptions.DoNotExpandEnvironmentNames);
Console.WriteLine(string.Format("{0}={1}", env, value);

GetValueの第3引数にRegistryValueOptions.DoNotExpandEnvironmentNames渡すと展開されずに取得できます。

意外と手こずったのでφ(..)メモメモ

IE 11で何故か互換表示になってしまう場合にデフォルト(Edge)に戻す

HTML IE

会社の中でIE使っているときはイントラネットゾーンになって、なんか互換表示になっていることがあります。

その時は、metaタグに

<meta http-equiv="X-UA-Compatible" content="IE=edge">

と入れることで元に戻るのですが、なんか戻らない時があるんですよね。

そんなときは、上の方(charsetのすぐ下とか)に書いてみると直ることがあります。

<head>
    <meta charset="UTF-8">
    .
    .
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    .

↓↓↓

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    .
    .
    .

まったく、何なんすかねこれヽ(`Д´)ノプンプン

Groovyでログから特定区間の処理時間を計算する

Groovy ag

障害調査とかログから処理時間を抽出するというのをごにょっとやったのでメモを残しておきます。(書いとかないと絶対忘れるやつだし)

ログファイルから欲しい情報を抜き出す

スタックトレースを起点に処理時間を抽出しようとするとその行には日付などの情報が入って無くて前後数行欲しいってありますよね。

$ ag --nonumbers -B4 -A1 -G "hogehoge.log*" "^System\.Data\.SqlClient\.SqlException"
  • -B4 は前4行を抜き出すということです
  • -A1 は後1行を抜き出すということです
  • -G正規表現を使うようにしてます

2点間の処理時間を調べる

上で抽出した中間ファイルからExcelで処理するようのCSVを作成します。
使い慣れたGroovyでやってみます。使い捨てのスクリプトなので適当に書いてます。

  • 取り敢えず半角スペースでsplitしながらぐるぐる回します
new File('hogehoge.txt').splitEachLine(' ') { logs ->
    // 処理
}
  • 上の処理の中身(回しながら欲しい2点間のログを抽出)
    def format = "yyyy-MM-dd HH:mm:ss,SSS"

    comment = logs[7]
    if (!comment) {
        //println "null"
    }
    else if (comment.contains("アプリケーションを開始")) {
        start = new Date().parse(format, "${logs[3]} ${logs[4]}")
    }
    else if (comment.contains("DBの接続に失敗しました")) {
        end = new Date().parse(format, "${logs[3]} ${logs[4]}")
        time = end.toTimestamp().getTime() - start.toTimestamp().getTime()
        println "${logs[0]}\t${logs[3]} ${logs[4]}\t${time}"
    }

logs[7]にログの文字列が入ってくる想定なので計測したい2点間のキーワードを引っ掛けて、ログの時間を取得して処理時間を計算します。
この時に、.NETのlog4netで出している形式をformatで定義してData().parseで変換してます。
yyyy-MM-dd HH:mm:ss,SSS こんな形式で実際には2017-01-16 12:34:56,789って感じのやつですね。
timeに処理時間を入れてます。計算するときは、toTimestamp().getTime()を使ってミリ秒単位で結果が出るようにしています。

って言うか、最初からログに処理時間を出すようにして欲しいデス!!!

おまけ

  • ログが巨大なのでちょっと1万行くらい処理して様子をみたいとかあるのですが、Groovyのeachの中ではbreak出来ないので無理やり抜けるようにしてました。
def i = 0
new File('hogehoge.txt').splitEachLine(' ') { logs ->
    // 処理
    i++
    if (i > 10000) { System.exit(0) }
}

他にいいやり方があれば教えて欲しい・・・。

  • 一応、ソース付けておきます。
def i = 0
def format = "yyyy-MM-dd HH:mm:ss,SSS"
def start
def end
def time
def comment

new File('hogehoge.txt').splitEachLine(' ') { logs ->
    comment = logs[7]
    if (!comment) {
        //println "null"
    }
    else if (comment.contains("アプリケーションを開始")) {
        start = new Date().parse(format, "${logs[3]} ${logs[4]}")
    }
    else if (comment.contains("DBの接続に失敗しました")) {
        end = new Date().parse(format, "${logs[3]} ${logs[4]}")
        time = end.toTimestamp().getTime() - start.toTimestamp().getTime()
        println "${logs[0]}\t${logs[3]} ${logs[4]}\t${time}"
    }
    // 1万行ほどやってみるときは下のコメントを外す
    //i++
    //if (i > 10000) { System.exit(0) }
}

iTerm2でvimを使うとマウスでコピペできなくなった

Mac Vim iTerm2

記憶は定かではないがある時を境にでビジュアルモードで選択されるようになった。 最初に何か設定したかもしれないけど思い出せないのでぐぐったら解決した。

d.hatena.ne.jp

ここに書いてあるとおりですが、設定項目は微妙に変わっていて、

  • iTerm2 > Preferences > Profiles > Terminal > Terminal Emulation
    • Enable mouse reporting のチェックを外す

でした。

ubuntuでntpdateを使って時刻合わせをする

会社では、例によってProxyがあるので社内のNTPサーバに同期させる。

/etc/default/ntpdateというファイルがあったので、NTPSERVERSに社内のサーバを記述してntpdateコマンドを叩いてみる

# ntpdate
 4 Jan 11:40:08 ntpdate[23795]: no servers can be used, exiting

うまくいかない・・・

調べたらどうやら/usr/sbin/ntpdate-debianというラッパーコマンドがあるらしい。
ただしこれを使うときは、NTPDATE_USE_NTP_CONF=noにしないと設定したサーバリストが使われない。

# The settings in this file are used by the program ntpdate-debian, but not
# by the upstream program ntpdate.

# Set to "yes" to take the server list from /etc/ntp.conf, from package ntp,
# so you only have to keep it in one place.
#NTPDATE_USE_NTP_CONF=yes
NTPDATE_USE_NTP_CONF=no

# List of NTP servers to use  (Separate multiple servers with spaces.)
# Not used if NTPDATE_USE_NTP_CONF is yes.
#NTPSERVERS="ntp.ubuntu.com"
NTPSERVERS="10.x.x.x"

# Additional options to pass to ntpdate
NTPOPTIONS=""

これで上手くいきました。

# ntpdate-debian
 4 Jan 11:44:30 ntpdate[23814]: step time server 10.x.x.x offset 253.208283 sec

このntpdate-debianは起動時に実行されるらしいので気になる場合はcronで定期実行させてもいいかも。