Показаны сообщения с ярлыком Go модули. Показать все сообщения
Показаны сообщения с ярлыком Go модули. Показать все сообщения

суббота, 25 января 2020 г.

Go модули: v2 и далее

По мере развития успешного проекта и добавления новых требований прошлые функции и проектные решения могут перестать иметь смысл. Разработчики могут захотеть объединить полученные уроки, удалив устаревшие функции, переименовав типы или разбив сложные пакеты на управляемые части. Такие изменения требуют усилий со стороны нижестоящих пользователей для переноса своего кода в новый API, поэтому их не следует вносить без тщательного учета того, что преимущества перевешивают затраты.

Для проектов, которые все еще являются экспериментальными - в основной версии v0 - пользователи ожидают случайных критических изменений. Для проектов, которые объявлены стабильными - в основной версии v1 или выше - в новой основной версии должны быть внесены критические изменения. В этом посте рассматривается семантика основных версий, как создавать и публиковать новые основные версии и как поддерживать несколько основных версий модуля.

Основные версии и пути модулей

Модули формализовали важный принцип в Go, правило совместимости импорта:

Если старый пакет и новый пакет имеют 
одинаковый путь импорта,
новый пакет должен быть обратно совместим 
со старым пакетом.

По определению, новая основная версия пакета не имеет обратной совместимости с предыдущей версией. Это означает, что новая основная версия модуля должна иметь путь к модулю, отличному от предыдущей версии. Начиная с версии v2 основная версия должна отображаться в конце пути к модулю (объявлена в выражении модуля в файле go.mod). Например, когда авторы модуля github.com/googleapis/gax-go разработали v2, они использовали новый путь к модулю github.com/googleapis/gax-go/v2. Пользователи, которые хотели использовать v2, должны были изменить свои импорт пакетов и требования к модулю на github.com/googleapis/gax-go/v2.

Потребность в суффиксах основной версии - это одно из отличий модулей Go от большинства других систем управления зависимостями. Суффиксы необходимы для решения проблемы diamond зависимости. Перед модулями Go gopkg.in позволял сопровождающим пакетов следовать тому, что мы теперь называем правилом совместимости импорта. С gopkg.in, если вы зависите от пакета, который импортирует gopkg.in/yaml.v1, и другого пакета, который импортирует gopkg.in/yaml.v2, конфликт не возникает, потому что два пакета yaml имеют разные пути импорта - они используют суффикс версии, как с модулями Go. Поскольку gopkg.in использует ту же методологию суффикса версии, что и модули Go, команда Go принимает .v2 в gopkg.in/yaml.v2 в качестве действительного суффикса основной версии. Это особый случай совместимости с gopkg.in: для модулей, размещенных в других доменах, требуется суффикс косой черты, например /v2.

Основные версии стратегий

Рекомендуемая стратегия заключается в разработке модулей v2+ в каталоге с именем суффикса основной версии.

github.com/googleapis/gax-go @ master branch
/go.mod    → module github.com/googleapis/gax-go
/v2/go.mod → module github.com/googleapis/gax-go/v2

Этот подход совместим с инструментами, которые не знают о модулях: пути к файлам в репозитории совпадают с путями, ожидаемыми go get в режиме GOPATH. Эта стратегия также позволяет разрабатывать все основные версии вместе в разных каталогах.

Другие стратегии могут хранить основные версии в отдельных ветках. Однако, если исходный код v2+ находится в ветке хранилища по умолчанию (обычно master), инструменты, не поддерживающие версию - включая команду go в режиме GOPATH - могут не различать основные версии.

Примеры в этом посте будут следовать стратегии подкаталога основной версии, поскольку она обеспечивает наибольшую совместимость. Авторам модулей рекомендуется следовать этой стратегии, если у них есть пользователи, работающие в режиме GOPATH.

Публикация v2 и далее

Этот пост использует github.com/googleapis/gax-go в качестве примера:

$ pwd
/tmp/gax-go
$ ls
CODE_OF_CONDUCT.md  call_option.go  internal
CONTRIBUTING.md     gax.go          invoke.go
LICENSE             go.mod          tools.go
README.md           go.sum          RELEASING.md
header.go
$ cat go.mod
module github.com/googleapis/gax-go

go 1.9

require (
    github.com/golang/protobuf v1.3.1
    golang.org/x/exp v0.0.0-20190221220918-438050ddec5e
    golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3
    golang.org/x/tools v0.0.0-20190114222345-bf090417da8b
    google.golang.org/grpc v1.19.0
    honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099
)
$

Чтобы начать разработку v2 на github.com/googleapis/gax-go, мы создадим новый каталог v2/ и скопируем в него наш пакет.

$ mkdir v2
$ cp *.go v2/
building file list ... done
call_option.go
gax.go
header.go
invoke.go
tools.go

sent 10588 bytes  received 130 bytes  21436.00 bytes/sec
total size is 10208  speedup is 0.95
$

Теперь создадим файл go.mod v2, скопировав текущий файл go.mod и добавив суффикс v2/ к пути модуля:

$ cp go.mod v2/go.mod
$ go mod edit -module github.com/googleapis/gax-go/v2 v2/go.mod
$

Обратите внимание, что версия v2 рассматривается как отдельный модуль от версий v0 / v1: оба могут сосуществовать в одной сборке. Итак, если ваш модуль v2+ имеет несколько пакетов, вы должны обновить их, чтобы использовать новый путь импорта /v2: в противном случае ваш модуль v2+ будет зависеть от вашего модуля v0 / v1. Например, чтобы обновить все ссылки на github.com/my/project на github.com/my/project/v2, вы можете использовать find и sed:

$ find . -type f \
    -name '*.go' \
    -exec sed -i -e 's,github.com/my/project,github.com/my/project/v2,g' {} \;
$

Теперь у нас есть модуль v2, но мы хотим поэкспериментировать и внести изменения перед публикацией релиза. До тех пор, пока мы не выпустим v2.0.0 (или любую версию без суффикса перед релизом), мы можем разрабатывать и вносить критические изменения в зависимости от нового API. Если мы хотим, чтобы пользователи могли экспериментировать с новым API, прежде чем мы официально сделаем его стабильным, мы можем опубликовать предварительную (pre-release) версию v2:

$ git tag v2.0.0-alpha.1
$ git push origin v2.0.0-alpha.1
$

Если мы довольны нашим v2 API и уверены, что нам не нужны какие-либо другие критические изменения, мы можем пометить v2.0.0:

$ git tag v2.0.0
$ git push origin v2.0.0
$

На данный момент есть две основные версии для поддержки. Обратно совместимые изменения и исправления ошибок приведут к появлению новых минорных и исправлений (например, v1.1.0, v2.0.1 и т. д.).

Вывод

Значительные изменения версии приводят к накладным расходам на разработку и обслуживание и требуют инвестиций от последующих пользователей для перехода. Чем больше проект, тем больше эти накладные расходы. Основное изменение версии должно произойти только после определения веской причины. Как только убедительная причина была найдена для серьезного изменения, рекомендуется разработать несколько основных версий в основной ветке, потому что она совместима с более широким спектром существующих инструментов.

Прерывание изменений в модуле v1+ всегда должно происходить в новом модуле vN+1. Когда выпущен новый модуль, это означает дополнительную работу для сопровождающих и пользователей, которым необходимо перейти на новый пакет. Поэтому сопровождающие должны проверить свои API-интерфейсы перед выпуском стабильного релиза и тщательно обдумать, действительно ли необходимы критические изменения после v1.


Читайте также:


суббота, 6 июля 2019 г.

Определение модуля, файл go.mod

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

"module path" ("путь к модулю") - это префикс пути импорта, соответствующий корню модуля. Файл go.mod определяет путь к модулю и перечисляет конкретные версии других модулей, которые следует использовать при разрешении импорта во время сборки, указав пути к ним и версии модулей.

Например, следующий go.mod объявляет, что каталог, в котором он находится, является корнем модуля с путем example.com/m, а также заявляет, что модуль зависит от конкретных версий golang.org/x/text и gopkg.in/yaml.v2:

module example.com/m

require (
    golang.org/x/text v0.3.0
    gopkg.in/yaml.v2 v2.1.0
)

Файл go.mod также может указывать замены и исключенные версии, которые применяются только при непосредственной сборке модуля; они игнорируются, когда модуль встроен в более крупную сборку. Для получения дополнительной информации о файле go.mod см. go help go.mod.

Чтобы запустить новый модуль, просто создайте файл go.mod в корне дерева каталогов модуля, содержащий только module утверждение. Команда 'go mod init' может использоваться для этого:

go mod init example.com/m

В проекте, уже использующем существующий инструмент управления зависимостями, такой как godep, glide или dep, 'go mod init' также добавит операторы require, соответствующие существующей конфигурации.

Как только файл go.mod существует, никаких дополнительных действий не требуется: команды go, такие как 'go build', 'go test' или даже 'go list', автоматически добавят новые зависимости, необходимые для удовлетворения импорта.


Читайте также:


Вспомогательные темы инструмента go: модули

go help modules

Модуль представляет собой набор связанных пакетов Go. Модули - это единицы исходного кода для обмена и управления версиями. Команда go имеет прямую поддержку для работы с модулями, включая запись и разрешение зависимостей от других модулей. Модули заменяют старый подход, основанный на GOPATH, для указания, какие исходные файлы используются в данной сборке.

Предварительная поддержка модуля

Go 1.11 включает в себя предварительную поддержку модулей Go, включая новую команду 'go get' с поддержкой модулей. Поддержка будет продолжатся, сохраняя совместимость, до тех пор, пока она не будет объявлена официальной (больше не предварительной), а затем на более позднем этапе возможно будет удалена поддержка работы в GOPATH и старая команда 'go get'.

Самый быстрый способ воспользоваться преимуществами поддержки модуля Go 1.11 - выгрузить свой репозиторий в каталог за пределами GOPATH/src, создать там файл go.mod и запустить команды go из этого файлового дерева.

Для более детального управления поддержка модулей в Go 1.11 учитывает временную переменную среды GO111MODULE, для которой может быть задано одно из трех строковых значений: off, on или auto (по умолчанию). Если GO111MODULE=off, то команда go никогда не использует поддержку модулей. Вместо этого она ищет в каталогах поставщиков и GOPATH, чтобы найти зависимости; это "GOPATH mode" ("GOPATH режим"). Если GO111MODULE=on, то команда go требует использования модулей, никогда не обращаясь к GOPATH. Это называется командой, которая работает с модулем или работает в "module-aware mode" ("режиме с поддержкой модулей"). Если GO111MODULE=auto или не задано, то команда go включает или отключает поддержку модулей на основе текущего каталога. Поддержка модулей включается, только если текущий каталог находится вне GOPATH/src и сам содержит файл go.mod или находится под каталогом, содержащим файл go.mod.

В режиме с поддержкой модулей GOPATH больше не определяет значение импорта во время сборки, но по-прежнему хранит загруженные зависимости (в GOPATH/pkg/mod) и установленные команды (в GOPATH/bin, если не установлен GOBIN).


Читайте также:


пятница, 5 июля 2019 г.

Вспомогательные темы инструмента go: протокол прокси модуля

go help goproxy

Команда go по умолчанию загружает модули из систем управления версиями напрямую, как это всегда бывает с go get. Переменная среды GOPROXY позволяет дополнительно контролировать источник загрузки. Если GOPROXY не установлен, является пустой строкой или строкой "direct", при загрузке используется прямое подключение по умолчанию к системам контроля версий. Отключение GOPROXY запрещает загрузку модулей из любого источника. В противном случае ожидается, что GOPROXY будет URL-адресом прокси модуля, и в этом случае команда go извлечет все модули из этого прокси. Независимо от источника модулей, загруженные модули должны соответствовать существующим записям в go.sum.

Прокси модуля Go - это любой веб-сервер, который может отвечать на запросы GET для URL-адресов заданной формы. Запросы не имеют параметров запроса, поэтому даже сайт, обслуживающий фиксированную файловую систему (включая file:/// URL), может быть прокси модуля.

GET-запросы, отправленные на прокси Go модуля:

GET $GOPROXY/<module>/@v/list возвращает список всех известных версий данного модуля, по одной на строку.

GET $GOPROXY/<module>/@v/<version>.info возвращает метаданные в формате JSON об этой версии данного модуля.

GET $GOPROXY/<module>/@v/<version>.mod возвращает файл go.mod для этой версии данного модуля.

GET $GOPROXY/<module>/@v/<version>.zip возвращает zip-архив для этой версии данного модуля.

Чтобы избежать проблем при обслуживании из файловых систем с учетом регистра, элементы <module> и <version> кодируются регистром, заменяя каждую заглавную букву восклицательным знаком, за которым следует соответствующая строчная буква: github.com/Azure кодируется как github.com/!azure.

Метаданные в формате JSON о данном модуле соответствуют этой структуре данных Go, которая может быть расширена в будущем:

type Info struct {
    Version string    // строка версии
    Time    time.Time // время комита
}

Zip-архив для конкретной версии данного модуля - это стандартный zip-файл, содержащий дерево файлов, соответствующее исходному коду модуля и связанным файлам. В архиве используются разделенные косой чертой пути, и каждый путь к файлу в архиве должен начинаться с <module>@<version>/, где модуль и версия подставляются напрямую, а не в регистре. Корень дерева файлов модулей соответствует префиксу <module>@<version>/ в архиве.

Даже при загрузке непосредственно из систем управления версиями команда go синтезирует явно info, mod и zip файлы и сохраняет их в своем локальном кэше, $GOPATH/pkg/mod/cache/download, так же, как если бы она загружала их непосредственно из прокси. Макет кэша такой же, как и у прокси-пространства URL, поэтому обслуживание $GOPATH/pkg/mod/cache/download в (или копирование его) https://example.com/proxy позволит другим пользователям получать доступ к этим версиям кэшированных модулей с GOPROXY=https://example.com/proxy.


Читайте также:


четверг, 4 июля 2019 г.

GOPATH и модули в Golang, внутренние каталоги

При использовании модулей GOPATH больше не используется для разрешения импорта. Однако он все еще используется для хранения загруженного исходного кода (в GOPATH/pkg/mod) и скомпилированных команд (в GOPATH/bin).

Внутренние каталоги

Код внутри или ниже каталога с именем "internal" импортируется только кодом в дереве каталогов, корнем которого является родительский элемент "internal". Вот расширенная версия макета каталога из поста:

/home/user/go/
    src/
        crash/
            bang/              (go код в пакете bang)
                b.go
        foo/                   (go код в пакете foo)
            f.go
            bar/               (go код в пакете bar)
                x.go
            internal/
                baz/           (go код в пакете baz)
                    z.go
            quux/              (go код в пакете main)
                y.go

Код в z.go импортируется как "foo/internal/baz", но этот оператор импорта может появляться только в исходных файлах в поддереве с корнем в foo. Исходные файлы foo/f.go, foo/bar/x.go и foo/quux/y.go могут импортировать "foo/internal/baz", но исходный файл crash/bang/b.go не может.


Читайте также:


вторник, 25 июня 2019 г.

Go модули: удаление неиспользуемых зависимостей

Мы удалили все наши использования rsc.io/quote, но он по-прежнему отображается в списке go -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 модули: обновление зависимости до новой основной версии

Завершим наше преобразование от использования 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
$

Мы можем обновить наше использование quote.Hello() в hello.go, чтобы использовать quoteV3.HelloV3():

package hello

import quoteV3 "rsc.io/quote/v3"

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

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

И затем на этом этапе больше нет необходимости в переименованном импорте, поэтому мы можем отменить это:

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


Читайте также:


Go модули: добавление зависимости от новой основной версии

Добавим новую функцию в наш пакет, созданный в предыдущих постах: func Proverb возвращает пословицу о конкурентности в Go, вызвав quote.Concurrency, который предоставляется модулем 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
$

Обратите внимание, что наш модуль теперь зависит как от 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
$

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

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


Читайте также:


Go модули: обновление зависимостей

В модулях Go в версии указываются теги семантической версии. Семантическая версия состоит из трех частей: мажорной, минорной и патча. Например, для v0.1.2 основной (мажорной) версией является 0, второстепенной (минорной) версией - 1, а версией патча - 2. Пройдемся по паре небольших обновлений версии. В следующем посте мы рассмотрим обновление основной версии.

Из вывода списка go -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 -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 был обновлен и теперь также указывает v0.3.0. indirect комментарий указывает, что зависимость не используется непосредственно этим модулем, только косвенно другими зависимостями модуля.

Теперь попробуем обновить минорную версию rsc.io/sampler. Начнем так же, запустив go get и запустив тесты:

$ go get rsc.io/sampler
go: finding rsc.io/sampler v1.99.99
go: downloading rsc.io/sampler v1.99.99
go: extracting rsc.io/sampler v1.99.99
$ go test
--- FAIL: TestHello (0.00s)
    hello_test.go:8: Hello() = "99 bottles of beer on the wall, 99 bottles of beer, ...", want "Hello, world."
FAIL
exit status 1
FAIL    example.com/hello    0.014s
$

Провал теста показывает, что последняя версия 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
$

Мы использовали v1.3.0; v1.99.99 явно не годится. Может быть, мы можем попробовать использовать 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 get, может иметь явную версию; значение по умолчанию - @latest, которое разрешается до последней версии, определенной ранее.


Читайте также:


Go модули: добавление зависимости

Основной мотивацией для модулей Go было улучшение опыта использования (то есть добавления зависимости) кода, написанного другими разработчиками.

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

package hello

import "rsc.io/quote"

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

Теперь запустим тест, который мы создали в предыдущем посте:

$ 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, используя последнюю версию. ("Последняя" определяется как последняя помеченная как stable (не prerelease) версия, либо последняя помеченная как prerelease версия, либо последняя версия без тегов.) В нашем примере 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 test
PASS
ok      example.com/hello    0.020s
$

Обратите внимание, что хотя команда go делает добавление новой зависимости быстро и просто, она не обходится без затрат. Ваш модуль теперь буквально зависит от новой зависимости в критических областях, таких как корректность, безопасность и правильное лицензирование, и это лишь некоторые из них.

Как мы видели выше, добавление одной прямой зависимости часто приводит и к другим косвенным зависимостям. Команда 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
$

В выводе списка переходов текущий модуль, также известный как основной (main) модуль, всегда является первой строкой, за которой следуют зависимости, отсортированные по пути модуля.

Golang.org/x/text version v0.0.0-20170915032832-14c0d48ead0c является примером псевдо-версии, которая является синтаксисом версии команды go для определенного коммита без тегов.

В дополнение к 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...
$

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


Читайте также:


понедельник, 24 июня 2019 г.

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)
    }
}

На данный момент каталог содержит пакет, но не модуль, потому что нет файла go.mod. Если бы мы работали в /home/gopher/hello и запускали go test сейчас, мы бы увидели:

$ 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 test
PASS
ok      example.com/hello    0.020s
$

Вы написали и протестировали свой первый модуль.

Команда 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 модули

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

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

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

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


Читайте также: