/*--------------------------------------------------------------------------------------------- * Copyright (c) Unity Technologies. * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ using System; using System.Collections.Generic; using System.Linq; using UnityEditor; using UnityEditor.Compilation; using UnityEditor.PackageManager; namespace Microsoft.Unity.VisualStudio.Editor { public interface IAssemblyNameProvider { string[] ProjectSupportedExtensions { get; } string ProjectGenerationRootNamespace { get; } ProjectGenerationFlag ProjectGenerationFlag { get; } string GetAssemblyNameFromScriptPath(string path); string GetAssemblyName(string assemblyOutputPath, string assemblyName); bool IsInternalizedPackagePath(string path); IEnumerable GetAssemblies(Func shouldFileBePartOfSolution); IEnumerable GetAllAssetPaths(); UnityEditor.PackageManager.PackageInfo FindForAssetPath(string assetPath); ResponseFileData ParseResponseFile(string responseFilePath, string projectDirectory, string[] systemReferenceDirectories); void ToggleProjectGeneration(ProjectGenerationFlag preference); } public class AssemblyNameProvider : IAssemblyNameProvider { private readonly Dictionary m_PackageInfoCache = new Dictionary(); ProjectGenerationFlag m_ProjectGenerationFlag = (ProjectGenerationFlag)EditorPrefs.GetInt( "unity_project_generation_flag", (int)(ProjectGenerationFlag.Local | ProjectGenerationFlag.Embedded)); public string[] ProjectSupportedExtensions => EditorSettings.projectGenerationUserExtensions; public string ProjectGenerationRootNamespace => EditorSettings.projectGenerationRootNamespace; public ProjectGenerationFlag ProjectGenerationFlag { get { return ProjectGenerationFlagImpl; } private set { ProjectGenerationFlagImpl = value;} } internal virtual ProjectGenerationFlag ProjectGenerationFlagImpl { get => m_ProjectGenerationFlag; private set { EditorPrefs.SetInt("unity_project_generation_flag", (int)value); m_ProjectGenerationFlag = value; } } public string GetAssemblyNameFromScriptPath(string path) { return CompilationPipeline.GetAssemblyNameFromScriptPath(path); } internal static readonly string AssemblyOutput = @"Temp\bin\Debug\".NormalizePathSeparators(); internal static readonly string PlayerAssemblyOutput = @"Temp\bin\Debug\Player\".NormalizePathSeparators(); public IEnumerable GetAssemblies(Func shouldFileBePartOfSolution) { IEnumerable assemblies = GetAssembliesByType(AssembliesType.Editor, shouldFileBePartOfSolution, AssemblyOutput); if (!ProjectGenerationFlag.HasFlag(ProjectGenerationFlag.PlayerAssemblies)) { return assemblies; } var playerAssemblies = GetAssembliesByType(AssembliesType.Player, shouldFileBePartOfSolution, PlayerAssemblyOutput); return assemblies.Concat(playerAssemblies); } private static IEnumerable GetAssembliesByType(AssembliesType type, Func shouldFileBePartOfSolution, string outputPath) { foreach (var assembly in CompilationPipeline.GetAssemblies(type)) { if (assembly.sourceFiles.Any(shouldFileBePartOfSolution)) { yield return new Assembly( assembly.name, outputPath, assembly.sourceFiles, assembly.defines, assembly.assemblyReferences, assembly.compiledAssemblyReferences, assembly.flags, assembly.compilerOptions #if UNITY_2020_2_OR_NEWER , assembly.rootNamespace #endif ); } } } public string GetCompileOutputPath(string assemblyName) { // We need to keep this one for API surface check (AssemblyNameProvider is public), but not used anymore throw new NotImplementedException(); } public IEnumerable GetAllAssetPaths() { return AssetDatabase.GetAllAssetPaths(); } private static string ResolvePotentialParentPackageAssetPath(string assetPath) { const string packagesPrefix = "packages/"; if (!assetPath.StartsWith(packagesPrefix, StringComparison.OrdinalIgnoreCase)) { return null; } var followupSeparator = assetPath.IndexOf('/', packagesPrefix.Length); if (followupSeparator == -1) { return assetPath.ToLowerInvariant(); } return assetPath.Substring(0, followupSeparator).ToLowerInvariant(); } public UnityEditor.PackageManager.PackageInfo FindForAssetPath(string assetPath) { var parentPackageAssetPath = ResolvePotentialParentPackageAssetPath(assetPath); if (parentPackageAssetPath == null) { return null; } if (m_PackageInfoCache.TryGetValue(parentPackageAssetPath, out var cachedPackageInfo)) { return cachedPackageInfo; } var result = UnityEditor.PackageManager.PackageInfo.FindForAssetPath(parentPackageAssetPath); m_PackageInfoCache[parentPackageAssetPath] = result; return result; } public bool IsInternalizedPackagePath(string path) { if (string.IsNullOrEmpty(path.Trim())) { return false; } var packageInfo = FindForAssetPath(path); if (packageInfo == null) { return false; } var packageSource = packageInfo.source; switch (packageSource) { case PackageSource.Embedded: return !ProjectGenerationFlag.HasFlag(ProjectGenerationFlag.Embedded); case PackageSource.Registry: return !ProjectGenerationFlag.HasFlag(ProjectGenerationFlag.Registry); case PackageSource.BuiltIn: return !ProjectGenerationFlag.HasFlag(ProjectGenerationFlag.BuiltIn); case PackageSource.Unknown: return !ProjectGenerationFlag.HasFlag(ProjectGenerationFlag.Unknown); case PackageSource.Local: return !ProjectGenerationFlag.HasFlag(ProjectGenerationFlag.Local); case PackageSource.Git: return !ProjectGenerationFlag.HasFlag(ProjectGenerationFlag.Git); case PackageSource.LocalTarball: return !ProjectGenerationFlag.HasFlag(ProjectGenerationFlag.LocalTarBall); } return false; } public ResponseFileData ParseResponseFile(string responseFilePath, string projectDirectory, string[] systemReferenceDirectories) { return CompilationPipeline.ParseResponseFile( responseFilePath, projectDirectory, systemReferenceDirectories ); } public void ToggleProjectGeneration(ProjectGenerationFlag preference) { if (ProjectGenerationFlag.HasFlag(preference)) { ProjectGenerationFlag ^= preference; } else { ProjectGenerationFlag |= preference; } } internal void ResetPackageInfoCache() { m_PackageInfoCache.Clear(); } public void ResetProjectGenerationFlag() { ProjectGenerationFlag = ProjectGenerationFlag.None; } public string GetAssemblyName(string assemblyOutputPath, string assemblyName) { if (assemblyOutputPath == PlayerAssemblyOutput) return assemblyName + ".Player"; return assemblyName; } } }