github.com/sercand/please@v13.4.0+incompatible/test/go_provider/main.go (about)

     1  // Package main implements a build provider for Please that understands Go files.
     2  // This could be considered a base for such a thing; it is not complete in regard to
     3  // all the subtleties of how Go would process them, and misses a lot of features
     4  // (like useful cross-package dependencies, for example).
     5  package main
     6  
     7  import (
     8  	"encoding/json"
     9  	"go/ast"
    10  	"go/parser"
    11  	"go/token"
    12  	"os"
    13  	"path"
    14  	"strings"
    15  	"text/template"
    16  
    17  	"gopkg.in/op/go-logging.v1"
    18  )
    19  
    20  var log = logging.MustGetLogger("go_provider")
    21  
    22  type Request struct {
    23  	Rule string `json:"rule"`
    24  }
    25  
    26  type Response struct {
    27  	Rule      string   `json:"rule"`
    28  	Success   bool     `json:"success"`
    29  	Messages  []string `json:"messages"`
    30  	BuildFile string   `json:"build_file"`
    31  }
    32  
    33  var tmpl = template.Must(template.New("build").Funcs(template.FuncMap{
    34  	"filter": func(in map[string]*ast.File, test bool) []string {
    35  		ret := []string{}
    36  		for name := range in {
    37  			if strings.HasSuffix(name, "test.go") == test {
    38  				ret = append(ret, path.Base(name))
    39  			}
    40  		}
    41  		return ret
    42  	},
    43  }).Parse(`
    44  {{ range $pkgName, $pkg := . }}
    45  go_library(
    46      name = "{{ $pkgName }}",
    47      srcs = [
    48          {{ range filter $pkg.Files false }}
    49          "{{ . }}",
    50          {{ end }}
    51      ],
    52  )
    53  
    54  {{ if filter $pkg.Files true }}
    55  go_test(
    56      name = "{{ $pkgName }}_test",
    57      srcs = [
    58          {{ range filter $pkg.Files true }}
    59          "{{ . }}",
    60          {{ end }}
    61      ],
    62      deps = [":{{ $pkgName }}"],
    63  )
    64  {{ end }}
    65  {{ end }}
    66  `))
    67  
    68  func provide(ch chan<- *Response, dir string) {
    69  	contents, err := parse(dir)
    70  	resp := &Response{
    71  		Rule:      dir,
    72  		BuildFile: contents,
    73  	}
    74  	if err != nil {
    75  		resp.Messages = []string{err.Error()}
    76  	}
    77  	ch <- resp
    78  }
    79  
    80  func parse(dir string) (string, error) {
    81  	var b strings.Builder
    82  	fs := token.NewFileSet()
    83  	pkgs, err := parser.ParseDir(fs, dir, nil, parser.ImportsOnly)
    84  	if err != nil {
    85  		return "", err
    86  	} else if err := tmpl.Execute(&b, pkgs); err != nil {
    87  		return "", err
    88  	}
    89  	return b.String(), nil
    90  }
    91  
    92  func main() {
    93  	decoder := json.NewDecoder(os.Stdin)
    94  	encoder := json.NewEncoder(os.Stdout)
    95  	ch := make(chan *Response, 10)
    96  	go func() {
    97  		for resp := range ch {
    98  			if err := encoder.Encode(resp); err != nil {
    99  				log.Error("Failed to encode message: %s", err)
   100  			}
   101  		}
   102  	}()
   103  	for {
   104  		req := &Request{}
   105  		if err := decoder.Decode(req); err != nil {
   106  			log.Error("Failed to decode incoming message: %s", err)
   107  			continue
   108  		}
   109  		go provide(ch, req.Rule)
   110  	}
   111  }