kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/go/platform/analysis/driver/driver.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 driver contains a Driver implementation that sends analyses to a 18 // CompilationAnalyzer based on a Queue of compilations. 19 package driver // import "kythe.io/kythe/go/platform/analysis/driver" 20 21 import ( 22 "context" 23 goerrors "errors" 24 "time" 25 26 "kythe.io/kythe/go/platform/analysis" 27 "kythe.io/kythe/go/util/log" 28 29 "github.com/pkg/errors" 30 31 apb "kythe.io/kythe/proto/analysis_go_proto" 32 ) 33 34 // A Compilation represents a compilation and other metadata needed to analyze it. 35 type Compilation struct { 36 Unit *apb.CompilationUnit // the compilation to analyze 37 Revision string // revision marker to attribute to the compilation 38 UnitDigest string // unit digest identifying the compilation in a KCD 39 BuildID string // id of the build executing the compilation 40 } 41 42 // CompilationFunc handles a single CompilationUnit. 43 type CompilationFunc func(context.Context, Compilation) error 44 45 // A Queue represents an ordered sequence of compilation units. 46 type Queue interface { 47 // Next invokes f with the next available compilation in the queue. If no 48 // further values are available, Next must return ErrEndOfQueue; otherwise, 49 // the return value from f is propagated to the caller of Next. 50 Next(_ context.Context, f CompilationFunc) error 51 } 52 53 // A Context packages callbacks invoked during analysis. 54 type Context interface { 55 // Setup is invoked after a compilation has been fetched from a Queue but 56 // before it is sent to the analyzer. If Setup reports an error, analysis 57 // is aborted. 58 Setup(context.Context, Compilation) error 59 60 // Teardown is invoked after a analysis has completed for the compilation. 61 // If Teardown reports an error after analysis succeeds, it is logged but 62 // does not cause the analysis to fail. 63 Teardown(context.Context, Compilation) error 64 65 // AnalysisError is invoked for each non-nil error reported by the analyzer 66 // prior to calling Teardown. The error returned from AnalysisError replaces 67 // the error returned by the analyzer itself. 68 // 69 // If AnalysisError returns the special value ErrRetry, the analysis is 70 // retried immediately. 71 AnalysisError(context.Context, Compilation, error) error 72 } 73 74 var ( 75 // ErrRetry can be returned from a Driver's AnalysisError function to signal 76 // that the driver should retry the analysis immediately. 77 ErrRetry = goerrors.New("retry analysis") 78 79 // ErrEndOfQueue can be returned from a Queue to signal there are no 80 // compilations left to analyze. 81 ErrEndOfQueue = goerrors.New("end of queue") 82 ) 83 84 // AnalysisOptions contains extra configuration for analysis requests. 85 type AnalysisOptions struct { 86 // Timeout, if nonzero, sets the given timeout for each analysis request. 87 Timeout time.Duration 88 } 89 90 // Driver sends compilations sequentially from a queue to an analyzer. 91 type Driver struct { 92 Analyzer analysis.CompilationAnalyzer 93 AnalysisOptions AnalysisOptions 94 95 FileDataService string 96 Context Context // if nil, callbacks are no-ops 97 WriteOutput analysis.OutputFunc // if nil, output is discarded 98 } 99 100 func (d *Driver) writeOutput(ctx context.Context, out *apb.AnalysisOutput) error { 101 if write := d.WriteOutput; write != nil { 102 return write(ctx, out) 103 } 104 return nil 105 } 106 107 func (d *Driver) setup(ctx context.Context, unit Compilation) error { 108 if c := d.Context; c != nil { 109 return c.Setup(ctx, unit) 110 } 111 return nil 112 } 113 114 func (d *Driver) teardown(ctx context.Context, unit Compilation) error { 115 if c := d.Context; c != nil { 116 return c.Teardown(ctx, unit) 117 } 118 return nil 119 } 120 121 func (d *Driver) analysisError(ctx context.Context, unit Compilation, err error) error { 122 if c := d.Context; c != nil && err != nil { 123 return c.AnalysisError(ctx, unit, err) 124 } 125 return err 126 } 127 128 // Run sends each compilation received from the driver's Queue to the driver's 129 // Analyzer. All outputs are passed to Output in turn. An error is immediately 130 // returned if the Analyzer, Output, or Compilations fields are unset. 131 func (d *Driver) Run(ctx context.Context, queue Queue) error { 132 if d.Analyzer == nil { 133 return errors.New("no analyzer has been specified") 134 } 135 136 for { 137 if err := queue.Next(ctx, func(ctx context.Context, cu Compilation) error { 138 if err := d.setup(ctx, cu); err != nil { 139 return errors.WithMessage(err, "driver: analysis setup") 140 } 141 err := ErrRetry 142 for err == ErrRetry { 143 err = d.analysisError(ctx, cu, d.runAnalysis(ctx, cu)) 144 } 145 if terr := d.teardown(ctx, cu); terr != nil { 146 if err == nil { 147 return errors.WithMessage(terr, "driver: analysis teardown") 148 } 149 log.WarningContextf(ctx, "analysis teardown failed: %v (analysis error: %v)", terr, err) 150 } 151 return err 152 }); err == ErrEndOfQueue { 153 return nil 154 } else if err != nil { 155 return err 156 } 157 } 158 } 159 160 func (d *Driver) runAnalysis(ctx context.Context, cu Compilation) error { 161 if d.AnalysisOptions.Timeout != 0 { 162 var cancel func() 163 ctx, cancel = context.WithTimeout(ctx, d.AnalysisOptions.Timeout) 164 defer cancel() 165 } 166 _, err := d.Analyzer.Analyze(ctx, &apb.AnalysisRequest{ 167 Compilation: cu.Unit, 168 FileDataService: d.FileDataService, 169 Revision: cu.Revision, 170 BuildId: cu.BuildID, 171 }, d.writeOutput) 172 return err 173 }