/// <nodoc/>
        public FrontEndPublicFacadeAndAstProvider(
            FrontEndEngineAbstraction engine,
            Logger logger,
            LoggingContext loggingContext,
            string frontEndEngineDirectory,
            bool logFrontEndStatistics,
            PathTable pathTable,
            IFrontEndStatistics statistics,
            CancellationToken cancellationToken)
        {
            Contract.Requires(engine != null);
            Contract.Requires(loggingContext != null);
            Contract.Requires(!string.IsNullOrEmpty(frontEndEngineDirectory));
            Contract.Requires(pathTable != null);
            Contract.Requires(statistics != null);

            m_engine       = engine;
            m_pathTable    = pathTable;
            m_statistics   = statistics;
            m_fileCombiner = new FileCombiner(
                loggingContext,
                SpecCacheFullPath(frontEndEngineDirectory),
                FileCombiner.FileCombinerUsage.IncrementalScriptFrontEnd,
                logFrontEndStatistics);
            m_logger         = logger;
            m_loggingContext = loggingContext;

            var queueOptions = new ExecutionDataflowBlockOptions {
                MaxDegreeOfParallelism = 1, CancellationToken = cancellationToken
            };

            Action <FileContentWithHash> action = SaveFile;

            m_filesToSaveQueue = new ActionBlock <FileContentWithHash>(action, queueOptions);
        }
Пример #2
0
        /// <nodoc />
        public PreludeManager(FrontEndEngineAbstraction engine, PathTable pathTable, Func <AbsolutePath, Task <Possible <ISourceFile> > > parser, int maxParseConcurrency, bool useOfficeBackCompatHacks)
        {
            Contract.Requires(engine != null);
            Contract.Requires(pathTable.IsValid);
            Contract.Requires(parser != null);
            Contract.Requires(maxParseConcurrency > 0);

            m_engine                   = engine;
            m_pathTable                = pathTable;
            m_parser                   = parser;
            m_maxParseConcurrency      = maxParseConcurrency;
            m_useOfficeBackCompatHacks = useOfficeBackCompatHacks;
        }
Пример #3
0
 private AppState(
     FrontEndEngineAbstraction engineAbstraction,
     TextDocumentManager documentManager,
     PathTable pathTable,
     IncrementalWorkspaceProvider incrementalWorkspaceProvider,
     Workspace workspace)
 {
     EngineAbstraction            = engineAbstraction;
     DocumentManager              = documentManager;
     PathTable                    = pathTable;
     IncrementalWorkspaceProvider = incrementalWorkspaceProvider;
     m_workspace                  = workspace;
 }
Пример #4
0
 /// <nodoc />
 public ConfigurationConversionHelper(
     [CanBeNull] FrontEndEngineAbstraction engine,
     ConfigurationKind kind,
     Logger logger,
     FrontEndHost host,
     FrontEndContext context,
     IConfiguration configuration,
     IFrontEndStatistics statistics)
     : base(statistics, logger, host, context, configuration)
 {
     Engine = engine ?? new SimpleFrontEndEngineAbstraction(context.PathTable, context.FileSystem, configuration);
     Kind   = kind;
     ConversionConfiguration = AstConversionConfiguration.ForConfiguration(FrontEndConfiguration);
     Linter = Lazy.Create(() => CreateLinter(ConversionConfiguration));
 }
Пример #5
0
 public static bool TryBuildWorkspace(
     ICommandLineConfiguration commandLineConfig,
     FrontEndContext frontEndContext,
     EngineContext engineContext,
     EvaluationFilter evaluationFilter,
     EventHandler <WorkspaceProgressEventArgs> progressHandler,
     out Workspace workspace,
     out FrontEndHostController frontEndHostController,
     out IMutablePipGraph pipGraph,
     WorkspaceBuilderConfiguration configuration,
     FrontEndEngineAbstraction frontEndEngineAbstraction = null,
     bool collectMemoryAsSoonAsPossible = true)
 {
     return(TryBuildWorkspaceInternal(commandLineConfig, frontEndContext, engineContext, evaluationFilter, progressHandler, out workspace, out frontEndHostController, out pipGraph,
                                      configuration, forIDE: false, frontEndEngineAbstraction, collectMemoryAsSoonAsPossible));
 }
Пример #6
0
        /// <summary>
        /// Get all the environment exposed to the process, with the values overriden by the engine
        /// </summary>
        public static IDictionary <string, string> GetEngineEnvironment(FrontEndEngineAbstraction engine, string frontEndName)
        {
            var         engineEnvironment = new Dictionary <string, string>();
            IDictionary environment       = Environment.GetEnvironmentVariables();

            foreach (string environmentVariable in environment.Keys)
            {
                // Expose as much of the environment as we can -- use the ones overriden by the Engine
                if (engine.TryGetBuildParameter(environmentVariable, frontEndName, out var value))
                {
                    engineEnvironment[environmentVariable] = value;
                }
            }

            return(engineEnvironment);
        }
Пример #7
0
        /// <summary>
        /// Tries to build a workspace that an IDE-like language service can use
        /// </summary>
        public static bool TryBuildWorkspaceForIde(
            FrontEndContext frontEndContext,
            EngineContext engineContext,
            FrontEndEngineAbstraction frontEndEngineAbstraction,
            AbsolutePath rootFolder,
            bool skipNuget,
            EventHandler <WorkspaceProgressEventArgs> progressHandler,
            out Workspace workspace,
            out FrontEndHostController controller)
        {
            Contract.Requires(frontEndEngineAbstraction != null);
            Contract.Requires(frontEndContext != null);
            Contract.Requires(engineContext != null);
            Contract.Requires(rootFolder.IsValid);

            var config = FindPrimaryConfiguration(rootFolder, frontEndEngineAbstraction, frontEndContext.PathTable);

            if (!config.IsValid)
            {
                workspace  = null;
                controller = null;
                return(false);
            }

            return(TryBuildWorkspace(
                       new CommandLineConfiguration()
            {
                Startup = { ConfigFile = config }
            },
                       frontEndContext,
                       engineContext,
                       EvaluationFilter.Empty, // TODO: consider passing a filter that scopes down the build to the root folder
                       progressHandler,
                       false,
                       out workspace,
                       out controller,
                       out _,
                       new WorkspaceBuilderConfiguration()
            {
                CancelOnFirstParsingFailure = false, // We always want to do as much as we can for the IDE,
                PublicFacadeOptimization = false,    // The IDE never wants public facades to be on, since this swaps specs under the hood.
                SaveBindingFingerprint = false,      // Off for IDE.
                SkipNuget = skipNuget,
            },
                       frontEndEngineAbstraction: frontEndEngineAbstraction,
                       collectMemoryAsSoonAsPossible: false));
        }
 /// <nodoc />
 public ConfigurationConversionHelper(
     [CanBeNull] FrontEndEngineAbstraction engine,
     ConfigurationKind kind,
     GlobalConstants constants,
     ModuleRegistry sharedModuleRegistry,
     Logger logger,
     FrontEndHost host,
     FrontEndContext context,
     IConfiguration configuration,
     IFrontEndStatistics statistics = null)
     : base(constants, sharedModuleRegistry, statistics ?? new FrontEndStatistics(), logger, host, context, configuration)
 {
     Engine = engine ?? new SimpleFrontEndEngineAbstraction(context.PathTable, context.FileSystem, configuration);
     Kind   = kind;
     ConversionConfiguration = AstConversionConfiguration.ForConfiguration(FrontEndConfiguration);
     Linter = Lazy.Create(() => CreateLinter(ConversionConfiguration));
 }
Пример #9
0
        /// <summary>
        /// Retrieves a list of search locations for an executable, inspecting a list of explicit candidates first or using PATH.
        /// The onEmptyResult action will be invoked if there are no search locations available
        /// The onPathParseFailure action will be invoked with the PATH as an argument if the PATH is malformed
        /// </summary>
        public static bool TryRetrieveExecutableSearchLocations(
            string frontEnd,
            FrontEndContext context,
            FrontEndEngineAbstraction engine,
            IReadOnlyCollection <AbsolutePath> explicitCandidates,
            out IEnumerable <AbsolutePath> searchLocations,
            Action onEmptyResult = null,
            Action <string> onPathParseFailure = null)
        {
            // If there are explicit search locations specified, use those
            if (explicitCandidates?.Count > 0)
            {
                searchLocations = explicitCandidates;
                return(true);
            }

            // Otherwise use %PATH%
            if (!engine.TryGetBuildParameter("PATH", frontEnd, out string paths))
            {
                onEmptyResult?.Invoke();
                searchLocations = null;
                return(false);
            }

            var locations = new List <AbsolutePath>();

            foreach (string path in paths.Split(new[] { Path.PathSeparator }, StringSplitOptions.RemoveEmptyEntries))
            {
                var nonEscapedPath = path.Trim('"');
                if (AbsolutePath.TryCreate(context.PathTable, nonEscapedPath, out var absolutePath))
                {
                    locations.Add(absolutePath);
                }
            }

            if (locations.Count == 0)
            {
                onPathParseFailure?.Invoke(paths);
                searchLocations = null;
                return(false);
            }

            searchLocations = locations;
            return(true);
        }
Пример #10
0
        public LanguageServiceProviders(ProviderContext providerContext, FrontEndEngineAbstraction engineAbstraction, IProgressReporter progressReporter)
        {
            Contract.Requires(providerContext != null);
            Contract.Requires(engineAbstraction != null);

            m_autoCompleteProvider   = new AutoCompleteProvider(providerContext);
            m_gotoDefinitionProvider = new GotoDefinitionProvider(providerContext);
            m_findReferencesProvider = new FindReferencesProvider(providerContext, progressReporter);
            m_formattingProvider     = new FormattingProvider(providerContext, engineAbstraction);
            m_executeCommandProvider = new ExecuteCommandProvider(providerContext);
            m_codeActionProvider     = new CodeActionProvider(providerContext, m_executeCommandProvider);
            m_codeLensProvider       = new CodeLensProvider(providerContext);
            m_hoverProvider          = new HoverProvider(providerContext);
            m_diagnosticProvider     = new DiagnosticProvider(providerContext);
            m_signatureHelpProvider  = new SignatureHelpProvider(providerContext);
            m_symbolProvider         = new SymbolProvider(providerContext);
            m_renameProvider         = new RenameProvider(providerContext, m_findReferencesProvider);
        }
        /// <nodoc/>
        public FrontEndArtifactManager(
            FrontEndEngineAbstraction engine,
            string frontEndEngineDirectory,
            Logger logger,
            LoggingContext loggingContext,
            IFrontEndStatistics frontEndStatistics,
            PathTable pathTable,
            IFrontEndConfiguration configuration,
            CancellationToken cancellationToken)
        {
            Contract.Requires(engine != null);
            Contract.Requires(loggingContext != null);
            Contract.Requires(frontEndStatistics != null);
            Contract.Requires(!string.IsNullOrEmpty(frontEndEngineDirectory));
            Contract.Requires(pathTable != null);

            m_engine        = engine;
            m_frontEndCache = new FrontEndCache(frontEndEngineDirectory, logger, loggingContext, frontEndStatistics, pathTable);

            // If engine state changed, delete facade and ast cache, because from one run to another the serialized AST bytes
            // can be different even if corresponding DScript sourced didn't change (reason: PathTable can change in the meantime)
            if (!engine.IsEngineStatePartiallyReloaded())
            {
                FrontEndPublicFacadeAndAstProvider.PurgeCache(frontEndEngineDirectory);
            }

            // If public facades are not used, then we don't create the public facade provider. This avoids
            // creating the public facade cache.
            // In particular, the IDE never uses this optimization, and the cache needs an exclusive lock.
            if (configuration.UseSpecPublicFacadeAndAstWhenAvailable())
            {
                m_publicFacadeAndAstProvider = new FrontEndPublicFacadeAndAstProvider(
                    engine,
                    logger,
                    loggingContext,
                    frontEndEngineDirectory,
                    configuration.LogStatistics,
                    pathTable,
                    frontEndStatistics,
                    cancellationToken);
            }
        }
Пример #12
0
        internal Workspace DoPhaseBuildWorkspace(IConfiguration configuration, FrontEndEngineAbstraction engineAbstraction, EvaluationFilter evaluationFilter)
        {
            if (!TryGetWorkspaceProvider(configuration, out var workspaceProvider, out var failures))
            {
                var workspaceConfiguration = GetWorkspaceConfiguration(configuration);

                return(Workspace.Failure(workspaceConfiguration: workspaceConfiguration, failures: failures.ToArray()));
            }

            var result = TaskUtilities.WithCancellationHandlingAsync(
                FrontEndContext.LoggingContext,
                BuildAndFilterWorkspaceAsync(workspaceProvider, engineAbstraction, evaluationFilter),
                m_logger.FrontEndBuildWorkspacePhaseCanceled,
                GetOrCreateComputationCancelledWorkspace(workspaceProvider),
                FrontEndContext.CancellationToken).GetAwaiter().GetResult();

            ReportWorkspaceParsingAndBindingErrorsIfNeeded(result);

            return(result);
        }
Пример #13
0
        private static AbsolutePath TryFindConfig(AbsolutePath startDir, FrontEndEngineAbstraction engine, PathTable pathTable)
        {
            Contract.Requires(startDir.IsValid);

            for (; startDir != AbsolutePath.Invalid; startDir = startDir.GetParent(pathTable))
            {
                var configFilename       = startDir.Combine(pathTable, Names.ConfigBc);
                var legacyConfigFilename = startDir.Combine(pathTable, Names.ConfigDsc);

                if (engine.FileExists(legacyConfigFilename))
                {
                    return(legacyConfigFilename);
                }

                if (engine.FileExists(configFilename))
                {
                    return(configFilename);
                }
            }

            return(AbsolutePath.Invalid);
        }
Пример #14
0
        /// <summary>
        /// The FrontEnds that use an out-of-proc tool should sandbox that process and call this method
        /// in order to tack the tool's file accesses, enumerations, etc. in order to make graph caching sound
        /// </summary>
        public static void TrackToolFileAccesses(FrontEndEngineAbstraction engine, FrontEndContext context, string frontEndName, ISet <ReportedFileAccess> fileAccesses, AbsolutePath frontEndFolder)
        {
            // Compute all parseable paths
            // TODO: does it make sense to consider enumerations, or as a result the graph will be too unstable? Does it matter for MsBuild graph construction?
            foreach (var access in fileAccesses)
            {
                string accessPath = access.GetPath(context.PathTable);
                if (AbsolutePath.TryCreate(context.PathTable, accessPath, out AbsolutePath path))
                {
                    // Ignore accesses under the frontend folder: these are files used for internal communication between
                    // BuildXL and the graph builder tool, and they are never files that MSBuild itself interacted with
                    if (path.IsWithin(context.PathTable, frontEndFolder))
                    {
                        continue;
                    }

                    if ((access.RequestedAccess & RequestedAccess.Enumerate) != 0)
                    {
                        engine.TrackDirectory(path.ToString(context.PathTable));
                    }
                    if ((access.RequestedAccess & RequestedAccess.Probe) != 0)
                    {
                        engine.FileExists(path);
                    }
                    if ((access.RequestedAccess & RequestedAccess.Read) != 0)
                    {
                        // Two things are happening here: we want to register if the file is present or absent. Engine.FileExists takes
                        // care of that. And in the case the file exists, record the content.
                        // There are apparently some repos that create and delete files during graph construction :(
                        // So we cannot trust detours and check for IsNonexistent on the access itself. Even though there were read/write accesses on a given file,
                        // the file may not exist at this point
                        if (engine.FileExists(path))
                        {
                            engine.RecordFrontEndFile(path, frontEndName);
                        }
                    }
                }
            }
        }
Пример #15
0
        /// <summary>
        /// Expands environment variables in a string by querying the engine for the corresponding build parameters
        /// </summary>
        /// <remarks>
        /// Even though there is a Environment.ExpandEnvironmentVariables(String) available in C# this function only works
        /// for Windows (https://github.com/dotnet/runtime/issues/25792). Furthermore, we need the expansion to consider
        /// the configured build parameters in addition to the actual environment, and to properly register the access to those parameters in order
        /// for incrementality to safely work.
        /// </remarks>
        /// <returns>Whether there is at least one environment variable in the string that was expanded</returns>
        public static bool TryExpandVariablesInString(string stringToExpand, FrontEndEngineAbstraction engine, out string expandedString)
        {
            // if the path does not contain an env variable, just return the original path to avoid extra allocations
#if NETCOREAPP
            if (stringToExpand?.Contains(s_variableSeparator) != true)
#else
            if (stringToExpand?.Contains(s_variableSeparator.ToString()) != true)
#endif
            {
                expandedString = stringToExpand;
                return(false);
            }

            // Here we build the result, containing all variables expanded
            StringBuilder result = new StringBuilder(stringToExpand.Length);
            // Here we build the variable name we'll query the environment with. Reused across each variable
            StringBuilder variableName = new StringBuilder();

            // Whether we are traversing a variable name or a regular part of the string
            bool inAVariableName = false;
            // Whether there has been at least one successful expansion of the provided string
            bool atLeastOneExpansion = false;

            foreach (char c in stringToExpand)
            {
                // If the character is a variable separator and we are not parsing a variable name, then
                // this is the start of a variable name. Both Windows and non-Windows case start with a distinguished separator
                if (c == s_variableSeparator && !inAVariableName)
                {
                    inAVariableName = true;
                    // The separator is always discarded and not part of the name
                    continue;
                }
                // We are not parsing a variable name and this is a regular character of a string. Just make it part of the result
                else if (c != s_variableSeparator && !inAVariableName)
                {
                    result.Append(c);
                }
                // We are parsing a variable name and we are past the initial separator
                else
                {
                    // This is the end of the variable name
                    if (IsEndOfVariableName(c))
                    {
                        // Get the name and query the engine for the result. If the variable is not defined, just append the name unexpanded
                        // Observe that even when the variable is not defined, by querying the engine we will record this as an anti-dependency
                        var name = variableName.ToString();
                        if (!string.IsNullOrEmpty(name) && engine.TryGetBuildParameter(name, "DScript", out string value) && !string.IsNullOrEmpty(value))
                        {
                            result.Append(value);
                            atLeastOneExpansion = true;
                        }
                        else
                        {
                            AppendVariableUnexpanded(result, name);
                        }

                        // We are done parsing the variable name. Clear up the builder so subsequent names can be constructed.
                        variableName.Clear();
                        inAVariableName = false;

                        // If the end of the variable name is flagged by an explicit separator (e.g. %PATH%), then there is nothing else to do
                        // and this ending separator won't be part of the result.
                        // Otherwise the ending character can be either the start of a new variable or a regular part of the string
                        if (!EndOfVariableNameIsSeparator())
                        {
                            if (c == s_variableSeparator)
                            {
                                inAVariableName = true;
                                // The separator is always discarded and not part of the name
                                continue;
                            }
                            else
                            {
                                result.Append(c);
                            }
                        }
                    }
                    // We are parsing a variable name and the current char is part of the name
                    else
                    {
                        variableName.Append(c);
                    }
                }
            }

            // We finished traversing the string while interpreting a variable name
            if (inAVariableName)
            {
                // If a separator is needed to end a variable name, then this is a malformed name. E.g."C:\foo\%PATH"
                // So we leave it unexpanded
                if (EndOfVariableNameIsSeparator())
                {
                    result.Append(s_variableSeparator);
                    result.Append(variableName);
                }
                else
                {
                    // Otherwise, the end of the string defines the end of the name. Expand it as needed
                    var name = variableName.ToString();
                    if (!string.IsNullOrEmpty(name) && engine.TryGetBuildParameter(name, "DScript", out string value) && !string.IsNullOrEmpty(value))
                    {
                        result.Append(value);
                        atLeastOneExpansion = true;
                    }
                    else
                    {
                        AppendVariableUnexpanded(result, name);
                    }
                }
            }

            expandedString = result.ToString();
            return(atLeastOneExpansion);
        }
Пример #16
0
        private static bool TryBuildWorkspaceInternal(
            ICommandLineConfiguration commandLineConfig,
            FrontEndContext frontEndContext,
            EngineContext engineContext,
            EvaluationFilter evaluationFilter,
            EventHandler <WorkspaceProgressEventArgs> progressHandler,
            out Workspace workspace,
            out FrontEndHostController frontEndHostController,
            out IMutablePipGraph pipGraph,
            WorkspaceBuilderConfiguration configuration,
            bool forIDE,
            FrontEndEngineAbstraction frontEndEngineAbstraction = null,
            bool collectMemoryAsSoonAsPossible = true)
        {
            Contract.Requires((commandLineConfig.Engine.Phase & (EnginePhases.ParseWorkspace | EnginePhases.AnalyzeWorkspace)) != EnginePhases.None);
            Contract.Requires(frontEndContext != null);
            Contract.Requires(engineContext != null);
            Contract.Requires(commandLineConfig.Startup.ConfigFile.IsValid);
            Contract.Requires(evaluationFilter != null);

            workspace = null;
            frontEndHostController = null;
            pipGraph = null;

            var pathTable      = engineContext.PathTable;
            var loggingContext = frontEndContext.LoggingContext;

            var mutableCommandlineConfig = GetCommandLineConfiguration(
                commandLineConfig,
                configuration);

            BuildXLEngine.ModifyConfigurationForCloudbuild(mutableCommandlineConfig, false, pathTable, loggingContext);
            BuildXLEngine.PopulateLoggingAndLayoutConfiguration(mutableCommandlineConfig, pathTable, bxlExeLocation: null);

            var statistics = new FrontEndStatistics(progressHandler);
            var frontEndControllerFactory = FrontEndControllerFactory.Create(
                mode: FrontEndMode.NormalMode,
                loggingContext: loggingContext,
                configuration: mutableCommandlineConfig,
                collector: null,
                statistics: statistics,
                collectMemoryAsSoonAsPossible: collectMemoryAsSoonAsPossible);

            var controller = frontEndControllerFactory.Create(engineContext.PathTable, engineContext.SymbolTable);

            controller.InitializeHost(frontEndContext, mutableCommandlineConfig);

            frontEndHostController = (FrontEndHostController)controller;

            // If there is an explicit engine abstraction, we set it. This is used by the IDE.
            if (frontEndEngineAbstraction != null)
            {
                // The IDE engine typically doesn't have mounts configured. We do it here if they haven't been configured yet.
                // Observe these are just the default mounts used for config evaluation.
                if (frontEndEngineAbstraction is BasicFrontEndEngineAbstraction basicEngine && !frontEndEngineAbstraction.GetMountNames("Script", BuildXL.Utilities.ModuleId.Invalid).Any())
                {
                    // If this fails we just ignore the failure. Mounts not being properly configured doesn't prevent the IDE plugin from working.
                    basicEngine.TryPopulateWithDefaultMountsTable(loggingContext, engineContext, mutableCommandlineConfig, mutableCommandlineConfig.Startup.Properties);
                }

                frontEndHostController.SetState(frontEndEngineAbstraction, pipGraph: null, configuration: mutableCommandlineConfig);
            }
            else
            {
                // Otherwise we construct one with all mounts populated for config evaluation
                var configurationEngine = new BasicFrontEndEngineAbstraction(engineContext.PathTable, engineContext.FileSystem, mutableCommandlineConfig);
                if (!configurationEngine.TryPopulateWithDefaultMountsTable(loggingContext, engineContext, mutableCommandlineConfig, mutableCommandlineConfig.Startup.Properties))
                {
                    // Errors are logged already
                    return(false);
                }

                frontEndEngineAbstraction = configurationEngine;
            }

            var config = controller.ParseConfig(frontEndEngineAbstraction, mutableCommandlineConfig);

            if (config == null)
            {
                return(false);
            }

            IMutablePipGraph pipGraphBuilder = null;

            using (var cache = Task.FromResult <Possible <EngineCache> >(
                       new EngineCache(
                           new InMemoryArtifactContentCache(),

                           // Note that we have an 'empty' store (no hits ever) rather than a normal in memory one.
                           new EmptyTwoPhaseFingerprintStore())))
            {
                var mountsTable = MountsTable.CreateAndRegister(loggingContext, engineContext, config, mutableCommandlineConfig.Startup.Properties);

                // For the IDE case, we want to make sure all config-specific mounts are properly populated
                if (forIDE && frontEndEngineAbstraction is BasicFrontEndEngineAbstraction languageServiceEngine)
                {
                    Contract.AssertNotNull(frontEndEngineAbstraction);

                    AddConfigurationMounts(config, mountsTable);
                    languageServiceEngine.SetMountsTable(mountsTable);
                }

                if (frontEndEngineAbstraction == null)
                {
                    if (mutableCommandlineConfig.Engine.Phase.HasFlag(EnginePhases.Schedule))
                    {
                        frontEndEngineAbstraction = new FrontEndEngineImplementation(
                            loggingContext,
                            frontEndContext.PathTable,
                            config,
                            mutableCommandlineConfig.Startup,
                            mountsTable,
                            InputTracker.CreateDisabledTracker(loggingContext),
                            null,
                            null,
                            () => FileContentTable.CreateStub(loggingContext),
                            5000,
                            false,
                            controller.RegisteredFrontEnds);

                        var searchPathToolsHash = new DirectoryMembershipFingerprinterRuleSet(config, engineContext.StringTable).ComputeSearchPathToolsHash();
                        pipGraphBuilder = new PipGraph.Builder(
                            EngineSchedule.CreateEmptyPipTable(engineContext),
                            engineContext,
                            Pips.Tracing.Logger.Log,
                            loggingContext,
                            config,
                            mountsTable.MountPathExpander,
                            fingerprintSalt: config.Cache.CacheSalt,
                            searchPathToolsHash: searchPathToolsHash);

                        // Observe mount table is completed during workspace construction
                        AddConfigurationMounts(config, mountsTable);

                        IDictionary <ModuleId, MountsTable> moduleMountsTableMap;
                        if (!mountsTable.PopulateModuleMounts(config.ModulePolicies.Values, out moduleMountsTableMap))
                        {
                            Contract.Assume(loggingContext.ErrorWasLogged, "An error should have been logged after MountTable.PopulateModuleMounts()");
                            return(false);
                        }
                    }
                    else
                    {
                        frontEndEngineAbstraction = new BasicFrontEndEngineAbstraction(frontEndContext.PathTable, frontEndContext.FileSystem, config);
                    }
                }

                using (frontEndEngineAbstraction is IDisposable ? (IDisposable)frontEndEngineAbstraction : null)
                {
                    // Attempt to build and/or analyze the workspace
                    if (!controller.PopulateGraph(
                            cache: cache,
                            graph: pipGraphBuilder,
                            engineAbstraction: frontEndEngineAbstraction,
                            evaluationFilter: evaluationFilter,
                            configuration: config,
                            startupConfiguration: mutableCommandlineConfig.Startup))
                    {
                        workspace = frontEndHostController.GetWorkspace();

                        // Error has been reported already
                        return(false);
                    }

                    pipGraph = pipGraphBuilder;
                }
            }

            Contract.Assert(frontEndHostController != null);

            workspace = frontEndHostController.GetWorkspace();

            if (mutableCommandlineConfig.Engine.Phase == EnginePhases.AnalyzeWorkspace)
            {
                // If workspace construction is successful, we run the linter on all specs.
                // This makes sure the workspace will carry all the errors that will occur when running the same specs in the regular engine path
                workspace = CreateLintedWorkspace(
                    workspace,
                    frontEndContext.LoggingContext,
                    config.FrontEnd,
                    pathTable);
            }

            return(true);
        }
Пример #17
0
        /// <summary>
        /// Tries to create a filtered workspace based on a front-end snapshot from the previous BuildXL invocation.
        /// </summary>
        /// <returns>
        /// * Possibke&lt;ValidConstructedWorkspace&gt; when the workspace was successfully constructed.
        /// * Possible&lt;null&gt; when the snapshot was unaiable.
        /// * Failure when the snapshot was available but parsing failed.
        /// </returns>
        private async Task <Possible <Workspace> > TryCreateFilteredWorkspaceAsync(Possible <WorkspaceDefinition> workspaceDefinition, IWorkspaceProvider workspaceProvider, FrontEndEngineAbstraction engineAbstraction, EvaluationFilter evaluationFilter)
        {
            if (!FrontEndConfiguration.ConstructAndSaveBindingFingerprint())
            {
                Logger.FailToReuseFrontEndSnapshot(
                    LoggingContext,
                    "Binding fingerprint is disabled. Please use 'constructAndSaveBindingFingerprint' option to turn it on");
                return(default(Possible <Workspace>));
            }

            // If a filter cannot be performed and public facade + AST is not to be used, then there is no point in continuing and we can
            // go to full mode
            if (!evaluationFilter.CanPerformPartialEvaluationScript(PrimaryConfigFile) && !CanUseSpecPublicFacadeAndAst())
            {
                var message = !CanUseSpecPublicFacadeAndAst()
                    ? "Engine state was not reloaded"
                    : "User filter was not specified";
                Logger.FailToReuseFrontEndSnapshot(LoggingContext, message);
                return(default(Possible <Workspace>));
            }

            var changedFiles = engineAbstraction.GetChangedFiles()?.ToList();

            if (changedFiles == null)
            {
                Logger.FailToReuseFrontEndSnapshot(LoggingContext, "Change journal is not available");
                return(default(Possible <Workspace>));
            }

            using (var sw = Watch.Start())
            {
                // We're potentially in incremental mode.
                var filteredDefinitionResult = await TryFilterWorkspaceDefinitionIncrementallyAsync(
                    changedFiles,
                    workspaceProvider,
                    workspaceDefinition.Result,
                    evaluationFilter);

                if (filteredDefinitionResult.Failed)
                {
                    return(filteredDefinitionResult.Failure);
                }

                if (filteredDefinitionResult.Filtered)
                {
                    var filteredDefinition = filteredDefinitionResult.FilteredDefinition;
                    Logger.WorkspaceDefinitionFiltered(
                        LoggingContext,
                        filteredDefinition.SpecCount,
                        workspaceDefinition.Result.SpecCount,
                        sw.ElapsedMilliseconds);

                    // TODO: with C# 7, use tuple instead of changing the workspace to carry the information about the filtering.
                    return(await workspaceProvider.CreateWorkspaceAsync(filteredDefinition, userFilterWasApplied : true));
                }
            }

            return(default(Possible <Workspace>));
        }
Пример #18
0
        private async Task <Workspace> BuildAndFilterWorkspaceAsync(WorkspaceDefinition workspaceDefinition, IWorkspaceProvider workspaceProvider, FrontEndEngineAbstraction engineAbstraction, EvaluationFilter evaluationFilter)
        {
            // First, trying to filter workspace based on information from the previous run
            var possibleFilteredWorkspace = await TryCreateFilteredWorkspaceAsync(workspaceDefinition, workspaceProvider, engineAbstraction, evaluationFilter);

            if (!possibleFilteredWorkspace.Succeeded)
            {
                // Error was already logged
                return(Workspace.Failure(workspaceProvider, workspaceProvider.Configuration, possibleFilteredWorkspace.Failure));
            }

            // If the filtered workspace is not null, just return it.
            // Otherwise falling back to the full parse mode.
            if (possibleFilteredWorkspace.Result != null)
            {
                return(possibleFilteredWorkspace.Result);
            }

            // "Incremental" workspace construction has failed, but we still can try to use module filter to build a smaller workspace.
            if (evaluationFilter.ModulesToResolve.Count != 0)
            {
                var filteredDefinition = this.ApplyModuleFilter(workspaceDefinition, evaluationFilter.ModulesToResolve);
                return(await workspaceProvider.CreateWorkspaceAsync(filteredDefinition, userFilterWasApplied : true));
            }

            Logger.BuildingFullWorkspace(LoggingContext);
            return(await workspaceProvider.CreateWorkspaceAsync(workspaceDefinition, userFilterWasApplied : false));
        }
Пример #19
0
        /// <summary>
        /// Builds and filters the worksapce.
        /// </summary>
        /// <remarks>
        /// This method not just builds the workspace from scratch, but it also tries to compute it in an efficient way.
        /// If there is a front end snapshot from the previous BuildXL run and the engine gives us a set of changed files,
        /// then we can build a filtered workspace based on the old spec-2-spec map without parsing the entire world.
        /// </remarks>
        private async Task <Workspace> BuildAndFilterWorkspaceAsync(IWorkspaceProvider workspaceProvider, FrontEndEngineAbstraction engineAbstraction, EvaluationFilter evaluationFilter)
        {
            // this step downloads nugets too, and that's why we want to do it outside of the progress reporting block below
            Possible <WorkspaceDefinition> workspaceDefinition = await TryGetWorkspaceDefinitionAsync(workspaceProvider);

            if (!workspaceDefinition.Succeeded)
            {
                return(Workspace.Failure(workspaceProvider, workspaceProvider.Configuration, workspaceDefinition.Failure));
            }

            return(await WithWorkspaceProgressReportingAsync(
                       numSpecs : workspaceDefinition.Result.SpecCount,
                       task : BuildAndFilterWorkspaceAsync(workspaceDefinition.Result, workspaceProvider, engineAbstraction, evaluationFilter)));
        }
Пример #20
0
        private bool ConstructAndEvaluateGraph(
            LoggingContext loggingContext,
            FrontEndEngineAbstraction frontEndEngineAbstration,
            CacheInitializationTask engineCacheTask,
            MountsTable mountsTable,
            EvaluationFilter evaluationFilter,
            [CanBeNull] GraphReuseResult reuseResult,
            out PipGraph pipGraph)
        {
            Contract.Requires(frontEndEngineAbstration != null);
            Contract.Requires(engineCacheTask != null);
            Contract.Requires(mountsTable != null);

            pipGraph = null;
            IPipGraphBuilder pipGraphBuilder = null;

            if (!AddConfigurationMountsAndCompleteInitialization(loggingContext, mountsTable))
            {
                return(false);
            }

            IDictionary <ModuleId, MountsTable> moduleMountsTableMap;

            if (!mountsTable.PopulateModuleMounts(Configuration.ModulePolicies.Values, out moduleMountsTableMap))
            {
                Contract.Assume(loggingContext.ErrorWasLogged, "An error should have been logged after MountTable.PopulateModuleMounts()");
                return(false);
            }

            m_visualization?.MountsTable.MakeAvailable(mountsTable);

            if ((Configuration.Engine.Phase & EnginePhases.Schedule) != 0)
            {
                pipGraphBuilder = CreatePipGraphBuilder(loggingContext, mountsTable, reuseResult);
            }

            // Have to do some horrible magic here to get to a proper Task<T> with the BuildXL cache since
            // someone updated the engine cache to be an await style pattern, and there is no way to get to the EngineCache
            // If the cache was fast to startup, but perhaps blocked itself on first access we wouldn't have to do all these hoops.
            Func <Task <Possible <EngineCache> > > getBuildCacheTask =
                async() =>
            {
                return((await engineCacheTask).Then(engineCache => engineCache.CreateCacheForContext()));
            };

            if (!FrontEndController.PopulateGraph(
                    getBuildCacheTask(),
                    pipGraphBuilder,
                    frontEndEngineAbstration,
                    evaluationFilter,
                    Configuration,
                    m_initialCommandLineConfiguration.Startup))
            {
                LogFrontEndStats(loggingContext);

                Contract.Assume(loggingContext.ErrorWasLogged, "An error should have been logged after FrontEndController.PopulateGraph()");
                return(false);
            }

            LogFrontEndStats(loggingContext);

            // Pip graph must become immutable now that evaluation is done (required to construct a scheduler).
            return(pipGraphBuilder == null || (pipGraph = pipGraphBuilder.Build()) != null);
        }
Пример #21
0
 /// <summary>
 /// Create a simple wrapper representing the real file system
 /// </summary>
 public EngineFileSystem(PathTable pathTable, FrontEndEngineAbstraction engine)
     : base(pathTable)
 {
     Contract.Requires(pathTable != null);
     m_engine = engine;
 }
Пример #22
0
 /// <nodoc />
 public FormattingProvider(ProviderContext providerContext, FrontEndEngineAbstraction engineAbstraction)
     : base(providerContext)
 {
     m_engineAbstraction = engineAbstraction;
 }
 /// <summary>
 /// Returns <code>true</code> IFF information about both changed and unchanged files is available.
 /// <see cref="FrontEndEngineAbstraction.GetChangedFiles"/>
 /// <see cref="FrontEndEngineAbstraction.GetUnchangedFiles"/>
 /// </summary>
 /// <remarks>
 /// Information about changed files may not always be available, e.g., because engine cache is missing,
 /// engine cache is corrupted, USNJournal is not working, etc.
 /// </remarks>
 public static bool IsSpecChangeInformationAvailable(this FrontEndEngineAbstraction engineAbstraction)
 {
     return
         (engineAbstraction.GetChangedFiles() != null &&
          engineAbstraction.GetUnchangedFiles() != null);
 }
Пример #24
0
        public static bool TryBuildWorkspace(
            EnginePhases phase,
            FrontEndContext frontEndContext,
            PipExecutionContext engineContext,
            AbsolutePath configFile,
            EvaluationFilter evaluationFilter,
            EventHandler <WorkspaceProgressEventArgs> progressHandler,
            out Workspace workspace,
            out FrontEndHostController frontEndHostController,
            WorkspaceBuilderConfiguration configuration,
            FrontEndEngineAbstraction frontEndEngineAbstraction = null,
            bool collectMemoryAsSoonAsPossible = true)
        {
            Contract.Requires((phase & (EnginePhases.ParseWorkspace | EnginePhases.AnalyzeWorkspace)) != EnginePhases.None);
            Contract.Requires(frontEndContext != null);
            Contract.Requires(engineContext != null);
            Contract.Requires(configFile.IsValid);
            Contract.Requires(evaluationFilter != null);

            workspace = null;

            var pathTable      = engineContext.PathTable;
            var loggingContext = frontEndContext.LoggingContext;

            var commandlineConfig = GetCommandLineConfiguration(configuration, phase, configFile);

            BuildXLEngine.PopulateLoggingAndLayoutConfiguration(commandlineConfig, pathTable, bxlExeLocation: null);

            var statistics = new FrontEndStatistics(progressHandler);
            var frontEndControllerFactory = FrontEndControllerFactory.Create(
                mode: FrontEndMode.NormalMode,
                loggingContext: loggingContext,
                configuration: commandlineConfig,
                collector: null,
                statistics: statistics,
                collectMemoryAsSoonAsPossible: collectMemoryAsSoonAsPossible);

            var controller = frontEndControllerFactory.Create(engineContext.PathTable, engineContext.SymbolTable);

            controller.InitializeHost(frontEndContext, commandlineConfig);

            frontEndHostController = controller as FrontEndHostController;

            // If there is an explicit engine abstraction, we set it
            if (frontEndEngineAbstraction != null)
            {
                frontEndHostController.SetState(frontEndEngineAbstraction, pipGraph: null, configuration: commandlineConfig);
            }

            var config = controller.ParseConfig(commandlineConfig);

            if (config == null)
            {
                frontEndHostController = null;
                return(false);
            }

            using (var cache = Task.FromResult <Possible <EngineCache> >(
                       new EngineCache(
                           new InMemoryArtifactContentCache(
                               new SchedulerContext(
                                   CancellationToken.None,
                                   frontEndContext.StringTable,
                                   frontEndContext.PathTable,
                                   frontEndContext.SymbolTable,
                                   frontEndContext.QualifierTable)),

                           // Note that we have an 'empty' store (no hits ever) rather than a normal in memory one.
                           new EmptyTwoPhaseFingerprintStore())))
            {
                // Attempt to build and/or analyze the workspace
                if (!controller.PopulateGraph(
                        cache: cache,
                        graph: null /* No need to create pips */,
                        engineAbstraction: frontEndEngineAbstraction ?? new BasicFrontEndEngineAbstraction(frontEndContext.PathTable, frontEndContext.FileSystem, config),
                        evaluationFilter: evaluationFilter,
                        configuration: config,
                        startupConfiguration: commandlineConfig.Startup))
                {
                    Contract.Assert(frontEndHostController != null);
                    workspace = frontEndHostController.GetWorkspace();

                    // Error has been reported already
                    return(false);
                }
            }

            Contract.Assert(frontEndHostController != null);

            // If workspace construction is successfull, we run the linter on all specs.
            // This makes sure the workspace will carry all the errors that will occur when running the same specs in the regular engine path
            workspace = CreateLintedWorkspace(
                frontEndHostController.GetWorkspace(),

                frontEndContext.LoggingContext,
                config.FrontEnd,
                pathTable);

            return(true);
        }
Пример #25
0
        public static bool TryBuildWorkspace(
            ICommandLineConfiguration commandLineConfig,
            FrontEndContext frontEndContext,
            EngineContext engineContext,
            EvaluationFilter evaluationFilter,
            EventHandler <WorkspaceProgressEventArgs> progressHandler,
            out Workspace workspace,
            out FrontEndHostController frontEndHostController,
            out IPipGraph pipGraph,
            WorkspaceBuilderConfiguration configuration,
            FrontEndEngineAbstraction frontEndEngineAbstraction = null,
            bool collectMemoryAsSoonAsPossible = true)
        {
            Contract.Requires((commandLineConfig.Engine.Phase & (EnginePhases.ParseWorkspace | EnginePhases.AnalyzeWorkspace)) != EnginePhases.None);
            Contract.Requires(frontEndContext != null);
            Contract.Requires(engineContext != null);
            Contract.Requires(commandLineConfig.Startup.ConfigFile.IsValid);
            Contract.Requires(evaluationFilter != null);

            workspace = null;
            frontEndHostController = null;
            pipGraph = null;

            var pathTable      = engineContext.PathTable;
            var loggingContext = frontEndContext.LoggingContext;

            var mutableCommandlineConfig = GetCommandLineConfiguration(
                commandLineConfig,
                configuration);

            BuildXLEngine.ModifyConfigurationForCloudbuild(mutableCommandlineConfig, false, pathTable, loggingContext);
            BuildXLEngine.PopulateLoggingAndLayoutConfiguration(mutableCommandlineConfig, pathTable, bxlExeLocation: null);

            var statistics = new FrontEndStatistics(progressHandler);
            var frontEndControllerFactory = FrontEndControllerFactory.Create(
                mode: FrontEndMode.NormalMode,
                loggingContext: loggingContext,
                configuration: mutableCommandlineConfig,
                collector: null,
                statistics: statistics,
                collectMemoryAsSoonAsPossible: collectMemoryAsSoonAsPossible);

            var controller = frontEndControllerFactory.Create(engineContext.PathTable, engineContext.SymbolTable);

            controller.InitializeHost(frontEndContext, mutableCommandlineConfig);

            frontEndHostController = (FrontEndHostController)controller;

            // If there is an explicit engine abstraction, we set it. This is used by IDE test.
            if (frontEndEngineAbstraction != null)
            {
                frontEndHostController.SetState(frontEndEngineAbstraction, pipGraph: null, configuration: mutableCommandlineConfig);
            }

            var config = controller.ParseConfig(mutableCommandlineConfig);

            if (config == null)
            {
                return(false);
            }

            IPipGraph pipGraphBuilder = null;

            using (var cache = Task.FromResult <Possible <EngineCache> >(
                       new EngineCache(
                           new InMemoryArtifactContentCache(),

                           // Note that we have an 'empty' store (no hits ever) rather than a normal in memory one.
                           new EmptyTwoPhaseFingerprintStore())))
            {
                if (frontEndEngineAbstraction == null)
                {
                    if (mutableCommandlineConfig.Engine.Phase.HasFlag(EnginePhases.Schedule))
                    {
                        var mountsTable = MountsTable.CreateAndRegister(loggingContext, engineContext, config, mutableCommandlineConfig.Startup.Properties);
                        frontEndEngineAbstraction = new FrontEndEngineImplementation(
                            loggingContext,
                            frontEndContext.PathTable,
                            config,
                            mutableCommandlineConfig.Startup,
                            mountsTable,
                            InputTracker.CreateDisabledTracker(loggingContext),
                            null,
                            null,
                            () => FileContentTable.CreateStub(),
                            5000,
                            false);

                        pipGraphBuilder = new PipGraph.Builder(
                            EngineSchedule.CreateEmptyPipTable(engineContext),
                            engineContext,
                            Scheduler.Tracing.Logger.Log,
                            loggingContext,
                            config,
                            mountsTable.MountPathExpander,
                            fingerprintSalt: config.Cache.CacheSalt,
                            directoryMembershipFingerprinterRules: new DirectoryMembershipFingerprinterRuleSet(config, engineContext.StringTable));

                        if (!AddConfigurationMountsAndCompleteInitialization(config, loggingContext, mountsTable))
                        {
                            return(false);
                        }

                        IDictionary <ModuleId, MountsTable> moduleMountsTableMap;
                        if (!mountsTable.PopulateModuleMounts(config.ModulePolicies.Values, out moduleMountsTableMap))
                        {
                            Contract.Assume(loggingContext.ErrorWasLogged, "An error should have been logged after MountTable.PopulateModuleMounts()");
                            return(false);
                        }
                    }
                    else
                    {
                        frontEndEngineAbstraction = new BasicFrontEndEngineAbstraction(frontEndContext.PathTable, frontEndContext.FileSystem, config);
                    }
                }

                using (frontEndEngineAbstraction is IDisposable ? (IDisposable)frontEndEngineAbstraction : null)
                {
                    // Attempt to build and/or analyze the workspace
                    if (!controller.PopulateGraph(
                            cache: cache,
                            graph: pipGraphBuilder,
                            engineAbstraction: frontEndEngineAbstraction,
                            evaluationFilter: evaluationFilter,
                            configuration: config,
                            startupConfiguration: mutableCommandlineConfig.Startup))
                    {
                        workspace = frontEndHostController.GetWorkspace();

                        // Error has been reported already
                        return(false);
                    }

                    pipGraph = pipGraphBuilder;
                }
            }

            Contract.Assert(frontEndHostController != null);

            workspace = frontEndHostController.GetWorkspace();

            if (mutableCommandlineConfig.Engine.Phase == EnginePhases.AnalyzeWorkspace)
            {
                // If workspace construction is successful, we run the linter on all specs.
                // This makes sure the workspace will carry all the errors that will occur when running the same specs in the regular engine path
                workspace = CreateLintedWorkspace(
                    workspace,
                    frontEndContext.LoggingContext,
                    config.FrontEnd,
                    pathTable);
            }

            return(true);
        }
Пример #26
0
 /// <summary>
 /// TODO: This is code duplicated from the configuration processor. Consider refactoring
 /// </summary>
 private static AbsolutePath FindPrimaryConfiguration(AbsolutePath rootFolder, FrontEndEngineAbstraction engine, PathTable pathTable)
 {
     return(TryFindConfig(rootFolder, engine, pathTable));
 }
        /// <summary>
        /// Builds and filters the worksapce.
        /// </summary>
        /// <remarks>
        /// This method not just builds the workspace from scratch, but it also tries to compute it in an efficient way.
        /// If there is a front end snapshot from the previous BuildXL run and the engine gives us a set of changed files,
        /// then we can build a filtered workspace based on the old spec-2-spec map without parsing the entire world.
        /// </remarks>
        private async Task <Workspace> BuildAndFilterWorkspaceAsync(IWorkspaceProvider workspaceProvider, FrontEndEngineAbstraction engineAbstraction, EvaluationFilter evaluationFilter)
        {
            // this step downloads nugets too, and that's why we want to do it outside of the progress reporting block below
            Possible <WorkspaceDefinition> workspaceDefinition = await TryGetWorkspaceDefinitionAsync(workspaceProvider);

            if (!workspaceDefinition.Succeeded)
            {
                // In some cases even if the workspace failed to build some of the pipeline still tries to continue
                // Complete mount initialization to enable mount related queries downstream
                Engine.CompleteMountInitialization();
                return(Workspace.Failure(workspaceProvider, workspaceProvider.Configuration, workspaceDefinition.Failure));
            }

            // As soon as we get the workspace definition, we can configure the mount table with the additional mounts modules may have defined and seal it
            foreach (var module in workspaceDefinition.Result.Modules.Where(module => module.Mounts != null))
            {
                foreach (var mount in module.Mounts)
                {
                    Engine.AddResolvedModuleDefinedMount(mount, LocationData.Create(module.ModuleConfigFile));
                }
            }

            // At this point the mount table can be completed
            if (!Engine.CompleteMountInitialization())
            {
                return(Workspace.Failure(workspaceProvider, workspaceProvider.Configuration, new GenericWorkspaceFailure("Mount points not properly defined. Detailed errors should have been logged.")));
            }

            return(await WithWorkspaceProgressReportingAsync(
                       numSpecs : workspaceDefinition.Result.SpecCount,
                       task : BuildAndFilterWorkspaceAsync(workspaceDefinition.Result, workspaceProvider, engineAbstraction, evaluationFilter)));
        }