Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

link: override dependency versions with the current dependent package #51

Merged
merged 4 commits into from
Sep 29, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
194 changes: 116 additions & 78 deletions link.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,30 +19,30 @@ var LinkCommand = cli.Command{
Usage: "Symlink packages to their dvcsimport repos, for local development.",
Description: `gx-go link eases local development by symlinking actual workspace repositories on demand.

Example workflow:

> gx-go link QmQA5mdxru8Bh6dpC9PJfSkumqnmHgJX7knxSgBo5Lpime QmVGtdTZdTFaLsaj2RwdVG8jcjNNcp1DE914DKZ2kHmXHw QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52
linked QmQA5mdxru8Bh6dpC9PJfSkumqnmHgJX7knxSgBo5Lpime /home/user/go/src/github.com/libp2p/go-libp2p
linked QmVGtdTZdTFaLsaj2RwdVG8jcjNNcp1DE914DKZ2kHmXHw /home/user/go/src/github.com/multiformats/go-multihash
linked QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52 /home/user/go/src/github.com/ipfs/go-log

> gx-go link
QmQA5mdxru8Bh6dpC9PJfSkumqnmHgJX7knxSgBo5Lpime /home/user/go/src/github.com/libp2p/go-libp2p
QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52 /home/user/go/src/github.com/ipfs/go-log
QmVGtdTZdTFaLsaj2RwdVG8jcjNNcp1DE914DKZ2kHmXHw /home/user/go/src/github.com/multiformats/go-multihash
gx-go link replaces the target gx package (either by name or hash) with
a symlink to the appropriate dvcs repo in your GOPATH. To make this
"symlinked" repo usable as a gx package, gx-go link rewrites the target
package's dvcs imports using the target package's package.json.
Unfortunately, this can cause build errors in packages depending on this
package if these dependent packages specify alternative, conflicting
dependency versions. We can work around this using the --override-deps flag
to rewrite the target package using dependencies from the current package
(the package you're trying to build) first, falling back on the target
package's package.json file.

> gx-go link -r QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52
unlinked QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52 /home/user/go/src/github.com/ipfs/go-log
Example workflow:

> gx-go link
QmQA5mdxru8Bh6dpC9PJfSkumqnmHgJX7knxSgBo5Lpime /home/user/go/src/github.com/libp2p/go-libp2p
QmVGtdTZdTFaLsaj2RwdVG8jcjNNcp1DE914DKZ2kHmXHw /home/user/go/src/github.com/multiformats/go-multihash
> cd $GOPATH/src/github.com/ipfs/go-ipfs
> gx-go link --override-deps go-unixfs
Replaced 39 entries in the rewrite map:
github.com/ipfs/go-ipfs-chunker
github.com/ipfs/go-ipfs-blockstore
github.com/libp2p/go-libp2p-net
[...]
linked go-unixfs /home/user/go/src/github.com/ipfs/go-unixfs

> gx-go link -r -a
unlinked QmQA5mdxru8Bh6dpC9PJfSkumqnmHgJX7knxSgBo5Lpime /home/user/go/src/github.com/libp2p/go-libp2p
unlinked QmVGtdTZdTFaLsaj2RwdVG8jcjNNcp1DE914DKZ2kHmXHw /home/user/go/src/github.com/multiformats/go-multihash

> gx-go link
unlinked go-unixfs /home/user/go/src/github.com/ipfs/go-unixfs
`,
Flags: []cli.Flag{
cli.BoolFlag{
Expand All @@ -53,21 +53,28 @@ unlinked QmVGtdTZdTFaLsaj2RwdVG8jcjNNcp1DE914DKZ2kHmXHw /home/user/go/src/github
Name: "a,all",
Usage: "Remove all existing symlinks and reinstate the gx packages. Use with -r.",
},
cli.BoolFlag{
Name: "o,override-deps",
Usage: "Override dependency versions of the target package with its current dependant package.",
},
},
Action: func(c *cli.Context) error {
remove := c.Bool("remove")
all := c.Bool("all")
overrideDeps := c.Bool("override-deps")

depRefs := c.Args()[:]
// It can either be a hash or a name.

hashes := c.Args()[:]
if len(hashes) == 0 {
if len(depRefs) == 0 {
links, err := listLinkedPackages()
if err != nil {
return err
}

if remove && all {
for _, link := range links {
hashes = append(hashes, link[0])
depRefs = append(depRefs, link[0])
}
}

Expand All @@ -78,28 +85,37 @@ unlinked QmVGtdTZdTFaLsaj2RwdVG8jcjNNcp1DE914DKZ2kHmXHw /home/user/go/src/github
return nil
}
}
pkg, _ := LoadPackageFile(gx.PkgFileName)

for _, hash := range hashes {
if pkg != nil {
// try to resolve
if dep := pkg.FindDep(hash); dep != nil {
hash = dep.Hash
}
// Path of the current dependant package.
parentPackagePath, err := gx.GetPackageRoot()
if err != nil {
return fmt.Errorf("error retrieving the parent package: %s", err)
}

parentPkg, err := LoadPackageFile(filepath.Join(parentPackagePath, gx.PkgFileName))
if err != nil {
return fmt.Errorf("parent package not found in %s: %s",
parentPackagePath, err)
}

for _, ref := range depRefs {
dep := parentPkg.FindDep(ref)
if dep == nil {
return fmt.Errorf("dependency reference not found in the parent package: %s", ref)
}

if remove {
target, err := unlinkPackage(hash)
target, err := unlinkDependency(dep)
if err != nil {
return err
}
fmt.Printf("unlinked %s %s\n", hash, target)
fmt.Printf("unlinked %s %s\n", dep.Name, target)
} else {
target, err := linkPackage(hash)
target, err := linkDependency(dep, overrideDeps, parentPackagePath)
if err != nil {
return err
}
fmt.Printf("linked %s %s\n", hash, target)
fmt.Printf("linked %s %s\n", dep.Name, target)
}
}

Expand Down Expand Up @@ -141,38 +157,38 @@ func listLinkedPackages() ([][]string, error) {
return links, nil
}

// gx get $hash
// go get $dvcsimport
// rm -rf $GOPATH/src/gx/ipfs/$hash/$pkgname
// ln -s $GOPATH/src/$dvcsimport $GOPATH/src/gx/ipfs/$hash/$pkgname
// cd $GOPATH/src/$dvcsimport && gx install && gx-go rewrite
func linkPackage(hash string) (string, error) {
srcdir, err := gx.InstallPath("go", "", true)
// Link the dependency package `dep` to the global Gx workspace.
//
// The dependency is first fetched to find its DVCS import path (`gx get`),
// then the repository is fetched through `go get` and sym-linked:
// `$GOPATH/gx/ipfs/<pkg-hash>/<pkg-name>/` -> `$GOPATH/src/<dvcs-import>`
// (`target`) -> (`linkPath`)
// If `overrideDeps` is set pass the option to the `post-install` hook to override
// dependency versions.
func linkDependency(dep *gx.Dependency, overrideDeps bool, parentPackagePath string) (string, error) {
gxSrcDir, err := gx.InstallPath("go", "", true)
if err != nil {
return "", err
}
gxdir := filepath.Join(srcdir, "gx", "ipfs", hash)

gxget := exec.Command("gx", "get", hash, "-o", gxdir)
gxget.Stdout = os.Stderr
gxget.Stderr = os.Stderr
if err = gxget.Run(); err != nil {
return "", fmt.Errorf("error during gx get: %s", err)
}

var pkg gx.Package
err = gx.FindPackageInDir(&pkg, gxdir)
dvcsImport, err := findDepDVCSimport(dep, gxSrcDir)
if err != nil {
return "", fmt.Errorf("error during gx.FindPackageInDir: %s", err)
return "", fmt.Errorf("error trying to get the DVCS import" +
"of the dependeny %s: %s", dep.Name, err)
}

dvcsimport := GxDvcsImport(&pkg)
target := filepath.Join(srcdir, dvcsimport)
gxtarget := filepath.Join(gxdir, pkg.Name)
target := filepath.Join(gxSrcDir, dvcsImport)

// Linked package directory, needed for the `post-install` hook.
linkPackageDir := filepath.Join(gxSrcDir, "gx", "ipfs", dep.Hash)
// TODO: this shouldn't be necessary, we should be able to just pass the
// `linkPath` (i.e., the directory with the name of the package).

linkPath := filepath.Join(linkPackageDir, dep.Name)

_, err = os.Stat(target)
if os.IsNotExist(err) {
goget := exec.Command("go", "get", dvcsimport+"/...")
goget := exec.Command("go", "get", dvcsImport+"/...")
goget.Stdout = nil
goget.Stderr = os.Stderr
if err = goget.Run(); err != nil {
Expand All @@ -182,12 +198,12 @@ func linkPackage(hash string) (string, error) {
return "", fmt.Errorf("error during os.Stat: %s", err)
}

err = os.RemoveAll(gxtarget)
err = os.RemoveAll(linkPath)
if err != nil {
return "", fmt.Errorf("error during os.RemoveAll: %s", err)
}

err = os.Symlink(target, gxtarget)
err = os.Symlink(target, linkPath)
if err != nil {
return "", fmt.Errorf("error during os.Symlink: %s", err)
}
Expand All @@ -200,53 +216,75 @@ func linkPackage(hash string) (string, error) {
return "", fmt.Errorf("error during gx install: %s", err)
}

rwcmd := exec.Command("gx-go", "hook", "post-install", gxdir)
rwcmdArgs := []string{"hook", "post-install", linkPackageDir}
if overrideDeps {
rwcmdArgs = append(rwcmdArgs, "--override-deps", parentPackagePath)
}
rwcmd := exec.Command("gx-go", rwcmdArgs...)
rwcmd.Dir = target
rwcmd.Stdout = os.Stdout
rwcmd.Stderr = os.Stderr
if err := rwcmd.Run(); err != nil {
return "", fmt.Errorf("error during gx-go rw: %s", err)
}
// TODO: Wrap command calls in a function.

return target, nil
}

// rm -rf $GOPATH/src/gx/ipfs/$hash
// gx get $hash
func unlinkPackage(hash string) (string, error) {
srcdir, err := gx.InstallPath("go", "", true)
// Return the DVCS import path of a dependency (fetching it
// if necessary).
func findDepDVCSimport(dep *gx.Dependency, gxSrcDir string) (string, error) {
gxdir := filepath.Join(gxSrcDir, "gx", "ipfs", dep.Hash)

// Get the dependency to find out its DVCS import.
err := gxGetPackage(dep.Hash)
if err != nil {
return "", err
}
gxdir := filepath.Join(srcdir, "gx", "ipfs", hash)

err = os.RemoveAll(gxdir)
var pkg gx.Package
err = gx.FindPackageInDir(&pkg, gxdir)
if err != nil {
return "", fmt.Errorf("error during os.RemoveAll: %s", err)
return "", fmt.Errorf("error during gx.FindPackageInDir: %s", err)
}

gxget := exec.Command("gx", "get", hash, "-o", gxdir)
gxget.Stdout = nil
gxget.Stderr = os.Stderr
if err = gxget.Run(); err != nil {
return "", fmt.Errorf("error during gx get: %s", err)
return GxDvcsImport(&pkg), nil
}

// rm -rf $GOPATH/src/gx/ipfs/$hash
// gx get $hash
func unlinkDependency(dep *gx.Dependency) (string, error) {
gxSrcDir, err := gx.InstallPath("go", "", true)
if err != nil {
return "", err
}

var pkg gx.Package
err = gx.FindPackageInDir(&pkg, gxdir)
dvcsImport, err := findDepDVCSimport(dep, gxSrcDir)
if err != nil {
return "", fmt.Errorf("error during gx.FindPackageInDir: %s", err)
return "", fmt.Errorf("error trying to get the DVCS import of the dependeny %s: %s", dep.Name, err)
}

dvcsimport := GxDvcsImport(&pkg)
target := filepath.Join(srcdir, dvcsimport)
target := filepath.Join(gxSrcDir, dvcsImport)

uwcmd := exec.Command("gx-go", "uw")
uwcmd := exec.Command("gx-go", "rw", "--fix")
// The `--fix` options is more time consuming (compared to the normal
// `gx-go uw` call) but as some of the import paths may have been written
// from synced dependencies (`gx-go link --sync`) of another package that
// may not be available now (to build the rewrite map) this is the safer
// option.
uwcmd.Dir = target
uwcmd.Stdout = nil
uwcmd.Stderr = os.Stderr
if err := uwcmd.Run(); err != nil {
return "", fmt.Errorf("error during gx-go uw: %s", err)
return "", fmt.Errorf("error during gx-go rw: %s", err)
}

// Remove the package at the end as `gx-go rw --fix` will need to use it
// (to find the DVCS import paths).
err = os.RemoveAll(filepath.Join(gxSrcDir, "gx", "ipfs", dep.Hash))
if err != nil {
return "", fmt.Errorf("error during os.RemoveAll: %s", err)
}

return target, nil
Expand Down
Loading