public RemoveOperation(RemoveOperationBuilder builder, LazyItemEvaluator <P, I, M, D> lazyEvaluator)
                : base(builder, lazyEvaluator)
            {
                _matchOnMetadata = builder.MatchOnMetadata.ToImmutable();

                ProjectFileErrorUtilities.VerifyThrowInvalidProjectFile(
                    _matchOnMetadata.IsEmpty || _itemSpec.Fragments.All(f => f is ItemSpec <P, I> .ItemExpressionFragment),
                    new BuildEventFileInfo(string.Empty),
                    "OM_MatchOnMetadataIsRestrictedToReferencedItems");

                if (!_matchOnMetadata.IsEmpty)
                {
                    _metadataSet = new MetadataTrie <P, I>(builder.MatchOnMetadataOptions, _matchOnMetadata, _itemSpec);
                }
            }
Пример #2
0
        private List <ProjectItemInstance> FindItemsMatchingMetadataSpecification(
            ICollection <ProjectItemInstance> group,
            ProjectItemGroupTaskItemInstance child,
            Expander <ProjectPropertyInstance, ProjectItemInstance> expander,
            HashSet <string> matchOnMetadata,
            MatchOnMetadataOptions matchingOptions)
        {
            ItemSpec <ProjectPropertyInstance, ProjectItemInstance> itemSpec = new ItemSpec <ProjectPropertyInstance, ProjectItemInstance>(child.Remove, expander, child.RemoveLocation, Project.Directory, true);

            ProjectFileErrorUtilities.VerifyThrowInvalidProjectFile(
                itemSpec.Fragments.All(f => f is ItemSpec <ProjectPropertyInstance, ProjectItemInstance> .ItemExpressionFragment),
                BuildEventFileInfo.Empty,
                "OM_MatchOnMetadataIsRestrictedToReferencedItems",
                child.RemoveLocation,
                child.Remove);
            MetadataTrie <ProjectPropertyInstance, ProjectItemInstance> metadataSet = new MetadataTrie <ProjectPropertyInstance, ProjectItemInstance>(matchingOptions, matchOnMetadata, itemSpec);

            return(group.Where(item => metadataSet.Contains(matchOnMetadata.Select(m => item.GetMetadataValue(m)))).ToList());
        }
Пример #3
0
            /// <summary>
            /// Apply the Remove operation.
            /// </summary>
            /// <remarks>
            /// This operation is mostly implemented in terms of the default <see cref="LazyItemOperation.ApplyImpl(ImmutableList{ItemData}.Builder, ImmutableHashSet{string})"/>.
            /// This override exists to apply the removing-everything short-circuit.
            /// </remarks>
            protected override void ApplyImpl(ImmutableList <ItemData> .Builder listBuilder, ImmutableHashSet <string> globsToIgnore)
            {
                var matchOnMetadataValid = !_matchOnMetadata.IsEmpty && _itemSpec.Fragments.Count == 1 &&
                                           _itemSpec.Fragments.First() is ItemSpec <ProjectProperty, ProjectItem> .ItemExpressionFragment;

                ProjectFileErrorUtilities.VerifyThrowInvalidProjectFile(
                    _matchOnMetadata.IsEmpty || (matchOnMetadataValid && _matchOnMetadata.Count == 1),
                    new BuildEventFileInfo(string.Empty),
                    "OM_MatchOnMetadataIsRestrictedToOnlyOneReferencedItem");

                if (_matchOnMetadata.IsEmpty && ItemspecContainsASingleBareItemReference(_itemSpec, _itemElement.ItemType))
                {
                    // Perf optimization: If the Remove operation references itself (e.g. <I Remove="@(I)"/>)
                    // then all items are removed and matching is not necessary
                    listBuilder.Clear();
                    return;
                }

                base.ApplyImpl(listBuilder, globsToIgnore);
            }
            // todo port the self referencing matching optimization (e.g. <I Remove="@(I)">) from Update to Remove as well. Ideally make one mechanism for both. https://github.com/Microsoft/msbuild/issues/2314
            // todo Perf: do not match against the globs: https://github.com/Microsoft/msbuild/issues/2329
            protected override ImmutableList <I> SelectItems(ImmutableList <ItemData> .Builder listBuilder, ImmutableHashSet <string> globsToIgnore)
            {
                var matchOnMetadataValid = !_matchOnMetadata.IsEmpty && _itemSpec.Fragments.Count == 1 &&
                                           _itemSpec.Fragments.First() is ItemSpec <ProjectProperty, ProjectItem> .ItemExpressionFragment;

                ProjectFileErrorUtilities.VerifyThrowInvalidProjectFile(
                    _matchOnMetadata.IsEmpty || matchOnMetadataValid && _matchOnMetadata.Count == 1,
                    new BuildEventFileInfo(string.Empty),
                    "OM_MatchOnMetadataIsRestrictedToOnlyOneReferencedItem");

                var items = ImmutableHashSet.CreateBuilder <I>();

                foreach (ItemData item in listBuilder)
                {
                    if (_matchOnMetadata.IsEmpty ? _itemSpec.MatchesItem(item.Item) : _itemSpec.MatchesItemOnMetadata(item.Item, _matchOnMetadata, _matchOnMetadataOptions))
                    {
                        items.Add(item.Item);
                    }
                }

                return(items.ToImmutableList());
            }
Пример #5
0
        private List <ProjectItemInstance> FindItemsUsingMatchOnMetadata(
            ICollection <ProjectItemInstance> items,
            ProjectItemGroupTaskItemInstance child,
            ItemBucket bucket,
            HashSet <string> matchOnMetadata,
            MatchOnMetadataOptions options)
        {
            ErrorUtilities.VerifyThrowArgumentNull(matchOnMetadata, nameof(matchOnMetadata));

            var itemSpec = new ItemSpec <ProjectPropertyInstance, ProjectItemInstance>(child.Remove, bucket.Expander, child.RemoveLocation, Project.Directory, true);

            ProjectFileErrorUtilities.VerifyThrowInvalidProjectFile(
                itemSpec.Fragments.Count == 1 &&
                itemSpec.Fragments.First() is ItemSpec <ProjectPropertyInstance, ProjectItemInstance> .ItemExpressionFragment &&
                matchOnMetadata.Count == 1,
                new BuildEventFileInfo(string.Empty),
                "OM_MatchOnMetadataIsRestrictedToOnlyOneReferencedItem",
                child.RemoveLocation,
                child.Remove);

            return(items.Where(item => itemSpec.MatchesItemOnMetadata(item, matchOnMetadata, options)).ToList());
        }
Пример #6
0
        private (IReadOnlyCollection <ProjectGraphEntryPoint> NewEntryPoints, IReadOnlyDictionary <string, IReadOnlyCollection <string> > SolutionDependencies) ExpandSolutionIfPresent(IReadOnlyCollection <ProjectGraphEntryPoint> entryPoints)
        {
            if (entryPoints.Count == 0 || !entryPoints.Any(e => FileUtilities.IsSolutionFilename(e.ProjectFile)))
            {
                return(entryPoints, null);
            }

            if (entryPoints.Count != 1)
            {
                throw new ArgumentException(
                          ResourceUtilities.FormatResourceStringIgnoreCodeAndKeyword(
                              "StaticGraphAcceptsSingleSolutionEntryPoint",
                              string.Join(";", entryPoints.Select(e => e.ProjectFile))));
            }

            ErrorUtilities.VerifyThrowArgument(entryPoints.Count == 1, "StaticGraphAcceptsSingleSolutionEntryPoint");

            var solutionEntryPoint       = entryPoints.Single();
            var solutionGlobalProperties = ImmutableDictionary.CreateRange(
                keyComparer: StringComparer.OrdinalIgnoreCase,
                valueComparer: StringComparer.OrdinalIgnoreCase,
                items: solutionEntryPoint.GlobalProperties ?? ImmutableDictionary <string, string> .Empty);

            var solution = SolutionFile.Parse(FileUtilities.NormalizePath(solutionEntryPoint.ProjectFile));

            if (solution.SolutionParserWarnings.Count != 0 || solution.SolutionParserErrorCodes.Count != 0)
            {
                throw new InvalidProjectFileException(
                          ResourceUtilities.FormatResourceStringIgnoreCodeAndKeyword(
                              "StaticGraphSolutionLoaderEncounteredSolutionWarningsAndErrors",
                              solutionEntryPoint.ProjectFile,
                              string.Join(";", solution.SolutionParserWarnings),
                              string.Join(";", solution.SolutionParserErrorCodes)));
            }

            var projectsInSolution = GetBuildableProjects(solution);

            var currentSolutionConfiguration = SelectSolutionConfiguration(solution, solutionGlobalProperties);

            var newEntryPoints = new List <ProjectGraphEntryPoint>(projectsInSolution.Count);

            foreach (var project in projectsInSolution)
            {
                if (project.ProjectConfigurations.Count == 0)
                {
                    continue;
                }

                var projectConfiguration = SelectProjectConfiguration(currentSolutionConfiguration, project.ProjectConfigurations);

                if (projectConfiguration.IncludeInBuild)
                {
                    newEntryPoints.Add(
                        new ProjectGraphEntryPoint(
                            FileUtilities.NormalizePath(project.AbsolutePath),
                            solutionGlobalProperties
                            .SetItem("Configuration", projectConfiguration.ConfigurationName)
                            .SetItem("Platform", projectConfiguration.PlatformName)
                            ));
                }
            }

            newEntryPoints.TrimExcess();

            return(newEntryPoints, GetSolutionDependencies(solution));

            IReadOnlyCollection <ProjectInSolution> GetBuildableProjects(SolutionFile solutionFile)
            {
                return(solutionFile.ProjectsInOrder.Where(p => p.ProjectType == SolutionProjectType.KnownToBeMSBuildFormat).ToImmutableArray());
            }

            SolutionConfigurationInSolution SelectSolutionConfiguration(SolutionFile solutionFile, ImmutableDictionary <string, string> globalProperties)
            {
                var solutionConfiguration = globalProperties.ContainsKey("Configuration")
                    ? globalProperties["Configuration"]
                    : solutionFile.GetDefaultConfigurationName();

                var solutionPlatform = globalProperties.ContainsKey("Platform")
                    ? globalProperties["Platform"]
                    : solutionFile.GetDefaultPlatformName();

                return(new SolutionConfigurationInSolution(solutionConfiguration, solutionPlatform));
            }

            ProjectConfigurationInSolution SelectProjectConfiguration(
                SolutionConfigurationInSolution solutionConfig,
                IReadOnlyDictionary <string, ProjectConfigurationInSolution> projectConfigs)
            {
                // implements the matching described in https://docs.microsoft.com/en-us/visualstudio/ide/understanding-build-configurations?view=vs-2019#how-visual-studio-assigns-project-configuration

                var solutionConfigFullName = solutionConfig.FullName;

                if (projectConfigs.ContainsKey(solutionConfigFullName))
                {
                    return(projectConfigs[solutionConfigFullName]);
                }

                var partiallyMarchedConfig = projectConfigs.FirstOrDefault(pc => pc.Value.ConfigurationName.Equals(solutionConfig.ConfigurationName, StringComparison.OrdinalIgnoreCase)).Value;

                return(partiallyMarchedConfig ?? projectConfigs.First().Value);
            }

            IReadOnlyDictionary <string, IReadOnlyCollection <string> > GetSolutionDependencies(SolutionFile solutionFile)
            {
                var solutionDependencies = new Dictionary <string, IReadOnlyCollection <string> >();

                foreach (var projectWithDependencies in solutionFile.ProjectsInOrder.Where(p => p.Dependencies.Count != 0))
                {
                    solutionDependencies[FileUtilities.NormalizePath(projectWithDependencies.AbsolutePath)] = projectWithDependencies.Dependencies.Select(
                        dependencyGuid =>
                    {
                        // code snippet cloned from SolutionProjectGenerator.AddPropertyGroupForSolutionConfiguration

                        if (!solutionFile.ProjectsByGuid.TryGetValue(dependencyGuid, out var dependencyProject))
                        {
                            // If it's not itself part of the solution, that's an invalid solution
                            ProjectFileErrorUtilities.VerifyThrowInvalidProjectFile(
                                dependencyProject != null,
                                "SubCategoryForSolutionParsingErrors",
                                new BuildEventFileInfo(solutionFile.FullPath),
                                "SolutionParseProjectDepNotFoundError",
                                projectWithDependencies.ProjectGuid,
                                dependencyGuid);
                        }

                        // Add it to the list of dependencies, but only if it should build in this solution configuration
                        // (If a project is not selected for build in the solution configuration, it won't build even if it's depended on by something that IS selected for build)
                        // .. and only if it's known to be MSBuild format, as projects can't use the information otherwise
                        return(dependencyProject?.ProjectType == SolutionProjectType.KnownToBeMSBuildFormat
                                ? FileUtilities.NormalizePath(dependencyProject.AbsolutePath)
                                : null);
                    })
                                                                                                              .Where(p => p != null)
                                                                                                              .ToArray();
                }

                return(solutionDependencies);
            }
        }
Пример #7
0
        /// <summary>
        /// Used to load information about default MSBuild tasks i.e. tasks that do not need to be explicitly declared in projects
        /// with the &lt;UsingTask&gt; element. Default task information is read from special files, which are located in the same
        /// directory as the MSBuild binaries.
        /// </summary>
        /// <remarks>
        /// 1) a default tasks file needs the &lt;Project&gt; root tag in order to be well-formed
        /// 2) the XML declaration tag &lt;?xml ...&gt; is ignored
        /// 3) comment tags are always ignored regardless of their placement
        /// 4) the rest of the tags are expected to be &lt;UsingTask&gt; tags
        /// </remarks>
        private void RegisterDefaultTasks(BuildEventContext buildEventContext)
        {
            if (!defaultTasksRegistrationAttempted)
            {
                try
                {
                    this.defaultTaskRegistry = new TaskRegistry();

                    string[] defaultTasksFiles = { };

                    try
                    {
                        defaultTasksFiles = getFiles(toolset.ToolsPath, defaultTasksFilePattern);

                        if (defaultTasksFiles.Length == 0)
                        {
                            loggingServices.LogWarning(buildEventContext, new BuildEventFileInfo(/* this warning truly does not involve any file */ String.Empty),
                                                       "DefaultTasksFileLoadFailureWarning",
                                                       defaultTasksFilePattern, toolset.ToolsPath, String.Empty);
                        }
                    }
                    // handle security problems when finding the default tasks files
                    catch (UnauthorizedAccessException e)
                    {
                        loggingServices.LogWarning(buildEventContext, new BuildEventFileInfo(/* this warning truly does not involve any file */ String.Empty),
                                                   "DefaultTasksFileLoadFailureWarning",
                                                   defaultTasksFilePattern, toolset.ToolsPath, e.Message);
                    }
                    // handle problems when reading the default tasks files
                    catch (Exception e) // Catching Exception, but rethrowing unless it's an IO related exception.
                    {
                        if (ExceptionHandling.NotExpectedException(e))
                        {
                            throw;
                        }

                        loggingServices.LogWarning(buildEventContext, new BuildEventFileInfo(/* this warning truly does not involve any file */ String.Empty),
                                                   "DefaultTasksFileLoadFailureWarning",
                                                   defaultTasksFilePattern, toolset.ToolsPath, e.Message);
                    }

                    BuildPropertyGroup propertyBag = null;

                    foreach (string defaultTasksFile in defaultTasksFiles)
                    {
                        try
                        {
                            XmlDocument defaultTasks = loadXmlFromPath(defaultTasksFile);

                            // look for the first root tag that is not a comment or an XML declaration -- this should be the <Project> tag
                            // NOTE: the XML parser will guarantee there is only one real root element in the file
                            // but we need to find it amongst the other types of XmlNode at the root.
                            foreach (XmlNode topLevelNode in defaultTasks.ChildNodes)
                            {
                                if (XmlUtilities.IsXmlRootElement(topLevelNode))
                                {
                                    ProjectErrorUtilities.VerifyThrowInvalidProject(topLevelNode.LocalName == XMakeElements.project,
                                                                                    topLevelNode, "UnrecognizedElement", topLevelNode.Name);

                                    ProjectErrorUtilities.VerifyThrowInvalidProject((topLevelNode.Prefix.Length == 0) && (String.Equals(topLevelNode.NamespaceURI, XMakeAttributes.defaultXmlNamespace, StringComparison.OrdinalIgnoreCase)),
                                                                                    topLevelNode, "ProjectMustBeInMSBuildXmlNamespace", XMakeAttributes.defaultXmlNamespace);

                                    // the <Project> tag can only the XML namespace -- no other attributes
                                    foreach (XmlAttribute projectAttribute in topLevelNode.Attributes)
                                    {
                                        ProjectXmlUtilities.VerifyThrowProjectInvalidAttribute(projectAttribute.Name == XMakeAttributes.xmlns, projectAttribute);
                                    }

                                    // look at all the child tags of the <Project> root tag we found
                                    foreach (XmlNode usingTaskNode in topLevelNode.ChildNodes)
                                    {
                                        if (usingTaskNode.NodeType != XmlNodeType.Comment)
                                        {
                                            ProjectErrorUtilities.VerifyThrowInvalidProject(usingTaskNode.Name == XMakeElements.usingTask,
                                                                                            usingTaskNode, "UnrecognizedElement", usingTaskNode.Name);

                                            // Initialize the property bag if it hasn't been already.
                                            if (propertyBag == null)
                                            {
                                                // Set the value of the MSBuildBinPath/ToolsPath properties.
                                                BuildPropertyGroup reservedPropertyBag = new BuildPropertyGroup();

                                                reservedPropertyBag.SetProperty(
                                                    new BuildProperty(ReservedPropertyNames.binPath, EscapingUtilities.Escape(toolset.ToolsPath),
                                                                      PropertyType.ReservedProperty));

                                                reservedPropertyBag.SetProperty(
                                                    new BuildProperty(ReservedPropertyNames.toolsPath, EscapingUtilities.Escape(toolset.ToolsPath),
                                                                      PropertyType.ReservedProperty));

                                                // Also set MSBuildAssemblyVersion so that the tasks file can tell between v4 and v12 MSBuild
                                                reservedPropertyBag.SetProperty(
                                                    new BuildProperty(ReservedPropertyNames.assemblyVersion, Constants.AssemblyVersion,
                                                                      PropertyType.ReservedProperty));

                                                propertyBag = new BuildPropertyGroup();
                                                propertyBag.ImportInitialProperties(parentEngine.EnvironmentProperties, reservedPropertyBag, BuildProperties, parentEngine.GlobalProperties);
                                            }

                                            defaultTaskRegistry.RegisterTask(new UsingTask((XmlElement)usingTaskNode, true), new Expander(propertyBag), loggingServices, buildEventContext);
                                        }
                                    }

                                    break;
                                }
                            }
                        }
                        // handle security problems when loading the default tasks file
                        catch (UnauthorizedAccessException e)
                        {
                            loggingServices.LogError(buildEventContext, new BuildEventFileInfo(defaultTasksFile), "DefaultTasksFileFailure", e.Message);
                            break;
                        }
                        // handle problems when loading the default tasks file
                        catch (IOException e)
                        {
                            loggingServices.LogError(buildEventContext, new BuildEventFileInfo(defaultTasksFile), "DefaultTasksFileFailure", e.Message);
                            break;
                        }
                        // handle XML errors in the default tasks file
                        catch (XmlException e)
                        {
                            ProjectFileErrorUtilities.VerifyThrowInvalidProjectFile(false, new BuildEventFileInfo(e),
                                                                                    "DefaultTasksFileFailure", e.Message);
                        }
                    }
                }
                finally
                {
                    defaultTasksRegistrationAttempted = true;
                }
            }
        }
        /// <summary>
        /// Validates a project against the given schema -- if no schema is provided, uses the default schema.
        /// </summary>
        /// <owner>JomoF</owner>
        /// <param name="contentReader"></param>
        /// <param name="schemaFile">Can be null.</param>
        /// <param name="projectFile"></param>
        private void VerifyProjectSchema
        (
            TextReader contentReader,
            string schemaFile,
            string projectFile
        )
        {
            // Options for XmlReader object can be set only in constructor. After the object is created, they
            // become read-only. Because of that we need to create
            // XmlSettings structure, fill it in with correct parameters and pass into XmlReader constructor.

            XmlReaderSettings validatorSettings = new XmlReaderSettings();

            validatorSettings.ValidationType          = ValidationType.Schema;
            validatorSettings.XmlResolver             = null;
            validatorSettings.ValidationEventHandler += this.OnSchemaValidationError;

            if (string.IsNullOrEmpty(schemaFile))
            {
                schemaFile = Path.Combine(binPath, "Microsoft.Build.xsd");
            }

            // Log the schema file we're using, particularly since it can vary
            // according to  the toolset being used
            engineLoggingServices.LogComment(buildEventContext, "SchemaFileLocation", schemaFile);

            XmlTextReader schemaReader = new XmlTextReader(schemaFile);

            schemaReader.DtdProcessing = DtdProcessing.Ignore;
            using (schemaReader)
            {
                try
                {
                    validatorSettings.Schemas.Add(XMakeAttributes.defaultXmlNamespace, schemaReader);

                    // We need full path to the project file to be able handle it as URI in ValidationEventHandler.
                    // Uri class cannot instantiate with relative paths.
                    if (projectFile.Length != 0)
                    {
                        projectFile = Path.GetFullPath(projectFile);
                    }

                    using (XmlReader validator = XmlReader.Create(contentReader, validatorSettings, projectFile)) // May also throw XmlSchemaException
                    {
                        this.syntaxError = false;
                        bool couldRead = true;

                        while (couldRead)
                        {
                            try
                            {
                                couldRead = validator.Read();
                            }
                            catch (XmlException)
                            {
                                // We swallow exception here because XmlValidator fires the validation event to report the error
                                // And we handle the event. Also XmlValidator can continue parsing Xml text after throwing an exception.
                                // Thus we don't need any special recover here.
                            }
                        }

                        ProjectFileErrorUtilities.VerifyThrowInvalidProjectFile(!this.syntaxError, "SubCategoryForSchemaValidationErrors",
                                                                                new BuildEventFileInfo(projectFile), "ProjectSchemaErrorHalt");
                    }
                }
                // handle errors in the schema itself
                catch (XmlException e)
                {
                    ProjectFileErrorUtilities.VerifyThrowInvalidProjectFile(false, "SubCategoryForSchemaValidationErrors", new BuildEventFileInfo(e),
                                                                            "InvalidSchemaFile", schemaFile, e.Message);
                }
                // handle errors in the schema itself
                catch (XmlSchemaException e)
                {
                    ProjectFileErrorUtilities.VerifyThrowInvalidProjectFile(false, "SubCategoryForSchemaValidationErrors", new BuildEventFileInfo(e),
                                                                            "InvalidSchemaFile", schemaFile, e.Message);
                }
            }
        }