Relative import from parent directory

45,106

Solution 1

Thanks for adding to your question. First, an answer, then some explanation. I built your code by,

  1. go get, just as you had it. (I ignored the error messages.)
  2. setting the import line in main.go back to "../../../meme", as you wanted to do.
  3. (commenting out a little bit of code containing an unused variable.)
  4. then in the meme/cmd/meme directory, either go run main.go or go build main.go worked.

I was wrong in my comment earlier when I said go install works; I should have said go build.

The key however is that go build alone does not work; you must type go build main.go. This is because the go command does not allow "local imports in non-local packages." You are right that spec is of little help here. It weasels out saying, "The interpretation of the ImportPath is implementation-dependent." The current implementation behavior was set with CL 5787055, which was subsequently debated at length on Go-nuts.

"Local" means indicated with a file system relative path. Obviously a relative path starting with .. is local, so the trick is just getting the go command to treat main as a local package as well. It apparently doesn't do this when you type go build, but does when you type go build main.go.

Solution 2

Edit: Relative import paths are not the way to go in Go. Lack of documentation shows something about popularity of relative paths, and I don't see a reason for using them. Go's recommended code organization works pretty well. Every package should have a unique import path and be imported everywhere using that same import path.

See how a package like github.com/ha/doozerd/peer imports its neighbors. This is a common practice among Go projects and I've seen it a lot of times. Package camlistore.org/pkg/auth (also on GitHub; written by one of the main authors of Go) imports camlistore.org/pkg/netutil by full path.

Even if you are having both commands and libraries in the same project this approach works. In your original questions you wisely asked for best practices. I did my best in explaining best practices on this matter.


Import paths can't be relative in Go. I recommend reading How to Write Go Code, the essential reading on organizing Go projects. Here's a short overview:

Make a directory like ~/go for your Go development. Then say:

$ export GOPATH=~/go
$ mkdir $GOPATH/{src,bin,pkg}

$GOPATH/src holds source code for all your Go packages, even the ones your download with go get. bin and pkg keep output of compilations. Packages with package name main are commands and yield to executable binaries which go to $GOPATH/bin. Other packages are libraries and their compiled object files are put in $GOPATH/pkg.

Now if you put your code in $GOPATH/src/matt/meme, you can import it by import "matt/meme". It's recommended to use a prefix for your package names and leave short package names for standard libraries. That's why I used $GOPATH/src/matt/meme instead of $GOPATH/src/meme.

Organize your code around this idea.

Solution 3

Relarive imports are supported when manually using the compiler, linker, ... directly. The 'go' (build) tool doesn't support the same (somehow comparable to eg Java).

Solution 4

This may not answer the original question, but I was trying to do the above when I didn't really need to, all I needed to do was update go.mod temporarily with a replace :

module github.com/pselle/foo

replace github.com/pselle/bar => /Users/pselle/Projects/bar

require (
    github.com/pselle/bar v1.0.0
)

reference: https://thewebivore.com/using-replace-in-go-mod-to-point-to-your-local-module/

Share:
45,106

Related videos on Youtube

Matt Joiner
Author by

Matt Joiner

About Me I like parsimonious code, with simple interfaces and excellent documentation. I'm not interested in enterprise, boiler-plate, or cookie-cutter nonsense. I oppose cruft and obfuscation. My favourite languages are Go, Python and C. I wish I was better at Haskell. Google+ GitHub Bitbucket Google code My favourite posts http://stackoverflow.com/questions/3609469/what-are-the-thread-limitations-when-working-on-linux-compared-to-processes-for/3705919#3705919 http://stackoverflow.com/questions/4352425/what-should-i-learn-first-before-heading-to-c/4352469#4352469 http://stackoverflow.com/questions/6167809/how-much-bad-can-be-done-using-register-variables-in-c/6168852#6168852 http://stackoverflow.com/questions/4141307/c-and-c-source-code-profiling-tools/4141345#4141345 http://stackoverflow.com/questions/3463207/how-big-can-a-malloc-be-in-c/3486163#3486163 http://stackoverflow.com/questions/4095637/memory-use-of-stl-data-structures-windows-vs-linux/4183178#4183178

Updated on November 11, 2020

Comments

  • Matt Joiner
    Matt Joiner over 3 years

    How does one do a relative import from a parent directory?

    From meme/cmd/meme:

    import "../../../meme"
    

    This gives an ambiguous error:

    matt@stanley:~/gopath/src/bitbucket.org/anacrolix/meme/cmd/meme$ go get bitbucket.org/anacrolix/meme/cmd/meme
    
    can't load package: /home/matt/gopath/src/bitbucket.org/anacrolix/meme/cmd/meme/main.go:8:2: local import "../../../meme" in non-local package
    
    matt@stanley:~/gopath/src/bitbucket.org/anacrolix/meme/cmd/meme$ echo $GOPATH
    
    /home/matt/gopath
    

    How do I import locally from a parent directory?

    • Sonia
      Sonia almost 12 years
      I tried this a couple of different ways and I didn't see the ambiguous error you mention. Do you mean you found the wording ambiguous or that the message text included the word ambiguous. The two ways I tried were meme/cmd/meme within GOPATH, and then outside of GOPATH. In both cases a relative import path worked fine for me. Can you give more details on what isn't working for you?
    • Matt Joiner
      Matt Joiner almost 12 years
      @Sonia did you try with the go tool? I'll add more detail.
    • Sonia
      Sonia almost 12 years
      Yes. I kind of assumed that you had a package in meme and an executable in meme/cmd/meme. For the GOPATH case, go run or go install on the executable just worked. For the out-of-GOPATH case I compiled the meme package with go tool 6g and go tool pack. The import in the main package then just needed to be ../../meme, to point to the .a but otherwise go tool 6g and go tool 6l build a working executable that accessed the package.
    • Matt Joiner
      Matt Joiner almost 12 years
      @Sonia: I've added some examples.
  • Matt Joiner
    Matt Joiner almost 12 years
    I believe they can Mostafa, but it's undocumented.
  • Mostafa
    Mostafa almost 12 years
    @MattJoiner You're right. I didn't know they're possible with the Go tool. But anyway I don't think we should use them. Have to update my answer.
  • Admin
    Admin over 10 years
    Relative paths are good for one (IMO critical) use case: dealing with forked repositories. If you fork someone's repo on github you'll have to update all of the import statements to refer to your copy, and then remember not to push those upstream (or expect that upstream would exclude them in the merge). Just gets sloppy.
  • Mostafa
    Mostafa over 10 years
    @dpk Then how can you fix that with relative import paths? You still have to update all import paths?
  • Admin
    Admin over 10 years
    @Mostafa The imports would need to be updated, once, upstream, and then stay that way.
  • morphatic
    morphatic over 9 years
    While I understand that this is the "best practice" way to organize a go project, in practice it breaks the standard workflow for contributing via github. e.g. if I fork github.com/ha/doozerd/peer, clone my fork (github.com/morphatic/doozerd/peer), make changes in neighbor files/folders and then try to run tests, my changes are not seen because the import statements refer to the upstream repo. Is there any way around this?
  • Mostafa
    Mostafa over 9 years
    Haven’t tested this, but should work out OK: Don’t download your fork in a separate directory. Add it as a remote to the current git repo you have in the correct directory.
  • Billy
    Billy about 9 years
    You can also git clone your fork to the path that the project you are forking would go. For example, if you are forking github.com/someone/someproj as github.com/you/someproj you can git clone your fork to ~/go/src/github.com/someone/someproj as though you were working on the main repo.
  • d11wtq
    d11wtq over 8 years
    How do you create a new project and have those import lines work before you have pushed to GitHub? Seems like there's a cyclic dependency here: you need the files to be on GitHub to import them, but you shouldn't put the files on GitHub until you've tested them.
  • Mostafa
    Mostafa over 8 years
    @d11wtq You should start your project by creating its directory at $GOPATH/src/github.com/username/projectname on your machine. It doesn’t have to be a Git repo. You can import it as usual and create its repo and push your code when you are ready.
  • user5359531
    user5359531 over 3 years
    I do not understand why Github has anything to do with importing my local package, and why I am "forced" to build my package in some hard-coded location. In pretty much every case, I am going to start my development in an arbitrary location that has nothing to do with GOPATH or Github. Even worse, what happens when Github goes away?