четверг, 17 января 2019 г.

Как писать Go код (устаревший вариант, с использованием GOPATH)

В этом посте мы продемонстрируем разработку простого Go пакета и представим go tool, стандартный способ получения, сборки и установки пакетов и команд в Go.

go tool требует, чтобы вы организовали свой код определенным образом.

Организация кода

  • Go программисты обычно хранят весь свой Go код в одном рабочем пространстве (workspace).
  • Рабочее пространство (workspace) содержит множество репозиториев систем управления версиями (например, Git).
  • Каждый репозиторий содержит один или несколько пакетов (packages).
  • Каждый пакет состоит из одного или нескольких исходных Go файлов в одном каталоге (папке).
  • Путь к каталогу пакета определяет его путь импорта (import path).

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

Рабочие пространства (Workspaces)

Рабочее пространство (workspace) - это иерархия каталогов с двумя каталогами в корне:

  • src содержит исходные файлы Go
  • bin содержит исполняемые команды

go tool создает и устанавливает бинарные файлы в каталог bin.

Подкаталог src обычно содержит несколько репозиторев систем управления версиями (например, Git или Mercurial), которые отслеживают развитие одного или более исходных пакетов.

Чтобы дать вам представление о том, как рабочее пространство выглядит на практике, приведем пример:

bin/
    hello                    # исполняемый файл
    outyet                   # исполняемый файл
src/
    github.com/golang/example/
        .git/                # метаданные Git репозитория
        hello/
            hello.go         # файл с исходным кодом
        outyet/
            main.go          # файл с исходным кодом
            main_test.go     # файл с исходным кодом тестов
        stringutil/
            reverse.go       # файл с исходным кодом пакета
            reverse_test.go  # файл с исходным кодом тестов
    golang.org/x/image/
        .git/                # метаданные Git репозитория
        bmp/
            reader.go        # файл с исходным кодом пакета
            writer.go        # файл с исходным кодом пакета
    ... (и далее еще репозитрии и пакеты) ...

Дерево выше показывает рабочее пространство, содержащее два репозитория (example и image). Репозиторий example содержит две команды (исполняемых файла) (hello и outyet) и одну библиотеку (stringutil). Репозиторий image содержит пакет bmp и несколько других пакетов.

Типичное рабочее пространство содержит множество исходных репозиториев, содержащих множество пакетов и команд (исполняемых файлов). Большинство Go программистов хранят весь свой исходный Go код и зависимости в едином рабочем пространстве.

Обратите внимание, что символические ссылки не должны использоваться для связи файлов или каталогов с вашей рабочей областью.

Команды и библиотеки создаются из разных видов исходных пакетов.

Переменная среды GOPATH

Переменная среды GOPATH указывает местоположение вашего рабочего окружения. По умолчанию это каталог с именем go внутри вашего домашнего каталога, то есть $HOME/go в Unix, и %USERPROFILE%\go (обычно C:\Users\YourName\go) в Windows.

Если вы хотите работать в другом месте, вам нужно будет задать в GOPATH путь к этому каталогу. (Другой распространенной настройкой является установка GOPATH=$HOME.) Обратите внимание, что GOPATH не должен быть тем же путем, что и путь вашей установки Go.

Команда go env GOPATH печатает действующий GOPATH; он печатает местоположение по умолчанию, если переменная окружения не установлена.

Для удобства добавьте подкаталог bin рабочего пространства в ваш PATH:

$ export PATH=$PATH:$(go env GOPATH)/bin

Скрипты в остальной части этого документа используют $GOPATH вместо $(go env GOPATH) для краткости. Чтобы скрипты работали как написано если вы не установили GOPATH, вы можете заменить $HOME/go в этих командах или выполнить:

$ export GOPATH=$(go env GOPATH)

Чтобы использовать пользовательское местоположение рабочего пространства, установите переменную среды GOPATH.

Пути импорта (Import paths)

путь импорта (import path) - это строка, которая однозначно идентифицирует пакет. Путь импорта пакета соответствует его местоположению в рабочем пространстве или в удаленном хранилище.

Пакеты из стандартной библиотеки имеют короткие пути импорта, такие как "fmt" и "net/http". Для ваших собственных пакетов вы должны выбрать базовый путь, который вряд ли столкнется с будущими дополнениями к стандартной библиотеке или другим внешним библиотекам.

Если вы храните свой код в исходном хранилище, то вам следует использовать корень этого исходного хранилища в качестве базового пути. Например, если у вас есть GitHub аккаунт на github.com/user, это должен быть ваш базовый путь.

Обратите внимание, что вам не требуется публиковать свой код в удаленном хранилище, прежде чем вы сможете собрать его (скомпилировать). Это просто хорошая привычка организовывать свой код, как будто вы будете публиковать это когда-нибудь. На практике вы можете выбрать любое произвольное имя пути, до тех пор, пока он уникален для стандартной библиотеки и большей экосистемы Go.

Мы будем использовать github.com/user в качестве основного пути. Создадим каталог внутри вашего рабочего пространства, в котором хранится исходный код:

$ mkdir -p $GOPATH/src/github.com/user

Ваша первая программа

Чтобы скомпилировать и запустить простую программу, сначала выберите путь к пакету (мы будем использовать github.com/user/hello) и создайте соответствующий каталог пакета внутри вашего рабочего пространства:

$ mkdir $GOPATH/src/github.com/user/hello

Затем создайте файл с именем hello.go внутри этого каталога, содержащий следующий Go код.

package main

import "fmt"

func main() {
    fmt.Println("Hello, world.")
}

Теперь вы можете собрать и установить эту программу с помощью инструмента go:

$ go install github.com/user/hello

Обратите внимание, что вы можете запустить эту команду из любой точки вашей системы. Инструмент go находит исходный код, производя поиск пакета github.com/user/hello внутри рабочего пространства, указанного в GOPATH.

Вы также можете пропустить путь к пакету, если запустите go install из каталога пакета:

$ cd $GOPATH/src/github.com/user/hello
$ go install

Эта команда создает команду hello, создавая исполняемый бинарный файл. Затем он устанавливается в bin каталоге рабочего пространства как hello (или в Windows hello.exe). В нашем примере это будет $GOPATH/bin/hello, который $HOME/bin/hello.

go tool будет печатать вывод только при возникновении ошибки, поэтому если эти команды не выдают результатов, значит они выполнились успешно.

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

$ $GOPATH/bin/hello

Hello, world.

Или, поскольку вы добавили $GOPATH/bin в свой PATH, просто введите двоичное имя:

$ hello

Hello, world.

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

$ cd $GOPATH/src/github.com/user/hello
$ git init
Initialized empty Git repository 
in /home/user/work/src/github.com/user/hello/.git/
$ git add hello.go
$ git commit -m "initial commit"
[master (root-commit) 0b4507d] initial commit
 1 file changed, 1 insertion(+)
  create mode 100644 hello.go

Ваша первая библиотека

Давайте напишем библиотеку и будем использовать ее из программы hello.

Опять же, первым шагом является выбор пути к пакету (мы будем использовать github.com/user/stringutil), создаем каталог пакета:

$ mkdir $GOPATH/src/github.com/user/stringutil

Затем создаем файл с именем reverse.go в этом каталоге.

// Пакет stringutil содержит утилитные функции 
// для работы со строками.
package stringutil

// Reverse возвращает его строку аргумент 
// развернутую в обратном порядке.
func Reverse(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)
}

Теперь проверьте, что пакет компилируется с помощью go build:

$ go build github.com/user/stringutil

Или, если вы работаете в исходном каталоге пакета, просто:

$ go build

Эта команда не будет производить выходной файл. Вместо этого она сохраняет скомпилированный пакет в локальном кэше сборки.

После подтверждения сборки пакета stringutil измените ваш оригинальный hello.go (который находится в $GOPATH/src/github.com/user/hello), чтобы использовать созданный пакет:

import (
    "fmt"
    "github.com/user/stringutil"
)

func main() {
    fmt.Println(stringutil.Reverse("!oG ,olleH"))
}

Установите программу hello:

$ go install github.com/user/hello

Запустив новую версию программы, вы должны увидеть новое, перевернутое сообщение:

$ hello
Hello, Go!

После выполнения описанных выше шагов ваше рабочее пространство должно выглядеть следующим образом:

bin/
    hello                 # исполняемый файл команды
src/
    github.com/user/
        hello/
            hello.go      # исходный код команды
        stringutil/
            reverse.go    # исходный код пакета

Имена пакетов

Первый оператор в исходном файле Go должен быть

package name

где name - это имя пакета по умолчанию для импорта. (Все файлы в пакете должны использовать одно и то же name.)

Общепринятое соглашение в Go состоит в том, что имя пакета является последним элементом пути импорта: пакет, импортированный как crypto/rot13 должно быть названо rot13.

Исполняемые команды всегда должны использовать package main.

Не требуется, чтобы имена пакетов были уникальными во всех пакетах, связанных в один двоичный файл, требуется только чтобы, что пути импорта (их полные имена файлов) были уникальными.

Тестирование

Go имеет легковесный фреймворк для тестирования, состоящий из go test команды и пакета testing.

Вы пишете тест, создавая файл с именем, заканчивающимся на _test.go, который содержит функции с именем TestXXX с подписью func (t * testing.T). Тестовый фреймворк выполняет каждую такую ​​функцию; если функция вызывает функцию отказа, такую ​​как t.Error или t.Fail, тест считается неудачным.

Добавим тест в пакет stringutil, создав файл $GOPATH/src/github.com/user/stringutil/reverse_test.go, содержащий следующий Go код

package stringutil

import "testing"

func TestReverse(t *testing.T) {
  cases := []struct {
    in, want string
  }{
    {"Hello, world", "dlrow ,olleH"},
    {"Hello, 世界", "界世 ,olleH"},
    {"", ""},
  }
  for _, c := range cases {
    got := Reverse(c.in)
    if got != c.want {
      t.Errorf("Reverse(%q) == %q, want %q",
               c.in, got, c.want)
    }
  }
}

Затем запустите тест с помощью go test:

$ go test github.com/user/stringutil

ok      github.com/user/stringutil 0.165s

Как всегда, если вы запускаете go tool из пакета каталога, вы можете пропустить путь пакета:

$ go test

ok      github.com/user/stringutil 0.165s

Удаленные пакеты (Remote packages)

Путь импорта может описать, как получить исходный код пакета, используя систему контроля версий, такую как Git или Mercurial. go tool использует это свойство для автоматической загрузки пакетов из удаленных репозиториев. Например, примеры, описанные в этом посте, также хранятся в Git-репозитории, размещенном на GitHub github.com/golang/example. Если вы включите URL-адрес хранилища в путь импорта пакета, go get автоматически получит, соберет и установит его:

$ go get github.com/golang/example/hello
$ $GOPATH/bin/hello
Hello, Go examples!

Если указанный пакет отсутствует в рабочем пространстве, go get поместит его в первое рабочее пространство, указанное в GOPATH. (Если пакет уже существует, go get пропускает получение удаленного пакета и ведет себя так же, как go install.)

После ввода вышеуказанной команды go get, дерево каталога рабочей области теперь должно выглядеть так:

bin/
    hello                     # исполняемая команда
src/
    github.com/golang/example/
        .git/                 # метаданные Git репозитория
        hello/
            hello.go          # исходный файл команды
        stringutil/
            reverse.go        # исходный файл пакета
            reverse_test.go   # исходный файл теста
    github.com/user/
        hello/
            hello.go          # исходный файл команды
        stringutil/
            reverse.go        # исходный файл пакета
            reverse_test.go   # исходный файл теста

Команда hello, размещенная на GitHub, зависит от пакета stringutil в том же хранилище. Импорт в файле hello.go использует то же соглашение о пути импорта, поэтому команда go get может найти и установить зависимый пакет.

import "github.com/golang/example/stringutil"

Это соглашение - самый простой способ сделать ваши Go пакеты доступными для использования другими.


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


Комментариев нет:

Отправить комментарий