├── Makefile ├── TODO ├── go.mod ├── LICENSE ├── README.md ├── rewrite └── rewrite.go ├── importer.go ├── link.go ├── go.sum └── main.go /Makefile: -------------------------------------------------------------------------------- 1 | deps: 2 | go get . 3 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | - 'working set' command to copy deps from global root to somewhere local (`vendor` dir) 2 | - this way we can easily hack on our deps without disturbing any other packages 3 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/whyrusleeping/gx-go 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/kr/fs v0.1.0 7 | github.com/mitchellh/go-homedir v1.1.0 8 | github.com/urfave/cli v1.22.2 9 | github.com/whyrusleeping/gx v0.14.3 10 | github.com/whyrusleeping/stump v0.0.0-20160611222256-206f8f13aae1 11 | ) 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Jeromy Johnson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gx-go 2 | 3 | A subtool for the gx package manager for packages written in go. 4 | 5 | ## Usage: 6 | ``` 7 | NAME: 8 | gx-go - gx extensions for golang 9 | 10 | USAGE: 11 | gx-go [global options] command [command options] [arguments...] 12 | 13 | VERSION: 14 | 1.8.0 15 | 16 | AUTHOR: 17 | whyrusleeping 18 | 19 | COMMANDS: 20 | dep-map prints out a json dep map for usage by 'import --map' 21 | hook go specific hooks to be called by the gx tool 22 | import import a go package and all its depencies into gx 23 | path prints the import path of the current package within GOPATH 24 | rewrite, rw temporary hack to evade causality 25 | uw 26 | update update a packages imports to a new path 27 | dvcs-deps display all dvcs deps 28 | link Symlink packages to their dvcsimport repos, for local development. 29 | devcopy Create a development copy of the given package 30 | get gx-ified `go get` 31 | help, h Shows a list of commands or help for one command 32 | 33 | GLOBAL OPTIONS: 34 | --verbose turn on verbose output 35 | --help, -h show help 36 | --version, -v print the version 37 | ``` 38 | 39 | ## Intro 40 | Using gx as a go vendoring tool and package manager is (or at least, should be) a 41 | very simple process. 42 | 43 | ### Creating a new package 44 | In the directory of your go package, just run: 45 | ``` 46 | gx init --lang=go 47 | ``` 48 | 49 | And gx will create a new `package.json` for you with some basic information 50 | filled out. From there, all you *have* to do is run `gx publish` (ensure you 51 | have a running ipfs daemon) and gx will give you a package hash. That works 52 | fine for the base case, but to work even more nicely with go, we recommend 53 | setting the import path of your package in your `package.json`, like so: 54 | 55 | package.json 56 | ```json 57 | { 58 | ... 59 | "gx":{ 60 | "dvcsimport":"github.com/whyrusleeping/gx-go" 61 | } 62 | } 63 | ``` 64 | 65 | If you're initializing a new gx package from the appropriate location within 66 | your `GOPATH`, `gx-go` will attempt to pre-fill the dvcsimport field for you 67 | automatically. 68 | 69 | ### Importing an existing package 70 | Importing an existing go package from gx is easy, just grab its hash from 71 | somewhere, and run: 72 | ``` 73 | gx import 74 | ``` 75 | 76 | If the package you are importing has its dvcs import path set as shown above, 77 | gx will ask if you want to rewrite your import paths with the new gx path. 78 | If you say no to this (as is the default), you can rewrite the paths at any time 79 | by running `gx-go rewrite`. 80 | 81 | ### Some notes on publishing 82 | It is recommended that when you publish, your import paths are *not* rewritten. 83 | The gx-go post install hook will fix that after the install, but for 'same package' 84 | imports, it works best to have gx rewrite things after the fact (Its also sometimes 85 | nicer for development). You can change paths back from their gx paths with: 86 | ``` 87 | gx-go rewrite --undo 88 | ``` 89 | 90 | A few other notes: 91 | 92 | - When publishing, make sure that you don't have any duplicate dependencies 93 | (different hash versions of the same package). You can check this with `gx 94 | deps dupes` 95 | - Make sure that you arent missing any dependencies, With your dependencies 96 | written in gx form, run `gx-go dvcs-deps`. If it outputs any package that is 97 | not the package you are publishing, you should probably look at importing 98 | that package to gx as well. 99 | - Make sure the tests pass with gx rewritten deps. `gx test` will write gx deps 100 | and run `go test` for you. 101 | 102 | ## NOTE: 103 | It is highly recommended that you set your `GOPATH` to a temporary directory when running import. 104 | This ensures that your current go packages are not affected, and also that fresh versions of 105 | the packages in question are pulled down. 106 | -------------------------------------------------------------------------------- /rewrite/rewrite.go: -------------------------------------------------------------------------------- 1 | package rewrite 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "fmt" 7 | "go/ast" 8 | "go/parser" 9 | "go/printer" 10 | "go/token" 11 | "io" 12 | "os" 13 | "path/filepath" 14 | "regexp" 15 | "runtime" 16 | "strconv" 17 | "strings" 18 | "sync" 19 | 20 | fs "github.com/kr/fs" 21 | ) 22 | 23 | var bufpool = &sync.Pool{ 24 | New: func() interface{} { 25 | return new(bytes.Buffer) 26 | }, 27 | } 28 | 29 | var cfg = &printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8} 30 | 31 | func RewriteImports(ipath string, rw func(string) string, filter func(string) bool) error { 32 | path, err := filepath.EvalSymlinks(ipath) 33 | if err != nil { 34 | return err 35 | } 36 | 37 | var rwLock sync.Mutex 38 | 39 | var wg sync.WaitGroup 40 | torewrite := make(chan string) 41 | for i := 0; i < runtime.NumCPU(); i++ { 42 | wg.Add(1) 43 | go func() { 44 | defer wg.Done() 45 | for path := range torewrite { 46 | err := rewriteImportsInFile(path, rw, &rwLock) 47 | if err != nil { 48 | fmt.Println("rewrite error: ", err) 49 | } 50 | } 51 | }() 52 | } 53 | 54 | w := fs.Walk(path) 55 | for w.Step() { 56 | rel := w.Path()[len(path):] 57 | if len(rel) == 0 { 58 | continue 59 | } 60 | rel = rel[1:] 61 | 62 | if strings.HasPrefix(rel, ".git") || strings.HasPrefix(rel, "vendor") { 63 | w.SkipDir() 64 | continue 65 | } 66 | 67 | if !strings.HasSuffix(w.Path(), ".go") { 68 | continue 69 | } 70 | 71 | if !filter(rel) { 72 | continue 73 | } 74 | torewrite <- w.Path() 75 | } 76 | close(torewrite) 77 | wg.Wait() 78 | return nil 79 | } 80 | 81 | // inspired by godeps rewrite, rewrites import paths with gx vendored names 82 | func rewriteImportsInFile(fi string, rw func(string) string, rwLock *sync.Mutex) error { 83 | // 1. Rewrite the imports (if we have any) 84 | fset := token.NewFileSet() 85 | file, err := parser.ParseFile(fset, fi, nil, parser.ParseComments|parser.ImportsOnly) 86 | if err != nil { 87 | return err 88 | } 89 | if len(file.Imports) == 0 { 90 | return nil 91 | } 92 | 93 | oldImportsEnd := fset.Position(file.Imports[len(file.Imports)-1].End()).Offset 94 | 95 | rwLock.Lock() 96 | var changed bool 97 | for _, imp := range file.Imports { 98 | p, err := strconv.Unquote(imp.Path.Value) 99 | if err != nil { 100 | rwLock.Unlock() 101 | return err 102 | } 103 | 104 | np := rw(p) 105 | 106 | if np != p { 107 | changed = true 108 | imp.Path.Value = strconv.Quote(np) 109 | } 110 | } 111 | rwLock.Unlock() 112 | 113 | if !changed { 114 | return nil 115 | } 116 | 117 | buf := bufpool.Get().(*bytes.Buffer) 118 | defer func() { 119 | bufpool.Put(buf) 120 | }() 121 | 122 | // Write them back to a temporary buffer 123 | 124 | buf.Reset() 125 | if err = cfg.Fprint(buf, fset, file); err != nil { 126 | return err 127 | } 128 | 129 | // 2. Read the imports back in to sort them. 130 | 131 | fset = token.NewFileSet() 132 | file, err = parser.ParseFile(fset, fi, buf, parser.ParseComments|parser.ImportsOnly) 133 | if err != nil { 134 | return err 135 | } 136 | 137 | ast.SortImports(fset, file) 138 | 139 | // Write them back to a temporary buffer 140 | 141 | buf.Reset() 142 | if err = cfg.Fprint(buf, fset, file); err != nil { 143 | return err 144 | } 145 | 146 | // 3. Read them back in to find the new end of the imports. 147 | 148 | fset = token.NewFileSet() 149 | file, err = parser.ParseFile(fset, fi, buf, parser.ParseComments|parser.ImportsOnly) 150 | if err != nil { 151 | return err 152 | } 153 | 154 | newImportsEnd := fset.Position(file.Imports[len(file.Imports)-1].End()).Offset 155 | 156 | // Write them back to the buffer and truncate. 157 | buf.Reset() 158 | if err = cfg.Fprint(buf, fset, file); err != nil { 159 | return err 160 | } 161 | buf.Truncate(newImportsEnd) 162 | 163 | // Finally, build the file. 164 | 165 | tmppath := fi + ".temp" 166 | tmp, err := os.Create(tmppath) 167 | if err != nil { 168 | return err 169 | } 170 | 171 | // Write the imports 172 | _, err = buf.WriteTo(tmp) 173 | if err != nil { 174 | return err 175 | } 176 | 177 | // Copy the rest 178 | src, err := os.Open(fi) 179 | if err != nil { 180 | return err 181 | } 182 | 183 | _, err = src.Seek(int64(oldImportsEnd), io.SeekStart) 184 | if err != nil { 185 | src.Close() 186 | return err 187 | } 188 | 189 | _, err = io.Copy(tmp, src) 190 | if err != nil { 191 | src.Close() 192 | return err 193 | } 194 | 195 | // Ignore any errors, we didn't modify this file. 196 | src.Close() 197 | 198 | // Update the file 199 | if err = tmp.Close(); err != nil { 200 | return err 201 | } 202 | 203 | return os.Rename(tmppath, fi) 204 | } 205 | 206 | func fixCanonicalImports(buf []byte) (bool, error) { 207 | var i int 208 | var changed bool 209 | for { 210 | n, tok, err := bufio.ScanLines(buf[i:], true) 211 | if err != nil { 212 | return false, err 213 | } 214 | if n == 0 { 215 | return changed, nil 216 | } 217 | i += n 218 | 219 | stripped := stripImportComment(tok) 220 | if stripped != nil { 221 | nstr := copy(tok, stripped) 222 | copy(tok[nstr:], bytes.Repeat([]byte(" "), len(tok)-nstr)) 223 | changed = true 224 | } 225 | } 226 | } 227 | 228 | // more code from our friends over at godep 229 | const ( 230 | importAnnotation = `import\s+(?:"[^"]*"|` + "`[^`]*`" + `)` 231 | importComment = `(?://\s*` + importAnnotation + `\s*$|/\*\s*` + importAnnotation + `\s*\*/)` 232 | ) 233 | 234 | var ( 235 | importCommentRE = regexp.MustCompile(`\s*(package\s+\w+)\s+` + importComment + `(.*)`) 236 | pkgPrefix = []byte("package ") 237 | ) 238 | 239 | func stripImportComment(line []byte) []byte { 240 | if !bytes.HasPrefix(line, pkgPrefix) { 241 | // Fast path; this will skip all but one line in the file. 242 | // This assumes there is no whitespace before the keyword. 243 | return nil 244 | } 245 | if m := importCommentRE.FindSubmatch(line); m != nil { 246 | return append(m[1], m[2]...) 247 | } 248 | return nil 249 | } 250 | -------------------------------------------------------------------------------- /importer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "go/build" 6 | "go/scanner" 7 | "io/ioutil" 8 | "os" 9 | "os/exec" 10 | "path" 11 | "path/filepath" 12 | "strings" 13 | 14 | rw "github.com/whyrusleeping/gx-go/rewrite" 15 | gx "github.com/whyrusleeping/gx/gxutil" 16 | . "github.com/whyrusleeping/stump" 17 | ) 18 | 19 | func doUpdate(dir, oldimp, newimp string) error { 20 | rwf := func(in string) string { 21 | if in == oldimp { 22 | return newimp 23 | } 24 | 25 | if strings.HasPrefix(in, oldimp+"/") { 26 | return strings.Replace(in, oldimp, newimp, 1) 27 | } 28 | 29 | return in 30 | } 31 | 32 | filter := func(in string) bool { 33 | return strings.HasSuffix(in, ".go") && !strings.HasPrefix(in, "vendor") 34 | } 35 | 36 | return rw.RewriteImports(dir, rwf, filter) 37 | } 38 | 39 | func pathIsNotStdlib(path string) bool { 40 | first := strings.Split(path, "/")[0] 41 | 42 | if len(strings.Split(first, ".")) > 1 { 43 | return true 44 | } 45 | return false 46 | } 47 | 48 | type Importer struct { 49 | pkgs map[string]*gx.Dependency 50 | gopath string 51 | pm *gx.PM 52 | rewrite bool 53 | yesall bool 54 | preMap map[string]string 55 | 56 | bctx build.Context 57 | } 58 | 59 | func NewImporter(rw bool, gopath string, premap map[string]string) (*Importer, error) { 60 | cfg, err := gx.LoadConfig() 61 | if err != nil { 62 | return nil, err 63 | } 64 | 65 | pm, err := gx.NewPM(cfg) 66 | if err != nil { 67 | return nil, err 68 | } 69 | 70 | if premap == nil { 71 | premap = make(map[string]string) 72 | } 73 | 74 | bctx := build.Default 75 | bctx.GOPATH = gopath 76 | 77 | return &Importer{ 78 | pkgs: make(map[string]*gx.Dependency), 79 | gopath: gopath, 80 | pm: pm, 81 | rewrite: rw, 82 | preMap: premap, 83 | bctx: bctx, 84 | }, nil 85 | } 86 | 87 | // this function is an attempt to keep subdirectories of a package as part of 88 | // the same logical gx package. It has a special case for golang.org/x/ packages 89 | func getBaseDVCS(path string) string { 90 | parts := strings.Split(path, "/") 91 | depth := 3 92 | /* 93 | if parts[0] == "golang.org" && parts[1] == "x" { 94 | depth = 4 95 | } 96 | */ 97 | 98 | if len(parts) > depth { 99 | return strings.Join(parts[:3], "/") 100 | } 101 | return path 102 | } 103 | 104 | func (i *Importer) GxPublishGoPackage(imppath string) (*gx.Dependency, error) { 105 | imppath = getBaseDVCS(imppath) 106 | if d, ok := i.pkgs[imppath]; ok { 107 | return d, nil 108 | } 109 | 110 | if hash, ok := i.preMap[imppath]; ok { 111 | pkg, err := i.pm.GetPackageTo(hash, filepath.Join(vendorDir, hash)) 112 | if err != nil { 113 | return nil, err 114 | } 115 | 116 | dep := &gx.Dependency{ 117 | Hash: hash, 118 | Name: pkg.Name, 119 | Version: pkg.Version, 120 | } 121 | i.pkgs[imppath] = dep 122 | return dep, nil 123 | } 124 | 125 | // make sure its local 126 | err := i.GoGet(imppath) 127 | if err != nil { 128 | if !strings.Contains(err.Error(), "no buildable Go source files") { 129 | Error("go get %s failed: %s", imppath, err) 130 | return nil, err 131 | } 132 | } 133 | 134 | pkgpath := path.Join(i.gopath, "src", imppath) 135 | pkgFilePath := path.Join(pkgpath, gx.PkgFileName) 136 | pkg, err := LoadPackageFile(pkgFilePath) 137 | if err != nil { 138 | if !os.IsNotExist(err) { 139 | return nil, err 140 | } 141 | 142 | // init as gx package 143 | parts := strings.Split(imppath, "/") 144 | pkgname := parts[len(parts)-1] 145 | if !i.yesall { 146 | p := fmt.Sprintf("enter name for import '%s'", imppath) 147 | nname, err := prompt(p, pkgname) 148 | if err != nil { 149 | return nil, err 150 | } 151 | 152 | pkgname = nname 153 | } 154 | 155 | err = i.pm.InitPkg(pkgpath, pkgname, "go", nil) 156 | if err != nil { 157 | return nil, err 158 | } 159 | 160 | pkg, err = LoadPackageFile(pkgFilePath) 161 | if err != nil { 162 | return nil, err 163 | } 164 | } 165 | 166 | // wipe out existing dependencies 167 | pkg.Dependencies = nil 168 | 169 | // recurse! 170 | depsToVendor, err := i.DepsToVendorForPackage(imppath) 171 | if err != nil { 172 | return nil, fmt.Errorf("error fetching deps for %s: %s", imppath, err) 173 | } 174 | 175 | for n, child := range depsToVendor { 176 | Log("- processing dep %s for %s [%d / %d]", child, imppath, n+1, len(depsToVendor)) 177 | if strings.HasPrefix(child, imppath) { 178 | continue 179 | } 180 | childdep, err := i.GxPublishGoPackage(child) 181 | if err != nil { 182 | return nil, err 183 | } 184 | 185 | pkg.Dependencies = append(pkg.Dependencies, childdep) 186 | } 187 | 188 | err = gx.SavePackageFile(pkg, pkgFilePath) 189 | if err != nil { 190 | return nil, err 191 | } 192 | 193 | fullpkgpath, err := filepath.Abs(pkgpath) 194 | if err != nil { 195 | return nil, err 196 | } 197 | 198 | err = i.rewriteImports(fullpkgpath) 199 | if err != nil { 200 | return nil, fmt.Errorf("rewriting imports failed: %s", err) 201 | } 202 | 203 | err = writeGxIgnore(pkgpath, []string{"Godeps/*"}) 204 | if err != nil { 205 | return nil, err 206 | } 207 | 208 | hash, err := i.pm.PublishPackage(pkgpath, &pkg.PackageBase) 209 | if err != nil { 210 | return nil, err 211 | } 212 | 213 | Log("published %s as %s", imppath, hash) 214 | 215 | dep := &gx.Dependency{ 216 | Hash: hash, 217 | Name: pkg.Name, 218 | Version: pkg.Version, 219 | } 220 | i.pkgs[imppath] = dep 221 | return dep, nil 222 | } 223 | 224 | func (i *Importer) DepsToVendorForPackage(path string) ([]string, error) { 225 | rdeps := make(map[string]struct{}) 226 | 227 | gopkg, err := i.bctx.Import(path, "", 0) 228 | if err != nil { 229 | switch err := err.(type) { 230 | case *build.NoGoError: 231 | // if theres no go code here, there still might be some in lower directories 232 | case scanner.ErrorList: 233 | Error("failed to scan file: %s", err) 234 | // continue anyway 235 | case *build.MultiplePackageError: 236 | Error("multiple package error: %s", err) 237 | default: 238 | Error("ERROR OF TYPE: %#v", err) 239 | return nil, err 240 | } 241 | 242 | } else { 243 | imps := append(gopkg.Imports, gopkg.TestImports...) 244 | // if the package existed and has go code in it 245 | gdeps := getBaseDVCS(path) + "/Godeps/_workspace/src/" 246 | for _, child := range imps { 247 | if strings.HasPrefix(child, gdeps) { 248 | child = child[len(gdeps):] 249 | } 250 | 251 | child = getBaseDVCS(child) 252 | if pathIsNotStdlib(child) && !strings.HasPrefix(child, path) { 253 | rdeps[child] = struct{}{} 254 | } 255 | } 256 | } 257 | 258 | dirents, err := ioutil.ReadDir(filepath.Join(i.gopath, "src", path)) 259 | if err != nil { 260 | return nil, err 261 | } 262 | 263 | for _, e := range dirents { 264 | if !e.IsDir() || skipDir(e.Name()) { 265 | continue 266 | } 267 | 268 | out, err := i.DepsToVendorForPackage(filepath.Join(path, e.Name())) 269 | if err != nil { 270 | return nil, err 271 | } 272 | 273 | for _, o := range out { 274 | rdeps[o] = struct{}{} 275 | } 276 | } 277 | 278 | var depsToVendor []string 279 | for d, _ := range rdeps { 280 | depsToVendor = append(depsToVendor, d) 281 | } 282 | 283 | return depsToVendor, nil 284 | } 285 | 286 | func skipDir(name string) bool { 287 | switch name { 288 | case "Godeps", "vendor", ".git": 289 | return true 290 | default: 291 | return false 292 | } 293 | } 294 | 295 | func (i *Importer) rewriteImports(pkgpath string) error { 296 | 297 | filter := func(p string) bool { 298 | return !strings.HasPrefix(p, "vendor") && 299 | !strings.HasPrefix(p, ".git") && 300 | strings.HasSuffix(p, ".go") && 301 | !strings.HasPrefix(p, "Godeps") 302 | } 303 | 304 | base := pkgpath[len(i.gopath)+5:] 305 | gdepath := base + "/Godeps/_workspace/src/" 306 | rwf := func(in string) string { 307 | if strings.HasPrefix(in, gdepath) { 308 | in = in[len(gdepath):] 309 | } 310 | 311 | if !i.rewrite { 312 | // if rewrite not specified, just fixup godeps paths 313 | return in 314 | } 315 | 316 | dep, ok := i.pkgs[in] 317 | if ok { 318 | return "gx/" + dep.Hash + "/" + dep.Name 319 | } 320 | 321 | parts := strings.Split(in, "/") 322 | if len(parts) > 3 { 323 | obase := strings.Join(parts[:3], "/") 324 | dep, bok := i.pkgs[obase] 325 | if !bok { 326 | return in 327 | } 328 | 329 | return strings.Replace(in, obase, "gx/"+dep.Hash+"/"+dep.Name, 1) 330 | } 331 | 332 | return in 333 | } 334 | 335 | return rw.RewriteImports(pkgpath, rwf, filter) 336 | } 337 | 338 | // TODO: take an option to grab packages from local GOPATH 339 | func (imp *Importer) GoGet(path string) error { 340 | cmd := exec.Command("go", "get", path) 341 | env := os.Environ() 342 | for i, e := range env { 343 | if strings.HasPrefix(e, "GOPATH=") { 344 | env[i] = "GOPATH=" + imp.gopath 345 | } 346 | } 347 | cmd.Env = env 348 | out, err := cmd.CombinedOutput() 349 | if err != nil { 350 | return fmt.Errorf("go get failed: %s - %s", string(out), err) 351 | } 352 | return nil 353 | } 354 | 355 | func writeGxIgnore(dir string, ignore []string) error { 356 | return ioutil.WriteFile(filepath.Join(dir, ".gxignore"), []byte(strings.Join(ignore, "\n")), 0644) 357 | } 358 | -------------------------------------------------------------------------------- /link.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "os" 7 | "os/exec" 8 | "path/filepath" 9 | "strings" 10 | 11 | cli "github.com/urfave/cli" 12 | gx "github.com/whyrusleeping/gx/gxutil" 13 | ) 14 | 15 | var pm *gx.PM 16 | 17 | var LinkCommand = cli.Command{ 18 | Name: "link", 19 | Usage: "Symlink packages to their dvcsimport repos, for local development.", 20 | Description: `gx-go link eases local development by symlinking actual workspace repositories on demand. 21 | 22 | gx-go link replaces the target gx package (either by name or hash) with 23 | a symlink to the appropriate dvcs repo in your GOPATH. To make this 24 | "symlinked" repo usable as a gx package, gx-go link rewrites the target 25 | package's dvcs imports using the target package's package.json. 26 | Unfortunately, this can cause build errors in packages depending on this 27 | package if these dependent packages specify alternative, conflicting 28 | dependency versions. We can work around this using the --override-deps flag 29 | to rewrite the target package using dependencies from the current package 30 | (the package you're trying to build) first, falling back on the target 31 | package's package.json file. 32 | 33 | Example workflow: 34 | 35 | > cd $GOPATH/src/github.com/ipfs/go-ipfs 36 | > gx-go link --override-deps go-unixfs 37 | Replaced 39 entries in the rewrite map: 38 | github.com/ipfs/go-ipfs-chunker 39 | github.com/ipfs/go-ipfs-blockstore 40 | github.com/libp2p/go-libp2p-net 41 | [...] 42 | linked go-unixfs /home/user/go/src/github.com/ipfs/go-unixfs 43 | 44 | > gx-go link -r -a 45 | unlinked go-unixfs /home/user/go/src/github.com/ipfs/go-unixfs 46 | `, 47 | Flags: []cli.Flag{ 48 | cli.BoolFlag{ 49 | Name: "r,remove", 50 | Usage: "Remove an existing symlink and reinstate the gx package.", 51 | }, 52 | cli.BoolFlag{ 53 | Name: "a,all", 54 | Usage: "Remove all existing symlinks and reinstate the gx packages. Use with -r.", 55 | }, 56 | cli.BoolFlag{ 57 | Name: "o,override-deps", 58 | Usage: "Override dependency versions of the target package with its current dependant package.", 59 | }, 60 | }, 61 | Action: func(c *cli.Context) error { 62 | remove := c.Bool("remove") 63 | all := c.Bool("all") 64 | overrideDeps := c.Bool("override-deps") 65 | 66 | depRefs := c.Args()[:] 67 | // It can either be a hash or a name. 68 | 69 | if len(depRefs) == 0 { 70 | links, err := listLinkedPackages() 71 | if err != nil { 72 | return err 73 | } 74 | 75 | if remove && all { 76 | for _, link := range links { 77 | depRefs = append(depRefs, link[0]) 78 | } 79 | } 80 | 81 | if !remove { 82 | for _, link := range links { 83 | fmt.Printf("%s %s\n", link[0], link[1]) 84 | } 85 | return nil 86 | } 87 | } 88 | 89 | // Path of the current dependant package. 90 | parentPackagePath, err := gx.GetPackageRoot() 91 | if err != nil { 92 | return fmt.Errorf("error retrieving the parent package: %s", err) 93 | } 94 | 95 | parentPkg, err := LoadPackageFile(filepath.Join(parentPackagePath, gx.PkgFileName)) 96 | if err != nil { 97 | return fmt.Errorf("parent package not found in %s: %s", 98 | parentPackagePath, err) 99 | } 100 | 101 | for _, ref := range depRefs { 102 | dep := parentPkg.FindDep(ref) 103 | if dep == nil { 104 | return fmt.Errorf("dependency reference not found in the parent package: %s", ref) 105 | } 106 | 107 | if remove { 108 | target, err := unlinkDependency(dep) 109 | if err != nil { 110 | return err 111 | } 112 | fmt.Printf("unlinked %s %s\n", dep.Name, target) 113 | } else { 114 | target, err := linkDependency(dep, overrideDeps, parentPackagePath) 115 | if err != nil { 116 | return err 117 | } 118 | fmt.Printf("linked %s %s\n", dep.Name, target) 119 | } 120 | } 121 | 122 | return nil 123 | }, 124 | } 125 | 126 | func listLinkedPackages() ([][]string, error) { 127 | var links [][]string 128 | 129 | srcdir, err := gx.InstallPath("go", "", true) 130 | if err != nil { 131 | return links, err 132 | } 133 | gxbase := filepath.Join(srcdir, "gx", "ipfs") 134 | 135 | filepath.Walk(gxbase, func(path string, fi os.FileInfo, err error) error { 136 | relpath, err := filepath.Rel(gxbase, path) 137 | if err != nil { 138 | return err 139 | } 140 | 141 | parts := strings.Split(relpath, string(os.PathSeparator)) 142 | if len(parts) != 2 { 143 | return nil 144 | } 145 | 146 | if fi.Mode()&os.ModeSymlink != 0 { 147 | target, err := filepath.EvalSymlinks(path) 148 | if err != nil { 149 | return err 150 | } 151 | links = append(links, []string{parts[0], target}) 152 | } 153 | 154 | return nil 155 | }) 156 | 157 | return links, nil 158 | } 159 | 160 | // Link the dependency package `dep` to the global Gx workspace. 161 | // 162 | // The dependency is first fetched to find its DVCS import path (`gx get`), 163 | // then the repository is fetched through `go get` and sym-linked: 164 | // `$GOPATH/gx/ipfs///` -> `$GOPATH/src/` 165 | // (`target`) -> (`linkPath`) 166 | // If `overrideDeps` is set pass the option to the `post-install` hook to override 167 | // dependency versions. 168 | func linkDependency(dep *gx.Dependency, overrideDeps bool, parentPackagePath string) (string, error) { 169 | gxSrcDir, err := gx.InstallPath("go", "", true) 170 | if err != nil { 171 | return "", err 172 | } 173 | 174 | dvcsImport, err := findDepDVCSimport(dep, gxSrcDir) 175 | if err != nil { 176 | return "", fmt.Errorf("error trying to get the DVCS import" + 177 | "of the dependeny %s: %s", dep.Name, err) 178 | } 179 | 180 | target := filepath.Join(gxSrcDir, dvcsImport) 181 | 182 | // Linked package directory, needed for the `post-install` hook. 183 | linkPackageDir := filepath.Join(gxSrcDir, "gx", "ipfs", dep.Hash) 184 | // TODO: this shouldn't be necessary, we should be able to just pass the 185 | // `linkPath` (i.e., the directory with the name of the package). 186 | 187 | linkPath := filepath.Join(linkPackageDir, dep.Name) 188 | 189 | _, err = os.Stat(target) 190 | if os.IsNotExist(err) { 191 | goget := exec.Command("go", "get", dvcsImport+"/...") 192 | goget.Stdout = nil 193 | goget.Stderr = os.Stderr 194 | if err = goget.Run(); err != nil { 195 | return "", fmt.Errorf("error during go get: %s", err) 196 | } 197 | } else if err != nil { 198 | return "", fmt.Errorf("error during os.Stat: %s", err) 199 | } 200 | 201 | err = os.RemoveAll(linkPath) 202 | if err != nil { 203 | return "", fmt.Errorf("error during os.RemoveAll: %s", err) 204 | } 205 | 206 | err = os.Symlink(target, linkPath) 207 | if err != nil { 208 | return "", fmt.Errorf("error during os.Symlink: %s", err) 209 | } 210 | 211 | gxinst := exec.Command("gx", "install") 212 | gxinst.Dir = target 213 | gxinst.Stdout = nil 214 | gxinst.Stderr = os.Stderr 215 | if err = gxinst.Run(); err != nil { 216 | return "", fmt.Errorf("error during gx install: %s", err) 217 | } 218 | 219 | rwcmdArgs := []string{"hook", "post-install", linkPackageDir} 220 | if overrideDeps { 221 | rwcmdArgs = append(rwcmdArgs, "--override-deps", parentPackagePath) 222 | } 223 | rwcmd := exec.Command("gx-go", rwcmdArgs...) 224 | rwcmd.Dir = target 225 | rwcmd.Stdout = os.Stdout 226 | rwcmd.Stderr = os.Stderr 227 | if err := rwcmd.Run(); err != nil { 228 | return "", fmt.Errorf("error during gx-go rw: %s", err) 229 | } 230 | // TODO: Wrap command calls in a function. 231 | 232 | return target, nil 233 | } 234 | 235 | // Return the DVCS import path of a dependency (fetching it 236 | // if necessary). 237 | func findDepDVCSimport(dep *gx.Dependency, gxSrcDir string) (string, error) { 238 | gxdir := filepath.Join(gxSrcDir, "gx", "ipfs", dep.Hash) 239 | 240 | // Get the dependency to find out its DVCS import. 241 | err := gxGetPackage(dep.Hash) 242 | if err != nil { 243 | return "", err 244 | } 245 | 246 | var pkg gx.Package 247 | err = gx.FindPackageInDir(&pkg, gxdir) 248 | if err != nil { 249 | return "", fmt.Errorf("error during gx.FindPackageInDir: %s", err) 250 | } 251 | 252 | return GxDvcsImport(&pkg), nil 253 | } 254 | 255 | // rm -rf $GOPATH/src/gx/ipfs/$hash 256 | // gx get $hash 257 | func unlinkDependency(dep *gx.Dependency) (string, error) { 258 | gxSrcDir, err := gx.InstallPath("go", "", true) 259 | if err != nil { 260 | return "", err 261 | } 262 | 263 | dvcsImport, err := findDepDVCSimport(dep, gxSrcDir) 264 | if err != nil { 265 | return "", fmt.Errorf("error trying to get the DVCS import of the dependeny %s: %s", dep.Name, err) 266 | } 267 | 268 | target := filepath.Join(gxSrcDir, dvcsImport) 269 | 270 | uwcmd := exec.Command("gx-go", "rw", "--fix") 271 | // The `--fix` options is more time consuming (compared to the normal 272 | // `gx-go uw` call) but as some of the import paths may have been written 273 | // from synced dependencies (`gx-go link --sync`) of another package that 274 | // may not be available now (to build the rewrite map) this is the safer 275 | // option. 276 | uwcmd.Dir = target 277 | uwcmd.Stdout = nil 278 | uwcmd.Stderr = os.Stderr 279 | if err := uwcmd.Run(); err != nil { 280 | return "", fmt.Errorf("error during gx-go rw: %s", err) 281 | } 282 | 283 | // Remove the package at the end as `gx-go rw --fix` will need to use it 284 | // (to find the DVCS import paths). 285 | err = os.RemoveAll(filepath.Join(gxSrcDir, "gx", "ipfs", dep.Hash)) 286 | if err != nil { 287 | return "", fmt.Errorf("error during os.RemoveAll: %s", err) 288 | } 289 | 290 | return target, nil 291 | } 292 | 293 | func GxDvcsImport(pkg *gx.Package) string { 294 | pkggx := make(map[string]interface{}) 295 | _ = json.Unmarshal(pkg.Gx, &pkggx) 296 | return pkggx["dvcsimport"].(string) 297 | } 298 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 2 | github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= 3 | github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= 4 | github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= 5 | github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32 h1:qkOC5Gd33k54tobS36cXdAzJbeHaduLtnLQQwNoIi78= 6 | github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8= 7 | github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= 8 | github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= 9 | github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= 10 | github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= 11 | github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= 12 | github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= 13 | github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= 14 | github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927 h1:SKI1/fuSdodxmNNyVBR8d7X/HuLnRpvvFO0AgyQk764= 15 | github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= 16 | github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 17 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= 18 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 19 | github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 20 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 21 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 22 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 23 | github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= 24 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 25 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 26 | github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU= 27 | github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48= 28 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 29 | github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= 30 | github.com/ipfs/go-ipfs-api v0.0.3 h1:1XZBfVDGj0GyyO5WItLrz2opCwezIm9LfFcBfe+sRxM= 31 | github.com/ipfs/go-ipfs-api v0.0.3/go.mod h1:EgBqlEzrA22SnNKq4tcP2GDPKxbfF+uRTd2YFmR1uUk= 32 | github.com/ipfs/go-ipfs-files v0.0.6 h1:sMRtPiSmDrTA2FEiFTtk1vWgO2Dkg7bxXKJ+s8/cDAc= 33 | github.com/ipfs/go-ipfs-files v0.0.6/go.mod h1:lVYE6sgAdtZN5825beJjSAHibw7WOBNPDWz5LaJeukg= 34 | github.com/ipfs/go-ipfs-util v0.0.1/go.mod h1:spsl5z8KUnrve+73pOhSVZND1SIxPW5RyBCNzQxlJBc= 35 | github.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8/go.mod h1:Ly/wlsjFq/qrU3Rar62tu1gASgGw6chQbSh/XgIIXCY= 36 | github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 37 | github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= 38 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 39 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 40 | github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= 41 | github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= 42 | github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= 43 | github.com/libp2p/go-flow-metrics v0.0.1 h1:0gxuFd2GuK7IIP5pKljLwps6TvcuYgvG7Atqi3INF5s= 44 | github.com/libp2p/go-flow-metrics v0.0.1/go.mod h1:Iv1GH0sG8DtYN3SVJ2eG221wMiNpZxBdp967ls1g+k8= 45 | github.com/libp2p/go-libp2p-core v0.0.1 h1:HSTZtFIq/W5Ue43Zw+uWZyy2Vl5WtF0zDjKN8/DT/1I= 46 | github.com/libp2p/go-libp2p-core v0.0.1/go.mod h1:g/VxnTZ/1ygHxH3dKok7Vno1VfpvGcGip57wjTU4fco= 47 | github.com/libp2p/go-libp2p-crypto v0.1.0 h1:k9MFy+o2zGDNGsaoZl0MA3iZ75qXxr9OOoAZF+sD5OQ= 48 | github.com/libp2p/go-libp2p-crypto v0.1.0/go.mod h1:sPUokVISZiy+nNuTTH/TY+leRSxnFj/2GLjtOTW90hI= 49 | github.com/libp2p/go-libp2p-metrics v0.1.0 h1:v7YMUTHNobFaQeqaMfJJMbnK3EPlZeb6/KFm4gE9dks= 50 | github.com/libp2p/go-libp2p-metrics v0.1.0/go.mod h1:rpoJmXWFxnj7qs5sJ02sxSzrhaZvpqBn8GCG6Sx6E1k= 51 | github.com/libp2p/go-libp2p-peer v0.2.0 h1:EQ8kMjaCUwt/Y5uLgjT8iY2qg0mGUT0N1zUjer50DsY= 52 | github.com/libp2p/go-libp2p-peer v0.2.0/go.mod h1:RCffaCvUyW2CJmG2gAWVqwePwW7JMgxjsHm7+J5kjWY= 53 | github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g= 54 | github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= 55 | github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= 56 | github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771 h1:MHkK1uRtFbVqvAgvWxafZe54+5uBxLluGylDiKgdhwo= 57 | github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= 58 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 59 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 60 | github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= 61 | github.com/mr-tron/base58 v1.1.1/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= 62 | github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= 63 | github.com/mr-tron/base58 v1.1.3 h1:v+sk57XuaCKGXpWtVBX8YJzO7hMGx4Aajh4TQbdEFdc= 64 | github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= 65 | github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= 66 | github.com/multiformats/go-multiaddr v0.0.2/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= 67 | github.com/multiformats/go-multiaddr v0.1.0/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= 68 | github.com/multiformats/go-multiaddr v0.2.0 h1:lR52sFwcTCuQb6bTfnXF6zA2XfyYvyd+5a9qECv/J90= 69 | github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4= 70 | github.com/multiformats/go-multiaddr-net v0.1.1/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ= 71 | github.com/multiformats/go-multiaddr-net v0.1.2 h1:P7zcBH9FRETdPkDrylcXVjQLQ2t1JQtNItZULWNWgeg= 72 | github.com/multiformats/go-multiaddr-net v0.1.2/go.mod h1:QsWt3XK/3hwvNxZJp92iMQKME1qHfpYmyIjFVsSOY6Y= 73 | github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs= 74 | github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U= 75 | github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= 76 | github.com/multiformats/go-multihash v0.0.13 h1:06x+mk/zj1FoMsgNejLpy6QTvJqlSt/BhLEy87zidlc= 77 | github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= 78 | github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= 79 | github.com/multiformats/go-varint v0.0.5 h1:XVZwSo04Cs3j/jS0uAEPpT3JY6DzMcVLLoWOSnCxOjg= 80 | github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= 81 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 82 | github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 83 | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 84 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 85 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 86 | github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= 87 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 88 | github.com/sabhiram/go-gitignore v0.0.0-20180611051255-d3107576ba94 h1:G04eS0JkAIVZfaJLjla9dNxkJCPiKIGZlw9AfOhzOD0= 89 | github.com/sabhiram/go-gitignore v0.0.0-20180611051255-d3107576ba94/go.mod h1:b18R55ulyQ/h3RaWyloPyER7fWQVZvimKKhnI5OfrJQ= 90 | github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= 91 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 92 | github.com/spacemonkeygo/openssl v0.0.0-20181017203307-c2dcc5cca94a h1:/eS3yfGjQKG+9kayBkj0ip1BGhq6zJ3eaVksphxAaek= 93 | github.com/spacemonkeygo/openssl v0.0.0-20181017203307-c2dcc5cca94a/go.mod h1:7AyxJNCJ7SBZ1MfVQCWD6Uqo2oubI2Eq2y2eqf+A5r0= 94 | github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+1+HkWaX/Yh71Ee5ZHaHYt7ZP4sQgUrm6cDU= 95 | github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc= 96 | github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= 97 | github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 98 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 99 | github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= 100 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 101 | github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo= 102 | github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 103 | github.com/whyrusleeping/gx v0.14.3 h1:Tn6kM1Rv2Dz6rI2cpWMOeCspdJFaza+cpv8/wIdUNUE= 104 | github.com/whyrusleeping/gx v0.14.3/go.mod h1:HfCLLEulN7GrYs60Cm6NIvvdohZamn/kjTWQX8uCb2o= 105 | github.com/whyrusleeping/json-filter v0.0.0-20160615203754-ff25329a9528 h1:mhhwUOVd274QT9VTCjIYhCDR4RCE9tqNDyHQVB5Rd9k= 106 | github.com/whyrusleeping/json-filter v0.0.0-20160615203754-ff25329a9528/go.mod h1:5a88m1gFWhTL3QwRdNm1fiRNrBTME7Ch8f8pZZZes9g= 107 | github.com/whyrusleeping/progmeter v0.0.0-20180725015555-f3e57218a75b h1:jMJLc+G2DWK2ZX+C+X4Jv7x2ss+XReNGNMpQ+a3fdqo= 108 | github.com/whyrusleeping/progmeter v0.0.0-20180725015555-f3e57218a75b/go.mod h1:gyCeSVnUb+LQh0QCWbg0Sl30ckl2YgNC+yvSFvy5mFY= 109 | github.com/whyrusleeping/stump v0.0.0-20160611222256-206f8f13aae1 h1:57Pu2W5KJQ3qRtqKYqOS4GQtkwxGH4ppL4mLWaPnfZU= 110 | github.com/whyrusleeping/stump v0.0.0-20160611222256-206f8f13aae1/go.mod h1:Qv7QS+Xqv+q5lhOfseae0ZWF0wliLmab2hmikKoLhgE= 111 | github.com/whyrusleeping/tar-utils v0.0.0-20180509141711-8c6c8ba81d5c h1:GGsyl0dZ2jJgVT+VvWBf/cNijrHRhkrTjkmp5wg7li0= 112 | github.com/whyrusleeping/tar-utils v0.0.0-20180509141711-8c6c8ba81d5c/go.mod h1:xxcJeBb7SIUl/Wzkz1eVKJE/CB34YNrqX2TQI6jY9zs= 113 | golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 114 | golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 115 | golang.org/x/crypto v0.0.0-20190225124518-7f87c0fbb88b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 116 | golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 117 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 118 | golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 h1:1wopBVtVdWnn03fZelqdXTqk7U7zPQCb+T4rbU9ZEoU= 119 | golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 120 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 121 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 122 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 123 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 124 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 125 | golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 126 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 127 | golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 128 | golang.org/x/sys v0.0.0-20190302025703-b6889370fb10/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 129 | golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= 130 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 131 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 132 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 133 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 134 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 135 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 136 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 137 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 138 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 139 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 140 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "encoding/json" 6 | "fmt" 7 | "io/ioutil" 8 | "os" 9 | "os/exec" 10 | "path" 11 | "path/filepath" 12 | "runtime" 13 | "sort" 14 | "strconv" 15 | "strings" 16 | "text/tabwriter" 17 | 18 | homedir "github.com/mitchellh/go-homedir" 19 | cli "github.com/urfave/cli" 20 | rw "github.com/whyrusleeping/gx-go/rewrite" 21 | gx "github.com/whyrusleeping/gx/gxutil" 22 | . "github.com/whyrusleeping/stump" 23 | ) 24 | 25 | var vendorDir = filepath.Join("vendor", "gx", "ipfs") 26 | 27 | var cwd string 28 | 29 | // for go packages, extra info 30 | type GoInfo struct { 31 | DvcsImport string `json:"dvcsimport,omitempty"` 32 | 33 | // GoVersion sets a compiler version requirement, users will be warned if installing 34 | // a package using an unsupported compiler 35 | GoVersion string `json:"goversion,omitempty"` 36 | } 37 | 38 | type Package struct { 39 | gx.PackageBase 40 | 41 | Gx GoInfo `json:"gx,omitempty"` 42 | } 43 | 44 | func LoadPackageFile(name string) (*Package, error) { 45 | fi, err := os.Open(name) 46 | if err != nil { 47 | return nil, err 48 | } 49 | 50 | var pkg Package 51 | err = json.NewDecoder(fi).Decode(&pkg) 52 | if err != nil { 53 | return nil, err 54 | } 55 | 56 | return &pkg, nil 57 | } 58 | 59 | func main() { 60 | app := cli.NewApp() 61 | app.Name = "gx-go" 62 | app.Author = "whyrusleeping" 63 | app.Usage = "gx extensions for golang" 64 | app.Version = "1.9.0" 65 | app.Flags = []cli.Flag{ 66 | cli.BoolFlag{ 67 | Name: "verbose", 68 | Usage: "turn on verbose output", 69 | }, 70 | } 71 | app.Before = func(c *cli.Context) error { 72 | Verbose = c.Bool("verbose") 73 | return nil 74 | } 75 | 76 | mcwd, err := os.Getwd() 77 | if err != nil { 78 | Fatal("failed to get cwd:", err) 79 | } 80 | lcwd, err := filepath.EvalSymlinks(mcwd) 81 | if err != nil { 82 | Fatal("failed to resolve symlinks of cdw:", err) 83 | } 84 | cwd = lcwd 85 | 86 | app.Commands = []cli.Command{ 87 | DepMapCommand, 88 | HookCommand, 89 | ImportCommand, 90 | PathCommand, 91 | RewriteCommand, 92 | rewriteUndoAlias, 93 | UpdateCommand, 94 | DvcsDepsCommand, 95 | LinkCommand, 96 | LockGenCommand, 97 | 98 | DevCopyCommand, 99 | // Go tool compat: 100 | GetCommand, 101 | } 102 | 103 | if err := app.Run(os.Args); err != nil { 104 | Fatal("Error: " + err.Error()) 105 | } 106 | } 107 | 108 | var DepMapCommand = cli.Command{ 109 | Name: "dep-map", 110 | Usage: "prints out a json dep map for usage by 'import --map'", 111 | Action: func(c *cli.Context) error { 112 | pkg, err := LoadPackageFile(gx.PkgFileName) 113 | if err != nil { 114 | return err 115 | } 116 | 117 | m := make(map[string]string) 118 | err = buildMap(pkg, m) 119 | if err != nil { 120 | return err 121 | } 122 | 123 | out, err := json.MarshalIndent(m, "", " ") 124 | if err != nil { 125 | return err 126 | } 127 | 128 | os.Stdout.Write(out) 129 | return nil 130 | }, 131 | } 132 | 133 | var LockGenCommand = cli.Command{ 134 | Name: "lock-gen", 135 | Usage: "Generate a lock file", 136 | Flags: []cli.Flag{ 137 | cli.BoolFlag{ 138 | Name: "ignore-conflicts", 139 | Usage: "Does not error on conflicting import in sub-packages", 140 | }, 141 | }, 142 | Action: func(c *cli.Context) error { 143 | ignoreConflict := c.Bool("ignore-conflicts") 144 | 145 | pkg, err := LoadPackageFile(gx.PkgFileName) 146 | if err != nil { 147 | return err 148 | } 149 | 150 | done := make(map[string]bool) 151 | lockFile := gx.LockFile{ 152 | LockVersion: gx.LockVersion, 153 | } 154 | lockFile.Language = pkg.Language 155 | lockFile.Deps = make(map[string]map[string]gx.Lock) 156 | lockFile.Deps[pkg.Language] = make(map[string]gx.Lock) 157 | 158 | if err := genLockDeps(pkg, lockFile.Deps[pkg.Language], done, ignoreConflict); err != nil { 159 | return err 160 | } 161 | 162 | m, err := json.MarshalIndent(lockFile, "", " ") 163 | if err != nil { 164 | return err 165 | } 166 | 167 | _, err = os.Stdout.Write(m) 168 | return err 169 | }, 170 | } 171 | 172 | var HookCommand = cli.Command{ 173 | Name: "hook", 174 | Usage: "go specific hooks to be called by the gx tool", 175 | Subcommands: []cli.Command{ 176 | postImportCommand, 177 | reqCheckCommand, 178 | installLocHookCommand, 179 | postInitHookCommand, 180 | postUpdateHookCommand, 181 | postInstallHookCommand, 182 | preTestHookCommand, 183 | postTestHookCommand, 184 | testHookCommand, 185 | }, 186 | Action: func(c *cli.Context) error { return nil }, 187 | } 188 | 189 | var ImportCommand = cli.Command{ 190 | Name: "import", 191 | Usage: "import a go package and all its depencies into gx", 192 | Description: `imports a given go package and all of its dependencies into gx 193 | producing a package.json for each, and outputting a package hash 194 | for each.`, 195 | Flags: []cli.Flag{ 196 | cli.BoolFlag{ 197 | Name: "rewrite", 198 | Usage: "rewrite import paths to use vendored packages", 199 | }, 200 | cli.BoolFlag{ 201 | Name: "yesall", 202 | Usage: "assume defaults for all options", 203 | }, 204 | cli.BoolFlag{ 205 | Name: "tmpdir", 206 | Usage: "create and use a temporary directory for the GOPATH", 207 | }, 208 | cli.StringFlag{ 209 | Name: "map", 210 | Usage: "json document mapping imports to prexisting hashes", 211 | }, 212 | }, 213 | Action: func(c *cli.Context) error { 214 | var mapping map[string]string 215 | preset := c.String("map") 216 | if preset != "" { 217 | err := loadMap(&mapping, preset) 218 | if err != nil { 219 | return err 220 | } 221 | } 222 | 223 | var gopath string 224 | if c.Bool("tmpdir") { 225 | dir, err := ioutil.TempDir("", "gx-go-import") 226 | if err != nil { 227 | return fmt.Errorf("creating temp dir: %s", err) 228 | } 229 | err = os.Setenv("GOPATH", dir) 230 | if err != nil { 231 | return fmt.Errorf("setting GOPATH: %s", err) 232 | } 233 | Log("setting GOPATH to", dir) 234 | 235 | gopath = dir 236 | } else { 237 | gp, err := getGoPath() 238 | if err != nil { 239 | return fmt.Errorf("couldnt determine gopath: %s", err) 240 | } 241 | 242 | gopath = gp 243 | } 244 | 245 | importer, err := NewImporter(c.Bool("rewrite"), gopath, mapping) 246 | if err != nil { 247 | return err 248 | } 249 | 250 | importer.yesall = c.Bool("yesall") 251 | 252 | if !c.Args().Present() { 253 | return fmt.Errorf("must specify a package name") 254 | } 255 | 256 | pkg := c.Args().First() 257 | Log("vendoring package %s", pkg) 258 | 259 | _, err = importer.GxPublishGoPackage(pkg) 260 | if err != nil { 261 | return err 262 | } 263 | 264 | return nil 265 | }, 266 | } 267 | 268 | var UpdateCommand = cli.Command{ 269 | Name: "update", 270 | Usage: "update a packages imports to a new path", 271 | ArgsUsage: "[old import] [new import]", 272 | Action: func(c *cli.Context) error { 273 | if len(c.Args()) < 2 { 274 | return fmt.Errorf("must specify current and new import names") 275 | } 276 | 277 | oldimp := c.Args()[0] 278 | newimp := c.Args()[1] 279 | 280 | err := doUpdate(cwd, oldimp, newimp) 281 | if err != nil { 282 | return err 283 | } 284 | 285 | return nil 286 | }, 287 | } 288 | 289 | var rewriteUndoAlias = cli.Command{ 290 | Name: "uw", 291 | Action: func(c *cli.Context) error { 292 | return fullRewrite(true) 293 | }, 294 | } 295 | 296 | var RewriteCommand = cli.Command{ 297 | Name: "rewrite", 298 | Usage: "temporary hack to evade causality", 299 | ArgsUsage: "[optional package name]", 300 | Aliases: []string{"rw"}, 301 | Flags: []cli.Flag{ 302 | cli.BoolFlag{ 303 | Name: "undo", 304 | Usage: "rewrite import paths back to dvcs", 305 | }, 306 | cli.BoolFlag{ 307 | Name: "dry-run", 308 | Usage: "print out mapping without touching files", 309 | }, 310 | cli.StringFlag{ 311 | Name: "pkgdir", 312 | Usage: "alternative location of the package directory", 313 | }, 314 | cli.BoolFlag{ 315 | Name: "fix", 316 | Usage: "more error tolerant version of '--undo'", 317 | }, 318 | }, 319 | Action: func(c *cli.Context) error { 320 | root, err := gx.GetPackageRoot() 321 | if err != nil { 322 | return err 323 | } 324 | 325 | if c.Bool("fix") { 326 | return fixImports(root) 327 | } 328 | 329 | pkg, err := LoadPackageFile(filepath.Join(root, gx.PkgFileName)) 330 | if err != nil { 331 | return err 332 | } 333 | 334 | pkgdir := filepath.Join(root, vendorDir) 335 | if pdopt := c.String("pkgdir"); pdopt != "" { 336 | pkgdir = pdopt 337 | } 338 | 339 | VLog(" - building rewrite mapping") 340 | mapping := make(map[string]string) 341 | if !c.Args().Present() { 342 | err = buildRewriteMapping(pkg, pkgdir, mapping, c.Bool("undo")) 343 | if err != nil { 344 | return fmt.Errorf("build of rewrite mapping failed:\n%s", err) 345 | } 346 | } else { 347 | for _, arg := range c.Args() { 348 | dep := pkg.FindDep(arg) 349 | if dep == nil { 350 | return fmt.Errorf("%s not found", arg) 351 | } 352 | 353 | pkg, err := loadDep(dep, pkgdir) 354 | if err != nil { 355 | return err 356 | } 357 | 358 | addRewriteForDep(dep, pkg, mapping, c.Bool("undo"), true) 359 | } 360 | } 361 | VLog(" - rewrite mapping complete") 362 | 363 | if c.Bool("dry-run") { 364 | tabPrintSortedMap(nil, mapping) 365 | return nil 366 | } 367 | 368 | err = doRewrite(pkg, root, mapping) 369 | if err != nil { 370 | return err 371 | } 372 | 373 | return nil 374 | }, 375 | } 376 | 377 | var DvcsDepsCommand = cli.Command{ 378 | Name: "dvcs-deps", 379 | Usage: "display all dvcs deps", 380 | Action: func(c *cli.Context) error { 381 | i, err := NewImporter(false, os.Getenv("GOPATH"), nil) 382 | if err != nil { 383 | return err 384 | } 385 | 386 | relp, err := getImportPath(cwd) 387 | if err != nil { 388 | return err 389 | } 390 | 391 | deps, err := i.DepsToVendorForPackage(relp) 392 | if err != nil { 393 | return err 394 | } 395 | 396 | sort.Strings(deps) 397 | for _, d := range deps { 398 | fmt.Println(d) 399 | } 400 | 401 | return nil 402 | }, 403 | } 404 | 405 | func getImportPath(pkgpath string) (string, error) { 406 | pkg, err := LoadPackageFile(filepath.Join(pkgpath, gx.PkgFileName)) 407 | if err != nil { 408 | return "", err 409 | } 410 | return pkg.Gx.DvcsImport, nil 411 | } 412 | 413 | var PathCommand = cli.Command{ 414 | Name: "path", 415 | Usage: "prints the import path of the current package within GOPATH", 416 | Action: func(c *cli.Context) error { 417 | rel, err := getImportPath(cwd) 418 | if err != nil { 419 | return err 420 | } 421 | 422 | fmt.Println(rel) 423 | return nil 424 | }, 425 | } 426 | 427 | func goGetPackage(path string) error { 428 | cmd := exec.Command("go", "get", "-d", path) 429 | cmd.Stdout = os.Stdout 430 | cmd.Stderr = os.Stderr 431 | cmd.Run() 432 | return nil 433 | } 434 | 435 | func gxGetPackage(hash string) error { 436 | srcdir, err := gx.InstallPath("go", "", true) 437 | if err != nil { 438 | return err 439 | } 440 | gxdir := filepath.Join(srcdir, "gx", "ipfs", hash) 441 | 442 | gxget := exec.Command("gx", "get", hash, "-o", gxdir) 443 | gxget.Stdout = os.Stderr 444 | gxget.Stderr = os.Stderr 445 | if err = gxget.Run(); err != nil { 446 | return fmt.Errorf("error during gx get: %s", err) 447 | } 448 | 449 | return nil 450 | } 451 | 452 | func fixImports(path string) error { 453 | fixmap := make(map[string]string) 454 | gopath := os.Getenv("GOPATH") 455 | rwf := func(imp string) string { 456 | if strings.HasPrefix(imp, "gx/ipfs/") { 457 | parts := strings.Split(imp, "/") 458 | canon := strings.Join(parts[:4], "/") 459 | rest := strings.Join(parts[4:], "/") 460 | if rest != "" { 461 | rest = "/" + rest 462 | } 463 | 464 | if base, ok := fixmap[canon]; ok { 465 | return base + rest 466 | } 467 | 468 | var pkg Package 469 | err := gx.FindPackageInDir(&pkg, filepath.Join(gopath, "src", canon)) 470 | if err != nil { 471 | hash := parts[2] 472 | err = gxGetPackage(hash) 473 | if err != nil { 474 | VLog(err) 475 | return imp 476 | } 477 | err := gx.FindPackageInDir(&pkg, filepath.Join(gopath, "src", canon)) 478 | if err != nil { 479 | VLog(err) 480 | return imp 481 | } 482 | } 483 | 484 | if pkg.Gx.DvcsImport != "" { 485 | fixmap[imp] = pkg.Gx.DvcsImport 486 | return pkg.Gx.DvcsImport + rest 487 | } 488 | fmt.Printf("Package %s has no dvcs import set!\n", imp) 489 | } 490 | return imp 491 | } 492 | 493 | filter := func(s string) bool { 494 | return strings.HasSuffix(s, ".go") 495 | } 496 | return rw.RewriteImports(path, rwf, filter) 497 | } 498 | 499 | var GetCommand = cli.Command{ 500 | Name: "get", 501 | Usage: "gx-ified `go get`", 502 | Action: func(c *cli.Context) error { 503 | pkgpath := c.Args().First() 504 | if err := goGetPackage(pkgpath); err != nil { 505 | return err 506 | } 507 | 508 | gpath, err := getGoPath() 509 | if err != nil { 510 | return err 511 | } 512 | 513 | pkgdir := filepath.Join(gpath, "src", pkgpath) 514 | 515 | cmd := exec.Command("gx", "install") 516 | cmd.Dir = pkgdir 517 | cmd.Stdout = os.Stdout 518 | cmd.Stderr = os.Stderr 519 | 520 | if err := cmd.Run(); err != nil { 521 | return err 522 | } 523 | 524 | var pkg Package 525 | if err := gx.LoadPackageFile(&pkg, filepath.Join(pkgdir, "package.json")); err != nil { 526 | return err 527 | } 528 | 529 | depsdir := filepath.Join(pkgdir, vendorDir) 530 | rwmapping := make(map[string]string) 531 | if err := buildRewriteMapping(&pkg, depsdir, rwmapping, false); err != nil { 532 | return err 533 | } 534 | 535 | if err := doRewrite(&pkg, pkgdir, rwmapping); err != nil { 536 | return err 537 | } 538 | 539 | return nil 540 | }, 541 | } 542 | 543 | func prompt(text, def string) (string, error) { 544 | scan := bufio.NewScanner(os.Stdin) 545 | fmt.Printf("%s (default: '%s') ", text, def) 546 | for scan.Scan() { 547 | if scan.Text() != "" { 548 | return scan.Text(), nil 549 | } 550 | return def, nil 551 | } 552 | 553 | return "", scan.Err() 554 | } 555 | 556 | func yesNoPrompt(prompt string, def bool) bool { 557 | opts := "[y/N]" 558 | if def { 559 | opts = "[Y/n]" 560 | } 561 | 562 | fmt.Printf("%s %s ", prompt, opts) 563 | scan := bufio.NewScanner(os.Stdin) 564 | for scan.Scan() { 565 | val := strings.ToLower(scan.Text()) 566 | switch val { 567 | case "": 568 | return def 569 | case "y": 570 | return true 571 | case "n": 572 | return false 573 | default: 574 | fmt.Println("please type 'y' or 'n'") 575 | } 576 | } 577 | 578 | panic("unexpected termination of stdin") 579 | } 580 | 581 | var postImportCommand = cli.Command{ 582 | Name: "post-import", 583 | Usage: "hook called after importing a new go package", 584 | Action: func(c *cli.Context) error { 585 | if !c.Args().Present() { 586 | Fatal("no package specified") 587 | } 588 | dephash := c.Args().First() 589 | 590 | pkg, err := LoadPackageFile(gx.PkgFileName) 591 | if err != nil { 592 | return err 593 | } 594 | 595 | err = postImportHook(pkg, dephash) 596 | if err != nil { 597 | return err 598 | } 599 | 600 | return nil 601 | }, 602 | } 603 | 604 | var reqCheckCommand = cli.Command{ 605 | Name: "req-check", 606 | Usage: "hook called to check if requirements of a package are met", 607 | Action: func(c *cli.Context) error { 608 | if !c.Args().Present() { 609 | Fatal("no package specified") 610 | } 611 | pkgpath := c.Args().First() 612 | 613 | err := reqCheckHook(pkgpath) 614 | if err != nil { 615 | return err 616 | } 617 | 618 | return nil 619 | }, 620 | } 621 | 622 | var postInitHookCommand = cli.Command{ 623 | Name: "post-init", 624 | Usage: "hook called to perform go specific package initialization", 625 | Action: func(c *cli.Context) error { 626 | var dir string 627 | if c.Args().Present() { 628 | dir = c.Args().First() 629 | } else { 630 | dir = cwd 631 | } 632 | 633 | pkgpath := filepath.Join(dir, gx.PkgFileName) 634 | pkg, err := LoadPackageFile(pkgpath) 635 | if err != nil { 636 | return err 637 | } 638 | 639 | imp, _ := packagesGoImport(dir) 640 | 641 | if imp != "" { 642 | pkg.Gx.DvcsImport = imp 643 | } 644 | 645 | err = gx.SavePackageFile(pkg, pkgpath) 646 | if err != nil { 647 | return err 648 | } 649 | 650 | return nil 651 | }, 652 | } 653 | 654 | var postInstallHookCommand = cli.Command{ 655 | Name: "post-install", 656 | Usage: "post install hook for newly installed go packages", 657 | Flags: []cli.Flag{ 658 | cli.BoolFlag{ 659 | Name: "global", 660 | Usage: "specifies whether or not the install was global", 661 | }, 662 | cli.StringFlag{ 663 | Name: "override-deps", 664 | Usage: "path of a package used to override dependency versions (intended to be used with the link command)", 665 | }, 666 | }, 667 | Action: func(c *cli.Context) error { 668 | if !c.Args().Present() { 669 | return fmt.Errorf("must specify path to newly installed package") 670 | } 671 | npkg := c.Args().First() 672 | // update sub-package refs here 673 | // ex: 674 | // if this package is 'github.com/X/Y' replace all imports 675 | // matching 'github.com/X/Y*' with 'gx//name*' 676 | 677 | var pkg Package 678 | err := gx.FindPackageInDir(&pkg, npkg) 679 | if err != nil { 680 | return fmt.Errorf("find package failed: %s", err) 681 | } 682 | 683 | dir := filepath.Join(npkg, pkg.Name) 684 | 685 | // build rewrite mapping from parent package if 686 | // this call is made on one in the vendor directory 687 | var reldir string 688 | if strings.Contains(npkg, "vendor/gx/ipfs") { 689 | reldir = strings.Split(npkg, "vendor/gx/ipfs")[0] 690 | reldir = filepath.Join(reldir, "vendor", "gx", "ipfs") 691 | } else { 692 | reldir = dir 693 | } 694 | 695 | var depsPkg *Package 696 | var depsPkgDir string 697 | if depsPkgDir = c.String("override-deps"); depsPkgDir != "" { 698 | err := gx.FindPackageInDir(&depsPkg, depsPkgDir) 699 | if err != nil { 700 | return fmt.Errorf("find deps package in %s failed : %s", depsPkgDir, err) 701 | } 702 | } 703 | 704 | mapping := make(map[string]string) 705 | err = buildRewriteMapping(&pkg, reldir, mapping, false) 706 | if err != nil { 707 | return fmt.Errorf("building rewrite mapping failed for package %s: %s", pkg.Name, err) 708 | } 709 | 710 | if depsPkg != nil { 711 | // Use the dependency versions of `depsPkg` in case of a mismatch 712 | // with the versions in `pkg`. 713 | 714 | depsRewriteMap := make(map[string]string) 715 | err = buildRewriteMapping(depsPkg, depsPkgDir, depsRewriteMap, false) 716 | if err != nil { 717 | return fmt.Errorf("building rewrite mapping failed for package %s: %s", depsPkg.Name, err) 718 | // TODO: All the dependencies of the deps package need to be fetched. Should we call 719 | // `gx install --global`? 720 | } 721 | 722 | // Iterate the `rewriteMap` indexed by the DVCS imports (since `undo` 723 | // is false) and replace them with dependencies found in the 724 | // `depsRewriteMap` if the gx import paths don't match (that is, 725 | // if their versions are different). 726 | var replacedImports []string 727 | for dvcsImport, gxImportPath := range mapping { 728 | depsGxImportPath, exists := depsRewriteMap[dvcsImport] 729 | 730 | if exists && gxImportPath != depsGxImportPath { 731 | mapping[dvcsImport] = depsGxImportPath 732 | replacedImports = append(replacedImports, dvcsImport) 733 | } 734 | } 735 | 736 | if len(replacedImports) > 0 { 737 | fmt.Printf("Replaced %d entries in the rewrite map:\n", len(replacedImports)) 738 | for _, dvcsImport := range replacedImports { 739 | fmt.Printf(" %s\n", dvcsImport) 740 | } 741 | } 742 | // TODO: This should be handled by the `VLog` function. 743 | // (At the moment this is called from `gx-go link` which doesn't 744 | // have access to the global `Verbose` flag to check whether to 745 | // include the `--verbose` argument or not.) 746 | } 747 | 748 | hash := filepath.Base(npkg) 749 | newimp := "gx/ipfs/" + hash + "/" + pkg.Name 750 | mapping[pkg.Gx.DvcsImport] = newimp 751 | 752 | err = doRewrite(&pkg, dir, mapping) 753 | if err != nil { 754 | return fmt.Errorf("rewrite failed: %s", err) 755 | } 756 | 757 | return nil 758 | }, 759 | } 760 | 761 | func doRewrite(pkg *Package, cwd string, mapping map[string]string) error { 762 | rwm := func(in string) string { 763 | m, ok := mapping[in] 764 | if ok { 765 | return m 766 | } 767 | 768 | for k, v := range mapping { 769 | if strings.HasPrefix(in, k+"/") { 770 | nmapping := strings.Replace(in, k, v, 1) 771 | mapping[in] = nmapping 772 | return nmapping 773 | } 774 | } 775 | 776 | mapping[in] = in 777 | return in 778 | } 779 | 780 | filter := func(s string) bool { 781 | return strings.HasSuffix(s, ".go") 782 | } 783 | 784 | VLog(" - rewriting imports") 785 | err := rw.RewriteImports(cwd, rwm, filter) 786 | if err != nil { 787 | return err 788 | } 789 | VLog(" - finished!") 790 | 791 | return nil 792 | } 793 | 794 | var installLocHookCommand = cli.Command{ 795 | Name: "install-path", 796 | Usage: "prints out install path", 797 | Flags: []cli.Flag{ 798 | cli.BoolFlag{ 799 | Name: "global", 800 | Usage: "print global install directory", 801 | }, 802 | }, 803 | Action: func(c *cli.Context) error { 804 | if c.Bool("global") { 805 | gpath, err := getGoPath() 806 | if err != nil { 807 | return fmt.Errorf("GOPATH not set") 808 | } 809 | fmt.Println(filepath.Join(gpath, "src")) 810 | return nil 811 | } else { 812 | cwd, err := os.Getwd() 813 | if err != nil { 814 | return fmt.Errorf("install-path cwd: %s", err) 815 | } 816 | 817 | fmt.Println(filepath.Join(cwd, "vendor")) 818 | return nil 819 | } 820 | }, 821 | } 822 | 823 | var postUpdateHookCommand = cli.Command{ 824 | Name: "post-update", 825 | Usage: "rewrite go package imports to new versions", 826 | Action: func(c *cli.Context) error { 827 | if len(c.Args()) < 2 { 828 | Fatal("must specify two arguments") 829 | } 830 | before := "gx/ipfs/" + c.Args()[0] 831 | after := "gx/ipfs/" + c.Args()[1] 832 | err := doUpdate(cwd, before, after) 833 | if err != nil { 834 | return err 835 | } 836 | 837 | return nil 838 | }, 839 | } 840 | 841 | var testHookCommand = cli.Command{ 842 | Name: "test", 843 | SkipFlagParsing: true, 844 | Action: func(c *cli.Context) error { 845 | args := []string{"test"} 846 | args = append(args, c.Args()...) 847 | cmd := exec.Command("go", args...) 848 | cmd.Stderr = os.Stderr 849 | cmd.Stdin = os.Stdin 850 | cmd.Stdout = os.Stdout 851 | return cmd.Run() 852 | }, 853 | } 854 | 855 | var preTestHookCommand = cli.Command{ 856 | Name: "pre-test", 857 | Usage: "", 858 | Action: func(c *cli.Context) error { 859 | return fullRewrite(false) 860 | }, 861 | } 862 | 863 | var postTestHookCommand = cli.Command{ 864 | Name: "post-test", 865 | Usage: "", 866 | Action: func(c *cli.Context) error { 867 | return fullRewrite(true) 868 | }, 869 | } 870 | 871 | var DevCopyCommand = cli.Command{ 872 | Name: "devcopy", 873 | Usage: "Create a development copy of the given package", 874 | Action: func(c *cli.Context) error { 875 | // gx install --local 876 | // gx-go rewrite --undo 877 | // symlink -> dvcs path 878 | 879 | Log("creating local copy of deps") 880 | cmd := exec.Command("gx", "install", "--local") 881 | cmd.Stderr = os.Stderr 882 | cmd.Stdout = os.Stdout 883 | if err := cmd.Run(); err != nil { 884 | return err 885 | } 886 | 887 | Log("change imports to dvcs") 888 | cmd = exec.Command("gx-go", "rewrite", "--undo") 889 | cmd.Stderr = os.Stderr 890 | cmd.Stdout = os.Stdout 891 | if err := cmd.Run(); err != nil { 892 | return err 893 | } 894 | 895 | pkg, err := LoadPackageFile(gx.PkgFileName) 896 | if err != nil { 897 | return err 898 | } 899 | 900 | return devCopySymlinking(filepath.Join(cwd, "vendor"), pkg, make(map[string]bool)) 901 | }, 902 | } 903 | 904 | func genLockDeps(pkg *Package, deps map[string]gx.Lock, done map[string]bool, ignoreConflict bool) error { 905 | for _, dep := range pkg.Dependencies { 906 | if done[dep.Hash] { 907 | continue 908 | } 909 | 910 | done[dep.Hash] = true 911 | 912 | var cpkg Package 913 | err := gx.LoadPackage(&cpkg, pkg.Language, dep.Hash) 914 | if err != nil { 915 | if os.IsNotExist(err) { 916 | VLog("LoadPackage error: ", err) 917 | return fmt.Errorf("package %s (%s) not found", dep.Name, dep.Hash) 918 | } 919 | return err 920 | } 921 | 922 | ref := fmt.Sprintf("/ipfs/%s/%s", dep.Hash, dep.Name) 923 | if d, found := deps[cpkg.Gx.DvcsImport]; found { 924 | if !ignoreConflict && ref != d.Ref { 925 | return fmt.Errorf("Found a duplicate import %s for package %s", cpkg.Gx.DvcsImport, pkg.Name) 926 | } 927 | } else { 928 | deps[cpkg.Gx.DvcsImport] = gx.Lock{ 929 | Ref: ref, 930 | } 931 | } 932 | 933 | if err := genLockDeps(&cpkg, deps, done, ignoreConflict); err != nil { 934 | return err 935 | } 936 | } 937 | 938 | return nil 939 | } 940 | 941 | func devCopySymlinking(root string, pkg *Package, done map[string]bool) error { 942 | for _, dep := range pkg.Dependencies { 943 | if done[dep.Hash] { 944 | continue 945 | } 946 | done[dep.Hash] = true 947 | 948 | var cpkg Package 949 | err := gx.LoadPackage(&cpkg, pkg.Language, dep.Hash) 950 | if err != nil { 951 | if os.IsNotExist(err) { 952 | VLog("LoadPackage error: ", err) 953 | return fmt.Errorf("package %s (%s) not found", dep.Name, dep.Hash) 954 | } 955 | return err 956 | } 957 | 958 | frompath := filepath.Join(root, "gx", "ipfs", dep.Hash, dep.Name) 959 | cmd := exec.Command("gx-go", "rewrite", "--undo") 960 | cmd.Stdout = os.Stdout 961 | cmd.Stderr = os.Stderr 962 | cmd.Dir = frompath 963 | if err := cmd.Run(); err != nil { 964 | return err 965 | } 966 | 967 | topath := filepath.Join(root, cpkg.Gx.DvcsImport) 968 | dir := filepath.Dir(topath) 969 | if err := os.MkdirAll(dir, 0755); err != nil { 970 | return err 971 | } 972 | 973 | if err := os.Symlink(frompath, topath); err != nil { 974 | return err 975 | } 976 | 977 | if err := devCopySymlinking(root, &cpkg, done); err != nil { 978 | return err 979 | } 980 | } 981 | return nil 982 | } 983 | 984 | func fullRewrite(undo bool) error { 985 | root, err := gx.GetPackageRoot() 986 | if err != nil { 987 | return err 988 | } 989 | 990 | pkg, err := LoadPackageFile(filepath.Join(root, gx.PkgFileName)) 991 | if err != nil { 992 | return err 993 | } 994 | 995 | pkgdir := filepath.Join(root, vendorDir) 996 | 997 | mapping := make(map[string]string) 998 | err = buildRewriteMapping(pkg, pkgdir, mapping, undo) 999 | if err != nil { 1000 | return fmt.Errorf("build of rewrite mapping failed:\n%s", err) 1001 | } 1002 | 1003 | return doRewrite(pkg, root, mapping) 1004 | } 1005 | 1006 | func packagesGoImport(p string) (string, error) { 1007 | gopath, err := getGoPath() 1008 | if err != nil { 1009 | return "", err 1010 | } 1011 | 1012 | srcdir := path.Join(gopath, "src") 1013 | srcdir += "/" 1014 | 1015 | if !strings.HasPrefix(p, srcdir) { 1016 | return "", fmt.Errorf("package not within GOPATH/src") 1017 | } 1018 | 1019 | return p[len(srcdir):], nil 1020 | } 1021 | 1022 | func postImportHook(pkg *Package, npkgHash string) error { 1023 | var npkg Package 1024 | err := gx.LoadPackage(&npkg, "go", npkgHash) 1025 | if err != nil { 1026 | return err 1027 | } 1028 | 1029 | if npkg.Gx.DvcsImport != "" { 1030 | q := fmt.Sprintf("update imports of %s to the newly imported package?", npkg.Gx.DvcsImport) 1031 | if yesNoPrompt(q, false) { 1032 | nimp := fmt.Sprintf("gx/ipfs/%s/%s", npkgHash, npkg.Name) 1033 | err := doUpdate(cwd, npkg.Gx.DvcsImport, nimp) 1034 | if err != nil { 1035 | return err 1036 | } 1037 | } 1038 | } 1039 | 1040 | return nil 1041 | } 1042 | 1043 | func reqCheckHook(pkgpath string) error { 1044 | var npkg Package 1045 | pkgfile := filepath.Join(pkgpath, gx.PkgFileName) 1046 | err := gx.LoadPackageFile(&npkg, pkgfile) 1047 | if err != nil { 1048 | return err 1049 | } 1050 | 1051 | if npkg.Gx.GoVersion != "" { 1052 | out, err := exec.Command("go", "version").CombinedOutput() 1053 | if err != nil { 1054 | return fmt.Errorf("no go compiler installed") 1055 | } 1056 | 1057 | parts := strings.Split(string(out), " ") 1058 | if len(parts) < 4 { 1059 | return fmt.Errorf("unrecognized output from go compiler") 1060 | } 1061 | if parts[2] == "devel" { 1062 | Log("warning: using unknown development version of go, proceed with caution") 1063 | return nil 1064 | } 1065 | 1066 | havevers := parts[2][2:] 1067 | 1068 | reqvers := npkg.Gx.GoVersion 1069 | 1070 | badreq, err := versionComp(havevers, reqvers) 1071 | if err != nil { 1072 | return err 1073 | } 1074 | if badreq { 1075 | return fmt.Errorf("package '%s' requires at least go version %s, you have %s installed.", npkg.Name, reqvers, havevers) 1076 | } 1077 | 1078 | gxgocompvers := runtime.Version() 1079 | if strings.HasPrefix(gxgocompvers, "devel") { 1080 | return nil 1081 | } 1082 | if strings.HasPrefix(gxgocompvers, "go") { 1083 | badreq, err := versionComp(gxgocompvers[2:], reqvers) 1084 | if err != nil { 1085 | return err 1086 | } 1087 | if badreq { 1088 | return fmt.Errorf("package '%s' requires at least go version %s.\nhowever, your gx-go binary was compiled with %s.\nPlease update gx-go (or recompile with your current go compiler)", npkg.Name, reqvers, gxgocompvers) 1089 | } 1090 | } else { 1091 | Log("gx-go was compiled with an unrecognized version of go. (%s)", gxgocompvers) 1092 | Log("If you encounter any strange issues during its usage, try rebuilding gx-go with go %s or higher", reqvers) 1093 | } 1094 | } 1095 | return nil 1096 | } 1097 | 1098 | func min(a, b int) int { 1099 | if a < b { 1100 | return a 1101 | } 1102 | return b 1103 | } 1104 | 1105 | func versionComp(have, req string) (bool, error) { 1106 | hp := strings.Split(have, ".") 1107 | rp := strings.Split(req, ".") 1108 | 1109 | // treat X.YrcZ or X.YbetaZ as simply X.Y 1110 | for _, s := range []string{"rc", "beta"} { 1111 | if strings.Contains(hp[len(hp)-1], s) { 1112 | hp[len(hp)-1] = strings.Split(hp[len(hp)-1], s)[0] 1113 | break 1114 | } 1115 | } 1116 | 1117 | l := min(len(hp), len(rp)) 1118 | hp = hp[:l] 1119 | rp = rp[:l] 1120 | for i, v := range hp { 1121 | hv, err := strconv.Atoi(v) 1122 | if err != nil { 1123 | return false, err 1124 | } 1125 | 1126 | rv, err := strconv.Atoi(rp[i]) 1127 | if err != nil { 1128 | return false, err 1129 | } 1130 | 1131 | if hv < rv { 1132 | return true, nil 1133 | } else if hv > rv { 1134 | return false, nil 1135 | } 1136 | } 1137 | return false, nil 1138 | } 1139 | 1140 | func globalPath() string { 1141 | gp, _ := getGoPath() 1142 | return filepath.Join(gp, "src", "gx", "ipfs") 1143 | } 1144 | 1145 | // Load the `Dependency` by its hash returning the `Package` where it's 1146 | // installed, `pkgDir` is an optional parameter with the directory 1147 | // where to look for that dependency. 1148 | // TODO: `pkgDir` isn't actually the package directory, it's where 1149 | // *all* the packages are stored, it should have another name (and 1150 | // it shouldn't be "packages directory"). 1151 | func loadDep(dep *gx.Dependency, pkgDir string) (*Package, error) { 1152 | var pkg Package 1153 | if pkgDir != "" { 1154 | pkgPath := filepath.Join(pkgDir, dep.Hash) 1155 | VLog(" - fetching dep: %s (%s)", dep.Name, dep.Hash) 1156 | err := gx.FindPackageInDir(&pkg, pkgPath) 1157 | if err == nil { 1158 | return &pkg, nil 1159 | } 1160 | } 1161 | 1162 | // Either `pkgDir` wasn't specified or it failed 1163 | // to find it there, try global path. 1164 | p := filepath.Join(globalPath(), dep.Hash) 1165 | VLog(" - checking in global namespace (%s)", p) 1166 | err := gx.FindPackageInDir(&pkg, p) 1167 | if err != nil { 1168 | // It didn't find the package in the glogal path, try 1169 | // fetching it. 1170 | err := gxGetPackage(dep.Hash) 1171 | // TODO: This works because `gxGetPackage` has the global path hard-coded. 1172 | if err != nil { 1173 | return nil, fmt.Errorf("failed to fetch package: %s", err) 1174 | } 1175 | 1176 | err = gx.FindPackageInDir(&pkg, p) 1177 | if err != nil { 1178 | return nil, fmt.Errorf("failed to find package: %s", err) 1179 | } 1180 | } 1181 | 1182 | return &pkg, nil 1183 | } 1184 | 1185 | // Rewrites the package `DvcsImport` with the dependency hash (or 1186 | // the other way around if `undo` is true). `overwrite` indicates 1187 | // whether or not to allow overwriting an existing entry in the map. 1188 | func addRewriteForDep(dep *gx.Dependency, pkg *Package, m map[string]string, undo bool, overwrite bool) { 1189 | if pkg.Gx.DvcsImport == "" { 1190 | return 1191 | // Nothing to do as there is no DVCS import path. 1192 | // TODO: Should this case be flagged? 1193 | } 1194 | 1195 | from := pkg.Gx.DvcsImport 1196 | to := "gx/ipfs/" + dep.Hash + "/" + pkg.Name 1197 | if undo { 1198 | from, to = to, from 1199 | } 1200 | 1201 | _, entryExists := m[from] 1202 | if !entryExists || overwrite { 1203 | m[from] = to 1204 | } else if entryExists && m[from] != to { 1205 | VLog("trying to overwrite rewrite map entry %s pointing to %s with %s", from, m[from], to) 1206 | } 1207 | } 1208 | 1209 | func buildRewriteMapping(pkg *Package, pkgdir string, m map[string]string, undo bool) error { 1210 | // TODO: Encapsulate `Package` and `pkgDir` in another structure 1211 | // (such as `installedPackage`). 1212 | 1213 | seen := make(map[string]struct{}) 1214 | var process func(pkg *Package, rootPackage bool) error 1215 | 1216 | // `rootPackage` indicates if we're processing the dependencies 1217 | // of the root package (declared in `package.json`) that should 1218 | // not be overwritten in the map with transitive dependencies 1219 | // (dependencies of other dependencies). 1220 | process = func(pkg *Package, rootPackage bool) error { 1221 | for _, dep := range pkg.Dependencies { 1222 | if _, ok := seen[dep.Hash]; ok { 1223 | continue 1224 | } 1225 | seen[dep.Hash] = struct{}{} 1226 | 1227 | cpkg, err := loadDep(dep, pkgdir) 1228 | if err != nil { 1229 | VLog("error loading dep %q of %q: %s", dep.Name, pkg.Name, err) 1230 | return fmt.Errorf("package %q not found. (dependency of %s)", dep.Name, pkg.Name) 1231 | } 1232 | 1233 | // Allow overwriting the map only if these are the dependencies 1234 | // of the root package. 1235 | addRewriteForDep(dep, cpkg, m, undo, rootPackage) 1236 | 1237 | // recurse! 1238 | err = process(cpkg, false) 1239 | if err != nil { 1240 | return err 1241 | } 1242 | } 1243 | return nil 1244 | } 1245 | return process(pkg, true) 1246 | } 1247 | 1248 | func buildMap(pkg *Package, m map[string]string) error { 1249 | for _, dep := range pkg.Dependencies { 1250 | var ch Package 1251 | err := gx.FindPackageInDir(&ch, filepath.Join(vendorDir, dep.Hash)) 1252 | if err != nil { 1253 | return err 1254 | } 1255 | 1256 | if ch.Gx.DvcsImport != "" { 1257 | e, ok := m[ch.Gx.DvcsImport] 1258 | if ok { 1259 | if e != dep.Hash { 1260 | Log("have two dep packages with same import path: ", ch.Gx.DvcsImport) 1261 | Log(" - ", e) 1262 | Log(" - ", dep.Hash) 1263 | } 1264 | continue 1265 | } 1266 | m[ch.Gx.DvcsImport] = dep.Hash 1267 | } 1268 | 1269 | err = buildMap(&ch, m) 1270 | if err != nil { 1271 | return err 1272 | } 1273 | } 1274 | return nil 1275 | } 1276 | 1277 | func loadMap(i interface{}, file string) error { 1278 | fi, err := os.Open(file) 1279 | if err != nil { 1280 | return err 1281 | } 1282 | defer fi.Close() 1283 | 1284 | return json.NewDecoder(fi).Decode(i) 1285 | } 1286 | 1287 | func tabPrintSortedMap(headers []string, m map[string]string) { 1288 | var names []string 1289 | for k, _ := range m { 1290 | names = append(names, k) 1291 | } 1292 | 1293 | sort.Strings(names) 1294 | 1295 | w := tabwriter.NewWriter(os.Stdout, 12, 4, 1, ' ', 0) 1296 | if headers != nil { 1297 | fmt.Fprintf(w, "%s\t%s\n", headers[0], headers[1]) 1298 | } 1299 | 1300 | for _, n := range names { 1301 | fmt.Fprintf(w, "%s\t%s\n", n, m[n]) 1302 | } 1303 | w.Flush() 1304 | } 1305 | 1306 | func getGoPath() (string, error) { 1307 | gp := os.Getenv("GOPATH") 1308 | if gp == "" { 1309 | return homedir.Expand("~/go") 1310 | } 1311 | 1312 | return filepath.SplitList(gp)[0], nil 1313 | } 1314 | --------------------------------------------------------------------------------