Search Unity

Unity strips out NuGet properties on csproj files

Discussion in 'Scripting' started by jwvanderbeck, May 27, 2017.

  1. jwvanderbeck

    jwvanderbeck

    Joined:
    Dec 4, 2014
    Posts:
    825
    With VisualStudio 2017, Microsoft is moving to embrace the NuGet packaging system and there are very useful utilities in there for Unity developers such as StyleCop and hundreds of others.

    Unfortunately Unity is stripping this data out of the CSPROJ file when it updates the project, so despite the NuGet package still technically being installed in the Solution, it no longer functions.

    Here is an example of the changes in Unified Patch format:
    @@ -624,13 +622,7 @@
    <None Include="Assets\TerrainComposer\Update\WorldComposer\last_checked.txt" />
    <None Include="Assets\TerrainComposer\Update\WorldComposer\version.txt" />
    <None Include="Assets\TerrainComposer\WorldComposer Release Notes.txt" />
    - <None Include="packages.config" />
    - </ItemGroup>
    - <ItemGroup>
    - <Analyzer Include="packages\StyleCop.Analyzers.1.0.0\analyzers\dotnet\cs\Newtonsoft.Json.dll" />
    - <Analyzer Include="packages\StyleCop.Analyzers.1.0.0\analyzers\dotnet\cs\StyleCop.Analyzers.CodeFixes.dll" />
    - <Analyzer Include="packages\StyleCop.Analyzers.1.0.0\analyzers\dotnet\cs\StyleCop.Analyzers.dll" />
    </ItemGroup>
    <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
    <Target Name="GenerateTargetFrameworkMonikerAttribute" />
    -</Project>
    \ No newline at end of file
    +</Project>

    And visually:
     
  2. jwvanderbeck

    jwvanderbeck

    Joined:
    Dec 4, 2014
    Posts:
    825
    Ok I finally managed to hack my way around this issue using the code below. This is obviously a very brittle fix, as it will only work for StyleCop specifically and only this version specifically. If StyleCop gets updated I will need to change the code.

    I **strongly** urge Unity Tech to consider better handling these as the usage of NuGet is only going to become more and more prevalent moving forward.

    For anyone needing to work around this issue for now, see the code below and adjust as needed. A couple of things I learned doing this (besides how painful working with xml is):
    1) Every element added needs an explicit namespace that matches the root document. If it doesn't then it will automatically get an xmlns="" attribute added which will break Visual Studio.
    2) For a long while I thought my changes weren't having any effect. Closing Visual Studio, and deleting the sln and csproj files forced Unity to do a full rebuild and that fixed it.

    Code (CSharp):
    1. using System;
    2. using System;
    3. using System.IO;
    4. using System.Linq;
    5. using System.Text;
    6. using System.Xml.Linq;
    7.  
    8. using UnityEngine;
    9. using UnityEditor;
    10.  
    11. using SyntaxTree.VisualStudio.Unity.Bridge;
    12.  
    13. [InitializeOnLoad]
    14. public class ProjectFileHook
    15. {
    16.     // necessary for XLinq to save the xml project file in utf8  
    17.     class Utf8StringWriter : StringWriter
    18.     {
    19.         public override Encoding Encoding
    20.         {
    21.             get { return Encoding.UTF8; }
    22.         }
    23.     }
    24.  
    25.     static ProjectFileHook()
    26.     {
    27.         ProjectFilesGenerator.ProjectFileGeneration += (string name, string content) =>
    28.         {
    29.             // parse the document and make some changes  
    30.             var document = XDocument.Parse(content);
    31.             //document.Root.Add(new XComment("FIX ME"));
    32.             XNamespace xmlns = "http://schemas.microsoft.com/developer/msbuild/2003";
    33.             XElement itemGroup = new XElement(xmlns + "ItemGroup");
    34.             itemGroup.Add(new XElement(xmlns + "Analyzer", new XAttribute("Include", "packages\\StyleCop.Analyzers.1.0.0\\analyzers\\dotnet\\cs\\Newtonsoft.Json.dll")));
    35.             itemGroup.Add(new XElement(xmlns + "Analyzer", new XAttribute("Include", "packages\\StyleCop.Analyzers.1.0.0\\analyzers\\dotnet\\cs\\StyleCop.Analyzers.CodeFixes.dll")));
    36.             itemGroup.Add(new XElement(xmlns + "Analyzer", new XAttribute("Include", "packages\\StyleCop.Analyzers.1.0.0\\analyzers\\dotnet\\cs\\StyleCop.Analyzers.dll")));
    37.             document.Root.Add(itemGroup);
    38.  
    39.             // save the changes using the Utf8StringWriter  
    40.             var str = new Utf8StringWriter();
    41.             document.Save(str);
    42.  
    43.             return str.ToString();
    44.         };
    45.     }
    46. }
    47.  
     
    Last edited: May 27, 2017
  3. jwvanderbeck

    jwvanderbeck

    Joined:
    Dec 4, 2014
    Posts:
    825
    Updated version of the script for StyleCop.Analyzers 1.0.2, as well as a few improvements:
    • Adds the stylecop.json config file to the projects
    • Wraps everything in a platform specific #if block so it won't cause issues if opened on say a Mac

    Code (CSharp):
    1.  
    2. #if UNITY_EDITOR_WIN
    3. using System.IO;
    4. using System.Text;
    5. using System.Xml.Linq;
    6. using SyntaxTree.VisualStudio.Unity.Bridge;
    7. using UnityEditor;
    8.  
    9. /// <summary>
    10. /// Provide a hook into Unity's Project File Generation so that StyleCop gets re-added each time
    11. /// </summary>
    12. [InitializeOnLoad]
    13. public class ProjectFileHook
    14. {
    15.     static ProjectFileHook()
    16.     {
    17.         ProjectFilesGenerator.ProjectFileGeneration += (string name, string content) =>
    18.         {
    19.             // parse the document and make some changes
    20.             var document = XDocument.Parse(content);
    21.             XNamespace xmlns = "http://schemas.microsoft.com/developer/msbuild/2003";
    22.             XElement itemGroup = new XElement(xmlns + "ItemGroup");
    23.             itemGroup.Add(new XElement(xmlns + "Analyzer", new XAttribute("Include", "packages\\StyleCop.Analyzers.1.0.2\\analyzers\\dotnet\\cs\\StyleCop.Analyzers.CodeFixes.dll")));
    24.             itemGroup.Add(new XElement(xmlns + "Analyzer", new XAttribute("Include", "packages\\StyleCop.Analyzers.1.0.2\\analyzers\\dotnet\\cs\\StyleCop.Analyzers.dll")));
    25.             document.Root.Add(itemGroup);
    26.             document.Root.Add(new XElement(xmlns + "ItemGroup", new XElement(xmlns + "AdditionalFiles", new XAttribute("Include", "stylecop.json"))));
    27.  
    28.             // save the changes using the Utf8StringWriter
    29.             var str = new Utf8StringWriter();
    30.             document.Save(str);
    31.  
    32.             return str.ToString();
    33.         };
    34.     }
    35.  
    36.     // necessary for XLinq to save the xml project file in utf8
    37.     private class Utf8StringWriter : StringWriter
    38.     {
    39.         public override Encoding Encoding
    40.         {
    41.             get { return Encoding.UTF8; }
    42.         }
    43.     }
    44. }
    45. #endif
    46.  
     
    ChessMax likes this.
  4. GlitchEnzo2

    GlitchEnzo2

    Joined:
    Nov 15, 2012
    Posts:
    5
    Selzier and nirvanajie like this.
  5. jwvanderbeck

    jwvanderbeck

    Joined:
    Dec 4, 2014
    Posts:
    825
  6. OFFIS_sebastian

    OFFIS_sebastian

    Joined:
    Sep 11, 2017
    Posts:
    2
    I tried using this to fix this problem, but it just led to a duplicate Systeml.Xml.XmlDocument definition. Do I have to seperate my VS Solution from Unity in order to get around this?