Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(107)

Side by Side Diff: vpython/application/application.go

Issue 2925723004: [vpython] Implement smart probing. (Closed)
Patch Set: sentinel text Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | vpython/application/probe.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2017 The LUCI Authors. All rights reserved. 1 // Copyright 2017 The LUCI Authors. All rights reserved.
2 // Use of this source code is governed under the Apache License, Version 2.0 2 // Use of this source code is governed under the Apache License, Version 2.0
3 // that can be found in the LICENSE file. 3 // that can be found in the LICENSE file.
4 4
5 package application 5 package application
6 6
7 import ( 7 import (
8 "flag" 8 "flag"
9 "fmt" 9 "fmt"
10 "io/ioutil" 10 "io/ioutil"
(...skipping 12 matching lines...) Expand all
23 "github.com/luci/luci-go/vpython/venv" 23 "github.com/luci/luci-go/vpython/venv"
24 24
25 cipdVersion "github.com/luci/luci-go/cipd/version" 25 cipdVersion "github.com/luci/luci-go/cipd/version"
26 "github.com/luci/luci-go/common/cli" 26 "github.com/luci/luci-go/common/cli"
27 "github.com/luci/luci-go/common/errors" 27 "github.com/luci/luci-go/common/errors"
28 "github.com/luci/luci-go/common/logging" 28 "github.com/luci/luci-go/common/logging"
29 "github.com/luci/luci-go/common/logging/gologger" 29 "github.com/luci/luci-go/common/logging/gologger"
30 "github.com/luci/luci-go/common/system/environ" 30 "github.com/luci/luci-go/common/system/environ"
31 "github.com/luci/luci-go/common/system/exitcode" 31 "github.com/luci/luci-go/common/system/exitcode"
32 "github.com/luci/luci-go/common/system/filesystem" 32 "github.com/luci/luci-go/common/system/filesystem"
33 "github.com/luci/luci-go/common/system/prober"
33 ) 34 )
34 35
35 const ( 36 const (
36 // VirtualEnvRootENV is an environment variable that, if set, will be us ed 37 // VirtualEnvRootENV is an environment variable that, if set, will be us ed
37 // as the default VirtualEnv root. 38 // as the default VirtualEnv root.
38 // 39 //
39 // This value overrides the default (~/.vpython), but can be overridden by the 40 // This value overrides the default (~/.vpython), but can be overridden by the
40 // "-root" flag. 41 // "-root" flag.
41 // 42 //
42 // Like "-root", if this value is present but empty, a tempdir will be u sed 43 // Like "-root", if this value is present but empty, a tempdir will be u sed
(...skipping 15 matching lines...) Expand all
58 type VerificationFunc func(context.Context, string, venv.PackageLoader, *vpython API.Environment) 59 type VerificationFunc func(context.Context, string, venv.PackageLoader, *vpython API.Environment)
59 60
60 // Config is an application's default configuration. 61 // Config is an application's default configuration.
61 type Config struct { 62 type Config struct {
62 // PackageLoader is the package loader to use. 63 // PackageLoader is the package loader to use.
63 PackageLoader venv.PackageLoader 64 PackageLoader venv.PackageLoader
64 65
65 // VENVPackage is the VirtualEnv package to use for bootstrap generation . 66 // VENVPackage is the VirtualEnv package to use for bootstrap generation .
66 VENVPackage vpythonAPI.Spec_Package 67 VENVPackage vpythonAPI.Spec_Package
67 68
69 // RelativePathOverride is a series of forward-slash-delimited paths to
70 // directories relative to the "vpython" executable that will be checked
71 // for Python targets prior to checking PATH. This allows bundles (e.g., CIPD)
72 // that include both the wrapper and a real implementation, to force the
73 // wrapper to use the bundled implementation if present.
74 //
75 // See "github.com/luci/luci-go/common/wrapper/prober.Probe"'s
76 // RelativePathOverride member for more information.
77 RelativePathOverride []string
78
68 // PruneThreshold, if > 0, is the maximum age of a VirtualEnv before it 79 // PruneThreshold, if > 0, is the maximum age of a VirtualEnv before it
69 // becomes candidate for pruning. If <= 0, no pruning will be performed. 80 // becomes candidate for pruning. If <= 0, no pruning will be performed.
70 // 81 //
71 // See venv.Config's PruneThreshold. 82 // See venv.Config's PruneThreshold.
72 PruneThreshold time.Duration 83 PruneThreshold time.Duration
73 // MaxPrunesPerSweep, if > 0, is the maximum number of VirtualEnv that s hould 84 // MaxPrunesPerSweep, if > 0, is the maximum number of VirtualEnv that s hould
74 // be pruned passively. If <= 0, no limit will be applied. 85 // be pruned passively. If <= 0, no limit will be applied.
75 // 86 //
76 // See venv.Config's MaxPrunesPerSweep. 87 // See venv.Config's MaxPrunesPerSweep.
77 MaxPrunesPerSweep int 88 MaxPrunesPerSweep int
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
138 fs.StringVar(&a.opts.EnvConfig.BaseDir, "root", a.opts.EnvConfig.BaseDir , 149 fs.StringVar(&a.opts.EnvConfig.BaseDir, "root", a.opts.EnvConfig.BaseDir ,
139 "Path to virtual environment root directory. Default is the work ing directory. "+ 150 "Path to virtual environment root directory. Default is the work ing directory. "+
140 "If explicitly set to empty string, a temporary director y will be used and cleaned up "+ 151 "If explicitly set to empty string, a temporary director y will be used and cleaned up "+
141 "on completion.") 152 "on completion.")
142 fs.StringVar(&a.specPath, "spec", a.specPath, 153 fs.StringVar(&a.specPath, "spec", a.specPath,
143 "Path to environment specification file to load. Default probes for one.") 154 "Path to environment specification file to load. Default probes for one.")
144 155
145 a.logConfig.AddFlags(fs) 156 a.logConfig.AddFlags(fs)
146 } 157 }
147 158
148 func (a *application) mainImpl(c context.Context, args []string) error { 159 func (a *application) mainImpl(c context.Context, argv0 string, args []string) e rror {
149 // Determine our VirtualEnv base directory. 160 // Determine our VirtualEnv base directory.
150 if v, ok := a.opts.Environ.Get(VirtualEnvRootENV); ok { 161 if v, ok := a.opts.Environ.Get(VirtualEnvRootENV); ok {
151 a.opts.EnvConfig.BaseDir = v 162 a.opts.EnvConfig.BaseDir = v
152 } else { 163 } else {
153 hdir, err := homedir.Dir() 164 hdir, err := homedir.Dir()
154 if err != nil { 165 if err != nil {
155 return errors.Annotate(err).Reason("failed to get user h ome directory").Err() 166 return errors.Annotate(err).Reason("failed to get user h ome directory").Err()
156 } 167 }
157 a.opts.EnvConfig.BaseDir = filepath.Join(hdir, ".vpython") 168 a.opts.EnvConfig.BaseDir = filepath.Join(hdir, ".vpython")
158 } 169 }
159 170
160 // Extract "vpython" arguments and parse them. 171 // Extract "vpython" arguments and parse them.
161 fs := flag.NewFlagSet("", flag.ExitOnError) 172 fs := flag.NewFlagSet("", flag.ExitOnError)
162 fs.SetOutput(os.Stdout) // Python uses STDOUT for help and flag informat ion. 173 fs.SetOutput(os.Stdout) // Python uses STDOUT for help and flag informat ion.
163 174
164 a.addToFlagSet(fs) 175 a.addToFlagSet(fs)
165 selfArgs, args := extractFlagsForSet(args, fs) 176 selfArgs, args := extractFlagsForSet(args, fs)
166 if err := fs.Parse(selfArgs); err != nil && err != flag.ErrHelp { 177 if err := fs.Parse(selfArgs); err != nil && err != flag.ErrHelp {
167 return errors.Annotate(err).Reason("failed to parse flags").Err( ) 178 return errors.Annotate(err).Reason("failed to parse flags").Err( )
168 } 179 }
169 180
181 // Identify the "self" executable. Use this to construct a "lookPath", w hich
182 // will be used to locate the base Python interpreter.
183 lp := lookPath{
184 probeBase: prober.Probe{
185 RelativePathOverride: a.RelativePathOverride,
186 },
187 env: a.opts.Environ,
188 }
189 if err := lp.probeBase.ResolveSelf(argv0); err != nil {
190 logging.WithError(err).Warningf(c, "Failed to resolve 'self'")
191 }
192 a.opts.EnvConfig.LookPathFunc = lp.look
193
170 if a.help { 194 if a.help {
171 » » return a.showPythonHelp(c, fs) 195 » » return a.showPythonHelp(c, fs, &lp)
172 } 196 }
173 197
174 c = a.logConfig.Set(c) 198 c = a.logConfig.Set(c)
175 199
176 // If an spec path was manually specified, load and use it. 200 // If an spec path was manually specified, load and use it.
177 if a.specPath != "" { 201 if a.specPath != "" {
178 var err error 202 var err error
179 if a.opts.EnvConfig.Spec, err = spec.Load(a.specPath); err != ni l { 203 if a.opts.EnvConfig.Spec, err = spec.Load(a.specPath); err != ni l {
180 return errors.Annotate(err).Reason("failed to load speci fication file (-spec) from: %(path)s"). 204 return errors.Annotate(err).Reason("failed to load speci fication file (-spec) from: %(path)s").
181 D("path", a.specPath). 205 D("path", a.specPath).
(...skipping 28 matching lines...) Expand all
210 // as our error. 234 // as our error.
211 if rc, has := exitcode.Get(errors.Unwrap(err)); has { 235 if rc, has := exitcode.Get(errors.Unwrap(err)); has {
212 err = ReturnCodeError(rc) 236 err = ReturnCodeError(rc)
213 } 237 }
214 238
215 return errors.Annotate(err).Err() 239 return errors.Annotate(err).Err()
216 } 240 }
217 return nil 241 return nil
218 } 242 }
219 243
220 func (a *application) showPythonHelp(c context.Context, fs *flag.FlagSet) error { 244 func (a *application) showPythonHelp(c context.Context, fs *flag.FlagSet, lp *lo okPath) error {
221 self, err := os.Executable() 245 self, err := os.Executable()
222 if err != nil { 246 if err != nil {
223 self = "vpython" 247 self = "vpython"
224 } 248 }
225 vers, err := cipdVersion.GetStartupVersion() 249 vers, err := cipdVersion.GetStartupVersion()
226 if err == nil && vers.PackageName != "" && vers.InstanceID != "" { 250 if err == nil && vers.PackageName != "" && vers.InstanceID != "" {
227 self = fmt.Sprintf("%s (%s@%s)", self, vers.PackageName, vers.In stanceID) 251 self = fmt.Sprintf("%s (%s@%s)", self, vers.PackageName, vers.In stanceID)
228 } 252 }
229 253
230 fmt.Fprintf(os.Stdout, "Usage of %s:\n", self) 254 fmt.Fprintf(os.Stdout, "Usage of %s:\n", self)
231 fs.SetOutput(os.Stdout) 255 fs.SetOutput(os.Stdout)
232 fs.PrintDefaults() 256 fs.PrintDefaults()
233 257
234 » i, err := python.Find(c, python.Version{}) 258 » i, err := python.Find(c, python.Version{}, lp.look)
235 if err != nil { 259 if err != nil {
236 return errors.Annotate(err).Reason("could not find Python interp reter for help").Err() 260 return errors.Annotate(err).Reason("could not find Python interp reter for help").Err()
237 } 261 }
238 262
239 // Redirect all "--help" to Stdout for consistency. 263 // Redirect all "--help" to Stdout for consistency.
240 fmt.Fprintf(os.Stdout, "\nPython help for %s:\n", i.Python) 264 fmt.Fprintf(os.Stdout, "\nPython help for %s:\n", i.Python)
241 cmd := i.IsolatedCommand(c, "--help") 265 cmd := i.IsolatedCommand(c, "--help")
242 cmd.Stdout = os.Stdout 266 cmd.Stdout = os.Stdout
243 cmd.Stderr = os.Stdout 267 cmd.Stderr = os.Stdout
244 if err := cmd.Run(); err != nil { 268 if err := cmd.Run(); err != nil {
245 return errors.Annotate(err).Reason("failed to dump Python help f rom: %(interpreter)s"). 269 return errors.Annotate(err).Reason("failed to dump Python help f rom: %(interpreter)s").
246 D("interpreter", i.Python). 270 D("interpreter", i.Python).
247 Err() 271 Err()
248 } 272 }
249 return nil 273 return nil
250 } 274 }
251 275
252 // Main is the main application entry point. 276 // Main is the main application entry point.
253 func (cfg *Config) Main(c context.Context) int { 277 func (cfg *Config) Main(c context.Context) int {
278 // Implementation of "checkWrapper": if CheckWrapperENV is set, we immed iately
279 // exit with a non-zero value.
280 env := environ.System()
281 if wrapperCheck(env) {
282 return 1
283 }
284
254 c = gologger.StdConfig.Use(c) 285 c = gologger.StdConfig.Use(c)
255 c = logging.SetLevel(c, logging.Error) 286 c = logging.SetLevel(c, logging.Error)
256 287
257 a := application{ 288 a := application{
258 Config: cfg, 289 Config: cfg,
259 opts: vpython.Options{ 290 opts: vpython.Options{
260 EnvConfig: venv.Config{ 291 EnvConfig: venv.Config{
261 BaseDir: "", // (Determined below). 292 BaseDir: "", // (Determined below).
262 MaxHashLen: 6, 293 MaxHashLen: 6,
263 Package: cfg.VENVPackage, 294 Package: cfg.VENVPackage,
264 PruneThreshold: cfg.PruneThreshold, 295 PruneThreshold: cfg.PruneThreshold,
265 MaxPrunesPerSweep: cfg.MaxPrunesPerSweep, 296 MaxPrunesPerSweep: cfg.MaxPrunesPerSweep,
266 MaxScriptPathLen: cfg.MaxScriptPathLen, 297 MaxScriptPathLen: cfg.MaxScriptPathLen,
267 Loader: cfg.PackageLoader, 298 Loader: cfg.PackageLoader,
268 }, 299 },
269 WaitForEnv: true, 300 WaitForEnv: true,
270 » » » Environ: environ.System(), 301 » » » Environ: env,
271 }, 302 },
272 logConfig: logging.Config{ 303 logConfig: logging.Config{
273 Level: logging.Error, 304 Level: logging.Error,
274 }, 305 },
275 } 306 }
276 307
277 return run(c, func(c context.Context) error { 308 return run(c, func(c context.Context) error {
278 » » return a.mainImpl(c, os.Args[1:]) 309 » » return a.mainImpl(c, os.Args[0], os.Args[1:])
279 }) 310 })
280 } 311 }
OLDNEW
« no previous file with comments | « no previous file | vpython/application/probe.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698