Организация локального кода в пакетах с использованием модулей 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. Три хороших статьи о том, как разложить код по пакетам:

Это классические статьи, предшествующие введению модулей, но изложенная в них философия по-прежнему применима к тому, как упорядочивать пакеты внутри модуля.

Импорт других пакетов в том же модуле

При импорте другого пакета с модулями вы всегда используете полный путь, включая путь к модулю. Это верно даже при импорте другого пакета в тот же модуль. Например, если модуль объявил свою идентичность в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". Путь импорта всегда начинается с пути к модулю этого пакета, независимо от того, находится ли этот пакет в том же модуле или в другом модуле.
Другие вопросы по тегам