Модули Go — Начало работы с модулями Go

Go
Модули Go — Начало работы с модулями Go

Версии 1.11 и 1.12 Go включают начальную поддержку модулей — новую систему управления зависимостями Go, которая делает информацию о версии зависимостей явной и упрощает управление. В этом сообщении блога рассматриваются основные операции, необходимые для начала работы с модулями.

Модули хранятся в корневом каталоге сgo.modКоллекция пакетов Go в файловом дереве файла.go.modФайл определяет путь модуля модуля (также путь импорта корневого каталога модуля) и требования других модулей, от которых зависит модуль, так что модуль может быть успешно собран, если требования зависимости выполнены. Требования для каждого зависимого модуля записываются в виде пути к модулю и соответствующей версии модуля.

Ниже показано простоеgo.modдокумент

module example.com/hello

go 1.12

require rsc.io/quote v1.5.2

Начиная с Go 1.11, когда текущий каталог или любой родительский каталогgo.mod, пока каталог находится в$GOPATH/srcКроме того, команда go может использовать модули. (существует$ GOPATH/srcВнутренне, для совместимости, даже если найденоgo.mod, команда go по-прежнему выполняется в старом режиме GOPATH. ) начиная с Go 1.13, модульный режим будет режимом по умолчанию для всей разработки.

В этой статье описывается ряд общих действий, которые происходят при разработке кода Go с использованием модулей:

  • Создайте новый модуль.
  • Добавьте зависимости модуля.
  • Обновите зависимости модуля.
  • Увеличьте основную версию зависимости.
  • Обновите зависимости до новой основной версии.
  • Удалите неиспользуемые зависимости.

Создать новый модуль

существует$GOPATH/srcСоздайте новый пустой каталог где-нибудь, кроме нового каталога, затем создайте новый исходный файл в новом каталоге.hello.go:

package hello

func Hello() string {
    return "Hello, world."
}

Также напишите свой тестовый файлhello_test.go

package hello

import "testing"

func TestHello(t *testing.T) {
    want := "Hello, world."
    if got := Hello(); got != want {
        t.Errorf("Hello() = %q, want %q", got, want)
    }
}

Предположим, что наш вновь созданный каталог/home/gopher/hello, в этот момент каталог содержит пакет, а не модуль, потому что нетgo.modдокумент. Запустив тест с помощью команды go, вы увидите:

$ go test
PASS
ok      _/home/gopher/hello    0.020s
$

Последняя строка вывода суммирует тестовую информацию для всего пакета. потому что мы работаем в$GOPATHи любой модуль, команда go не знает путь импорта текущего каталога (путь импорта — это уникальный строковый идентификатор, который идентифицирует пакет), поэтому создается фальшивый путь импорта на основе местоположения каталога_/home/gopher/hello

давайте использоватьgo mod initУстановите текущий каталог в качестве корневого каталога модуля и выполните снова.go test:

$ go mod init example.com/hello
go: creating new go.mod: module example.com/hello

go mod initкоманда пишетgo.modдокумент:

$ cat go.mod
module example.com/hello

go 1.12
$

go.modПоявляется только в корневом каталоге модуля. Путь импорта для пакета, расположенного в подкаталоге, будет состоять из пути к модулю и пути к подкаталогу. Например, если мы создадим подкаталогworldНе нужно (и не хочется) в нем бегатьgo mod init.该包将自动被识别为example.com/helloчасть модуля, путь импортаexample.com/hello/world.

запустить его сейчасgo testРезультаты его работы следующие:

$ go test
PASS
ok      example.com/hello    0.020s
$

Теперь путь импорта на выходе становитсяexample.com/hello, неосознанно пишем и тестируем наш первый модуль go.

добавить зависимости модуля

Основная мотивация для модулей Go — улучшить опыт управления кодом, написанным другими разработчиками (зависимости кода). давайте обновимhello.goимпортироватьrsc.io/quoteи использовать его для достиженияHelloфункция:

package hello

import "rsc.io/quote"

func Hello() string {
    return quote.Hello()
}

теперь беги сноваgo test:

$ go test
go: finding rsc.io/quote v1.5.2
go: downloading rsc.io/quote v1.5.2
go: extracting rsc.io/quote v1.5.2
go: finding rsc.io/sampler v1.3.0
go: finding golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
go: downloading rsc.io/sampler v1.3.0
go: extracting rsc.io/sampler v1.3.0
go: downloading golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
go: extracting golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
PASS
ok      example.com/hello    0.023s
$

goкоманда, используемая вgo.modУказанная версия модуля зависимостей, указанная в разрешении импорта при обнаруженииgo.modПри импорте пакета, предоставленного любым модулем вgoКоманда автоматически найдет модуль, содержащий пакет, использует его последнюю стабильную версию и добавит его в go.mod. В нашем примереgo testимпортировать новыйrsc.io/quoteрешаетrsc.io/quote v1.5.2модуль, который также загружаетrsc.io/quoteДве используемые зависимости, а именноrsc.io/samplerиgolang.org/x/text. Но только прямые зависимости задокументированы вgo.modВ файле:

$ cat go.mod
module example.com/hello

go 1.12

require rsc.io/quote v1.5.2
$

бежать сноваgo testКоманда не повторяет описанную выше работу по загрузке зависимостей, потому чтоgo.modтеперь обновлен, а загруженные модули кэшируются локально в$ GOPATH/pkg / modбинго.

Как мы видели выше, добавление прямой зависимости часто влечет за собой и другие косвенные зависимости. Команда go list -m all выводит текущий модуль и все его зависимости:

$ go list -m all
example.com/hello
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
rsc.io/quote v1.5.2
rsc.io/sampler v1.3.0
$

существуетgo listВ выводе текущий модуль, также известный как основной модуль, всегда появляется в первой строке, за которой следуют отображаемые зависимости, отсортированные по пути к модулю:

Кромеgo.modКроме,goКоманда также поддерживает файл с именемgo.sum, который содержит криптографические хэши версий зависимых модулей:

$ cat go.sum
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZO...
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:Nq...
rsc.io/quote v1.5.2 h1:w5fcysjrx7yqtD/aO+QwRjYZOKnaM9Uh2b40tElTs3...
rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPX...
rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/Q...
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9...
$

использовать команду gogo.sumфайл, чтобы гарантировать, что будущие загрузки этих модулей будут такими же, как и первая загрузка, чтобы гарантировать, что модули, от которых зависит проект, не будут случайно изменены из-за злонамеренных, случайных или других причин. также**go.sumне похожеpackage-lock.jsonФайл блокировки диспетчера пакетов**, который представляет собой файл отслеживания состояния сборки. Он записывает все прямые и косвенные зависимости текущего модуля, а также контрольные суммы этих зависимостей, тем самым обеспечивая 100% воспроизводимый процесс сборки и гарантии безопасности построенных объектов. такдолжно бытьgo.modиgo.sumдобавляются в систему контроля версий.go.sumПри этом информация о версиях пакетов, использовавшихся в прошлом, будет сохранена для возможного отката версии в будущем, что также отличается от обычных файлов блокировки. Таким образом, go.sum не является файлом блокировки менеджера пакетов.

обновить зависимости

Для модулей Go используйте семантический тег версии для ссылки на версию модуля. Семантическое управление версиями состоит из трех частей: основной, дополнительной и исправления. Например, для версии 0.1.2 основная версия — 0, дополнительная версия — 1, а версия исправления — 2. Давайте пройдемся по нескольким второстепенным обновлениям версии. В следующем разделе мы рассмотрим возможность обновления основной версии.

отgo list -m allв выводе мы видим, что мы используем немаркированную версиюgolang.org/x/text. Давайте обновимся до последней версии с тегами и проверим, что все работает:

$ go get golang.org/x/text
go: finding golang.org/x/text v0.3.0
go: downloading golang.org/x/text v0.3.0
go: extracting golang.org/x/text v0.3.0
$ go test
PASS
ok      example.com/hello    0.013s
$

Испытание прошло. давайте посмотрим еще разgo list -m allВывод и содержимое файла go.mod:

$ go list -m all
example.com/hello
golang.org/x/text v0.3.0
rsc.io/quote v1.5.2
rsc.io/sampler v1.3.0
$ cat go.mod
module example.com/hello

go 1.12

require (
    golang.org/x/text v0.3.0 // indirect
    rsc.io/quote v1.5.2
)
$

golang.org/x/textПакет обновлен до последней версии с тегами (v0.3.0).go.modв файлеgolang.org/x/textТакже обновлено, чтобы указатьv0.3.0.indirectАннотация указывает, что зависимость используется не непосредственно текущим модулем, а модулями, от которых она зависит.

Теперь попробуем обновитьrsc.io/samplerДля указанной версии сначала перечислите ее доступные версии:

$ go list -m -versions rsc.io/sampler
rsc.io/sampler v1.0.0 v1.2.0 v1.2.1 v1.3.0 v1.3.1 v1.99.99
$

мы будемrsc.io/samplerобновитесь доv1.3.1

$ go get rsc.io/sampler@v1.3.1
go: finding rsc.io/sampler v1.3.1
go: downloading rsc.io/sampler v1.3.1
go: extracting rsc.io/sampler v1.3.1
$ go test
PASS
ok      example.com/hello    0.022s
$

Обратите внимание на явный @v1.3.1 в параметре go get. В общем, каждый параметр, передаваемый в get, может принимать явную форму. Значение по умолчанию — @latest, которое преобразуется в последнюю ранее определенную версию.

Добавьте основную версию зависимости

Добавим в пакет новую функцию: functionProverbпозвонивquote.ConcurrencyВозвращаясь к поговорке о параллелизме Go (то есть к золотой фразе, которую Пайк произнес на конференции по разработке Go в определенном году: «Параллелизм — это не параллелизм»), созданнойrsc.io/quote/v3обеспечивается модулем. Во-первых, мы обновляемhello.goчтобы добавить новые функции:

package hello

import (
    "rsc.io/quote"
    quoteV3 "rsc.io/quote/v3"
)

func Hello() string {
    return quote.Hello()
}

func Proverb() string {
    return quoteV3.Concurrency()
}

Тогда мыhello_test.goДобавьте тестовый метод в:

func TestProverb(t *testing.T) {
    want := "Concurrency is not parallelism."
    if got := Proverb(); got != want {
        t.Errorf("Proverb() = %q, want %q", got, want)
    }
}

Затем запускаем тест:

$ go test
go: finding rsc.io/quote/v3 v3.1.0
go: downloading rsc.io/quote/v3 v3.1.0
go: extracting rsc.io/quote/v3 v3.1.0
PASS
ok      example.com/hello    0.024s
$

Вы можете видеть, что команда go загружена и установлена.rsc.io/quote/v3модулей, теперь наши модули также зависят отrsc.io/quoteиrsc.io/quote/v3:

$ go list -m rsc.io/q...
rsc.io/quote v1.5.2
rsc.io/quote/v3 v3.1.0
$

Каждая основная версия модуля Go (v1, v2 и т. д.) использует свой путь к модулю: начиная с v2 путь должен заканчиваться основной версией. В примереrsc.io/quoteВерсия v3 пути к модулю больше неrsc.io/quote, ноrsc.io/quote/v3. Эта конвенция называетсяСемантический импорт, что дает несовместимым пакетам (пакетам с разными основными версиями) разные имена. Напротив,rsc.io/quote的v1.6.0должно быть сv1.5.2обратно совместим, поэтому он повторно использует именаrsc.io/quote.

Команда go требует, чтобы путь к каждому модулю основной версии был неповторяемым, и каждая основная версия имела не более:rsc.io/quote,Одинrsc.io/quote/v2,Одинrsc.io/quote/v3,Так далее и тому подобное. Это дает авторам модулей четкие правила о возможных дублирующихся путях модулей: программы не могут использовать оба пути.rsc.io/quotev1.5.2 иrsc.io/quotev1.6.0 для сборки. В то же время, разрешение разных основных версий модуля (поскольку у них разные пути) позволяет потребителям модулей постепенно обновляться до новых основных версий. В этом примере мы хотим использоватьrsc/quote/v3в версии 3.1.0quote.Concurrency, но не готов к миграцииrsc.io/quoteИспользование версии 1.5.2. Возможность поэтапной миграции особенно важна для больших программ или кодовых баз.

Обновите зависимости до новой основной версии

Начнем с использованияrsc.io/quoteДве версии пакета для использования толькоrsc.io/quote/v3преобразование. Из-за критических изменений в версиях следует ожидать, что некоторые API могли быть удалены, переименованы или иным образом изменены несовместимым образом. Читая документацию, мы видим, что Hello сталHelloV3:

$ go doc rsc.io/quote/v3
package quote // import "rsc.io/quote"

Package quote collects pithy sayings.

func Concurrency() string
func GlassV3() string
func GoV3() string
func HelloV3() string
func OptV3() string
$

мы можем поставитьhello.goсредняя параqoute.Hello()Вызов обновлен для использованияquoteV3.HelloV3(), теперь нет необходимости переименовывать путь импорта версии v3, поэтому мы отменяем переименование пакета (обратите внимание, что имя пакета по умолчанию не содержит суффикс версии).

package hello

import "rsc.io/quote/v3"

func Hello() string {
    return quote.HelloV3()
}

func Proverb() string {
    return quote.Concurrency()
}

Повторите тесты, чтобы убедиться, что все работает:

$ go test
PASS
ok      example.com/hello       0.014s

Удалить неиспользуемые зависимости

мы удалилиrsc.io/quote, но он все еще отображается вgo list -m allвыход иgo.modВ файле:

$ go list -m all
example.com/hello
golang.org/x/text v0.3.0
rsc.io/quote v1.5.2
rsc.io/quote/v3 v3.1.0
rsc.io/sampler v1.3.1
$ cat go.mod
module example.com/hello

go 1.12

require (
    golang.org/x/text v0.3.0 // indirect
    rsc.io/quote v1.5.2
    rsc.io/quote/v3 v3.0.0
    rsc.io/sampler v1.3.1 // indirect
)
$

Зачем? Потому что сборка одного пакета (например, с помощью go build или go test) позволяет легко сказать, что чего-то не хватает и нужно добавить, но невозможно определить, безопасно ли что-то удалить. Зависимости можно удалить только после проверки всех пакетов в модуле и всех возможных комбинаций флагов сборки для этих пакетов. нормальныйbuildКоманда не загружает эту информацию, поэтому не может безопасно удалить зависимости.

go mod tidyКоманда очищает эти неиспользуемые зависимости:

$ go mod tidy
$ go list -m all
example.com/hello
golang.org/x/text v0.3.0
rsc.io/quote/v3 v3.1.0
rsc.io/sampler v1.3.1
$ cat go.mod
module example.com/hello

go 1.12

require (
    golang.org/x/text v0.3.0 // indirect
    rsc.io/quote/v3 v3.1.0
    rsc.io/sampler v1.3.1 // indirect
)

$ go test
PASS
ok      example.com/hello    0.020s
$

Суммировать

Модули Go — это будущее управления зависимостями в Go. Функционал модуля доступен начиная с Go 1.11. В этом посте описываются эти рабочие процессы с использованием модулей Go:

  • go mod init создает новый модуль, инициализируя описывающий его файл go.mod.

  • go build , go test и другие команды сборки пакетов по мере необходимостиgo.modДобавьте новые зависимости.

  • go list -m all выводит зависимости текущего модуля.

  • go get изменяет версию требуемой зависимости (или добавляет новую).

  • go mod tidy удаляет неиспользуемые зависимости.

Справочная статья:blog.gowaves.org/using-go-mo…

Сейчас все больше и больше проектов используют Go Modules для управления пакетами зависимостей. Я только начал пытаться перевести существующие проекты в режим, управляемый Go Modules. На практике я обнаружил, что еще многое предстоит узнать, чем я буду делиться позже Другие обучающие статьи и обзоры в этой области.