Хабрахабр

Правда ли, что GOPATH и GOROOT больше не нужны?

Вот и в чате конференции GolangConf тоже задавался этот вопрос. Так повелось, что разработчики, еще только начинающие знакомиться с Go, часто сталкиваются с проблемой выбора рабочей директории для Go-проектов. Однако, в руководствах по быстрому старту с текущей версией Go (1. Новые гоферы часто пугают друг друга словами GOPATH и GOROOT. 13) упоминания эти двух «страшных» слов вообще нет.

Для чистоты эксперимента я развернула свежую Ubuntu на виртуальной машине и установила Go по инструкции из Wiki: Давайте посмотрим, почему так.

sudo add-apt-repository ppa:longsleep/golang-backports
sudo apt-get update
sudo apt-get install golang-go

13 установлен и готов к использованию: Go 1.

$ go version
go version go1.13 linux/amd64
$ which go
/usr/bin/go
$ whereis go
go: /usr/bin/go /usr/lib/go /usr/share/go /usr/share/man/man1/go.1.gz

GOROOT

Про GOROOT уже было прекрасно написано в статье 2015-года, и эта информация до сих пор актуальна.

Забавно, что среди списка директорий, выданных последней командой (whereis go), GOROOT на самом деле нет:

$ go env GOROOT
/usr/lib/go-1.13

13. Итак, например, если для IDE мне понадобится указать путь к файлам стандартной библиотеки Go, я укажу /usr/lib/go-1. Пожалуй, на этом сценарии использования GOROOT в повседневной жизни заканчиваются.

GOPATH и модули

На самом деле GOPATH уже и так задан: Казалось бы, в этом месте надо кинуться устанавливать GOPATH, но я не буду этого делать.

$ go env GOPATH
/home/elena/go

Меня устраивает вариант с GOPATH в ~/go, а значит, менять его я не буду.

Это можно сделать в любом месте, например, прямо в домашнем каталоге. Я сразу создам директорию для своего первого проекта на Go. Также я сразу начну работать с инструментом Go Modules:

$ mkdir ~/hello
$ go mod init github.com/rumyantseva/hello
go: creating new go.mod: module github.com/rumyantseva/hello

По этому пути прокси или другой инструмент в случае необходимости сможет найти файлы моего проекта. Для команды go mod init я указала уникальный путь модуля моего проекта.

После вызова команды go mod init в моем домашнем каталоге появилась директория go:

$ tree ~/go
/home/elena/go
└── pkg └── mod └── cache └── lock 3 directories, 1 file

При этом lock-файл (в самом низу дерева), пока пуст.

В каталоге ~/hello появился файл go.mod со следующим содержанием:

module github.com/rumyantseva/hello go 1.13

Именно в go.mod впоследствии будет храниться вся информация о зависимостях моего модуля.

В директории ~/hello я создаю файл main.go и пишу в него такой код: Давайте теперь напишем приложение, использующее внешнюю зависимость.

package main import ( "github.com/sirupsen/logrus"
) func main() { logrus.Info("Hello, world!")
}

Конечно, в реальной жизни для написания "Hello, world!" можно обойтись и без logrus, но в этом примере эта библиотека поможет нам узнать, где хранятся файлы внешних зависимостей.

Запускаю приложение самым простым способом:

$ go run main.go
go: finding github.com/sirupsen/logrus v1.4.2
go: downloading github.com/sirupsen/logrus v1.4.2
go: extracting github.com/sirupsen/logrus v1.4.2
go: downloading golang.org/x/sys v0.0.0-20190422165155-953cdadca894
go: extracting golang.org/x/sys v0.0.0-20190422165155-953cdadca894
go: finding golang.org/x/sys v0.0.0-20190422165155-953cdadca894
INFO[0000] Hello, world!

Он определил мою внешнюю зависимость github.com/sirupsen/logrus, взял ее последнюю на данный момент версию v1. Перед тем, как приложение было собрано и запущено, сработал инструмент go mod. 2 и отправился за транзитивными зависимостями. 4.

В файл go.mod добавилась строка с описанием зависимости от logrus:

module github.com/rumyantseva/hello go 1.13 require github.com/sirupsen/logrus v1.4.2 // indirect

Также появился файл go.sum, в котором помимо хэша зависимости logrus хранится информация о хэшах транзитивных зависимостей:

github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

Его можно найти в ~/go/pkg/mod. Где же сам код зависимостей? Также, в ~/go/pkg будут храниться контрольные суммы и другая служебная информация для работы с зависимостями.

Но go mod работает не так. Если вы уже сталкивались с инструментом go get, знаете, что он при вытягивании зависимостей фактически клонирует репозитории (например, в случае git с помощью git clone). Модули представляют собой архивы. Для go mod основная единица кода — модуль. Давайте посмотрим, как прокси задан в Go 1. Во время работы с зависимостями go mod явно (если вы вызывали команду go mod download) или неявно (если вы запустили компиляцию приложения) скачивает и распаковывает архивы через GOPROXY. 13 по умолчанию:

$ go env GOPROXY
https://proxy.golang.org,direct

Конечно, эту переменную можно изменить, выбрав другое хранилище модулей. Итак, в качестве прокси при сборке моего "Hello, World!" использовался proxy.golang.org. Например, можно развернуть свой собственный внутренний прокси компании, где будут храниться, в том числе, внутренние библиотеки, код которых не публиковался в open source.

Go самостоятельно создаст директорию ~/go тогда, когда это будет нужно. В общем, если я начинаю новый проект и не против использования Go Modules, мне можно ничего не знать о GOPATH.

Когда нужен GOPATH?

Если вы принципиально не используете Go Modules (например, в легаси-проекте), уйти от более явной работы с GOPATH может быть не так просто.

Также я удалю и ~/go, чтобы вернуться к тому состоянию системы, которое у меня было в самом начале: Чтобы посмотреть, что будет с моим проектом, если я решила не использовать go mod, удаляю файлы ~/hello/go.mod и ~/hello/go.sum.

rm -rf ~/go ~/hello/go.mod ~/hello/go.sum

Что теперь произойдет, если я попробую запустить его с помощью go run? В директории ~/hello остается только файл main.go.

$ go run main.go
main.go:4:2: cannot find package "github.com/sirupsen/logrus" in any of: /usr/lib/go-1.13/src/github.com/sirupsen/logrus (from $GOROOT) /home/elena/go/src/github.com/sirupsen/logrus (from $GOPATH)

Вот они, эти страшные GOROOT и GOPATH 🙂

Делаю это с помощью старого доброго go get: Для того, чтобы скомпилировать приложение, мне надо подянуть зависимость в GOPATH.

$ go get -v github.com/sirupsen/logrus
github.com/sirupsen/logrus (download)
created GOPATH=/home/elena/go; see 'go help gopath'
get "golang.org/x/sys/unix": found meta tag get.metaImport at //golang.org/x/sys/unix?go-get=1
get "golang.org/x/sys/unix": verifying non-authoritative meta tag
golang.org/x/sys (download)
golang.org/x/sys/unix
github.com/sirupsen/logrus

Первым делом, go get создал директорию ~/go (ту, что указана в качестве GOPATH). Что произошло? Забавно, что клонирование репозиториев выглядит заметно медленнее, чем вариант, когда мы использовали go mod для скачивания и распаковки модулей. Затем начался процесс клонирования репозиториев с зависимостями. Тем не менее, код зависимостей теперь можно найти внутри ~/go/src/.

Кстати, на моей чистой установке Ubuntu до сих пор не было клиента git и, для того чтобы go get сработал, пришлось его установить.

Запускаю приложение:

$ go run main.go INFO[0000] Hello, world!

Работает!

Что если из-за уязвимости в какой-то момент в репозитории github.com/sirupsen/logrus окажется не ожидаемый мной логгер, а какой-нибудь вредоносный код? Вот только на уровне приложения я теперь не отслеживаю версии внешних зависимостей. Рано или поздно, мне всё-таки понадобится инструмент для работы с зависимостями, и если Go Modules по какой-то причине не подходит, придётся искать что-то другое...

Заключение

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

Возвращаться к старому подходу к работе с зависимостями имеет смысл, только если что-то пошло не так. Если вы начинаете новый проект, попробуйте Go Modules! Кстати, если вы предпочитаете хранить все зависимости внутри проекта, Go Modules поддерживает режим vendor.

Если в проект придут новички незнакомые со старыми подходами к работе с зависимостями, им будет гораздо проще разобраться с проектом, если вся документация будет на месте. Если вам нужно работать с уже существующим проектом, и по каким-то причинам его не хочется переводить на Go Modules, в документации к проекту важно указать особенности его развертывания и управления зависимостями.

Установить Go? Кстати, 7-го октября на конференции GolangConf в качестве одной из специальных активностей, мы планируем экспертную зону, где любой желающий сможет задать любые вопросы по Go членам программного комитета конференции и энтузиастам российского Go-сообщества. Написать микросервис? Разобраться с зависимостями? Это к нам!

Показать больше

Похожие публикации

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»