PowershellでC#等をコンパイルする

.Netにはもれなくコンパイラーが含まれている。
このため、VisualStudio等を導入しなくても、C#等のビルドが可能である。


しかし、ググッても一ページ目に出なかったので、メモ書きする。



追記 20140116
Powershell v2.0以降なら、Add-Typeでビルドすべきと気づいたので修正。
こんな簡単じゃあ、、、ぐぐっても出ないよねぇ。

Add-Typeでビルド

$sourceにソース、$outpathが出力先として、
C# DLLなら、OutputAssemblyを指定するだけ。

Add-Type $source -OutputAssembly $outpath

ビルドと同時にロードもしたいなら、-PassThruを指定してやって。
VisualBasic/JScriptなら、Languageを指定する。
それ以外は、下とあんまり変わらないので割愛(CodeDomProviderを指定する必要がある)


C# EXEなら、OutputTypeを追加。
コンソールアプリケーションなら

Add-Type $source -OutputAssembly $outpath -OutputType ConsoleApplication

コンソールいらないなら

Add-Type $source -OutputAssembly $outpath -OutputType WindowsApplication

メインクラスを指定する必要がある場合は、CompilerParametersを指定してやる必要がある気がします。
その場合、OutputTypeは指定しなくて良いと思われる(CompilerParametersに含まれるため)
CompilerParametersの作り方は、下を参照してくださいな。

DLLのビルド

$sourceにソース、$outpathが出力先(DLL名)

$provider = [System.CodeDom.Compiler.CodeDomProvider]::CreateProvider("CSharp")
$parameters = New-Object System.CodeDom.Compiler.CompilerParameters
$parameters.OutputAssembly = $outpath
$provider.CompileAssemblyFromSource($parameters, $source)

EXEのビルド

$sourceにソース、$outpathが出力先(EXE名)、$mainがMainのクラス

$provider = [System.CodeDom.Compiler.CodeDomProvider]::CreateProvider("CSharp")
$parameters = New-Object System.CodeDom.Compiler.CompilerParameters
$parameters.GenerateExecutable = $true
$parameters.OutputAssembly = $outpath
$parameters.MainClass = $main
$provider.CompileAssemblyFromSource($parameters, $source)

Hello, world!

$source = @"
public class Hello {
	public static void Main(string[] args) {
		System.Console.WriteLine("Hello, world!");
	}
}
"@
$outpath = '.\hello.exe'
$main = "Hello"
$provider = [System.CodeDom.Compiler.CodeDomProvider]::CreateProvider("CSharp")
$parameters = New-Object System.CodeDom.Compiler.CompilerParameters
$parameters.GenerateExecutable = $true
$parameters.OutputAssembly = $outpath
$parameters.MainClass = $main
$provider.CompileAssemblyFromSource($parameters, $source)
&$outpath

使える言語は?

プロバイダーを「CSharp」で作成してますから、C#
同じノリでVBとかもいけますよ!
一覧は以下をどうぞ。別名も確認できますよ。

[System.CodeDom.Compiler.CodeDomProvider]::GetAllCompilerInfo() | ForEach-Object {$_.GetLanguages()}

なお、言語名に大文字・小文字の区別はありません。

$source = @"
Imports Microsoft.VisualBasic
Public Class Hello
	Public Shared Function Main(ByVal args() As String) As Integer
		return MsgBox("Hello, world!")
	End Function
End Class
"@
$outpath = '.\hello.exe'
$main = "Hello"
$provider = [System.CodeDom.Compiler.CodeDomProvider]::CreateProvider("VisualBasic")
$parameters = New-Object System.CodeDom.Compiler.CompilerParameters
$parameters.GenerateExecutable = $true
$parameters.OutputAssembly = $outpath
$parameters.MainClass = $main
$provider.CompileAssemblyFromSource($parameters, $source)
&$outpath

備考

この方法で、クラスをメモリーコンパイルしてファイル化することなく使うことも可能ですが、それならAdd-Typeを使った方が楽でしょう。
いずれにしてもメモリーコンパイルではクラスを排除する方法はないので、クラスを作り直すことを考慮するなら、AppDomainを別にしてAppDomain毎破棄とか、PowershellPowershellで「exit」するとかが必要です。


XPが死滅しようとしている今、バイナリーの持ち込みを禁止することは、ほぼ不可能なのです。
ネットワーク管理者は無駄な抵抗はやめるべきです。