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  }