Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Local package and directory structure in Visual Studio solution

Discussion in 'Package Manager' started by Tom-Atom, Jul 10, 2019.

  1. Tom-Atom

    Tom-Atom

    Joined:
    Jun 29, 2014
    Posts:
    153
    Hi,

    I successfully created local package with some common code and added it into my project. I can also see it in Visual Studio 2017 solution of that project. Unfortunately, folder structure is not preserved.

    Original folder structure looks like this on disk:
    FolderStructure.png

    Structure of package in Visual Studio (inside solution of Unity project using package) looks like this:
    VSStructure.png

    Is there any way how to preserve structure of folders?
    Is it Unity problem (like generating .csproj file for package)? Or is it VS problem?


    OK... I played a bit with .csproj file befeore sending this question and it seems that .csproj can be generated better to preserve folders.
    Currently, this is how part of .csproj file looks:

    <ItemGroup>
    <Compile Include="D:\MyUnityApps\_Common_\BSPMapGenerator\Runtime\AbstractGeneratorRequest.cs" />
    <Compile Include="D:\MyUnityApps\_Common_\BSPMapGenerator\Runtime\AbstractMapGenerator.cs" />
    <Compile Include="D:\MyUnityApps\_Common_\BSPMapGenerator\Runtime\BspNode.cs" />
    <Compile Include="D:\MyUnityApps\_Common_\BSPMapGenerator\Runtime\BspNodeFactory.cs" />
    <Compile Include="D:\MyUnityApps\_Common_\BSPMapGenerator\Runtime\Interfaces\IBspNodeFactory.cs" />
    <Compile Include="D:\MyUnityApps\_Common_\BSPMapGenerator\Runtime\Interfaces\IGeneratorRequest.cs" />
    <Compile Include="D:\MyUnityApps\_Common_\BSPMapGenerator\Runtime\Interfaces\IGeneratorResult.cs" />
    <Compile Include="D:\MyUnityApps\_Common_\BSPMapGenerator\Runtime\ScriptableObjects\AbstractGeneratorParameters.cs" />


    If it was generated with small change like this:

    <ItemGroup>
    <Compile Include="D:\MyUnityApps\_Common_\BSPMapGenerator\Runtime\AbstractGeneratorRequest.cs">
    <Link>Runtime\AbstractGeneratorRequest.cs</Link>
    </Compile>
    <Compile Include="D:\MyUnityApps\_Common_\BSPMapGenerator\Runtime\AbstractMapGenerator.cs" >
    <Link>Runtime\AbstractMapGenerator.cs</Link>
    </Compile>
    <Compile Include="D:\MyUnityApps\_Common_\BSPMapGenerator\Runtime\BspNode.cs" >
    <Link>Runtime\BspNode.cs</Link>
    </Compile>
    <Compile Include="D:\MyUnityApps\_Common_\BSPMapGenerator\Runtime\BspNodeFactory.cs" >
    <Link>Runtime\BspNodeFactory.cs</Link>
    </Compile>
    <Compile Include="D:\MyUnityApps\_Common_\BSPMapGenerator\Runtime\Interfaces\IBspNodeFactory.cs" >
    <Link>Runtime\Interfaces\IBspNodeFactory.cs</Link>
    </Compile>
    <Compile Include="D:\MyUnityApps\_Common_\BSPMapGenerator\Runtime\Interfaces\IGeneratorRequest.cs" >
    <Link>Runtime\Interfaces\IGeneratorRequest.cs</Link>
    </Compile>
    <Compile Include="D:\MyUnityApps\_Common_\BSPMapGenerator\Runtime\Interfaces\IGeneratorResult.cs" >
    <Link>Runtime\Interfaces\IGeneratorResult.cs</Link>
    </Compile>
    <Compile Include="D:\MyUnityApps\_Common_\BSPMapGenerator\Runtime\ScriptableObjects\AbstractGeneratorParameters.cs" >
    <Link>Runtime\ScriptableObjects\AbstractGeneratorParameters.cs</Link>
    </Compile>


    Then code structure in Visual Studio looks like this - with folders srtucture preserved:
    NiceVSStructure.png

    Unfortunately, it is not preseved between Unity launches - next time, you run Unity, it is overwritten with original version without Link tag...

    Any chance you could change how .csproj is generated?
     
    Last edited: Jul 10, 2019
    Egad_McDad and littledwarf like this.
  2. okcompute_unity

    okcompute_unity

    Joined:
    Jan 16, 2017
    Posts:
    756
    Hi @Tom-Atom ,

    Let's me reach out the Microsoft's VSTU team and see what we can do.

    Regards,

    Pascal
     
  3. okcompute_unity

    okcompute_unity

    Joined:
    Jan 16, 2017
    Posts:
    756
  4. Tom-Atom

    Tom-Atom

    Joined:
    Jun 29, 2014
    Posts:
    153
    Hi Pascal,

    thanks for answer! Yes, it helped. I created little script - anyone interested can get it below (must be placed in Editor folder, name it as you want). It adds Link element under Compile elements in .csproj file.

    Here are some important points:
    - it is called on every .csproj in project. I am using it only for my custom packages and name of all these begin with SBC, like SBC.BSPMapGenerator. ProcessFile method decides, which .csproj files need adjustemtns (those, that begin with "SBC."
    - value of added link attribute is part of original file path. To know, which part of path to cut out I am looking for part beginning either with \Editor\ or \Runtime\. These are folder names recommended by Unity (see https://docs.unity3d.com/Manual/cus-layout.html)

    Best Regards,
    Tomas

    Code (CSharp):
    1. #if ENABLE_VSTU
    2.  
    3. using System.IO;
    4. using System.Text;
    5. using System.Xml.Linq;
    6.  
    7. using UnityEditor;
    8.  
    9. using SyntaxTree.VisualStudio.Unity.Bridge;
    10. using System.Text.RegularExpressions;
    11. using System.Collections.Generic;
    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.         // -----------------------------------------------------------
    20.         public override Encoding Encoding {
    21.             get { return Encoding.UTF8; }
    22.         }
    23.     }
    24.  
    25.     // -----------------------------------------------------------
    26.     static ProjectFileHook() {
    27.  
    28.         ProjectFilesGenerator.ProjectFileGeneration += (string name, string content) => {
    29.  
    30.             // process only file beginning SBC - my custom packages
    31.             if (!ProcessFile(name)) {
    32.                 return content;
    33.             }
    34.  
    35.             // parse the document and make some changes
    36.             XDocument document = XDocument.Parse(content);
    37.             AdjustDocument(document);
    38.  
    39.             // save the changes using the Utf8StringWriter
    40.             Utf8StringWriter str = new Utf8StringWriter();
    41.             document.Save(str);
    42.  
    43.             return str.ToString();
    44.         };
    45.     }
    46.  
    47.     // -----------------------------------------------------------
    48.     static bool ProcessFile(string name) {
    49.  
    50.         Regex regex = new Regex(@"[\/\\]SBC\..*\.csproj$");
    51.         Match match = regex.Match(name);
    52.  
    53.         return match.Success;
    54.     }
    55.  
    56.     // -----------------------------------------------------------
    57.     static void AdjustDocument(XDocument document) {
    58.  
    59.         // get namespace of document
    60.         XNamespace ns = document.Root.Name.Namespace;
    61.  
    62.         // get all Compile elements
    63.         IEnumerable<XElement> compileElements = document.Root.Descendants(ns + "Compile");
    64.  
    65.         // regex to find which part of Include attribute of Compile element to use for Link element value
    66.         // check for Editor or Runtime (recommended folders: https://docs.unity3d.com/Manual/cus-layout.html)
    67.         Regex regex = new Regex(@"\\(Runtime|Editor)\\.*\.cs$");
    68.  
    69.         // add child Link element to each Compile element
    70.         foreach (XElement el in compileElements) {
    71.  
    72.             string fileName = el.Attribute("Include").Value;
    73.  
    74.             Match match = regex.Match(fileName);
    75.  
    76.             if (match.Success) {
    77.  
    78.                 // substr from 1 to exclude initial slash character
    79.                 XElement link = new XElement(ns + "Link") {
    80.                     Value = match.Value.Substring(1)
    81.                 };
    82.  
    83.                 el.Add(link);
    84.             }
    85.         }
    86.     }
    87. }
    88.  
    89. #endif
    90.  
     
    Last edited: Jul 12, 2019
    Egad_McDad, ribbanya, orca2 and 2 others like this.
  5. Art-bit

    Art-bit

    Joined:
    Feb 21, 2018
    Posts:
    2
    Hi,

    Have some of you managed to fix namespaces?

    I faced the same problem with folders structure and solved by the script you posted above, but now another issue: all subfolders are simply ignored by R# and for every single file it suggests the root namespace (expected "root.subfolder" instead)
     
  6. Tom-Atom

    Tom-Atom

    Joined:
    Jun 29, 2014
    Posts:
    153
    R# is Resharper? I am sorry, but I have no experience with it...
     
  7. Art-bit

    Art-bit

    Joined:
    Feb 21, 2018
    Posts:
    2
    yes. I think this is a minor issue.
     
  8. guoxx_

    guoxx_

    Joined:
    Mar 16, 2021
    Posts:
    55
  9. guoxx_

    guoxx_

    Joined:
    Mar 16, 2021
    Posts:
    55
    After dig into the "Unity Visual Studio Editor" source code, I found that the fix is actually pretty simple: in ProjectGeneration.cs add "Link" tag if file path is rooted.

    I have attached original and modified files for comparison. Can anyone in Unity help to integrate the fix into official release? BTW, there are several unity package not host there source code in github, it would be nice that if unity put those into github so that developers can make contribution.
     

    Attached Files:

  10. okcompute_unity

    okcompute_unity

    Joined:
    Jan 16, 2017
    Posts:
    756
    Hi @hellkbd,

    I have notified the package owners. They will look into adding this fix in a forthcoming release.

    Thank you!

    Pascal