github.com/vanadium-archive/go.jiri@v0.0.0-20160715023856-abfb8b131290/x.go (about) 1 // Copyright 2015 The Vanadium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package jiri provides utilities used by the jiri tool and related tools. 6 package jiri 7 8 // TODO(toddw): Rename this package to v.io/jiri, and rename the tool itself to 9 // v.io/jiri/cmd/jiri 10 11 import ( 12 "fmt" 13 "os" 14 "path/filepath" 15 16 "v.io/jiri/tool" 17 "v.io/x/lib/cmdline" 18 "v.io/x/lib/envvar" 19 "v.io/x/lib/timing" 20 ) 21 22 const ( 23 RootEnv = "JIRI_ROOT" 24 RootMetaDir = ".jiri_root" 25 ProjectMetaDir = ".jiri" 26 ProjectMetaFile = "metadata.v2" 27 ProfilesDBDir = RootMetaDir + string(filepath.Separator) + "profile_db" 28 ProfilesRootDir = RootMetaDir + string(filepath.Separator) + "profiles" 29 JiriManifestFile = ".jiri_manifest" 30 31 // PreservePathEnv is the name of the environment variable that, when set to a 32 // non-empty value, causes jiri tools to use the existing PATH variable, 33 // rather than mutating it. 34 PreservePathEnv = "JIRI_PRESERVE_PATH" 35 ) 36 37 // X holds the execution environment for the jiri tool and related tools. This 38 // includes the jiri filesystem root directory. 39 // 40 // TODO(toddw): Other jiri state should be transitioned to this struct, 41 // including the manifest and related operations. 42 type X struct { 43 *tool.Context 44 Root string 45 Usage func(format string, args ...interface{}) error 46 } 47 48 // NewX returns a new execution environment, given a cmdline env. 49 // It also prepends $JIRI_ROOT/.jiri_root/bin to the PATH. 50 func NewX(env *cmdline.Env) (*X, error) { 51 ctx := tool.NewContextFromEnv(env) 52 root, err := findJiriRoot(ctx.Timer()) 53 if err != nil { 54 return nil, err 55 } 56 x := &X{ 57 Context: ctx, 58 Root: root, 59 Usage: env.UsageErrorf, 60 } 61 if ctx.Env()[PreservePathEnv] == "" { 62 // Prepend $JIRI_ROOT/.jiri_root/bin to the PATH, so execing a binary will 63 // invoke the one in that directory, if it exists. This is crucial for jiri 64 // subcommands, where we want to invoke the binary that jiri installed, not 65 // whatever is in the user's PATH. 66 // 67 // Note that we must modify the actual os env variable with os.SetEnv and 68 // also the ctx.env, so that execing a binary through the os/exec package 69 // and with ctx.Run both have the correct behavior. 70 newPath := envvar.PrependUniqueToken(ctx.Env()["PATH"], string(os.PathListSeparator), x.BinDir()) 71 ctx.Env()["PATH"] = newPath 72 if err := os.Setenv("PATH", newPath); err != nil { 73 return nil, err 74 } 75 } 76 return x, nil 77 } 78 79 func findJiriRoot(timer *timing.Timer) (string, error) { 80 if timer != nil { 81 timer.Push("find JIRI_ROOT") 82 defer timer.Pop() 83 } 84 if root := os.Getenv(RootEnv); root != "" { 85 // Always use JIRI_ROOT if it's set. 86 result, err := filepath.EvalSymlinks(root) 87 if err != nil { 88 return "", fmt.Errorf("%v EvalSymlinks(%v) failed: %v", RootEnv, root, err) 89 } 90 if !filepath.IsAbs(result) { 91 return "", fmt.Errorf("%v isn't an absolute path: %v", RootEnv, result) 92 } 93 return filepath.Clean(result), nil 94 } 95 // TODO(toddw): Try to find the root by walking up the filesystem. 96 return "", fmt.Errorf("%v is not set", RootEnv) 97 } 98 99 // FindRoot returns the root directory of the jiri environment. All state 100 // managed by jiri resides under this root. 101 // 102 // If the RootEnv environment variable is non-empty, we always attempt to use 103 // it. It must point to an absolute path, after symlinks are evaluated. 104 // TODO(toddw): Walk up the filesystem too. 105 // 106 // Returns an empty string if the root directory cannot be determined, or if any 107 // errors are encountered. 108 // 109 // FindRoot should be rarely used; typically you should use NewX to create a new 110 // execution environment, and handle errors. An example of a valid usage is to 111 // initialize default flag values in an init func before main. 112 func FindRoot() string { 113 root, _ := findJiriRoot(nil) 114 return root 115 } 116 117 // Clone returns a clone of the environment. 118 func (x *X) Clone(opts tool.ContextOpts) *X { 119 return &X{ 120 Context: x.Context.Clone(opts), 121 Root: x.Root, 122 Usage: x.Usage, 123 } 124 } 125 126 // UsageErrorf prints the error message represented by the printf-style format 127 // and args, followed by the usage output. The implementation typically calls 128 // cmdline.Env.UsageErrorf. 129 func (x *X) UsageErrorf(format string, args ...interface{}) error { 130 if x.Usage != nil { 131 return x.Usage(format, args...) 132 } 133 return fmt.Errorf(format, args...) 134 } 135 136 // RootMetaDir returns the path to the root metadata directory. 137 func (x *X) RootMetaDir() string { 138 return filepath.Join(x.Root, RootMetaDir) 139 } 140 141 // JiriManifestFile returns the path to the .jiri_manifest file. 142 func (x *X) JiriManifestFile() string { 143 return filepath.Join(x.Root, JiriManifestFile) 144 } 145 146 // BinDir returns the path to the bin directory. 147 func (x *X) BinDir() string { 148 return filepath.Join(x.RootMetaDir(), "bin") 149 } 150 151 // ScriptsDir returns the path to the scripts directory. 152 func (x *X) ScriptsDir() string { 153 return filepath.Join(x.RootMetaDir(), "scripts") 154 } 155 156 // UpdateHistoryDir returns the path to the update history directory. 157 func (x *X) UpdateHistoryDir() string { 158 return filepath.Join(x.RootMetaDir(), "update_history") 159 } 160 161 // ProfilesDBDir returns the path to the profiles data base directory. 162 func (x *X) ProfilesDBDir() string { 163 return filepath.Join(x.RootMetaDir(), "profile_db") 164 } 165 166 // ProfilesRootDir returns the path to the root of the profiles installation. 167 func (x *X) ProfilesRootDir() string { 168 return filepath.Join(x.RootMetaDir(), "profiles") 169 } 170 171 // UpdateHistoryLatestLink returns the path to a symlink that points to the 172 // latest update in the update history directory. 173 func (x *X) UpdateHistoryLatestLink() string { 174 return filepath.Join(x.UpdateHistoryDir(), "latest") 175 } 176 177 // UpdateHistorySecondLatestLink returns the path to a symlink that points to 178 // the second latest update in the update history directory. 179 func (x *X) UpdateHistorySecondLatestLink() string { 180 return filepath.Join(x.UpdateHistoryDir(), "second-latest") 181 } 182 183 // RunnerFunc is an adapter that turns regular functions into cmdline.Runner. 184 // This is similar to cmdline.RunnerFunc, but the first function argument is 185 // jiri.X, rather than cmdline.Env. 186 func RunnerFunc(run func(*X, []string) error) cmdline.Runner { 187 return runner(run) 188 } 189 190 type runner func(*X, []string) error 191 192 func (r runner) Run(env *cmdline.Env, args []string) error { 193 x, err := NewX(env) 194 if err != nil { 195 return err 196 } 197 return r(x, args) 198 }