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

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

Issue 2705593003: vpython: Add application entry point. (Closed)
Patch Set: rebarse? Created 3 years, 9 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/subcommand_install.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 "flag"
9 "fmt"
10 "io/ioutil"
11 "os"
12 "path/filepath"
13 "time"
14
15 "github.com/luci/luci-go/vpython"
16 vpythonAPI "github.com/luci/luci-go/vpython/api/vpython"
17 "github.com/luci/luci-go/vpython/spec"
18 "github.com/luci/luci-go/vpython/venv"
19
20 "github.com/luci/luci-go/common/cli"
21 "github.com/luci/luci-go/common/errors"
22 "github.com/luci/luci-go/common/logging"
23 "github.com/luci/luci-go/common/logging/gologger"
24 "github.com/luci/luci-go/common/system/environ"
25 "github.com/luci/luci-go/common/system/exitcode"
26 "github.com/luci/luci-go/common/system/filesystem"
27
28 "github.com/maruel/subcommands"
29 "github.com/mitchellh/go-homedir"
30 "golang.org/x/net/context"
31 )
32
33 // ReturnCodeError is an error wrapping a return code value.
34 type ReturnCodeError int
35
36 func (err ReturnCodeError) Error() string {
37 return fmt.Sprintf("python interpreter returned non-zero error: %d", err )
38 }
39
40 // Config is an application's default configuration.
41 type Config struct {
42 // PackageLoader is the package loader to use.
43 PackageLoader venv.PackageLoader
44
45 // VENVPackage is the VirtualEnv package to use for bootstrap generation .
46 VENVPackage vpythonAPI.Spec_Package
47
48 // PruneThreshold, if > 0, is the maximum age of a VirtualEnv before it
49 // becomes candidate for pruning. If <= 0, no pruning will be performed.
50 //
51 // See venv.Config's PruneThreshold.
52 PruneThreshold time.Duration
53 // MaxPrunesPerSweep, if > 0, is the maximum number of VirtualEnv that s hould
54 // be pruned passively. If <= 0, no limit will be applied.
55 //
56 // See venv.Config's MaxPrunesPerSweep.
57 MaxPrunesPerSweep int
58
59 // MaxScriptPathLen, if > 0, is the maximum generated script path lengt. If
60 // a generated script is expected to exist longer than this, we will err or.
61 //
62 // See venv.Config's MaxScriptPathLen.
63 MaxScriptPathLen int
64
65 // Opts is the set of configured options.
66 Opts vpython.Options
67 }
68
69 func (cfg *Config) mainDev(c context.Context) error {
70 app := cli.Application{
71 Name: "vpython",
72 Title: "VirtualEnv Python Bootstrap (Development Mode)",
73 Context: func(context.Context) context.Context {
74 // Discard the entry Context and use the one passed to u s.
75 c := c
76
77 // Install our Config instance into the Context.
78 c = withConfig(c, cfg)
79
80 // Drop down to Info level debugging.
81 if logging.GetLevel(c) > logging.Info {
82 c = logging.SetLevel(c, logging.Info)
83 }
84 return c
85 },
86 Commands: []*subcommands.Command{
87 subcommands.CmdHelp,
88 subcommandInstall,
89 },
90 }
91
92 return ReturnCodeError(subcommands.Run(&app, cfg.Opts.Args))
93 }
94
95 func (cfg *Config) mainImpl(c context.Context, args []string) error {
96 logConfig := logging.Config{
97 Level: logging.Warning,
98 }
99
100 hdir, err := homedir.Dir()
101 if err != nil {
102 return errors.Annotate(err).Reason("failed to get user home dire ctory").Err()
103 }
104
105 cfg.Opts = vpython.Options{
106 EnvConfig: venv.Config{
107 BaseDir: filepath.Join(hdir, ".vpython"),
108 MaxHashLen: 6,
109 Package: cfg.VENVPackage,
110 PruneThreshold: cfg.PruneThreshold,
111 MaxPrunesPerSweep: cfg.MaxPrunesPerSweep,
112 MaxScriptPathLen: cfg.MaxScriptPathLen,
113 Loader: cfg.PackageLoader,
114 },
115 WaitForEnv: true,
116 Environ: environ.System(),
117 }
118 var specPath string
119 var devMode bool
120
121 fs := flag.NewFlagSet("", flag.ExitOnError)
122 fs.BoolVar(&devMode, "dev", devMode,
123 "Enter development / subcommand mode (use 'help' for more option s).")
124 fs.StringVar(&cfg.Opts.EnvConfig.Python, "python", cfg.Opts.EnvConfig.Py thon,
125 "Path to system Python interpreter to use. Default is found on P ATH.")
126 fs.StringVar(&cfg.Opts.WorkDir, "workdir", cfg.Opts.WorkDir,
127 "Working directory to run the Python interpreter in. Default is current working directory.")
128 fs.StringVar(&cfg.Opts.EnvConfig.BaseDir, "root", cfg.Opts.EnvConfig.Bas eDir,
129 "Path to virtual enviornment root directory. Default is the work ing directory. "+
130 "If explicitly set to empty string, a temporary director y will be used and cleaned up "+
131 "on completion.")
132 fs.StringVar(&specPath, "spec", specPath,
133 "Path to enviornment specification file to load. Default probes for one.")
134 logConfig.AddFlags(fs)
135
136 if err := fs.Parse(args); err != nil {
137 if err == flag.ErrHelp {
138 return nil
139 }
140 return errors.Annotate(err).Reason("failed to parse flags").Err( )
141 }
142 cfg.Opts.Args = fs.Args()
143
144 c = logConfig.Set(c)
145
146 // If an spec path was manually specified, load and use it.
147 if specPath != "" {
148 var err error
149 if cfg.Opts.EnvConfig.Spec, err = spec.Load(specPath); err != ni l {
150 return errors.Annotate(err).Reason("failed to load speci fication file (-spec) from: %(path)s").
151 D("path", specPath).
152 Err()
153 }
154 }
155
156 // If an empty BaseDir was specified, use a temporary directory and clea n it
157 // up on completion.
158 if cfg.Opts.EnvConfig.BaseDir == "" {
159 tdir, err := ioutil.TempDir("", "vpython")
160 if err != nil {
161 return errors.Annotate(err).Reason("failed to create tem porary directory").Err()
162 }
163 defer func() {
164 logging.Debugf(c, "Removing temporary directory: %s", td ir)
165 if terr := filesystem.RemoveAll(tdir); terr != nil {
166 logging.WithError(terr).Warningf(c, "Failed to c lean up temporary directory; leaking: %s", tdir)
167 }
168 }()
169 cfg.Opts.EnvConfig.BaseDir = tdir
170 }
171
172 // Development mode (subcommands).
173 if devMode {
174 return cfg.mainDev(c)
175 }
176
177 if err := vpython.Run(c, cfg.Opts); err != nil {
178 // If the process failed because of a non-zero return value, ret urn that
179 // as our error.
180 if rc, has := exitcode.Get(errors.Unwrap(err)); has {
181 err = ReturnCodeError(rc)
182 }
183
184 return errors.Annotate(err).Err()
185 }
186 return nil
187 }
188
189 // Main is the main application entry point.
190 func (cfg *Config) Main(c context.Context) int {
191 c = gologger.StdConfig.Use(c)
192 c = logging.SetLevel(c, logging.Warning)
193
194 return run(c, func(c context.Context) error {
195 return cfg.mainImpl(c, os.Args[1:])
196 })
197 }
OLDNEW
« no previous file with comments | « no previous file | vpython/application/subcommand_install.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698