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;
}