HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-03-10
1 <p>Так повелось, что разработчики, ещё только начинающие знакомиться с Go, часто сталкиваются с проблемой<strong>выбора рабочей директории</strong>для Go-проектов. Новые гоферы часто пугают друг друга словами GOPATH и GOROOT.</p>
1 <p>Так повелось, что разработчики, ещё только начинающие знакомиться с Go, часто сталкиваются с проблемой<strong>выбора рабочей директории</strong>для Go-проектов. Новые гоферы часто пугают друг друга словами GOPATH и GOROOT.</p>
2 <p>Однако, в руководствах по быстрому старту с текущей версией Go (1.13) упоминания эти двух "страшных" слов вообще нет.</p>
2 <p>Однако, в руководствах по быстрому старту с текущей версией Go (1.13) упоминания эти двух "страшных" слов вообще нет.</p>
3 <p>Давайте посмотрим, почему так. Для чистоты эксперимента я развернула свежую Ubuntu на виртуальной машине и установила Go по<a>инструкции из Wiki</a>:</p>
3 <p>Давайте посмотрим, почему так. Для чистоты эксперимента я развернула свежую Ubuntu на виртуальной машине и установила Go по<a>инструкции из Wiki</a>:</p>
4 sudo add-apt-repository ppa:longsleep/golang-backports sudo apt-get update sudo apt-get install golang-go<p>Go 1.13 установлен и готов к использованию:</p>
4 sudo add-apt-repository ppa:longsleep/golang-backports sudo apt-get update sudo apt-get install golang-go<p>Go 1.13 установлен и готов к использованию:</p>
5 $ 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<h2>GOROOT</h2>
5 $ 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<h2>GOROOT</h2>
6 <p>Про GOROOT уже было прекрасно написано в<a>статье 2015 года</a>, и эта информация до сих пор актуальна.</p>
6 <p>Про GOROOT уже было прекрасно написано в<a>статье 2015 года</a>, и эта информация до сих пор актуальна.</p>
7 <p>Забавно, что среди списка директорий, выданных последней командой (whereis go), GOROOT на самом деле нет:</p>
7 <p>Забавно, что среди списка директорий, выданных последней командой (whereis go), GOROOT на самом деле нет:</p>
8 $ go env GOROOT /usr/lib/go-1.13<p>Итак, например, если для IDE мне понадобится указать путь к файлам стандартной библиотеки Go, я укажу /usr/lib/go-1.13. Пожалуй, на этом сценарии использования GOROOT в повседневной жизни заканчиваются.</p>
8 $ go env GOROOT /usr/lib/go-1.13<p>Итак, например, если для IDE мне понадобится указать путь к файлам стандартной библиотеки Go, я укажу /usr/lib/go-1.13. Пожалуй, на этом сценарии использования GOROOT в повседневной жизни заканчиваются.</p>
9 <h2>GOPATH и модули</h2>
9 <h2>GOPATH и модули</h2>
10 <p>Казалось бы, в этом месте надо бежать устанавливать GOPATH, но я не буду этого делать. На самом деле GOPATH уже и так задан:</p>
10 <p>Казалось бы, в этом месте надо бежать устанавливать GOPATH, но я не буду этого делать. На самом деле GOPATH уже и так задан:</p>
11 $ go env GOPATH /home/elena/go<p>Меня устраивает вариант с GOPATH в ~/go, а значит, менять его я не буду.</p>
11 $ go env GOPATH /home/elena/go<p>Меня устраивает вариант с GOPATH в ~/go, а значит, менять его я не буду.</p>
12 <p>Я сразу создам директорию для своего первого проекта на Go. Это можно сделать в любом месте, например, прямо в домашнем каталоге. Также я сразу начну работать с инструментом<a>Go Modules</a>:</p>
12 <p>Я сразу создам директорию для своего первого проекта на Go. Это можно сделать в любом месте, например, прямо в домашнем каталоге. Также я сразу начну работать с инструментом<a>Go Modules</a>:</p>
13 $ mkdir ~/hello $ go mod init github.com/rumyantseva/hello go: creating new go.mod: module github.com/rumyantseva/hello<p>Для команды go mod init я указала уникальный путь модуля моего проекта. По этому пути прокси или другой инструмент в случае необходимости смогут найти файлы моего проекта.</p>
13 $ mkdir ~/hello $ go mod init github.com/rumyantseva/hello go: creating new go.mod: module github.com/rumyantseva/hello<p>Для команды go mod init я указала уникальный путь модуля моего проекта. По этому пути прокси или другой инструмент в случае необходимости смогут найти файлы моего проекта.</p>
14 <p>После вызова команды go mod init в моём домашнем каталоге появилась директория go:</p>
14 <p>После вызова команды go mod init в моём домашнем каталоге появилась директория go:</p>
15 $ tree ~/go /home/elena/go └── pkg └── mod └── cache └── lock 3 directories, 1 file<p>При этом lock-файл (в самом низу дерева) пока пуст. В каталоге ~/hello появился файл go.mod со следующим содержанием:</p>
15 $ tree ~/go /home/elena/go └── pkg └── mod └── cache └── lock 3 directories, 1 file<p>При этом lock-файл (в самом низу дерева) пока пуст. В каталоге ~/hello появился файл go.mod со следующим содержанием:</p>
16 module github.com/rumyantseva/hello go 1.13<p>Именно в go.mod впоследствии будет храниться вся информация о зависимостях моего модуля.</p>
16 module github.com/rumyantseva/hello go 1.13<p>Именно в go.mod впоследствии будет храниться вся информация о зависимостях моего модуля.</p>
17 <p>Давайте теперь напишем приложение, использующее внешнюю зависимость. В директории ~/hello я создаю файл main.go и пишу в него такой код:</p>
17 <p>Давайте теперь напишем приложение, использующее внешнюю зависимость. В директории ~/hello я создаю файл main.go и пишу в него такой код:</p>
18 package main import ( "github.com/sirupsen/logrus" ) func main() { logrus.Info("Hello, world!") }<p>Конечно, в реальной жизни для написания "Hello, world!" можно обойтись и без<a>logrus</a>, но в этом примере эта библиотека поможет нам узнать, где хранятся файлы внешних зависимостей.</p>
18 package main import ( "github.com/sirupsen/logrus" ) func main() { logrus.Info("Hello, world!") }<p>Конечно, в реальной жизни для написания "Hello, world!" можно обойтись и без<a>logrus</a>, но в этом примере эта библиотека поможет нам узнать, где хранятся файлы внешних зависимостей.</p>
19 <p>Запускаю приложение самым простым способом:</p>
19 <p>Запускаю приложение самым простым способом:</p>
20 $ 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!<p>Перед тем, как приложение было собрано и запущено, сработал инструмент go mod. Он определил мою внешнюю зависимость github.com/sirupsen/logrus, взял её последнюю на данный момент версию v1.4.2 и отправился за транзитивными зависимостями.</p>
20 $ 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!<p>Перед тем, как приложение было собрано и запущено, сработал инструмент go mod. Он определил мою внешнюю зависимость github.com/sirupsen/logrus, взял её последнюю на данный момент версию v1.4.2 и отправился за транзитивными зависимостями.</p>
21 <p>В файл go.mod добавилась строка с описанием зависимости от logrus:</p>
21 <p>В файл go.mod добавилась строка с описанием зависимости от logrus:</p>
22 module github.com/rumyantseva/hello go 1.13 require github.com/sirupsen/logrus v1.4.2 // indirect<p>Также появился файл go.sum, в котором, помимо хэша зависимости logrus, хранится информация о хэшах транзитивных зависимостей:</p>
22 module github.com/rumyantseva/hello go 1.13 require github.com/sirupsen/logrus v1.4.2 // indirect<p>Также появился файл go.sum, в котором, помимо хэша зависимости logrus, хранится информация о хэшах транзитивных зависимостей:</p>
23 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=<p>Где же сам код зависимостей? Его можно найти в ~/go/pkg/mod. Также, в ~/go/pkg будут храниться контрольные суммы и другая служебная информация для работы с зависимостями.</p>
23 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=<p>Где же сам код зависимостей? Его можно найти в ~/go/pkg/mod. Также, в ~/go/pkg будут храниться контрольные суммы и другая служебная информация для работы с зависимостями.</p>
24 <p>Если вы уже сталкивались с инструментом<strong>go get</strong>, знаете, что он при вытягивании зависимостей фактически клонирует репозитории (например, в случае git с помощью git clone). Но go mod работает не так. Для go mod основная единица кода - модуль.</p>
24 <p>Если вы уже сталкивались с инструментом<strong>go get</strong>, знаете, что он при вытягивании зависимостей фактически клонирует репозитории (например, в случае git с помощью git clone). Но go mod работает не так. Для go mod основная единица кода - модуль.</p>
25 <p>Модули представляют собой архивы. Во время работы с зависимостями go mod явно (если вы вызывали команду go mod download) или неявно (если вы запустили компиляцию приложения) скачивает и распаковывает архивы через GOPROXY. Давайте посмотрим, как прокси задан в Go 1.13 по умолчанию:</p>
25 <p>Модули представляют собой архивы. Во время работы с зависимостями go mod явно (если вы вызывали команду go mod download) или неявно (если вы запустили компиляцию приложения) скачивает и распаковывает архивы через GOPROXY. Давайте посмотрим, как прокси задан в Go 1.13 по умолчанию:</p>
26 $ go env GOPROXY https://proxy.golang.org,direct<p>Итак, в качестве прокси при сборке моего "Hello, World!" использовался<a>proxy.golang.org</a>. Конечно, эту переменную можно изменить, выбрав другое хранилище модулей. Например, можно развернуть свой собственный внутренний прокси компании, где будут храниться, в том числе, внутренние библиотеки, код которых не публиковался в open source.</p>
26 $ go env GOPROXY https://proxy.golang.org,direct<p>Итак, в качестве прокси при сборке моего "Hello, World!" использовался<a>proxy.golang.org</a>. Конечно, эту переменную можно изменить, выбрав другое хранилище модулей. Например, можно развернуть свой собственный внутренний прокси компании, где будут храниться, в том числе, внутренние библиотеки, код которых не публиковался в open source.</p>
27 <p>В общем, если я начинаю новый проект и не против использования Go Modules, мне можно ничего не знать о GOPATH. Go самостоятельно создаст директорию ~/go тогда, когда это будет нужно.</p>
27 <p>В общем, если я начинаю новый проект и не против использования Go Modules, мне можно ничего не знать о GOPATH. Go самостоятельно создаст директорию ~/go тогда, когда это будет нужно.</p>
28 <h2>Когда нужен GOPATH?</h2>
28 <h2>Когда нужен GOPATH?</h2>
29 <p>Если вы принципиально не используете Go Modules (например, в легаси-проекте), уйти от более явной работы с GOPATH может быть не так просто.</p>
29 <p>Если вы принципиально не используете Go Modules (например, в легаси-проекте), уйти от более явной работы с GOPATH может быть не так просто.</p>
30 <p>Чтобы посмотреть, что будет с моим проектом, если я решила не использовать go mod, удаляю файлы ~/hello/go.mod и ~/hello/go.sum. Также я удалю и ~/go, чтобы вернуться к тому состоянию системы, которое у меня было в самом начале:</p>
30 <p>Чтобы посмотреть, что будет с моим проектом, если я решила не использовать go mod, удаляю файлы ~/hello/go.mod и ~/hello/go.sum. Также я удалю и ~/go, чтобы вернуться к тому состоянию системы, которое у меня было в самом начале:</p>
31 rm -rf ~/go ~/hello/go.mod ~/hello/go.sum<p>В директории ~/hello остаётся только файл main.go. Что теперь произойдет, если я попробую запустить его с помощью go run?</p>
31 rm -rf ~/go ~/hello/go.mod ~/hello/go.sum<p>В директории ~/hello остаётся только файл main.go. Что теперь произойдет, если я попробую запустить его с помощью go run?</p>
32 $ 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)<p>Вот они, эти страшные GOROOT и GOPATH!</p>
32 $ 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)<p>Вот они, эти страшные GOROOT и GOPATH!</p>
33 <p>Чтобы скомпилировать приложение, мне надо подтянуть зависимость в GOPATH. Делаю это с помощью старого доброго go get:</p>
33 <p>Чтобы скомпилировать приложение, мне надо подтянуть зависимость в GOPATH. Делаю это с помощью старого доброго go get:</p>
34 $ 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{Prefix:"golang.org/x/sys", VCS:"git", RepoRoot:"https://go.googlesource.com/sys"} 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<p>Что произошло? Первым делом, go get создал директорию ~/go (ту, что указана в качестве GOPATH). Затем начался процесс клонирования репозиториев с зависимостями. Забавно, что клонирование репозиториев выглядит заметно медленнее, чем вариант, когда мы использовали go mod для скачивания и распаковки модулей. Тем не менее, код зависимостей теперь можно найти внутри ~/go/src/.</p>
34 $ 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{Prefix:"golang.org/x/sys", VCS:"git", RepoRoot:"https://go.googlesource.com/sys"} 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<p>Что произошло? Первым делом, go get создал директорию ~/go (ту, что указана в качестве GOPATH). Затем начался процесс клонирования репозиториев с зависимостями. Забавно, что клонирование репозиториев выглядит заметно медленнее, чем вариант, когда мы использовали go mod для скачивания и распаковки модулей. Тем не менее, код зависимостей теперь можно найти внутри ~/go/src/.</p>
35 <p>Кстати, на моей чистой установке Ubuntu до сих пор не было клиента git и, чтобы go get сработал, пришлось его установить.</p>
35 <p>Кстати, на моей чистой установке Ubuntu до сих пор не было клиента git и, чтобы go get сработал, пришлось его установить.</p>
36 <p>Запускаю приложение:</p>
36 <p>Запускаю приложение:</p>
37 $ go run main.go INFO[0000] Hello, world!<h4>Работает!</h4>
37 $ go run main.go INFO[0000] Hello, world!<h4>Работает!</h4>
38 <p>Вот только на уровне приложения я теперь не отслеживаю версии внешних зависимостей. Что, если из-за уязвимости в какой-то момент в репозитории github.com/sirupsen/logrus окажется не ожидаемый мной логгер, а какой-нибудь вредоносный код?</p>
38 <p>Вот только на уровне приложения я теперь не отслеживаю версии внешних зависимостей. Что, если из-за уязвимости в какой-то момент в репозитории github.com/sirupsen/logrus окажется не ожидаемый мной логгер, а какой-нибудь вредоносный код?</p>
39 <p>Рано или поздно, мне всё-таки понадобится инструмент для работы с зависимостями, и если Go Modules по какой-то причине не подходит, придётся искать что-то другое...</p>
39 <p>Рано или поздно, мне всё-таки понадобится инструмент для работы с зависимостями, и если Go Modules по какой-то причине не подходит, придётся искать что-то другое...</p>
40 <h2>Заключение</h2>
40 <h2>Заключение</h2>
41 <p>В этой статье не рассмотрены некоторые специфичные моменты, и работа с внешними зависимостями в Go по-прежнему может вызвать много вопросов. Тем не менее, новые версии Go хотя бы не накладывают ограничений на то, где могут быть созданы рабочие каталоги ваших проектов.</p>
41 <p>В этой статье не рассмотрены некоторые специфичные моменты, и работа с внешними зависимостями в Go по-прежнему может вызвать много вопросов. Тем не менее, новые версии Go хотя бы не накладывают ограничений на то, где могут быть созданы рабочие каталоги ваших проектов.</p>
42 <p>Если вы начинаете новый проект, попробуйте Go Modules! Возвращаться к старому подходу к работе с зависимостями имеет смысл, только если что-то пошло не так. Кстати, если вы предпочитаете хранить все зависимости внутри проекта, Go Modules поддерживает режим vendor.</p>
42 <p>Если вы начинаете новый проект, попробуйте Go Modules! Возвращаться к старому подходу к работе с зависимостями имеет смысл, только если что-то пошло не так. Кстати, если вы предпочитаете хранить все зависимости внутри проекта, Go Modules поддерживает режим vendor.</p>
43 <p>Если вам нужно работать с уже существующим проектом, и по каким-то причинам его не хочется переводить на Go Modules, в документации к проекту важно указать<strong>особенности его развёртывания и управления зависимостями</strong>. Если в проект придут новички, незнакомые со старыми подходами к работе с зависимостями, им будет гораздо проще разобраться с проектом, если вся документация будет на месте.</p>
43 <p>Если вам нужно работать с уже существующим проектом, и по каким-то причинам его не хочется переводить на Go Modules, в документации к проекту важно указать<strong>особенности его развёртывания и управления зависимостями</strong>. Если в проект придут новички, незнакомые со старыми подходами к работе с зависимостями, им будет гораздо проще разобраться с проектом, если вся документация будет на месте.</p>
44  
44