簡體   English   中英

如何隱藏Visual Studio中自定義工具生成的文件

[英]How to hide files generated by custom tool in Visual Studio

我想隱藏我的自定義工具生成的文件,但我找不到任何關於如何完成此操作的文檔。

我正在尋找的一個例子是文件背后的WPF代碼。 這些文件不會顯示在Visual Studio項目視圖中,而是使用項目進行編譯,並在IntelliSense中可用。 文件后面的WPF代碼(例如,Window1.gics)由自定義工具生成。

解決方案是創建一個Target,將您的文件添加到Compile ItemGroup,而不是在.csproj文件中顯式添加它們。 這樣,Intellisense將會看到它們並將它們編譯到您的可執行文件中,但它們不會顯示在Visual Studio中。

簡單的例子

您還需要確保將目標添加到CoreCompileDependsOn屬性,以便它在編譯器運行之前執行。

這是一個非常簡單的例子:

<PropertyGroup>
  <CoreCompileDependsOn>$(CoreCompileDependsOn);AddToolOutput</CoreCompileDependsOn>
</PropertyGroup>

<Target Name="AddToolOutput">
  <ItemGroup>
    <Compile Include="HiddenFile.cs" />
  </ItemGroup>
</Target>

如果將其添加到.csproj文件的底部(就在</Project>之前),則“HiddenFile.cs”將包含在您的編譯中,即使它沒有出現在Visual Studio中。

使用單獨的.targets文件

您通常將它放在單獨的.targets文件中,而不是直接將其放在.csproj文件中,該文件包含在:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  ...
</Project>

並使用<Import Project="MyTool.targets"> .csproj。 即使是一次性案例,也建議使用.targets文件,因為它將自定義代碼與Visual Studio維護的.csproj中的內容分開。

構造生成的文件名

如果要創建通用工具和/或使用單獨的.targets文件,則可能不希望顯式列出每個隱藏文件。 相反,您希望從項目中的其他設置生成隱藏文件名。 例如,如果您希望所有資源文件在“obj”目錄中具有相應的工具生成文件,那么您的目標將是:

<Target Name="AddToolOutput">
  <ItemGroup>
    <Compile Include="@(Resource->'$(IntermediateOutputPath)%(FileName)%(Extension).g.cs')" />
  </ItemGroup>
</Target>

“IntermediateOutputPath”屬性是我們所知的“obj”目錄,但如果.targets的最終用戶已經自定義了這個,那么您的中間文件將在同一個地方找到。 如果您希望生成的文件位於主項目目錄中而不是“obj”目錄中,則可以將其保留為關閉狀態。

如果您只想要自定義工具處理現有項類型的某些文件? 例如,您可能希望為所有具有“.xyz”擴展名的頁面和資源文件生成文件。

<Target Name="AddToolOutput">
  <ItemGroup>
    <MyToolFiles Include="@(Page);@(Resource)" Condition="'%(Extension)'=='.xyz' />
    <Compile Include="@(MyToolFiles->'$(IntermediateOutputPath)%(FileName)%(Extension).g.cs')"/>
  </ItemGroup>
</Target>

請注意,您不能在頂級ItemGroup中使用%(擴展)等元數據語法,但您可以在Target中執行此操作。

使用自定義項類型(又名構建操作)

上面的處理文件具有現有的項目類型,如頁面,資源或編譯(Visual Studio將其稱為“構建操作”)。 如果您的商品是新類型的商品,則可以使用自己的自定義商品類型。 例如,如果您的輸入文件被稱為“Xyz”文件,則您的項目文件可以將“Xyz”定義為有效的項類型:

<ItemGroup>
  <AvailableItemName Include="Xyz" />
</ItemGroup>

之后,Visual Studio將允許您在文件屬性的Build Action中選擇“Xyz”,從而將其添加到.csproj中:

<ItemGroup>
  <Xyz Include="Something.xyz" />
</ItemGroup>

現在,您可以使用“Xyz”項類型為工具輸出創建文件名,就像之前使用“Resource”項類型一樣:

<Target Name="AddToolOutput">
  <ItemGroup>
    <Compile Include="@(Xyz->'$(IntermediateOutputPath)%(FileName)%(Extension).g.cs')" />
  </ItemGroup>
</Target>

使用自定義項目類型時,您可以通過將項目映射到另一個項目類型(也稱為構建操作)來使您的項目也由內置機制處理。 如果您的“Xyz”文件確實是.cs文件或.xaml,或者如果需要它們,則此選項非常有用

EmbeddedResources。 例如,您可以編譯具有Xyz“Build Action”的所有文件:

<ItemGroup>
  <Compile Include="@(Xyz)" />
</ItemGroup>

或者,如果您的“Xyz”源文件應存儲為嵌入式資源,您可以這樣表達:

<ItemGroup>
  <EmbeddedResource Include="@(Xyz)" />
</ItemGroup>

請注意,如果將其放在Target中,則第二個示例將不起作用,因為直到核心編譯之前才對目標進行求值。 要在Target中進行此工作,您必須在PrepareForBuildDependsOn屬性中列出目標名稱而不是CoreCompileDependsOn。

從MSBuild調用自定義代碼生成器

除了創建.targets文件之外,您可以考慮直接從MSBuild調用工具,而不是使用單獨的預構建事件或Visual Studio有缺陷的“自定義工具”機制。

去做這個:

  1. 創建一個類庫項目,引用Microsoft.Build.Framework
  2. 添加代碼以實現自定義代碼生成器
  3. 添加一個實現ITask的類,並在Execute方法中調用自定義代碼生成器
  4. 在.targets文件中添加一個UsingTask元素,並在目標中添加對新任務的調用

以下是實現ITask所需的全部內容:

public class GenerateCodeFromXyzFiles : ITask
{
  public IBuildEngine BuildEngine { get; set; }
  public ITaskHost HostObject { get; set; }

  public ITaskItem[] InputFiles { get; set; }
  public ITaskItem[] OutputFiles { get; set; }

  public bool Execute()
  {
    for(int i=0; i<InputFiles.Length; i++)
      File.WriteAllText(OutputFiles[i].ItemSpec,
        ProcessXyzFile(
          File.ReadAllText(InputFiles[i].ItemSpec)));
  }

  private string ProcessXyzFile(string xyzFileContents)
  {
    // Process file and return generated code
  }
}

這里是UsingTask元素和一個調用它的Target:

<UsingTask TaskName="MyNamespace.GenerateCodeFromXyzFiles" AssemblyFile="MyTaskProject.dll" />


<Target Name="GenerateToolOutput">

  <GenerateCodeFromXyzFiles
      InputFiles="@(Xyz)"
      OutputFiles="@(Xyz->'$(IntermediateOutputPath)%(FileName)%(Extension).g.cs')">

    <Output TaskParameter="OutputFiles" ItemGroup="Compile" />

  </GenerateCodeFromXyzFiles>
</Target>

請注意,此目標的Output元素將輸出文件列表直接放入Compile中,因此無需使用單獨的ItemGroup來執行此操作。

舊的“自定義工具”機制是如何存在缺陷的,為什么不使用它

關於Visual Studio的“自定義工具”機制的注釋:在.NET Framework 1.x中,我們沒有MSBuild,因此我們不得不依賴Visual Studio來構建我們的項目。 為了在生成的代碼上獲取Intellisense,Visual Studio有一個名為“自定義工具”的機制,可以在文件的“屬性”窗口中進行設置。 該機制在幾個方面存在根本缺陷,這就是它被MSBuild目標取代的原因。 “自定義工具”功能的一些問題是:

  1. 每當編輯和保存文件時,“自定義工具”都會構造生成的文件,而不是在編譯項目時。 這意味着在外部修改文件的任何內容(例如修訂控制系統)都不會更新生成的文件,並且您經常會在可執行文件中獲得過時的代碼。
  2. 除非您的收件人同時擁有Visual Studio和“自定義工具”,否則“自定義工具”的輸出必須隨源樹一起提供。
  3. “自定義工具”必須安裝在注冊表中,不能簡單地從項目文件中引用。
  4. “自定義工具”的輸出未存儲在“obj”目錄中。

如果您使用舊的“自定義工具”功能,我強烈建議您切換到使用MSBuild任務。 它可以很好地與Intellisense一起使用,並允許您在不安裝Visual Studio的情況下構建項目(您只需要.NET Framework)。

您的自定義構建任務何時運行?

通常,您的自定義構建任務將運行:

  • 在Visual Studio打開解決方案的后台,如果生成的文件不是最新的
  • 在后台隨時保存Visual Studio中的一個輸入文件
  • 如果生成的文件不是最新的,那么無論何時構建
  • 任何時候你重建

更確切地說:

  1. Visual Studio啟動時以及每次在Visual Studio中保存任何文件時都會運行IntelliSense增量構建。 如果輸出文件丟失,這將運行您的生成器任何輸入文件都比生成器輸出更新。
  2. 只要在Visual Studio中使用任何“構建”或“運行”命令(包括菜單選項並按F5),或從命令行運行“MSBuild”,就會運行常規增量構建。 與IntelliSense增量構建一樣,如果生成的文件不是最新的,它也將僅運行您的生成器
  3. 只要在Visual Studio中使用任何“重建”命令,或者從命令行運行“MSBuild / t:Rebuild”,就會運行常規完整構建。 如果有任何輸入或輸出,它將始終運行您的發電機。

您可能希望強制您的生成器在其他時間運行,例如某些環境變量更改時,或強制它在后台同步運行。

  • 要使生成器重新運行,即使沒有輸入文件已更改,最好的方法通常是向目標添加一個額外的輸入,這是一個存儲在“obj”目錄中的虛擬輸入文件。 然后,每當環境變量或某些外部設置發生變化時,應該強制生成器工具重新運行,只需觸摸此文件(即創建它或更新其修改日期)。

  • 要強制生成器同步運行而不是等待IntelliSense在后台運行它,只需使用MSBuild構建您的特定目標。 這可以像執行“MSBuild / t:GenerateToolOutput”一樣簡單,或者VSIP可以提供調用自定義構建目標的內置方式。 或者,您只需調用Build命令並等待它完成。

請注意,本節中的“輸入文件”是指Target元素的“Inputs”屬性中列出的內容。

最后的筆記

您可能會收到來自Visual Studio的警告,它不知道是否信任您的自定義工具.targets文件。 要解決此問題,請將其添加到HKEY_LOCAL_MACHINE \\ SOFTWARE \\ Microsoft \\ VisualStudio \\ 9.0 \\ MSBuild \\ SafeImports注冊表項。

以下是所有部分的實際.targets文件的概述:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <PropertyGroup>
    <CoreCompileDependsOn>$(CoreCompileDependsOn);GenerateToolOutput</CoreCompileDependsOn>
  </PropertyGroup>

  <UsingTask TaskName="MyNamespace.GenerateCodeFromXyzFiles" AssemblyFile="MyTaskProject.dll" />


  <Target Name="GenerateToolOutput" Inputs="@(Xyz)" Outputs="@(Xyz->'$(IntermediateOutputPath)%(FileName)%(Extension).g.cs')">

    <GenerateCodeFromXyzFiles
        InputFiles="@(Xyz)"
        OutputFiles="@(Xyz->'$(IntermediateOutputPath)%(FileName)%(Extension).g.cs')">

      <Output TaskParameter="OutputFiles" ItemGroup="Compile" />

    </GenerateCodeFromXyzFiles>
  </Target>

</Project>

如果您有任何問題或者您有任何不明白的地方,請告訴我。

要從Visual Studio隱藏項目,請向項目添加Visible元數據屬性。 InProject元數據顯然也是這樣做的。

可見: http//msdn.microsoft.com/en-us/library/ms171468(VS.90).aspx

InProject: http//blogs.msdn.com/b/jomo_fisher/archive/2005/01/25/360302.aspx

<ItemGroup>
  <Compile Include="$(AssemblyInfoPath)">
    <!-- either: -->
    <InProject>false</InProject>
    <!-- or: -->
    <Visible>false</Visible>
  </Compile>
</ItemGroup>

我知道這樣做的唯一方法是在proj文件中添加生成的文件以依賴於您希望它隱藏在后面的文件。

例如:

 <ItemGroup>
    <Compile Include="test.cs" />
    <Compile Include="test.g.i.cs">
      <DependentUpon>test.cs</DependentUpon>
    </Compile>
  </ItemGroup>

如果您刪除了DependentUpon元素,那么該文件會顯示在另一個文件旁邊而不是它后面...您的生成器如何添加文件? 你能告訴我們用例以及你希望它如何工作嗎?

我想你想看看這里: http//msdn.microsoft.com/en-us/library/ms171453.aspx

具體來說,“執行期間創建項目”部分。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM