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) }