1*9bb1b549SSpandan Das// Copyright 2021 The Bazel Authors. All rights reserved. 2*9bb1b549SSpandan Das// 3*9bb1b549SSpandan Das// Licensed under the Apache License, Version 2.0 (the "License"); 4*9bb1b549SSpandan Das// you may not use this file except in compliance with the License. 5*9bb1b549SSpandan Das// You may obtain a copy of the License at 6*9bb1b549SSpandan Das// 7*9bb1b549SSpandan Das// http://www.apache.org/licenses/LICENSE-2.0 8*9bb1b549SSpandan Das// 9*9bb1b549SSpandan Das// Unless required by applicable law or agreed to in writing, software 10*9bb1b549SSpandan Das// distributed under the License is distributed on an "AS IS" BASIS, 11*9bb1b549SSpandan Das// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*9bb1b549SSpandan Das// See the License for the specific language governing permissions and 13*9bb1b549SSpandan Das// limitations under the License. 14*9bb1b549SSpandan Das 15*9bb1b549SSpandan Daspackage main 16*9bb1b549SSpandan Das 17*9bb1b549SSpandan Dasimport ( 18*9bb1b549SSpandan Das "encoding/json" 19*9bb1b549SSpandan Das "fmt" 20*9bb1b549SSpandan Das "go/parser" 21*9bb1b549SSpandan Das "go/token" 22*9bb1b549SSpandan Das "os" 23*9bb1b549SSpandan Das "strconv" 24*9bb1b549SSpandan Das "strings" 25*9bb1b549SSpandan Das) 26*9bb1b549SSpandan Das 27*9bb1b549SSpandan Dastype ResolvePkgFunc func(importPath string) string 28*9bb1b549SSpandan Das 29*9bb1b549SSpandan Das// Copy and pasted from golang.org/x/tools/go/packages 30*9bb1b549SSpandan Dastype FlatPackagesError struct { 31*9bb1b549SSpandan Das Pos string // "file:line:col" or "file:line" or "" or "-" 32*9bb1b549SSpandan Das Msg string 33*9bb1b549SSpandan Das Kind FlatPackagesErrorKind 34*9bb1b549SSpandan Das} 35*9bb1b549SSpandan Das 36*9bb1b549SSpandan Dastype FlatPackagesErrorKind int 37*9bb1b549SSpandan Das 38*9bb1b549SSpandan Dasconst ( 39*9bb1b549SSpandan Das UnknownError FlatPackagesErrorKind = iota 40*9bb1b549SSpandan Das ListError 41*9bb1b549SSpandan Das ParseError 42*9bb1b549SSpandan Das TypeError 43*9bb1b549SSpandan Das) 44*9bb1b549SSpandan Das 45*9bb1b549SSpandan Dasfunc (err FlatPackagesError) Error() string { 46*9bb1b549SSpandan Das pos := err.Pos 47*9bb1b549SSpandan Das if pos == "" { 48*9bb1b549SSpandan Das pos = "-" // like token.Position{}.String() 49*9bb1b549SSpandan Das } 50*9bb1b549SSpandan Das return pos + ": " + err.Msg 51*9bb1b549SSpandan Das} 52*9bb1b549SSpandan Das 53*9bb1b549SSpandan Das// FlatPackage is the JSON form of Package 54*9bb1b549SSpandan Das// It drops all the type and syntax fields, and transforms the Imports 55*9bb1b549SSpandan Dastype FlatPackage struct { 56*9bb1b549SSpandan Das ID string 57*9bb1b549SSpandan Das Name string `json:",omitempty"` 58*9bb1b549SSpandan Das PkgPath string `json:",omitempty"` 59*9bb1b549SSpandan Das Errors []FlatPackagesError `json:",omitempty"` 60*9bb1b549SSpandan Das GoFiles []string `json:",omitempty"` 61*9bb1b549SSpandan Das CompiledGoFiles []string `json:",omitempty"` 62*9bb1b549SSpandan Das OtherFiles []string `json:",omitempty"` 63*9bb1b549SSpandan Das ExportFile string `json:",omitempty"` 64*9bb1b549SSpandan Das Imports map[string]string `json:",omitempty"` 65*9bb1b549SSpandan Das Standard bool `json:",omitempty"` 66*9bb1b549SSpandan Das} 67*9bb1b549SSpandan Das 68*9bb1b549SSpandan Dastype ( 69*9bb1b549SSpandan Das PackageFunc func(pkg *FlatPackage) 70*9bb1b549SSpandan Das PathResolverFunc func(path string) string 71*9bb1b549SSpandan Das) 72*9bb1b549SSpandan Das 73*9bb1b549SSpandan Dasfunc resolvePathsInPlace(prf PathResolverFunc, paths []string) { 74*9bb1b549SSpandan Das for i, path := range paths { 75*9bb1b549SSpandan Das paths[i] = prf(path) 76*9bb1b549SSpandan Das } 77*9bb1b549SSpandan Das} 78*9bb1b549SSpandan Das 79*9bb1b549SSpandan Dasfunc WalkFlatPackagesFromJSON(jsonFile string, onPkg PackageFunc) error { 80*9bb1b549SSpandan Das f, err := os.Open(jsonFile) 81*9bb1b549SSpandan Das if err != nil { 82*9bb1b549SSpandan Das return fmt.Errorf("unable to open package JSON file: %w", err) 83*9bb1b549SSpandan Das } 84*9bb1b549SSpandan Das defer f.Close() 85*9bb1b549SSpandan Das 86*9bb1b549SSpandan Das decoder := json.NewDecoder(f) 87*9bb1b549SSpandan Das for decoder.More() { 88*9bb1b549SSpandan Das pkg := &FlatPackage{} 89*9bb1b549SSpandan Das if err := decoder.Decode(&pkg); err != nil { 90*9bb1b549SSpandan Das return fmt.Errorf("unable to decode package in %s: %w", f.Name(), err) 91*9bb1b549SSpandan Das } 92*9bb1b549SSpandan Das 93*9bb1b549SSpandan Das onPkg(pkg) 94*9bb1b549SSpandan Das } 95*9bb1b549SSpandan Das return nil 96*9bb1b549SSpandan Das} 97*9bb1b549SSpandan Das 98*9bb1b549SSpandan Dasfunc (fp *FlatPackage) ResolvePaths(prf PathResolverFunc) error { 99*9bb1b549SSpandan Das resolvePathsInPlace(prf, fp.CompiledGoFiles) 100*9bb1b549SSpandan Das resolvePathsInPlace(prf, fp.GoFiles) 101*9bb1b549SSpandan Das resolvePathsInPlace(prf, fp.OtherFiles) 102*9bb1b549SSpandan Das fp.ExportFile = prf(fp.ExportFile) 103*9bb1b549SSpandan Das return nil 104*9bb1b549SSpandan Das} 105*9bb1b549SSpandan Das 106*9bb1b549SSpandan Das// FilterFilesForBuildTags filters the source files given the current build 107*9bb1b549SSpandan Das// tags. 108*9bb1b549SSpandan Dasfunc (fp *FlatPackage) FilterFilesForBuildTags() { 109*9bb1b549SSpandan Das fp.GoFiles = filterSourceFilesForTags(fp.GoFiles) 110*9bb1b549SSpandan Das fp.CompiledGoFiles = filterSourceFilesForTags(fp.CompiledGoFiles) 111*9bb1b549SSpandan Das} 112*9bb1b549SSpandan Das 113*9bb1b549SSpandan Dasfunc (fp *FlatPackage) IsStdlib() bool { 114*9bb1b549SSpandan Das return fp.Standard 115*9bb1b549SSpandan Das} 116*9bb1b549SSpandan Das 117*9bb1b549SSpandan Dasfunc (fp *FlatPackage) ResolveImports(resolve ResolvePkgFunc) error { 118*9bb1b549SSpandan Das // Stdlib packages are already complete import wise 119*9bb1b549SSpandan Das if fp.IsStdlib() { 120*9bb1b549SSpandan Das return nil 121*9bb1b549SSpandan Das } 122*9bb1b549SSpandan Das 123*9bb1b549SSpandan Das fset := token.NewFileSet() 124*9bb1b549SSpandan Das 125*9bb1b549SSpandan Das for _, file := range fp.CompiledGoFiles { 126*9bb1b549SSpandan Das f, err := parser.ParseFile(fset, file, nil, parser.ImportsOnly) 127*9bb1b549SSpandan Das if err != nil { 128*9bb1b549SSpandan Das return err 129*9bb1b549SSpandan Das } 130*9bb1b549SSpandan Das // If the name is not provided, fetch it from the sources 131*9bb1b549SSpandan Das if fp.Name == "" { 132*9bb1b549SSpandan Das fp.Name = f.Name.Name 133*9bb1b549SSpandan Das } 134*9bb1b549SSpandan Das 135*9bb1b549SSpandan Das for _, rawImport := range f.Imports { 136*9bb1b549SSpandan Das imp, err := strconv.Unquote(rawImport.Path.Value) 137*9bb1b549SSpandan Das if err != nil { 138*9bb1b549SSpandan Das continue 139*9bb1b549SSpandan Das } 140*9bb1b549SSpandan Das // We don't handle CGo for now 141*9bb1b549SSpandan Das if imp == "C" { 142*9bb1b549SSpandan Das continue 143*9bb1b549SSpandan Das } 144*9bb1b549SSpandan Das if _, ok := fp.Imports[imp]; ok { 145*9bb1b549SSpandan Das continue 146*9bb1b549SSpandan Das } 147*9bb1b549SSpandan Das 148*9bb1b549SSpandan Das if pkgID := resolve(imp); pkgID != "" { 149*9bb1b549SSpandan Das fp.Imports[imp] = pkgID 150*9bb1b549SSpandan Das } 151*9bb1b549SSpandan Das } 152*9bb1b549SSpandan Das } 153*9bb1b549SSpandan Das 154*9bb1b549SSpandan Das return nil 155*9bb1b549SSpandan Das} 156*9bb1b549SSpandan Das 157*9bb1b549SSpandan Dasfunc (fp *FlatPackage) IsRoot() bool { 158*9bb1b549SSpandan Das return strings.HasPrefix(fp.ID, "//") 159*9bb1b549SSpandan Das} 160