Организация локального кода в пакетах с использованием модулей Go
Я не могу найти способ выделить какой-то код из main.go
в локальный пакет при использовании модулей Go (go version >= 1.11) вне $GOPATH
,
Я не импортирую внешние зависимости, которые необходимо включить в go.mod
Я просто пытаюсь организовать локально исходный код этого модуля Go.
Файл main.go
:
package main
// this import does not work
import "./stuff"
func main() {
stuff.PrintBaz()
}
Файл ./stuff/bar.go
(притворяется местным пакетом):
package stuff
import "log"
type Bar struct {
Baz int
}
func PrintBaz() {
baz := Bar{42}
log.Printf("Bar struct: %v", baz)
}
Файл go.mod
(команда go mod init foo
):
module foo
go 1.12
При выполнении go run main.go
:
- Если я
import "./stuff"
тогда я вижуbuild command-line-arguments: cannot find module for path _/home/<PATH_TO>/fooprj/stuff
, - Если я
import "stuff"
тогда я вижуbuild command-line-arguments: cannot load stuff: cannot find module providing package stuff
, - Если я
import stuff "./stuff"
с псевдонимом пакета, затем я снова вижу:build command-line-arguments: cannot find module for path _/home/<PATH_TO>/fooprj/stuff
,
Я не могу найти способ заставить локальные пакеты работать с модулями go.
- Что не так с кодом выше?
- Как я могу импортировать локальные пакеты в другой код Go внутри проекта, определенного с помощью модулей Go (файл
go.mod
)?
1 ответ
Сначала вы должны выбрать имя для вашего проекта и записать его в файл go.mod. Это имя принадлежит корневому каталогу проекта. Каждый новый пакет, который вы создаете, должен находиться внутри своего собственного подкаталога, а его имя должно совпадать с именем каталога.
go.mod:
module myprojectname
или же
module github.com/myname/myproject
Затем импортируйте пакеты вашего проекта, такие как:
import myprojectname/stuff
или же
import github.com/myname/myproject/stuff
Файлы пакета stuff
должен быть расположен внутри проекта stuff
каталог. Вы называете эти файлы как хотите.
Также возможно создание более глубокой структуры проекта. Например, вы решили отделить файлы исходного кода от других (таких как конфиги приложения, файлы Docker, статические файлы и т. Д.). Давай двигаться stuff
каталог внутри pkg
, каждый файл Go внутри pkg/stuff
осталось stuff
имя пакета. Чтобы импортировать пакет вещи просто напишите:
import myprojectname/pkg/stuff
Ничто не мешает вам создавать больше уровней в иерархии, как github.com/myuser/myproject/pkg/db/provider/postgresql
, где:
github.com/myuser/myproject
- название проекта.postgresql
- имя пакета.pkg/db/provider/postgresql
- путь к пакету относительно корня проекта.
Вы можете прочитать больше о модулях go здесь: https://github.com/golang/go/wiki/Modules
Проверьте этот репозиторий, чтобы получить полезную информацию о различных шаблонах, используемых при организации проекта: https://github.com/golang-standards/project-layout Если вы зайдете внутрь pkg
каталог вы узнаете, какие проекты с открытым исходным кодом использовать pkg
каталог в их структуре.
Структура модуля
Самый распространенный и простой подход:
- Используйте один
go.mod
на репозиторий, и - Поместите сингл
go.mod
файл в корне репозитория и - Используйте имя репозитория в качестве пути к модулю, объявленного в
module
линия вgo.mod
- (Если вы используете настраиваемый путь импорта, например
me.io/mymod
вместо использования пути импорта на основе хоста VCS, вы должны использовать пользовательский путь импорта вместо имени репозитория в вашемgo.mod
).
- (Если вы используете настраиваемый путь импорта, например
Например, если ваше репо github.com/my/repo
, то поместите один go.mod
в корне репо, при чтении первой строки module github.com/my/repo
. Это может быть созданоcd
вход в корень репо и запуск go mod init github.com/my/repo
.
Это поможет вам оставаться на счастливом пути с модулями и позволит избежать множества тонкостей.
Расс Кокс прокомментировал в № 26664:
Для всех, кроме опытных пользователей, вы, вероятно, захотите принять обычное соглашение о том, что одно репо = один модуль. Для долгосрочного развития вариантов хранения кода важно, чтобы репо могло содержать несколько модулей, но по умолчанию это почти наверняка не то, что вы хотите делать.
Более подробную информацию о многомодульных репозиториях можно найти в разделе часто задаваемых вопросов "Многомодульные репозитории" на вики-странице модулей. Те 6 или около того часто задаваемых вопросов в этом разделе должны быть полностью прочитаны любым, кто собирается отклониться от приведенной выше рекомендации.
Размещение пакетов внутри модуля
После того, как вы настроили свой go.mod
, вы можете расположить свои пакеты в каталогах, как вы считаете нужным, в каталогах под каталогом, содержащим go.mod
, а также в каталоге с go.mod
. Три хороших статьи о том, как разложить код по пакетам:
- https://rakyll.org/style-packages/
- https://medium.com/@benbjohnson/standard-package-layout-7cdbc8391fc1
- https://www.goinggo.net/2017/02/design-philosophy-on-packaging.html
Это классические статьи, предшествующие введению модулей, но изложенная в них философия по-прежнему применима к тому, как упорядочивать пакеты внутри модуля.
Импорт других пакетов в том же модуле
При импорте другого пакета с модулями вы всегда используете полный путь, включая путь к модулю. Это верно даже при импорте другого пакета в тот же модуль. Например, если модуль объявил свою идентичность вgo.mod
как модуль github.com/my/repo
, и у вас была эта организация:
repo/
├── go.mod <<<<< Note go.mod is located in repo root
├── pkg1
│ └── pkg1.go
└── pkg2
└── pkg1.go
потом pkg1
будет импортировать свой одноранговый пакет как import "github.com/my/repo/pkg2"
. Обратите внимание, что вы не можете использовать относительные пути импорта, напримерimport "../pkg2"
или import "./subpkg"
. (Это часть того, что OP ударил выше сimport "./stuff"
).
Модули против репозиториев против пакетов против путей импорта
Модуль Go - это набор связанных пакетов Go, которые управляются версиями вместе как единое целое. Модули записывают точные требования к зависимостям и создают воспроизводимые сборки.
Подводя итог взаимосвязи между репозиториями, модулями и пакетами:
- Хранилище содержит один или несколько Go модулей (чаще всего именно один модуль в корне хранилища).
- Каждый модуль содержит один или несколько пакетов Go.
- Каждый пакет состоит из одного или нескольких исходных файлов Go, которые находятся в одном каталоге.
- Исходный код Go:
- объявляет свой пакет с
package foo
заявление. - автоматически получает доступ к другому исходному коду Go в том же пакете.
- импортирует код из другого пакета через путь импорта, указанный в операторе импорта, например
import "github.com/my/repo/pkg1"
. Путь импорта всегда начинается с пути к модулю этого пакета, независимо от того, находится ли этот пакет в том же модуле или в другом модуле.
- объявляет свой пакет с