blob: 0f29ac7c1db3d7497aa734891b6e2f8cd77c4eef [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package main
import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
"time"
"go.chromium.org/luci/hardcoded/chromeinfra"
"go.chromium.org/luci/vpython/api/vpython"
"go.chromium.org/luci/vpython/application"
"go.chromium.org/luci/vpython/cipd"
"go.chromium.org/luci/vpython/spec"
"github.com/mitchellh/go-homedir"
cipdClient "go.chromium.org/luci/cipd/client/cipd"
"go.chromium.org/luci/cipd/client/cipd/plugin/host"
"go.chromium.org/luci/common/logging"
"go.chromium.org/luci/common/logging/gologger"
"go.chromium.org/luci/common/system/environ"
)
const (
// BypassENV is an environment variable that is used to detect if we shouldn't
// do any vpython stuff at all, but should instead directly invoke the next
// `python` on PATH.
BypassENV = "VPYTHON_BYPASS"
// BypassSentinel must be the BypassENV value (verbatim) in order to trigger
// vpython bypass.
BypassSentinel = "manually managed python not supported by chrome operations"
)
var cipdPackageLoader = cipd.PackageLoader{
Options: cipdClient.ClientOptions{
ServiceURL: chromeinfra.CIPDServiceURL,
UserAgent: fmt.Sprintf("vpython, %s", cipdClient.UserAgent),
},
Template: func(c context.Context, tags []*vpython.PEP425Tag) (map[string]string, error) {
tag := pep425TagSelector(tags)
if tag == nil {
return nil, nil
}
return getPEP425CIPDTemplateForTag(tag)
},
}
var defaultConfig = application.Config{
PackageLoader: &cipdPackageLoader,
SpecLoader: spec.Loader{
CommonFilesystemBarriers: []string{
".gclient",
},
CommonSpecNames: []string{
".vpython",
},
PartnerSuffix: ".vpython",
},
DefaultSpec: vpython.Spec{
PythonVersion: "2",
},
VENVPackage: vpython.Spec_Package{
Name: "infra/python/virtualenv",
Version: "version:15.1.0",
},
PruneThreshold: 7 * 24 * time.Hour, // One week.
MaxPrunesPerSweep: 3,
DefaultVerificationTags: verificationScenarios,
}
func mainImpl(c context.Context, argv []string, env environ.Env) int {
// Enable plugins support. Important to call it before LoadFromEnv to make
// sure plugin related env vars are recognized.
cipdPackageLoader.Options.PluginHost = &host.Host{PluginsContext: c}
// Initialize our CIPD package loader from the environment.
//
// If we don't have an environment-specific CIPD cache directory, use one
// relative to the user's home directory.
if err := cipdPackageLoader.Options.LoadFromEnv(env.GetEmpty); err != nil {
logging.Errorf(c, "Could not inialize CIPD package loader: %s", err)
return 1
}
if cipdPackageLoader.Options.CacheDir == "" {
hd, err := homedir.Dir()
if err == nil {
cipdPackageLoader.Options.CacheDir = filepath.Join(hd, ".vpython_cipd_cache")
} else {
logging.WithError(err).Warningf(c,
"Failed to resolve user home directory. No CIPD cache will be enabled.")
}
}
// Determine if we're bypassing "vpython".
defaultConfig.Bypass = env.GetEmpty(BypassENV) == BypassSentinel
// Determine if we're operating in "vpython3" mode (invoked as ./vpython3, ./vpython3.exe,
// ./python3, or ./python3.exe).
if strings.HasSuffix(argv[0], "python3") || strings.HasSuffix(argv[0], "python3.exe") {
defaultConfig.SpecLoader.CommonSpecNames = []string{".vpython3"}
defaultConfig.SpecLoader.PartnerSuffix = ".vpython3"
defaultConfig.DefaultSpec.PythonVersion = "3"
defaultConfig.VpythonOptIn = true
}
return defaultConfig.Main(c, argv, env)
}
func main() {
c := context.Background()
c = gologger.StdConfig.Use(logging.SetLevel(c, logging.Warning))
ret := mainImpl(c, os.Args, environ.System())
// os.Exit seems not to flush logging targets on Windows. The logger stores
// the logging target as io.Writer which has no mechanism to flush. Knowing
// gologger.StdConfig is configured to use os.Stderr, flush it directly.
// https://crbug.com/1017136.
os.Stderr.Sync()
os.Exit(ret)
}