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

Unified 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | vpython/application/subcommand_install.go » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: vpython/application/application.go
diff --git a/vpython/application/application.go b/vpython/application/application.go
new file mode 100644
index 0000000000000000000000000000000000000000..2322cfbe7a249c8eb48ac9737cd50036dacc0f0a
--- /dev/null
+++ b/vpython/application/application.go
@@ -0,0 +1,197 @@
+// Copyright 2017 The LUCI Authors. All rights reserved.
+// Use of this source code is governed under the Apache License, Version 2.0
+// that can be found in the LICENSE file.
+
+package application
+
+import (
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "time"
+
+ "github.com/luci/luci-go/vpython"
+ vpythonAPI "github.com/luci/luci-go/vpython/api/vpython"
+ "github.com/luci/luci-go/vpython/spec"
+ "github.com/luci/luci-go/vpython/venv"
+
+ "github.com/luci/luci-go/common/cli"
+ "github.com/luci/luci-go/common/errors"
+ "github.com/luci/luci-go/common/logging"
+ "github.com/luci/luci-go/common/logging/gologger"
+ "github.com/luci/luci-go/common/system/environ"
+ "github.com/luci/luci-go/common/system/exitcode"
+ "github.com/luci/luci-go/common/system/filesystem"
+
+ "github.com/maruel/subcommands"
+ "github.com/mitchellh/go-homedir"
+ "golang.org/x/net/context"
+)
+
+// ReturnCodeError is an error wrapping a return code value.
+type ReturnCodeError int
+
+func (err ReturnCodeError) Error() string {
+ return fmt.Sprintf("python interpreter returned non-zero error: %d", err)
+}
+
+// Config is an application's default configuration.
+type Config struct {
+ // PackageLoader is the package loader to use.
+ PackageLoader venv.PackageLoader
+
+ // VENVPackage is the VirtualEnv package to use for bootstrap generation.
+ VENVPackage vpythonAPI.Spec_Package
+
+ // PruneThreshold, if > 0, is the maximum age of a VirtualEnv before it
+ // becomes candidate for pruning. If <= 0, no pruning will be performed.
+ //
+ // See venv.Config's PruneThreshold.
+ PruneThreshold time.Duration
+ // MaxPrunesPerSweep, if > 0, is the maximum number of VirtualEnv that should
+ // be pruned passively. If <= 0, no limit will be applied.
+ //
+ // See venv.Config's MaxPrunesPerSweep.
+ MaxPrunesPerSweep int
+
+ // MaxScriptPathLen, if > 0, is the maximum generated script path lengt. If
+ // a generated script is expected to exist longer than this, we will error.
+ //
+ // See venv.Config's MaxScriptPathLen.
+ MaxScriptPathLen int
+
+ // Opts is the set of configured options.
+ Opts vpython.Options
+}
+
+func (cfg *Config) mainDev(c context.Context) error {
+ app := cli.Application{
+ Name: "vpython",
+ Title: "VirtualEnv Python Bootstrap (Development Mode)",
+ Context: func(context.Context) context.Context {
+ // Discard the entry Context and use the one passed to us.
+ c := c
+
+ // Install our Config instance into the Context.
+ c = withConfig(c, cfg)
+
+ // Drop down to Info level debugging.
+ if logging.GetLevel(c) > logging.Info {
+ c = logging.SetLevel(c, logging.Info)
+ }
+ return c
+ },
+ Commands: []*subcommands.Command{
+ subcommands.CmdHelp,
+ subcommandInstall,
+ },
+ }
+
+ return ReturnCodeError(subcommands.Run(&app, cfg.Opts.Args))
+}
+
+func (cfg *Config) mainImpl(c context.Context, args []string) error {
+ logConfig := logging.Config{
+ Level: logging.Warning,
+ }
+
+ hdir, err := homedir.Dir()
+ if err != nil {
+ return errors.Annotate(err).Reason("failed to get user home directory").Err()
+ }
+
+ cfg.Opts = vpython.Options{
+ EnvConfig: venv.Config{
+ BaseDir: filepath.Join(hdir, ".vpython"),
+ MaxHashLen: 6,
+ Package: cfg.VENVPackage,
+ PruneThreshold: cfg.PruneThreshold,
+ MaxPrunesPerSweep: cfg.MaxPrunesPerSweep,
+ MaxScriptPathLen: cfg.MaxScriptPathLen,
+ Loader: cfg.PackageLoader,
+ },
+ WaitForEnv: true,
+ Environ: environ.System(),
+ }
+ var specPath string
+ var devMode bool
+
+ fs := flag.NewFlagSet("", flag.ExitOnError)
+ fs.BoolVar(&devMode, "dev", devMode,
+ "Enter development / subcommand mode (use 'help' for more options).")
+ fs.StringVar(&cfg.Opts.EnvConfig.Python, "python", cfg.Opts.EnvConfig.Python,
+ "Path to system Python interpreter to use. Default is found on PATH.")
+ fs.StringVar(&cfg.Opts.WorkDir, "workdir", cfg.Opts.WorkDir,
+ "Working directory to run the Python interpreter in. Default is current working directory.")
+ fs.StringVar(&cfg.Opts.EnvConfig.BaseDir, "root", cfg.Opts.EnvConfig.BaseDir,
+ "Path to virtual enviornment root directory. Default is the working directory. "+
+ "If explicitly set to empty string, a temporary directory will be used and cleaned up "+
+ "on completion.")
+ fs.StringVar(&specPath, "spec", specPath,
+ "Path to enviornment specification file to load. Default probes for one.")
+ logConfig.AddFlags(fs)
+
+ if err := fs.Parse(args); err != nil {
+ if err == flag.ErrHelp {
+ return nil
+ }
+ return errors.Annotate(err).Reason("failed to parse flags").Err()
+ }
+ cfg.Opts.Args = fs.Args()
+
+ c = logConfig.Set(c)
+
+ // If an spec path was manually specified, load and use it.
+ if specPath != "" {
+ var err error
+ if cfg.Opts.EnvConfig.Spec, err = spec.Load(specPath); err != nil {
+ return errors.Annotate(err).Reason("failed to load specification file (-spec) from: %(path)s").
+ D("path", specPath).
+ Err()
+ }
+ }
+
+ // If an empty BaseDir was specified, use a temporary directory and clean it
+ // up on completion.
+ if cfg.Opts.EnvConfig.BaseDir == "" {
+ tdir, err := ioutil.TempDir("", "vpython")
+ if err != nil {
+ return errors.Annotate(err).Reason("failed to create temporary directory").Err()
+ }
+ defer func() {
+ logging.Debugf(c, "Removing temporary directory: %s", tdir)
+ if terr := filesystem.RemoveAll(tdir); terr != nil {
+ logging.WithError(terr).Warningf(c, "Failed to clean up temporary directory; leaking: %s", tdir)
+ }
+ }()
+ cfg.Opts.EnvConfig.BaseDir = tdir
+ }
+
+ // Development mode (subcommands).
+ if devMode {
+ return cfg.mainDev(c)
+ }
+
+ if err := vpython.Run(c, cfg.Opts); err != nil {
+ // If the process failed because of a non-zero return value, return that
+ // as our error.
+ if rc, has := exitcode.Get(errors.Unwrap(err)); has {
+ err = ReturnCodeError(rc)
+ }
+
+ return errors.Annotate(err).Err()
+ }
+ return nil
+}
+
+// Main is the main application entry point.
+func (cfg *Config) Main(c context.Context) int {
+ c = gologger.StdConfig.Use(c)
+ c = logging.SetLevel(c, logging.Warning)
+
+ return run(c, func(c context.Context) error {
+ return cfg.mainImpl(c, os.Args[1:])
+ })
+}
« 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