function ConvertFrom-SlnFile { <# .SYNOPSIS Reads in a solution file and returns a useful output object #> [CmdletBinding()] [OutputType([object])] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] $Path ) $logLead = (Get-LogLeadName) if (!(Test-Path -Path $Path)) { Write-Warning "$logLead : Can not find the file specified at [$Path]. Please verify it exists and is accessible." return } if (!$Path.EndsWith('.sln')) { if ((Get-Item -Path $Path).PSIsContainer) { $paths = (Get-ChildItem -Path (Join-Path $Path "*.sln" -Recurse)) $pathsCount = $paths.Count if ($pathsCount -gt 0) { $Path = $paths[0] if ($pathsCount -eq 1) { Write-Host "$logLead : Found the following file under the specified folder. Using this file [$Path]." } else { Write-Host "$logLead : Found $($pathsCount) paths in subfolder. Will process only the first record at [$Path]." } } else { Write-Warning "$logLead : Found no sln folders in file. Can not do anything. Returning null." return $null } } else { Write-Warning "$logLead : Path [$Path] does not end with [.sln] and is not a folder, may not parse properly" } } $solutionPath = (Split-Path -Path $Path -Parent) $lines = (Get-Content -Path $Path) if ($lines.Count -eq 0) { Write-Error "$logLead : No contents found in [$Path]. Can not continue." return $null } $solutionFile = @{ Projects = @(); } $parsingProject = $false $parsingProjectSection = $false $parsingGlobal = $false $currentProject = @{ Name = ''; Type = ''; Path = ''; Guid = ''; } foreach ($line in $lines) { $line = $line.Trim() if ($line.StartsWith('#') -or [string]::IsNullOrWhiteSpace($line)) { # Skip the comment and blank lines, we don't care } elseif ($line.StartsWith("Project(")) { # Parse project line $parsingProject = $true # example line # Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Alkami.Admin.FicoScore", "Alkami.Admin.FicoScore\Alkami.Admin.FicoScore.csproj", "{E1475E57-C4FB-401E-9404-1B48BD473C5C}" # Project(clsid) = projectName, projectPath (from solution root), projectGuid $splits = $line -split ',' if ($splits.Count -ne 3) { Write-Warning "$logLead : An unexpected (found: $($splits.Count), expected: 3) number of segments were found for this line [$line]. Expected format: [Project(clsid) = projectName, projectPath, projectGuid]. Will attempt to process." } $projectClsidAndName = $splits[0].Trim().Replace('"','').Replace('{','').Replace('}','') $projectRelativePath = $splits[1].Trim().Replace('"','') $projectGuid = $splits[2].Trim().Replace('"','').Replace('{','').Replace('}','') $projectClsidAndNameSplits = $projectClsidAndName -split('=') $projectClsid = $projectClsidAndNameSplits[0].Trim().Replace('Project(','').Replace(')','') $projectName = $projectClsidAndNameSplits[1].Trim() $projectType = (Get-DotNetProjectFileTypeFromGuid -ProjectTypeGuid $projectClsid) if ($projectType -eq 'Solution Folder') { # there is no path here } else { # there is a path here $fullProjectPath = (Join-Path -Path $solutionPath -ChildPath $projectRelativePath) $currentProject.Path = $fullProjectPath } $currentProject.Name = $projectName $currentProject.Guid = $projectGuid $currentProject.Type = $projectType } elseif ($line.StartsWith("EndProject")) { # End parsing a project if (![string]::IsNullOrWhiteSpace($currentProject.Name)) { $solutionFile.Projects += $currentProject } $parsingProject = $false $currentProject = @{ Name = ''; Type = ''; Path = ''; Guid = ''; } } elseif ($line -eq "Global") { # Start parsing global configuration data $parsingGlobal = $true } elseif ($line -eq "EndGlobal") { # End parsing global configuration data $parsingGlobal = $false } elseif ($line.StartsWith("VisualStudioVersion")) { # Parse global configuration data $solutionFile.VisualStudioVersion = $line.Split('=')[0].Trim() } elseif ($line.StartsWith("MinimumVisualStudioVersion")) { # Parse global configuration data $solutionFile.MinimumVisualStudioVersion = $line.Split('=')[0].Trim() } elseif ($line.StartsWith("Microsoft Visual Studio Solution File")) { # Parse global configuration data $solutionFile.SolutionFileVersion = $line.Split(',')[1].Trim() } elseif ($parsingProject) { # Parse project information if ($line.StartsWith('ProjectSection')) { $parsingProjectSection = $true $currentProject.ProjectFiles = @() } elseif ($line.StartsWith('EndProjectSection')) { $parsingProjectSection = $false } elseif ($parsingProjectSection) { $splits = $line -split '=' $currentProject.ProjectFiles += @{ Name = $splits[0].Trim(); Value = $splits[1].Trim(); } } else { Write-Host "$logLead : Not sure how to parse this projectSection line`r`n`t$line" } } elseif ($parsingGlobal) { # Parse global configuration data # TODO: I don't need this information right now # Still here in case I decide one day I want to capture it } else { Write-Host "$logLead : Not sure how to parse this line`r`n`t$line" } } return $solutionFile }