本文告訴大家如何在 MSBuild 裏使用 Copy 複製文件
需要知道 Rosyln 是 MSBuild 的 dotnet core 版本。
在 MSBuild 裏可以使用很多命令,本文告訴大家如何使用 Copy 這個 Task 來複制文件
在開始本文之前,希望大家已經知道了一些關於 csproj 文件格式,如果還是不知道,請看理解 C# 項目 csproj 文件格式的本質和編譯流程 - walterlv
最簡單的複製命令請看代碼
<Copy SourceFiles="lindexi.txt" DestinationFolder="LetirNuhe\" ></Copy>
需要注意,不要把 Copy 直接寫在 Project 下,如下面的代碼
<Project Sdk="Microsoft.NET.Sdk"> <!-- 忽略代碼 --> <Copy SourceFiles="lindexi.txt" DestinationFolder="LetirNuhe\" ></Copy> </Project>
就會出現下面異常
D:\林德熙\代碼\測試代碼\CemfeetoQewasXaiki\CemfeetoQewasXaiki.csproj : error : 無法識別元素 <Project> 下面的元素 <Copy>。 D:\林德熙\代碼\測試代碼\CemfeetoQewasXaiki
爲了運行 Copy 需要使用下面代碼
<Target Name="Copy" BeforeTargets="CoreCompile"> <Copy SourceFiles="LekeexelSurgooHerkassayyayTowjome.txt" DestinationFolder="LetirNuhe\"></Copy> </Target>
需要知道 Target 需要給 Name 並且告訴他在什麼時候運行,這裏使用 BeforeTargets 告訴在開始編譯前,也就是複製的文件會被編譯。
對於複製資源文件或需要編譯的資源,就設置 BeforeTargets 在編譯前,如果是不需要進行編譯的文件,如 dll 就可以設置在編譯後運行。
重新生成項目,可以看到文件夾存在文件
如果剛纔沒有創建 文件,複製時找不到文件,就會出現在重新編譯出現無法編譯
error MSB3030: 無法複製文件“lindexi.txt”,原因是找不到該文件
複製有多個方式,下面讓我來一個個和大家說
文件到文件
第一個方法是最簡單的,複製文件到文件
例如我需要複製 lindiexi.txt 到 LetirNuhe\lindexi
,可以使用下面代碼
<Target Name="Copy" BeforeTargets="CoreCompile"> <Copy SourceFiles="lindexi.txt" DestinationFiles="LetirNuhe\lindexi.txt"></Copy> </Target>
那麼如果需要複製多個文件到多個文件?
可以看到 SourceFiles 是可以輸入多個文件,只需要使用;
作爲多個文件
下面複製 lindexi.txt
和 lindexi.gitee.io.txt
到 LetirNuhe
文件夾下
<Target Name="Copy" BeforeTargets="CoreCompile"> <Copy SourceFiles="lindexi.txt;lindexi.gitee.io.txt" DestinationFiles="LetirNuhe\lindexi.txt;LetirNuhe\lindexi.gitee.io.txt"></Copy> </Target>
這裏的文件是對應的,也就是第一個文件是 lindexi.txt
在 DestinationFiles 也需要寫第一個文件是lindexi.txt
的,如果寫爲lindexi2.txt
會自動把 lindexi.txt
複製並且修改名字。第一個文件對應 DestinationFiles 寫的第一個文件,也就是項對應。
因爲從文件複製到文件的代碼太多了,如果只是需要把文件都放在相同的文件夾,可以使用下面的方法
文件到文件夾
如果需要把文件都複製到相同的文件夾,可以使用下面代碼
<Target Name="Copy" BeforeTargets="CoreCompile"> <Copy SourceFiles="lindexi.txt;lindexi.gitee.io.txt" DestinationFolder="LetirNuhe\"></Copy> </Target>
使用 DestinationFolder 指定文件夾,在文件夾不存在的時候會自動創建,剛纔的代碼也是。
文件列表到文件夾
實際上剛纔是寫 SourceFiles ,但是實際這樣寫無法使用通配,也就是*.txt
的方法,如果需要使用就需要用文件列表
<ItemGroup> <Txt Include="*.txt"></Txt> </ItemGroup> <Target Name="Copy" BeforeTargets="CoreCompile"> <Copy SourceFiles="@(Txt)" DestinationFolder="LetirNuhe\"></Copy> </Target>
多個文件的列表是在 ItemGroup 裏添加 一個新的標籤,這個標籤是可以自己定義名字的,我這裏定義了 Txt ,讓他包含了 *.txt
,現在就可以在 SourceFiles 使用。使用數組的方法是 @(Txt)
,通過 @ 和 標籤名就可以拿到標籤的文件。如果這時輸出@(Txt)
會看到下面代碼
xx\lindexi.txt;xx\lindexi.gitee.io.txt
因爲 ItemGroup 可以寫多個標籤,可以修改下面代碼
<ItemGroup> <Txt Include="lindexi.txt"></Txt> <Txt Include="lindexi.gitee.io.txt"></Txt> </ItemGroup> <Target Name="Copy" BeforeTargets="CoreCompile"> <Copy SourceFiles="@(Txt)" DestinationFolder="LetirNuhe\"></Copy> </Target>
較新才複製
如果不想每次編譯都複製,可以設置SkipUnchangedFiles="True"
只有在發現文件較新才複製。
判斷文件較新使用的是判斷兩個文件的最後更改時間和文件大小。
軟連接
可以通過設置 UseHardlinksIfPossible="True"
不復制文件,而是設置文件的軟連接,也就是修改一個文件可以兩個地方生效
設置軟連接可以做到在多個項目看起來都有自己的文件,但是實際都是指向相同的文件
需要說的是,這個是軟連接,但是在系統是硬連接方式。
判斷文件存在就不復制
如果需要判斷文件存在就不復制,可以使用 Condition
判斷
<Copy SourceFiles="@(Txt)" DestinationFolder="LetirNuhe\" SkipUnchangedFiles="True" OverwriteReadOnlyFiles="True" Condition="!Exists('LetirNuhe\lindexi.txt')"></Copy>
通過 Exists
判斷文件是否存在,如果存在就不復制。
更多 MSBuild 相關博客請看
理解 C# 項目 csproj 文件格式的本質和編譯流程 - walterlv
如何創建一個基於命令行工具的跨平臺的 NuGet 工具包 - walterlv
如何使用 MSBuild Target(Exec)中的控制檯輸出 - walterlv
更多關於 Roslyn 請看 手把手教你寫 Roslyn 修改編譯