| OLD | NEW |
| 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 venv | 5 package venv |
| 6 | 6 |
| 7 import ( | 7 import ( |
| 8 "fmt" | 8 "fmt" |
| 9 "os" | 9 "os" |
| 10 "path/filepath" | 10 "path/filepath" |
| (...skipping 15 matching lines...) Expand all Loading... |
| 26 // | 26 // |
| 27 // A VirtualEnv is specified based on its resolved vpython.Spec. | 27 // A VirtualEnv is specified based on its resolved vpython.Spec. |
| 28 type Config struct { | 28 type Config struct { |
| 29 // MaxHashLen is the maximum number of hash characters to use in Virtual
Env | 29 // MaxHashLen is the maximum number of hash characters to use in Virtual
Env |
| 30 // directory names. | 30 // directory names. |
| 31 MaxHashLen int | 31 MaxHashLen int |
| 32 | 32 |
| 33 // BaseDir is the parent directory of all VirtualEnv. | 33 // BaseDir is the parent directory of all VirtualEnv. |
| 34 BaseDir string | 34 BaseDir string |
| 35 | 35 |
| 36 // OverrideName overrides the name of the specified VirtualEnv. |
| 37 // |
| 38 // Because the name is no longer derived from the specification, this wi
ll |
| 39 // force revalidation and deletion of any existing content if it is not
a |
| 40 // fully defined and matching VirtualEnv |
| 41 OverrideName string |
| 42 |
| 36 // Package is the VirtualEnv package to install. It must be non-nil and | 43 // Package is the VirtualEnv package to install. It must be non-nil and |
| 37 // valid. It will be used if the environment specification doesn't suppl
y an | 44 // valid. It will be used if the environment specification doesn't suppl
y an |
| 38 // overriding one. | 45 // overriding one. |
| 39 Package vpython.Spec_Package | 46 Package vpython.Spec_Package |
| 40 | 47 |
| 41 // Python is the Python interpreter to use. If empty, one will be resolv
ed | 48 // Python is the Python interpreter to use. If empty, one will be resolv
ed |
| 42 // based on the Spec and the current PATH. | 49 // based on the Spec and the current PATH. |
| 43 Python string | 50 Python string |
| 44 | 51 |
| 45 // Spec is the specification file to use to construct the VirtualEnv. If | 52 // Spec is the specification file to use to construct the VirtualEnv. If |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 90 | 97 |
| 91 // WithoutWheels returns a clone of cfg that depends on no additional packages. | 98 // WithoutWheels returns a clone of cfg that depends on no additional packages. |
| 92 // | 99 // |
| 93 // If cfg is already an empty it will be returned directly. | 100 // If cfg is already an empty it will be returned directly. |
| 94 func (cfg *Config) WithoutWheels() *Config { | 101 func (cfg *Config) WithoutWheels() *Config { |
| 95 if !cfg.HasWheels() { | 102 if !cfg.HasWheels() { |
| 96 return cfg | 103 return cfg |
| 97 } | 104 } |
| 98 | 105 |
| 99 clone := *cfg | 106 clone := *cfg |
| 107 clone.OverrideName = "" |
| 100 clone.Spec = clone.Spec.Clone() | 108 clone.Spec = clone.Spec.Clone() |
| 101 clone.Spec.Wheel = nil | 109 clone.Spec.Wheel = nil |
| 102 return &clone | 110 return &clone |
| 103 } | 111 } |
| 104 | 112 |
| 105 // HasWheels returns true if this environment declares wheel dependencies. | 113 // HasWheels returns true if this environment declares wheel dependencies. |
| 106 func (cfg *Config) HasWheels() bool { | 114 func (cfg *Config) HasWheels() bool { |
| 107 return cfg.Spec != nil && len(cfg.Spec.Wheel) > 0 | 115 return cfg.Spec != nil && len(cfg.Spec.Wheel) > 0 |
| 108 } | 116 } |
| 109 | 117 |
| (...skipping 28 matching lines...) Expand all Loading... |
| 138 if longestPathLen > cfg.MaxScriptPathLen { | 146 if longestPathLen > cfg.MaxScriptPathLen { |
| 139 return nil, errors.Reason("expected deepest path
length (%(len)d) exceeds threshold (%(threshold)d)"). | 147 return nil, errors.Reason("expected deepest path
length (%(len)d) exceeds threshold (%(threshold)d)"). |
| 140 D("len", longestPathLen). | 148 D("len", longestPathLen). |
| 141 D("threshold", cfg.MaxScriptPathLen). | 149 D("threshold", cfg.MaxScriptPathLen). |
| 142 D("longestPath", longestPath). | 150 D("longestPath", longestPath). |
| 143 Err() | 151 Err() |
| 144 } | 152 } |
| 145 } | 153 } |
| 146 } | 154 } |
| 147 | 155 |
| 148 // Ensure and normalize our specification file. | |
| 149 if cfg.Spec == nil { | |
| 150 cfg.Spec = &vpython.Spec{} | |
| 151 } else { | |
| 152 cfg.Spec = cfg.Spec.Clone() | |
| 153 } | |
| 154 if err := spec.Normalize(cfg.Spec, &cfg.Package); err != nil { | |
| 155 return nil, errors.Annotate(err).Reason("invalid specification")
.Err() | |
| 156 } | |
| 157 | |
| 158 // Choose our VirtualEnv package. | |
| 159 if cfg.Spec.Virtualenv == nil { | |
| 160 cfg.Spec.Virtualenv = &cfg.Package | |
| 161 } | |
| 162 | |
| 163 // Construct a new, independent Environment for this Env. | 156 // Construct a new, independent Environment for this Env. |
| 164 e = e.Clone() | 157 e = e.Clone() |
| 165 » e.Spec = cfg.Spec.Clone() | 158 » if cfg.Spec != nil { |
| 159 » » e.Spec = cfg.Spec.Clone() |
| 160 » } |
| 161 » if err := spec.NormalizeEnvironment(e); err != nil { |
| 162 » » return nil, errors.Annotate(err).Reason("invalid environment").E
rr() |
| 163 » } |
| 164 |
| 165 » // If the environment doesn't specify a VirtualEnv package (expected), u
se |
| 166 » // our default. |
| 167 » if e.Spec.Virtualenv == nil { |
| 168 » » e.Spec.Virtualenv = &cfg.Package |
| 169 » } |
| 166 | 170 |
| 167 if err := cfg.Loader.Resolve(c, e); err != nil { | 171 if err := cfg.Loader.Resolve(c, e); err != nil { |
| 168 return nil, errors.Annotate(err).Reason("failed to resolve packa
ges").Err() | 172 return nil, errors.Annotate(err).Reason("failed to resolve packa
ges").Err() |
| 169 } | 173 } |
| 170 | 174 |
| 171 if err := cfg.resolvePythonInterpreter(c, e.Spec); err != nil { | 175 if err := cfg.resolvePythonInterpreter(c, e.Spec); err != nil { |
| 172 return nil, errors.Annotate(err).Reason("failed to resolve syste
m Python interpreter").Err() | 176 return nil, errors.Annotate(err).Reason("failed to resolve syste
m Python interpreter").Err() |
| 173 } | 177 } |
| 174 » rt := vpython.Runtime{ | 178 » e.Runtime.Path = cfg.si.Python |
| 175 » » Path: cfg.si.Python, | 179 » e.Runtime.Version = e.Spec.PythonVersion |
| 176 » » Version: e.Spec.PythonVersion, | |
| 177 » } | |
| 178 | 180 |
| 179 var err error | 181 var err error |
| 180 » if rt.Hash, err = cfg.si.Hash(); err != nil { | 182 » if e.Runtime.Hash, err = cfg.si.Hash(); err != nil { |
| 181 return nil, err | 183 return nil, err |
| 182 } | 184 } |
| 183 » e.Runtime = &rt | 185 » logging.Debugf(c, "Resolved system Python runtime (%s @ %s): %s", |
| 184 » logging.Debugf(c, "Resolved system Python runtime (%s @ %s): %s", rt.Ver
sion, rt.Hash, rt.Path) | 186 » » e.Runtime.Version, e.Runtime.Hash, e.Runtime.Path) |
| 185 | 187 |
| 186 // Ensure that our base directory exists. | 188 // Ensure that our base directory exists. |
| 187 if err := filesystem.MakeDirs(cfg.BaseDir); err != nil { | 189 if err := filesystem.MakeDirs(cfg.BaseDir); err != nil { |
| 188 return nil, errors.Annotate(err).Reason("could not create enviro
nment root: %(root)s"). | 190 return nil, errors.Annotate(err).Reason("could not create enviro
nment root: %(root)s"). |
| 189 D("root", cfg.BaseDir). | 191 D("root", cfg.BaseDir). |
| 190 Err() | 192 Err() |
| 191 } | 193 } |
| 192 | 194 |
| 193 // Generate our environment name based on the deterministic hash of its | 195 // Generate our environment name based on the deterministic hash of its |
| 194 // fully-resolved specification. | 196 // fully-resolved specification. |
| 195 » return cfg.envForName(cfg.envNameForSpec(e.Spec, e.Runtime), e), nil | 197 » envName := cfg.OverrideName |
| 198 » if envName == "" { |
| 199 » » envName = cfg.envNameForSpec(e.Spec, e.Runtime) |
| 200 » } |
| 201 » env := cfg.envForName(envName, e) |
| 202 » return env, nil |
| 196 } | 203 } |
| 197 | 204 |
| 198 // EnvName returns the VirtualEnv environment name for the environment that cfg | 205 // EnvName returns the VirtualEnv environment name for the environment that cfg |
| 199 // describes. | 206 // describes. |
| 200 func (cfg *Config) envNameForSpec(s *vpython.Spec, rt *vpython.Runtime) string { | 207 func (cfg *Config) envNameForSpec(s *vpython.Spec, rt *vpython.Runtime) string { |
| 201 name := spec.Hash(s, rt, EnvironmentVersion) | 208 name := spec.Hash(s, rt, EnvironmentVersion) |
| 202 if cfg.MaxHashLen > 0 && len(name) > cfg.MaxHashLen { | 209 if cfg.MaxHashLen > 0 && len(name) > cfg.MaxHashLen { |
| 203 name = name[:cfg.MaxHashLen] | 210 name = name[:cfg.MaxHashLen] |
| 204 } | 211 } |
| 205 return name | 212 return name |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 281 // Resolve to absolute path. | 288 // Resolve to absolute path. |
| 282 if err := filesystem.AbsPath(&cfg.Python); err != nil { | 289 if err := filesystem.AbsPath(&cfg.Python); err != nil { |
| 283 return errors.Annotate(err).Reason("could not get absolute path
for: %(python)s"). | 290 return errors.Annotate(err).Reason("could not get absolute path
for: %(python)s"). |
| 284 D("python", cfg.Python). | 291 D("python", cfg.Python). |
| 285 Err() | 292 Err() |
| 286 } | 293 } |
| 287 return nil | 294 return nil |
| 288 } | 295 } |
| 289 | 296 |
| 290 func (cfg *Config) systemInterpreter() *python.Interpreter { return cfg.si } | 297 func (cfg *Config) systemInterpreter() *python.Interpreter { return cfg.si } |
| OLD | NEW |