kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/go/platform/analysis/local/local.go (about) 1 /* 2 * Copyright 2015 The Kythe Authors. All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 // Package local implements CompilationAnalyzer utilities for local analyses. 18 package local // import "kythe.io/kythe/go/platform/analysis/local" 19 20 import ( 21 "context" 22 "errors" 23 "fmt" 24 "io" 25 "path/filepath" 26 27 "kythe.io/kythe/go/platform/analysis" 28 "kythe.io/kythe/go/platform/analysis/driver" 29 "kythe.io/kythe/go/platform/kzip" 30 "kythe.io/kythe/go/platform/vfs" 31 "kythe.io/kythe/go/util/log" 32 33 apb "kythe.io/kythe/proto/analysis_go_proto" 34 ) 35 36 // Options control the behaviour of a FileQueue. 37 type Options struct { 38 // The revision marker to attribute to each compilation. 39 Revision string 40 } 41 42 func (o *Options) revision() string { 43 if o == nil { 44 return "" 45 } 46 return o.Revision 47 } 48 49 // A FileQueue is a driver.Queue reading each compilation from a sequence of 50 // .kzip files. On each call to the driver.CompilationFunc, the 51 // FileQueue's analysis.Fetcher interface exposes the current file's contents. 52 type FileQueue struct { 53 index int // the next index to consume from paths 54 paths []string // the paths of kzip files to read 55 units []*apb.CompilationUnit // units waiting to be delivered 56 revision string // revision marker for each compilation 57 58 fetcher analysis.Fetcher 59 closer io.Closer 60 } 61 62 // NewFileQueue returns a new FileQueue over the given paths to .kzip files. 63 func NewFileQueue(paths []string, opts *Options) *FileQueue { 64 return &FileQueue{ 65 paths: paths, 66 revision: opts.revision(), 67 } 68 } 69 70 // Next implements the driver.Queue interface. 71 func (q *FileQueue) Next(ctx context.Context, f driver.CompilationFunc) error { 72 for len(q.units) == 0 { 73 if q.closer != nil { 74 q.closer.Close() 75 q.closer = nil 76 } 77 if q.index >= len(q.paths) { 78 return driver.ErrEndOfQueue 79 } 80 81 path := q.paths[q.index] 82 q.index++ 83 switch filepath.Ext(path) { 84 case ".kzip": 85 f, err := vfs.Open(ctx, path) 86 if err != nil { 87 return fmt.Errorf("opening kzip file %q: %v", path, err) 88 } 89 rc, ok := f.(kzip.File) 90 if !ok { 91 f.Close() 92 return fmt.Errorf("reader %T does not implement kzip.File", rc) 93 } 94 if err := kzip.Scan(rc, func(r *kzip.Reader, unit *kzip.Unit) error { 95 q.fetcher = kzipFetcher{r} 96 q.units = append(q.units, unit.Proto) 97 return nil 98 }); err != nil { 99 f.Close() 100 return fmt.Errorf("scanning kzip %q: %v", path, err) 101 } 102 q.closer = f 103 104 default: 105 log.WarningContextf(ctx, "Skipped unknown file kind: %q", path) 106 continue 107 } 108 } 109 110 // If we get here, we have at least one more compilation in the queue. 111 next := q.units[0] 112 q.units = q.units[1:] 113 return f(ctx, driver.Compilation{ 114 Unit: next, 115 Revision: q.revision, 116 }) 117 } 118 119 // Fetch implements the analysis.Fetcher interface by delegating to the 120 // currently-active input file. Only files in the current archive will be 121 // accessible for a given invocation of Fetch. 122 func (q *FileQueue) Fetch(path, digest string) ([]byte, error) { 123 if q.fetcher == nil { 124 return nil, errors.New("no data source available") 125 } 126 return q.fetcher.Fetch(path, digest) 127 } 128 129 type kzipFetcher struct{ r *kzip.Reader } 130 131 // Fetch implements the required method of analysis.Fetcher. 132 func (k kzipFetcher) Fetch(_, digest string) ([]byte, error) { return k.r.ReadAll(digest) }