summaryrefslogtreecommitdiff
path: root/src/_posts/2013-07-11-goplus.md
blob: 5e63eb2acb0c71730dcf4f2eb5b17443bd417362 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
---
title: Go+
description: >-
    A simple proof-of-concept script for doing go dependency management.
tags: tech
---

Compared to other languages go has some strange behavior regarding its project
root settings. If you import a library called `somelib`, go will look for a
`src/somelib` folder in all of the folders in the `$GOPATH` environment
variable. This works nicely for globally installed packages, but it makes
encapsulating a project with a specific version, or modified version, rather
tedious. Whenever you go to work on this project you'll have to add its path to
your `$GOPATH`, or add the path permanently, which could break other projects
which may use a different version of `somelib`.

My solution is in the form of a simple script I'm calling go+. go+ will search
in currrent directory and all of its parents for a file called `GOPROJROOT`. If
it finds that file in a directory, it prepends that directory's absolute path to
your `$GOPATH` and stops the search. Regardless of whether or not `GOPROJROOT`
was found go+ will passthrough all arguments to the actual go call. The
modification to `$GOPATH` will only last the duration of the call.

As an example, consider the following:
```
/tmp
    /hello
        GOPROJROOT
        /src
            /somelib/somelib.go
            /hello.go
```

If `hello.go` depends on `somelib`, as long as you run go+ from `/tmp/hello` or
one of its children your project will still compile

Here is the source code for go+:

```bash
#!/bin/sh

SEARCHING_FOR=GOPROJROOT
ORIG_DIR=$(pwd)

STOPSEARCH=0
SEARCH_DIR=$ORIG_DIR
while [ $STOPSEARCH = 0 ]; do

    RES=$( find $SEARCH_DIR -maxdepth 1 -type f -name $SEARCHING_FOR | \
           grep -P "$SEARCHING_FOR$" | \
           head -n1 )

    if [ "$RES" = "" ]; then
        if [ "$SEARCH_DIR" = "/" ]; then
            STOPSEARCH=1
        fi
        cd ..
        SEARCH_DIR=$(pwd)
    else
        export GOPATH=$SEARCH_DIR:$GOPATH
        STOPSEARCH=1
    fi
done

cd "$ORIG_DIR"
exec go $@
```

## UPDATE: Goat

I'm leaving this post for posterity, but go+ has some serious flaws in it. For
one, it doesn't allow for specifying the version of a dependency you want to
use. To this end, I wrote [goat][0] which does all the things go+ does, plus
real dependency management, PLUS it is built in a way that if you've been
following go's best-practices for code organization you shouldn't have to change
any of your existing code AT ALL. It's cool, check it out.

[0]: http://github.com/mediocregopher/goat