sln ファイルと csproj ファイル の関係と特定の プロジェクトだけビルドする方法

Azure Functions のプロジェクトを VSTS でビルド/テストをして、デプロイする環境を作った。その過程で、いろいろ気になることを調べてみた。

# Solution と Project 

最初に初めてC# を触った時に、一番頭が混乱したことが、`sln` ファイルとか、`csproj` ファイルが自動的に出来上がることだった。テンプレートでできるので、「作れる」のだが、意味がわからないのは相当気持ち悪かった。整理のために、今まで理解したことを書いておく。

Project が exe を生成する単位。ソースだけではなく、設定ファイルや、データ、イメージも含まれる。拡張子は、様々なバリエーションがあって、`csproj` だったり、`sfproj` だったりする。


![solutionandproject.jpeg](https://qiita-image-store.s3.amazonaws.com/0/3470/f3e8027d-ef18-58ce-c20d-ca750c7b63d9.jpeg)

# Solution と Project の実態

テキストファイルである。ただし、あまり手で編集することを想定されていない（が、たまにある）sln が、基本的にはプロジェクトへの参照とグローバルのコンフィグレーション、projファイルが、実際のプロジェクトの実態になっている。

それぞれがどんな内容になるのかは、最小限のMSプロジェクトを公開してくれている人がいるので、それがわかりやすい

* [Minimal C# project - CS CSPROJ SLN](https://gist.github.com/darthwalsh/f66f064c072876f4e50f)

# ビルドの用語

MS 独特の用語が説明もなく出てくるので最初は戸惑うかもしれない。

## Build Configuration 

Build Configuration は、環境ごとのコンフィグで、`Release` `Debug` が、最初からデフォルトである。環境ごとにコンフィグレーションを作ることができる。これらは、Configuration Manager から操作される（C# では)

* [Understanding Build Configurations](https://msdn.microsoft.com/en-us/library/kkz9kefa.aspx)

## Build Platform 

ビルドプラットフォームは、`x64` `x86` `Any CPU`から選べる。ビルドするときのその名の通りのプラットフォーム

* [Understanding Build Platforms](https://msdn.microsoft.com/en-us/library/ms165407.aspx)

# 特定のプロジェクトのみビルドする

VSTS や、Visual Studio でビルドに使われているのは、`msbuild` というツールだ。一つのソリューション(sln) に複数のプロジェクトが含まれている場合、特定のプロジェクトのみビルドしたいという場合があるが、いくつかの方法がある。

## 1. msbuild のオプションを指定する

大抵ツールで隠蔽されているが、中身では、単純にmsbuildが動いている。`/t` オプションで必要となるプロジェクトのみターゲットを指定する

```
msbuild functions.sln /t:"Telemetry:Clean;Build";"Telemetry_Test:Clean;Build"
```

## 1.2. '.' は '_' に

尚、この際、VSTS もしくは他のツールでも、衝撃のポイントがあった。`Telemetry.Test` というプロジェクト名がテストプロジェクトでこれは普通のプラクティスだろう。その際、`/t` を使うときに、`.` を使うとヒットしない。代わりに `_` を使う必要があった。だから上記のように、実際のプロジェクトは、`Telemetry.Test` でも、`Telemetry_Test` と記述している。

* [Why won't MSBuild build a project with a dot in the name?
](https://stackoverflow.com/questions/5863043/why-wont-msbuild-build-a-project-with-a-dot-in-the-name)

ちなみに、WebApp や AzureFunctions デプロイの際には、msbuild で zip に固められる。そしてそれを、msdeploy 

* [MS Build](https://msdn.microsoft.com/en-us/library/0k6kkbsd.aspx)
* [MS Deploy](https://technet.microsoft.com/en-us/library/dd569106(v=ws.10).aspx)

VSTS で標準でついてくる Azure App Service Deploy でも、 VSTS のログを見ると、msdeploy を使っている。

 ```
"C:\Program Files\IIS\Microsoft Web Deploy V3\msdeploy.exe" -verb:sync -source:package='d:\a\1\a\WebApp.zip' -dest:auto,ComputerName='https://japanfunctions.scm.azurewebsites.net:443/msdeploy.axd?site=JapanFunctions',UserName='********',Password='********',AuthType='Basic' -setParam:name='IIS Web Application Name',value='JapanFunctions' -enableRule:DoNotDeleteRule -userAgent:VSTS_0de2e4a9-d53c-4227-a23d-f0b322cc38f2_build_36_225
```

* [How To Build A Specific Project From A Solution with MSBuild](Deploying Visual Studio 2017 Function Projects with VSTS)

## 2. ターゲットを sln ではなく、xxproj ファイルにする

上記のものは、`functions.sln` がターゲットになっているが、ソリューションではなく、個別のプロジェクトを指定してビルドをしてもよい。結果として、

```
bin
  Debug               <- bulid configuration
    net461
Obj
  Debug               <- build configuration
    net461
```

といった構造がプロジェクトごとに作られて、DLL 等が格納される。

## 3. sln ファイルの該当プロジェクトをコメントアウトする

ビルドすると見当たらないものが見つかるようなプロジェクトはコメントアウトしても上記と同じ結果が得られるが、お勧めはしない。バッドノウハウだ。

```
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Telemetry", "Telemetry\Telemetry.csproj", "{7556824E-51EE-40F8-8A83-BCAE8975E7A6}"
EndProject
# Project("{E6C563F2-BDC2-4A24-ACBF-01715157DCBB}") = "ASAApplication", "ASAApplication\ASAApplication.asaproj", "{8741C1EE-B4E6-4620-80F4-FFFBF4B89019}"
# EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Telemetry.Test", "Telemetry.Test\Telemetry.Test.csproj", "{BAA6AC78-59C2-4472-ABE4-4064C38DDD07}"
EndProject
# Project("{00D1A9C2-B5F0-4AF3-8072-F6C62B433612}") = "ASASQL", "ASASQL\ASASQL.sqlproj", "{C018046A-28D0-4B9F-AF1B-2A0CC6B8FD6F}"
# EndProject
```

# テンプレートのオプションを解析してみる

ついでなので、VSTS のテンプレートについてくる、Azure Functions をデプロイするときのビルドのオプションをみてみよう。

```
/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:DesktopBuildPackageLocation="$(build.artifactstagingdirectory)\WebApp.zip" /p:DeployIisAppPath="Default Web Site" /t:"Telemetry:Clean;Build";"Telemetry_Test:Clean;Build"
```

## /p

プロパティの書き換え。例えば次のような感じ。`/p` = `/property` プロパティの区切りは `;` 

```
/property:WarningLevel=2;OutDir=bin\Debug
```
### DeployOnBuild 
ビルドの際に、パッケージ化/デプロイされるようにする指定
### WebPublishMethod
デプロイメントパッケージの場合は `Package` を指定。ほかには file system などがある。
### PackageAsSingleFile
アウトプットを、１つのzip にまとめ上げる指定
### SkipInvalidConfigrations
不正なコンフィグを無視する指定
### DesktopBuildPackageLocation
zip ファイルの場所を指定している
### DeployIisAppPath
おそらく、デプロイ先の IIS のパス指定。

__NOTE: ここはまだよくわかっていないので継続調査__

[How to package a web project for deployment from the command line](https://blogs.endjin.com/2016/10/how-to-package-a-web-project-for-deployment-from-the-command-line/)

## /t

先ほど解説したターゲットプロジェクトの指定


* [MSBuild Command-Line Reference](https://msdn.microsoft.com/en-us/library/ms164311.aspx)

# まとめ

簡単だが、ビルド周りに関する基礎知識をまとめてみた。実は、Azure Functions の push 型デプロイは完成しているのだが、それはまた次回以降で。

# Resource

* [Solutions and Projects in Visual Studio](https://msdn.microsoft.com/en-us/library/b142f8e7.aspx)
* [Deploying Visual Studio 2017 Function Projects with VSTS](https://blogs.msdn.microsoft.com/appserviceteam/2017/06/01/deploying-visual-studio-2017-function-projects-with-vsts/)