...

Source file src/edge-infra.dev/hack/build/rules/gazelle/constants/resolve.go

Documentation: edge-infra.dev/hack/build/rules/gazelle/constants

     1  package constants
     2  
     3  import (
     4  	"fmt"
     5  	"path/filepath"
     6  	"slices"
     7  	"strings"
     8  
     9  	"github.com/bazelbuild/bazel-gazelle/language"
    10  	"github.com/bazelbuild/buildtools/build"
    11  	bzlFile "github.com/bazelbuild/buildtools/file"
    12  )
    13  
    14  type BazelConstMap map[string]string
    15  
    16  type constantSource struct {
    17  	Module    string
    18  	Filepath  string
    19  	Constants []string
    20  }
    21  
    22  func (cs constantSource) LoadBazelConstMap(cm BazelConstMap) (BazelConstMap, error) {
    23  	moduleFileBytes, _, err := bzlFile.ReadFile(cs.Filepath)
    24  	if err != nil {
    25  		return cm, fmt.Errorf("error reading loaded file %s: err: %w", cs.Filepath, err)
    26  	}
    27  
    28  	moduleBzlFile, err := build.ParseBzl(cs.Filepath, moduleFileBytes)
    29  	if err != nil {
    30  		return cm, fmt.Errorf("error parsing file %s: %w", cs.Filepath, err)
    31  	}
    32  
    33  	for _, stmt := range moduleBzlFile.Stmt {
    34  		if assignStmt, isAssign := stmt.(*build.AssignExpr); isAssign {
    35  			var constName string
    36  			var constValue string
    37  			if ident, ok := assignStmt.LHS.(*build.Ident); ok {
    38  				if !slices.Contains(cs.Constants, ident.Name) {
    39  					continue
    40  				}
    41  				constName = ident.Name
    42  			}
    43  
    44  			// Resolve values
    45  			if val, ok := assignStmt.RHS.(*build.StringExpr); ok {
    46  				constValue = val.Value
    47  			}
    48  
    49  			// ignore all other attr types besides StringExpr for now
    50  			cm[constName] = constValue
    51  		}
    52  	}
    53  	return cm, nil
    54  }
    55  
    56  // ResolveConstants resolves values from the current file as well as from load statements
    57  // in the file
    58  func ResolveConstants(args language.GenerateArgs, f *build.File, constMap BazelConstMap) (BazelConstMap, error) {
    59  	constantSources := []constantSource{}
    60  	for _, s := range f.Stmt {
    61  		switch stmt := s.(type) {
    62  		case *build.LoadStmt:
    63  			cs := constantSource{
    64  				Module:   stmt.Module.Value,
    65  				Filepath: labelToPath(args, stmt.Module.Value),
    66  			}
    67  			for _, from := range stmt.From {
    68  				if from.Name != strings.ToUpper(from.Name) {
    69  					// Not a constant
    70  					continue
    71  				}
    72  				cs.Constants = append(cs.Constants, from.Name)
    73  			}
    74  			if len(cs.Constants) == 0 {
    75  				// No constants to resolve, don't add to resolve list
    76  				continue
    77  			}
    78  			constantSources = append(constantSources, cs)
    79  		case *build.AssignExpr:
    80  			// Only handle string constants for now
    81  			constName, constValue, canAssign := assignExprVals(stmt)
    82  			if canAssign {
    83  				constMap[constName] = constValue
    84  			}
    85  		}
    86  	}
    87  
    88  	for _, constSource := range constantSources {
    89  		constMap, err := constSource.LoadBazelConstMap(constMap)
    90  		if err != nil {
    91  			return constMap, err
    92  		}
    93  	}
    94  
    95  	return constMap, nil
    96  }
    97  
    98  func ResolveAttr(rule *build.Rule, key string, constMap BazelConstMap) string {
    99  	attr := rule.Attr(key)
   100  	if reg, ok := attr.(*build.Ident); ok {
   101  		return constMap[reg.Name]
   102  	}
   103  	if reg, ok := attr.(*build.StringExpr); ok {
   104  		return reg.Value
   105  	}
   106  	return ""
   107  }
   108  
   109  func labelToPath(args language.GenerateArgs, label string) string {
   110  	if strings.HasPrefix(label, ":") {
   111  		label = strings.TrimPrefix(label, ":")
   112  		return filepath.Join(args.Dir, label)
   113  	}
   114  	label = strings.TrimPrefix(label, "//")
   115  	label = strings.ReplaceAll(label, ":", "/")
   116  	return filepath.Join(args.Config.RepoRoot, label)
   117  }
   118  
   119  // assignExprVals extracts the key and value from an assignment
   120  // func assignExprVals(e build.Expr) (string, string) {
   121  func assignExprVals(assign *build.AssignExpr) (string, string, bool) {
   122  	var constName string
   123  	var constValue string
   124  	switch ident := assign.LHS.(type) {
   125  	case *build.Ident:
   126  		constName = ident.Name
   127  	default:
   128  		// only try ident for now
   129  		return "", "", false
   130  	}
   131  
   132  	// Resolve values
   133  	switch val := assign.RHS.(type) {
   134  	case *build.StringExpr:
   135  		constValue = val.Value
   136  		return constName, constValue, true
   137  	default:
   138  		// only load strings for now
   139  		return "", "", false
   140  	}
   141  }
   142  

View as plain text