A game about forced loneliness, made by TACStudios
1/*---------------------------------------------------------------------------------------------
2 * Copyright (c) Unity Technologies.
3 * Copyright (c) Microsoft Corporation. All rights reserved.
4 * Licensed under the MIT License. See License.txt in the project root for license information.
5 *--------------------------------------------------------------------------------------------*/
6using System;
7using System.Collections.Generic;
8using System.Linq;
9using UnityEditor;
10using UnityEditor.Compilation;
11using UnityEditor.PackageManager;
12
13namespace Microsoft.Unity.VisualStudio.Editor
14{
15 public interface IAssemblyNameProvider
16 {
17 string[] ProjectSupportedExtensions { get; }
18 string ProjectGenerationRootNamespace { get; }
19 ProjectGenerationFlag ProjectGenerationFlag { get; }
20
21 string GetAssemblyNameFromScriptPath(string path);
22 string GetAssemblyName(string assemblyOutputPath, string assemblyName);
23 bool IsInternalizedPackagePath(string path);
24 IEnumerable<Assembly> GetAssemblies(Func<string, bool> shouldFileBePartOfSolution);
25 IEnumerable<string> GetAllAssetPaths();
26 UnityEditor.PackageManager.PackageInfo FindForAssetPath(string assetPath);
27 ResponseFileData ParseResponseFile(string responseFilePath, string projectDirectory, string[] systemReferenceDirectories);
28 void ToggleProjectGeneration(ProjectGenerationFlag preference);
29 }
30
31 public class AssemblyNameProvider : IAssemblyNameProvider
32 {
33 private readonly Dictionary<string, UnityEditor.PackageManager.PackageInfo> m_PackageInfoCache = new Dictionary<string, UnityEditor.PackageManager.PackageInfo>();
34
35 ProjectGenerationFlag m_ProjectGenerationFlag = (ProjectGenerationFlag)EditorPrefs.GetInt(
36 "unity_project_generation_flag",
37 (int)(ProjectGenerationFlag.Local | ProjectGenerationFlag.Embedded));
38
39 public string[] ProjectSupportedExtensions => EditorSettings.projectGenerationUserExtensions;
40
41 public string ProjectGenerationRootNamespace => EditorSettings.projectGenerationRootNamespace;
42
43 public ProjectGenerationFlag ProjectGenerationFlag
44 {
45 get { return ProjectGenerationFlagImpl; }
46 private set { ProjectGenerationFlagImpl = value;}
47 }
48
49 internal virtual ProjectGenerationFlag ProjectGenerationFlagImpl
50 {
51 get => m_ProjectGenerationFlag;
52 private set
53 {
54 EditorPrefs.SetInt("unity_project_generation_flag", (int)value);
55 m_ProjectGenerationFlag = value;
56 }
57 }
58
59 public string GetAssemblyNameFromScriptPath(string path)
60 {
61 return CompilationPipeline.GetAssemblyNameFromScriptPath(path);
62 }
63
64 internal static readonly string AssemblyOutput = @"Temp\bin\Debug\".NormalizePathSeparators();
65 internal static readonly string PlayerAssemblyOutput = @"Temp\bin\Debug\Player\".NormalizePathSeparators();
66
67 public IEnumerable<Assembly> GetAssemblies(Func<string, bool> shouldFileBePartOfSolution)
68 {
69 IEnumerable<Assembly> assemblies = GetAssembliesByType(AssembliesType.Editor, shouldFileBePartOfSolution, AssemblyOutput);
70
71 if (!ProjectGenerationFlag.HasFlag(ProjectGenerationFlag.PlayerAssemblies))
72 {
73 return assemblies;
74 }
75 var playerAssemblies = GetAssembliesByType(AssembliesType.Player, shouldFileBePartOfSolution, PlayerAssemblyOutput);
76 return assemblies.Concat(playerAssemblies);
77 }
78
79 private static IEnumerable<Assembly> GetAssembliesByType(AssembliesType type, Func<string, bool> shouldFileBePartOfSolution, string outputPath)
80 {
81 foreach (var assembly in CompilationPipeline.GetAssemblies(type))
82 {
83 if (assembly.sourceFiles.Any(shouldFileBePartOfSolution))
84 {
85 yield return new Assembly(
86 assembly.name,
87 outputPath,
88 assembly.sourceFiles,
89 assembly.defines,
90 assembly.assemblyReferences,
91 assembly.compiledAssemblyReferences,
92 assembly.flags,
93 assembly.compilerOptions
94#if UNITY_2020_2_OR_NEWER
95 , assembly.rootNamespace
96#endif
97 );
98 }
99 }
100 }
101
102 public string GetCompileOutputPath(string assemblyName)
103 {
104 // We need to keep this one for API surface check (AssemblyNameProvider is public), but not used anymore
105 throw new NotImplementedException();
106 }
107
108 public IEnumerable<string> GetAllAssetPaths()
109 {
110 return AssetDatabase.GetAllAssetPaths();
111 }
112
113 private static string ResolvePotentialParentPackageAssetPath(string assetPath)
114 {
115 const string packagesPrefix = "packages/";
116 if (!assetPath.StartsWith(packagesPrefix, StringComparison.OrdinalIgnoreCase))
117 {
118 return null;
119 }
120
121 var followupSeparator = assetPath.IndexOf('/', packagesPrefix.Length);
122 if (followupSeparator == -1)
123 {
124 return assetPath.ToLowerInvariant();
125 }
126
127 return assetPath.Substring(0, followupSeparator).ToLowerInvariant();
128 }
129
130 public UnityEditor.PackageManager.PackageInfo FindForAssetPath(string assetPath)
131 {
132 var parentPackageAssetPath = ResolvePotentialParentPackageAssetPath(assetPath);
133 if (parentPackageAssetPath == null)
134 {
135 return null;
136 }
137
138 if (m_PackageInfoCache.TryGetValue(parentPackageAssetPath, out var cachedPackageInfo))
139 {
140 return cachedPackageInfo;
141 }
142
143 var result = UnityEditor.PackageManager.PackageInfo.FindForAssetPath(parentPackageAssetPath);
144 m_PackageInfoCache[parentPackageAssetPath] = result;
145 return result;
146 }
147
148 public bool IsInternalizedPackagePath(string path)
149 {
150 if (string.IsNullOrEmpty(path.Trim()))
151 {
152 return false;
153 }
154 var packageInfo = FindForAssetPath(path);
155 if (packageInfo == null)
156 {
157 return false;
158 }
159 var packageSource = packageInfo.source;
160 switch (packageSource)
161 {
162 case PackageSource.Embedded:
163 return !ProjectGenerationFlag.HasFlag(ProjectGenerationFlag.Embedded);
164 case PackageSource.Registry:
165 return !ProjectGenerationFlag.HasFlag(ProjectGenerationFlag.Registry);
166 case PackageSource.BuiltIn:
167 return !ProjectGenerationFlag.HasFlag(ProjectGenerationFlag.BuiltIn);
168 case PackageSource.Unknown:
169 return !ProjectGenerationFlag.HasFlag(ProjectGenerationFlag.Unknown);
170 case PackageSource.Local:
171 return !ProjectGenerationFlag.HasFlag(ProjectGenerationFlag.Local);
172 case PackageSource.Git:
173 return !ProjectGenerationFlag.HasFlag(ProjectGenerationFlag.Git);
174 case PackageSource.LocalTarball:
175 return !ProjectGenerationFlag.HasFlag(ProjectGenerationFlag.LocalTarBall);
176 }
177
178 return false;
179 }
180
181 public ResponseFileData ParseResponseFile(string responseFilePath, string projectDirectory, string[] systemReferenceDirectories)
182 {
183 return CompilationPipeline.ParseResponseFile(
184 responseFilePath,
185 projectDirectory,
186 systemReferenceDirectories
187 );
188 }
189
190 public void ToggleProjectGeneration(ProjectGenerationFlag preference)
191 {
192 if (ProjectGenerationFlag.HasFlag(preference))
193 {
194 ProjectGenerationFlag ^= preference;
195 }
196 else
197 {
198 ProjectGenerationFlag |= preference;
199 }
200 }
201
202 internal void ResetPackageInfoCache()
203 {
204 m_PackageInfoCache.Clear();
205 }
206
207 public void ResetProjectGenerationFlag()
208 {
209 ProjectGenerationFlag = ProjectGenerationFlag.None;
210 }
211
212 public string GetAssemblyName(string assemblyOutputPath, string assemblyName)
213 {
214 if (assemblyOutputPath == PlayerAssemblyOutput)
215 return assemblyName + ".Player";
216
217 return assemblyName;
218 }
219 }
220}