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 static List <string> GetPrecompiledReferences(ScriptAssembly scriptAssembly, TargetAssemblyType targetAssemblyType, EditorScriptCompilationOptions options, EditorCompatibility editorCompatibility, IEnumerable <PrecompiledAssembly> implicitPrecompiledAssemblies, IEnumerable <PrecompiledAssembly> explicitPrecompiledAssemblies, ICompilationSetupWarningTracker warningSink)
        {
            var references = new List <string>();

            bool buildingForEditor  = (options & EditorScriptCompilationOptions.BuildingForEditor) == EditorScriptCompilationOptions.BuildingForEditor;
            bool assemblyEditorOnly = (scriptAssembly.Flags & AssemblyFlags.EditorOnly) == AssemblyFlags.EditorOnly;
            bool isCustomAssembly   = (targetAssemblyType & TargetAssemblyType.Custom) == TargetAssemblyType.Custom;

            void AddReferenceIfMatchBuildTargetAndEditorFlag(PrecompiledAssembly precompiledAssembly, bool explicitReference)
            {
                bool compiledAssemblyEditorOnly = (precompiledAssembly.Flags & AssemblyFlags.EditorOnly) == AssemblyFlags.EditorOnly;

                // Add all pre-compiled runtime assemblies as references to all script assemblies. Don't add pre-compiled editor assemblies as dependencies to runtime assemblies.
                if (!compiledAssemblyEditorOnly || assemblyEditorOnly || (isCustomAssembly && buildingForEditor && editorCompatibility == EditorCompatibility.CompatibleWithEditor))
                {
                    if (IsPrecompiledAssemblyCompatibleWithBuildTarget(precompiledAssembly, scriptAssembly.BuildTarget))
                    {
                        references.Add(precompiledAssembly.Path);
                    }
                    // we don't warn on build target mismatch, as this is actually a common pattern (an asmdef with multiple references to different "target-specific" assemblies with the same symbols - e.g. foo.XboxOne.dll, foo.PS5.dll, foo.WebGL.dll)
                }
                else if (explicitReference && !string.IsNullOrEmpty(scriptAssembly.AsmDefPath))
                {
                    warningSink?.AddAssetWarning(scriptAssembly.AsmDefPath, $"{scriptAssembly.Filename}: can't add reference to {precompiledAssembly.Path} as it is an editor-only assembly");
                }
            }

            if (implicitPrecompiledAssemblies != null)
            {
                foreach (var precompiledAssembly in implicitPrecompiledAssemblies)
                {
                    AddReferenceIfMatchBuildTargetAndEditorFlag(precompiledAssembly, false);
                }
            }
            if (explicitPrecompiledAssemblies != null)
            {
                foreach (var precompiledAssembly in explicitPrecompiledAssemblies)
                {
                    AddReferenceIfMatchBuildTargetAndEditorFlag(precompiledAssembly, true);
                }
            }

            return(references);
        }
        internal static void AddScriptAssemblyReferences(ref ScriptAssembly scriptAssembly, TargetAssembly targetAssembly, ScriptAssemblySettings settings,
                                                         CompilationAssemblies assemblies,
                                                         IDictionary <TargetAssembly, ScriptAssembly> targetToScriptAssembly, ICompilationSetupWarningTracker warningSink)
        {
            var  scriptAssemblyReferences = new List <ScriptAssembly>(targetAssembly.References.Count);
            var  references         = new List <string>();
            bool buildingForEditor  = settings.BuildingForEditor;
            bool noEngineReferences = (targetAssembly.Flags & AssemblyFlags.NoEngineReferences) == AssemblyFlags.NoEngineReferences;

            bool shouldProcessPredefinedCustomTargets = assemblies.CustomTargetAssemblies != null && (targetAssembly.Type & TargetAssemblyType.Predefined) == TargetAssemblyType.Predefined;
            var  predefinedCustomTargetReferences     = Enumerable.Empty <TargetAssembly>();

            if (shouldProcessPredefinedCustomTargets && assemblies.PredefinedAssembliesCustomTargetReferences != null)
            {
                predefinedCustomTargetReferences = assemblies.PredefinedAssembliesCustomTargetReferences;
            }

            var unityReferences = new Dictionary <string, string>();

            // Add Unity assemblies (UnityEngine.dll, UnityEditor.dll) references, as long as the target
            // doesn't specify that it doesn't want them.
            if (!noEngineReferences)
            {
                // Add predefined custom target references in a hash-set for fast lookup
                var predefinedCustomTargetRefs = new HashSet <string>(predefinedCustomTargetReferences.Select(x => x.Filename));
                unityReferences = GetUnityReferences(scriptAssembly, targetAssembly, assemblies.UnityAssemblies, predefinedCustomTargetRefs, settings.CompilationOptions, UnityReferencesOptions.None);
                references.AddRange(unityReferences.Values);
            }

            AddTestRunnerCustomReferences(ref targetAssembly, assemblies.CustomTargetAssemblies);

            // Setup target assembly references
            foreach (var reference in targetAssembly.References)
            {
                ScriptAssembly scriptAssemblyReference;

                // If the assembly already showed up in the unity references, don't reference it here.
                // This can happen when an assembly is configured as a unity assembly override, but
                // overrides are disabled. The Unity assembly should take precedence in that case.
                if (unityReferences.ContainsKey(reference.Filename))
                {
                    continue;
                }

                // Add ScriptAssembly references to other dirty script assemblies that also need to be rebuilt.
                if (targetToScriptAssembly.TryGetValue(reference, out scriptAssemblyReference))
                {
                    System.Diagnostics.Debug.Assert(scriptAssemblyReference != null);
                    scriptAssemblyReferences.Add(scriptAssemblyReference);
                }
            }

            // For predefined target assembly add references to custom target assemblies
            if (shouldProcessPredefinedCustomTargets)
            {
                foreach (var customTargetAssembly in predefinedCustomTargetReferences)
                {
                    ScriptAssembly scriptAssemblyReference;

                    // Only add ScriptAssembly reference if the custom target assembly is dirty, e.g. is in targetToScriptAssembly dictionary
                    // Otherwise just add already compiled custom target assembly as precompiled reference.
                    if (targetToScriptAssembly.TryGetValue(customTargetAssembly, out scriptAssemblyReference))
                    {
                        scriptAssemblyReferences.Add(scriptAssemblyReference);
                    }
                }
            }

            // Add pre-compiled assemblies as references
            var allPrecompiledAssemblies = assemblies.PrecompiledAssemblies ?? new Dictionary <string, PrecompiledAssembly>(0);
            List <PrecompiledAssembly> precompiledReferences = new List <PrecompiledAssembly>(allPrecompiledAssemblies.Count);
            var explicitPrecompiledReferences = new List <PrecompiledAssembly>(targetAssembly.ExplicitPrecompiledReferences.Count);

            if ((targetAssembly.Flags & AssemblyFlags.ExplicitReferences) == AssemblyFlags.ExplicitReferences)
            {
                if (!noEngineReferences)
                {
                    precompiledReferences.AddRange(allPrecompiledAssemblies
                                                   .Where(x => (x.Value.Flags & AssemblyFlags.UserAssembly) != AssemblyFlags.UserAssembly)
                                                   .Select(x => x.Value));
                }

                foreach (var explicitPrecompiledReference in targetAssembly.ExplicitPrecompiledReferences)
                {
                    PrecompiledAssembly assembly;
                    if (allPrecompiledAssemblies.TryGetValue(explicitPrecompiledReference, out assembly))
                    {
                        explicitPrecompiledReferences.Add(assembly);
                    }
                }
            }
            else
            {
                var precompiledAssemblies = allPrecompiledAssemblies.Values.Where(x => (x.Flags & AssemblyFlags.ExplicitlyReferenced) != AssemblyFlags.ExplicitlyReferenced).ToList();

                // if noEngineReferences, add just the non-explicitly-referenced user assemblies
                if (noEngineReferences)
                {
                    precompiledReferences.AddRange(precompiledAssemblies.Where(x => (x.Flags & AssemblyFlags.UserAssembly) == AssemblyFlags.UserAssembly));
                }
                else
                {
                    precompiledReferences.AddRange(precompiledAssemblies);
                }
            }

            AddTestRunnerPrecompiledReferences(targetAssembly, allPrecompiledAssemblies, ref precompiledReferences);

            var precompiledReferenceNames = GetPrecompiledReferences(scriptAssembly, targetAssembly.Type, settings.CompilationOptions, targetAssembly.editorCompatibility, precompiledReferences, explicitPrecompiledReferences, warningSink);

            references.AddRange(precompiledReferenceNames);

            if (buildingForEditor && assemblies.EditorAssemblyReferences != null)
            {
                references.AddRange(assemblies.EditorAssemblyReferences);
            }

            references.AddRange(MonoLibraryHelpers.GetSystemLibraryReferences(scriptAssembly.CompilerOptions.ApiCompatibilityLevel));

            scriptAssembly.ScriptAssemblyReferences = scriptAssemblyReferences.ToArray();
            scriptAssembly.References = references.ToArray();
        }
        internal static ScriptAssembly[] ToScriptAssemblies(
            IDictionary <TargetAssembly, DirtyTargetAssembly> targetAssemblies,
            ScriptAssemblySettings settings,
            CompilationAssemblies assemblies, ICompilationSetupWarningTracker warningSink, ISafeModeInfo safeModeInfo)
        {
            var scriptAssemblies = new ScriptAssembly[targetAssemblies.Count];

            var targetToScriptAssembly = new Dictionary <TargetAssembly, ScriptAssembly>();
            int index = 0;

            bool buildingForEditor = settings.BuildingForEditor;
            var  safeModeWhiteList = new HashSet <string>(safeModeInfo.GetWhiteListAssemblyNames());

            foreach (var entry in targetAssemblies)
            {
                var targetAssembly      = entry.Key;
                var dirtyTargetAssembly = entry.Value;
                var scriptAssembly      = new ScriptAssembly();

                // Setup TargetAssembly -> ScriptAssembly mapping for converting references
                scriptAssemblies[index] = scriptAssembly;
                targetToScriptAssembly[targetAssembly] = scriptAssemblies[index++];

                // Setup ScriptAssembly
                scriptAssembly.Flags         = targetAssembly.Flags;
                scriptAssembly.BuildTarget   = settings.BuildTarget;
                scriptAssembly.OriginPath    = targetAssembly.PathPrefix;
                scriptAssembly.Filename      = targetAssembly.Filename;
                scriptAssembly.SkipCodeGen   = safeModeWhiteList.Contains(targetAssembly.Filename);
                scriptAssembly.RootNamespace = targetAssembly.Type == TargetAssemblyType.Predefined ? settings.ProjectRootNamespace : targetAssembly.RootNamespace;

                scriptAssembly.OutputDirectory    = settings.OutputDirectory;
                scriptAssembly.Defines            = targetAssembly.Defines == null ? s_CSharpVersionDefines : targetAssembly.Defines.Concat(s_CSharpVersionDefines).ToArray();
                scriptAssembly.Files              = dirtyTargetAssembly.SourceFiles.ToArray();
                scriptAssembly.TargetAssemblyType = targetAssembly.Type;
                scriptAssembly.AsmDefPath         = targetAssembly.AsmDefPath;

                if (scriptAssembly.TargetAssemblyType == TargetAssemblyType.Predefined)
                {
                    scriptAssembly.CompilerOptions = new ScriptCompilerOptions(settings.PredefinedAssembliesCompilerOptions);
                }
                else
                {
                    scriptAssembly.CompilerOptions = targetAssembly.CompilerOptions;
                }

                scriptAssembly.CompilerOptions.RoslynAnalyzerDllPaths      = new string[0];
                scriptAssembly.CompilerOptions.RoslynAnalyzerRulesetPath   = string.Empty;
                scriptAssembly.CompilerOptions.AdditionalCompilerArguments = settings.AdditionalCompilerArguments;

                var editorOnlyTargetAssembly = (targetAssembly.Flags & AssemblyFlags.EditorOnly) == AssemblyFlags.EditorOnly;

                if (editorOnlyTargetAssembly)
                {
                    scriptAssembly.CompilerOptions.ApiCompatibilityLevel = ApiCompatibilityLevel.NET_Unity_4_8;
                }
                else
                {
                    scriptAssembly.CompilerOptions.ApiCompatibilityLevel = settings.PredefinedAssembliesCompilerOptions.ApiCompatibilityLevel;
                }

                if ((settings.CompilationOptions & EditorScriptCompilationOptions.BuildingUseDeterministicCompilation) == EditorScriptCompilationOptions.BuildingUseDeterministicCompilation)
                {
                    scriptAssembly.CompilerOptions.UseDeterministicCompilation = true;
                }
                else
                {
                    scriptAssembly.CompilerOptions.UseDeterministicCompilation = false;
                }

                scriptAssembly.CompilerOptions.CodeOptimization = settings.CodeOptimization;
            }

            // Don't add the auto-referenced engine assemblies if the assembly either has the flag set, or
            // is a codegen assembly
            AutoReferencedPackageAssemblies.AddReferences(assemblies.CustomTargetAssemblies, settings.CompilationOptions,
                                                          t =>
            {
                var hasNoEngineReferencesFlag = (t.Flags & AssemblyFlags.NoEngineReferences) == AssemblyFlags.NoEngineReferences;
                if (hasNoEngineReferencesFlag)
                {
                    return(false);
                }

                return(!UnityCodeGenHelpers.IsCodeGen(t.Filename));
            });

            // Setup ScriptAssembly references
            index = 0;
            foreach (var entry in targetAssemblies)
            {
                var scriptAssembly = scriptAssemblies[index++];
                AddScriptAssemblyReferences(ref scriptAssembly, entry.Key, settings, assemblies, targetToScriptAssembly, warningSink);

                if (UnityCodeGenHelpers.IsCodeGen(entry.Key.Filename) ||
                    UnityCodeGenHelpers.IsCodeGenTest(entry.Key.Filename) ||
                    CompilationPipelineCommonHelper.ShouldAdd(entry.Key.Filename))
                {
                    CompilationPipelineCommonHelper.UpdateScriptAssemblyReference(ref scriptAssembly);
                }

                if (!buildingForEditor)
                {
                    PlatformSupportModuleHelpers.AddAdditionalPlatformSupportData(settings.CompilationExtension, ref scriptAssembly);
                }
            }

            if ((settings.CompilationOptions & EditorScriptCompilationOptions.BuildingWithRoslynAnalysis) != 0)
            {
                var analyzers = assemblies.RoslynAnalyzerDllPaths;
                foreach (var a in analyzers)
                {
                    var targetAssemblyOwningAnalyzer = assemblies.CustomTargetAssemblies.Values
                                                       .OrderBy(c => c.PathFilter(a)).LastOrDefault();

                    if (targetAssemblyOwningAnalyzer?.PathFilter(a) <= 0)
                    {
                        targetAssemblyOwningAnalyzer = null;
                    }

                    foreach (var scriptAssembly in scriptAssemblies)
                    {
                        if (ShouldUseAnalyzerForScriptAssembly(scriptAssembly, targetAssemblyOwningAnalyzer))
                        {
                            scriptAssembly.CompilerOptions.RoslynAnalyzerDllPaths    = scriptAssembly.CompilerOptions.RoslynAnalyzerDllPaths.Concat(new[] { a }).ToArray();
                            scriptAssembly.CompilerOptions.RoslynAnalyzerRulesetPath =
                                scriptAssembly.TargetAssemblyType == TargetAssemblyType.Predefined
                                ? RuleSetFileCache.GetRuleSetFilePathInRootFolder(Path.ChangeExtension(scriptAssembly.Filename, null))
                                : RuleSetFileCache.GetPathForAssembly(scriptAssembly.OriginPath);
                        }
                    }
                }
            }

            return(scriptAssemblies);
        }