Go get cannot find local packages when using multiple modules in a repo
Asked Answered
R

3

47

I'm having issues with go's new module system, as I'd like to define a local module and import it in the main program. The local package resides in a folder of the main package/root folder. Imagine the following project structure outside the $GOPATH.

Project structure

./main.go

package main

import "fmt"
import "example.com/localModule/model"

func main() {
    var p = model.Person{name: "Dieter", age:25}
    fmt.Printf("Hello %s\n", p.name)
}

./model/person.go

package model

type Person struct {
    name string
    age int
}

In the root folder I initialized a module by calling

go mod init example.com/localModule

In the model/ folder I then initialized the submodule by calling

go mod init example.com/localModule/model

The error

In the root folder calling the following commands fail.

$ go get
go build example.com/localModule/model: no Go files in

$ go build
main.go:4:8: unknown import path "example.com/localModule/model": cannot find module providing package example.com/localModule/model

The error message for go get is cut off, I din't parse it wrongly.

I do not plan on pushing the module to a server and and just needed a way of referencing the local package model, so I chose example.com/localModule/ and example.com/localModule/model respectively.

I'm using go1.11 darwin/amd64 on a Macbook running MacOS 10.13.6.

Ribbing answered 29/8, 2018 at 14:16 Comment(0)
M
45

You can have local "sub" modules like you ask for by adding a require statement and a matching replace statement with a relative file path in go.mod.

In the "root" ./go.mod:

module example.com/localModule

require example.com/localModule/model v0.0.0

replace example.com/localModule/model v0.0.0 => ./model
Musky answered 12/9, 2018 at 19:46 Comment(2)
This is one possible solution, but it is a more complex solution than the more common approach of one module per repository, which avoids this error and problem.Atul
Note that there are definitely some footguns with a multi-module repo approach, so anyone considering a replace + multi-module repo approach as outlined in this answer should carefully evaluate other options before going down that path. Some more details in this answer.Atul
A
8

Root problem

The reason you are getting this error is because you have defined two modules that do not know how to find each other on your local disk.

$ go build
main.go:4:8: unknown import path "example.com/localModule/model": 
 cannot find module providing package example.com/localModule/model

Solution 1: replace

You can add a replace directive to the top module's go.mod file, such as:

replace example.com/localModule/model v0.0.0 => ./model

which lets the top module find the other module on disk. This is covered in more detail in the replace FAQ and "Multi-module repositories" section on the Modules wiki.

However, this is a more complex solution, can be hard to get right, is usually more on-going work, and has some limitations like replace is ignored for all modules except the current module. For most people, a multi-module repo is probably is not what they want. It is relatively rare at this point to really need to have multiple modules in a single repository.

Solution 2: one repo == one module

While it is possible to use a replace directive, the more common and straightforward solution is:

  • Have a single go.mod file in your repository, and
  • Place that single go.mod file in the root of your repository.

This is a very simple solution, and it means your two packages within the repository will be able to find each other automatically without needing any replace, and which avoids the error message you reported.

replace with multi-module repo, vs. single module repo?

Russ Cox commented in #26664:

For all but power users, you probably want to adopt the usual convention that one repo = one module. It's important for long-term evolution of code storage options that a repo can contain multiple modules, but it's almost certainly not something you want to do by default.

If you instead want to have multiple modules defined within a single repository, there is a fair amount of nuance about how to do so properly, and there is an entire section of the Modules wiki that should be read about how to manage the complexity associated with multiple modules in a single repository, including this advice:

Adding modules, removing modules, and versioning modules with multiple modules in a single repository requires considerable care and deliberation, so it is almost always easier and simpler to manage a single-module repository rather than multiple modules in an existing repository.

Atul answered 17/4, 2019 at 20:23 Comment(1)
Does option 2 one repo == one module mean that all go-files (here ./main.go and ./model/person.go) have to use the same package name (for example package myAwesomeApp) ?Roustabout
B
0

for me below commands worked

create go.mod go mod init "github.com/udacity/ud615/app/health"

and then download other modules as go mod tidy

Bumblebee answered 2/10, 2023 at 18:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.