github.com/bitrise-io/go-xamarin@v0.0.0-20211005113058-bf60a8bae851/analyzers/solution/solution.go (about) 1 package solution 2 3 import ( 4 "bufio" 5 "fmt" 6 "path/filepath" 7 "regexp" 8 "strings" 9 10 "github.com/bitrise-io/go-utils/fileutil" 11 "github.com/bitrise-io/go-utils/pathutil" 12 "github.com/bitrise-io/go-xamarin/analyzers/project" 13 "github.com/bitrise-io/go-xamarin/constants" 14 "github.com/bitrise-io/go-xamarin/utility" 15 ) 16 17 const ( 18 solutionProjectsPattern = `Project\("{(?P<solution_id>[^"]*)}"\) = "(?P<project_name>[^"]*)", "(?P<project_path>[^"]*)", "{(?P<project_id>[^"]*)}"` 19 20 solutionConfigurationPlatformsSectionStartPattern = `GlobalSection\(SolutionConfigurationPlatforms\) = preSolution` 21 solutionConfigurationPlatformsSectionEndPattern = `EndGlobalSection` 22 solutionConfigurationPlatformPattern = `(?P<config>[^|]*)\|(?P<platform>[^|]*) = (?P<m_config>[^|]*)\|(?P<m_platform>[^|]*)` 23 24 projectConfigurationPlatformsSectionStartPattern = `GlobalSection\(ProjectConfigurationPlatforms\) = postSolution` 25 projectConfigurationPlatformsSectionEndPattern = `EndGlobalSection` 26 projectConfigurationPlatformPattern = `{(?P<project_id>.*)}.(?P<config>.*)\|(?P<platform>.*)\.Build.* = (?P<mapped_config>.*)\|(?P<mapped_platform>.*)` 27 ) 28 29 // Model ... 30 type Model struct { 31 Pth string 32 Name string 33 ID string 34 35 ConfigMap map[string]string // Internal Configuartion|Platform - External Configuartion|Platform map 36 37 ProjectMap map[string]project.Model // Project ID - Project Model map 38 } 39 40 // New ... 41 func New(pth string, loadProjects bool) (Model, error) { 42 return analyzeSolution(pth, loadProjects) 43 } 44 45 // ConfigList ... 46 func (solution Model) ConfigList() []string { 47 configList := []string{} 48 for config := range solution.ConfigMap { 49 configList = append(configList, config) 50 } 51 return configList 52 } 53 54 func analyzeSolution(pth string, analyzeProjects bool) (Model, error) { 55 absPth, err := pathutil.AbsPath(pth) 56 if err != nil { 57 return Model{}, fmt.Errorf("Failed to expand path (%s), error: %s", pth, err) 58 } 59 60 fileName := filepath.Base(absPth) 61 ext := filepath.Ext(absPth) 62 fileName = strings.TrimSuffix(fileName, ext) 63 64 solution := Model{ 65 Pth: absPth, 66 Name: fileName, 67 ConfigMap: map[string]string{}, 68 ProjectMap: map[string]project.Model{}, 69 } 70 71 isSolutionConfigurationPlatformsSection := false 72 isProjectConfigurationPlatformsSection := false 73 74 solutionDir := filepath.Dir(absPth) 75 76 content, err := fileutil.ReadStringFromFile(absPth) 77 if err != nil { 78 return Model{}, fmt.Errorf("failed to read solution (%s), error: %s", absPth, err) 79 } 80 81 scanner := bufio.NewScanner(strings.NewReader(content)) 82 for scanner.Scan() { 83 line := strings.TrimSpace(scanner.Text()) 84 85 // Projects 86 if matches := regexp.MustCompile(solutionProjectsPattern).FindStringSubmatch(line); len(matches) == 5 { 87 ID := strings.ToUpper(matches[1]) 88 projectName := matches[2] 89 projectID := strings.ToUpper(matches[4]) 90 projectRelativePth := utility.FixWindowsPath(matches[3]) 91 projectPth := filepath.Join(solutionDir, projectRelativePth) 92 93 if strings.HasSuffix(projectPth, constants.CSProjExt) || 94 strings.HasSuffix(projectPth, constants.SHProjExt) || 95 strings.HasSuffix(projectPth, constants.FSProjExt) { 96 97 project := project.Model{ 98 ID: projectID, 99 Name: projectName, 100 Pth: projectPth, 101 102 ConfigMap: map[string]string{}, 103 Configs: map[string]project.ConfigurationPlatformModel{}, 104 } 105 solution.ProjectMap[projectID] = project 106 } 107 108 solution.ID = ID 109 110 continue 111 } 112 113 // GlobalSection(SolutionConfigurationPlatforms) = preSolution 114 if isSolutionConfigurationPlatformsSection { 115 if match := regexp.MustCompile(solutionConfigurationPlatformsSectionEndPattern).FindString(line); match != "" { 116 isSolutionConfigurationPlatformsSection = false 117 continue 118 } 119 } 120 121 if match := regexp.MustCompile(solutionConfigurationPlatformsSectionStartPattern).FindString(line); match != "" { 122 isSolutionConfigurationPlatformsSection = true 123 continue 124 } 125 126 if isSolutionConfigurationPlatformsSection { 127 if matches := regexp.MustCompile(solutionConfigurationPlatformPattern).FindStringSubmatch(line); len(matches) == 5 { 128 configuration := matches[1] 129 platform := matches[2] 130 131 mappedConfiguration := matches[3] 132 mappedPlatform := matches[4] 133 134 solution.ConfigMap[utility.ToConfig(configuration, platform)] = utility.ToConfig(mappedConfiguration, mappedPlatform) 135 136 continue 137 } 138 } 139 140 // GlobalSection(ProjectConfigurationPlatforms) = postSolution 141 if isProjectConfigurationPlatformsSection { 142 if match := regexp.MustCompile(projectConfigurationPlatformsSectionEndPattern).FindString(line); match != "" { 143 isProjectConfigurationPlatformsSection = false 144 continue 145 } 146 } 147 148 if match := regexp.MustCompile(projectConfigurationPlatformsSectionStartPattern).FindString(line); match != "" { 149 isProjectConfigurationPlatformsSection = true 150 continue 151 } 152 153 if isProjectConfigurationPlatformsSection { 154 if matches := regexp.MustCompile(projectConfigurationPlatformPattern).FindStringSubmatch(line); len(matches) == 6 { 155 projectID := strings.ToUpper(matches[1]) 156 157 project, found := solution.ProjectMap[projectID] 158 if !found { 159 continue 160 } 161 162 solutionConfiguration := matches[2] 163 solutionPlatform := matches[3] 164 projectConfiguration := matches[4] 165 projectPlatform := matches[5] 166 if projectPlatform == "Any CPU" { 167 projectPlatform = "AnyCPU" 168 } 169 170 project.ConfigMap[utility.ToConfig(solutionConfiguration, solutionPlatform)] = utility.ToConfig(projectConfiguration, projectPlatform) 171 172 solution.ProjectMap[projectID] = project 173 174 continue 175 } 176 } 177 } 178 if err := scanner.Err(); err != nil { 179 return Model{}, err 180 } 181 182 if analyzeProjects { 183 projectMap := map[string]project.Model{} 184 185 for projectID, proj := range solution.ProjectMap { 186 projectDefinition, err := project.New(proj.Pth) 187 if err != nil { 188 return Model{}, fmt.Errorf("failed to analyze project (%s), error: %s", proj.Pth, err) 189 } 190 191 projectDefinition.Name = proj.Name 192 projectDefinition.Pth = proj.Pth 193 projectDefinition.ConfigMap = proj.ConfigMap 194 195 projectMap[projectID] = projectDefinition 196 } 197 198 solution.ProjectMap = projectMap 199 } 200 201 return solution, nil 202 }