Beispiel #1
0
 internal static string GetMetadataValue(string currentDirectory, string evaluatedIncludeBeforeWildcardExpansionEscaped, string evaluatedIncludeEscaped, string definingProjectEscaped, string name, ref string fullPath)
 {
     return(EscapingUtilities.UnescapeAll(GetMetadataValueEscaped(currentDirectory, evaluatedIncludeBeforeWildcardExpansionEscaped, evaluatedIncludeEscaped, definingProjectEscaped, name, ref fullPath)));
 }
        /// <summary>
        /// Retrieves the list of dependencies this target needs to have built and moves the target to the next state.
        /// Never returns null.
        /// </summary>
        /// <returns>A collection of targets on which this target depends.</returns>
        internal List <TargetSpecification> GetDependencies(ProjectLoggingContext projectLoggingContext)
        {
            VerifyState(_state, TargetEntryState.Dependencies);

            // Resolve the target now, since from this point on we are going to be doing work with the actual instance.
            GetTargetInstance();

            // We first make sure no batching was attempted with the target's condition.
            // UNDONE: (Improvement) We want to allow this actually.  In order to do this we need to determine what the
            // batching buckets are, and if there are any which aren't empty, return our list of dependencies.
            // Only in the case where all bucket conditions fail do we want to skip the target entirely (and
            // this skip building the dependencies.)
            if (ExpressionShredder.ContainsMetadataExpressionOutsideTransform(_target.Condition))
            {
                ProjectErrorUtilities.ThrowInvalidProject(_target.ConditionLocation, "TargetConditionHasInvalidMetadataReference", _target.Name, _target.Condition);
            }

            // If condition is false (based on propertyBag), set this target's state to
            // "Skipped" since we won't actually build it.
            bool condition = ConditionEvaluator.EvaluateCondition
                             (
                _target.Condition,
                ParserOptions.AllowPropertiesAndItemLists,
                _expander,
                ExpanderOptions.ExpandPropertiesAndItems,
                _requestEntry.ProjectRootDirectory,
                _target.ConditionLocation,
                projectLoggingContext.LoggingService,
                projectLoggingContext.BuildEventContext
                             );

            if (!condition)
            {
                _targetResult = new TargetResult(Array.Empty <TaskItem>(), new WorkUnitResult(WorkUnitResultCode.Skipped, WorkUnitActionCode.Continue, null));
                _state        = TargetEntryState.Completed;

                if (!projectLoggingContext.LoggingService.OnlyLogCriticalEvents)
                {
                    // Expand the expression for the Log.  Since we know the condition evaluated to false, leave unexpandable properties in the condition so as not to cause an error
                    string expanded = _expander.ExpandIntoStringAndUnescape(_target.Condition, ExpanderOptions.ExpandPropertiesAndItems | ExpanderOptions.LeavePropertiesUnexpandedOnError, _target.ConditionLocation);

                    // By design: Not building dependencies. This is what NAnt does too.
                    // NOTE: In the original code, this was logged from the target logging context.  However, the target
                    // hadn't been "started" by then, so you'd get a target message outside the context of a started
                    // target.  In the Task builder (and original Task Engine), a Task Skipped message would be logged in
                    // the context of the target, not the task.  This should be the same, especially given that we
                    // wish to allow batching on the condition of a target.
                    projectLoggingContext.LogComment(MessageImportance.Low, "TargetSkippedFalseCondition", _target.Name, _target.Condition, expanded);
                }

                return(new List <TargetSpecification>());
            }

            var dependencies = _expander.ExpandIntoStringListLeaveEscaped(_target.DependsOnTargets, ExpanderOptions.ExpandPropertiesAndItems, _target.DependsOnTargetsLocation);
            List <TargetSpecification> dependencyTargets = new List <TargetSpecification>();

            foreach (string escapedDependency in dependencies)
            {
                string dependencyTargetName = EscapingUtilities.UnescapeAll(escapedDependency);
                dependencyTargets.Add(new TargetSpecification(dependencyTargetName, _target.DependsOnTargetsLocation));
            }

            _state = TargetEntryState.Execution;

            return(dependencyTargets);
        }
Beispiel #3
0
            public string GetMetadata(string metadataName)
            {
                string metadataValue = (this as ITaskItem2).GetMetadataValueEscaped(metadataName);

                return(EscapingUtilities.UnescapeAll(metadataValue));
            }
Beispiel #4
0
 public virtual IEnumerable <string> GetReferencedItems()
 {
     return(Enumerable.Repeat(EscapingUtilities.UnescapeAll(TextFragment), 1));
 }
Beispiel #5
0
 public override IEnumerable <string> GetReferencedItems()
 {
     return(ReferencedItems.Select(v => EscapingUtilities.UnescapeAll(v.ItemAsValueFragment.TextFragment)));
 }
 /// <summary>
 /// Escape the string according to MSBuild's escaping rules
 /// </summary>
 internal static string Escape(string unescaped)
 {
     return(EscapingUtilities.Escape(unescaped));
 }
        /// <summary>
        /// Get the value of any metadata in the item that has the specified
        /// name, otherwise returns null
        /// </summary>
        public string GetMetadataValue(string name)
        {
            string escapedValue = (this as IMetadataTable).GetEscapedValue(name);

            return((escapedValue == null) ? null : EscapingUtilities.UnescapeAll(escapedValue));
        }
        /// <summary>
        /// Main task method
        /// </summary>
        /// <returns></returns>
        public override bool Execute()
        {
            try
            {
                if (!VerifyProjectReferenceItems(ProjectReferences, true /* treat problems as errors */))
                {
                    return(false);
                }

                ArrayList resolvedReferences   = new ArrayList(ProjectReferences.GetLength(0));
                ArrayList unresolvedReferences = new ArrayList(ProjectReferences.GetLength(0));

                if (!String.IsNullOrEmpty(SolutionConfigurationContents))
                {
                    CacheProjectElementsFromXml(SolutionConfigurationContents);
                }

                if (AddSyntheticProjectReferencesForSolutionDependencies)
                {
                    // The solution may have had project to project dependencies expressed in it, which were passed in with the blob.
                    // Add those to the list of project references as if they were regular project references.
                    AddSyntheticProjectReferences(CurrentProject);
                }

                foreach (ITaskItem projectRef in ProjectReferences)
                {
                    bool      resolveSuccess = false;
                    ITaskItem resolvedReference;

                    resolveSuccess = ResolveProject(projectRef, out resolvedReference);

                    if (resolveSuccess)
                    {
                        resolvedReferences.Add(resolvedReference);

                        Log.LogMessageFromResources(MessageImportance.Low, "AssignProjectConfiguration.ProjectConfigurationResolutionSuccess", projectRef.ItemSpec, resolvedReference.GetMetadata(attrFullConfiguration));
                    }
                    else
                    {
                        // If the reference was unresolved, we want to undefine the Configuration and Platform
                        // global properties, so that the project will build using its default Configuration and
                        // Platform rather than that of its parent.
                        if (ShouldUnsetParentConfigurationAndPlatform)
                        {
                            string globalPropertiesToRemove = projectRef.GetMetadata("GlobalPropertiesToRemove");

                            if (!String.IsNullOrEmpty(globalPropertiesToRemove))
                            {
                                globalPropertiesToRemove += ";";
                            }

                            if (projectRef is ITaskItem2)
                            {
                                ((ITaskItem2)projectRef).SetMetadataValueLiteral("GlobalPropertiesToRemove", globalPropertiesToRemove + "Configuration;Platform");
                            }
                            else
                            {
                                projectRef.SetMetadata("GlobalPropertiesToRemove", EscapingUtilities.Escape(globalPropertiesToRemove + "Configuration;Platform"));
                            }
                        }

                        unresolvedReferences.Add(projectRef);

                        // This is not an error - we pass unresolved references to UnresolvedProjectReferences for further
                        // processing in the .targets file. This means this project was not checked for building in the
                        // active solution configuration.
                        Log.LogMessageFromResources(MessageImportance.Low, "AssignProjectConfiguration.ProjectConfigurationUnresolved", projectRef.ItemSpec);
                    }
                }

                AssignedProjects   = (ITaskItem[])resolvedReferences.ToArray(typeof(ITaskItem));
                UnassignedProjects = (ITaskItem[])unresolvedReferences.ToArray(typeof(ITaskItem));
            }
            catch (XmlException e)
            {
                Log.LogErrorWithCodeFromResources("General.ErrorExecutingTask", this.GetType().Name, e.Message);
                return(false);
            }

            return(true);
        }
        /// <summary>
        /// Initializes a persisted target from an existing &lt;Target&gt; element which exists either in the main parent project
        /// file or one of the imported files.
        /// </summary>
        /// <param name="targetElement"></param>
        /// <param name="project"></param>
        /// <param name="importedFromAnotherProject"></param>
        internal Target
        (
            XmlElement targetElement,
            Project project,
            bool importedFromAnotherProject
        )
        {
            // Make sure a valid node has been given to us.
            error.VerifyThrow(targetElement != null, "Need a valid XML node.");

            // Make sure this really is the <target> node.
            ProjectXmlUtilities.VerifyThrowElementName(targetElement, XMakeElements.target);

            this.targetElement              = targetElement;
            this.parentProject              = project;
            this.parentEngine               = project.ParentEngine;
            this.conditionAttribute         = null;
            this.taskElementList            = null;
            this.importedFromAnotherProject = importedFromAnotherProject;
            this.buildState = BuildState.NotStarted;
            this.id         = project.ParentEngine.GetNextTargetId();

            // The target name and target dependendencies (dependencies on other
            // targets) are specified as attributes of the <target> element.

            XmlAttribute returnsAttribute = null;

            // Loop through all the attributes on the <target> element.
            foreach (XmlAttribute targetAttribute in targetElement.Attributes)
            {
                switch (targetAttribute.Name)
                {
                // Process the "condition" attribute.
                case XMakeAttributes.condition:
                    this.conditionAttribute = targetAttribute;
                    break;

                // Process the "name" attribute.
                case XMakeAttributes.name:
                    this.targetName = EscapingUtilities.UnescapeAll(targetAttribute.Value);

                    // Target names cannot contain MSBuild special characters, embedded properties,
                    // or item lists.
                    int indexOfSpecialCharacter = this.targetName.IndexOfAny(XMakeElements.illegalTargetNameCharacters);
                    if (indexOfSpecialCharacter >= 0)
                    {
                        ProjectErrorUtilities.VerifyThrowInvalidProject(false,
                                                                        targetAttribute, "NameInvalid", targetName, targetName[indexOfSpecialCharacter]);
                    }

                    break;

                // Process the "dependsOnTargets" attribute.
                case XMakeAttributes.dependsOnTargets:
                    this.dependsOnTargetsAttribute = targetAttribute;
                    break;

                case XMakeAttributes.inputs:
                    this.inputsAttribute           = targetAttribute;
                    recalculateBatchableParameters = true;
                    break;

                case XMakeAttributes.outputs:
                    this.outputsAttribute          = targetAttribute;
                    recalculateBatchableParameters = true;
                    break;

                // This is only recognized by the new OM:
                // so that the compat tests keep passing,
                // ignore it.
                case XMakeAttributes.keepDuplicateOutputs:
                    break;

                // This is only recognized by the new OM:
                // so that the compat tests keep passing,
                // ignore it.
                case XMakeAttributes.returns:
                    returnsAttribute = targetAttribute;
                    break;

                // These are only recognized by the new OM:
                // while the solution wrapper generator is using
                // the old OM to parse projects for dependencies,
                // we must make sure to not fail for these
                case XMakeAttributes.beforeTargets:
                case XMakeAttributes.afterTargets:
                    break;

                default:
                    ProjectXmlUtilities.ThrowProjectInvalidAttribute(targetAttribute);
                    break;
                }
            }

            // Hack to help the 3.5 engine at least pretend to still be able to build on top of
            // the 4.0 targets.  In cases where there is no Outputs attribute, just a Returns attribute,
            // we can approximate the correct behaviour by making the Returns attribute our "outputs" attribute.
            if (this.outputsAttribute == null && returnsAttribute != null)
            {
                this.outputsAttribute          = returnsAttribute;
                recalculateBatchableParameters = true;
            }

            // It's considered an error if a target does not have a name.
            ProjectErrorUtilities.VerifyThrowInvalidProject(!string.IsNullOrEmpty(targetName),
                                                            targetElement, "MissingRequiredAttribute", XMakeAttributes.name, XMakeElements.target);

            this.taskElementList = new ArrayList();

            // Process each of the child nodes beneath the <Target>.
            XmlElement        anyOnErrorElement = null;
            List <XmlElement> childElements     = ProjectXmlUtilities.GetValidChildElements(targetElement);

            foreach (XmlElement childElement in childElements)
            {
                bool onErrorOutOfOrder = false;
                switch (childElement.Name)
                {
                case XMakeElements.onError:
                    anyOnErrorElement = childElement;
                    break;

                default:
                    onErrorOutOfOrder = (anyOnErrorElement != null);
                    this.taskElementList.Add(new BuildTask(childElement,
                                                           this, this.importedFromAnotherProject));
                    break;
                }

                // Check for out-of-order OnError
                ProjectErrorUtilities.VerifyThrowInvalidProject(!onErrorOutOfOrder,
                                                                anyOnErrorElement, "NodeMustBeLastUnderElement", XMakeElements.onError, XMakeElements.target, childElement.Name);
            }
        }
Beispiel #10
0
        private static bool IsValidExclude(string exclude)
        {
            // TODO: assumption on legal path characters: https://github.com/Microsoft/msbuild/issues/781
            // Excludes that have both wildcards and non escaped wildcards will never be matched on Windows, because
            // wildcard characters are invalid in Windows paths.
            // Filtering these excludes early keeps the glob expander simpler. Otherwise unescaping logic would reach all the way down to
            // filespec parsing (parse escaped string (to correctly ignore escaped wildcards) and then
            // unescape the path fragments to unfold potentially escaped wildcard chars)
            var hasBothWildcardsAndEscapedWildcards = FileMatcher.HasWildcards(exclude) && EscapingUtilities.ContainsEscapedWildcards(exclude);

            return(!hasBothWildcardsAndEscapedWildcards);
        }
Beispiel #11
0
        /// <summary>
        /// Get a cached build result if available for the given request. This method is thread safe.
        /// </summary>
        /// <param name="buildRequest"></param>
        /// <param name="actuallyBuiltTargets"></param>
        /// <returns></returns>
        internal BuildResult GetCachedBuildResult(BuildRequest buildRequest, out ArrayList actuallyBuiltTargets)
        {
            actuallyBuiltTargets = null;

            PropertyCacheEntry defaultTargetsCacheEntry, initialTargetsCacheEntry, projectIdCacheEntry;

            // No writes here, but since we're reading multiple values we want to get a consistent view of the cache
            cacheScopeReaderWriterLock.AcquireReaderLock(Timeout.Infinite);

            try
            {
                defaultTargetsCacheEntry = (PropertyCacheEntry)GetCacheEntry(Constants.defaultTargetCacheName);
                initialTargetsCacheEntry = (PropertyCacheEntry)GetCacheEntry(Constants.initialTargetCacheName);
                projectIdCacheEntry      = (PropertyCacheEntry)GetCacheEntry(Constants.projectIdCacheName);
            }
            finally
            {
                cacheScopeReaderWriterLock.ReleaseReaderLock();
            }

            // If we ever built anything in this project we must have the default and initial targets.
            if (defaultTargetsCacheEntry == null && initialTargetsCacheEntry == null)
            {
                return(null);
            }

            ErrorUtilities.VerifyThrow(projectIdCacheEntry != null, "We should always have the projectId cache entry");

            ErrorUtilities.VerifyThrow(defaultTargetsCacheEntry != null && initialTargetsCacheEntry != null,
                                       "We should have both the initial and default targets in the cache");

            ArrayList targetsToBuild = new ArrayList(initialTargetsCacheEntry.Value.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries));

            if (buildRequest.TargetNames == null || buildRequest.TargetNames.Length == 0)
            {
                targetsToBuild.AddRange(defaultTargetsCacheEntry.Value.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries));
            }
            else
            {
                targetsToBuild.AddRange(buildRequest.TargetNames);
            }

            // Create variable to hold the cached outputs
            Hashtable outputsByTargetName = new Hashtable(targetsToBuild.Count);
            Hashtable resultByTarget      = new Hashtable(targetsToBuild.Count, StringComparer.OrdinalIgnoreCase);

            bool overallSuccess = true;
            bool missingValues  = false;

            // No writes here, but since we're reading multiple values we want to get a consistent view of the cache
            cacheScopeReaderWriterLock.AcquireReaderLock(Timeout.Infinite);

            try
            {
                for (int i = 0; i < targetsToBuild.Count; i++)
                {
                    string targetName = EscapingUtilities.UnescapeAll((string)targetsToBuild[i]);
                    if (ContainsCacheEntry(targetName))
                    {
                        BuildResultCacheEntry cacheEntry = (BuildResultCacheEntry)GetCacheEntry(targetName);
                        overallSuccess             = overallSuccess && cacheEntry.BuildResult;
                        resultByTarget[targetName] = (cacheEntry.BuildResult) ?
                                                     Target.BuildState.CompletedSuccessfully : Target.BuildState.CompletedUnsuccessfully;

                        // Restore output items for successful targets
                        if (cacheEntry.BuildResult)
                        {
                            outputsByTargetName[targetName] = cacheEntry.BuildItems;
                        }
                        // We found a failed target - cut the loop short
                        else
                        {
                            break;
                        }
                    }
                    else
                    {
                        missingValues = true;
                        break;
                    }
                }
            }
            finally
            {
                cacheScopeReaderWriterLock.ReleaseReaderLock();
            }

            if (missingValues)
            {
                return(null);
            }

            actuallyBuiltTargets = targetsToBuild;

            return(new BuildResult(outputsByTargetName, resultByTarget, overallSuccess, buildRequest.HandleId, buildRequest.RequestId,
                                   int.Parse(projectIdCacheEntry.Value, CultureInfo.InvariantCulture), false /* use results cache */,
                                   defaultTargetsCacheEntry.Value, initialTargetsCacheEntry.Value, 0, 0, 0));
        }
Beispiel #12
0
        /// <summary>
        /// Used for the purposes of evaluating an item specification. Given a filespec that may include wildcard characters * and
        /// ?, we translate it into an actual list of files. If the input filespec doesn't contain any wildcard characters, and it
        /// doesn't appear to point to an actual file on disk, then we just give back the input string as an array of length one,
        /// assuming that it wasn't really intended to be a filename (as items are not required to necessarily represent files).
        /// Any wildcards passed in that are unescaped will be treated as real wildcards.
        /// The "include" of items passed back from the filesystem will be returned canonically escaped.
        /// The ordering of the list returned is deterministic (it is sorted).
        /// Will never throw IO exceptions: if there is no match, returns the input verbatim.
        /// </summary>
        /// <param name="directoryEscaped">The directory to evaluate, escaped.</param>
        /// <param name="filespecEscaped">The filespec to evaluate, escaped.</param>
        /// <param name="returnEscaped"><code>true</code> to return escaped specs.</param>
        /// <param name="forceEvaluateWildCards">Whether to force file glob expansion when eager expansion is turned off</param>
        /// <param name="excludeSpecsEscaped">The exclude specification, escaped.</param>
        /// <param name="fileMatcher"></param>
        /// <returns>Array of file paths.</returns>
        private static string[] GetFileList
        (
            string directoryEscaped,
            string filespecEscaped,
            bool returnEscaped,
            bool forceEvaluateWildCards,
            IEnumerable <string> excludeSpecsEscaped,
            FileMatcher fileMatcher
        )
        {
            ErrorUtilities.VerifyThrowInternalLength(filespecEscaped, nameof(filespecEscaped));

            string[] fileList;

            if (!FilespecHasWildcards(filespecEscaped) ||
                FilespecMatchesLazyWildcard(filespecEscaped, forceEvaluateWildCards))
            {
                // Just return the original string.
                fileList = new string[] { returnEscaped?filespecEscaped : EscapingUtilities.UnescapeAll(filespecEscaped) };
            }
            else
            {
                if (Traits.Instance.LogExpandedWildcards)
                {
                    ErrorUtilities.DebugTraceMessage("Expanding wildcard for file spec {0}", filespecEscaped);
                }

                // Unescape before handing it to the filesystem.
                var directoryUnescaped    = EscapingUtilities.UnescapeAll(directoryEscaped);
                var filespecUnescaped     = EscapingUtilities.UnescapeAll(filespecEscaped);
                var excludeSpecsUnescaped = excludeSpecsEscaped?.Where(IsValidExclude).Select(i => EscapingUtilities.UnescapeAll(i)).ToList();

                // Get the list of actual files which match the filespec.  Put
                // the list into a string array.  If the filespec started out
                // as a relative path, we will get back a bunch of relative paths.
                // If the filespec started out as an absolute path, we will get
                // back a bunch of absolute paths.
                fileList = fileMatcher.GetFiles(directoryUnescaped, filespecUnescaped, excludeSpecsUnescaped);

                ErrorUtilities.VerifyThrow(fileList != null, "We must have a list of files here, even if it's empty.");

                // Before actually returning the file list, we sort them alphabetically.  This
                // provides a certain amount of extra determinism and reproducability.  That is,
                // we're sure that the build will behave in exactly the same way every time,
                // and on every machine.
                Array.Sort(fileList, StringComparer.OrdinalIgnoreCase);

                if (returnEscaped)
                {
                    // We must now go back and make sure all special characters are escaped because we always
                    // store data in the engine in escaped form so it doesn't interfere with our parsing.
                    // Note that this means that characters that were not escaped in the original filespec
                    // may now be escaped, but that's not easy to avoid.
                    for (int i = 0; i < fileList.Length; i++)
                    {
                        fileList[i] = EscapingUtilities.Escape(fileList[i]);
                    }
                }
            }

            return(fileList);
        }
Beispiel #13
0
 /// <summary>
 /// Get the evaluated value of a metadata on this item, possibly from an item definition.
 /// Returns empty string if it does not exist.
 /// To determine whether a piece of metadata does not exist vs. simply has no value, use <see cref="HasMetadata(string)">HasMetadata</see>.
 /// May be used to access the value of built-in metadata, such as "FullPath".
 /// Attempting to get built-in metadata on a value that is not a valid path throws InvalidOperationException.
 /// </summary>
 public string GetMetadataValue(string name)
 {
     return(EscapingUtilities.UnescapeAll(((IItem)this).GetMetadataValueEscaped(name)));
 }
Beispiel #14
0
        /// <summary>
        /// Used for the purposes of evaluating an item specification. Given a filespec that may include wildcard characters * and
        /// ?, we translate it into an actual list of files. If the input filespec doesn't contain any wildcard characters, and it
        /// doesn't appear to point to an actual file on disk, then we just give back the input string as an array of length one,
        /// assuming that it wasn't really intended to be a filename (as items are not required to necessarily represent files).
        /// Any wildcards passed in that are unescaped will be treated as real wildcards.
        /// The "include" of items passed back from the filesystem will be returned canonically escaped.
        /// The ordering of the list returned is deterministic (it is sorted).
        /// Will never throw IO exceptions: if there is no match, returns the input verbatim.
        /// </summary>
        /// <param name="directoryEscaped">The directory to evaluate, escaped.</param>
        /// <param name="filespecEscaped">The filespec to evaluate, escaped.</param>
        /// <returns>Array of file paths.</returns>
        private static string[] GetFileList
        (
            string directoryEscaped,
            string filespecEscaped,
            bool returnEscaped
        )
        {
            ErrorUtilities.VerifyThrowInternalLength(filespecEscaped, "filespecEscaped");

            string[] fileList;

            bool containsEscapedWildcards = EscapingUtilities.ContainsEscapedWildcards(filespecEscaped);
            bool containsRealWildcards    = FileMatcher.HasWildcards(filespecEscaped);

            if (containsEscapedWildcards && containsRealWildcards)
            {
                // Umm, this makes no sense.  The item's Include has both escaped wildcards and
                // real wildcards.  What does he want us to do?  Go to the file system and find
                // files that literally have '*' in their filename?  Well, that's not going to
                // happen because '*' is an illegal character to have in a filename.

                // Just return the original string.
                fileList = new string[] { returnEscaped?filespecEscaped : EscapingUtilities.UnescapeAll(filespecEscaped) };
            }
            else if (!containsEscapedWildcards && containsRealWildcards)
            {
                // Unescape before handing it to the filesystem.
                string directoryUnescaped = EscapingUtilities.UnescapeAll(directoryEscaped);
                string filespecUnescaped  = EscapingUtilities.UnescapeAll(filespecEscaped);

                // Get the list of actual files which match the filespec.  Put
                // the list into a string array.  If the filespec started out
                // as a relative path, we will get back a bunch of relative paths.
                // If the filespec started out as an absolute path, we will get
                // back a bunch of absolute paths.
                fileList = FileMatcher.GetFiles(directoryUnescaped, filespecUnescaped);

                ErrorUtilities.VerifyThrow(fileList != null, "We must have a list of files here, even if it's empty.");

                // Before actually returning the file list, we sort them alphabetically.  This
                // provides a certain amount of extra determinism and reproducability.  That is,
                // we're sure that the build will behave in exactly the same way every time,
                // and on every machine.
                Array.Sort(fileList, StringComparer.OrdinalIgnoreCase);

                if (returnEscaped)
                {
                    // We must now go back and make sure all special characters are escaped because we always
                    // store data in the engine in escaped form so it doesn't interfere with our parsing.
                    // Note that this means that characters that were not escaped in the original filespec
                    // may now be escaped, but that's not easy to avoid.
                    for (int i = 0; i < fileList.Length; i++)
                    {
                        fileList[i] = EscapingUtilities.Escape(fileList[i]);
                    }
                }
            }
            else
            {
                // No real wildcards means we just return the original string.  Don't even bother
                // escaping ... it should already be escaped appropriately since it came directly
                // from the project file or the OM host.
                fileList = new string[] { returnEscaped?filespecEscaped : EscapingUtilities.UnescapeAll(filespecEscaped) };
            }

            return(fileList);
        }
Beispiel #15
0
        /// <summary>
        /// Execute the task.
        /// </summary>
        /// <returns></returns>
        public override bool Execute()
        {
            _assignedFiles = new TaskItem[Files.Length];

            if (Files.Length > 0)
            {
                // Compose a file in the root folder.
                // NOTE: at this point fullRootPath may or may not have a trailing
                // slash because Path.GetFullPath() does not add or remove it
                string fullRootPath = Path.GetFullPath(this.RootFolder);

                // Ensure trailing slash otherwise c:\bin appears to match part of c:\bin2\foo
                fullRootPath = FileUtilities.EnsureTrailingSlash(fullRootPath);

                string currentDirectory = Directory.GetCurrentDirectory();

                // check if the root folder is the same as the current directory
                // NOTE: the path returned from Directory.GetCurrentDirectory()
                // does not have a trailing slash, but fullRootPath does
                bool isRootFolderSameAsCurrentDirectory =
                    ((fullRootPath.Length - 1 /* exclude trailing slash */) == currentDirectory.Length)
                    &&
                    (String.Compare(
                         fullRootPath, 0,
                         currentDirectory, 0,
                         (fullRootPath.Length - 1) /* don't compare trailing slash */,
                         StringComparison.OrdinalIgnoreCase) == 0);

                for (int i = 0; i < Files.Length; ++i)
                {
                    string link = Files[i].GetMetadata(ItemMetadataNames.link);
                    AssignedFiles[i] = new TaskItem(Files[i]);

                    // If file has a link, use that.
                    string targetPath = link;

                    if (link == null || link.Length == 0)
                    {
                        if (// if the file path is relative
                            !Path.IsPathRooted(Files[i].ItemSpec) &&
                            // if the file path doesn't contain any relative specifiers
                            !Files[i].ItemSpec.Contains("." + Path.DirectorySeparatorChar) &&
                            // if the file path is already relative to the root folder
                            isRootFolderSameAsCurrentDirectory)
                        {
                            // then just use the file path as-is
                            // PERF NOTE: we do this to avoid calling Path.GetFullPath() below,
                            // because that method consumes a lot of memory, esp. when we have
                            // a lot of items coming through this task
                            targetPath = Files[i].ItemSpec;
                        }
                        else
                        {
                            // PERF WARNING: Path.GetFullPath() is expensive in terms of memory;
                            // we should avoid calling it whenever possible
                            string itemSpecFullFileNamePath = Path.GetFullPath(Files[i].ItemSpec);

                            if (String.Compare(fullRootPath, 0, itemSpecFullFileNamePath, 0, fullRootPath.Length, StringComparison.CurrentCultureIgnoreCase) == 0)
                            {
                                // The item spec file is in the "cone" of the RootFolder. Return the relative path from the cone root.
                                targetPath = itemSpecFullFileNamePath.Substring(fullRootPath.Length);
                            }
                            else
                            {
                                // The item spec file is not in the "cone" of the RootFolder. Return the filename only.
                                targetPath = Path.GetFileName(Files[i].ItemSpec);
                            }
                        }
                    }

                    AssignedFiles[i].SetMetadata(ItemMetadataNames.targetPath, EscapingUtilities.Escape(targetPath));
                }
            }

            return(true);
        }
        /// <summary>
        /// Builds this target if it has not already been built as part of its parent project. Before we actually execute the
        /// tasks for this target, though, we first call on all the dependent targets to build themselves.
        /// This function may throw InvalidProjectFileException
        /// </summary>
        internal void Build
        (
            ProjectBuildState buildContext
        )
        {
            // Depending on the build state, we may do different things.
            switch (buildState)
            {
            case BuildState.InProgress:
                // In single proc mode if the build state was already "in progress"
                // and somebody just told us to build ourselves, it means that there is
                // a loop (circular dependency) in the target dependency graph. In multi
                // proc mode we need to analyze the dependency graph before we can
                // tell if there a circular dependency or if two independent chains
                // of targets happen to need the result of this target.
                if (parentEngine.Router.SingleThreadedMode || buildContext.ContainsCycle(this.Name))
                {
                    ProjectErrorUtilities.VerifyThrowInvalidProject(false, TargetElement, "CircularDependency", targetName);
                }
                else
                {
                    buildContext.CurrentBuildContextState = ProjectBuildState.BuildContextState.WaitingForTarget;
                    this.executionState.AddWaitingBuildContext(buildContext);
                }
                break;

            case BuildState.CompletedSuccessfully:
            case BuildState.CompletedUnsuccessfully:
                // If this target has already been built as part of this project,
                // we're not going to build it again.  Just return the result
                // from when it was built previously.  Note:  This condition
                // could really only ever hold true if the user specifically
                // requested us to build multiple targets and there existed
                // a direct or indirect dependency relationship between two or
                // more of those top-level targets.
                // Note: we aren't really entering the target in question here, so don't use the target
                // event context. Using the target ID for skipped messages would force us to
                // cache the individual target IDs for unloaded projects and it's not really worth the trouble.
                // Just use the parent event context.
                parentEngine.LoggingServices.LogComment(buildContext.ProjectBuildEventContext,
                                                        (buildState == BuildState.CompletedSuccessfully) ? "TargetAlreadyCompleteSuccess" : "TargetAlreadyCompleteFailure",
                                                        this.targetName);

                // Only contexts which are generated from an MSBuild task could need
                // the outputs of this target, such contexts have a non-null evaluation
                // request
                if ((buildState == BuildState.CompletedSuccessfully) &&
                    (buildContext.BuildRequest.OutputsByTarget != null &&
                     buildContext.NameOfBlockingTarget == null))
                {
                    error.VerifyThrow(
                        String.Equals(EscapingUtilities.UnescapeAll(buildContext.NameOfTargetInProgress), this.Name, StringComparison.OrdinalIgnoreCase),
                        "The name of the target in progress is inconsistent with the target being built");

                    error.VerifyThrow(targetOutputItems != null,
                                      "If the target built successfully, we must have its outputs.");

                    buildContext.BuildRequest.OutputsByTarget[Name] = targetOutputItems.ToArray();
                }

                if (buildContext.NameOfBlockingTarget == null)
                {
                    buildContext.BuildRequest.ResultByTarget[Name] = buildState;
                }
                break;

            case BuildState.NotStarted:
            case BuildState.Skipped:
            {
                // Always have to create a new context in build as other projects or targets may try and build this target
                BuildEventContext targetBuildEventContext = new BuildEventContext
                                                            (
                    buildContext.ProjectBuildEventContext.NodeId,
                    this.id,
                    buildContext.ProjectBuildEventContext.ProjectContextId,
                    buildContext.ProjectBuildEventContext.TaskId
                                                            );

                Expander expander = new Expander(this.parentProject.evaluatedProperties, this.parentProject.evaluatedItemsByName);

                // We first make sure no batching was attempted with the target's condition.
                if (!conditionCheckedForInvalidMetadataReferences)
                {
                    if (ExpressionShredder.ContainsMetadataExpressionOutsideTransform(this.Condition))
                    {
                        ProjectErrorUtilities.ThrowInvalidProject(this.conditionAttribute, "TargetConditionHasInvalidMetadataReference", targetName, this.Condition);
                    }
                    conditionCheckedForInvalidMetadataReferences = true;
                }

                // If condition is false (based on propertyBag), set this target's state to
                // "Skipped" since we won't actually build it.
                if (!Utilities.EvaluateCondition(this.Condition, this.conditionAttribute,
                                                 expander, null, ParserOptions.AllowProperties | ParserOptions.AllowItemLists,
                                                 parentEngine.LoggingServices, targetBuildEventContext))
                {
                    buildState = BuildState.Skipped;

                    if (buildContext.NameOfBlockingTarget == null)
                    {
                        buildContext.BuildRequest.ResultByTarget[Name] = buildState;
                    }

                    if (!parentEngine.LoggingServices.OnlyLogCriticalEvents)
                    {
                        // Expand the expression for the Log.
                        string expanded = expander.ExpandAllIntoString(this.Condition, this.conditionAttribute);
                        // By design: Not building dependencies. This is what NAnt does too.
                        parentEngine.LoggingServices.LogComment(targetBuildEventContext, "TargetSkippedFalseCondition",
                                                                this.targetName, this.Condition, expanded);
                    }
                }
                else
                {
                    // This target has not been built yet.  So build it!
                    // Change our state to "in progress". TargetParameters will need to be re-calculated if Inputs and Outputs attribute has changed.
                    buildState = BuildState.InProgress;
                    List <string> batchableTargetParameters = GetBatchableTargetParameters();
                    executionState = new TargetExecutionWrapper(this, taskElementList, batchableTargetParameters, targetElement, expander, targetBuildEventContext);
                    ContinueBuild(buildContext, null);
                }
            }
            break;

            default:
                error.VerifyThrow(false, "Build state {0} not handled in Target.Build method", buildState);
                break;
            }
        }
Beispiel #17
0
 /// <summary>
 /// Adds a new item to the ItemGroup, optional treating the item Include as literal so that
 /// any special characters will be escaped before persisting it.
 /// </summary>
 public BuildItem AddNewItem(string itemName, string itemInclude, bool treatItemIncludeAsLiteral)
 {
     return(AddNewItem(itemName, treatItemIncludeAsLiteral ? EscapingUtilities.Escape(itemInclude) : itemInclude));
 }
Beispiel #18
0
            protected override ImmutableList <I> SelectItems(OrderedItemDataCollection.Builder listBuilder, ImmutableHashSet <string> globsToIgnore)
            {
                var itemsToAdd = ImmutableList.CreateBuilder <I>();

                Lazy <Func <string, bool> > excludeTester = null;

                ImmutableList <string> .Builder excludePatterns = ImmutableList.CreateBuilder <string>();
                if (_excludes != null)
                {
                    // STEP 4: Evaluate, split, expand and subtract any Exclude
                    foreach (string exclude in _excludes)
                    {
                        string excludeExpanded = _expander.ExpandIntoStringLeaveEscaped(exclude, ExpanderOptions.ExpandPropertiesAndItems, _itemElement.ExcludeLocation);
                        var    excludeSplits   = ExpressionShredder.SplitSemiColonSeparatedList(excludeExpanded);
                        excludePatterns.AddRange(excludeSplits);
                    }

                    if (excludePatterns.Count > 0)
                    {
                        excludeTester = new Lazy <Func <string, bool> >(() => EngineFileUtilities.GetFileSpecMatchTester(excludePatterns, _rootDirectory));
                    }
                }

                ISet <string> excludePatternsForGlobs = null;

                foreach (var fragment in _itemSpec.Fragments)
                {
                    if (fragment is ItemSpec <P, I> .ItemExpressionFragment itemReferenceFragment)
                    {
                        // STEP 3: If expression is "@(x)" copy specified list with its metadata, otherwise just treat as string
                        var itemsFromExpression = _expander.ExpandExpressionCaptureIntoItems(
                            itemReferenceFragment.Capture,
                            _evaluatorData,
                            _itemFactory,
                            ExpanderOptions.ExpandItems,
                            includeNullEntries: false,
                            isTransformExpression: out _,
                            elementLocation: _itemElement.IncludeLocation);

                        itemsToAdd.AddRange(
                            excludeTester != null
                                ? itemsFromExpression.Where(item => !excludeTester.Value(item.EvaluatedInclude))
                                : itemsFromExpression);
                    }
                    else if (fragment is ValueFragment valueFragment)
                    {
                        string value = valueFragment.TextFragment;

                        if (excludeTester?.Value(EscapingUtilities.UnescapeAll(value)) != true)
                        {
                            var item = _itemFactory.CreateItem(value, value, _itemElement.ContainingProject.FullPath);
                            itemsToAdd.Add(item);
                        }
                    }
                    else if (fragment is GlobFragment globFragment)
                    {
                        // If this item is behind a false condition and represents a full drive/filesystem scan, expanding it is
                        // almost certainly undesired. It should be skipped to avoid evaluation taking an excessive amount of time.
                        bool skipGlob = !_conditionResult && globFragment.IsFullFileSystemScan && !Traits.Instance.EscapeHatches.AlwaysEvaluateDangerousGlobs;
                        if (!skipGlob)
                        {
                            string glob = globFragment.TextFragment;

                            if (excludePatternsForGlobs == null)
                            {
                                excludePatternsForGlobs = BuildExcludePatternsForGlobs(globsToIgnore, excludePatterns);
                            }

                            string[] includeSplitFilesEscaped;
                            if (MSBuildEventSource.Log.IsEnabled())
                            {
                                MSBuildEventSource.Log.ExpandGlobStart(_rootDirectory, glob, string.Join(", ", excludePatternsForGlobs));
                            }

                            using (_lazyEvaluator._evaluationProfiler.TrackGlob(_rootDirectory, glob, excludePatternsForGlobs))
                            {
                                includeSplitFilesEscaped = EngineFileUtilities.GetFileListEscaped(
                                    _rootDirectory,
                                    glob,
                                    excludePatternsForGlobs,
                                    fileMatcher: FileMatcher,
                                    loggingMechanism: _lazyEvaluator._loggingContext,
                                    includeLocation: _itemElement.IncludeLocation,
                                    excludeLocation: _itemElement.ExcludeLocation);
                            }

                            if (MSBuildEventSource.Log.IsEnabled())
                            {
                                MSBuildEventSource.Log.ExpandGlobStop(_rootDirectory, glob, string.Join(", ", excludePatternsForGlobs));
                            }

                            foreach (string includeSplitFileEscaped in includeSplitFilesEscaped)
                            {
                                itemsToAdd.Add(_itemFactory.CreateItem(includeSplitFileEscaped, glob, _itemElement.ContainingProject.FullPath));
                            }
                        }
                    }
                    else
                    {
                        throw new InvalidOperationException(fragment.GetType().ToString());
                    }
                }

                return(itemsToAdd.ToImmutable());
            }
Beispiel #19
0
 /// <summary>
 /// Unescape the string according to MSBuild's escaping rules
 /// </summary>
 internal static string Unescape(string escaped)
 {
     return(EscapingUtilities.UnescapeAll(escaped));
 }
Beispiel #20
0
 public string GetMetadata(string metadataName)
 {
     return(EscapingUtilities.UnescapeAll(((ITaskItem2)this).GetMetadataValueEscaped(metadataName)));
 }
Beispiel #21
0
        private List <ItemSpecFragment> BuildItemFragments(IElementLocation itemSpecLocation, string projectDirectory, bool expandProperties)
        {
            //  Code corresponds to Evaluator.CreateItemsFromInclude
            var evaluatedItemspecEscaped = ItemSpecString;

            if (string.IsNullOrEmpty(evaluatedItemspecEscaped))
            {
                return(new List <ItemSpecFragment>());
            }

            // STEP 1: Expand properties in Include
            if (expandProperties)
            {
                evaluatedItemspecEscaped = Expander.ExpandIntoStringLeaveEscaped(
                    ItemSpecString,
                    ExpanderOptions.ExpandProperties,
                    itemSpecLocation);
            }

            var semicolonCount = 0;

            foreach (var c in evaluatedItemspecEscaped)
            {
                if (c == ';')
                {
                    semicolonCount++;
                }
            }

            // estimate the number of fragments with the number of semicolons. This is will overestimate in case of transforms with semicolons, but won't underestimate.
            var fragments = new List <ItemSpecFragment>(semicolonCount + 1);

            // STEP 2: Split Include on any semicolons, and take each split in turn
            if (evaluatedItemspecEscaped.Length > 0)
            {
                var splitsEscaped = ExpressionShredder.SplitSemiColonSeparatedList(evaluatedItemspecEscaped);

                foreach (var splitEscaped in splitsEscaped)
                {
                    // STEP 3: If expression is "@(x)" copy specified list with its metadata, otherwise just treat as string
                    var itemReferenceFragment = ProcessItemExpression(
                        splitEscaped,
                        itemSpecLocation,
                        projectDirectory,
                        out var isItemListExpression);

                    if (isItemListExpression)
                    {
                        fragments.Add(itemReferenceFragment);
                    }
                    else
                    {
                        // The expression is not of the form "@(X)". Treat as string

                        //  Code corresponds to EngineFileUtilities.GetFileList
                        if (!FileMatcher.HasWildcards(splitEscaped))
                        {
                            // No real wildcards means we just return the original string.  Don't even bother
                            // escaping ... it should already be escaped appropriately since it came directly
                            // from the project file

                            fragments.Add(new ValueFragment(splitEscaped, projectDirectory));
                        }
                        else if (EscapingUtilities.ContainsEscapedWildcards(splitEscaped))
                        {
                            // '*' is an illegal character to have in a filename.
                            // todo: file-system assumption on legal path characters: https://github.com/Microsoft/msbuild/issues/781
                            // Just return the original string.
                            fragments.Add(new ValueFragment(splitEscaped, projectDirectory));
                        }
                        else
                        {
                            // Unescape before handing it to the filesystem.
                            var filespecUnescaped = EscapingUtilities.UnescapeAll(splitEscaped);

                            fragments.Add(new GlobFragment(filespecUnescaped, projectDirectory));
                        }
                    }
                }
            }

            return(fragments);
        }
Beispiel #22
0
        /// <summary>
        /// Parse a ProjectTargetElement
        /// </summary>
        private ProjectTargetElement ParseProjectTargetElement(XmlElementWithLocation element)
        {
            ProjectXmlUtilities.VerifyThrowProjectAttributes(element, s_validAttributesOnTarget);
            ProjectXmlUtilities.VerifyThrowProjectRequiredAttribute(element, XMakeAttributes.name);

            string targetName = ProjectXmlUtilities.GetAttributeValue(element, XMakeAttributes.name);

            // Orcas compat: all target names are automatically unescaped
            targetName = EscapingUtilities.UnescapeAll(targetName);

            int indexOfSpecialCharacter = targetName.IndexOfAny(XMakeElements.illegalTargetNameCharacters);

            if (indexOfSpecialCharacter >= 0)
            {
                ProjectErrorUtilities.ThrowInvalidProject(element.GetAttributeLocation(XMakeAttributes.name), "NameInvalid", targetName, targetName[indexOfSpecialCharacter]);
            }

            ProjectTargetElement  target  = new ProjectTargetElement(element, _project, _project);
            ProjectOnErrorElement onError = null;

            foreach (XmlElementWithLocation childElement in ProjectXmlUtilities.GetVerifyThrowProjectChildElements(element))
            {
                ProjectElement child = null;

                switch (childElement.Name)
                {
                case XMakeElements.propertyGroup:
                    if (onError != null)
                    {
                        ProjectErrorUtilities.ThrowInvalidProject(onError.Location, "NodeMustBeLastUnderElement", XMakeElements.onError, XMakeElements.target, childElement.Name);
                    }

                    child = ParseProjectPropertyGroupElement(childElement, target);
                    break;

                case XMakeElements.itemGroup:
                    if (onError != null)
                    {
                        ProjectErrorUtilities.ThrowInvalidProject(onError.Location, "NodeMustBeLastUnderElement", XMakeElements.onError, XMakeElements.target, childElement.Name);
                    }

                    child = ParseProjectItemGroupElement(childElement, target);
                    break;

                case XMakeElements.onError:
                    onError = ParseProjectOnErrorElement(childElement, target);
                    child   = onError;
                    break;

                case XMakeElements.itemDefinitionGroup:
                    ProjectErrorUtilities.ThrowInvalidProject(childElement.Location, "ItemDefinitionGroupNotLegalInsideTarget", childElement.Name);
                    break;

                default:
                    if (onError != null)
                    {
                        ProjectErrorUtilities.ThrowInvalidProject(onError.Location, "NodeMustBeLastUnderElement", XMakeElements.onError, XMakeElements.target, childElement.Name);
                    }

                    child = ParseProjectTaskElement(childElement, target);
                    break;
                }

                target.AppendParentedChildNoChecks(child);
            }

            return(target);
        }
Beispiel #23
0
 protected virtual IMSBuildGlob CreateMsBuildGlob()
 {
     return(MSBuildGlob.Parse(ProjectDirectory, EscapingUtilities.UnescapeAll(TextFragment)));
 }
Beispiel #24
0
            protected override ICollection <I> SelectItems(ImmutableList <ItemData> .Builder listBuilder, ImmutableHashSet <string> globsToIgnore)
            {
                List <I> itemsToAdd = new List <I>();

                Lazy <Func <string, bool> > excludeTester = null;

                ImmutableList <string> .Builder excludePatterns = ImmutableList.CreateBuilder <string>();
                if (_excludes != null)
                {
                    // STEP 4: Evaluate, split, expand and subtract any Exclude
                    foreach (string exclude in _excludes)
                    {
                        string         excludeExpanded = _expander.ExpandIntoStringLeaveEscaped(exclude, ExpanderOptions.ExpandPropertiesAndItems, _itemElement.ExcludeLocation);
                        IList <string> excludeSplits   = ExpressionShredder.SplitSemiColonSeparatedList(excludeExpanded);
                        excludePatterns.AddRange(excludeSplits);
                    }

                    if (excludePatterns.Any())
                    {
                        excludeTester = new Lazy <Func <string, bool> >(() => EngineFileUtilities.GetFileSpecMatchTester(excludePatterns, _rootDirectory));
                    }
                }

                ISet <string> excludePatternsForGlobs = null;

                foreach (var fragment in _itemSpec.Fragments)
                {
                    if (fragment is ItemExpressionFragment <P, I> )
                    {
                        // STEP 3: If expression is "@(x)" copy specified list with its metadata, otherwise just treat as string
                        bool throwaway;
                        var  itemsFromExpression = _expander.ExpandExpressionCaptureIntoItems(
                            ((ItemExpressionFragment <P, I>)fragment).Capture, _evaluatorData, _itemFactory, ExpanderOptions.ExpandItems,
                            false /* do not include null expansion results */, out throwaway, _itemElement.IncludeLocation);

                        if (excludeTester != null)
                        {
                            itemsToAdd.AddRange(itemsFromExpression.Where(item => !excludeTester.Value(item.EvaluatedInclude)));
                        }
                        else
                        {
                            itemsToAdd.AddRange(itemsFromExpression);
                        }
                    }
                    else if (fragment is ValueFragment)
                    {
                        string value = ((ValueFragment)fragment).ItemSpecFragment;

                        if (excludeTester == null ||
                            !excludeTester.Value(EscapingUtilities.UnescapeAll(value)))
                        {
                            var item = _itemFactory.CreateItem(value, value, _itemElement.ContainingProject.FullPath);
                            itemsToAdd.Add(item);
                        }
                    }
                    else if (fragment is GlobFragment)
                    {
                        string glob = ((GlobFragment)fragment).ItemSpecFragment;

                        if (excludePatternsForGlobs == null)
                        {
                            excludePatternsForGlobs = BuildExcludePatternsForGlobs(globsToIgnore, excludePatterns);
                        }

                        string[] includeSplitFilesEscaped = EngineFileUtilities.GetFileListEscaped(
                            _rootDirectory,
                            glob,
                            excludePatternsForGlobs
                            );

                        // itemsToAdd might grow 0 or more times during the following iteration. Proactively increase its capacity to ensure only one growth happens
                        IncreaseListCapacityIfNecessary(itemsToAdd, includeSplitFilesEscaped.Length);

                        foreach (string includeSplitFileEscaped in includeSplitFilesEscaped)
                        {
                            itemsToAdd.Add(_itemFactory.CreateItem(includeSplitFileEscaped, glob, _itemElement.ContainingProject.FullPath));
                        }
                    }
                    else
                    {
                        throw new InvalidOperationException(fragment.GetType().ToString());
                    }
                }

                return(itemsToAdd);
            }
Beispiel #25
0
            protected override ImmutableList <I> SelectItems(ImmutableList <ItemData> .Builder listBuilder, ImmutableHashSet <string> globsToIgnore)
            {
                var itemsToAdd = ImmutableList.CreateBuilder <I>();

                Lazy <Func <string, bool> > excludeTester = null;

                ImmutableList <string> .Builder excludePatterns = ImmutableList.CreateBuilder <string>();
                if (_excludes != null)
                {
                    // STEP 4: Evaluate, split, expand and subtract any Exclude
                    foreach (string exclude in _excludes)
                    {
                        string excludeExpanded = _expander.ExpandIntoStringLeaveEscaped(exclude, ExpanderOptions.ExpandPropertiesAndItems, _itemElement.ExcludeLocation);
                        var    excludeSplits   = ExpressionShredder.SplitSemiColonSeparatedList(excludeExpanded);
                        excludePatterns.AddRange(excludeSplits);
                    }

                    if (excludePatterns.Count > 0)
                    {
                        excludeTester = new Lazy <Func <string, bool> >(() => EngineFileUtilities.GetFileSpecMatchTester(excludePatterns, _rootDirectory));
                    }
                }

                ISet <string> excludePatternsForGlobs = null;

                foreach (var fragment in _itemSpec.Fragments)
                {
                    if (fragment is ItemSpec <P, I> .ItemExpressionFragment itemReferenceFragment)
                    {
                        // STEP 3: If expression is "@(x)" copy specified list with its metadata, otherwise just treat as string
                        var itemsFromExpression = _expander.ExpandExpressionCaptureIntoItems(
                            itemReferenceFragment.Capture,
                            _evaluatorData,
                            _itemFactory,
                            ExpanderOptions.ExpandItems,
                            includeNullEntries: false,
                            isTransformExpression: out _,
                            elementLocation: _itemElement.IncludeLocation);

                        itemsToAdd.AddRange(
                            excludeTester != null
                                ? itemsFromExpression.Where(item => !excludeTester.Value(item.EvaluatedInclude))
                                : itemsFromExpression);
                    }
                    else if (fragment is ValueFragment valueFragment)
                    {
                        string value = valueFragment.TextFragment;

                        if (excludeTester == null ||
                            !excludeTester.Value(EscapingUtilities.UnescapeAll(value)))
                        {
                            var item = _itemFactory.CreateItem(value, value, _itemElement.ContainingProject.FullPath);
                            itemsToAdd.Add(item);
                        }
                    }
                    else if (fragment is GlobFragment globFragment)
                    {
                        string glob = globFragment.TextFragment;

                        if (excludePatternsForGlobs == null)
                        {
                            excludePatternsForGlobs = BuildExcludePatternsForGlobs(globsToIgnore, excludePatterns);
                        }

                        string[] includeSplitFilesEscaped;
                        if (MSBuildEventSource.Log.IsEnabled())
                        {
                            MSBuildEventSource.Log.ExpandGlobStart(_rootDirectory, glob, excludePatternsForGlobs.ToList().Aggregate((f, s) => f + ", " + s));
                        }
                        using (_lazyEvaluator._evaluationProfiler.TrackGlob(_rootDirectory, glob, excludePatternsForGlobs))
                        {
                            includeSplitFilesEscaped = EngineFileUtilities.GetFileListEscaped(
                                _rootDirectory,
                                glob,
                                excludePatternsForGlobs
                                );
                        }
                        if (MSBuildEventSource.Log.IsEnabled())
                        {
                            MSBuildEventSource.Log.ExpandGlobStop(_rootDirectory, glob, excludePatternsForGlobs.ToList().Aggregate((f, s) => f + ", " + s));
                        }

                        foreach (string includeSplitFileEscaped in includeSplitFilesEscaped)
                        {
                            itemsToAdd.Add(_itemFactory.CreateItem(includeSplitFileEscaped, glob, _itemElement.ContainingProject.FullPath));
                        }
                    }
                    else
                    {
                        throw new InvalidOperationException(fragment.GetType().ToString());
                    }
                }

                return(itemsToAdd.ToImmutable());
            }
Beispiel #26
0
        /// <summary>
        /// Returns a list of all items in the provided item group whose itemspecs match the specification, after it is split and any wildcards are expanded.
        /// If no items match, returns null.
        /// </summary>
        /// <param name="items">The items to match</param>
        /// <param name="specification">The specification to match against the items.</param>
        /// <param name="specificationLocation">The specification to match against the provided items</param>
        /// <param name="expander">The expander to use</param>
        /// <returns>A list of matching items</returns>
        private List <ProjectItemInstance> FindItemsMatchingSpecification
        (
            ICollection <ProjectItemInstance> items,
            string specification,
            ElementLocation specificationLocation,
            Expander <ProjectPropertyInstance, ProjectItemInstance> expander
        )
        {
            if (items.Count == 0 || specification.Length == 0)
            {
                return(null);
            }

            // This is a hashtable whose key is the filename for the individual items
            // in the Exclude list, after wildcard expansion.
            HashSet <string> specificationsToFind = new HashSet <string>(StringComparer.OrdinalIgnoreCase);

            // Split by semicolons
            var specificationPieces = expander.ExpandIntoStringListLeaveEscaped(specification, ExpanderOptions.ExpandAll, specificationLocation);

            foreach (string piece in specificationPieces)
            {
                // Take each individual path or file expression, and expand any
                // wildcards.  Then loop through each file returned, and add it
                // to our hashtable.

                // Don't unescape wildcards just yet - if there were any escaped, the caller wants to treat them
                // as literals. Everything else is safe to unescape at this point, since we're only matching
                // against the file system.
                string[] fileList = _engineFileUtilities.GetFileListEscaped(Project.Directory, piece);

                foreach (string file in fileList)
                {
                    // Now unescape everything, because this is the end of the road for this filename.
                    // We're just going to compare it to the unescaped include path to filter out the
                    // file excludes.
                    specificationsToFind.Add(EscapingUtilities.UnescapeAll(file));
                }
            }

            if (specificationsToFind.Count == 0)
            {
                return(null);
            }

            // Now loop through our list and filter out any that match a
            // filename in the remove list.
            List <ProjectItemInstance> itemsRemoved = new List <ProjectItemInstance>();

            foreach (ProjectItemInstance item in items)
            {
                // Even if the case for the excluded files is different, they
                // will still get excluded, as expected.  However, if the excluded path
                // references the same file in a different way, such as by relative
                // path instead of absolute path, we will not realize that they refer
                // to the same file, and thus we will not exclude it.
                if (specificationsToFind.Contains(item.EvaluatedInclude))
                {
                    itemsRemoved.Add(item);
                }
            }

            return(itemsRemoved);
        }
Beispiel #27
0
        private void WriteITaskItem(ITranslator translator, ITaskItem wrappedItem)
        {
            ErrorUtilities.VerifyThrow(translator.Mode == TranslationDirection.WriteToStream, "Cannot call this method when reading!");

            if (!TranslateNullable(translator, wrappedItem))
            {
                return;
            }

            string      escapedItemSpec;
            string      escapedDefiningProject;
            IDictionary wrappedMetadata;
            bool        wrappedMetadataIsEscaped;

            ITaskItem2 wrappedItemAsITaskItem2 = wrappedItem as ITaskItem2;

            if (wrappedItemAsITaskItem2 != null)
            {
                escapedItemSpec          = wrappedItemAsITaskItem2.EvaluatedIncludeEscaped;
                escapedDefiningProject   = wrappedItemAsITaskItem2.GetMetadataValueEscaped(FileUtilities.ItemSpecModifiers.DefiningProjectFullPath);
                wrappedMetadata          = wrappedItemAsITaskItem2.CloneCustomMetadataEscaped();
                wrappedMetadataIsEscaped = true;
            }
            else
            {
                // We know that the ITaskItem constructor expects an escaped string, and that ITaskItem.ItemSpec
                // is expected to be unescaped, so make sure we give the constructor what it wants.
                escapedItemSpec          = EscapingUtilities.Escape(wrappedItem.ItemSpec);
                escapedDefiningProject   = EscapingUtilities.EscapeWithCaching(wrappedItem.GetMetadata(FileUtilities.ItemSpecModifiers.DefiningProjectFullPath));
                wrappedMetadata          = wrappedItem.CloneCustomMetadata();
                wrappedMetadataIsEscaped = false;
            }

            Dictionary <string, string> escapedGenericWrappedMetadata = wrappedMetadata as Dictionary <string, string>;

            if (escapedGenericWrappedMetadata == null)
            {
                escapedGenericWrappedMetadata = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase);

                foreach (object key in wrappedMetadata.Keys)
                {
                    string value = (string)wrappedMetadata[key];

                    if (!wrappedMetadataIsEscaped)
                    {
                        value = (value == null) ? value : EscapingUtilities.Escape(value);
                    }

                    escapedGenericWrappedMetadata.Add((string)key, value);
                }
            }
            else if (!wrappedMetadataIsEscaped)
            {
                foreach (KeyValuePair <string, string> entry in escapedGenericWrappedMetadata)
                {
                    escapedGenericWrappedMetadata[entry.Key] = entry.Value == null ? entry.Value : EscapingUtilities.Escape(entry.Value);
                }
            }

            translator.Translate(ref escapedItemSpec);
            translator.Translate(ref escapedDefiningProject);
            translator.TranslateDictionary(ref escapedGenericWrappedMetadata, StringComparer.OrdinalIgnoreCase);
        }
Beispiel #28
0
        private IEnumerable <ItemFragment> BuildItemFragments(IElementLocation itemSpecLocation, bool expandProperties)
        {
            var builder = ImmutableList.CreateBuilder <ItemFragment>();

            //  Code corresponds to Evaluator.CreateItemsFromInclude
            var evaluatedItemspecEscaped = ItemSpecString;

            if (string.IsNullOrEmpty(evaluatedItemspecEscaped))
            {
                return(builder.ToImmutable());
            }

            // STEP 1: Expand properties in Include
            if (expandProperties)
            {
                evaluatedItemspecEscaped = Expander.ExpandIntoStringLeaveEscaped(ItemSpecString, ExpanderOptions.ExpandProperties, itemSpecLocation);
            }

            // STEP 2: Split Include on any semicolons, and take each split in turn
            if (evaluatedItemspecEscaped.Length > 0)
            {
                var splitsEscaped = ExpressionShredder.SplitSemiColonSeparatedList(evaluatedItemspecEscaped);

                foreach (var splitEscaped in splitsEscaped)
                {
                    // STEP 3: If expression is "@(x)" copy specified list with its metadata, otherwise just treat as string
                    bool isItemListExpression;
                    var  itemReferenceFragment = ProcessItemExpression(splitEscaped, itemSpecLocation, out isItemListExpression);

                    if (isItemListExpression)
                    {
                        builder.Add(itemReferenceFragment);
                    }
                    else
                    {
                        // The expression is not of the form "@(X)". Treat as string

                        //  Code corresponds to EngineFileUtilities.GetFileList
                        var containsEscapedWildcards = EscapingUtilities.ContainsEscapedWildcards(splitEscaped);
                        var containsRealWildcards    = FileMatcher.HasWildcards(splitEscaped);

                        // '*' is an illegal character to have in a filename.
                        // todo file-system assumption on legal path characters: https://github.com/Microsoft/msbuild/issues/781
                        if (containsEscapedWildcards && containsRealWildcards)
                        {
                            // Just return the original string.
                            builder.Add(new ValueFragment(splitEscaped, itemSpecLocation.File));
                        }
                        else if (!containsEscapedWildcards && containsRealWildcards)
                        {
                            // Unescape before handing it to the filesystem.
                            var filespecUnescaped = EscapingUtilities.UnescapeAll(splitEscaped);

                            builder.Add(new GlobFragment(filespecUnescaped, itemSpecLocation.File));
                        }
                        else
                        {
                            // No real wildcards means we just return the original string.  Don't even bother
                            // escaping ... it should already be escaped appropriately since it came directly
                            // from the project file

                            builder.Add(new ValueFragment(splitEscaped, itemSpecLocation.File));
                        }
                    }
                }
            }

            return(builder.ToImmutable());
        }
Beispiel #29
0
 void ITaskItem2.SetMetadataValueLiteral(string metadataName, string metadataValue)
 {
     SetMetadata(metadataName, EscapingUtilities.Escape(metadataValue));
 }
Beispiel #30
0
        /// <summary>
        /// Execute the task.
        /// </summary>
        public override bool Execute()
        {
            var inPathList    = new List <ITaskItem>();
            var outOfPathList = new List <ITaskItem>();

            string conePath;

            try
            {
                conePath =
                    OpportunisticIntern.InternStringIfPossible(
                        System.IO.Path.GetFullPath(FileUtilities.FixFilePath(Path.ItemSpec)));
                conePath = FileUtilities.EnsureTrailingSlash(conePath);
            }
            catch (Exception e) when(ExceptionHandling.IsIoRelatedException(e))
            {
                Log.LogErrorWithCodeFromResources(null, "", 0, 0, 0, 0,
                                                  "FindUnderPath.InvalidParameter", "Path", Path.ItemSpec, e.Message);
                return(false);
            }

            int conePathLength = conePath.Length;

            Log.LogMessageFromResources(MessageImportance.Low, "FindUnderPath.ComparisonPath", Path.ItemSpec);

            foreach (ITaskItem item in Files)
            {
                string fullPath;
                try
                {
                    fullPath =
                        OpportunisticIntern.InternStringIfPossible(
                            System.IO.Path.GetFullPath(FileUtilities.FixFilePath(item.ItemSpec)));
                }
                catch (Exception e) when(ExceptionHandling.IsIoRelatedException(e))
                {
                    Log.LogErrorWithCodeFromResources(null, "", 0, 0, 0, 0,
                                                      "FindUnderPath.InvalidParameter", "Files", item.ItemSpec, e.Message);
                    return(false);
                }

                // Compare the left side of both strings to see if they're equal.
                if (String.Compare(conePath, 0, fullPath, 0, conePathLength, StringComparison.OrdinalIgnoreCase) == 0)
                {
                    // If we should use the absolute path, update the item contents
                    // Since ItemSpec, which fullPath comes from, is unescaped, re-escape when setting
                    // item.ItemSpec, since the setter for ItemSpec expects an escaped value.
                    if (UpdateToAbsolutePaths)
                    {
                        item.ItemSpec = EscapingUtilities.Escape(fullPath);
                    }

                    inPathList.Add(item);
                }
                else
                {
                    outOfPathList.Add(item);
                }
            }

            InPath    = inPathList.ToArray();
            OutOfPath = outOfPathList.ToArray();
            return(true);
        }