...

Source file src/github.com/bazelbuild/rules_go/go/tools/builders/generate_nogo_main.go

Documentation: github.com/bazelbuild/rules_go/go/tools/builders

     1  /* Copyright 2018 The Bazel Authors. All rights reserved.
     2  
     3  Licensed under the Apache License, Version 2.0 (the "License");
     4  you may not use this file except in compliance with the License.
     5  You may obtain a copy of the License at
     6  
     7     http://www.apache.org/licenses/LICENSE-2.0
     8  
     9  Unless required by applicable law or agreed to in writing, software
    10  distributed under the License is distributed on an "AS IS" BASIS,
    11  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  See the License for the specific language governing permissions and
    13  limitations under the License.
    14  */
    15  
    16  // Generates the nogo binary to analyze Go source code at build time.
    17  
    18  package main
    19  
    20  import (
    21  	"encoding/json"
    22  	"errors"
    23  	"flag"
    24  	"fmt"
    25  	"io/ioutil"
    26  	"math"
    27  	"os"
    28  	"regexp"
    29  	"strconv"
    30  	"text/template"
    31  )
    32  
    33  const nogoMainTpl = `
    34  package main
    35  
    36  
    37  import (
    38  {{- if .NeedRegexp }}
    39  	"regexp"
    40  {{- end}}
    41  {{- range $import := .Imports}}
    42  	{{$import.Name}} "{{$import.Path}}"
    43  {{- end}}
    44  	"golang.org/x/tools/go/analysis"
    45  )
    46  
    47  var analyzers = []*analysis.Analyzer{
    48  {{- range $import := .Imports}}
    49  	{{$import.Name}}.Analyzer,
    50  {{- end}}
    51  }
    52  
    53  // configs maps analysis names to configurations.
    54  var configs = map[string]config{
    55  {{- range $name, $config := .Configs}}
    56  	{{printf "%q" $name}}: config{
    57  		{{- if $config.AnalyzerFlags }}
    58  		analyzerFlags: map[string]string {
    59  			{{- range $flagKey, $flagValue := $config.AnalyzerFlags}}
    60  			{{printf "%q: %q" $flagKey $flagValue}},
    61  			{{- end}}
    62  		},
    63  		{{- end -}}
    64  		{{- if $config.OnlyFiles}}
    65  		onlyFiles: []*regexp.Regexp{
    66  			{{- range $path, $comment := $config.OnlyFiles}}
    67  			{{- if $comment}}
    68  			// {{$comment}}
    69  			{{end -}}
    70  			{{printf "regexp.MustCompile(%q)" $path}},
    71  			{{- end}}
    72  		},
    73  		{{- end -}}
    74  		{{- if $config.ExcludeFiles}}
    75  		excludeFiles: []*regexp.Regexp{
    76  			{{- range $path, $comment := $config.ExcludeFiles}}
    77  			{{- if $comment}}
    78  			// {{$comment}}
    79  			{{end -}}
    80  			{{printf "regexp.MustCompile(%q)" $path}},
    81  			{{- end}}
    82  		},
    83  		{{- end}}
    84  	},
    85  {{- end}}
    86  }
    87  `
    88  
    89  func genNogoMain(args []string) error {
    90  	analyzerImportPaths := multiFlag{}
    91  	flags := flag.NewFlagSet("generate_nogo_main", flag.ExitOnError)
    92  	out := flags.String("output", "", "output file to write (defaults to stdout)")
    93  	flags.Var(&analyzerImportPaths, "analyzer_importpath", "import path of an analyzer library")
    94  	configFile := flags.String("config", "", "nogo config file")
    95  	if err := flags.Parse(args); err != nil {
    96  		return err
    97  	}
    98  	if *out == "" {
    99  		return errors.New("must provide output file")
   100  	}
   101  
   102  	outFile := os.Stdout
   103  	var cErr error
   104  	outFile, err := os.Create(*out)
   105  	if err != nil {
   106  		return fmt.Errorf("os.Create(%q): %v", *out, err)
   107  	}
   108  	defer func() {
   109  		if err := outFile.Close(); err != nil {
   110  			cErr = fmt.Errorf("error closing %s: %v", outFile.Name(), err)
   111  		}
   112  	}()
   113  
   114  	config, err := buildConfig(*configFile)
   115  	if err != nil {
   116  		return err
   117  	}
   118  
   119  	type Import struct {
   120  		Path, Name string
   121  	}
   122  	// Create unique name for each imported analyzer.
   123  	suffix := 1
   124  	imports := make([]Import, 0, len(analyzerImportPaths))
   125  	for _, path := range analyzerImportPaths {
   126  		imports = append(imports, Import{
   127  			Path: path,
   128  			Name: "analyzer" + strconv.Itoa(suffix)})
   129  		if suffix == math.MaxInt32 {
   130  			return fmt.Errorf("cannot generate more than %d analyzers", suffix)
   131  		}
   132  		suffix++
   133  	}
   134  	data := struct {
   135  		Imports    []Import
   136  		Configs    Configs
   137  		NeedRegexp bool
   138  	}{
   139  		Imports: imports,
   140  		Configs: config,
   141  	}
   142  	for _, c := range config {
   143  		if len(c.OnlyFiles) > 0 || len(c.ExcludeFiles) > 0 {
   144  			data.NeedRegexp = true
   145  			break
   146  		}
   147  	}
   148  
   149  	tpl := template.Must(template.New("source").Parse(nogoMainTpl))
   150  	if err := tpl.Execute(outFile, data); err != nil {
   151  		return fmt.Errorf("template.Execute failed: %v", err)
   152  	}
   153  	return cErr
   154  }
   155  
   156  func buildConfig(path string) (Configs, error) {
   157  	if path == "" {
   158  		return Configs{}, nil
   159  	}
   160  	b, err := ioutil.ReadFile(path)
   161  	if err != nil {
   162  		return Configs{}, fmt.Errorf("failed to read config file: %v", err)
   163  	}
   164  	configs := make(Configs)
   165  	if err = json.Unmarshal(b, &configs); err != nil {
   166  		return Configs{}, fmt.Errorf("failed to unmarshal config file: %v", err)
   167  	}
   168  	for name, config := range configs {
   169  		for pattern := range config.OnlyFiles {
   170  			if _, err := regexp.Compile(pattern); err != nil {
   171  				return Configs{}, fmt.Errorf("invalid pattern for analysis %q: %v", name, err)
   172  			}
   173  		}
   174  		for pattern := range config.ExcludeFiles {
   175  			if _, err := regexp.Compile(pattern); err != nil {
   176  				return Configs{}, fmt.Errorf("invalid pattern for analysis %q: %v", name, err)
   177  			}
   178  		}
   179  		configs[name] = Config{
   180  			// Description is currently unused.
   181  			OnlyFiles:     config.OnlyFiles,
   182  			ExcludeFiles:  config.ExcludeFiles,
   183  			AnalyzerFlags: config.AnalyzerFlags,
   184  		}
   185  	}
   186  	return configs, nil
   187  }
   188  
   189  type Configs map[string]Config
   190  
   191  type Config struct {
   192  	Description   string
   193  	OnlyFiles     map[string]string `json:"only_files"`
   194  	ExcludeFiles  map[string]string `json:"exclude_files"`
   195  	AnalyzerFlags map[string]string `json:"analyzer_flags"`
   196  }
   197  

View as plain text