Karlom
Karlom

Reputation: 14884

How to import local packages in go?

I am new to go and working on an example code that I want to localize.

In the original main.go import statement it was:

 import (
    "log"
    "net/http"
    "github.com/foo/bar/myapp/common"
    "github.com/foo/bar/myapp/routers"
)

Now I have common and routers package in /home/me/go/src/myapp

So I converted the import statement to:

import (
    "log"
    "net/http"
    "./common"
    "./routers"
)

But when I run go install myapp I get these errors:

can't load package: /home/me/go/src/myapp/main.go:7:3: local import "./common" in non-local package

Also, when I use common and routers instead of ./common and ./routers in the import statement, I get:

myapp/main.go:7:3: cannot find package "common" in any of:
    /usr/local/go/src/common (from $GOROOT)
    /home/me/go/src/common (from $GOPATH)
myapp/main.go:8:2: cannot find package "routers" in any of:
    /usr/local/go/src/routers (from $GOROOT)
    /home/me/go/src/routers (from $GOPATH)

How can I fix this?

Upvotes: 255

Views: 434170

Answers (12)

Ninja Ikta
Ninja Ikta

Reputation: 53

If you are using GOMODULE111=on, then

  1. The local module being imported (and packages within) can be anywhere on your local machine. No need to know about GOPATH or GOROOT or GOPRIVATE or GONOPROXY.
  2. The local module need not be published on any vcs.
  3. The only requirement is for local module's module path in its go.mod to be defined as string1.string2/string3. All 3 can be arbitrary strings. No need to make string1.string2 as github.com or any such valid url. string3 need not even match the local directory at which the module is located.

Suppose, your local module is located at "/Users/me/dir_mod"

Then the go.mod within it should declare the module path as: "module abc.def/xyz"

Suppose the module has a package at a "/Users/me/dir_mod/dir_package".

This package folder has file somefile.go declaring its package as "package random"

Note: The module path as well as the package name are not required to be the same as their respective actual directory names.

Let's say, you want to now import "random" package in another module.

The go.mod of calling project requires 2 lines (without the quotes):

  1. "replace abc.def/xyz v0.0.0 => /Users/me/dir_mod"
  2. "require abc.def/xyz v0.0.0"

The version number can be any random vX.Y.Z format string where X, Y, and Z have characters only between 0-9 (e.g. v1.2.3, v0.100.2, v00.1.345).

Now any file in a package inside the calling module can do:

"import another_random abc.def/xyz/dir_package" => using aliasing of packages to rename "package random" contained in ""/Users/me/dir_mod/dir_package"" as "another_random"


The above is just to illustrate that except for module path naming convention of abc.def/xyz and including a pseudo-version number, everything else is flexible. You wouldn't be having mixed names of packages and directories in real projects, but is helpful to know what is absolutely essential vs not.

Upvotes: 2

nanobar
nanobar

Reputation: 66475

You should have created your package with go mod init e.g. go mod init github.com/my-org/my-package

Now in my-package you have a sub module called utils for example.

main.go
utils
 |- randstr.go

And your randstr.go looks like this:

package utils

func RandStr(n int) string {
    // TODO: Generate random string....
    return "I am a random string"
}

And then anywhere in your project you would use exported (capitalized) functions from the utils package like this, for example in main.go:

package main

import (
    "fmt"

    // "github.com/my-org/my-package" is the
    // module name at the top of your `go.mod`
    "github.com/my-org/my-package/utils"
)

func main() {
    fmt.Printf("Random string: %s\n", utils.RandStr(20))
}

Upvotes: 102

Inuart
Inuart

Reputation: 1512

Another approach, available since go1.18, is to use a go.work file.

First, the local common package has to be a module, so provide a go.mod file inside the common folder:

module common

go 1.18

You can now create a go.work file in the root of your directory manually or call go work init, then go work use . and finally go work use ./common. It will look like this:

go 1.18

use (
    .
    ./common
)

Finally you can import the package in your code by name

package main

import "common"

Just remember to not commit your go.work files :)

Upvotes: 8

BAR
BAR

Reputation: 17171

Follow instructions here https://go.dev/doc/tutorial/call-module-code

Mainly you need the replace call in your go.mod file.

module example.com/hello

go 1.16

replace example.com/greetings => ../greetings

Upvotes: 28

Идентикон
Идентикон

Reputation: 21

Try to change the package name with the go mod init command.

So, I have go 1.17, and I have the same import problem. My project directory is $GOPATH/src/myswagger/app-swagger-test. I ran this command into app-swagger-test dir:

go mod init app-swagger-test
go mod tidy

In my new go.mod file the package name is app-swagger-test. For example, this import was wrong:

import (
    ...
    "myswagger/app-swagger-test/internal/generated/restapi"
    "myswagger/app-swagger-test/internal/generated/restapi/operations"
)

So I removed go.mod and go.sum. And I ran next commands into app-swagger-test dir:

go mod init myswagger/app-swagger-test
go mod tidy

After that all imports in the project were imported successfully. In the new go.mod file the first line is:

module myswagger/app-swagger-test

Maybe this information is common, but I did not find it. Thanks!

Upvotes: 2

sunil karki
sunil karki

Reputation: 407

As in the question, the folder structure is:

/home/me/go/src/myapp
                └─ common
                └─ routers

So go to myapp dir

cd /home/me/go/src/myapp

Do

go mod init myapp

This will create a go.mod file which lets Go know the name of the module myapp so that when it’s looking at import paths in any package, it knows not to look elsewhere for myapp

Then you can do the following in the code:

import (
    "log"
    "net/http"
    "myapp/common"
    "myapp/routers"
)

Now package common and routers gets imported.

Upvotes: 6

Jacob
Jacob

Reputation: 2444

The key is how you name your module in the following command

go mod init <TheNameGiven>

Then refer the modules in the inner folder with,

TheNameGiven/folder

I have found the best solution here... Read More

Upvotes: 4

SiminSimin
SiminSimin

Reputation: 382

an example:

  1. in ./greetings, do go mod init example.com/greetings

  2. from another module, do go mod edit -replace=example.com/greetings=../greetings

  3. go get example.com/greetings

from the go tutorial

Upvotes: 16

tangxinfa
tangxinfa

Reputation: 1520

Local package is a annoying problem in go.

For some projects in our company we decide not use sub packages at all.

  • $ glide install
  • $ go get
  • $ go install

All work.

For some projects we use sub packages, and import local packages with full path:

import "xxxx.gitlab.xx/xxgroup/xxproject/xxsubpackage

But if we fork this project, then the subpackages still refer the original one.

Upvotes: 8

arimaulana
arimaulana

Reputation: 1061

If you are using Go 1.5 above, you can try to use vendoring feature. It allows you to put your local package under vendor folder and import it with shorter path. In your case, you can put your common and routers folder inside vendor folder so it would be like

myapp/
--vendor/
----common/
----routers/
------middleware/
--main.go

and import it like this

import (
    "common"
    "routers"
    "routers/middleware"
)

This will work because Go will try to lookup your package starting at your project’s vendor directory (if it has at least one .go file) instead of $GOPATH/src.

FYI: You can do more with vendor, because this feature allows you to put "all your dependency’s code" for a package inside your own project's directory so it will be able to always get the same dependencies versions for all builds. It's like npm or pip in python, but you need to manually copy your dependencies to you project, or if you want to make it easy, try to look govendor by Daniel Theophanes

For more learning about this feature, try to look up here

Understanding and Using Vendor Folder by Daniel Theophanes

Understanding Go Dependency Management by Lucas Fernandes da Costa

I hope you or someone else find it helpfully

Upvotes: 68

Karlom
Karlom

Reputation: 14884

Well, I figured out the problem. Basically Go starting path for import is $HOME/go/src

So I just needed to add myapp in front of the package names, that is, the import should be:

import (
    "log"
    "net/http"
    "myapp/common"
    "myapp/routers"
)

Upvotes: 157

wlredeye
wlredeye

Reputation: 1014

Import paths are relative to your $GOPATH and $GOROOT environment variables. For example, with the following $GOPATH:

GOPATH=/home/me/go

Packages located in /home/me/go/src/lib/common and /home/me/go/src/lib/routers are imported respectively as:

import (
    "lib/common"
    "lib/routers"
)

Upvotes: 25

Related Questions