| 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 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 67 // | 67 // |
| 68 // This can be used to enforce "shebang" length limits, whereupon genera
ted | 68 // This can be used to enforce "shebang" length limits, whereupon genera
ted |
| 69 // VirtualEnv scripts may be generated with a "shebang" (#!) line longer
than | 69 // VirtualEnv scripts may be generated with a "shebang" (#!) line longer
than |
| 70 // what is allowed by the operating system. | 70 // what is allowed by the operating system. |
| 71 MaxScriptPathLen int | 71 MaxScriptPathLen int |
| 72 | 72 |
| 73 // si is the system Python interpreter. It is resolved during | 73 // si is the system Python interpreter. It is resolved during |
| 74 // "resolvePythonInterpreter". | 74 // "resolvePythonInterpreter". |
| 75 si *python.Interpreter | 75 si *python.Interpreter |
| 76 | 76 |
| 77 // rt is the resolved Python runtime. |
| 78 rt vpython.Runtime |
| 79 |
| 77 // testPreserveInstallationCapability is a testing parameter. If true, t
he | 80 // testPreserveInstallationCapability is a testing parameter. If true, t
he |
| 78 // VirtualEnv's ability to install will be preserved after the setup. Th
is is | 81 // VirtualEnv's ability to install will be preserved after the setup. Th
is is |
| 79 // used by the test whell generation bootstrap code. | 82 // used by the test whell generation bootstrap code. |
| 80 testPreserveInstallationCapability bool | 83 testPreserveInstallationCapability bool |
| 81 | 84 |
| 82 // testLeaveReadWrite, if true, instructs the VirtualEnv setup to leave
the | 85 // testLeaveReadWrite, if true, instructs the VirtualEnv setup to leave
the |
| 83 // directory read/write. This makes it easier to manage, and is safe sin
ce it | 86 // directory read/write. This makes it easier to manage, and is safe sin
ce it |
| 84 // is not a production directory. | 87 // is not a production directory. |
| 85 testLeaveReadWrite bool | 88 testLeaveReadWrite bool |
| 86 } | 89 } |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 161 e = e.Clone() | 164 e = e.Clone() |
| 162 e.Spec = cfg.Spec.Clone() | 165 e.Spec = cfg.Spec.Clone() |
| 163 | 166 |
| 164 if err := cfg.Loader.Resolve(c, e); err != nil { | 167 if err := cfg.Loader.Resolve(c, e); err != nil { |
| 165 return nil, errors.Annotate(err).Reason("failed to resolve packa
ges").Err() | 168 return nil, errors.Annotate(err).Reason("failed to resolve packa
ges").Err() |
| 166 } | 169 } |
| 167 | 170 |
| 168 if err := cfg.resolvePythonInterpreter(c, e.Spec); err != nil { | 171 if err := cfg.resolvePythonInterpreter(c, e.Spec); err != nil { |
| 169 return nil, errors.Annotate(err).Reason("failed to resolve syste
m Python interpreter").Err() | 172 return nil, errors.Annotate(err).Reason("failed to resolve syste
m Python interpreter").Err() |
| 170 } | 173 } |
| 174 rt := vpython.Runtime{ |
| 175 Path: cfg.si.Python, |
| 176 Version: e.Spec.PythonVersion, |
| 177 } |
| 178 |
| 179 var err error |
| 180 if rt.Hash, err = cfg.si.Hash(); err != nil { |
| 181 return nil, err |
| 182 } |
| 183 e.Runtime = &rt |
| 184 logging.Debugf(c, "Resolved system Python runtime (%s @ %s): %s", rt.Ver
sion, rt.Hash, rt.Path) |
| 171 | 185 |
| 172 // Ensure that our base directory exists. | 186 // Ensure that our base directory exists. |
| 173 if err := filesystem.MakeDirs(cfg.BaseDir); err != nil { | 187 if err := filesystem.MakeDirs(cfg.BaseDir); err != nil { |
| 174 return nil, errors.Annotate(err).Reason("could not create enviro
nment root: %(root)s"). | 188 return nil, errors.Annotate(err).Reason("could not create enviro
nment root: %(root)s"). |
| 175 D("root", cfg.BaseDir). | 189 D("root", cfg.BaseDir). |
| 176 Err() | 190 Err() |
| 177 } | 191 } |
| 178 | 192 |
| 179 // Generate our environment name based on the deterministic hash of its | 193 // Generate our environment name based on the deterministic hash of its |
| 180 // fully-resolved specification. | 194 // fully-resolved specification. |
| 181 » return cfg.envForName(cfg.envNameForSpec(e.Spec), e), nil | 195 » return cfg.envForName(cfg.envNameForSpec(e.Spec, e.Runtime), e), nil |
| 182 } | 196 } |
| 183 | 197 |
| 184 // EnvName returns the VirtualEnv environment name for the environment that cfg | 198 // EnvName returns the VirtualEnv environment name for the environment that cfg |
| 185 // describes. | 199 // describes. |
| 186 func (cfg *Config) envNameForSpec(s *vpython.Spec) string { | 200 func (cfg *Config) envNameForSpec(s *vpython.Spec, rt *vpython.Runtime) string { |
| 187 » name := spec.Hash(s, EnvironmentVersion) | 201 » name := spec.Hash(s, rt, EnvironmentVersion) |
| 188 if cfg.MaxHashLen > 0 && len(name) > cfg.MaxHashLen { | 202 if cfg.MaxHashLen > 0 && len(name) > cfg.MaxHashLen { |
| 189 name = name[:cfg.MaxHashLen] | 203 name = name[:cfg.MaxHashLen] |
| 190 } | 204 } |
| 191 return name | 205 return name |
| 192 } | 206 } |
| 193 | 207 |
| 194 // Prune performs a pruning round on the environment set described by this | 208 // Prune performs a pruning round on the environment set described by this |
| 195 // Config. | 209 // Config. |
| 196 func (cfg *Config) Prune(c context.Context) error { | 210 func (cfg *Config) Prune(c context.Context) error { |
| 197 if err := prune(c, cfg, nil); err != nil { | 211 if err := prune(c, cfg, nil); err != nil { |
| 198 return errors.Annotate(err).Err() | 212 return errors.Annotate(err).Err() |
| 199 } | 213 } |
| 200 return nil | 214 return nil |
| 201 } | 215 } |
| 202 | 216 |
| 203 // envForName creates an Env for a named directory. | 217 // envForName creates an Env for a named directory. |
| 218 // |
| 219 // The Environment, e, can be nil; however, code paths that require it may not |
| 220 // be called. |
| 204 func (cfg *Config) envForName(name string, e *vpython.Environment) *Env { | 221 func (cfg *Config) envForName(name string, e *vpython.Environment) *Env { |
| 205 // Env-specific root directory: <BaseDir>/<name> | 222 // Env-specific root directory: <BaseDir>/<name> |
| 206 venvRoot := filepath.Join(cfg.BaseDir, name) | 223 venvRoot := filepath.Join(cfg.BaseDir, name) |
| 207 binDir := venvBinDir(venvRoot) | 224 binDir := venvBinDir(venvRoot) |
| 208 return &Env{ | 225 return &Env{ |
| 209 Config: cfg, | 226 Config: cfg, |
| 210 Root: venvRoot, | 227 Root: venvRoot, |
| 211 Name: name, | 228 Name: name, |
| 212 Python: filepath.Join(binDir, "python"), | 229 Python: filepath.Join(binDir, "python"), |
| 213 Environment: e, | 230 Environment: e, |
| (...skipping 20 matching lines...) Expand all Loading... |
| 234 return errors.Annotate(err).Reason("could not find Pytho
n for: %(vers)s"). | 251 return errors.Annotate(err).Reason("could not find Pytho
n for: %(vers)s"). |
| 235 D("vers", specVers). | 252 D("vers", specVers). |
| 236 Err() | 253 Err() |
| 237 } | 254 } |
| 238 cfg.Python = cfg.si.Python | 255 cfg.Python = cfg.si.Python |
| 239 } else { | 256 } else { |
| 240 cfg.si = &python.Interpreter{ | 257 cfg.si = &python.Interpreter{ |
| 241 Python: cfg.Python, | 258 Python: cfg.Python, |
| 242 } | 259 } |
| 243 } | 260 } |
| 261 if err := cfg.si.Normalize(); err != nil { |
| 262 return err |
| 263 } |
| 244 | 264 |
| 245 // Confirm that the version of the interpreter matches that which is | 265 // Confirm that the version of the interpreter matches that which is |
| 246 // expected. | 266 // expected. |
| 247 interpreterVers, err := cfg.si.GetVersion(c) | 267 interpreterVers, err := cfg.si.GetVersion(c) |
| 248 if err != nil { | 268 if err != nil { |
| 249 return errors.Annotate(err).Reason("failed to determine Python v
ersion for: %(python)s"). | 269 return errors.Annotate(err).Reason("failed to determine Python v
ersion for: %(python)s"). |
| 250 D("python", cfg.Python). | 270 D("python", cfg.Python). |
| 251 Err() | 271 Err() |
| 252 } | 272 } |
| 253 if !specVers.IsSatisfiedBy(interpreterVers) { | 273 if !specVers.IsSatisfiedBy(interpreterVers) { |
| 254 return errors.Reason("supplied Python version (%(supplied)s) doe
sn't match specification (%(spec)s)"). | 274 return errors.Reason("supplied Python version (%(supplied)s) doe
sn't match specification (%(spec)s)"). |
| 255 D("supplied", interpreterVers). | 275 D("supplied", interpreterVers). |
| 256 D("spec", specVers). | 276 D("spec", specVers). |
| 257 Err() | 277 Err() |
| 258 } | 278 } |
| 259 s.PythonVersion = interpreterVers.String() | 279 s.PythonVersion = interpreterVers.String() |
| 260 | 280 |
| 261 // Resolve to absolute path. | 281 // Resolve to absolute path. |
| 262 if err := filesystem.AbsPath(&cfg.Python); err != nil { | 282 if err := filesystem.AbsPath(&cfg.Python); err != nil { |
| 263 return errors.Annotate(err).Reason("could not get absolute path
for: %(python)s"). | 283 return errors.Annotate(err).Reason("could not get absolute path
for: %(python)s"). |
| 264 D("python", cfg.Python). | 284 D("python", cfg.Python). |
| 265 Err() | 285 Err() |
| 266 } | 286 } |
| 267 logging.Debugf(c, "Resolved system Python interpreter (%s): %s", s.Pytho
nVersion, cfg.Python) | |
| 268 return nil | 287 return nil |
| 269 } | 288 } |
| 270 | 289 |
| 271 func (cfg *Config) systemInterpreter() *python.Interpreter { return cfg.si } | 290 func (cfg *Config) systemInterpreter() *python.Interpreter { return cfg.si } |
| OLD | NEW |