1070 lines
		
	
	
		
			43 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
		
		
			
		
	
	
			1070 lines
		
	
	
		
			43 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| 
								 | 
							
								using System;
							 | 
						||
| 
								 | 
							
								using System.Collections.Generic;
							 | 
						||
| 
								 | 
							
								using System.IO;
							 | 
						||
| 
								 | 
							
								using System.Linq;
							 | 
						||
| 
								 | 
							
								using System.Security;
							 | 
						||
| 
								 | 
							
								using System.Text;
							 | 
						||
| 
								 | 
							
								using Packages.Rider.Editor.Util;
							 | 
						||
| 
								 | 
							
								using UnityEditor;
							 | 
						||
| 
								 | 
							
								using UnityEditor.Compilation;
							 | 
						||
| 
								 | 
							
								using UnityEngine;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								namespace Packages.Rider.Editor.ProjectGeneration
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  internal class ProjectGeneration : IGenerator
							 | 
						||
| 
								 | 
							
								  {
							 | 
						||
| 
								 | 
							
								    private enum ScriptingLanguage
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      None,
							 | 
						||
| 
								 | 
							
								      CSharp
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /// <summary>
							 | 
						||
| 
								 | 
							
								    /// Map source extensions to ScriptingLanguages
							 | 
						||
| 
								 | 
							
								    /// </summary>
							 | 
						||
| 
								 | 
							
								    private static readonly Dictionary<string, ScriptingLanguage> k_BuiltinSupportedExtensions =
							 | 
						||
| 
								 | 
							
								      new Dictionary<string, ScriptingLanguage>
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        { ".cs", ScriptingLanguage.CSharp },
							 | 
						||
| 
								 | 
							
								        { ".uxml", ScriptingLanguage.None },
							 | 
						||
| 
								 | 
							
								        { ".uss", ScriptingLanguage.None },
							 | 
						||
| 
								 | 
							
								        { ".shader", ScriptingLanguage.None },
							 | 
						||
| 
								 | 
							
								        { ".compute", ScriptingLanguage.None },
							 | 
						||
| 
								 | 
							
								        { ".cginc", ScriptingLanguage.None },
							 | 
						||
| 
								 | 
							
								        { ".hlsl", ScriptingLanguage.None },
							 | 
						||
| 
								 | 
							
								        { ".glslinc", ScriptingLanguage.None },
							 | 
						||
| 
								 | 
							
								        { ".template", ScriptingLanguage.None },
							 | 
						||
| 
								 | 
							
								        { ".raytrace", ScriptingLanguage.None },
							 | 
						||
| 
								 | 
							
								        { ".json", ScriptingLanguage.None},
							 | 
						||
| 
								 | 
							
								        { ".rsp", ScriptingLanguage.None},
							 | 
						||
| 
								 | 
							
								        { ".asmdef", ScriptingLanguage.None},
							 | 
						||
| 
								 | 
							
								        { ".asmref", ScriptingLanguage.None},
							 | 
						||
| 
								 | 
							
								        { ".xaml", ScriptingLanguage.None},
							 | 
						||
| 
								 | 
							
								        { ".tt", ScriptingLanguage.None},
							 | 
						||
| 
								 | 
							
								        { ".t4", ScriptingLanguage.None},
							 | 
						||
| 
								 | 
							
								        { ".ttinclude", ScriptingLanguage.None}
							 | 
						||
| 
								 | 
							
								      };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private string[] m_ProjectSupportedExtensions = Array.Empty<string>();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Note that ProjectDirectory can be assumed to be the result of Path.GetFullPath
							 | 
						||
| 
								 | 
							
								    public string ProjectDirectory { get; }
							 | 
						||
| 
								 | 
							
								    public string ProjectDirectoryWithSlash { get; }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private readonly string m_ProjectName;
							 | 
						||
| 
								 | 
							
								    private readonly IAssemblyNameProvider m_AssemblyNameProvider;
							 | 
						||
| 
								 | 
							
								    private readonly IFileIO m_FileIOProvider;
							 | 
						||
| 
								 | 
							
								    private readonly IGUIDGenerator m_GUIDGenerator;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private readonly Dictionary<string, string> m_ProjectGuids = new Dictionary<string, string>();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // If we have multiple projects, the same assembly references are reused for each. Caching the normalised paths and
							 | 
						||
| 
								 | 
							
								    // names is actually cheaper than recalculating each time, in terms of both time and memory allocations
							 | 
						||
| 
								 | 
							
								    private readonly Dictionary<string, string> m_NormalisedPaths = new Dictionary<string, string>();
							 | 
						||
| 
								 | 
							
								    private readonly Dictionary<string, string> m_AssemblyNames = new Dictionary<string, string>();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    internal static bool isRiderProjectGeneration; // workaround to https://github.cds.internal.unity3d.com/unity/com.unity.ide.rider/issues/28
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    IAssemblyNameProvider IGenerator.AssemblyNameProvider => m_AssemblyNameProvider;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public ProjectGeneration()
							 | 
						||
| 
								 | 
							
								      : this(Directory.GetParent(Application.dataPath).FullName) { }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public ProjectGeneration(string projectDirectory)
							 | 
						||
| 
								 | 
							
								      : this(projectDirectory, new AssemblyNameProvider(), new FileIOProvider(), new GUIDProvider()) { }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public ProjectGeneration(string projectDirectory, IAssemblyNameProvider assemblyNameProvider, IFileIO fileIoProvider, IGUIDGenerator guidGenerator)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      ProjectDirectory = Path.GetFullPath(projectDirectory.NormalizePath());
							 | 
						||
| 
								 | 
							
								      ProjectDirectoryWithSlash = ProjectDirectory + Path.DirectorySeparatorChar;
							 | 
						||
| 
								 | 
							
								      m_ProjectName = Path.GetFileName(ProjectDirectory);
							 | 
						||
| 
								 | 
							
								      m_AssemblyNameProvider = assemblyNameProvider;
							 | 
						||
| 
								 | 
							
								      m_FileIOProvider = fileIoProvider;
							 | 
						||
| 
								 | 
							
								      m_GUIDGenerator = guidGenerator;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /// <summary>
							 | 
						||
| 
								 | 
							
								    /// Syncs the scripting solution if any affected files are relevant.
							 | 
						||
| 
								 | 
							
								    /// </summary>
							 | 
						||
| 
								 | 
							
								    /// <returns>
							 | 
						||
| 
								 | 
							
								    /// Whether the solution was synced.
							 | 
						||
| 
								 | 
							
								    /// </returns>
							 | 
						||
| 
								 | 
							
								    /// <param name='affectedFiles'>
							 | 
						||
| 
								 | 
							
								    /// A set of files whose status has changed
							 | 
						||
| 
								 | 
							
								    /// </param>
							 | 
						||
| 
								 | 
							
								    /// <param name="reimportedFiles">
							 | 
						||
| 
								 | 
							
								    /// A set of files that got reimported
							 | 
						||
| 
								 | 
							
								    /// </param>
							 | 
						||
| 
								 | 
							
								    /// <param name="checkProjectFiles">
							 | 
						||
| 
								 | 
							
								    /// Check if project files were changed externally
							 | 
						||
| 
								 | 
							
								    /// </param>
							 | 
						||
| 
								 | 
							
								    public bool SyncIfNeeded(IEnumerable<string> affectedFiles, IEnumerable<string> reimportedFiles, bool checkProjectFiles = false)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      SetupSupportedExtensions();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      PackageManagerTracker.SyncIfNeeded(checkProjectFiles);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (HasFilesBeenModified(affectedFiles, reimportedFiles) || RiderScriptEditorData.instance.hasChanges
							 | 
						||
| 
								 | 
							
								                                                               || RiderScriptEditorData.instance.HasChangesInCompilationDefines()
							 | 
						||
| 
								 | 
							
								                                                               || (checkProjectFiles && LastWriteTracker.HasLastWriteTimeChanged()))
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        Sync();
							 | 
						||
| 
								 | 
							
								        return true;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      return false;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private bool HasFilesBeenModified(IEnumerable<string> affectedFiles, IEnumerable<string> reimportedFiles)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      return affectedFiles.Any(it => ShouldFileBePartOfSolution(it) || ShouldSyncOnAffectedFiles(it)) 
							 | 
						||
| 
								 | 
							
								             || reimportedFiles.Any(ShouldSyncOnReimportedAsset);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private static bool ShouldSyncOnAffectedFiles(string asset)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      var extension = Path.GetExtension(asset);
							 | 
						||
| 
								 | 
							
								      // https://docs.unity3d.com/6000.2/Documentation/ScriptReference/Compilation.ScriptCompilerOptions.RoslynAdditionalFilePaths.html
							 | 
						||
| 
								 | 
							
								      // Source Generated files should get updated, when the additionalfile files are renamed/moved
							 | 
						||
| 
								 | 
							
								      return extension.Equals(".additionalfile", StringComparison.OrdinalIgnoreCase); 
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private static bool ShouldSyncOnReimportedAsset(string asset)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      var extension = Path.GetExtension(asset);
							 | 
						||
| 
								 | 
							
								      return extension.Equals(".asmdef", StringComparison.OrdinalIgnoreCase) ||
							 | 
						||
| 
								 | 
							
								             extension.Equals(".asmref", StringComparison.OrdinalIgnoreCase) ||
							 | 
						||
| 
								 | 
							
								             Path.GetFileName(asset).Equals("csc.rsp", StringComparison.OrdinalIgnoreCase);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public void Sync()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      SetupSupportedExtensions();
							 | 
						||
| 
								 | 
							
								      var types = GetAssetPostprocessorTypes();
							 | 
						||
| 
								 | 
							
								      isRiderProjectGeneration = true;
							 | 
						||
| 
								 | 
							
								      var externalCodeAlreadyGeneratedProjects = OnPreGeneratingCSProjectFiles(types);
							 | 
						||
| 
								 | 
							
								      isRiderProjectGeneration = false;
							 | 
						||
| 
								 | 
							
								      if (!externalCodeAlreadyGeneratedProjects)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        GenerateAndWriteSolutionAndProjects(types);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      OnGeneratedCSProjectFiles(types);
							 | 
						||
| 
								 | 
							
								      m_AssemblyNameProvider.ResetCaches();
							 | 
						||
| 
								 | 
							
								      m_AssemblyNames.Clear();
							 | 
						||
| 
								 | 
							
								      m_NormalisedPaths.Clear();
							 | 
						||
| 
								 | 
							
								      m_ProjectGuids.Clear();
							 | 
						||
| 
								 | 
							
								      _buffer = null;
							 | 
						||
| 
								 | 
							
								      RiderScriptEditorData.instance.hasChanges = false;
							 | 
						||
| 
								 | 
							
								      RiderScriptEditorData.instance.InvalidateSavedCompilationDefines();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public bool HasSolutionBeenGenerated()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      return m_FileIOProvider.Exists(SolutionFile());
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private void SetupSupportedExtensions()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      var extensions = m_AssemblyNameProvider.ProjectSupportedExtensions;
							 | 
						||
| 
								 | 
							
								      m_ProjectSupportedExtensions = new string[extensions.Length];
							 | 
						||
| 
								 | 
							
								      for (var i = 0; i < extensions.Length; i++)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        m_ProjectSupportedExtensions[i] = "." + extensions[i];
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private bool ShouldFileBePartOfSolution(string file)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      // Exclude files coming from packages except if they are internalized.
							 | 
						||
| 
								 | 
							
								      if (m_AssemblyNameProvider.IsInternalizedPackagePath(file))
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								          return false;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      return HasValidExtension(file);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public bool HasValidExtension(string file)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      // Dll's are not scripts but still need to be included..
							 | 
						||
| 
								 | 
							
								      if (file.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
							 | 
						||
| 
								 | 
							
								          return true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var extension = Path.GetExtension(file);
							 | 
						||
| 
								 | 
							
								      return IsSupportedExtension(extension);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private bool IsSupportedExtension(string extension)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      return k_BuiltinSupportedExtensions.ContainsKey(extension) || m_ProjectSupportedExtensions.Contains(extension);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private class AssemblyUsage
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      private readonly HashSet<string> m_ProjectAssemblies = new HashSet<string>();
							 | 
						||
| 
								 | 
							
								      private readonly HashSet<string> m_PrecompiledAssemblies = new HashSet<string>();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      public void AddProjectAssembly(Assembly assembly)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        m_ProjectAssemblies.Add(assembly.name);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      public void AddPrecompiledAssembly(Assembly assembly)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        m_PrecompiledAssemblies.Add(assembly.name);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      public bool IsProjectAssembly(Assembly assembly) => m_ProjectAssemblies.Contains(assembly.name);
							 | 
						||
| 
								 | 
							
								      public bool IsPrecompiledAssembly(Assembly assembly) => m_PrecompiledAssemblies.Contains(assembly.name);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private void GenerateAndWriteSolutionAndProjects(Type[] types)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      // Only synchronize islands that have associated source files and ones that we actually want in the project.
							 | 
						||
| 
								 | 
							
								      // This also filters out DLLs coming from .asmdef files in packages.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // Get all of the assemblies that Unity will compile from source. This includes Assembly-CSharp, all user assembly
							 | 
						||
| 
								 | 
							
								      // definitions, and all packages. Not all of the returned assemblies will require project files - by default,
							 | 
						||
| 
								 | 
							
								      // registry, git and local tarball packages are pre-compiled by Unity and will not require a project. This can be
							 | 
						||
| 
								 | 
							
								      // changed by the user in the External Tools settings page.
							 | 
						||
| 
								 | 
							
								      // Each assembly instance contains source files, output path, defines, compiler options and references. There
							 | 
						||
| 
								 | 
							
								      // will be `compiledAssemblyReferences`, which are DLLs, such as UnityEngine.dll, and assembly references, which
							 | 
						||
| 
								 | 
							
								      // are references to other assemblies that Unity will compile from source. Again, these assemblies might be
							 | 
						||
| 
								 | 
							
								      // projects, or pre-compiled by Unity, depending on the options selected by the user.
							 | 
						||
| 
								 | 
							
								      var allAssemblies = m_AssemblyNameProvider.GetAllAssemblies();
							 | 
						||
| 
								 | 
							
								      var assemblyUsage = new AssemblyUsage();
							 | 
						||
| 
								 | 
							
								      foreach (var assembly in allAssemblies)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        if (assembly.sourceFiles.Any(ShouldFileBePartOfSolution))
							 | 
						||
| 
								 | 
							
								          assemblyUsage.AddProjectAssembly(assembly);
							 | 
						||
| 
								 | 
							
								        else
							 | 
						||
| 
								 | 
							
								          assemblyUsage.AddPrecompiledAssembly(assembly);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // Get additional assets (other than source files) that we want to add to the projects, e.g. shaders, asmdef, etc.
							 | 
						||
| 
								 | 
							
								      var additionalAssetsByAssembly = GetAdditionalAssets();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var projectParts = new List<ProjectPart>();
							 | 
						||
| 
								 | 
							
								      var assemblyNamesWithSource = new HashSet<string>();
							 | 
						||
| 
								 | 
							
								      foreach (var assembly in allAssemblies)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        if (!assemblyUsage.IsProjectAssembly(assembly))
							 | 
						||
| 
								 | 
							
								          continue;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // TODO: Will this check ever be true? Player assemblies don't have the same name as editor assemblies, right?
							 | 
						||
| 
								 | 
							
								        if (assemblyNamesWithSource.Contains(assembly.name))
							 | 
						||
| 
								 | 
							
								          projectParts.Add(new ProjectPart(assembly.name, assembly, new List<string>())); // do not add asset project parts to both editor and player projects
							 | 
						||
| 
								 | 
							
								        else
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          additionalAssetsByAssembly.TryGetValue(assembly.name, out var additionalAssetsForProject);
							 | 
						||
| 
								 | 
							
								          projectParts.Add(new ProjectPart(assembly.name, assembly, additionalAssetsForProject));
							 | 
						||
| 
								 | 
							
								          assemblyNamesWithSource.Add(assembly.name);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // If there are any assets that should be in a separate assembly, but that assembly folder doesn't contain any
							 | 
						||
| 
								 | 
							
								      // source files, we'll have orphaned assets. Create a project for these assemblies, with references based on the
							 | 
						||
| 
								 | 
							
								      // Rider package assembly
							 | 
						||
| 
								 | 
							
								      // TODO: Would this produce the same results if we removed the check for ShouldFileBePartOfSolution above?
							 | 
						||
| 
								 | 
							
								      // I suspect the only difference would be output path and references, and potentially simplify things
							 | 
						||
| 
								 | 
							
								      var executingAssemblyName = typeof(ProjectGeneration).Assembly.GetName().Name;
							 | 
						||
| 
								 | 
							
								      var riderAssembly = m_AssemblyNameProvider.GetNamedAssembly(executingAssemblyName);
							 | 
						||
| 
								 | 
							
								      string[] coreReferences = null;
							 | 
						||
| 
								 | 
							
								      foreach (var pair in additionalAssetsByAssembly)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        var assembly = pair.Key;
							 | 
						||
| 
								 | 
							
								        var additionalAssets = pair.Value;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (!assemblyNamesWithSource.Contains(assembly))
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          if (coreReferences == null)
							 | 
						||
| 
								 | 
							
								          {
							 | 
						||
| 
								 | 
							
								            coreReferences = riderAssembly?.compiledAssemblyReferences.Where(a =>
							 | 
						||
| 
								 | 
							
								              a.EndsWith("UnityEditor.dll", StringComparison.Ordinal) ||
							 | 
						||
| 
								 | 
							
								              a.EndsWith("UnityEngine.dll", StringComparison.Ordinal) ||
							 | 
						||
| 
								 | 
							
								              a.EndsWith("UnityEngine.CoreModule.dll", StringComparison.Ordinal)).ToArray();
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          var rootNamespace = string.Empty;
							 | 
						||
| 
								 | 
							
								#if UNITY_2020_2_OR_NEWER
							 | 
						||
| 
								 | 
							
								          if (additionalAssets.Any()) 
							 | 
						||
| 
								 | 
							
								            rootNamespace = CompilationPipeline.GetAssemblyRootNamespaceFromScriptPath(additionalAssets.First());
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								          projectParts.Add(AddProjectPart(assembly, riderAssembly, coreReferences, additionalAssets, rootNamespace));
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var stringBuilder = new StringBuilder();
							 | 
						||
| 
								 | 
							
								      SyncSolution(stringBuilder, projectParts, types);
							 | 
						||
| 
								 | 
							
								      stringBuilder.Clear();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      foreach (var projectPart in projectParts)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        SyncProject(stringBuilder, projectPart, assemblyUsage, types);
							 | 
						||
| 
								 | 
							
								        stringBuilder.Clear();
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private static ProjectPart AddProjectPart(string assemblyName, Assembly riderAssembly, string[] coreReferences,
							 | 
						||
| 
								 | 
							
								      List<string> additionalAssets, string rootNamespace)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      Assembly assembly = null;
							 | 
						||
| 
								 | 
							
								      if (riderAssembly != null)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        // We want to add those references, so that Rider would detect Unity path and version and provide rich features for shader files
							 | 
						||
| 
								 | 
							
								        // Note that output path will be Library/ScriptAssemblies
							 | 
						||
| 
								 | 
							
								        assembly = new Assembly(assemblyName, riderAssembly.outputPath, Array.Empty<string>(),
							 | 
						||
| 
								 | 
							
								          new []{"UNITY_EDITOR"},
							 | 
						||
| 
								 | 
							
								          Array.Empty<Assembly>(),
							 | 
						||
| 
								 | 
							
								          coreReferences,
							 | 
						||
| 
								 | 
							
								          riderAssembly.flags
							 | 
						||
| 
								 | 
							
								#if UNITY_2020_2_OR_NEWER
							 | 
						||
| 
								 | 
							
								          , new ScriptCompilerOptions(),
							 | 
						||
| 
								 | 
							
								          rootNamespace
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								          );
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      return new ProjectPart(assemblyName, assembly, additionalAssets);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private Dictionary<string, List<string>> GetAdditionalAssets()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      var assemblyDllNames = new FilePathTrie<string>();
							 | 
						||
| 
								 | 
							
								      var interestingAssets = new List<string>();
							 | 
						||
| 
								 | 
							
								      foreach (var assetPath in m_AssemblyNameProvider.GetAllAssetPaths())
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        if (m_AssemblyNameProvider.IsInternalizedPackagePath(assetPath))
							 | 
						||
| 
								 | 
							
								          continue;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // Find all the .asmdef and .asmref files. Then get the assembly for a file in the same folder. Anything in that
							 | 
						||
| 
								 | 
							
								        // folder or below will be in the same assembly (unless there's another nested .asmdef, obvs)
							 | 
						||
| 
								 | 
							
								        if (assetPath.EndsWith(".asmdef", StringComparison.OrdinalIgnoreCase)
							 | 
						||
| 
								 | 
							
								            || assetPath.EndsWith(".asmref", StringComparison.OrdinalIgnoreCase))
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          // This call is very expensive when working with a very large project (e.g. called for 50,000+ assets), hence
							 | 
						||
| 
								 | 
							
								          // the approach of working with assembly definition root folders. We don't need a real script file to get the
							 | 
						||
| 
								 | 
							
								          // assembly DLL name
							 | 
						||
| 
								 | 
							
								          var assemblyDllName = m_AssemblyNameProvider.GetAssemblyNameFromScriptPath(assetPath + ".cs");
							 | 
						||
| 
								 | 
							
								          assemblyDllNames.Insert(Path.GetDirectoryName(assetPath), assemblyDllName);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        interestingAssets.Add(assetPath);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      const string fallbackAssemblyDllName = "Assembly-CSharp.dll";
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var assetsByAssemblyDll = new Dictionary<string, List<string>>();
							 | 
						||
| 
								 | 
							
								      foreach (var asset in interestingAssets)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        // TODO: Can we remove folders from generated projects?
							 | 
						||
| 
								 | 
							
								        // Why do we add them? We get an asset for every folder, including intermediate folders. We add folders that
							 | 
						||
| 
								 | 
							
								        // contain assets that we don't add to project files, so they appear empty. Adding them to the project file does
							 | 
						||
| 
								 | 
							
								        // not give us anything special - they appear as a folder in the Solution Explorer, so we can right click and
							 | 
						||
| 
								 | 
							
								        // add a file, but we could also "Show All Files" and do the same. Equally, Rider defaults to the Unity Explorer
							 | 
						||
| 
								 | 
							
								        // view, which shows all files and folders by default.
							 | 
						||
| 
								 | 
							
								        // We gain nothing by adding folders, and for very large projects, it can be very expensive to discover what
							 | 
						||
| 
								 | 
							
								        // project they should be added to, since most paths will be _above_ asmdef files, or inside Assets (which
							 | 
						||
| 
								 | 
							
								        // requires the full expensive check due to Editor, Resources, etc.)
							 | 
						||
| 
								 | 
							
								        // (E.g. an example large project with 45,600 assets, 5,000 are folders and only 2,500 are useful assets)
							 | 
						||
| 
								 | 
							
								        if (AssetDatabase.IsValidFolder(asset))
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          // var assemblyDllName = assemblyDllNames.FindClosestMatch(asset);
							 | 
						||
| 
								 | 
							
								          // if (string.IsNullOrEmpty(assemblyDllName))
							 | 
						||
| 
								 | 
							
								          // {
							 | 
						||
| 
								 | 
							
								          //   // Can't find it in trie (Assembly-CSharp and related projects don't have .asmdef files)
							 | 
						||
| 
								 | 
							
								          //   assemblyDllName = m_AssemblyNameProvider.GetAssemblyNameFromScriptPath($"{asset}/asset.cs");
							 | 
						||
| 
								 | 
							
								          // }
							 | 
						||
| 
								 | 
							
								          // if (string.IsNullOrEmpty(assemblyDllName))
							 | 
						||
| 
								 | 
							
								          //  assemblyDllName = fallbackAssemblyDllName;
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								          // if (!stringBuilders.TryGetValue(assemblyDllName, out var projectBuilder))
							 | 
						||
| 
								 | 
							
								          // {
							 | 
						||
| 
								 | 
							
								          //   projectBuilder = new StringBuilder();
							 | 
						||
| 
								 | 
							
								          //   stringBuilders[assemblyDllName] = projectBuilder;
							 | 
						||
| 
								 | 
							
								          //}
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								          // projectBuilder.Append("     <Folder Include=\"")
							 | 
						||
| 
								 | 
							
								          //   .Append(m_FileIOProvider.EscapedRelativePathFor(asset, ProjectDirectoryWithSlash))
							 | 
						||
| 
								 | 
							
								          //   .Append("\" />")
							 | 
						||
| 
								 | 
							
								          //   .AppendLine();
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        else
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          if (!asset.EndsWith(".cs", StringComparison.OrdinalIgnoreCase) && IsSupportedExtension(Path.GetExtension(asset)))
							 | 
						||
| 
								 | 
							
								          {
							 | 
						||
| 
								 | 
							
								            var assemblyDllName = assemblyDllNames.FindClosestMatch(asset);
							 | 
						||
| 
								 | 
							
								            if (string.IsNullOrEmpty(assemblyDllName))
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								              // Can't find it in trie (Assembly-CSharp and related projects don't have .asmdef files)
							 | 
						||
| 
								 | 
							
								              assemblyDllName = m_AssemblyNameProvider.GetAssemblyNameFromScriptPath($"{asset}.cs");
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            if (string.IsNullOrEmpty(assemblyDllName))
							 | 
						||
| 
								 | 
							
								              assemblyDllName = fallbackAssemblyDllName;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (!assetsByAssemblyDll.TryGetValue(assemblyDllName, out var assets))
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								              assets = new List<string>();
							 | 
						||
| 
								 | 
							
								              assetsByAssemblyDll[assemblyDllName] = assets;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            assets.Add(m_FileIOProvider.EscapedRelativePathFor(asset, ProjectDirectoryWithSlash));
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var assetsByAssemblyName = new Dictionary<string, List<string>>(assetsByAssemblyDll.Count);
							 | 
						||
| 
								 | 
							
								      foreach (var entry in assetsByAssemblyDll)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        var assemblyName = FileSystemUtil.FileNameWithoutExtension(entry.Key);
							 | 
						||
| 
								 | 
							
								        assetsByAssemblyName[assemblyName] = entry.Value;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      return assetsByAssemblyName;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private void SyncProject(StringBuilder stringBuilder, ProjectPart island, AssemblyUsage assemblyUsage, Type[] types)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      SyncProjectFileIfNotChanged(
							 | 
						||
| 
								 | 
							
								        ProjectFile(island),
							 | 
						||
| 
								 | 
							
								        ProjectText(stringBuilder, island, assemblyUsage),
							 | 
						||
| 
								 | 
							
								        types);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private void SyncProjectFileIfNotChanged(string path, string newContents, Type[] types)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      if (Path.GetExtension(path) == ".csproj")
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        newContents = OnGeneratedCSProject(path, newContents, types);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      SyncFileIfNotChanged(path, newContents);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private void SyncSolutionFileIfNotChanged(string path, string newContents, Type[] types)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      newContents = OnGeneratedSlnSolution(path, newContents, types);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      SyncFileIfNotChanged(path, newContents);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private static void OnGeneratedCSProjectFiles(Type[] types)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      foreach (var type in types)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        var method = type.GetMethod("OnGeneratedCSProjectFiles",
							 | 
						||
| 
								 | 
							
								          System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic |
							 | 
						||
| 
								 | 
							
								          System.Reflection.BindingFlags.Static);
							 | 
						||
| 
								 | 
							
								        if (method == null)
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          continue;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Debug.LogWarning("OnGeneratedCSProjectFiles is not supported.");
							 | 
						||
| 
								 | 
							
								        // RIDER-51958
							 | 
						||
| 
								 | 
							
								        //method.Invoke(null, args);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public static Type[] GetAssetPostprocessorTypes()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      return TypeCache.GetTypesDerivedFrom<AssetPostprocessor>().ToArray(); // doesn't find types from EditorPlugin, which is fine
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private static bool OnPreGeneratingCSProjectFiles(Type[] types)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      var result = false;
							 | 
						||
| 
								 | 
							
								      foreach (var type in types)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        var args = new object[0];
							 | 
						||
| 
								 | 
							
								        var method = type.GetMethod("OnPreGeneratingCSProjectFiles",
							 | 
						||
| 
								 | 
							
								          System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic |
							 | 
						||
| 
								 | 
							
								          System.Reflection.BindingFlags.Static);
							 | 
						||
| 
								 | 
							
								        if (method == null)
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          continue;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var returnValue = method.Invoke(null, args);
							 | 
						||
| 
								 | 
							
								        if (method.ReturnType == typeof(bool))
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          result |= (bool)returnValue;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      return result;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private static string OnGeneratedCSProject(string path, string content, Type[] types)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      foreach (var type in types)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        var args = new[] { path, content };
							 | 
						||
| 
								 | 
							
								        var method = type.GetMethod("OnGeneratedCSProject",
							 | 
						||
| 
								 | 
							
								          System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic |
							 | 
						||
| 
								 | 
							
								          System.Reflection.BindingFlags.Static);
							 | 
						||
| 
								 | 
							
								        if (method == null)
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          continue;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var returnValue = method.Invoke(null, args);
							 | 
						||
| 
								 | 
							
								        if (method.ReturnType == typeof(string))
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          content = (string)returnValue;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      return content;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private static string OnGeneratedSlnSolution(string path, string content, Type[] types)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      foreach (var type in types)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        var args = new[] { path, content };
							 | 
						||
| 
								 | 
							
								        var method = type.GetMethod("OnGeneratedSlnSolution",
							 | 
						||
| 
								 | 
							
								          System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic |
							 | 
						||
| 
								 | 
							
								          System.Reflection.BindingFlags.Static);
							 | 
						||
| 
								 | 
							
								        if (method == null)
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          continue;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var returnValue = method.Invoke(null, args);
							 | 
						||
| 
								 | 
							
								        if (method.ReturnType == typeof(string))
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          content = (string)returnValue;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      return content;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private void SyncFileIfNotChanged(string path, string newContents)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      if (HasChanged(path, newContents))
							 | 
						||
| 
								 | 
							
								        m_FileIOProvider.WriteAllText(path, newContents);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private static char[] _buffer = null;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private bool HasChanged(string path, string newContents)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      try
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        if (!m_FileIOProvider.Exists(path))
							 | 
						||
| 
								 | 
							
								          return true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        const int bufferSize = 100 * 1024; // 100kb - big enough to read most project files in a single read
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (_buffer == null)
							 | 
						||
| 
								 | 
							
								          _buffer = new char[bufferSize];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        using (var reader = m_FileIOProvider.GetReader(path))
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          int read, offset = 0;
							 | 
						||
| 
								 | 
							
								          do
							 | 
						||
| 
								 | 
							
								          {
							 | 
						||
| 
								 | 
							
								            read = reader.ReadBlock(_buffer, 0, _buffer.Length);
							 | 
						||
| 
								 | 
							
								            for (var i = 0; i < read; i++)
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								              if (_buffer[i] != newContents[offset + i])
							 | 
						||
| 
								 | 
							
								                return true;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            offset += read;
							 | 
						||
| 
								 | 
							
								          } while (read > 0);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          var isSame = offset == newContents.Length;
							 | 
						||
| 
								 | 
							
								          return !isSame;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      catch (Exception exception)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        Debug.LogException(exception);
							 | 
						||
| 
								 | 
							
								        return true;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private string ProjectText(StringBuilder projectBuilder, ProjectPart assembly, AssemblyUsage assemblyUsage)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      var responseFilesData = assembly.GetResponseFileData(m_AssemblyNameProvider, ProjectDirectory);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      ProjectHeader(projectBuilder, assembly, responseFilesData);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      projectBuilder.AppendLine("  <ItemGroup>");
							 | 
						||
| 
								 | 
							
								      foreach (var file in assembly.SourceFiles)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        var fullFile = m_FileIOProvider.EscapedRelativePathFor(file, ProjectDirectory);
							 | 
						||
| 
								 | 
							
								        projectBuilder.Append("    <Compile Include=\"").Append(fullFile).AppendLine("\" />");
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      foreach (var additionalAsset in (IEnumerable<string>)assembly.AdditionalAssets ?? Array.Empty<string>())
							 | 
						||
| 
								 | 
							
								        projectBuilder.Append("    <None Include=\"").Append(additionalAsset).AppendLine("\" />");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var binaryReferences = new HashSet<string>(assembly.CompiledAssemblyReferences);
							 | 
						||
| 
								 | 
							
								      foreach (var responseFileData in responseFilesData)
							 | 
						||
| 
								 | 
							
								        binaryReferences.UnionWith(responseFileData.FullPathReferences);
							 | 
						||
| 
								 | 
							
								      foreach (var assemblyReference in assembly.AssemblyReferences)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        if (assemblyUsage.IsPrecompiledAssembly(assemblyReference))
							 | 
						||
| 
								 | 
							
								          binaryReferences.Add(assemblyReference.outputPath);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      foreach (var reference in binaryReferences)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        var escapedFullPath = GetNormalisedAssemblyPath(reference);
							 | 
						||
| 
								 | 
							
								        var assemblyName = GetAssemblyNameFromPath(reference);
							 | 
						||
| 
								 | 
							
								        projectBuilder
							 | 
						||
| 
								 | 
							
								          .Append("    <Reference Include=\"").Append(assemblyName).AppendLine("\">")
							 | 
						||
| 
								 | 
							
								          .Append("      <HintPath>").Append(escapedFullPath).AppendLine("</HintPath>")
							 | 
						||
| 
								 | 
							
								          .AppendLine("    </Reference>");
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (0 < assembly.AssemblyReferences.Length)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        projectBuilder
							 | 
						||
| 
								 | 
							
								          .AppendLine("  </ItemGroup>")
							 | 
						||
| 
								 | 
							
								          .AppendLine("  <ItemGroup>");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        foreach (var reference in assembly.AssemblyReferences)
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          if (assemblyUsage.IsProjectAssembly(reference))
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								            var name = m_AssemblyNameProvider.GetProjectName(reference.name, reference.defines);
							 | 
						||
| 
								 | 
							
								            projectBuilder
							 | 
						||
| 
								 | 
							
								              .Append("    <ProjectReference Include=\"").Append(name).AppendLine(".csproj\">")
							 | 
						||
| 
								 | 
							
								              .Append("      <Project>{").Append(ProjectGuid(name)).AppendLine("}</Project>")
							 | 
						||
| 
								 | 
							
								              .Append("      <Name>").Append(name).AppendLine("</Name>")
							 | 
						||
| 
								 | 
							
								              .AppendLine("    </ProjectReference>");
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      projectBuilder
							 | 
						||
| 
								 | 
							
								        .AppendLine("  </ItemGroup>")
							 | 
						||
| 
								 | 
							
								        .AppendLine("  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />")
							 | 
						||
| 
								 | 
							
								        .AppendLine(
							 | 
						||
| 
								 | 
							
								          "  <!-- To modify your build process, add your task inside one of the targets below and uncomment it.")
							 | 
						||
| 
								 | 
							
								        .AppendLine("       Other similar extension points exist, see Microsoft.Common.targets.")
							 | 
						||
| 
								 | 
							
								        .AppendLine("  <Target Name=\"BeforeBuild\">")
							 | 
						||
| 
								 | 
							
								        .AppendLine("  </Target>")
							 | 
						||
| 
								 | 
							
								        .AppendLine("  <Target Name=\"AfterBuild\">")
							 | 
						||
| 
								 | 
							
								        .AppendLine("  </Target>")
							 | 
						||
| 
								 | 
							
								        .AppendLine("  -->")
							 | 
						||
| 
								 | 
							
								        .AppendLine("</Project>");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      return projectBuilder.ToString();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private string ProjectFile(ProjectPart projectPart)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      return Path.Combine(ProjectDirectory, $"{m_AssemblyNameProvider.GetProjectName(projectPart.Name, projectPart.Defines)}.csproj");
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public string SolutionFile()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      return Path.Combine(ProjectDirectory, $"{m_ProjectName}.sln");
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private void ProjectHeader(StringBuilder stringBuilder, ProjectPart assembly, List<ResponseFileData> responseFilesData)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      var responseFilesDataArgs = GetOtherArgumentsFromResponseFilesData(responseFilesData);
							 | 
						||
| 
								 | 
							
								      stringBuilder
							 | 
						||
| 
								 | 
							
								        .AppendLine("<?xml version=\"1.0\" encoding=\"utf-8\"?>")
							 | 
						||
| 
								 | 
							
								        .AppendLine(
							 | 
						||
| 
								 | 
							
								          "<Project ToolsVersion=\"4.0\" DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">")
							 | 
						||
| 
								 | 
							
								        .AppendLine("  <PropertyGroup>")
							 | 
						||
| 
								 | 
							
								        .Append("    <LangVersion>").Append(GetLangVersion(responseFilesDataArgs["langversion"], assembly)).AppendLine("</LangVersion>")
							 | 
						||
| 
								 | 
							
								        .AppendLine(
							 | 
						||
| 
								 | 
							
								          "    <_TargetFrameworkDirectories>non_empty_path_generated_by_unity.rider.package</_TargetFrameworkDirectories>")
							 | 
						||
| 
								 | 
							
								        .AppendLine(
							 | 
						||
| 
								 | 
							
								          "    <_FullFrameworkReferenceAssemblyPaths>non_empty_path_generated_by_unity.rider.package</_FullFrameworkReferenceAssemblyPaths>")
							 | 
						||
| 
								 | 
							
								        .AppendLine("    <DisableHandlePackageFileConflicts>true</DisableHandlePackageFileConflicts>");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var rulesetPaths = GetRoslynAnalyzerRulesetPaths(assembly, responseFilesDataArgs);
							 | 
						||
| 
								 | 
							
								      foreach (var path in rulesetPaths)
							 | 
						||
| 
								 | 
							
								        stringBuilder.Append("    <CodeAnalysisRuleSet>").Append(path).AppendLine("</CodeAnalysisRuleSet>");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      stringBuilder
							 | 
						||
| 
								 | 
							
								        .AppendLine("  </PropertyGroup>")
							 | 
						||
| 
								 | 
							
								        .AppendLine("  <PropertyGroup>")
							 | 
						||
| 
								 | 
							
								        .AppendLine("    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>")
							 | 
						||
| 
								 | 
							
								        .AppendLine("    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>")
							 | 
						||
| 
								 | 
							
								        .AppendLine("    <ProductVersion>10.0.20506</ProductVersion>")
							 | 
						||
| 
								 | 
							
								        .AppendLine("    <SchemaVersion>2.0</SchemaVersion>")
							 | 
						||
| 
								 | 
							
								        .Append("    <RootNamespace>").Append(assembly.RootNamespace).AppendLine("</RootNamespace>")
							 | 
						||
| 
								 | 
							
								        .Append("    <ProjectGuid>{").Append(ProjectGuid(m_AssemblyNameProvider.GetProjectName(assembly.Name, assembly.Defines))).AppendLine("}</ProjectGuid>")
							 | 
						||
| 
								 | 
							
								        .AppendLine(
							 | 
						||
| 
								 | 
							
								          "    <ProjectTypeGuids>{E097FAD1-6243-4DAD-9C02-E9B9EFC3FFC1};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>")
							 | 
						||
| 
								 | 
							
								        .AppendLine("    <OutputType>Library</OutputType>")
							 | 
						||
| 
								 | 
							
								        .AppendLine("    <AppDesignerFolder>Properties</AppDesignerFolder>")
							 | 
						||
| 
								 | 
							
								        .Append("    <AssemblyName>").Append(assembly.Name).AppendLine("</AssemblyName>")
							 | 
						||
| 
								 | 
							
								        .AppendLine("    <TargetFrameworkVersion>v4.7.1</TargetFrameworkVersion>")
							 | 
						||
| 
								 | 
							
								        .AppendLine("    <FileAlignment>512</FileAlignment>")
							 | 
						||
| 
								 | 
							
								        .AppendLine("    <BaseDirectory>.</BaseDirectory>")
							 | 
						||
| 
								 | 
							
								        .AppendLine("  </PropertyGroup>")
							 | 
						||
| 
								 | 
							
								        .AppendLine("  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">")
							 | 
						||
| 
								 | 
							
								        .AppendLine("    <DebugSymbols>true</DebugSymbols>")
							 | 
						||
| 
								 | 
							
								        .AppendLine("    <DebugType>full</DebugType>")
							 | 
						||
| 
								 | 
							
								        .AppendLine("    <Optimize>false</Optimize>")
							 | 
						||
| 
								 | 
							
								        .Append("    <OutputPath>").Append(assembly.OutputPath).AppendLine("</OutputPath>");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var defines = new HashSet<string>(assembly.Defines);
							 | 
						||
| 
								 | 
							
								      foreach (var responseFileData in responseFilesData)
							 | 
						||
| 
								 | 
							
								        defines.UnionWith(responseFileData.Defines);
							 | 
						||
| 
								 | 
							
								      stringBuilder
							 | 
						||
| 
								 | 
							
								        .Append("    <DefineConstants>").CompatibleAppendJoin(';', defines).AppendLine("</DefineConstants>")
							 | 
						||
| 
								 | 
							
								        .AppendLine("    <ErrorReport>prompt</ErrorReport>");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var warningLevel = responseFilesDataArgs["warn"].Concat(responseFilesDataArgs["w"]).Distinct().FirstOrDefault();
							 | 
						||
| 
								 | 
							
								      stringBuilder
							 | 
						||
| 
								 | 
							
								        .Append("    <WarningLevel>").Append(!string.IsNullOrWhiteSpace(warningLevel) ? warningLevel : "4").AppendLine("</WarningLevel>")
							 | 
						||
| 
								 | 
							
								        .Append("    <NoWarn>").CompatibleAppendJoin(',', GetNoWarn(responseFilesDataArgs["nowarn"].ToList())).AppendLine("</NoWarn>")
							 | 
						||
| 
								 | 
							
								        .Append("    <AllowUnsafeBlocks>").Append(assembly.CompilerOptions.AllowUnsafeCode | responseFilesData.Any(x => x.Unsafe)).AppendLine("</AllowUnsafeBlocks>");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      AppendWarningAsError(stringBuilder, responseFilesDataArgs["warnaserror"],
							 | 
						||
| 
								 | 
							
								        responseFilesDataArgs["warnaserror-"], responseFilesDataArgs["warnaserror+"]);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // TODO: Can we have multiple documentation files in a project file?
							 | 
						||
| 
								 | 
							
								      foreach (var docFile in responseFilesDataArgs["doc"])
							 | 
						||
| 
								 | 
							
								        stringBuilder.Append("    <DocumentationFile>").Append(docFile).AppendLine("</DocumentationFile>");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var nullable = responseFilesDataArgs["nullable"].FirstOrDefault();
							 | 
						||
| 
								 | 
							
								      if (!string.IsNullOrEmpty(nullable))
							 | 
						||
| 
								 | 
							
								        stringBuilder.Append("    <Nullable>").Append(nullable).AppendLine("</Nullable>");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      stringBuilder
							 | 
						||
| 
								 | 
							
								        .AppendLine("  </PropertyGroup>")
							 | 
						||
| 
								 | 
							
								        .AppendLine("  <PropertyGroup>")
							 | 
						||
| 
								 | 
							
								        .AppendLine("    <NoConfig>true</NoConfig>")
							 | 
						||
| 
								 | 
							
								        .AppendLine("    <NoStdLib>true</NoStdLib>")
							 | 
						||
| 
								 | 
							
								        .AppendLine("    <AddAdditionalExplicitAssemblyReferences>false</AddAdditionalExplicitAssemblyReferences>")
							 | 
						||
| 
								 | 
							
								        .AppendLine("    <ImplicitlyExpandNETStandardFacades>false</ImplicitlyExpandNETStandardFacades>")
							 | 
						||
| 
								 | 
							
								        .AppendLine("    <ImplicitlyExpandDesignTimeFacades>false</ImplicitlyExpandDesignTimeFacades>")
							 | 
						||
| 
								 | 
							
								        .AppendLine("  </PropertyGroup>");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var analyzers = GetRoslynAnalyzers(assembly, responseFilesDataArgs);
							 | 
						||
| 
								 | 
							
								      if (analyzers.Length > 0)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        stringBuilder.AppendLine("  <ItemGroup>");
							 | 
						||
| 
								 | 
							
								        foreach (var analyzer in analyzers)
							 | 
						||
| 
								 | 
							
								          stringBuilder.AppendLine($"    <Analyzer Include=\"{analyzer.NormalizePath()}\" />");
							 | 
						||
| 
								 | 
							
								        stringBuilder.AppendLine("  </ItemGroup>");
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var additionalFiles = GetRoslynAdditionalFiles(assembly, responseFilesDataArgs);
							 | 
						||
| 
								 | 
							
								      if (additionalFiles.Length > 0)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        stringBuilder.AppendLine("  <ItemGroup>");
							 | 
						||
| 
								 | 
							
								        foreach (var additionalFile in additionalFiles)
							 | 
						||
| 
								 | 
							
								          stringBuilder.AppendLine($"    <AdditionalFiles Include=\"{additionalFile}\" />");
							 | 
						||
| 
								 | 
							
								        stringBuilder.AppendLine("  </ItemGroup>");
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var configFile = GetGlobalAnalyzerConfigFile(assembly);
							 | 
						||
| 
								 | 
							
								      if (!string.IsNullOrEmpty(configFile))
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        stringBuilder
							 | 
						||
| 
								 | 
							
								          .AppendLine("  <ItemGroup>")
							 | 
						||
| 
								 | 
							
								          .Append("    <GlobalAnalyzerConfigFiles Include=\"").Append(configFile).AppendLine("\" />")
							 | 
						||
| 
								 | 
							
								          .AppendLine("  </ItemGroup>");
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private static string GetGlobalAnalyzerConfigFile(ProjectPart assembly)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      var configFile = string.Empty;
							 | 
						||
| 
								 | 
							
								#if UNITY_2021_3 // https://github.com/JetBrains/resharper-unity/issues/2401
							 | 
						||
| 
								 | 
							
								      var type = assembly.CompilerOptions.GetType();
							 | 
						||
| 
								 | 
							
								      var propertyInfo = type.GetProperty("AnalyzerConfigPath");
							 | 
						||
| 
								 | 
							
								      if (propertyInfo != null && propertyInfo.GetValue(assembly.CompilerOptions) is string value)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        configFile = value;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								#elif UNITY_2022_2_OR_NEWER
							 | 
						||
| 
								 | 
							
								        configFile = assembly.CompilerOptions.AnalyzerConfigPath; // https://docs.unity3d.com/2021.3/Documentation/ScriptReference/Compilation.ScriptCompilerOptions.AnalyzerConfigPath.html
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      return configFile;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private static string[] GetRoslynAdditionalFiles(ProjectPart assembly, ILookup<string, string> otherResponseFilesData)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      var additionalFilePathsFromCompilationPipeline = Array.Empty<string>();
							 | 
						||
| 
								 | 
							
								#if UNITY_2021_3 // https://github.com/JetBrains/resharper-unity/issues/2401
							 | 
						||
| 
								 | 
							
								      var type = assembly.CompilerOptions.GetType();
							 | 
						||
| 
								 | 
							
								      var propertyInfo = type.GetProperty("RoslynAdditionalFilePaths");
							 | 
						||
| 
								 | 
							
								      if (propertyInfo != null && propertyInfo.GetValue(assembly.CompilerOptions) is string[] value)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        additionalFilePathsFromCompilationPipeline = value;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								#elif UNITY_2022_2_OR_NEWER // https://docs.unity3d.com/2021.3/Documentation/ScriptReference/Compilation.ScriptCompilerOptions.RoslynAdditionalFilePaths.html
							 | 
						||
| 
								 | 
							
								        additionalFilePathsFromCompilationPipeline = assembly.CompilerOptions.RoslynAdditionalFilePaths;
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								      return otherResponseFilesData["additionalfile"]
							 | 
						||
| 
								 | 
							
								        .SelectMany(x=>x.Split(';'))
							 | 
						||
| 
								 | 
							
								        .Concat(additionalFilePathsFromCompilationPipeline)
							 | 
						||
| 
								 | 
							
								        .Distinct().ToArray();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    string[] GetRoslynAnalyzers(ProjectPart assembly, ILookup<string, string> otherResponseFilesData)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								#if UNITY_2020_2_OR_NEWER
							 | 
						||
| 
								 | 
							
								      return otherResponseFilesData["analyzer"].Concat(otherResponseFilesData["a"])
							 | 
						||
| 
								 | 
							
								        .SelectMany(x=>x.Split(';'))
							 | 
						||
| 
								 | 
							
								#if !ROSLYN_ANALYZER_FIX
							 | 
						||
| 
								 | 
							
								        .Concat(m_AssemblyNameProvider.GetRoslynAnalyzerPaths())
							 | 
						||
| 
								 | 
							
								#else
							 | 
						||
| 
								 | 
							
								        .Concat(assembly.CompilerOptions.RoslynAnalyzerDllPaths)
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								        .Select(GetNormalisedAssemblyPath)
							 | 
						||
| 
								 | 
							
								        .Distinct()
							 | 
						||
| 
								 | 
							
								        .ToArray();
							 | 
						||
| 
								 | 
							
								#else
							 | 
						||
| 
								 | 
							
								      return otherResponseFilesData["analyzer"].Concat(otherResponseFilesData["a"])
							 | 
						||
| 
								 | 
							
								        .SelectMany(x=>x.Split(';'))
							 | 
						||
| 
								 | 
							
								        .Distinct()
							 | 
						||
| 
								 | 
							
								        .Select(GetNormalisedAssemblyPath)
							 | 
						||
| 
								 | 
							
								        .ToArray();
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private IEnumerable<string> GetRoslynAnalyzerRulesetPaths(ProjectPart assembly, ILookup<string, string> otherResponseFilesData)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      var paths = new HashSet<string>(otherResponseFilesData["ruleset"]);
							 | 
						||
| 
								 | 
							
								#if UNITY_2020_2_OR_NEWER
							 | 
						||
| 
								 | 
							
								      if (!string.IsNullOrEmpty(assembly.CompilerOptions.RoslynAnalyzerRulesetPath))
							 | 
						||
| 
								 | 
							
								        paths.Add(assembly.CompilerOptions.RoslynAnalyzerRulesetPath);
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      return paths.Select(GetNormalisedAssemblyPath);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private static void AppendWarningAsError(StringBuilder stringBuilder,
							 | 
						||
| 
								 | 
							
								      IEnumerable<string> args, IEnumerable<string> argsMinus, IEnumerable<string> argsPlus)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      var treatWarningsAsErrors = false;
							 | 
						||
| 
								 | 
							
								      var warningIds = new List<string>();
							 | 
						||
| 
								 | 
							
								      var notWarningIds = new List<string>(argsMinus);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      foreach (var s in args)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        if (s == "+" || s == "") treatWarningsAsErrors = true;
							 | 
						||
| 
								 | 
							
								        else if (s == "-") treatWarningsAsErrors = false;
							 | 
						||
| 
								 | 
							
								        else warningIds.Add(s);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      warningIds.AddRange(argsPlus);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      stringBuilder.Append("    <TreatWarningsAsErrors>").Append(treatWarningsAsErrors) .AppendLine("</TreatWarningsAsErrors>");
							 | 
						||
| 
								 | 
							
								      if (warningIds.Count > 0)
							 | 
						||
| 
								 | 
							
								        stringBuilder.Append("    <WarningsAsErrors>").CompatibleAppendJoin(';', warningIds).AppendLine("</WarningsAsErrors>");
							 | 
						||
| 
								 | 
							
								      if (notWarningIds.Count > 0)
							 | 
						||
| 
								 | 
							
								        stringBuilder.Append("    <WarningsNotAsErrors>").CompatibleAppendJoin(';', notWarningIds) .AppendLine("</WarningsNotAsErrors>");
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private void SyncSolution(StringBuilder stringBuilder, List<ProjectPart> islands, Type[] types)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      SyncSolutionFileIfNotChanged(SolutionFile(), SolutionText(stringBuilder, islands), types);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private string SolutionText(StringBuilder stringBuilder, List<ProjectPart> islands)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      stringBuilder
							 | 
						||
| 
								 | 
							
								        .AppendLine()
							 | 
						||
| 
								 | 
							
								        .AppendLine("Microsoft Visual Studio Solution File, Format Version 11.00")
							 | 
						||
| 
								 | 
							
								        .AppendLine("# Visual Studio 2010");
							 | 
						||
| 
								 | 
							
								      foreach (var island in islands)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        var projectName = m_AssemblyNameProvider.GetProjectName(island.Name, island.Defines);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // GUID is for C# class libraries
							 | 
						||
| 
								 | 
							
								        stringBuilder
							 | 
						||
| 
								 | 
							
								          .Append("Project(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"")
							 | 
						||
| 
								 | 
							
								          .Append(island.Name)
							 | 
						||
| 
								 | 
							
								          .Append("\", \"")
							 | 
						||
| 
								 | 
							
								          .Append(projectName)
							 | 
						||
| 
								 | 
							
								          .Append(".csproj\", \"{")
							 | 
						||
| 
								 | 
							
								          .Append(ProjectGuid(projectName))
							 | 
						||
| 
								 | 
							
								          .AppendLine("}\"")
							 | 
						||
| 
								 | 
							
								          .AppendLine("EndProject");
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      stringBuilder.AppendLine("Global")
							 | 
						||
| 
								 | 
							
								        .AppendLine("\tGlobalSection(SolutionConfigurationPlatforms) = preSolution")
							 | 
						||
| 
								 | 
							
								        .AppendLine("\t\tDebug|Any CPU = Debug|Any CPU")
							 | 
						||
| 
								 | 
							
								        .AppendLine("\tEndGlobalSection")
							 | 
						||
| 
								 | 
							
								        .AppendLine("\tGlobalSection(ProjectConfigurationPlatforms) = postSolution");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      foreach (var island in islands)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        var projectGuid = ProjectGuid(m_AssemblyNameProvider.GetProjectName(island.Name, island.Defines));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        stringBuilder
							 | 
						||
| 
								 | 
							
								          .Append("\t\t{").Append(projectGuid).AppendLine("}.Debug|Any CPU.ActiveCfg = Debug|Any CPU")
							 | 
						||
| 
								 | 
							
								          .Append("\t\t{").Append(projectGuid).AppendLine("}.Debug|Any CPU.Build.0 = Debug|Any CPU");
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      stringBuilder.AppendLine("\tEndGlobalSection")
							 | 
						||
| 
								 | 
							
								        .AppendLine("\tGlobalSection(SolutionProperties) = preSolution")
							 | 
						||
| 
								 | 
							
								        .AppendLine("\t\tHideSolutionNode = FALSE")
							 | 
						||
| 
								 | 
							
								        .AppendLine("\tEndGlobalSection")
							 | 
						||
| 
								 | 
							
								        .AppendLine("EndGlobal");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      return stringBuilder.ToString();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private static ILookup<string, string> GetOtherArgumentsFromResponseFilesData(List<ResponseFileData> responseFilesData)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      var paths = responseFilesData.SelectMany(x =>
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          return x.OtherArguments
							 | 
						||
| 
								 | 
							
								            .Where(a => a.StartsWith("/", StringComparison.Ordinal) || a.StartsWith("-", StringComparison.Ordinal))
							 | 
						||
| 
								 | 
							
								            .Select(b =>
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								              var index = b.IndexOf(":", StringComparison.Ordinal);
							 | 
						||
| 
								 | 
							
								              if (index > 0 && b.Length > index)
							 | 
						||
| 
								 | 
							
								              {
							 | 
						||
| 
								 | 
							
								                var key = b.Substring(1, index - 1);
							 | 
						||
| 
								 | 
							
								                return new KeyValuePair<string, string>(key, b.Substring(index + 1));
							 | 
						||
| 
								 | 
							
								              }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								              const string warnaserror = "warnaserror";
							 | 
						||
| 
								 | 
							
								              if (b.Substring(1).StartsWith(warnaserror, StringComparison.Ordinal))
							 | 
						||
| 
								 | 
							
								              {
							 | 
						||
| 
								 | 
							
								                return new KeyValuePair<string, string>(warnaserror, b.Substring(warnaserror.Length + 1));
							 | 
						||
| 
								 | 
							
								              }
							 | 
						||
| 
								 | 
							
								              const string nullable = "nullable";
							 | 
						||
| 
								 | 
							
								              if (b.Substring(1).StartsWith(nullable, StringComparison.Ordinal))
							 | 
						||
| 
								 | 
							
								              {
							 | 
						||
| 
								 | 
							
								                var res = b.Substring(nullable.Length + 1);
							 | 
						||
| 
								 | 
							
								                if (string.IsNullOrWhiteSpace(res) || res.Equals("+"))
							 | 
						||
| 
								 | 
							
								                  res = "enable";
							 | 
						||
| 
								 | 
							
								                else if (res.Equals("-"))
							 | 
						||
| 
								 | 
							
								                  res = "disable";
							 | 
						||
| 
								 | 
							
								                return new KeyValuePair<string, string>(nullable, res);
							 | 
						||
| 
								 | 
							
								              }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								              return default;
							 | 
						||
| 
								 | 
							
								            });
							 | 
						||
| 
								 | 
							
								        })
							 | 
						||
| 
								 | 
							
								        .Distinct()
							 | 
						||
| 
								 | 
							
								        .ToLookup(o => o.Key, pair => pair.Value);
							 | 
						||
| 
								 | 
							
								      return paths;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private string GetLangVersion(IEnumerable<string> langVersionList, ProjectPart assembly)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      var langVersion = langVersionList.FirstOrDefault();
							 | 
						||
| 
								 | 
							
								      if (!string.IsNullOrWhiteSpace(langVersion))
							 | 
						||
| 
								 | 
							
								        return langVersion;
							 | 
						||
| 
								 | 
							
								#if UNITY_2020_2_OR_NEWER
							 | 
						||
| 
								 | 
							
								      return assembly.CompilerOptions.LanguageVersion;
							 | 
						||
| 
								 | 
							
								#else
							 | 
						||
| 
								 | 
							
								      return "latest";
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public static IEnumerable<string> GetNoWarn(List<string> codes)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								#if UNITY_2019_4 || UNITY_2020_1 // RIDER-77206 Unity 2020.1.3 'PlayerSettings' does not contain a definition for 'suppressCommonWarnings'
							 | 
						||
| 
								 | 
							
								      var type = typeof(PlayerSettings);
							 | 
						||
| 
								 | 
							
								      var propertyInfo = type.GetProperty("suppressCommonWarnings");
							 | 
						||
| 
								 | 
							
								      if (propertyInfo != null && propertyInfo.GetValue(null) is bool && (bool)propertyInfo.GetValue(null))
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        codes.AddRange(new[] {"0169", "0649"});
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								#elif UNITY_2020_2_OR_NEWER
							 | 
						||
| 
								 | 
							
								      if (PlayerSettings.suppressCommonWarnings)
							 | 
						||
| 
								 | 
							
								        codes.AddRange(new[] {"0169", "0649"});
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      return codes.Distinct();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private string ProjectGuid(string name)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      if (!m_ProjectGuids.TryGetValue(name, out var guid))
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        guid = m_GUIDGenerator.ProjectGuid(m_ProjectName + name);
							 | 
						||
| 
								 | 
							
								        m_ProjectGuids.Add(name, guid);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      return guid;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private string GetNormalisedAssemblyPath(string path)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      if (!m_NormalisedPaths.TryGetValue(path, out var normalisedPath))
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        normalisedPath = Path.IsPathRooted(path) ? path : Path.GetFullPath(path);
							 | 
						||
| 
								 | 
							
								        normalisedPath = SecurityElement.Escape(normalisedPath).NormalizePath();
							 | 
						||
| 
								 | 
							
								        m_NormalisedPaths.Add(path, normalisedPath);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      return normalisedPath;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private string GetAssemblyNameFromPath(string path)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      if (!m_AssemblyNames.TryGetValue(path, out var name))
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        name = FileSystemUtil.FileNameWithoutExtension(path);
							 | 
						||
| 
								 | 
							
								        m_AssemblyNames.Add(path, name);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      return name;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  internal class FilePathTrie<TData>
							 | 
						||
| 
								 | 
							
								  {
							 | 
						||
| 
								 | 
							
								    private static readonly char[] Separators = { '\\', '/' };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private readonly TrieNode m_Root = new TrieNode();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private class TrieNode
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      public Dictionary<string, TrieNode> Children;
							 | 
						||
| 
								 | 
							
								      public TData Data;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public void Insert(string filePath, TData data)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      var parts = filePath.Split(Separators);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var node = m_Root;
							 | 
						||
| 
								 | 
							
								      foreach (var part in parts)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        if (node.Children == null)
							 | 
						||
| 
								 | 
							
								          node.Children = new Dictionary<string, TrieNode>(StringComparer.OrdinalIgnoreCase);
							 | 
						||
| 
								 | 
							
								        // ReSharper disable once CanSimplifyDictionaryLookupWithTryAdd
							 | 
						||
| 
								 | 
							
								        if (!node.Children.ContainsKey(part))
							 | 
						||
| 
								 | 
							
								          node.Children[part] = new TrieNode();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        node = node.Children[part];
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      node.Data = data;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public TData FindClosestMatch(string filePath)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      var parts = filePath.Split(Separators);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var node = m_Root;
							 | 
						||
| 
								 | 
							
								      foreach (var part in parts)
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        if (node.Children != null && node.Children.TryGetValue(part, out var next))
							 | 
						||
| 
								 | 
							
								          node = next;
							 | 
						||
| 
								 | 
							
								        else
							 | 
						||
| 
								 | 
							
								          break;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      return node.Data;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 |