/// <summary> /// Add a new folder build item to the project /// </summary> /// <param name="folder">The folder name</param> /// <returns>The new <see cref="FileItem"/>.</returns> /// <remarks>If the folder does not exist in the project, it is added and created if not already there. /// If the folder is already part of the project, the existing item is returned.</remarks> /// <exception cref="ArgumentException">This is thrown if the path matches the project root path or is /// not below it.</exception> public FileItem AddFolderToProject(string folder) { FolderPath folderPath; FileItem newFileItem = null; string folderAction = BuildAction.Folder.ToString(), rootPath = Path.GetDirectoryName(msBuildProject.FullPath); if(folder.Length != 0 && folder[folder.Length - 1] == '\\') folder = folder.Substring(0, folder.Length - 1); if(!Path.IsPathRooted(folder)) folder = Path.GetFullPath(Path.Combine(rootPath, folder)); if(String.Compare(folder, 0, rootPath, 0, rootPath.Length, StringComparison.OrdinalIgnoreCase) != 0) throw new ArgumentException("The folder must be below the project's root path", "folder"); if(folder.Length == rootPath.Length) throw new ArgumentException("The folder cannot match the project's root path", "folder"); folderPath = new FolderPath(folder, this); // Note that Visual Studio doesn't add the trailing backslash so look for a match with and without it. // Folders don't always have a relative path in the item when first added. As such, check both the // relative and full paths for a match. foreach(ProjectItem item in msBuildProject.GetItems(folderAction)) if(item.EvaluatedInclude == folderPath.PersistablePath || item.EvaluatedInclude + @"\" == folderPath.PersistablePath || item.EvaluatedInclude == folderPath.Path || item.EvaluatedInclude + @"\" == folderPath.Path) { newFileItem = new FileItem(this, item); break; } if(!Directory.Exists(folderPath)) Directory.CreateDirectory(folderPath); if(newFileItem == null) newFileItem = new FileItem(this, folderAction, folder); return newFileItem; }
/// <summary> /// This is used to set the named property to the specified value using Reflection /// </summary> /// <param name="name">The name of the property to set</param> /// <param name="value">The value to which it is set</param> /// <remarks>Property name matching is case insensitive as are the values. This is used to allow setting /// of simple project properties (non-collection) from the MSBuild project file. Unknown properties are /// ignored.</remarks> /// <exception cref="ArgumentNullException">This is thrown if the name parameter is null or an empty /// string.</exception> /// <exception cref="BuilderException">This is thrown if an error occurs while trying to set the named /// property.</exception> private void SetLocalProperty(string name, string value) { TypeConverter tc; EscapeValueAttribute escAttr; PropertyInfo property; FilePath filePath; object parsedValue; if(String.IsNullOrEmpty(name)) throw new ArgumentNullException("name"); // Ignore unknown properties if(!propertyCache.TryGetValue(name, out property)) return; if(!property.CanWrite || property.IsDefined(typeof(XmlIgnoreAttribute), true)) throw new BuilderException("PRJ0004", String.Format(CultureInfo.CurrentCulture, "An attempt was made to set a read-only or ignored property: {0} Value: {1}", name, value)); // If escaped, unescape it escAttr = pdcCache[name].Attributes[typeof(EscapeValueAttribute)] as EscapeValueAttribute; if(escAttr != null) value = EscapeValueAttribute.Unescape(value); try { if(property.PropertyType.IsEnum) parsedValue = Enum.Parse(property.PropertyType, value, true); else if(property.PropertyType == typeof(Version)) parsedValue = new Version(value); else { if(property.PropertyType == typeof(FilePath)) parsedValue = new FilePath(value, this); else if(property.PropertyType == typeof(FolderPath)) parsedValue = new FolderPath(value, this); else { tc = TypeDescriptor.GetConverter(property.PropertyType); parsedValue = tc.ConvertFromString(value); } // If it's a file or folder path, set the IsFixedPath property based on whether or not // it is rooted. filePath = parsedValue as FilePath; if(filePath != null && Path.IsPathRooted(value)) filePath.IsFixedPath = true; } } catch(Exception ex) { // Ignore exceptions for the Language property. A few people have had an environment variable // with that name that gets picked up as a default and the value isn't typically valid for a // culture name. if(!name.Equals("Language", StringComparison.OrdinalIgnoreCase)) throw new BuilderException("PRJ0005", "Unable to parse value '" + value + "' for property '" + name + "'", ex); parsedValue = null; } property.SetValue(this, parsedValue, null); }
/// <summary> /// This helper method can be used to convert a relative path to an absolute path based on the given base /// path. /// </summary> /// <param name="basePath">The base path</param> /// <param name="relativePath">A relative path</param> /// <returns>An absolute path</returns> /// <remarks>If the base path is null or empty, the current working folder is used.</remarks> /// <example> /// <code lang="cs"> /// string basePath = @"E:\DotNet\CS\TestProject\Source"; /// string relativePath = @"..\Doc\Help.html"; /// /// string absolutePath = FilePath.RelativeToAbsolutePath(basePath, /// relativePath); /// /// Console.WriteLine(absolutePath); /// /// // Results in: E:\DotNet\CS\TestProject\Doc\Help.html /// </code> /// <code lang="vbnet"> /// Dim basePath As String = "E:\DotNet\CS\TestProject\Source" /// Dim relativePath As String = "..\Doc\Help.html" /// /// Dim absolutePath As String = _ /// FilePath.RelativeToAbsolutePath(basePath, relativePath); /// /// Console.WriteLine(absolutePath) /// /// ' Results in: E:\DotNet\CS\TestProject\Doc\Help.html /// </code> /// </example> public static string RelativeToAbsolutePath(string basePath, string relativePath) { int idx; // If blank return the base path if (String.IsNullOrEmpty(relativePath)) { return(basePath); } // Don't bother if already absolute if (IOPath.IsPathRooted(relativePath)) { return(relativePath); } // If not specified, use the current folder as the base path if (basePath == null || basePath.Trim().Length == 0) { basePath = Directory.GetCurrentDirectory(); } else { basePath = IOPath.GetFullPath(basePath); } // Remove trailing backslashes for comparison if (FolderPath.IsPathTerminated(basePath)) { basePath = basePath.Substring(0, basePath.Length - 1); } if (relativePath == ".") { relativePath = String.Empty; } // Remove ".\" or "./" if it's there if (relativePath.Length > 1 && relativePath[0] == '.' && (relativePath[1] == IOPath.DirectorySeparatorChar || relativePath[1] == IOPath.AltDirectorySeparatorChar)) { relativePath = relativePath.Substring(2); } // Split the paths into their component parts string[] baseParts = basePath.Split(IOPath.DirectorySeparatorChar); string[] relParts = relativePath.Split(IOPath.DirectorySeparatorChar); // Figure out how far to move up from the relative path for (idx = 0; idx < relParts.Length; ++idx) { if (relParts[idx] != "..") { break; } } // If it's below the base path, just add it to the base path if (idx == 0) { return(FilePath.GetFullPath(basePath + IOPath.DirectorySeparatorChar + relativePath)); } string absPath = String.Join(IOPath.DirectorySeparatorChar.ToString(), baseParts, 0, Math.Max(0, baseParts.Length - idx)); absPath += IOPath.DirectorySeparatorChar + String.Join(IOPath.DirectorySeparatorChar.ToString(), relParts, idx, relParts.Length - idx); return(FilePath.GetFullPath(absPath)); }
/// <summary> /// This helper method can be used to convert an absolute path to one that is relative to the given base /// path. /// </summary> /// <param name="basePath">The base path</param> /// <param name="absolutePath">An absolute path</param> /// <returns>A path to the given absolute path that is relative to the given base path</returns> /// <remarks>If the base path is null or empty, the current working folder is used.</remarks> /// <example> /// <code lang="cs"> /// string basePath = @"E:\DotNet\CS\TestProject\Source"; /// string absolutePath = @"E:\DotNet\CS\TestProject\Doc\Help.html"; /// /// string relativePath = FilePath.AbsoluteToRelativePath(basePath, /// absolutePath); /// /// Console.WriteLine(relativePath); /// /// // Results in: ..\Doc\Help.html /// </code> /// <code lang="vbnet"> /// Dim basePath As String = "E:\DotNet\CS\TestProject\Source" /// Dim absolutePath As String = "E:\DotNet\CS\TestProject\Doc\Help.html" /// /// Dim relativePath As String = _ /// FilePath.AbsoluteToRelativePath(basePath, absolutePath); /// /// Console.WriteLine(relativePath) /// /// ' Results in: ..\Doc\Help.html /// </code> /// </example> public static string AbsoluteToRelativePath(string basePath, string absolutePath) { bool hasBackslash = false; string relPath; int minLength, idx; // If not specified, use the current folder as the base path if (basePath == null || basePath.Trim().Length == 0) { basePath = Directory.GetCurrentDirectory(); } else { basePath = IOPath.GetFullPath(basePath); } if (absolutePath == null) { absolutePath = String.Empty; } // Just in case, make sure the path is absolute if (!IOPath.IsPathRooted(absolutePath)) { absolutePath = FilePath.GetFullPath(absolutePath); } // Remove trailing backslashes for comparison if (FolderPath.IsPathTerminated(basePath)) { basePath = basePath.Substring(0, basePath.Length - 1); } if (FolderPath.IsPathTerminated(absolutePath)) { absolutePath = absolutePath.Substring(0, absolutePath.Length - 1); hasBackslash = true; } // Split the paths into their component parts char[] separators = { IOPath.DirectorySeparatorChar, IOPath.AltDirectorySeparatorChar, IOPath.VolumeSeparatorChar }; string[] baseParts = basePath.Split(separators); string[] absParts = absolutePath.Split(separators); // Find the common base path minLength = Math.Min(baseParts.Length, absParts.Length); for (idx = 0; idx < minLength; idx++) { if (String.Compare(baseParts[idx], absParts[idx], StringComparison.OrdinalIgnoreCase) != 0) { break; } } // Use the absolute path if there's nothing in common (i.e. they are on different drives or network // shares. if (idx == 0) { relPath = absolutePath; } else { // If equal to the base path, it doesn't have to go anywhere. Otherwise, work up from the base // path to the common root. if (idx == baseParts.Length) { relPath = String.Empty; } else { relPath = new String(' ', baseParts.Length - idx).Replace(" ", ".." + IOPath.DirectorySeparatorChar); } // And finally, add the path from the common root to the absolute path relPath += String.Join(IOPath.DirectorySeparatorChar.ToString(), absParts, idx, absParts.Length - idx); } return((hasBackslash) ? FolderPath.TerminatePath(relPath) : relPath); }
/// <summary> /// This is used to set the named property to the specified value /// using Reflection. /// </summary> /// <param name="name">The name of the property to set</param> /// <param name="value">The value to which it is set</param> /// <returns>The parsed object value to which the property was set.</returns> /// <remarks>Property name matching is case insensitive as are the /// values. This is used to allow setting of simple project properties /// (non-collection) from the MSBuild project file. Unknown properties /// are ignored.</remarks> /// <exception cref="ArgumentNullException">This is thrown if the /// name parameter is null or an empty string.</exception> /// <exception cref="BuilderException">This is thrown if an error /// occurs while trying to set the named property.</exception> private void SetLocalProperty(string name, string value) { TypeConverter tc; EscapeValueAttribute escAttr; PropertyInfo property; FilePath filePath; object parsedValue; if(String.IsNullOrEmpty(name)) throw new ArgumentNullException("name"); // Ignore unknown properties if(!propertyCache.TryGetValue(name, out property)) { property = null; // Could be mismatched by case, so try again the long way foreach(string key in propertyCache.Keys) if(String.Compare(key, name, StringComparison.OrdinalIgnoreCase) == 0) { name = key; property = propertyCache[name]; break; } if(property == null) return; } if(!property.CanWrite || property.IsDefined( typeof(XmlIgnoreAttribute), true)) throw new BuilderException("PRJ0004", String.Format( CultureInfo.InvariantCulture, "An attempt was " + "made to set a read-only or ignored property: {0}" + " Value: {1}", name, value)); // If escaped, unescape it escAttr = pdcCache[name].Attributes[typeof(EscapeValueAttribute)] as EscapeValueAttribute; if(escAttr != null) value = EscapeValueAttribute.Unescape(value); try { if(property.PropertyType.IsEnum) parsedValue = Enum.Parse(property.PropertyType, value, true); else if(property.PropertyType == typeof(Version)) parsedValue = new Version(value); else { if(property.PropertyType == typeof(FilePath)) parsedValue = new FilePath(value, this); else if(property.PropertyType == typeof(FolderPath)) parsedValue = new FolderPath(value, this); else { tc = TypeDescriptor.GetConverter( property.PropertyType); parsedValue = tc.ConvertFromString(value); } // If it's a file or folder path, set the IsFixedPath // property based on whether or not it is rooted. filePath = parsedValue as FilePath; if(filePath != null && Path.IsPathRooted(value)) filePath.IsFixedPath = true; } } catch(Exception ex) { throw new BuilderException("PRJ0005", "Unable to parse value '" + value + "' for property '" + name + "'", ex); } property.SetValue(this, parsedValue, null); }
/// <summary> /// Add a new folder build item to the project /// </summary> /// <param name="folder">The folder name</param> /// <returns>The new <see cref="FileItem"/>.</returns> /// <remarks>If the folder does not exist in the project, it is added /// and created if not already there. If the folder is already part of /// the project, the existing item is returned.</remarks> /// <exception cref="ArgumentException">This is thrown if the path /// matches the project root path or is not below it.</exception> public FileItem AddFolderToProject(string folder) { FolderPath folderPath; FileItem newFileItem = null; string folderAction = BuildAction.Folder.ToString(), rootPath = Path.GetDirectoryName(msBuildProject.FullFileName); if(folder.Length != 0 && folder[folder.Length - 1] == '\\') folder = folder.Substring(0, folder.Length - 1); if(!Path.IsPathRooted(folder)) folder = Path.GetFullPath(Path.Combine(rootPath, folder)); if(String.Compare(folder, 0, rootPath, 0, rootPath.Length, StringComparison.OrdinalIgnoreCase) != 0) throw new ArgumentException("The folder must be below the " + "project's root path", "folder"); if(folder.Length == rootPath.Length) throw new ArgumentException("The folder cannot match the " + "project's root path", "folder"); folderPath = new FolderPath(folder, this); foreach(BuildItem item in msBuildProject.GetEvaluatedItemsByName(folderAction)) if(item.Include == folderPath.PersistablePath) { newFileItem = new FileItem(new ProjectElement(this, item)); break; } if(!Directory.Exists(folderPath)) Directory.CreateDirectory(folderPath); if(newFileItem == null) newFileItem = new FileItem(new ProjectElement(this, folderAction, folder)); return newFileItem; }