...

Source file src/github.com/bazelbuild/rules_go/go/tools/gopackagesdriver/flatpackage.go

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

     1  // Copyright 2021 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  package main
    16  
    17  import (
    18  	"encoding/json"
    19  	"fmt"
    20  	"go/parser"
    21  	"go/token"
    22  	"os"
    23  	"strconv"
    24  	"strings"
    25  )
    26  
    27  type ResolvePkgFunc func(importPath string) string
    28  
    29  // Copy and pasted from golang.org/x/tools/go/packages
    30  type FlatPackagesError struct {
    31  	Pos  string // "file:line:col" or "file:line" or "" or "-"
    32  	Msg  string
    33  	Kind FlatPackagesErrorKind
    34  }
    35  
    36  type FlatPackagesErrorKind int
    37  
    38  const (
    39  	UnknownError FlatPackagesErrorKind = iota
    40  	ListError
    41  	ParseError
    42  	TypeError
    43  )
    44  
    45  func (err FlatPackagesError) Error() string {
    46  	pos := err.Pos
    47  	if pos == "" {
    48  		pos = "-" // like token.Position{}.String()
    49  	}
    50  	return pos + ": " + err.Msg
    51  }
    52  
    53  // FlatPackage is the JSON form of Package
    54  // It drops all the type and syntax fields, and transforms the Imports
    55  type FlatPackage struct {
    56  	ID              string
    57  	Name            string              `json:",omitempty"`
    58  	PkgPath         string              `json:",omitempty"`
    59  	Errors          []FlatPackagesError `json:",omitempty"`
    60  	GoFiles         []string            `json:",omitempty"`
    61  	CompiledGoFiles []string            `json:",omitempty"`
    62  	OtherFiles      []string            `json:",omitempty"`
    63  	ExportFile      string              `json:",omitempty"`
    64  	Imports         map[string]string   `json:",omitempty"`
    65  	Standard        bool                `json:",omitempty"`
    66  }
    67  
    68  type (
    69  	PackageFunc      func(pkg *FlatPackage)
    70  	PathResolverFunc func(path string) string
    71  )
    72  
    73  func resolvePathsInPlace(prf PathResolverFunc, paths []string) {
    74  	for i, path := range paths {
    75  		paths[i] = prf(path)
    76  	}
    77  }
    78  
    79  func WalkFlatPackagesFromJSON(jsonFile string, onPkg PackageFunc) error {
    80  	f, err := os.Open(jsonFile)
    81  	if err != nil {
    82  		return fmt.Errorf("unable to open package JSON file: %w", err)
    83  	}
    84  	defer f.Close()
    85  
    86  	decoder := json.NewDecoder(f)
    87  	for decoder.More() {
    88  		pkg := &FlatPackage{}
    89  		if err := decoder.Decode(&pkg); err != nil {
    90  			return fmt.Errorf("unable to decode package in %s: %w", f.Name(), err)
    91  		}
    92  
    93  		onPkg(pkg)
    94  	}
    95  	return nil
    96  }
    97  
    98  func (fp *FlatPackage) ResolvePaths(prf PathResolverFunc) error {
    99  	resolvePathsInPlace(prf, fp.CompiledGoFiles)
   100  	resolvePathsInPlace(prf, fp.GoFiles)
   101  	resolvePathsInPlace(prf, fp.OtherFiles)
   102  	fp.ExportFile = prf(fp.ExportFile)
   103  	return nil
   104  }
   105  
   106  // FilterFilesForBuildTags filters the source files given the current build
   107  // tags.
   108  func (fp *FlatPackage) FilterFilesForBuildTags() {
   109  	fp.GoFiles = filterSourceFilesForTags(fp.GoFiles)
   110  	fp.CompiledGoFiles = filterSourceFilesForTags(fp.CompiledGoFiles)
   111  }
   112  
   113  func (fp *FlatPackage) filterTestSuffix(files []string) (err error, testFiles []string, xTestFiles, nonTestFiles []string) {
   114  	for _, filename := range files {
   115  		if strings.HasSuffix(filename, "_test.go") {
   116  			fset := token.NewFileSet()
   117  			f, err := parser.ParseFile(fset, filename, nil, parser.PackageClauseOnly)
   118  			if err != nil {
   119  				return err, nil, nil, nil
   120  			}
   121  			if f.Name.Name == fp.Name {
   122  				testFiles = append(testFiles, filename)
   123  			} else {
   124  				xTestFiles = append(xTestFiles, filename)
   125  			}
   126  		} else {
   127  			nonTestFiles = append(nonTestFiles, filename)
   128  		}
   129  	}
   130  	return
   131  }
   132  
   133  func (fp *FlatPackage) MoveTestFiles() *FlatPackage {
   134  	err, tgf, xtgf, gf := fp.filterTestSuffix(fp.GoFiles)
   135  
   136  	if err != nil {
   137  		return nil
   138  	}
   139  	fp.GoFiles = append(gf, tgf...)
   140  	fp.CompiledGoFiles = append(gf, tgf...)
   141  
   142  	if len(xtgf) == 0 {
   143  		return nil
   144  	}
   145  
   146  	newImports := make(map[string]string, len(fp.Imports))
   147  	for k, v := range fp.Imports {
   148  		newImports[k] = v
   149  	}
   150  
   151  	newImports[fp.PkgPath] = fp.ID
   152  
   153  	// Clone package, only xtgf files
   154  	return &FlatPackage{
   155  		ID:              fp.ID + "_xtest",
   156  		Name:            fp.Name + "_test",
   157  		PkgPath:         fp.PkgPath + "_test",
   158  		Imports:         newImports,
   159  		Errors:          fp.Errors,
   160  		GoFiles:         append([]string{}, xtgf...),
   161  		CompiledGoFiles: append([]string{}, xtgf...),
   162  		OtherFiles:      fp.OtherFiles,
   163  		ExportFile:      fp.ExportFile,
   164  		Standard:        fp.Standard,
   165  	}
   166  }
   167  
   168  func (fp *FlatPackage) IsStdlib() bool {
   169  	return fp.Standard
   170  }
   171  
   172  func (fp *FlatPackage) ResolveImports(resolve ResolvePkgFunc) error {
   173  	// Stdlib packages are already complete import wise
   174  	if fp.IsStdlib() {
   175  		return nil
   176  	}
   177  
   178  	fset := token.NewFileSet()
   179  
   180  	for _, file := range fp.CompiledGoFiles {
   181  		f, err := parser.ParseFile(fset, file, nil, parser.ImportsOnly)
   182  		if err != nil {
   183  			return err
   184  		}
   185  		// If the name is not provided, fetch it from the sources
   186  		if fp.Name == "" {
   187  			fp.Name = f.Name.Name
   188  		}
   189  
   190  		for _, rawImport := range f.Imports {
   191  			imp, err := strconv.Unquote(rawImport.Path.Value)
   192  			if err != nil {
   193  				continue
   194  			}
   195  			// We don't handle CGo for now
   196  			if imp == "C" {
   197  				continue
   198  			}
   199  			if _, ok := fp.Imports[imp]; ok {
   200  				continue
   201  			}
   202  
   203  			if pkgID := resolve(imp); pkgID != "" {
   204  				fp.Imports[imp] = pkgID
   205  			}
   206  		}
   207  	}
   208  
   209  	return nil
   210  }
   211  
   212  func (fp *FlatPackage) IsRoot() bool {
   213  	return strings.HasPrefix(fp.ID, "//")
   214  }
   215  

View as plain text