private LoadImportsResult ExpandAndLoadImportsFromUnescapedImportExpression()

in src/Build/Evaluation/Evaluator.cs [2404:2603]


        private LoadImportsResult ExpandAndLoadImportsFromUnescapedImportExpression(string directoryOfImportingFile, ProjectImportElement importElement, string unescapedExpression,
                                            bool throwOnFileNotExistsError, out List<ProjectRootElement> imports)
        {
            string importExpressionEscaped = _expander.ExpandIntoStringLeaveEscaped(unescapedExpression, ExpanderOptions.ExpandProperties, importElement.ProjectLocation);
            ElementLocation importLocationInProject = importElement.Location;

            bool atleastOneImportIgnored = false;
            imports = new List<ProjectRootElement>();
            string[] importFilesEscaped = null;

            try
            {
                // Handle the case of an expression expanding to nothing specially;
                // force an exception here to give a nicer message, that doesn't show the project directory in it.
                if (importExpressionEscaped.Length == 0 || importExpressionEscaped.Trim().Length == 0)
                {
                    FileUtilities.NormalizePath(EscapingUtilities.UnescapeAll(importExpressionEscaped));
                }

                // Expand the wildcards and provide an alphabetical order list of import statements.
                importFilesEscaped = EngineFileUtilities.GetFileListEscaped(directoryOfImportingFile, importExpressionEscaped);
            }
            catch (Exception ex) when (ExceptionHandling.IsIoRelatedException(ex))
            {
                ProjectErrorUtilities.ThrowInvalidProject(importLocationInProject, "InvalidAttributeValueWithException", EscapingUtilities.UnescapeAll(importExpressionEscaped), XMakeAttributes.project, XMakeElements.import, ex.Message);
            }

            foreach (string importFileEscaped in importFilesEscaped)
            {
                string importFileUnescaped = EscapingUtilities.UnescapeAll(importFileEscaped);

                // GetFileListEscaped may not return a rooted path, we need to root it. Also if there are no wild cards we still need to get the full path on the filespec.
                try
                {
                    if (directoryOfImportingFile != null && !Path.IsPathRooted(importFileUnescaped))
                    {
                        importFileUnescaped = Path.Combine(directoryOfImportingFile, importFileUnescaped);
                    }

                    // Canonicalize to eg., eliminate "\..\"
                    importFileUnescaped = FileUtilities.NormalizePath(importFileUnescaped);
                }
                catch (Exception ex) when (ExceptionHandling.IsIoRelatedException(ex))
                {
                    ProjectErrorUtilities.ThrowInvalidProject(importLocationInProject, "InvalidAttributeValueWithException", importFileUnescaped, XMakeAttributes.project, XMakeElements.import, ex.Message);
                }

                // If a file is included twice, or there is a cycle of imports, we ignore all but the first import
                // and issue a warning to that effect.
                if (String.Equals(_projectRootElement.FullPath, importFileUnescaped, StringComparison.OrdinalIgnoreCase) /* We are trying to import ourselves */)
                {
                    _loggingService.LogWarning(_buildEventContext, null, new BuildEventFileInfo(importLocationInProject), "SelfImport", importFileUnescaped);
                    atleastOneImportIgnored = true;

                    continue;
                }

                // Circular dependencies (e.g. t0.targets imports t1.targets, t1.targets imports t2.targets and t2.targets imports t0.targets) will be
                // caught by the check for duplicate imports which is done later in the method. However, if the project load setting requires throwing
                // on circular imports or recording duplicate-but-not-circular imports, then we need to do exclusive check for circular imports here.
                if ((_loadSettings & ProjectLoadSettings.RejectCircularImports) != 0 || (_loadSettings & ProjectLoadSettings.RecordDuplicateButNotCircularImports) != 0)
                {
                    // Check if this import introduces circularity.
                    if (IntroducesCircularity(importFileUnescaped, importElement))
                    {
                        // Get the full path of the MSBuild file that has this import.
                        string importedBy = importElement.ContainingProject.FullPath ?? String.Empty;

                        _loggingService.LogWarning(_buildEventContext, null, new BuildEventFileInfo(importLocationInProject), "ImportIntroducesCircularity", importFileUnescaped, importedBy);

                        // Throw exception if the project load settings requires us to stop the evaluation of a project when circular imports are detected.
                        if ((_loadSettings & ProjectLoadSettings.RejectCircularImports) != 0)
                        {
                            ProjectErrorUtilities.ThrowInvalidProject(importLocationInProject, "ImportIntroducesCircularity", importFileUnescaped, importedBy);
                        }

                        // Ignore this import and no more further processing on it.
                        atleastOneImportIgnored = true;
                        continue;
                    }
                }

                ProjectImportElement previouslyImportedAt;
                bool duplicateImport = false;

                if (_importsSeen.TryGetValue(importFileUnescaped, out previouslyImportedAt))
                {
                    string parenthesizedProjectLocation = String.Empty;

                    // If neither file involved is the project itself, append its path in square brackets
                    if (previouslyImportedAt.ContainingProject != _projectRootElement && importElement.ContainingProject != _projectRootElement)
                    {
                        parenthesizedProjectLocation = "[" + _projectRootElement.FullPath + "]";
                    }
                    // TODO: Detect if the duplicate import came from an SDK attribute
                    _loggingService.LogWarning(_buildEventContext, null, new BuildEventFileInfo(importLocationInProject), "DuplicateImport", importFileUnescaped, previouslyImportedAt.Location.LocationString, parenthesizedProjectLocation);
                    duplicateImport = true;
                }

                ProjectRootElement importedProjectElement;

                try
                {
                    // We take the explicit loaded flag from the project ultimately being evaluated.  The goal being that
                    // if a project system loaded a user's project, all imports (which would include property sheets and .user file)
                    // may impact evaluation and should be included in the weak cache without ever being cleared out to avoid
                    // the project system being exposed to multiple PRE instances for the same file.  We only want to consider
                    // clearing the weak cache (and therefore setting explicitload=false) for projects the project system never
                    // was directly interested in (i.e. the ones that were reached for purposes of building a P2P.)
                    bool explicitlyLoaded = importElement.ContainingProject.IsExplicitlyLoaded;
                    importedProjectElement = _projectRootElementCache.Get(
                        importFileUnescaped,
                        (p, c) => ProjectRootElement.OpenProjectOrSolution(
                            importFileUnescaped,
                            new ReadOnlyConvertingDictionary<string, ProjectPropertyInstance, string>(
                                _data.GlobalPropertiesDictionary,
                                instance => ((IProperty)instance).EvaluatedValueEscaped),
                            _data.ExplicitToolsVersion,
                            _loggingService,
                            _projectRootElementCache,
                            _buildEventContext,
                            explicitlyLoaded),
                        explicitlyLoaded,
                        // don't care about formatting, reuse whatever is there
                        preserveFormatting: null);

                    if (duplicateImport)
                    {
                        // Only record the data if we want to record duplicate imports
                        if ((_loadSettings & ProjectLoadSettings.RecordDuplicateButNotCircularImports) != 0)
                        {
                            _data.RecordImportWithDuplicates(importElement, importedProjectElement,
                                importedProjectElement.Version);
                        }

                        // Since we have already seen this we need to not continue on in the processing.
                        atleastOneImportIgnored = true;
                        continue;
                    }
                    else
                    {
                        imports.Add(importedProjectElement);
                    }
                }
                catch (InvalidProjectFileException ex) when (ExceptionHandling.IsIoRelatedException(ex.InnerException))
                {
                    // The import couldn't be read from disk, or something similar. In that case,
                    // the error message would be more useful if it pointed to the location in the importing project file instead.
                    // Perhaps the import tag has a typo in, for example.

                    // There's a specific message for file not existing
                    if (!File.Exists(importFileUnescaped))
                    {
                        if (!throwOnFileNotExistsError ||
                            (_loadSettings & ProjectLoadSettings.IgnoreMissingImports) != 0)
                        {
                            continue;
                        }

                        ProjectErrorUtilities.ThrowInvalidProject(importLocationInProject, "ImportedProjectNotFound",
                            importFileUnescaped);
                    }
                    else
                    {
                        // Otherwise a more generic message, still pointing to the location of the import tag
                        ProjectErrorUtilities.ThrowInvalidProject(importLocationInProject, "InvalidImportedProjectFile",
                            importFileUnescaped, ex.InnerException.Message);
                    }
                }

                // Because these expressions will never be expanded again, we 
                // can store the unescaped value. The only purpose of escaping is to 
                // avoid undesired splitting or expansion.
                _importsSeen.Add(importFileUnescaped, importElement);
            }

            if (imports.Count > 0)
            {
                return LoadImportsResult.ProjectsImported;
            }

            if (atleastOneImportIgnored)
            {
                return LoadImportsResult.FoundFilesToImportButIgnored;
            }

            if (importFilesEscaped.Length == 0)
            {
                // Expression resolved to "", eg. a wildcard
                return LoadImportsResult.ImportExpressionResolvedToNothing;
            }

            // No projects were imported, none were ignored but we did have atleast
            // one file to process, which means that we did try to load a file but
            // failed w/o an exception escaping from here.
            // We ignore only the file not existing error, so, that is the case here
            // (if @throwOnFileNotExistsError==true, then it would have thrown
            //  and we wouldn't be here)
            return LoadImportsResult.TriedToImportButFileNotFound;
        }