Этот пост демонстрирует разработку простого пакета Go внутри модуля и знакомит с инструментом go, стандартным способом получения, сборки и установки модулей, пакетов и команд Go.
Примечание. В этом посте предполагается, что вы используете Go 1.13 или новее, а переменная среды GO111MODULE не установлена. Если вы ищете старую версию этого поста, предшествующую модулям, она находится здесь.
Организация кода
Программы Go организованы в пакеты. Пакет - это набор исходных файлов в одном каталоге, которые скомпилированы вместе. Функции, типы, переменные и константы, определенные в одном исходном файле, видны всем другим исходным файлам в том же пакете.
Репозиторий содержит один или несколько модулей. Модуль - это набор связанных пакетов Go, которые выпускаются вместе. Репозиторий Go обычно содержит только один модуль, расположенный в корне репозитория. Файл с именем go.mod объявляет путь к модулю: префикс пути импорта для всех пакетов в модуле. Модуль содержит пакеты в каталоге, содержащем его файл go.mod, а также подкаталоги этого каталога до следующего подкаталога, содержащего другой файл go.mod (если есть).
Обратите внимание, что вам не нужно публиковать свой код в удаленном репозитории перед его сборкой. Модуль может быть определен локально без принадлежности к репозиторию. Тем не менее, это хорошая привычка организовывать свой код так, как будто вы когда-нибудь его опубликуете.
Путь к каждому модулю не только служит префиксом пути импорта для его пакетов, но также указывает, где команда go должна искать его для его загрузки. Например, чтобы загрузить модуль golang.org/x/tools, команда go будет обращаться к репозиторию, указанному https://golang.org/x/tools.
Путь импорта - это строка, используемая для импорта пакета. Путь импорта пакета - это путь к модулю, соединенный с его подкаталогом внутри модуля. Например, модуль github.com/google/go-cmp содержит пакет в каталоге cmp/. Путь импорта этого пакета - github.com/google/go-cmp/cmp. Пакеты в стандартной библиотеке не имеют префикса пути к модулю.
Ваша первая программа
Чтобы скомпилировать и запустить простую программу, сначала выберите путь к модулю (мы будем использовать example.com/user/hello) и создайте файл go.mod, который его объявляет:
$ mkdir hello # Или клонируйте его, если он уже существует в системе контроля версий.
$ cd hello
$ go mod init example.com/user/hello
go: creating new go.mod: module example.com/user/hello
$ cat go.mod
module example.com/user/hello
go 1.16
$
Первым оператором в исходном файле Go должно быть имя пакета. Исполняемые команды всегда должны использовать пакет main.
Затем создайте внутри этого каталога файл с именем hello.go, содержащий следующий код Go:
package main
import "fmt"
func main() {
fmt.Println("Hello, world.")
}
Теперь вы можете собрать и установить эту программу с помощью инструмента go:
$ go install example.com/user/hello
$
Эта команда создает команду hello, создавая исполняемый двоичный файл. Затем он устанавливает этот двоичный файл как $HOME/go/bin/hello (или, в Windows, %USERPROFILE%\go\bin\hello.exe).
Каталог установки управляется переменными среды GOPATH и GOBIN. Если установлен GOBIN, в этот каталог устанавливаются двоичные файлы. Если установлен GOPATH, двоичные файлы устанавливаются в подкаталог bin первого каталога в списке GOPATH. В противном случае двоичные файлы устанавливаются в подкаталог bin GOPATH по умолчанию ($HOME/go или %USERPROFILE%\go).
Вы можете использовать команду go env для переносимой установки значения по умолчанию для переменной среды для будущих команд go:
$ go env -w GOBIN=/somewhere/else/bin
$
Чтобы отключить переменную, ранее установленную с помощью go env -w, используйте go env -u:
$ go env -u GOBIN
$
Такие команды, как go install, применяются в контексте модуля, содержащего текущий рабочий каталог. Если рабочий каталог не находится в модуле example.com/user/hello, go install может завершиться ошибкой.
Для удобства команды go принимают пути относительно рабочего каталога и по умолчанию используют пакет в текущем рабочем каталоге, если не указан другой путь. Итак, в нашем рабочем каталоге все следующие команды эквивалентны:
$ go install example.com/user/hello
$ go install .
$ go install
Затем запустим программу, чтобы убедиться, что она работает. Для дополнительного удобства мы добавим каталог установки в наш PATH, чтобы упростить запуск двоичных файлов:
$ export PATH=$PATH:$(dirname $(go list -f '{{.Target}}' .))
$ hello
Hello, world.
$
Пользователи Windows могут проконсультироваться в этом посте по установке %PATH%.
Если вы используете систему управления версиями, сейчас самое подходящее время для инициализации репозитория, добавления файлов и фиксации вашего первого изменения. Опять же, этот шаг не является обязательным: вам не нужно использовать систему управления версиями для написания кода Go.
$ git init
Initialized empty Git repository in /home/user/hello/.git/
$ git add go.mod hello.go
$ git commit -m "initial commit"
[master (root-commit) 0b4507d] initial commit
1 file changed, 7 insertion(+)
create mode 100644 go.mod hello.go
$
Команда go находит репозиторий, содержащий заданный путь к модулю, запрашивая соответствующий HTTPS URL-адрес и считывая метаданные, встроенные в HTML ответ. Многие службы хостинга уже предоставляют эти метаданные для репозиториев, содержащих код Go, поэтому самый простой способ сделать ваш модуль доступным для использования другими - это, как правило, сделать так, чтобы его путь модуля соответствовал URL-адресу репозитория.
Импорт пакетов из вашего модуля
Напишем пакет morestrings и воспользуемся им из программы hello. Сначала создайте каталог для пакета с именем $HOME/hello/morestrings, а затем файл с именем reverse.go в этом каталоге со следующим содержимым:
// Пакет morestrings реализует дополнительные функции
// для управления UTF-8 закодированными строками сверх того,
// что предусмотрено в стандартном пакете "strings".
package morestrings
// ReverseRunes возвращает строку аргументов,
// перевернутую по рунам слева направо.
func ReverseRunes(s string) string {
r := []rune(s)
for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
r[i], r[j] = r[j], r[i]
}
return string(r)
}
Поскольку наша функция ReverseRunes начинается с заглавной буквы, она экспортируется и может использоваться в других пакетах, которые импортируют наш пакет morestrings.
Давайте проверим, что пакет компилируется с помощью go build:
$ cd $HOME/hello/morestrings
$ go build
$
Это не приведет к созданию выходного файла. Вместо этого он сохраняет скомпилированный пакет в локальном кеше сборки.
Убедившись, что пакет morestrings собирается, давайте воспользуемся им из программы hello. Для этого измените исходный $HOME/hello/hello.go, чтобы использовать пакет morestrings:
package main
import (
"fmt"
"example.com/user/hello/morestrings"
)
func main() {
fmt.Println(morestrings.ReverseRunes("!oG ,olleH"))
}
Установите программу hello:
$ go install example.com/user/hello
Запустив новую версию программы, вы должны увидеть новое перевернутое сообщение:
$ hello
Hello, Go!
Импорт пакетов из удаленных модулей
Путь импорта может описывать, как получить исходный код пакета с помощью системы контроля версий, такой как Git или Mercurial. Инструмент go использует это свойство для автоматической загрузки пакетов из удаленных репозиториев. Например, чтобы использовать github.com/google/go-cmp/cmp в своей программе:
package main
import (
"fmt"
"example.com/user/hello/morestrings"
"github.com/google/go-cmp/cmp"
)
func main() {
fmt.Println(morestrings.ReverseRunes("!oG ,olleH"))
fmt.Println(cmp.Diff("Hello World", "Hello Go"))
}
Теперь, когда у вас есть зависимость от внешнего модуля, вам нужно загрузить этот модуль и записать его версию в файл go.mod.
Команда go mod tidy добавляет требования к отсутствующим модулям для импортированных пакетов и удаляет требования к модулям, которые больше не используются.
$ go mod tidy
go: finding module for package github.com/google/go-cmp/cmp
go: found github.com/google/go-cmp/cmp in github.com/google/go-cmp v0.5.4
$ go install example.com/user/hello
$ hello
Hello, Go!
string(
- "Hello World",
+ "Hello Go",
)
$ cat go.mod
module example.com/user/hello
go 1.16
require github.com/google/go-cmp v0.5.4
$
Зависимости модулей автоматически загружаются в подкаталог pkg/mod каталога, указанного переменной среды GOPATH. Загруженное содержимое для данной версии модуля совместно используется всеми другими модулями, которым требуется эта версия, поэтому команда go помечает эти файлы и каталоги как доступные только для чтения. Чтобы удалить все загруженные модули, вы можете передать флаг -modcache для очистки:
$ go clean -modcache
$
Тестирование
Go имеет легкий фреймворк тестирования, состоящую из команды go test и пакета testing.
Вы пишете тест, создавая файл с именем, заканчивающимся на _test.go, который содержит функции с именем TestXXX с сигнатурой func (t *testing.T). Фреймворк тестирования запускает каждую такую функцию; если функция вызывает функцию сбоя, такую как t.Error или t.Fail, тест считается неудачным.
Добавьте тест в пакет morestrings, создав файл $HOME/hello/morestrings/reverse_test.go, содержащий следующий код Go.
package morestrings
import "testing"
func TestReverseRunes(t *testing.T) {
cases := []struct {
in, want string
}{
{"Hello, world", "dlrow ,olleH"},
{"Hello, 世界", "界世 ,olleH"},
{"", ""},
}
for _, c := range cases {
got := ReverseRunes(c.in)
if got != c.want {
t.Errorf("ReverseRunes(%q) == %q, want %q", c.in, got, c.want)
}
}
}
Затем запустите тест с помощью go test:
$ go test
PASS
ok example.com/user/morestrings 0.165s
$
Что дальше
Чтобы ознакомиться с оcновами Go воспользуйтесь серией постов Основы Go.
Подборка Эффективный Go содержит советы по написанию понятного идиоматического Go кода.
Читайте также:
Комментариев нет:
Отправить комментарий