Relative import from parent directory
Asked Answered
A

4

52

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?

Aggarwal answered 21/5, 2012 at 14:53 Comment(4)
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?Sciuroid
@Sciuroid did you try with the go tool? I'll add more detail.Aggarwal
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.Sciuroid
@Sonia: I've added some examples.Aggarwal
S
20

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.

Sciuroid answered 22/5, 2012 at 19:29 Comment(0)
O
55

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.

Operative answered 21/5, 2012 at 15:23 Comment(11)
I believe they can Mostafa, but it's undocumented.Aggarwal
@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.Operative
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.Jewbaiting
@dpk Then how can you fix that with relative import paths? You still have to update all import paths?Operative
@Operative The imports would need to be updated, once, upstream, and then stay that way.Jewbaiting
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?Japan
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.Operative
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.Baggy
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.Cold
@Cold 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.Operative
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?Wive
S
20

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.

Sciuroid answered 22/5, 2012 at 19:29 Comment(0)
C
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).

Cerelia answered 21/5, 2012 at 15:17 Comment(0)
H
3

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/

Haymo answered 10/11, 2020 at 14:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.