/// <summary> /// /// This method processes a "Project" section in the solution file opened by the specified /// StreamReader, and returns a populated ProjectInSolution instance, if successful. /// An exception is thrown if the solution file is invalid. /// /// The format of the parts of a Project section that we care about is as follows: /// /// Project("{Project type GUID}") = "Project name", "Relative path to project file", "{Project GUID}" /// ProjectSection(ProjectDependencies) = postProject /// {Parent project unique name} = {Parent project unique name} /// ... /// EndProjectSection /// EndProject /// /// </summary> /// <param name="firstLine"></param> /// <returns></returns> private void ParseProject(string firstLine) { ErrorUtilities.VerifyThrow((firstLine != null) && (firstLine.Length != 0), "ParseProject() got a null firstLine!"); ErrorUtilities.VerifyThrow(_reader != null, "ParseProject() got a null reader!"); ProjectInSolution proj = new ProjectInSolution(this); // Extract the important information from the first line. ParseFirstProjectLine(firstLine, proj); // Search for project dependencies. Keeping reading lines until we either 1.) reach // the end of the file, 2.) see "ProjectSection(ProjectDependencies)" at the beginning // of the line, or 3.) see "EndProject" at the beginning of the line. string line; while ((line = ReadLine()) != null) { // If we see an "EndProject", well ... that's the end of this project! if (line == "EndProject") { break; } else if (line.StartsWith("ProjectSection(ProjectDependencies)", StringComparison.Ordinal)) { // We have a ProjectDependencies section. Each subsequent line should identify // a dependency. line = ReadLine(); while ((line != null) && (!line.StartsWith("EndProjectSection", StringComparison.Ordinal))) { // This should be a dependency. The GUID identifying the parent project should // be both the property name and the property value. Match match = s_crackPropertyLine.Value.Match(line); ProjectFileErrorUtilities.VerifyThrowInvalidProjectFile(match.Success, "SubCategoryForSolutionParsingErrors", new BuildEventFileInfo(FullPath, _currentLineNumber, 0), "SolutionParseProjectDepGuidError", proj.ProjectName); string parentGuid = match.Groups["PROPERTYNAME"].Value.Trim(); proj.AddDependency(parentGuid); line = ReadLine(); } } else if (line.StartsWith("ProjectSection(WebsiteProperties)", StringComparison.Ordinal)) { // We have a WebsiteProperties section. This section is present only in Venus // projects, and contains properties that we'll need in order to call the // AspNetCompiler task. line = ReadLine(); while ((line != null) && (!line.StartsWith("EndProjectSection", StringComparison.Ordinal))) { Match match = s_crackPropertyLine.Value.Match(line); ProjectFileErrorUtilities.VerifyThrowInvalidProjectFile(match.Success, "SubCategoryForSolutionParsingErrors", new BuildEventFileInfo(FullPath, _currentLineNumber, 0), "SolutionParseWebProjectPropertiesError", proj.ProjectName); string propertyName = match.Groups["PROPERTYNAME"].Value.Trim(); string propertyValue = match.Groups["PROPERTYVALUE"].Value.Trim(); ParseAspNetCompilerProperty(proj, propertyName, propertyValue); line = ReadLine(); } } } ProjectFileErrorUtilities.VerifyThrowInvalidProjectFile(line != null, "SubCategoryForSolutionParsingErrors", new BuildEventFileInfo(FullPath), "SolutionParseProjectEofError", proj.ProjectName); // Add the project to the collection AddProjectToSolution(proj); // If the project is an etp project then parse the etp project file // to get the projects contained in it. if (IsEtpProjectFile(proj.RelativePath)) { ParseEtpProject(proj); } } // ParseProject()
/// <summary> /// Adds a dependency to the project based on the specified guid string. /// </summary> /// <remarks> /// If the string is null or empty, no dependency is added and this is not considered an error. /// </remarks> private void AddDependencyByGuid(ProjectInSolution project, string dependencyGuid) { if (!String.IsNullOrEmpty(dependencyGuid)) { if (_solutionFile.ProjectsByGuid.ContainsKey(dependencyGuid)) { project.AddDependency(dependencyGuid); } else { _loggingService.LogWarning ( _projectBuildEventContext, "SubCategoryForSolutionParsingErrors", new BuildEventFileInfo(_solutionFile.FullPath), "SolutionParseProjectDepNotFoundError", project.ProjectGuid, dependencyGuid ); } } }
/// <summary> /// Takes a property name / value that comes from the SLN file for a Venus project, and /// stores it appropriately in our data structures. /// </summary> /// <param name="proj"></param> /// <param name="propertyName"></param> /// <param name="propertyValue"></param> private void ParseAspNetCompilerProperty ( ProjectInSolution proj, string propertyName, string propertyValue ) { // What we expect to find in the SLN file is something that looks like this: // // Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "c:\...\myfirstwebsite\", "..\..\..\..\..\..\rajeev\temp\websites\myfirstwebsite", "{956CC04E-FD59-49A9-9099-96888CB6F366}" // ProjectSection(WebsiteProperties) = preProject // TargetFrameworkMoniker = ".NETFramework,Version%3Dv4.0" // ProjectReferences = "{FD705688-88D1-4C22-9BFF-86235D89C2FC}|CSClassLibrary1.dll;{F0726D09-042B-4A7A-8A01-6BED2422BD5D}|VCClassLibrary1.dll;" // Debug.AspNetCompiler.VirtualPath = "/publishfirst" // Debug.AspNetCompiler.PhysicalPath = "..\..\..\..\..\..\rajeev\temp\websites\myfirstwebsite\" // Debug.AspNetCompiler.TargetPath = "..\..\..\..\..\..\rajeev\temp\publishfirst\" // Debug.AspNetCompiler.ForceOverwrite = "true" // Debug.AspNetCompiler.Updateable = "true" // Debug.AspNetCompiler.Enabled = "true" // Debug.AspNetCompiler.Debug = "true" // Debug.AspNetCompiler.KeyFile = "" // Debug.AspNetCompiler.KeyContainer = "" // Debug.AspNetCompiler.DelaySign = "true" // Debug.AspNetCompiler.AllowPartiallyTrustedCallers = "true" // Debug.AspNetCompiler.FixedNames = "true" // Release.AspNetCompiler.VirtualPath = "/publishfirst" // Release.AspNetCompiler.PhysicalPath = "..\..\..\..\..\..\rajeev\temp\websites\myfirstwebsite\" // Release.AspNetCompiler.TargetPath = "..\..\..\..\..\..\rajeev\temp\publishfirst\" // Release.AspNetCompiler.ForceOverwrite = "true" // Release.AspNetCompiler.Updateable = "true" // Release.AspNetCompiler.Enabled = "true" // Release.AspNetCompiler.Debug = "false" // Release.AspNetCompiler.KeyFile = "" // Release.AspNetCompiler.KeyContainer = "" // Release.AspNetCompiler.DelaySign = "true" // Release.AspNetCompiler.AllowPartiallyTrustedCallers = "true" // Release.AspNetCompiler.FixedNames = "true" // EndProjectSection // EndProject // // This method is responsible for parsing each of the lines within the "WebsiteProperties" section. // The first component of each property name is actually the configuration for which that // property applies. int indexOfFirstDot = propertyName.IndexOf('.'); if (indexOfFirstDot != -1) { // The portion before the first dot is the configuration name. string configurationName = propertyName.Substring(0, indexOfFirstDot); // The rest of it is the actual property name. string aspNetPropertyName = ((propertyName.Length - indexOfFirstDot) > 0) ? propertyName.Substring(indexOfFirstDot + 1, propertyName.Length - indexOfFirstDot - 1) : ""; // And the part after the <equals> sign is the property value (which was parsed out for us prior // to calling this method). propertyValue = TrimQuotes(propertyValue); // Grab the parameters for this specific configuration if they exist. object aspNetCompilerParametersObject = proj.AspNetConfigurations[configurationName]; AspNetCompilerParameters aspNetCompilerParameters; if (aspNetCompilerParametersObject == null) { // If it didn't exist, create a new one. aspNetCompilerParameters = new AspNetCompilerParameters(); aspNetCompilerParameters.aspNetVirtualPath = String.Empty; aspNetCompilerParameters.aspNetPhysicalPath = String.Empty; aspNetCompilerParameters.aspNetTargetPath = String.Empty; aspNetCompilerParameters.aspNetForce = String.Empty; aspNetCompilerParameters.aspNetUpdateable = String.Empty; aspNetCompilerParameters.aspNetDebug = String.Empty; aspNetCompilerParameters.aspNetKeyFile = String.Empty; aspNetCompilerParameters.aspNetKeyContainer = String.Empty; aspNetCompilerParameters.aspNetDelaySign = String.Empty; aspNetCompilerParameters.aspNetAPTCA = String.Empty; aspNetCompilerParameters.aspNetFixedNames = String.Empty; } else { // Otherwise just unbox it. aspNetCompilerParameters = (AspNetCompilerParameters)aspNetCompilerParametersObject; } // Update the appropriate field within the parameters struct. if (aspNetPropertyName == "AspNetCompiler.VirtualPath") { aspNetCompilerParameters.aspNetVirtualPath = propertyValue; } else if (aspNetPropertyName == "AspNetCompiler.PhysicalPath") { aspNetCompilerParameters.aspNetPhysicalPath = propertyValue; } else if (aspNetPropertyName == "AspNetCompiler.TargetPath") { aspNetCompilerParameters.aspNetTargetPath = propertyValue; } else if (aspNetPropertyName == "AspNetCompiler.ForceOverwrite") { aspNetCompilerParameters.aspNetForce = propertyValue; } else if (aspNetPropertyName == "AspNetCompiler.Updateable") { aspNetCompilerParameters.aspNetUpdateable = propertyValue; } else if (aspNetPropertyName == "AspNetCompiler.Debug") { aspNetCompilerParameters.aspNetDebug = propertyValue; } else if (aspNetPropertyName == "AspNetCompiler.KeyFile") { aspNetCompilerParameters.aspNetKeyFile = propertyValue; } else if (aspNetPropertyName == "AspNetCompiler.KeyContainer") { aspNetCompilerParameters.aspNetKeyContainer = propertyValue; } else if (aspNetPropertyName == "AspNetCompiler.DelaySign") { aspNetCompilerParameters.aspNetDelaySign = propertyValue; } else if (aspNetPropertyName == "AspNetCompiler.AllowPartiallyTrustedCallers") { aspNetCompilerParameters.aspNetAPTCA = propertyValue; } else if (aspNetPropertyName == "AspNetCompiler.FixedNames") { aspNetCompilerParameters.aspNetFixedNames = propertyValue; } // Store the updated parameters struct back into the hashtable by configuration name. proj.AspNetConfigurations[configurationName] = aspNetCompilerParameters; } else { // ProjectReferences = "{FD705688-88D1-4C22-9BFF-86235D89C2FC}|CSClassLibrary1.dll;{F0726D09-042B-4A7A-8A01-6BED2422BD5D}|VCClassLibrary1.dll;" if (string.Compare(propertyName, "ProjectReferences", StringComparison.OrdinalIgnoreCase) == 0) { string[] projectReferenceEntries = propertyValue.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); foreach (string projectReferenceEntry in projectReferenceEntries) { int indexOfBar = projectReferenceEntry.IndexOf('|'); // indexOfBar could be -1 if we had semicolons in the file names, so skip entries that // don't contain a guid. File names may not contain the '|' character if (indexOfBar != -1) { int indexOfOpeningBrace = projectReferenceEntry.IndexOf('{'); if (indexOfOpeningBrace != -1) { int indexOfClosingBrace = projectReferenceEntry.IndexOf('}', indexOfOpeningBrace); if (indexOfClosingBrace != -1) { string referencedProjectGuid = projectReferenceEntry.Substring(indexOfOpeningBrace, indexOfClosingBrace - indexOfOpeningBrace + 1); proj.AddDependency(referencedProjectGuid); proj.ProjectReferences.Add(referencedProjectGuid); } } } } } else if (String.Compare(propertyName, "TargetFrameworkMoniker", StringComparison.OrdinalIgnoreCase) == 0) { //Website project need to back support 3.5 msbuild parser for the Blend (it is not move to .Net4.0 yet.) //However, 3.5 version of Solution parser can't handle a equal sign in the value. //The "=" in targetframeworkMoniker was escaped to "%3D" for Orcas string targetFrameworkMoniker = TrimQuotes(propertyValue); proj.TargetFrameworkMoniker = Microsoft.Build.Shared.EscapingUtilities.UnescapeAll(targetFrameworkMoniker); } } }