One place for hosting & domains

      Создание приложений Go для различных операционных систем и разной архитектуры


      При разработке программного обеспечения очень важно учитывать операционную систему и архитектуру процессора, для которого вы будете компилировать ваш бинарный файл. Поскольку часто бывает очень трудно или просто невозможно запустить один бинарный файл на разных ОС/архитектурах, довольно распространенной практикой является сборка итогового бинарного файла для набора различных платформ для максимального увеличения аудитории вашей программы. Однако это может оказаться затруднительным, когда платформа, которую вы используете для разработки, отличается от платформы, где вы хотите развернуть вашу программу. В прошлом, например, при разработке программы на Windows и ее последующем развертывании на компьютере с Linux или macOS могла требоваться настройка инструмента для сборки для каждой среды, где вам требуется бинарный файл. Кроме того, вам нужно поддерживать ваш инструментарий в синхронизированном состоянии, а также учитывать другие моменты, что будет приводить к росту издержек и затруднит совместное тестирование и распространение.

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

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

      Предварительные требования

      Для выполнения примера из этой статьи вам потребуется следующее:

      Возможные платформы GOOS и GOARCH

      Прежде чем продемонстрировать, как вы можете контролировать процесс сборки бинарных файлов для различных платформ, давайте узнаем, для каких платформ Go в состоянии выполнять сборку, а также как Go ссылается на эти платформы, используя переменные среды GOOS и GOARCH.

      Инструменты Go имеют команду, которая может вывести список поддерживаемых Go платформ для сборки. Этот список может меняться с каждым новым релизом Go, так что описанные здесь комбинации могут быть иными в другой версии Go. На момент написания настоящего руководства текущая версия Go — 1.13.

      Чтобы просмотреть список поддерживаемых платформ, запустите следующую команду:

      Вы получите примерно следующий результат:

      Output

      aix/ppc64 freebsd/amd64 linux/mipsle openbsd/386 android/386 freebsd/arm linux/ppc64 openbsd/amd64 android/amd64 illumos/amd64 linux/ppc64le openbsd/arm android/arm js/wasm linux/s390x openbsd/arm64 android/arm64 linux/386 nacl/386 plan9/386 darwin/386 linux/amd64 nacl/amd64p32 plan9/amd64 darwin/amd64 linux/arm nacl/arm plan9/arm darwin/arm linux/arm64 netbsd/386 solaris/amd64 darwin/arm64 linux/mips netbsd/amd64 windows/386 dragonfly/amd64 linux/mips64 netbsd/arm windows/amd64 freebsd/386 linux/mips64le netbsd/arm64 windows/arm

      Данный вывод — это набор пар ключ-значение, разделенных /. Первая часть комбинации перед символом / — это операционная система. В Go эти операционные системы — это возможные значения для переменной среды GOOS, произносится “goose”, название которой означает операционная система Go. Вторая часть, идущая после /, — это архитектура. Как и в случае с операционной системой, это все возможные значения для переменной среды: GOARCH. Это сокращение произносится “gore-ch” и означает архитектура Go.

      Давайте разберем одну из этих комбинаций, чтобы понять, что это означает и как работает, воспользовавшись linux/386 в качестве примера. Пара ключ-значение начинается с переменной GOOS, которая в этом случае будет linux, то есть ОС Linux. Для GOARCH здесь указано значение 386, что означает микропроцессор Intel 80386.

      Существует множество доступных для команды go build платформ, но в большинстве случаев вы будете использовать linux, windows или darwin в качестве значения для GOOS. Это позволяет покрыть три важнейшие платформы OS: Linux, Windows и macOS, которая основана на операционной системе Darwin и поэтому называется darwin. Однако Go позволяет охватить и менее популярные платформы, например nacl, т. е. Native Client от Google.

      Когда вы запускаете команду, например go build​​​, Go использует GOOS и GOARCH для определения способа сборки бинарного файла. Чтобы узнать, какая конфигурация у вашей платформы, вы можете использовать команду go env и передать GOOS и GOARCH в качестве аргументов:

      При тестировании этого примера мы запускаем эту команду в macOS на компьютере с архитектурой AMD64, поэтому мы получим следующее:

      Output

      darwin amd64

      Здесь вывод команды указывает нам, что в нашей системе GOOS=darwin и GOARCH=amd64.

      Теперь вы знаете, что такое GOOS и GOARCH в Go, а также их возможные значения. Далее вы выполните сборку программы для использования в качестве примера использования этих переменных среды и меток сборки для получения бинарных файлов для других платформ.

      Написание платформо-зависимой программы с помощью filepath.Join()

      Прежде чем начать сборку бинарных файлов для других платформ, мы создадим пример программы. Для этой задачи отлично подходит функция Join в пакете path/filepath в стандартной библиотеке Go. Эта функция получает ряд строк и возвращает одну строку, к которой добавлен соответствующий разделитель пути файла.

      Это отличный пример программы, поскольку работа программы зависит от того, в какой ОС она запущена. Для Windows разделитель пути файла — это обратный слэш ​​​, а в системах Unix используется прямой слэш /.

      Давайте начнем сборку приложения, которое использует filepath.Join(), а затем вы напишете свою собственную реализацию функции Join(), которая настраивает код в соответствии с платформой.

      Во-первых, создайте папку в директории src с именем вашего приложения:

      Перейдите в эту директорию:

      Далее создайте новый файл в текстовом редакторе по вашему выбору с именем main.go. В этом обучающем руководстве мы будем использовать Nano:

      После открытия файла добавьте следующий код:

      src/app/main.go

      package main
      
      import (
        "fmt"
        "path/filepath"
      )
      
      func main() {
        s := filepath.Join("a", "b", "c")
        fmt.Println(s)
      }
      

      Функция main() в этом файле использует filepath.Join() для конкатенации трех строк вместе с правильным платформо-зависимым разделителем пути.

      Сохраните и закройте файл, а затем запустите программу:

      При запуске этой программы вы получите разный вывод в зависимости от того, какую платформу вы используете. В Windows вы увидите, что строки будут разделены :

      Output

      abc

      В системах Unix, в том числе macOS и Linux, вы получите следующее:

      Output

      a/b/c

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

      Давайте рассмотрим, откуда функция filepath.Join() получает нужный разделитель. Запустите следующую команду для просмотра соответствующего сниппета из стандартной библиотеки Go:

      • less /usr/local/go/src/os/path_unix.go

      В результате вы увидите содержимое path_unix.go. Найдите следующую часть файла:

      /usr/local/go/os/path_unix.go

      . . .
      // +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris
      
      package os
      
      const (
        PathSeparator     = '/' // OS-specific path separator
        PathListSeparator = ':' // OS-specific path list separator
      )
      . . .
      

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

      Нажмите q для возврата к командной строке.

      Далее откройте файл, который определяет поведение filepath.Join() при использовании в Windows:

      • less /usr/local/go/src/os/path_windows.go

      Вы увидите следующее:

      /usr/local/go/os/path_unix.go

      . . .
      package os
      
      const (
              PathSeparator     = '\' // OS-specific path separator
              PathListSeparator = ';'  // OS-specific path list separator
      )
      . . .
      

      Хотя здесь значение PathSeparator будет \, код будет отображать отдельный обратный слэш (), необходимый для путей файлов в Windows, поскольку первый обратный слэш требуется только в качестве символа перехода.

      Обратите внимание, что в отличие от файла Unix, сверху нет меток сборки. Это объясняется тем, что GOOS и GOARCH также могут быть переданы команде go build, добавив нижнее подчеркивание (_) и значение переменной среды в качестве суффикса для имени файла. Мы обсудим это подробнее в разделе Использование суффиксов имен файлов GOOS и GOARCH. Здесь часть _windows названия имени path_windows.go заставляет файл вести себя так, будто у него есть маркер сборки // +build windows в верхней части файла. В связи с этим, когда ваша программа запускается в Windows, она будет использовать константы PathSeparator и PathListSeparator из сниппета кода path_windows.go.

      Чтобы вернуться к командной строке, выйдите из less, нажав q.

      На этом шаге вы создали программу, показывающую, как Go автоматически преобразовывает GOOS и GOARCH в маркеры сборки. Держа это в уме, вы можете обновить вашу программу и написать свою собственную реализацию filepath.Join(), используя маркеры сборки, чтобы вручную задать правильный PathSeparator для платформ Windows и Unix.

      Реализация платформенно-зависимой функции

      Теперь, когда вы знаете, как стандартная библиотека Go имплементирует специфичный для платформы код, вы можете использовать маркеры сборки, чтобы сделать это в вашей собственной программе app. Для этого вам нужно будет написать свою собственную реализацию filepath.Join().

      Откройте ваш файл main.go:

      Замените содержимое main.go на следующее, используя свою собственную функцию с именем Join():

      src/app/main.go

      package main
      
      import (
        "fmt"
        "strings"
      )
      
      func Join(parts ...string) string {
        return strings.Join(parts, PathSeparator)
      }
      
      func main() {
        s := Join("a", "b", "c")
        fmt.Println(s)
      }
      

      Функция Join получает ряд частей и объединяет их вместе с помощью метода strings.Join() из пакета strings для конкатенации частей, используя PathSeparator.

      Вы еще не определили PathSeparator, поэтому давайте сделаем это в другом файле. Сохраните и выйдите из main.go, откройте ваш любимый редактор и создайте новый файл с именем path.go:

      nano path.go
      

      Определите PathSeparator и установите его равным разделителю пути файла Unix /:

      src/app/path.go

      package main
      
      const PathSeparator = "/"
      

      Скомпилируйте и запустите приложение:

      Вывод должен выглядеть следующим образом:

      Output

      a/b/c

      Это значит, что программа успешно запускается для получения пути файла в стиле Unix. Однако это еще не то, что нам нужно: вывод всегда будет a/b/c, вне зависимости от того, на какой платформе программа запущена. Чтобы добавить функционал создания пути файла в стиле Windows, вам нужно добавить версию PathSeparator для Windows и указать команде go build, какую версию следует использовать. В следующем разделе вы сможете воспользоваться маркерами сборки, чтобы выполнить эту задачу.

      Использование маркеров GOOS или GOARCH

      Чтобы учитывать платформы Windows, вы создадите альтернативный файл для path.go и будете использовать маркеры сборки, чтобы убедиться, что сниппеты кода запускаются, только когда GOOS и GOARCH принадлежат к соответствующей платформе.

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

      Добавьте в файл выделенный маркер сборки:

      src/app/path.go

      // +build !windows
      
      package main
      
      const PathSeparator = "/"
      

      Маркеры сборки Go позволяют использовать инвертирование, что означает, что вы можете указать Go выполнить сборку этого файла для любой платформы, кроме Windows. Чтобы инвертировать маркер доступа, добавьте ! перед маркером.

      Сохраните и закройте файл.

      Теперь, если бы вы захотели запустить эту программу в Windows, то получили бы следующую ошибку:

      Output

      ./main.go:9:29: undefined: PathSeparator

      В данном случае Go не сможет включить path.go для определения переменной PathSeparator.

      Теперь, когда вы убедились, что path.go не будет запускаться, когда для GOOS используется значение Windows, добавьте новый файл, windows.go:

      В файле windows.go необходимо определить PathSeparator для Windows, а также маркер сборки, чтобы команда go build смогла понимать, что это реализация для Windows:

      src/app/windows.go

      // +build windows
      
      package main
      
      const PathSeparator = "\"
      

      Сохраните файл и выйдите из текстового редактора. Теперь приложение может компилироваться одним образом для Windows, а другим образом для всех остальных платформ.

      Хотя теперь сборка бинарных файлов будет выполняться корректно для соответствующих платформ, есть и другие изменения, которые нужно внести для выполнения компиляции для платформы, к которой у вас нет доступа. Для этого на следующем шаге нам нужно будет изменить локальные переменные GOOS и GOARCH.

      Использование ваших локальных переменных среды GOOS и GOARCH

      Ранее вы запускали команду go env GOOS GOARCH для получения информации о том, в какой ОС и с какой архитектурой вы работаете. При запуске команды go env выполняется поиск двух переменных среды GOOS и GOARCH; если их удалось найти, будут использоваться их значения, но если они не были найдены, Go будет использовать для них информацию для текущей платформы. Это означает, что вы можете изменить значения GOOS или GOARCH, чтобы они не совпадали по умолчанию с данными локальной операционной системы и архитектуры.

      Команда go build ведет себя примерно так же, как и команда go env. Вы можете задать для переменных среды GOOS или GOARCH значение для получения сборки для другой платформы с помощью команды go build.

      Если вы не используете систему Windows, создайте бинарный файл windows для app, установив для переменной среды GOOS значение windows при запуске команды go build:

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

      Вывод списка файлов в директории теперь показывает, что в директории проекта есть исполняемый файл app.exe для Windows:

      Output

      app app.exe main.go path.go windows.go

      Используя команду file, вы можете получить дополнительную информацию об этом файле, подтвердив его сборку:

      Вы получите следующий результат:

      Output

      app.exe: PE32+ executable (console) x86-64 (stripped to external PDB), for MS Windows

      Также вы можете задать значение для одной или обеих переменных среды во время сборки. Запустите следующую команду:

      • GOOS=linux GOARCH=ppc64 go build

      Теперь ваш исполняемый файл app будет заменен файлом для другой архитектуры. Запустите команду file для этого бинарного файла:

      Вывод будет выглядеть следующим образом:

      app: ELF 64-bit MSB executable, 64-bit PowerPC or cisco 7500, version 1 (SYSV), statically linked, not stripped
      

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

      Использование суффиксов файлов имен GOOS и GOARCH

      Как вы уже видели ранее, в стандартной библиотеке Go активно используются маркеры сборки для упрощения кода, что позволяет разделять реализации для разных платформ в разных файлах. Когда вы открывали файл os/path_unix.go, там был маркер сборки, который содержал список всех возможных комбинаций, рассматриваемых в качестве Unix-платформ. Однако в файле os/path_windows.go отсутствуют маркеры для сборки, поскольку суффикса в имени файла достаточно, чтобы Go мог понять, для какой платформы предназначен этот файл.

      Давайте рассмотрим синтаксис этого элемента. При присвоении имени файла .go вы можете добавить GOOS и GOARCH в качестве суффиксов к имени файла в этом порядке, отделяя значения нижним подчеркиванием (_). Если у вас есть файл Go с именем filename.go, вы можете указать ОС и архитектуру, изменив имя файла на filename_GOOS_GOARCH.go. Например, если вы хотите скомпилировать его для Windows с 64-битной архитектурой ARM, вы должны использовать имя файла filename_windows_arm64.go. Такое соглашение о наименованиях помогает поддерживать код в организованном виде.

      Обновите вашу программу для использования суффиксов имени файла вместо маркеров сборки. Во-первых, переименуйте файл path.go и windows.go для использования соглашения, используемого в пакете os:

      • mv path.go path_unix.go
      • mv windows.go path_windows.go

      После изменения имен двух файлов вы можете удалить маркер сборки, который вы добавили в path_windows.go:

      Удалите // +build windows, чтобы ваш файл выглядел следующим образом:

      path_windows.go

      package main
      
      const PathSeparator = "\"
      

      Сохраните и закройте файл.

      Поскольку unix — это недействительное значение для GOOS, суффикс _unix.go не будет иметь значение для компилятора Go. Однако он передает предполагаемое назначение файла. Как и в файле os/path_unix.go, ваш файл path_unix.go все еще требует использования маркеров для сборки, поэтому этот файл будет сохранен без изменений.

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

      Заключение

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

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



      Source link