private bool DiscoverInterpreters()

in Python/Product/VSInterpreters/Interpreter/MSBuildProjectInterpreterFactoryProvider.cs [290:424]


        private bool DiscoverInterpreters(ProjectInfo projectInfo) {
            // <Interpreter Include="InterpreterDirectory">
            //   <Id>factoryProviderId;interpreterFactoryId</Id>
            //   <Version>...</Version>
            //   <InterpreterPath>...</InterpreterPath>
            //   <WindowsInterpreterPath>...</WindowsInterpreterPath>
            //   <PathEnvironmentVariable>...</PathEnvironmentVariable>
            //   <Description>...</Description>
            // </Interpreter>
            var projectHome = PathUtils.GetAbsoluteDirectoryPath(
                Path.GetDirectoryName(projectInfo.FullPath),
                projectInfo.GetPropertyValue("ProjectHome")
            );
            var factories = new Dictionary<string, FactoryInfo>();
            foreach (var item in projectInfo.GetInterpreters()) {
                // Errors in these options are fatal, so we set anyError and
                // continue with the next entry.
                var dir = GetValue(item, "EvaluatedInclude");
                if (!PathUtils.IsValidPath(dir)) {
                    Log("Interpreter has invalid path: {0}", dir ?? "(null)");
                    continue;
                }
                dir = PathUtils.GetAbsoluteDirectoryPath(projectHome, dir);

                var id = GetValue(item, MSBuildConstants.IdKey);
                if (string.IsNullOrEmpty(id)) {
                    Log("Interpreter {0} has invalid value for '{1}': {2}", dir, MSBuildConstants.IdKey, id);
                    continue;
                }
                if (factories.ContainsKey(id)) {
                    Log("Interpreter {0} has a non-unique id: {1}", dir, id);
                    continue;
                }

                var verStr = GetValue(item, MSBuildConstants.VersionKey);
                Version ver;
                if (string.IsNullOrEmpty(verStr) || !Version.TryParse(verStr, out ver)) {
                    Log("Interpreter {0} has invalid value for '{1}': {2}", dir, MSBuildConstants.VersionKey, verStr);
                    continue;
                }

                // The rest of the options are non-fatal. We create an instance
                // of NotFoundError with an amended description, which will
                // allow the user to remove the entry from the project file
                // later.
                bool hasError = false;

                var description = GetValue(item, MSBuildConstants.DescriptionKey);
                if (string.IsNullOrEmpty(description)) {
                    description = PathUtils.CreateFriendlyDirectoryPath(projectHome, dir);
                }

                var path = GetValue(item, MSBuildConstants.InterpreterPathKey);
                if (!PathUtils.IsValidPath(path)) {
                    Log("Interpreter {0} has invalid value for '{1}': {2}", dir, MSBuildConstants.InterpreterPathKey, path);
                    hasError = true;
                } else if (!hasError) {
                    path = PathUtils.GetAbsoluteFilePath(dir, path);
                }

                var winPath = GetValue(item, MSBuildConstants.WindowsPathKey);
                if (!PathUtils.IsValidPath(winPath)) {
                    Log("Interpreter {0} has invalid value for '{1}': {2}", dir, MSBuildConstants.WindowsPathKey, winPath);
                    hasError = true;
                } else if (!hasError) {
                    winPath = PathUtils.GetAbsoluteFilePath(dir, winPath);
                }

                var pathVar = GetValue(item, MSBuildConstants.PathEnvVarKey);
                if (string.IsNullOrEmpty(pathVar)) {
                    pathVar = "PYTHONPATH";
                }

                var arch = InterpreterArchitecture.TryParse(GetValue(item, MSBuildConstants.ArchitectureKey));

                string fullId = GetInterpreterId(projectInfo.FullPath, id);

                FactoryInfo info;
                if (hasError) {
                    info = new ErrorFactoryInfo(fullId, ver, description, dir);
                } else {
                    info = new ConfiguredFactoryInfo(this, new VisualStudioInterpreterConfiguration(
                        fullId,
                        description,
                        dir,
                        path,
                        winPath,
                        pathVar,
                        arch,
                        ver,
                        InterpreterUIMode.CannotBeDefault | InterpreterUIMode.CannotBeConfigured
                    ));
                }

                MergeFactory(projectInfo, factories, info);
            }

            HashSet<FactoryInfo> previousFactories = new HashSet<FactoryInfo>();
            if (projectInfo.Factories != null) {
                previousFactories.UnionWith(projectInfo.Factories.Values);
            }
            HashSet<FactoryInfo> newFactories = new HashSet<FactoryInfo>(factories.Values);

            bool anyChange = !newFactories.SetEquals(previousFactories);
            if (anyChange || projectInfo.Factories == null) {
                // Lock here mainly to ensure that any searches complete before
                // we trigger the changed event.
                lock (projectInfo) {
                    projectInfo.Factories = factories;
                }

                foreach (var removed in previousFactories.Except(newFactories)) {
                    projectInfo.ContextProvider.InterpreterUnloaded(
                        projectInfo.Context,
                        removed.Config
                    );

                    IDisposable disp = removed as IDisposable;
                    if (disp != null) {
                        disp.Dispose();
                    }
                }

                foreach (var added in newFactories.Except(previousFactories)) {
                    foreach (var factory in factories) {
                        projectInfo.ContextProvider.InterpreterLoaded(
                            projectInfo.Context,
                            factory.Value.Config
                        );
                    }
                }
            }

            return anyChange;
        }