public MonoIsland ToMonoIsland(EditorScriptCompilationOptions options, string buildOutputDirectory, string projectPath = null) { bool buildingForEditor = (options & EditorScriptCompilationOptions.BuildingForEditor) == EditorScriptCompilationOptions.BuildingForEditor; bool developmentBuild = (options & EditorScriptCompilationOptions.BuildingDevelopmentBuild) == EditorScriptCompilationOptions.BuildingDevelopmentBuild; var references = ScriptAssemblyReferences.Select(a => AssetPath.Combine(a.OutputDirectory, a.Filename)); var referencesArray = references.Concat(References).ToArray(); var responseFileProvider = Language?.CreateResponseFileProvider(); if (!string.IsNullOrEmpty(projectPath) && responseFileProvider != null) { responseFileProvider.ProjectPath = projectPath; } List <string> reposeFiles = responseFileProvider?.Get(OriginPath) ?? new List <string>(); var outputPath = AssetPath.Combine(buildOutputDirectory, Filename); return(new MonoIsland(BuildTarget, buildingForEditor, developmentBuild, CompilerOptions.AllowUnsafeCode, CompilerOptions.ApiCompatibilityLevel, Files, referencesArray, Defines, outputPath, reposeFiles.ToArray())); }
public static bool IsReferenceAssemblyUnchanged(ScriptAssembly assembly, string outputDirectory) { var referenceAssemblyPath = AssetPath.Combine(outputDirectory, assembly.ReferenceAssemblyFilename); var assemblyFileHashPath = AssetPath.Combine(outputDirectory, $"{assembly.Filename}.hash"); return(IsFileUnchanged(referenceAssemblyPath, assemblyFileHashPath)); }
public static void UpdateScriptAssemblyReference(ref ScriptAssembly scriptAssembly) { int referencesLength = scriptAssembly.References.Length; var newReferences = new string[referencesLength + 1]; Array.Copy(scriptAssembly.References, newReferences, referencesLength); newReferences[referencesLength] = AssetPath.Combine(EditorApplication.applicationContentsPath, "Managed", "Unity.CompilationPipeline.Common.dll"); scriptAssembly.References = newReferences; }
public static ScriptAssembly[] GetAllScriptAssemblies( Dictionary <string, string> allSourceFiles, String projectDirectory, ScriptAssemblySettings settings, CompilationAssemblies assemblies, ISafeModeInfo safeModeInfo, TargetAssemblyType onlyIncludeType = TargetAssemblyType.Undefined, Func <TargetAssembly, bool> targetAssemblyCondition = null, ICompilationSetupWarningTracker warningSink = null) { if (allSourceFiles == null || allSourceFiles.Count == 0) { return(new ScriptAssembly[0]); } var targetAssemblyFiles = new Dictionary <TargetAssembly, DirtyTargetAssembly>(); foreach (var entry in allSourceFiles) { var scriptFile = entry.Key; var assemblyName = entry.Value; var targetAssembly = GetTargetAssembly(scriptFile, assemblyName, projectDirectory, assemblies.CustomTargetAssemblies); if (targetAssembly == null) { continue; } if (!IsCompatibleWithPlatformAndDefines(targetAssembly, settings)) { continue; } if (targetAssemblyCondition != null && !targetAssemblyCondition(targetAssembly)) { continue; } // Optionally only include specific TargetAssemblyType assemblies. if (onlyIncludeType != TargetAssemblyType.Undefined && targetAssembly.Type != onlyIncludeType) { continue; } DirtyTargetAssembly dirtyTargetAssembly; if (!targetAssemblyFiles.TryGetValue(targetAssembly, out dirtyTargetAssembly)) { dirtyTargetAssembly = new DirtyTargetAssembly(); targetAssemblyFiles[targetAssembly] = dirtyTargetAssembly; } dirtyTargetAssembly.SourceFiles.Add(AssetPath.Combine(projectDirectory, scriptFile)); } return(ToScriptAssemblies(targetAssemblyFiles, settings, assemblies, warningSink, safeModeInfo)); }
public MonoIsland ToMonoIsland(EditorScriptCompilationOptions options, string buildOutputDirectory) { bool editor = (options & EditorScriptCompilationOptions.BuildingForEditor) == EditorScriptCompilationOptions.BuildingForEditor; bool development_player = (options & EditorScriptCompilationOptions.BuildingDevelopmentBuild) == EditorScriptCompilationOptions.BuildingDevelopmentBuild; IEnumerable <string> first = from a in this.ScriptAssemblyReferences select AssetPath.Combine(a.OutputDirectory, a.Filename); string[] references = first.Concat(this.References).ToArray <string>(); string output = AssetPath.Combine(buildOutputDirectory, this.Filename); return(new MonoIsland(this.BuildTarget, editor, development_player, this.ApiCompatibilityLevel, this.Files, references, this.Defines, output)); }
public static ScriptAssembly[] GetAllScriptAssemblies(IEnumerable <string> allSourceFiles, string projectDirectory, ScriptAssemblySettings settings, CompilationAssemblies assemblies, TargetAssemblyType onlyIncludeType = TargetAssemblyType.Undefined) { if (allSourceFiles == null || allSourceFiles.Count() == 0) { return(new ScriptAssembly[0]); } var targetAssemblyFiles = new Dictionary <TargetAssembly, HashSet <string> >(); foreach (var scriptFile in allSourceFiles) { var targetAssembly = GetTargetAssembly(scriptFile, projectDirectory, assemblies.CustomTargetAssemblies); if (!IsCompatibleWithPlatform(targetAssembly, settings)) { continue; } // Optionally only include specific TargetAssemblyType assemblies. if (onlyIncludeType != TargetAssemblyType.Undefined && targetAssembly.Type != onlyIncludeType) { continue; } var scriptExtension = ScriptCompilers.GetExtensionOfSourceFile(scriptFile); var scriptLanguage = ScriptCompilers.GetLanguageFromExtension(scriptExtension); if (targetAssembly.Language == null && targetAssembly.Type == TargetAssemblyType.Custom) { targetAssembly.Language = scriptLanguage; } HashSet <string> assemblySourceFiles; if (!targetAssemblyFiles.TryGetValue(targetAssembly, out assemblySourceFiles)) { assemblySourceFiles = new HashSet <string>(); targetAssemblyFiles[targetAssembly] = assemblySourceFiles; } assemblySourceFiles.Add(AssetPath.Combine(projectDirectory, scriptFile)); } return(ToScriptAssemblies(targetAssemblyFiles, settings, assemblies, null)); }
public void Start() { var runner = Paths.Combine(EditorApplication.applicationContentsPath, "Tools", "ILPostProcessorRunner", "ILPostProcessorRunner.exe"); if (!File.Exists(runner)) { throw new Exception(string.Format($"'{runner}' not found. Is your Unity installation corrupted?")); } var assemblyPath = AssetPath.GetFullPath(AssetPath.Combine(tempOutputDirectory, Assembly.Filename)); var assemblyReferencesPaths = Assembly.GetAllReferences().ToArray(); var assemblyFolderPaths = ilPostProcessing.AssemblySearchPaths; var outputDirectory = AssetPath.GetFullPath(tempOutputDirectory); var postProcessorPaths = ilPostProcessing.PostProcessorAssemblyPaths; var arguments = new List <string> { $"-a \"{assemblyPath}\"", $"-f \"{string.Join(",", assemblyFolderPaths)}\"", $"-r \"{string.Join(",", assemblyReferencesPaths)}\"", $"-d \"{string.Join(",", Assembly.Defines)}\"", $"-p \"{string.Join(",", postProcessorPaths)}\"", $"-o \"{outputDirectory}\"", }; var responseFilePath = PrepareFileName(AssetPath.GetFullPath(CommandLineFormatter.GenerateResponseFile(arguments))); var args = $"@{responseFilePath}"; // Always run on Mono until all ILPostProcessors have been fixed // to run on NET Core. //if (NetCoreRunProgram.IsSupported()) //{ // process = new NetCoreRunProgram(runner, args, null); //} //else { process = new ManagedProgram(MonoInstallationFinder.GetMonoInstallation("MonoBleedingEdge"), null, runner, args, false, null); } process.Start(); }
public MonoIsland ToMonoIsland(EditorScriptCompilationOptions options, string buildOutputDirectory) { bool buildingForEditor = (options & EditorScriptCompilationOptions.BuildingForEditor) == EditorScriptCompilationOptions.BuildingForEditor; bool developmentBuild = (options & EditorScriptCompilationOptions.BuildingDevelopmentBuild) == EditorScriptCompilationOptions.BuildingDevelopmentBuild; var references = ScriptAssemblyReferences.Select(a => AssetPath.Combine(a.OutputDirectory, a.Filename)); var referencesArray = references.Concat(References).ToArray(); var outputPath = AssetPath.Combine(buildOutputDirectory, Filename); return(new MonoIsland(BuildTarget, buildingForEditor, developmentBuild, CompilerOptions.AllowUnsafeCode, ApiCompatibilityLevel, Files, referencesArray, Defines, outputPath)); }
public static void UpdateCodeGenScriptAssembly(ref ScriptAssembly scriptAssembly) { scriptAssembly.ScriptAssemblyReferences = new ScriptAssembly[0]; int newReferenceCount = 0; var references = new string[scriptAssembly.References.Length]; foreach (var reference in scriptAssembly.References) { var name = AssetPath.GetFileName(reference); if (!Utility.FastStartsWith(name, k_UnityEngineModules, k_UnityEngineModulesLower) && !Utility.FastStartsWith(name, k_UnityEditorModules, k_UnityEditorModulesLower)) { references[newReferenceCount] = reference; newReferenceCount++; } } var result = new string[newReferenceCount + 1]; Array.Copy(references, result, newReferenceCount); result[newReferenceCount] = AssetPath.Combine(EditorApplication.applicationContentsPath, "Managed", "Unity.CompilationPipeline.Common.dll"); scriptAssembly.References = result; }
internal static TargetAssembly GetCustomTargetAssembly(string scriptPath, string projectDirectory, IDictionary <string, TargetAssembly> customTargetAssemblies) { if (customTargetAssemblies == null) { return(null); } int highestPathDepth = -1; TargetAssembly resultAssembly = null; // CustomScriptAssembly paths are absolute, so we convert the scriptPath to an absolute path, if necessary. bool isPathAbsolute = AssetPath.IsPathRooted(scriptPath); var fullPath = isPathAbsolute ? AssetPath.GetFullPath(scriptPath) : AssetPath.Combine(projectDirectory, scriptPath); foreach (var entry in customTargetAssemblies) { var assembly = entry.Value; if (assembly.MaxPathLength <= highestPathDepth) { continue; } int pathDepth = assembly.PathFilter(fullPath); if (pathDepth <= highestPathDepth) { continue; } resultAssembly = assembly; highestPathDepth = pathDepth; } return(resultAssembly); }
// Returns true when compilation is finished due to one of these reasons // * Was stopped (CompilationTask.Stopped will be true) // * Compilation had errors (CompilationTask.CompileErrors will be true) // * Compilation succesfully completed without errors. public bool Poll() { HandleOnCompilationTaskStarted(); if (Stopped) { HandleOnCompilationTaskFinished(); return(true); } Dictionary <ScriptAssembly, ScriptCompilerBase> finishedCompilerTasks = null; // Check if any compiler processes are finished. foreach (var task in compilerTasks) { var compiler = task.Value; // Did compiler task finish? if (compiler.Poll()) { if (finishedCompilerTasks == null) { finishedCompilerTasks = new Dictionary <ScriptAssembly, ScriptCompilerBase>(); } var assembly = task.Key; finishedCompilerTasks.Add(assembly, compiler); } } // Save compiler messages from finished compiler processes and check for compile errors. if (finishedCompilerTasks != null) { foreach (var task in finishedCompilerTasks) { var assembly = task.Key; var compiler = task.Value; var messages = compiler.GetCompilerMessages(); var messagesList = messages.ToList(); compiledAssemblies.Add(assembly, messagesList.ToArray()); bool havePostProcessors = ilPostProcessing != null && ilPostProcessing.HasPostProcessors; bool isCodeGenAssembly = codeGenAssemblies.Contains(assembly); bool hasCompileErrors = messagesList.Any(m => m.type == CompilerMessageType.Error); if (isCodeGenAssembly) { if (hasCompileErrors) { notCompiledCodeGenAssemblies.Add(assembly); } else { compiledCodeGenAssemblies.Add(assembly); } } if (havePostProcessors && notCompiledCodeGenAssemblies.Count == 0 && !hasCompileErrors && !isCodeGenAssembly) { var assemblySourcePath = AssetPath.Combine(buildOutputDirectory, assembly.Filename); var pdbSourcePath = AssetPath.Combine(buildOutputDirectory, assembly.PdbFilename); try { if (assemblySourcePath != assembly.FullPath) { File.Copy(assemblySourcePath, assembly.FullPath, true); } if (pdbSourcePath != assembly.PdbFullPath) { File.Copy(pdbSourcePath, assembly.PdbFullPath, true); } var postProcessorTask = new PostProcessorTask(assembly, messagesList, buildOutputDirectory, ilPostProcessing); pendingPostProcessorTasks.Add(postProcessorTask); } catch (IOException e) { UnityEngine.Debug.LogError($"Fail to copy {assemblySourcePath} or {pdbSourcePath} to {AssetPath.GetDirectoryName(assembly.FullPath)} before post processing the assembly. Skipping post processing.\n{e}"); // OnCompilationFinished callbacks might add more compiler messages OnCompilationFinished?.Invoke(assembly, messagesList); processedAssemblies.Add(assembly, messagesList.ToArray()); } } else { // OnCompilationFinished callbacks might add more compiler messages OnCompilationFinished?.Invoke(assembly, messagesList); processedAssemblies.Add(assembly, messagesList.ToArray()); } if (!CompileErrors) { CompileErrors = messagesList.Any(m => m.type == CompilerMessageType.Error); } // If a codgen / IL Post processor has compile errors, clear // pending assemblies waiting for compilation and assemblies // waiting to get post processed. if (isCodeGenAssembly && hasCompileErrors) { pendingPostProcessorTasks.Clear(); pendingAssemblies.Clear(); } compilerTasks.Remove(assembly); compiler.Dispose(); } } if (ilPostProcessing != null && ilPostProcessing.HasPostProcessors) { PollPostProcessors(); } // If StopOnFirstError is set, do not queue assemblies for compilation in case of compile errors. bool stopOnFirstError = (compilationTaskOptions & CompilationTaskOptions.StopOnFirstError) == CompilationTaskOptions.StopOnFirstError; if (stopOnFirstError && CompileErrors) { foreach (var pendingAssembly in pendingAssemblies) { if (UnityCodeGenHelpers.IsCodeGen(pendingAssembly.Filename)) { notCompiledCodeGenAssemblies.Add(pendingAssembly); } } pendingAssemblies.Clear(); if (FinishedCompilation) { HandleOnCompilationTaskFinished(); } return(FinishedCompilation); } // Queue pending assemblies for compilation if we have no running compilers or if compilers have finished. if (compilerTasks.Count == 0 || (finishedCompilerTasks != null && finishedCompilerTasks.Count > 0)) { QueuePendingAssemblies(); } if (FinishedCompilation) { HandleOnCompilationTaskFinished(); } return(FinishedCompilation); }
public string FullPath(string outputDirectory, string filenameSuffix) { return(AssetPath.Combine(outputDirectory, FilenameWithSuffix(filenameSuffix))); }
public static ScriptAssembly[] GenerateChangedScriptAssemblies(GenerateChangedScriptAssembliesArgs args) { var dirtyTargetAssemblies = new Dictionary <TargetAssembly, HashSet <string> >(); var allTargetAssemblies = args.Assemblies.CustomTargetAssemblies == null ? predefinedTargetAssemblies : predefinedTargetAssemblies.Concat(args.Assemblies.CustomTargetAssemblies).ToArray(); // Mark all assemblies that the script updater must be run on as dirty. if (args.RunUpdaterAssemblies != null) { foreach (var assemblyFilename in args.RunUpdaterAssemblies) { var targetAssembly = allTargetAssemblies.First(a => a.FilenameWithSuffix(args.Settings.FilenameSuffix) == assemblyFilename); dirtyTargetAssemblies[targetAssembly] = new HashSet <string>(); } } // Collect all dirty TargetAssemblies foreach (var dirtySourceFile in args.DirtySourceFiles) { var targetAssembly = GetTargetAssembly(dirtySourceFile, args.ProjectDirectory, args.Assemblies.CustomTargetAssemblies); if (!IsCompatibleWithPlatform(targetAssembly, args.Settings)) { continue; } HashSet <string> assemblySourceFiles; var scriptExtension = ScriptCompilers.GetExtensionOfSourceFile(dirtySourceFile); var scriptLanguage = ScriptCompilers.GetLanguageFromExtension(scriptExtension); if (!dirtyTargetAssemblies.TryGetValue(targetAssembly, out assemblySourceFiles)) { assemblySourceFiles = new HashSet <string>(); dirtyTargetAssemblies[targetAssembly] = assemblySourceFiles; if (targetAssembly.Type == TargetAssemblyType.Custom) { targetAssembly.Language = scriptLanguage; } } assemblySourceFiles.Add(AssetPath.Combine(args.ProjectDirectory, dirtySourceFile)); // If there are mixed languages in a custom script folder, mark the assembly to not be compiled. if (scriptLanguage != targetAssembly.Language) { args.NotCompiledTargetAssemblies.Add(targetAssembly); } } bool isAnyCustomScriptAssemblyDirty = dirtyTargetAssemblies.Any(entry => entry.Key.Type == TargetAssemblyType.Custom); // If we have any dirty custom target assemblies, then the predefined target assemblies are marked as dirty, // as the predefined assemblies always reference the custom script assemblies. if (isAnyCustomScriptAssemblyDirty) { foreach (var assembly in predefinedTargetAssemblies) { if (!IsCompatibleWithPlatform(assembly, args.Settings)) { continue; } if (!dirtyTargetAssemblies.ContainsKey(assembly)) { dirtyTargetAssemblies[assembly] = new HashSet <string>(); } } } // Collect any TargetAssemblies that reference the dirty TargetAssemblies, as they will also be dirty. int dirtyAssemblyCount; do { dirtyAssemblyCount = 0; foreach (var assembly in allTargetAssemblies) { if (!IsCompatibleWithPlatform(assembly, args.Settings)) { continue; } // If already dirty, skip. if (dirtyTargetAssemblies.ContainsKey(assembly)) { continue; } foreach (var reference in assembly.References) { if (dirtyTargetAssemblies.ContainsKey(reference)) { dirtyTargetAssemblies[assembly] = new HashSet <string>(); dirtyAssemblyCount++; break; } } } }while (dirtyAssemblyCount > 0); // Add any non-dirty source files that belong to dirty TargetAssemblies foreach (var sourceFile in args.AllSourceFiles) { var targetAssembly = GetTargetAssembly(sourceFile, args.ProjectDirectory, args.Assemblies.CustomTargetAssemblies); if (!IsCompatibleWithPlatform(targetAssembly, args.Settings)) { continue; } HashSet <string> assemblySourceFiles; var scriptExtension = ScriptCompilers.GetExtensionOfSourceFile(sourceFile); var scriptLanguage = ScriptCompilers.GetLanguageFromExtension(scriptExtension); if (targetAssembly.Language == null && targetAssembly.Type == TargetAssemblyType.Custom) { targetAssembly.Language = scriptLanguage; } // If there are mixed languages in a custom script folder, mark the assembly to not be compiled. if (scriptLanguage != targetAssembly.Language) { args.NotCompiledTargetAssemblies.Add(targetAssembly); } if (dirtyTargetAssemblies.TryGetValue(targetAssembly, out assemblySourceFiles)) { assemblySourceFiles.Add(AssetPath.Combine(args.ProjectDirectory, sourceFile)); } } // Remove any target assemblies which have no source files associated with them. dirtyTargetAssemblies = dirtyTargetAssemblies.Where(e => e.Value.Count > 0).ToDictionary(e => e.Key, e => e.Value); // Remove any target assemblies which have been marked as do not compile. foreach (var removeAssembly in args.NotCompiledTargetAssemblies) { dirtyTargetAssemblies.Remove(removeAssembly); } // Convert TargetAssemblies to ScriptAssembiles var scriptAssemblies = ToScriptAssemblies(dirtyTargetAssemblies, args.Settings, args.Assemblies, args.RunUpdaterAssemblies); return(scriptAssemblies); }
public string FullPath(string outputDirectory) { return(AssetPath.Combine(outputDirectory, Filename)); }
public void SetAllCustomScriptAssemblyReferenceJsonsContents(string[] paths, string[] contents) { var assemblyRefs = new List <CustomScriptAssemblyReference>(); var exceptions = new List <Exception>(); // We only construct this lookup if it is required, which is when we are using guids instead of assembly names. Dictionary <string, CustomScriptAssembly> guidsToAssemblies = null; // To check if a path prefix is already being used we use a Dictionary where the key is the prefix and the value is the file path. var prefixToFilePathLookup = m_SkipCustomScriptAssemblyGraphValidation ? null : CustomScriptAssemblies.GroupBy(x => x.PathPrefix).ToDictionary(x => x.First().PathPrefix, x => new List <string>() { x.First().FilePath }, StringComparer.OrdinalIgnoreCase); for (var i = 0; i < paths.Length; ++i) { var path = paths[i]; CustomScriptAssemblyReference loadedCustomScriptAssemblyReference = null; try { var fullPath = AssetPath.IsPathRooted(path) ? AssetPath.GetFullPath(path) : AssetPath.Combine(m_ProjectDirectory, path); if (contents != null) { var jsonContents = contents[i]; loadedCustomScriptAssemblyReference = LoadCustomScriptAssemblyReferenceFromJson(fullPath, jsonContents); } else { loadedCustomScriptAssemblyReference = LoadCustomScriptAssemblyReferenceFromJsonPath(fullPath); } if (!m_SkipCustomScriptAssemblyGraphValidation) { // Check both asmdef and asmref files. if (prefixToFilePathLookup.TryGetValue(loadedCustomScriptAssemblyReference.PathPrefix, out var duplicateFilePaths)) { var filePaths = new List <string> { loadedCustomScriptAssemblyReference.FilePath }; filePaths.AddRange(duplicateFilePaths); throw new AssemblyDefinitionException( $"Folder '{loadedCustomScriptAssemblyReference.PathPrefix}' contains multiple assembly definition files", filePaths.ToArray()); } } // Convert GUID references to assembly names if (GUIDReference.IsGUIDReference(loadedCustomScriptAssemblyReference.Reference)) { // Generate the guid to assembly lookup? guidsToAssemblies = guidsToAssemblies ?? CustomScriptAssemblies.ToDictionary(x => x.GUID); var guid = Utility.FastToLower(GUIDReference.GUIDReferenceToGUID(loadedCustomScriptAssemblyReference.Reference)); if (guidsToAssemblies.TryGetValue(guid, out var foundAssembly)) { loadedCustomScriptAssemblyReference.Reference = foundAssembly.Name; } } } catch (Exception e) { m_CompilationSetupErrorsTracker.SetCompilationSetupErrors(CompilationSetupErrors.LoadError); exceptions.Add(e); } if (loadedCustomScriptAssemblyReference != null) { assemblyRefs.Add(loadedCustomScriptAssemblyReference); if (m_SkipCustomScriptAssemblyGraphValidation) { continue; } if (!prefixToFilePathLookup.TryGetValue(loadedCustomScriptAssemblyReference.PathPrefix, out var duplicateFilePaths)) { duplicateFilePaths = new List <string>(); prefixToFilePathLookup[loadedCustomScriptAssemblyReference.PathPrefix] = duplicateFilePaths; } duplicateFilePaths.Add(loadedCustomScriptAssemblyReference.FilePath); } } CustomScriptAssemblyReferences = assemblyRefs; Exceptions = exceptions.ToArray(); }
// Returns true when compilation is finished due to one of these reasons // * Was stopped (CompilationTask.Stopped will be true) // * Compilation had errors (CompilationTask.CompileErrors will be true) // * Compilation succesfully completed without errors. public bool Poll() { HandleOnCompilationTaskStarted(); if (Stopped) { HandleOnCompilationTaskFinished(); return(true); } Dictionary <ScriptAssembly, ScriptCompilerBase> finishedCompilerTasks = null; // Check if any compiler processes are finished. foreach (var task in compilerTasks) { var compiler = task.Value; // Did compiler task finish? if (compiler.Poll()) { if (finishedCompilerTasks == null) { finishedCompilerTasks = new Dictionary <ScriptAssembly, ScriptCompilerBase>(); } var assembly = task.Key; finishedCompilerTasks.Add(assembly, compiler); } } // Save compiler messages from finished compiler processes and check for compile errors. if (finishedCompilerTasks != null) { foreach (var task in finishedCompilerTasks) { var assembly = task.Key; var compiler = task.Value; var messages = compiler.GetCompilerMessages(); var messagesList = messages.ToList(); compiledAssemblies.Add(assembly, messagesList.ToArray()); if (RunPostProcessors && !messagesList.Any(m => m.type == CompilerMessageType.Error)) { var sourcePath = AssetPath.Combine(buildOutputDirectory, assembly.Filename); try { File.Copy(sourcePath, assembly.FullPath, true); var postProcessorTask = new PostProcessorTask(assembly, messagesList, buildOutputDirectory, postProcessFunc); postProcessorTask.Poll(); OnPostProcessingStarted?.Invoke(assembly); postProcessorTasks.Add(postProcessorTask); } catch (IOException e) { UnityEngine.Debug.LogError($"Fail to copy {sourcePath} to {assembly.FullPath} before post processing the assembly. Skipping post processing.\n{e}"); // OnCompilationFinished callbacks might add more compiler messages OnCompilationFinished?.Invoke(assembly, messagesList); processedAssemblies.Add(assembly, messagesList.ToArray()); } } else { // OnCompilationFinished callbacks might add more compiler messages OnCompilationFinished?.Invoke(assembly, messagesList); processedAssemblies.Add(assembly, messagesList.ToArray()); } if (!CompileErrors) { CompileErrors = messagesList.Any(m => m.type == CompilerMessageType.Error); } compilerTasks.Remove(assembly); compiler.Dispose(); } } HashSet <PostProcessorTask> finishedPostProcessorTasks = null; foreach (var postProcessorTask in postProcessorTasks) { // We break out of this loop instead to continuing to ensure that // OnCompilationFinished events are emitted in the same order as // they finished compiling. if (!postProcessorTask.Poll()) { break; } // Do not copy the post processed assembly in OnCompilationFinished // if any of the running compilers have a reference to the assembly. // As we might copy it while the compiler has the assembly open. if (AnyRunningCompilerHasReference(postProcessorTask.Assembly)) { break; } var messagesList = postProcessorTask.CompilerMessages; // OnCompilationFinished callbacks might add more compiler messages OnCompilationFinished?.Invoke(postProcessorTask.Assembly, messagesList); processedAssemblies.Add(postProcessorTask.Assembly, messagesList.ToArray()); if (!CompileErrors) { CompileErrors = messagesList.Any(m => m.type == CompilerMessageType.Error); } if (finishedPostProcessorTasks == null) { finishedPostProcessorTasks = new HashSet <PostProcessorTask>(); } finishedPostProcessorTasks.Add(postProcessorTask); } if (finishedPostProcessorTasks != null) { foreach (var finishedPostProcessorTask in finishedPostProcessorTasks) { postProcessorTasks.Remove(finishedPostProcessorTask); } } // If StopOnFirstError is set, do not queue assemblies for compilation in case of compile errors. bool stopOnFirstError = (compilationTaskOptions & CompilationTaskOptions.StopOnFirstError) == CompilationTaskOptions.StopOnFirstError; if (stopOnFirstError && CompileErrors) { pendingAssemblies.Clear(); if (FinishedCompilation) { HandleOnCompilationTaskFinished(); } return(FinishedCompilation); } // Queue pending assemblies for compilation if we have no running compilers or if compilers have finished. if (compilerTasks.Count == 0 || (finishedCompilerTasks != null && finishedCompilerTasks.Count > 0)) { QueuePendingAssemblies(); } if (FinishedCompilation) { HandleOnCompilationTaskFinished(); } return(FinishedCompilation); }
public void SetAllCustomScriptAssemblyJsonContents(string[] paths, string[] contents, string[] guids) { var assemblies = new List <CustomScriptAssembly>(); var assemblyLowercaseNamesLookup = new Dictionary <string, CustomScriptAssembly>(); var exceptions = new List <Exception>(); var guidsToAssemblies = new Dictionary <string, CustomScriptAssembly>(); HashSet <string> predefinedAssemblyNames = null; // To check if a path prefix is already being used we use a Dictionary where the key is the prefix and the value is the file path. var prefixToFilePathLookup = CustomScriptAssemblyReferences.ToDictionary(x => x.PathPrefix, x => new List <string> { x.FilePath }, StringComparer.OrdinalIgnoreCase); m_CompilationSetupErrorsTracker.ClearCompilationSetupErrors(CompilationSetupErrors.LoadError); // Load first to setup guidsToAssemblies dictionary and convert guids to assembly names // before checking for assembly reference errors, so errors emit assembly names instead of guids. for (var i = 0; i < paths.Length; ++i) { var path = paths[i]; var guid = guids[i]; string lowerCaseName = null; CustomScriptAssembly loadedCustomScriptAssembly = null; try { var fullPath = AssetPath.IsPathRooted(path) ? AssetPath.GetFullPath(path) : AssetPath.Combine(m_ProjectDirectory, path); loadedCustomScriptAssembly = contents != null ? LoadCustomScriptAssemblyFromJson(fullPath, contents[i], guid) : LoadCustomScriptAssemblyFromJsonPath(fullPath, guid); loadedCustomScriptAssembly.References = loadedCustomScriptAssembly.References ?? new string[0]; lowerCaseName = Utility.FastToLower(loadedCustomScriptAssembly.Name); guidsToAssemblies[Utility.FastToLower(guid)] = loadedCustomScriptAssembly; if (!m_SkipCustomScriptAssemblyGraphValidation) { if (predefinedAssemblyNames == null) { predefinedAssemblyNames = new HashSet <string>(EditorBuildRules.PredefinedTargetAssemblyNames); var netfw = MonoLibraryHelpers .GetSystemLibraryReferences(ApiCompatibilityLevel.NET_Unity_4_8).Select(Path.GetFileNameWithoutExtension); var netstandard21 = MonoLibraryHelpers .GetSystemLibraryReferences(ApiCompatibilityLevel.NET_Standard).Select(Path.GetFileNameWithoutExtension); predefinedAssemblyNames.UnionWith(netfw); predefinedAssemblyNames.UnionWith(netstandard21); } CheckPredefinedAssemblyNames( predefinedAssemblyNames, assemblyLowercaseNamesLookup, lowerCaseName, prefixToFilePathLookup, ref loadedCustomScriptAssembly); } } catch (Exception e) { m_CompilationSetupErrorsTracker.SetCompilationSetupErrors(CompilationSetupErrors.LoadError); exceptions.Add(e); } if (loadedCustomScriptAssembly == null || m_SkipCustomScriptAssemblyGraphValidation && assemblyLowercaseNamesLookup.ContainsKey(lowerCaseName)) { continue; } loadedCustomScriptAssembly.References = loadedCustomScriptAssembly.References ?? new string[0]; assemblyLowercaseNamesLookup[lowerCaseName] = loadedCustomScriptAssembly; assemblies.Add(loadedCustomScriptAssembly); if (!prefixToFilePathLookup.TryGetValue(loadedCustomScriptAssembly.PathPrefix, out var duplicateFilePaths)) { duplicateFilePaths = new List <string>(); prefixToFilePathLookup[loadedCustomScriptAssembly.PathPrefix] = duplicateFilePaths; } duplicateFilePaths.Add(loadedCustomScriptAssembly.FilePath); } ConvertGUIDReferencesToAssemblyNames(assemblies, guidsToAssemblies); if (!m_SkipCustomScriptAssemblyGraphValidation) { CheckForReferenceErrors(assemblies, exceptions); } CustomScriptAssemblies = assemblies.ToArray(); Exceptions = exceptions.ToArray(); }
void QueuePendingAssemblies() { if (pendingAssemblies.Count == 0) { return; } List <ScriptAssembly> assemblyCompileQueue = null; List <ScriptAssembly> removePendingAssemblies = null; // Find assemblies that have all their references already compiled. foreach (var pendingAssembly in pendingAssemblies) { bool compileAssembly = true; int unchangedAssemblyReferencesCount = 0; foreach (var reference in pendingAssembly.ScriptAssemblyReferences) { CompilerMessage[] messages; if (unchangedCompiledAssemblies.Contains(reference)) { unchangedAssemblyReferencesCount++; continue; } if (!compiledAssemblies.TryGetValue(reference, out messages)) { // If a reference is not compiling and not pending // also remove this assembly from pending. if (!compilerTasks.ContainsKey(reference) && !pendingAssemblies.Contains(reference)) { if (removePendingAssemblies == null) { removePendingAssemblies = new List <ScriptAssembly>(); } removePendingAssemblies.Add(pendingAssembly); } compileAssembly = false; break; } // If reference has compile errors, do not compile the pending assembly. bool compileErrors = messages.Any(m => m.type == CompilerMessageType.Error); if (compileErrors) { if (removePendingAssemblies == null) { removePendingAssemblies = new List <ScriptAssembly>(); } removePendingAssemblies.Add(pendingAssembly); compileAssembly = false; break; } } // If the assembly exists and is up to date (no compile errors) // and it was dirtied because it is a reference to one or more // dirty assemblies and none of them changed, then we do not // need to recompile the assembly. // Most expensive checks at the bottom. if (UseReferenceAssemblies && BuildingForEditor && !pendingAssembly.HasCompileErrors && pendingAssembly.DirtySource == DirtySource.DirtyReference && unchangedAssemblyReferencesCount > 0 && unchangedAssemblyReferencesCount == pendingAssembly.ScriptAssemblyReferences.Length && File.Exists(pendingAssembly.FullPath)) { var assemblyOutputPath = AssetPath.Combine(pendingAssembly.OutputDirectory, pendingAssembly.Filename); Console.WriteLine($"- Skipping compile {assemblyOutputPath} because all references are unchanged"); unchangedCompiledAssemblies.Add(pendingAssembly); if (removePendingAssemblies == null) { removePendingAssemblies = new List <ScriptAssembly>(); } removePendingAssemblies.Add(pendingAssembly); compileAssembly = false; } if (compileAssembly) { if (assemblyCompileQueue == null) { assemblyCompileQueue = new List <ScriptAssembly>(); } assemblyCompileQueue.Add(pendingAssembly); } } if (removePendingAssemblies != null) { foreach (var assembly in removePendingAssemblies) { pendingAssemblies.Remove(assembly); // If a codegen assembly was removed fro pending assemblies, // clear all pending compilation and post processing. if (codeGenAssemblies.Contains(assembly)) { notCompiledCodeGenAssemblies.Add(assembly); pendingPostProcessorTasks.Clear(); pendingAssemblies.Clear(); assemblyCompileQueue = null; break; } } // All pending assemblies were removed and no assemblies // were queued for compilation. if (assemblyCompileQueue == null) { return; } } // No assemblies to compile, need to wait for more references to finish compiling. if (assemblyCompileQueue == null) { if (compilerTasks.Count == 0) { throw new Exception("No pending assemblies queued for compilation and no compilers running. Compilation will never finish."); } return; } // Begin compiling any queued assemblies foreach (var assembly in assemblyCompileQueue) { pendingAssemblies.Remove(assembly); if (assembly.CallOnBeforeCompilationStarted && OnBeforeCompilationStarted != null) { OnBeforeCompilationStarted(assembly, compilePhase); } var compiler = compilerFactory.Create(assembly, buildOutputDirectory); compilerTasks.Add(assembly, compiler); // Start compiler process compiler.BeginCompiling(); LogStartInfo($"# Starting compiling {assembly.Filename}", compiler.GetProcessStartInfo()); if (OnCompilationStarted != null) { OnCompilationStarted(assembly, compilePhase); } if (RunningMaxConcurrentProcesses) { break; } } compilePhase++; }