/// <summary> /// Constructor taking a delegate for unit test purposes only /// </summary> internal ToolsetConfigurationReader(PropertyDictionary <ProjectPropertyInstance> environmentProperties, PropertyDictionary <ProjectPropertyInstance> globalProperties, Func <Configuration> readApplicationConfiguration) : base(environmentProperties, globalProperties) { ErrorUtilities.VerifyThrowArgumentNull(readApplicationConfiguration, "readApplicationConfiguration"); _readApplicationConfiguration = readApplicationConfiguration; _projectImportSearchPathsCache = new Dictionary <string, Dictionary <string, List <string> > >(StringComparer.OrdinalIgnoreCase); }
/// <summary> /// Rename an entry in the cache. /// Entry must already be in the cache. /// </summary> internal void RenameEntry(string oldFullPath, ProjectRootElement projectRootElement) { lock (_locker) { ErrorUtilities.VerifyThrowArgumentLength(oldFullPath, "oldFullPath"); RenameEntryInternal(oldFullPath, projectRootElement); } }
/// <summary> /// Creates a new property /// </summary> /// <param name="name">The property name</param> /// <param name="value">The property value</param> /// <param name="source">The property source</param> public ToolsetPropertyDefinition(string name, string value, IElementLocation source) { ErrorUtilities.VerifyThrowArgumentLength(name, "name"); ErrorUtilities.VerifyThrowArgumentNull(source, "source"); // value can be the empty string but not null ErrorUtilities.VerifyThrowArgumentNull(value, "value"); _name = name; _value = value; _source = source; }
/// <summary> /// Forces a removal of a project root element from the weak cache if it is present. /// </summary> /// <param name="projectRootElement">The project root element to remove.</param> /// <remarks> /// No exception is thrown if this project root element is in use by currently loaded projects /// by this method. The calling method must know that this is a safe operation. /// There may of course be strong references to the project root element from customer code. /// The assumption is that when they instruct the project collection to unload it, which /// leads to this being called, they are releasing their strong references too (or it doesn't matter) /// </remarks> internal void DiscardAnyWeakReference(ProjectRootElement projectRootElement) { ErrorUtilities.VerifyThrowArgumentNull(projectRootElement, "projectRootElement"); // A PRE may be unnamed if it was only used in memory. if (projectRootElement.FullPath != null) { lock (_locker) { _weakCache.Remove(projectRootElement.FullPath); } } }
/// <summary> /// Cleanse the project name, by replacing characters like '@', '$' with '_' /// </summary> /// <param name="projectName">The name to be cleansed</param> /// <returns>string</returns> private static string CleanseProjectName(string projectName) { ErrorUtilities.VerifyThrow(projectName != null, "Null strings not allowed."); // If there are no special chars, just return the original string immediately. // Don't even instantiate the StringBuilder. int indexOfChar = projectName.IndexOfAny(s_charsToCleanse); if (indexOfChar == -1) { return(projectName); } // This is where we're going to work on the final string to return to the caller. var cleanProjectName = new StringBuilder(projectName); // Replace each unclean character with a clean one foreach (char uncleanChar in s_charsToCleanse) { cleanProjectName.Replace(uncleanChar, cleanCharacter); } return(cleanProjectName.ToString()); }
/// <summary> /// Add or rename an entry in the cache. /// Old full path may be null iff it was not already in the cache. /// </summary> /// <remarks> /// Must be called within the cache lock. /// </remarks> private void RenameEntryInternal(string oldFullPathIfAny, ProjectRootElement projectRootElement) { ErrorUtilities.VerifyThrowInternalNull(projectRootElement.FullPath, "FullPath"); if (oldFullPathIfAny != null) { ErrorUtilities.VerifyThrowInternalRooted(oldFullPathIfAny); ErrorUtilities.VerifyThrow(_weakCache[oldFullPathIfAny] == projectRootElement, "Should already be present"); _weakCache.Remove(oldFullPathIfAny); } // There may already be a ProjectRootElement in the cache with the new name. In this case we cannot throw an exception; // we must merely replace it. This is because it may be an unrooted entry // (and thus gone from the client's point of view) that merely remains // in the cache because we still have a reference to it from our strong cache. // Another possibility is that there are two, unrelated, un-saved, in-memory projects that were given the same path. // Replacing the cache entry does not in itself cause a problem -- if there are any actual users of the old // entry they will not be affected. There would then exist more than one ProjectRootElement with the same path, // but clients ought not get themselves into such a state - and unless they save them to disk, // it may not be a problem. Replacing also doesn't cause a problem for the strong cache, // as it is never consulted by us, but it is reasonable for us to remove the old entry in that case. ProjectRootElement existingWeakEntry; _weakCache.TryGetValue(projectRootElement.FullPath, out existingWeakEntry); if (existingWeakEntry != null && !Object.ReferenceEquals(existingWeakEntry, projectRootElement)) { _strongCache.Remove(existingWeakEntry); RaiseProjectRootElementRemovedFromStrongCache(existingWeakEntry); } DebugTraceCache("Adding: ", projectRootElement.FullPath); _weakCache[projectRootElement.FullPath] = projectRootElement; BoostEntryInStrongCache(projectRootElement); }
/// <summary> /// Changes the unique name of the project. /// </summary> internal void UpdateUniqueProjectName(string newUniqueName) { ErrorUtilities.VerifyThrowArgumentLength(newUniqueName, nameof(newUniqueName)); _uniqueProjectName = newUniqueName; }
/// <summary> /// Returns an existing ProjectRootElement for the specified file path, if any. /// If none exists, calls the provided delegate to load one, and adds that to the cache. /// The reason that it calls back to do this is so that the cache is locked between determining /// that the entry does not exist and adding the entry. /// /// If <see cref="_autoReloadFromDisk"/> was set to true, and the file on disk has changed since it was cached, /// it will be reloaded before being returned. /// /// Thread safe. /// </summary> /// <remarks> /// Never needs to consult the strong cache as well, since if the item is in there, it will /// not have left the weak cache. /// If item is found, boosts it to the top of the strong cache. /// </remarks> /// <param name="projectFile">The project file which contains the ProjectRootElement. Must be a full path.</param> /// <param name="openProjectRootElement">The delegate to use to load if necessary. May be null.</param> /// <param name="isExplicitlyLoaded"><code>true</code> if the project is explicitly loaded, otherwise <code>false</code>.</param> /// <param name="preserveFormatting"><code>true</code> to the project was loaded with the formated preserved, otherwise <code>false</code>.</param> /// <returns>The ProjectRootElement instance if one exists. Null otherwise.</returns> internal ProjectRootElement Get(string projectFile, OpenProjectRootElement openProjectRootElement, bool isExplicitlyLoaded, bool?preserveFormatting) { // Should already have been canonicalized ErrorUtilities.VerifyThrowInternalRooted(projectFile); lock (_locker) { ProjectRootElement projectRootElement; _weakCache.TryGetValue(projectFile, out projectRootElement); if (preserveFormatting != null && projectRootElement != null && projectRootElement.XmlDocument.PreserveWhitespace != preserveFormatting) { // Cached project doesn't match preserveFormatting setting, so reload it projectRootElement.Reload(true, preserveFormatting); } if (projectRootElement != null && _autoReloadFromDisk) { var fileInfo = FileUtilities.GetFileInfoNoThrow(projectFile); // If the file doesn't exist on disk, go ahead and use the cached version. // It's an in-memory project that hasn't been saved yet. if (fileInfo != null) { bool forgetEntry = false; if (fileInfo.LastWriteTime != projectRootElement.LastWriteTimeWhenRead) { // File was changed on disk by external means. Cached version is no longer reliable. // We could throw here or ignore the problem, but it is a common and reasonable pattern to change a file // externally and load a new project over it to see the new content. So we dump it from the cache // to force a load from disk. There might then exist more than one ProjectRootElement with the same path, // but clients ought not get themselves into such a state - and unless they save them to disk, // it may not be a problem. forgetEntry = true; } else if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("MSBUILDCACHECHECKFILECONTENT"))) { // QA tests run too fast for the timestamp check to work. This environment variable is for their // use: it checks the file content as well as the timestamp. That's better than completely disabling // the cache as we get test coverage of the rest of the cache code. XmlDocument document = new XmlDocument(); document.PreserveWhitespace = projectRootElement.XmlDocument.PreserveWhitespace; using (var xtr = XmlReaderExtension.Create(projectRootElement.FullPath, projectRootElement.ProjectRootElementCache.LoadProjectsReadOnly)) { document.Load(xtr.Reader); } string diskContent = document.OuterXml; string cacheContent = projectRootElement.XmlDocument.OuterXml; if (diskContent != cacheContent) { forgetEntry = true; } } if (forgetEntry) { ForgetEntry(projectRootElement); DebugTraceCache("Out of date dropped from XML cache: ", projectFile); projectRootElement = null; } } } if (projectRootElement == null && openProjectRootElement != null) { projectRootElement = openProjectRootElement(projectFile, this); ErrorUtilities.VerifyThrowInternalNull(projectRootElement, "projectRootElement"); ErrorUtilities.VerifyThrow(projectRootElement.FullPath == projectFile, "Got project back with incorrect path"); ErrorUtilities.VerifyThrow(_weakCache.Contains(projectFile), "Open should have renamed into cache and boosted"); } else if (projectRootElement != null) { DebugTraceCache("Satisfied from XML cache: ", projectFile); BoostEntryInStrongCache(projectRootElement); } // An implicit load will never reset the explicit flag. if (projectRootElement != null && isExplicitlyLoaded) { projectRootElement.MarkAsExplicitlyLoaded(); } return(projectRootElement); } }
internal override ProjectRootElement Get(string projectFile, OpenProjectRootElement loadProjectRootElement, bool isExplicitlyLoaded, bool?preserveFormatting) { #if DEBUG // Verify that loadProjectRootElement delegate does not call ProjectRootElementCache.Get(). s_getEntriesNumber++; ErrorUtilities.VerifyThrow( s_getEntriesNumber == 1, "Reentrance to the ProjectRootElementCache.Get function detected." ); try { #endif // Should already have been canonicalized ErrorUtilities.VerifyThrowInternalRooted(projectFile); ProjectRootElement projectRootElement; lock (_locker) { _weakCache.TryGetValue(projectFile, out projectRootElement); if (projectRootElement != null) { BoostEntryInStrongCache(projectRootElement); // An implicit load will never reset the explicit flag. if (isExplicitlyLoaded) { projectRootElement.MarkAsExplicitlyLoaded(); } } else { DebugTraceCache("Not found in cache: ", projectFile); } if (preserveFormatting != null && projectRootElement != null && projectRootElement.XmlDocument.PreserveWhitespace != preserveFormatting) { // Cached project doesn't match preserveFormatting setting, so reload it projectRootElement.Reload(true, preserveFormatting); } } bool projectRootElementIsInvalid = IsInvalidEntry(projectFile, projectRootElement); if (projectRootElementIsInvalid) { DebugTraceCache("Not satisfied from cache: ", projectFile); ForgetEntryIfExists(projectRootElement); } if (loadProjectRootElement == null) { if (projectRootElement == null || projectRootElementIsInvalid) { return(null); } else { DebugTraceCache("Satisfied from XML cache: ", projectFile); return(projectRootElement); } } // Use openProjectRootElement to reload the element if the cache element does not exist or need to be reloaded. if (projectRootElement == null || projectRootElementIsInvalid) { // We do not lock loading with common _locker of the cache, to avoid lock contention. // Decided also not to lock this section with the key specific locker to avoid the overhead and code overcomplication, as // it is not likely that two threads would use Get function for the same project simultaneously and it is not a big deal if in some cases we load the same project twice. projectRootElement = loadProjectRootElement(projectFile, this); ErrorUtilities.VerifyThrowInternalNull(projectRootElement, "projectRootElement"); ErrorUtilities.VerifyThrow( projectRootElement.FullPath.Equals(projectFile, StringComparison.OrdinalIgnoreCase), "Got project back with incorrect path. Expected path: {0}, received path: {1}.", projectFile, projectRootElement.FullPath ); // An implicit load will never reset the explicit flag. if (isExplicitlyLoaded) { projectRootElement.MarkAsExplicitlyLoaded(); } // Update cache element. // It is unlikely, but it might be that while without the lock, the projectRootElement in cache was updated by another thread. // And here its entry will be replaced with the loaded projectRootElement. This is fine: // if loaded projectRootElement is out of date (so, it changed since the time we loaded it), it will be updated the next time some thread calls Get function. AddEntry(projectRootElement); } else { DebugTraceCache("Satisfied from XML cache: ", projectFile); } return(projectRootElement); #if DEBUG } finally { s_getEntriesNumber--; } #endif }
internal static string GetItemSpecModifier(string currentDirectory, string itemSpec, string modifier, ref CopyOnWriteDictionary <string, string> cachedModifiers) { ErrorUtilities.VerifyThrow(itemSpec != null, "Need item-spec to modify."); ErrorUtilities.VerifyThrow(modifier != null, "Need modifier to apply to item-spec."); string fullPath = null; if (cachedModifiers != null) { cachedModifiers.TryGetValue(modifier, out fullPath); } if (fullPath == null) { bool flag = true; try { if (string.Compare(modifier, "FullPath", StringComparison.OrdinalIgnoreCase) == 0) { if (currentDirectory == null) { currentDirectory = string.Empty; } fullPath = FileUtilities.GetFullPath(itemSpec, currentDirectory); ThrowForUrl(fullPath, itemSpec, currentDirectory); } else if (string.Compare(modifier, "RootDir", StringComparison.OrdinalIgnoreCase) == 0) { string str2; if (currentDirectory == null) { currentDirectory = string.Empty; } if ((cachedModifiers == null) || !cachedModifiers.TryGetValue("FullPath", out str2)) { str2 = FileUtilities.GetFullPath(itemSpec, currentDirectory); ThrowForUrl(str2, itemSpec, currentDirectory); } fullPath = Path.GetPathRoot(str2); if (!FileUtilities.EndsWithSlash(fullPath)) { ErrorUtilities.VerifyThrow(FileUtilitiesRegex.UNCPattern.IsMatch(fullPath), "Only UNC shares should be missing trailing slashes."); fullPath = fullPath + Path.DirectorySeparatorChar; } } else if (string.Compare(modifier, "Filename", StringComparison.OrdinalIgnoreCase) == 0) { if (Path.GetDirectoryName(itemSpec) == null) { fullPath = string.Empty; } else { fullPath = Path.GetFileNameWithoutExtension(itemSpec); } } else if (string.Compare(modifier, "Extension", StringComparison.OrdinalIgnoreCase) == 0) { if (Path.GetDirectoryName(itemSpec) == null) { fullPath = string.Empty; } else { fullPath = Path.GetExtension(itemSpec); } } else if (string.Compare(modifier, "RelativeDir", StringComparison.OrdinalIgnoreCase) == 0) { fullPath = FileUtilities.GetDirectory(itemSpec); } else if (string.Compare(modifier, "Directory", StringComparison.OrdinalIgnoreCase) == 0) { string str3; if (currentDirectory == null) { currentDirectory = string.Empty; } if ((cachedModifiers == null) || !cachedModifiers.TryGetValue("FullPath", out str3)) { str3 = FileUtilities.GetFullPath(itemSpec, currentDirectory); ThrowForUrl(str3, itemSpec, currentDirectory); } fullPath = FileUtilities.GetDirectory(str3); Match match = FileUtilitiesRegex.DrivePattern.Match(fullPath); if (!match.Success) { match = FileUtilitiesRegex.UNCPattern.Match(fullPath); } if (match.Success) { ErrorUtilities.VerifyThrow((fullPath.Length > match.Length) && FileUtilities.IsSlash(fullPath[match.Length]), "Root directory must have a trailing slash."); fullPath = fullPath.Substring(match.Length + 1); } } else if (string.Compare(modifier, "RecursiveDir", StringComparison.OrdinalIgnoreCase) == 0) { fullPath = string.Empty; } else if (string.Compare(modifier, "Identity", StringComparison.OrdinalIgnoreCase) == 0) { flag = cachedModifiers != null; fullPath = itemSpec; } else if (string.Compare(modifier, "ModifiedTime", StringComparison.OrdinalIgnoreCase) == 0) { flag = false; FileInfo fileInfoNoThrow = FileUtilities.GetFileInfoNoThrow(EscapingUtilities.UnescapeAll(itemSpec)); if (fileInfoNoThrow != null) { fullPath = fileInfoNoThrow.LastWriteTime.ToString("yyyy'-'MM'-'dd HH':'mm':'ss'.'fffffff", null); } else { fullPath = string.Empty; } } else if (string.Compare(modifier, "CreatedTime", StringComparison.OrdinalIgnoreCase) == 0) { flag = false; string path = EscapingUtilities.UnescapeAll(itemSpec); if (File.Exists(path)) { fullPath = File.GetCreationTime(path).ToString("yyyy'-'MM'-'dd HH':'mm':'ss'.'fffffff", null); } else { fullPath = string.Empty; } } else if (string.Compare(modifier, "AccessedTime", StringComparison.OrdinalIgnoreCase) == 0) { flag = false; string str6 = EscapingUtilities.UnescapeAll(itemSpec); if (File.Exists(str6)) { fullPath = File.GetLastAccessTime(str6).ToString("yyyy'-'MM'-'dd HH':'mm':'ss'.'fffffff", null); } else { fullPath = string.Empty; } } else { ErrorUtilities.VerifyThrow(false, "\"{0}\" is not a valid item-spec modifier.", modifier); } } catch (Exception exception) { if (ExceptionHandling.NotExpectedException(exception)) { throw; } ErrorUtilities.VerifyThrowInvalidOperation(false, "Shared.InvalidFilespecForTransform", modifier, itemSpec, exception.Message); } ErrorUtilities.VerifyThrow(fullPath != null, "The item-spec modifier \"{0}\" was not evaluated.", modifier); if (flag) { if (cachedModifiers == null) { cachedModifiers = new CopyOnWriteDictionary <string, string>(StringComparer.OrdinalIgnoreCase); } cachedModifiers[modifier] = fullPath; } } return(fullPath); }
/// <summary> /// Constructor taking a delegate for unit test purposes only /// </summary> internal ToolsetConfigurationReader(PropertyDictionary <ProjectPropertyInstance> environmentProperties, PropertyDictionary <ProjectPropertyInstance> globalProperties, ReadApplicationConfiguration readApplicationConfiguration) : base(environmentProperties, globalProperties) { ErrorUtilities.VerifyThrowArgumentNull(readApplicationConfiguration, "readApplicationConfiguration"); _readApplicationConfiguration = readApplicationConfiguration; }
/// <summary> /// Add an assemblyNameExtension which represents an assembly name which was mapped to THIS assemblyName. /// </summary> internal void AddRemappedAssemblyName(AssemblyNameExtension extensionToAdd) { ErrorUtilities.VerifyThrow(extensionToAdd.Immutable, "ExtensionToAdd is not immutable"); InitializeRemappedFrom(); remappedFrom.Add(extensionToAdd); }
/// <summary> /// Given two MSBuildArchitecture values, returns the concrete result of merging the two. If the merge fails, the merged architecture /// string is returned null, and the return value of the method is false. Otherwise, if the merge succeeds, the method returns /// true with the merged architecture value. E.g.: /// "x86" + "x64" = null (false) /// "x86" + "don't care" = "x86" (true) /// "current architecture" + "x86" = "x86" (true) on a 32-bit process, and null (false) on a 64-bit process /// "current architecture" + "don't care" = "x86" (true) on a 32-bit process, and "x64" (true) on a 64-bit process /// A null or empty string is interpreted as "don't care". /// If both specify "don't care", then defaults to whatever the current process architecture is. /// </summary> internal static bool TryMergeArchitectureValues(string architectureA, string architectureB, out string mergedArchitecture) { ErrorUtilities.VerifyThrow(architectureA != String.Empty && architectureB != String.Empty, "We should never get an empty string passed to this method"); // set up the defaults if (architectureA == null) { architectureA = MSBuildArchitectureValues.any; } if (architectureB == null) { architectureB = MSBuildArchitectureValues.any; } string currentArchitecture = GetCurrentMSBuildArchitecture(); // if they're equal, then there's no problem -- just return the equivalent runtime. if (architectureA.Equals(architectureB, StringComparison.OrdinalIgnoreCase)) { if (architectureA.Equals(MSBuildArchitectureValues.currentArchitecture, StringComparison.OrdinalIgnoreCase) || architectureA.Equals(MSBuildArchitectureValues.any, StringComparison.OrdinalIgnoreCase)) { mergedArchitecture = currentArchitecture; } else { mergedArchitecture = architectureA; } return(true); } // if both A and B are one of CLR4, don't care, or current, then the end result will be CLR4 no matter what. if ( ( architectureA.Equals(currentArchitecture, StringComparison.OrdinalIgnoreCase) || architectureA.Equals(MSBuildArchitectureValues.currentArchitecture, StringComparison.OrdinalIgnoreCase) || architectureA.Equals(MSBuildArchitectureValues.any, StringComparison.OrdinalIgnoreCase) ) && ( architectureB.Equals(currentArchitecture, StringComparison.OrdinalIgnoreCase) || architectureB.Equals(MSBuildArchitectureValues.currentArchitecture, StringComparison.OrdinalIgnoreCase) || architectureB.Equals(MSBuildArchitectureValues.any, StringComparison.OrdinalIgnoreCase) ) ) { mergedArchitecture = currentArchitecture; return(true); } // If A doesn't care, then it's B -- and we can say B straight out, because if B were one of the // special cases (current runtime or don't care) then it would already have been caught in the // previous clause. if (architectureA.Equals(MSBuildArchitectureValues.any, StringComparison.OrdinalIgnoreCase)) { mergedArchitecture = architectureB; return(true); } // And vice versa if (architectureB.Equals(MSBuildArchitectureValues.any, StringComparison.OrdinalIgnoreCase)) { mergedArchitecture = architectureA; return(true); } // and now we've run out of things that it could be -- all the remaining options are non-matches. mergedArchitecture = null; return(false); }
internal static string GetItemSpecModifier(string currentDirectory, string itemSpec, string definingProjectEscaped, string modifier, ref string fullPath) { ErrorUtilities.VerifyThrow(itemSpec != null, "Need item-spec to modify."); ErrorUtilities.VerifyThrow(modifier != null, "Need modifier to apply to item-spec."); string modifiedItemSpec = null; try { if (String.Compare(modifier, FileUtilities.ItemSpecModifiers.FullPath, StringComparison.OrdinalIgnoreCase) == 0) { if (fullPath != null) { return(fullPath); } if (currentDirectory == null) { currentDirectory = String.Empty; } modifiedItemSpec = GetFullPath(itemSpec, currentDirectory); fullPath = modifiedItemSpec; ThrowForUrl(modifiedItemSpec, itemSpec, currentDirectory); } else if (String.Compare(modifier, FileUtilities.ItemSpecModifiers.RootDir, StringComparison.OrdinalIgnoreCase) == 0) { GetItemSpecModifier(currentDirectory, itemSpec, definingProjectEscaped, ItemSpecModifiers.FullPath, ref fullPath); modifiedItemSpec = Path.GetPathRoot(fullPath); if (!EndsWithSlash(modifiedItemSpec)) { ErrorUtilities.VerifyThrow(FileUtilitiesRegex.UNCPattern.IsMatch(modifiedItemSpec), "Only UNC shares should be missing trailing slashes."); // restore/append trailing slash if Path.GetPathRoot() has either removed it, or failed to add it // (this happens with UNC shares) modifiedItemSpec += Path.DirectorySeparatorChar; } } else if (String.Compare(modifier, FileUtilities.ItemSpecModifiers.Filename, StringComparison.OrdinalIgnoreCase) == 0) { // if the item-spec is a root directory, it can have no filename if (Path.GetDirectoryName(itemSpec) == null) { // NOTE: this is to prevent Path.GetFileNameWithoutExtension() from treating server and share elements // in a UNC file-spec as filenames e.g. \\server, \\server\share modifiedItemSpec = String.Empty; } else { modifiedItemSpec = Path.GetFileNameWithoutExtension(itemSpec); } } else if (String.Compare(modifier, FileUtilities.ItemSpecModifiers.Extension, StringComparison.OrdinalIgnoreCase) == 0) { // if the item-spec is a root directory, it can have no extension if (Path.GetDirectoryName(itemSpec) == null) { // NOTE: this is to prevent Path.GetExtension() from treating server and share elements in a UNC // file-spec as filenames e.g. \\server.ext, \\server\share.ext modifiedItemSpec = String.Empty; } else { modifiedItemSpec = Path.GetExtension(itemSpec); } } else if (String.Compare(modifier, FileUtilities.ItemSpecModifiers.RelativeDir, StringComparison.OrdinalIgnoreCase) == 0) { modifiedItemSpec = GetDirectory(itemSpec); } else if (String.Compare(modifier, FileUtilities.ItemSpecModifiers.Directory, StringComparison.OrdinalIgnoreCase) == 0) { GetItemSpecModifier(currentDirectory, itemSpec, definingProjectEscaped, ItemSpecModifiers.FullPath, ref fullPath); modifiedItemSpec = GetDirectory(fullPath); if (NativeMethodsShared.IsWindows) { Match root = FileUtilitiesRegex.DrivePattern.Match(modifiedItemSpec); if (!root.Success) { root = FileUtilitiesRegex.UNCPattern.Match(modifiedItemSpec); } if (root.Success) { ErrorUtilities.VerifyThrow((modifiedItemSpec.Length > root.Length) && IsSlash(modifiedItemSpec[root.Length]), "Root directory must have a trailing slash."); modifiedItemSpec = modifiedItemSpec.Substring(root.Length + 1); } } else { ErrorUtilities.VerifyThrow(!string.IsNullOrEmpty(modifiedItemSpec) && IsSlash(modifiedItemSpec[0]), "Expected a full non-windows path rooted at '/'."); // A full unix path is always rooted at // `/`, and a root-relative path is the // rest of the string. modifiedItemSpec = modifiedItemSpec.Substring(1); } } else if (String.Compare(modifier, FileUtilities.ItemSpecModifiers.RecursiveDir, StringComparison.OrdinalIgnoreCase) == 0) { // only the BuildItem class can compute this modifier -- so leave empty modifiedItemSpec = String.Empty; } else if (String.Compare(modifier, FileUtilities.ItemSpecModifiers.Identity, StringComparison.OrdinalIgnoreCase) == 0) { modifiedItemSpec = itemSpec; } else if (String.Compare(modifier, FileUtilities.ItemSpecModifiers.ModifiedTime, StringComparison.OrdinalIgnoreCase) == 0) { // About to go out to the filesystem. This means data is leaving the engine, so need // to unescape first. string unescapedItemSpec = EscapingUtilities.UnescapeAll(itemSpec); FileInfo info = FileUtilities.GetFileInfoNoThrow(unescapedItemSpec); if (info != null) { modifiedItemSpec = info.LastWriteTime.ToString(FileTimeFormat, null); } else { // File does not exist, or path is a directory modifiedItemSpec = String.Empty; } } else if (String.Compare(modifier, FileUtilities.ItemSpecModifiers.CreatedTime, StringComparison.OrdinalIgnoreCase) == 0) { // About to go out to the filesystem. This means data is leaving the engine, so need // to unescape first. string unescapedItemSpec = EscapingUtilities.UnescapeAll(itemSpec); if (File.Exists(unescapedItemSpec)) { modifiedItemSpec = File.GetCreationTime(unescapedItemSpec).ToString(FileTimeFormat, null); } else { // File does not exist, or path is a directory modifiedItemSpec = String.Empty; } } else if (String.Compare(modifier, FileUtilities.ItemSpecModifiers.AccessedTime, StringComparison.OrdinalIgnoreCase) == 0) { // About to go out to the filesystem. This means data is leaving the engine, so need // to unescape first. string unescapedItemSpec = EscapingUtilities.UnescapeAll(itemSpec); if (File.Exists(unescapedItemSpec)) { modifiedItemSpec = File.GetLastAccessTime(unescapedItemSpec).ToString(FileTimeFormat, null); } else { // File does not exist, or path is a directory modifiedItemSpec = String.Empty; } } else if (IsDefiningProjectModifier(modifier)) { if (String.IsNullOrEmpty(definingProjectEscaped)) { // We have nothing to work with, but that's sometimes OK -- so just return String.Empty modifiedItemSpec = String.Empty; } else { if (String.Compare(modifier, FileUtilities.ItemSpecModifiers.DefiningProjectDirectory, StringComparison.OrdinalIgnoreCase) == 0) { // ItemSpecModifiers.Directory does not contain the root directory modifiedItemSpec = Path.Combine ( GetItemSpecModifier(currentDirectory, definingProjectEscaped, null, ItemSpecModifiers.RootDir), GetItemSpecModifier(currentDirectory, definingProjectEscaped, null, ItemSpecModifiers.Directory) ); } else { string additionalModifier = null; if (String.Compare(modifier, FileUtilities.ItemSpecModifiers.DefiningProjectFullPath, StringComparison.OrdinalIgnoreCase) == 0) { additionalModifier = ItemSpecModifiers.FullPath; } else if (String.Compare(modifier, FileUtilities.ItemSpecModifiers.DefiningProjectName, StringComparison.OrdinalIgnoreCase) == 0) { additionalModifier = ItemSpecModifiers.Filename; } else if (String.Compare(modifier, FileUtilities.ItemSpecModifiers.DefiningProjectExtension, StringComparison.OrdinalIgnoreCase) == 0) { additionalModifier = ItemSpecModifiers.Extension; } else { ErrorUtilities.ThrowInternalError("\"{0}\" is not a valid item-spec modifier.", modifier); } modifiedItemSpec = GetItemSpecModifier(currentDirectory, definingProjectEscaped, null, additionalModifier); } } } else { ErrorUtilities.ThrowInternalError("\"{0}\" is not a valid item-spec modifier.", modifier); } } catch (Exception e) when(ExceptionHandling.IsIoRelatedException(e)) { ErrorUtilities.VerifyThrowInvalidOperation(false, "Shared.InvalidFilespecForTransform", modifier, itemSpec, e.Message); } return(modifiedItemSpec); }
/// <summary> /// Creates an instance of this class using the information in the given XmlException and file location. /// </summary> internal BuildEventFileInfo(string file, XmlException e) : this(e) { ErrorUtilities.VerifyThrowArgumentNull(file, nameof(file)); _file = file; }
/// <summary> /// Constructor /// </summary> internal AssemblyLoadInfoWithFile(string assemblyFile) { ErrorUtilities.VerifyThrow(Path.IsPathRooted(assemblyFile), "Assembly file path should be rooted"); _assemblyFile = assemblyFile; }