Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2017 The LUCI Authors. All rights reserved. | |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | |
| 3 // that can be found in the LICENSE file. | |
| 4 | |
| 5 package application | |
| 6 | |
| 7 import ( | |
| 8 "os/exec" | |
| 9 | |
| 10 "golang.org/x/net/context" | |
| 11 | |
| 12 "github.com/luci/luci-go/vpython/python" | |
| 13 | |
| 14 "github.com/luci/luci-go/common/errors" | |
| 15 "github.com/luci/luci-go/common/logging" | |
| 16 "github.com/luci/luci-go/common/system/environ" | |
| 17 "github.com/luci/luci-go/common/system/exitcode" | |
| 18 "github.com/luci/luci-go/common/system/prober" | |
| 19 ) | |
| 20 | |
| 21 const ( | |
| 22 // CheckWrapperENV is an environment variable that is used to detect if a | |
| 23 // given binary is a "vpython" wrapper instance. The "vpython" Main func tion | |
| 24 // will immediately exit with a non-zero return code if this environment | |
| 25 // variable is set. | |
| 26 // | |
| 27 // See checkWrapper for more details. | |
| 28 CheckWrapperENV = "VPYTHON_CHECK_WRAPPER" | |
| 29 ) | |
| 30 | |
| 31 type lookPath struct { | |
| 32 // probeBase is a base prober whose Self and SelfStat fields will be use d | |
| 33 // during a lookPathImpl call. This ensures that we only have to call | |
| 34 // ResolveSelf once for any given application. | |
| 35 probeBase prober.Probe | |
| 36 | |
| 37 env environ.Env | |
| 38 lastPath string | |
| 39 lastVersionOutput []byte | |
| 40 } | |
| 41 | |
| 42 func (lp *lookPath) look(c context.Context, target string) (*python.LookPathResu lt, error) { | |
| 43 // Construct our probe, derived it from our "probeBase" and tailored to the | |
| 44 // specific "target" we're looking for. | |
| 45 probe := lp.probeBase | |
| 46 probe.Target = target | |
| 47 probe.CheckWrapper = lp.checkWrapper | |
| 48 | |
| 49 var result python.LookPathResult | |
| 50 var err error | |
| 51 if result.Path, err = probe.Locate(c, "", lp.env.Clone()); err != nil { | |
| 52 return nil, err | |
| 53 } | |
| 54 | |
| 55 // During probing, we try and capture the output of "--version". If we d o, | |
| 56 // and if we confirm that the "path" returned by the probe matches "last Path", | |
| 57 // return this version along with the resolution. | |
| 58 if result.Path == lp.lastPath { | |
| 59 var err error | |
| 60 if result.Version, err = python.ParseVersionOutput(lp.lastVersio nOutput); err == nil { | |
| 61 logging.Debugf(c, "Detected Python version %q from probe candidate [%s]", result.Version, result.Path) | |
| 62 } else { | |
| 63 logging.Debugf(c, "Failed to parse version from probe ca ndidate [%s]: %s", result.Path, err) | |
| 64 } | |
| 65 } | |
| 66 | |
| 67 return &result, nil | |
| 68 } | |
| 69 | |
| 70 // checkWrapper is a prober.CheckWrapperFunc that detects if a given path is a | |
| 71 // "vpython" instance. | |
| 72 // | |
| 73 // It does this by running it "path --version" with CheckWrapperENV set. If | |
| 74 // the target is a "vpython" wrapper, it will immediately exit with a non-zero | |
| 75 // value (see Main). If it is not a valid Python program, its behavior is | |
| 76 // undefined. If it is a valid Pytohn instance, it will exit with a Python | |
| 77 // version string. | |
| 78 // | |
| 79 // As a side effect, the text output of the last version probe will be stored | |
| 80 // in lp.lastVersionOutput. The "look" function can pass this value on to | |
| 81 // the LookPathResult. | |
| 82 func (lp *lookPath) checkWrapper(c context.Context, path string, env environ.Env ) (isWrapper bool, err error) { | |
| 83 lp.lastPath = path | |
| 84 env.Set(CheckWrapperENV, "1") | |
| 85 | |
| 86 cmd := exec.CommandContext(c, path, "--version") | |
| 87 cmd.Env = env.Sorted() | |
| 88 | |
| 89 lp.lastVersionOutput, err = cmd.CombinedOutput() | |
| 90 rc, ok := exitcode.Get(err) | |
| 91 if !ok { | |
| 92 err = errors.Annotate(err).Reason("failed to check if %(path)q i s a wrapper"). | |
| 93 D("path", path). | |
| 94 Err() | |
| 95 return | |
| 96 } | |
| 97 | |
| 98 // If "--version" didn't return "0", we determine that this is a wrapper , | |
| 99 // since no Python interpreter would return non-zero for "--version". | |
|
iannucci
2017/06/07 17:40:26
it might if it failed to load properly though, rig
dnj
2017/06/07 17:43:26
If it fails to load properly, we should not use it
dnj
2017/06/07 17:45:51
OTOH if Python is broken, I don't want "vpython" t
| |
| 100 isWrapper, err = (rc != 0), nil | |
| 101 return | |
| 102 } | |
| OLD | NEW |