private void ProcessSortedAsmDef(AssemblyDefinitionInfo[] set, ref int currentIndex, Func <Uri, bool> childOfParentFunc, Action <AssemblyDefinitionInfo> addAsChild) { Uri GetUri(DirectoryInfo d) => d.FullName.EndsWith("\\") ? new Uri(d.FullName) : new Uri(d.FullName + "\\"); for (; currentIndex < set.Length;) { AssemblyDefinitionInfo current = set[currentIndex]; addAsChild(current); if (currentIndex + 1 == set.Length) { return; } currentIndex++; AssemblyDefinitionInfo next = set[currentIndex]; Uri potentialBase = GetUri(current.Directory); Uri potentialChild = GetUri(next.Directory); if (!childOfParentFunc(potentialChild)) { return; } else if (potentialBase.IsBaseOf(potentialChild)) { ProcessSortedAsmDef(set, ref currentIndex, potentialBase.IsBaseOf, (a) => current.NestedAssemblyDefinitionFiles.Add(a)); if (!childOfParentFunc(potentialChild)) { return; } } } }
/// <summary> /// Parses an asmdef file creating a new instance of <see cref="AssemblyDefinitionInfo"/>. /// </summary> /// <param name="file">The file representing asmdef.</param> /// <param name="unityProjectInfo">Instance of <see cref="UnityProjectInfo"/>,</param> /// <param name="assembly">The Unity assembly reference.</param> /// <param name="isBuiltInPackage">True whether this asmdef lives in the editor installation folder.</param> /// <returns></returns> public static AssemblyDefinitionInfo Parse(FileInfo file, UnityProjectInfo unityProjectInfo, Assembly assembly, bool isBuiltInPackage = false) { if (file.Extension != ".asmdef") { throw new ArgumentException($"Given file '{file.FullName}' is not an assembly definition file."); } else if (!file.Exists) { throw new ArgumentException($"Given file '{file.FullName}' does not exist."); } AssemblyDefinitionInfo toReturn = JsonUtility.FromJson <AssemblyDefinitionInfo>(File.ReadAllText(file.FullName)); if (!Utilities.TryGetGuidForAsset(file, out Guid guid)) { Debug.LogError($"Failed to parse AsmDef meta for asm def: '{file.FullName}', didn't find guid."); } toReturn.assembly = assembly; toReturn.Directory = file.Directory; toReturn.AssetLocation = Utilities.GetAssetLocation(file); toReturn.file = file; toReturn.Guid = guid; toReturn.BuiltInPackage = isBuiltInPackage; toReturn.Validate(unityProjectInfo.AvailablePlatforms); toReturn.PrecompiledAssemblyReferences = new HashSet <string>(toReturn.precompiledReferences?.Select(t => t.Replace(".dll", string.Empty)) ?? Array.Empty <string>()); return(toReturn); }
/// <summary> /// Exports the project given a template. /// </summary> /// <param name="projectFileTemplateText">The template of the csproj file.</param> /// <param name="projectFilesPath">The output folder where all the props were added.</param> internal void ExportProject(string projectFileTemplateText, string projectFilesPath) { if (File.Exists(ReferencePath.AbsolutePath)) { File.Delete(ReferencePath.AbsolutePath); } if (Utilities.TryGetXMLTemplate(projectFileTemplateText, "PROJECT_REFERENCE_SET", out string projectReferenceSetTemplate) && Utilities.TryGetXMLTemplate(projectFileTemplateText, "SOURCE_INCLUDE", out string sourceIncludeTemplate) && Utilities.TryGetXMLTemplate(projectFileTemplateText, "SUPPORTED_PLATFORM_BUILD_CONDITION", out string suportedPlatformBuildConditionTemplate)) { List <string> sourceIncludes = new List <string>(); Dictionary <Guid, string> sourceGuidToClassName = new Dictionary <Guid, string>(); foreach (SourceFileInfo source in AssemblyDefinitionInfo.GetSources()) { ProcessSourceFile(source, sourceIncludeTemplate, sourceIncludes, sourceGuidToClassName); } File.WriteAllLines(Path.Combine(projectFilesPath, $"{Guid.ToString()}.csmap"), sourceGuidToClassName.Select(t => $"{t.Key.ToString("N")}:{t.Value}")); List <string> supportedPlatformBuildConditions = new List <string>(); PopulateSupportedPlatformBuildConditions(supportedPlatformBuildConditions, suportedPlatformBuildConditionTemplate, "InEditor", InEditorPlatforms); PopulateSupportedPlatformBuildConditions(supportedPlatformBuildConditions, suportedPlatformBuildConditionTemplate, "Player", PlayerPlatforms); HashSet <string> inEditorSearchPaths = new HashSet <string>(), playerSearchPaths = new HashSet <string>(); string projectReferences = string.Join("\r\n", CreateProjectReferencesSet(projectReferenceSetTemplate, inEditorSearchPaths, true), CreateProjectReferencesSet(projectReferenceSetTemplate, playerSearchPaths, false)); Dictionary <string, string> tokens = new Dictionary <string, string>() { { "<!--PROJECT_GUID_TOKEN-->", Guid.ToString() }, { "<!--ALLOW_UNSAFE_TOKEN-->", AssemblyDefinitionInfo.allowUnsafeCode.ToString() }, { "<!--LANGUAGE_VERSION_TOKEN-->", MSBuildTools.CSharpVersion }, { "<!--DEVELOPMENT_BUILD_TOKEN-->", "false" }, // Default to false { "<!--IS_EDITOR_ONLY_TARGET_TOKEN-->", (ProjectType == ProjectType.EditorAsmDef || ProjectType == ProjectType.PredefinedEditorAssembly).ToString() }, { "<!--UNITY_EDITOR_INSTALL_FOLDER-->", Path.GetDirectoryName(EditorApplication.applicationPath) + "\\" }, { "<!--DEFAULT_PLATFORM_TOKEN-->", UnityProjectInfo.AvailablePlatforms.First(t => t.BuildTarget == BuildTarget.StandaloneWindows).Name }, { "<!--SUPPORTED_PLATFORMS_TOKEN-->", string.Join(";", UnityProjectInfo.AvailablePlatforms.Select(t => t.Name)) }, { "<!--INEDITOR_ASSEMBLY_SEARCH_PATHS_TOKEN-->", string.Join(";", inEditorSearchPaths) }, { "<!--PLAYER_ASSEMBLY_SEARCH_PATHS_TOKEN-->", string.Join(";", playerSearchPaths) }, { "##PLATFORM_PROPS_FOLDER_PATH_TOKEN##", projectFilesPath }, { projectReferenceSetTemplate, projectReferences }, { sourceIncludeTemplate, string.Join("\r\n", sourceIncludes) }, { suportedPlatformBuildConditionTemplate, string.Join("\r\n", supportedPlatformBuildConditions) } }; projectFileTemplateText = Utilities.ReplaceTokens(projectFileTemplateText, tokens, true); } else { Debug.LogError("Failed to find ProjectReferenceSet and/or Source_Include templates in the project template file."); } File.WriteAllText(ReferencePath.AbsolutePath, projectFileTemplateText); }
/// <summary> /// Creates an instance of <see cref="AssemblyDefinitionInfo"/> for the default projects (such as Assembly-CSharp) /// </summary> /// <param name="assembly">The Unity assembly reference.</param> /// <returns>A new instance.</returns> public static AssemblyDefinitionInfo GetDefaultAssemblyCSharpInfo(Assembly assembly) { AssemblyDefinitionInfo toReturn = new AssemblyDefinitionInfo() { IsDefaultAssembly = true, Guid = Guid.NewGuid(), Directory = new DirectoryInfo(Utilities.AssetPath) }; toReturn.assembly = assembly; toReturn.name = assembly.name; toReturn.references = assembly.assemblyReferences.Select(t => t.name).ToArray(); toReturn.PrecompiledAssemblyReferences = new HashSet <string>(); return(toReturn); }
/// <summary> /// Creates a new instance of the CSProject info. /// </summary> /// <param name="unityProjectInfo">Instance of parsed unity project info.</param> /// <param name="guid">The unique Guid of this reference item.</param> /// <param name="assemblyDefinitionInfo">The associated Assembly-Definition info.</param> /// <param name="assembly">The Unity assembly object associated with this csproj.</param> /// <param name="baseOutputPath">The output path where everything will be outputted.</param> internal CSProjectInfo(UnityProjectInfo unityProjectInfo, AssemblyDefinitionInfo assemblyDefinitionInfo, string baseOutputPath) : base(unityProjectInfo, assemblyDefinitionInfo.Guid, new Uri(Path.Combine(baseOutputPath, $"{assemblyDefinitionInfo.Name}.csproj")), assemblyDefinitionInfo.Name) { AssemblyDefinitionInfo = assemblyDefinitionInfo; ProjectType = GetProjectType(assemblyDefinitionInfo); InEditorPlatforms = GetCompilationPlatforms(true); PlayerPlatforms = GetCompilationPlatforms(false); if (InEditorPlatforms.Count == 0 && PlayerPlatforms.Count == 0) { Debug.LogError($"The assembly project '{Name}' doesn't contain any supported in-editor or player platform targets."); } ProjectDependencies = new ReadOnlyCollection <CSProjectDependency <CSProjectInfo> >(csProjectDependencies); }
/// <summary> /// Creates a new instance of the CSProject info. /// </summary> /// <param name="unityProjectInfo">Instance of parsed unity project info.</param> /// <param name="guid">The unique Guid of this reference item.</param> /// <param name="assemblyDefinitionInfo">The associated Assembly-Definition info.</param> /// <param name="assembly">The Unity assembly object associated with this csproj.</param> internal CSProjectInfo(UnityProjectInfo unityProjectInfo, AssemblyDefinitionInfo assemblyDefinitionInfo) : base(unityProjectInfo, assemblyDefinitionInfo.Guid, assemblyDefinitionInfo.Name) { AssemblyDefinitionInfo = assemblyDefinitionInfo; ProjectType = GetProjectType(assemblyDefinitionInfo); InEditorPlatforms = GetCompilationPlatforms(true); PlayerPlatforms = GetCompilationPlatforms(false); if (InEditorPlatforms.Count == 0 && PlayerPlatforms.Count == 0) { Debug.LogError($"The assembly project '{Name}' doesn't contain any supported in-editor or player platform targets."); } ProjectDependencies = new ReadOnlyCollection <CSProjectDependency <CSProjectInfo> >(csProjectDependencies); PluginDependencies = new ReadOnlyCollection <CSProjectDependency <PluginAssemblyInfo> >(pluginAssemblyDependencies); WinMDDependencies = new ReadOnlyCollection <CSProjectDependency <WinMDInfo> >(winmdDependencies); }
private ProjectType GetProjectType(AssemblyDefinitionInfo assemblyDefinitionInfo) { if (!assemblyDefinitionInfo.IsDefaultAssembly) { return(assemblyDefinitionInfo.EditorPlatformSupported && !assemblyDefinitionInfo.NonEditorPlatformSupported ? ProjectType.EditorAsmDef : ProjectType.AsmDef); } switch (assemblyDefinitionInfo.Name) { case "Assembly-CSharp": case "Assembly-CSharp-firstpass": return(ProjectType.PredefinedAssembly); case "Assembly-CSharp-Editor": case "Assembly-CSharp-Editor-firstpass": return(ProjectType.PredefinedEditorAssembly); default: throw new InvalidOperationException($"Predefined assembly '{assemblyDefinitionInfo.Name}' was not recognized, this generally means it should be added to the switch statement in CSProjectInfo:GetProjectType. Treating is as a PredefinedAssembly instead of PredefinedEditorAssembly."); } }
private Dictionary <string, CSProjectInfo> CreateUnityProjects() { // Not all of these will be converted to C# objects, only the ones found to be referenced Dictionary <string, AssemblyDefinitionInfo> asmDefInfoMap = new Dictionary <string, AssemblyDefinitionInfo>(); SortedSet <AssemblyDefinitionInfo> asmDefDirectoriesSorted = new SortedSet <AssemblyDefinitionInfo>(Comparer <AssemblyDefinitionInfo> .Create((a, b) => a.Directory.FullName.CompareTo(b.Directory.FullName))); HashSet <string> builtInPackagesWithoutSource = new HashSet <string>(); // Parse the builtInPackagesFirst DirectoryInfo builtInPackagesDirectory = new DirectoryInfo(Utilities.BuiltInPackagesPath); foreach (DirectoryInfo packageDirectory in builtInPackagesDirectory.GetDirectories()) { FileInfo[] asmDefFiles = packageDirectory.GetFiles("*.asmdef", SearchOption.AllDirectories); if (asmDefFiles.Length == 0) { builtInPackagesWithoutSource.Add(packageDirectory.Name.ToLower()); continue; } foreach (FileInfo fileInfo in asmDefFiles) { AssemblyDefinitionInfo assemblyDefinitionInfo = AssemblyDefinitionInfo.Parse(fileInfo, this, null, true); asmDefDirectoriesSorted.Add(assemblyDefinitionInfo); asmDefInfoMap.Add(Path.GetFileNameWithoutExtension(fileInfo.Name), assemblyDefinitionInfo); } } Dictionary <string, string> packageCacheVersionedMap = new Dictionary <string, string>(); foreach (string directory in Directory.GetDirectories(Utilities.PackageLibraryCachePath)) { string directoryName = Path.GetFileName(directory); packageCacheVersionedMap.Add(directoryName.Split('@')[0], directoryName); } Dictionary <string, Assembly> unityAssemblies = CompilationPipeline.GetAssemblies().ToDictionary(t => t.name); Dictionary <string, CSProjectInfo> projectsMap = new Dictionary <string, CSProjectInfo>(); Queue <string> projectsToProcess = new Queue <string>(); // Parse the unity assemblies foreach (KeyValuePair <string, Assembly> pair in unityAssemblies) { if (!asmDefInfoMap.TryGetValue(pair.Key, out AssemblyDefinitionInfo assemblyDefinitionInfo)) { string asmDefPath = CompilationPipeline.GetAssemblyDefinitionFilePathFromAssemblyName(pair.Key); if (string.IsNullOrEmpty(asmDefPath)) { if (!pair.Key.StartsWith("Assembly-CSharp")) { throw new InvalidOperationException($"Failed to retrieve AsmDef for script assembly: {pair.Key}"); } Guid guid; switch (pair.Key) { case "Assembly-CSharp": guid = config.AssemblyCSharpGuid; break; case "Assembly-CSharp-firstpass": guid = config.AssemblyCSharpFirstPassGuid; break; case "Assembly-CSharp-Editor": guid = config.AssemblyCSharpEditorGuid; break; case "Assembly-CSharp-Editor-firstpass": guid = config.AssemblyCSharpFirstPassEditorGuid; break; default: throw new InvalidOperationException($"Predefined assembly '{assemblyDefinitionInfo.Name}' was not recognized, this generally means it should be added to the switch statement in CSProjectInfo:GetProjectType."); } assemblyDefinitionInfo = AssemblyDefinitionInfo.GetDefaultAssemblyCSharpInfo(pair.Value, guid); projectsToProcess.Enqueue(pair.Key); } else { assemblyDefinitionInfo = AssemblyDefinitionInfo.Parse(new FileInfo(Utilities.GetFullPathFromKnownRelative(asmDefPath)), this, pair.Value); if (asmDefPath.StartsWith("Assets/")) { // Add as mandatory projectsToProcess.Enqueue(pair.Key); } } asmDefDirectoriesSorted.Add(assemblyDefinitionInfo); asmDefInfoMap.Add(pair.Key, assemblyDefinitionInfo); } } // This will parse additional asmdefs that are not part of current compilation set, but we still need foreach (string asmdefGuid in AssetDatabase.FindAssets("t:asmdef")) { string asmDefPath = AssetDatabase.GUIDToAssetPath(asmdefGuid); string asmDefKey = Path.GetFileNameWithoutExtension(asmDefPath); if (!asmDefInfoMap.ContainsKey(asmDefKey)) { AssemblyDefinitionInfo assemblyDefinitionInfo = AssemblyDefinitionInfo.Parse(new FileInfo(Utilities.GetFullPathFromKnownRelative(asmDefPath)), this, null); asmDefDirectoriesSorted.Add(assemblyDefinitionInfo); asmDefInfoMap.Add(asmDefKey, assemblyDefinitionInfo); } } // Now we have all of the assembly definiton files, let's run a quick validation. ValidateAndPatchAssemblyDefinitions(asmDefInfoMap); int index = 0; ProcessSortedAsmDef(asmDefDirectoriesSorted.ToArray(), ref index, (uri) => true, (a) => { }); while (projectsToProcess.Count > 0) { string projectKey = projectsToProcess.Dequeue(); if (!projectsMap.ContainsKey(projectKey)) { GetProjectInfo(projectsMap, asmDefInfoMap, builtInPackagesWithoutSource, projectKey); } } return(projectsMap); }
private Dictionary <string, CSProjectInfo> CreateUnityProjects(string projectOutputPath) { // Not all of these will be converted to C# objects, only the ones found to be referenced Dictionary <string, AssemblyDefinitionInfo> asmDefInfoMap = new Dictionary <string, AssemblyDefinitionInfo>(); HashSet <string> builtInPackagesWithoutSource = new HashSet <string>(); // Parse the builtInPackagesFirst DirectoryInfo builtInPackagesDirectory = new DirectoryInfo(Utilities.BuiltInPackagesPath); foreach (DirectoryInfo packageDirectory in builtInPackagesDirectory.GetDirectories()) { FileInfo[] asmDefFiles = packageDirectory.GetFiles("*.asmdef", SearchOption.AllDirectories); if (asmDefFiles.Length == 0) { builtInPackagesWithoutSource.Add(packageDirectory.Name.ToLower()); continue; } foreach (FileInfo fileInfo in asmDefFiles) { AssemblyDefinitionInfo assemblyDefinitionInfo = AssemblyDefinitionInfo.Parse(fileInfo, this, null, true); asmDefInfoMap.Add(Path.GetFileNameWithoutExtension(fileInfo.Name), assemblyDefinitionInfo); } } Dictionary <string, Assembly> unityAssemblies = CompilationPipeline.GetAssemblies().ToDictionary(t => t.name); Dictionary <string, CSProjectInfo> projectsMap = new Dictionary <string, CSProjectInfo>(); Queue <string> projectsToProcess = new Queue <string>(); // Parse the unity assemblies foreach (KeyValuePair <string, Assembly> pair in unityAssemblies) { if (!asmDefInfoMap.TryGetValue(pair.Key, out AssemblyDefinitionInfo assemblyDefinitionInfo)) { string asmDefPath = CompilationPipeline.GetAssemblyDefinitionFilePathFromAssemblyName(pair.Key); if (string.IsNullOrEmpty(asmDefPath)) { if (!pair.Key.StartsWith("Assembly-CSharp")) { throw new InvalidOperationException($"Failed to retrieve AsmDef for script assembly: {pair.Key}"); } assemblyDefinitionInfo = AssemblyDefinitionInfo.GetDefaultAssemblyCSharpInfo(pair.Value); projectsToProcess.Enqueue(pair.Key); } else { assemblyDefinitionInfo = AssemblyDefinitionInfo.Parse(new FileInfo(Utilities.GetFullPathFromKnownRelative(asmDefPath)), this, pair.Value); if (asmDefPath.StartsWith("Assets/")) { // Add as mandatory projectsToProcess.Enqueue(pair.Key); } } asmDefInfoMap.Add(pair.Key, assemblyDefinitionInfo); } } while (projectsToProcess.Count > 0) { string projectKey = projectsToProcess.Dequeue(); if (!projectsMap.ContainsKey(projectKey)) { GetProjectInfo(projectsMap, asmDefInfoMap, builtInPackagesWithoutSource, projectKey, projectOutputPath); } } return(projectsMap); }