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); }
public string GetMetadata(string metadataName) { string metadataValue = (this as ITaskItem2).GetMetadataValueEscaped(metadataName); return(EscapingUtilities.UnescapeAll(metadataValue)); }
public virtual IEnumerable <string> GetReferencedItems() { return(Enumerable.Repeat(EscapingUtilities.UnescapeAll(TextFragment), 1)); }
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 <Target> 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); } }
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); }
/// <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)); }
/// <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); }
/// <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))); }
/// <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); }
/// <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; } }
/// <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)); }
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()); }
/// <summary> /// Unescape the string according to MSBuild's escaping rules /// </summary> internal static string Unescape(string escaped) { return(EscapingUtilities.UnescapeAll(escaped)); }
public string GetMetadata(string metadataName) { return(EscapingUtilities.UnescapeAll(((ITaskItem2)this).GetMetadataValueEscaped(metadataName))); }
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); }
/// <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); }
protected virtual IMSBuildGlob CreateMsBuildGlob() { return(MSBuildGlob.Parse(ProjectDirectory, EscapingUtilities.UnescapeAll(TextFragment))); }
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); }
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()); }
/// <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); }
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); }
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()); }
void ITaskItem2.SetMetadataValueLiteral(string metadataName, string metadataValue) { SetMetadata(metadataName, EscapingUtilities.Escape(metadataValue)); }
/// <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); }