Thứ ba, 30/05/2017 | 00:00 GMT+7

Cách tạo Go Executables cho nhiều nền tảng trên Ubuntu 16.04

Ngôn ngữ lập trình Go đi kèm với một chuỗi công cụ phong phú giúp việc lấy các gói và xây dựng các file thực thi trở nên vô cùng dễ dàng. Một trong những tính năng mạnh mẽ nhất của Go là khả năng xây dựng chéo các file thi hành cho bất kỳ nền tảng nước ngoài nào được Go hỗ trợ. Điều này làm cho việc kiểm tra và phân phối gói dễ dàng hơn nhiều, vì bạn không cần phải có quyền truy cập vào một nền tảng cụ thể để phân phối gói của bạn cho nó.

Trong hướng dẫn này, bạn sẽ sử dụng các công cụ của Go để lấy một gói từ kiểm soát version và tự động cài đặt file thực thi của nó. Sau đó, bạn sẽ xây dựng và cài đặt file thi hành theo cách thủ công để bạn có thể làm quen với quy trình. Sau đó, bạn sẽ xây dựng một file thực thi cho một kiến trúc khác và tự động hóa quá trình xây dựng để tạo file thực thi cho nhiều nền tảng. Khi hoàn tất, bạn sẽ biết cách xây dựng file thực thi cho Windows và macOS, cũng như các nền tảng khác mà bạn muốn hỗ trợ.

Yêu cầu

Để làm theo hướng dẫn này, bạn cần :

Bước 1 - Cài đặt chương trình Go từ Kiểm soát version

Trước khi có thể tạo file thực thi từ gói Go, ta phải lấy mã nguồn của nó. Công cụ go get có thể tìm nạp các gói từ hệ thống kiểm soát version như GitHub. Dưới mui xe, go get các gói sao chép vào folder con của folder $GOPATH/src/ . Sau đó, nếu có thể, nó sẽ cài đặt gói bằng cách xây dựng file thực thi của nó và đặt nó trong folder $GOPATH/bin . Nếu bạn đã cấu hình Go như được mô tả trong hướng dẫn yêu cầu , thì folder $GOPATH/bin có trong biến môi trường $PATH của bạn, điều này đảm bảo bạn có thể sử dụng các gói đã cài đặt từ bất kỳ đâu trên hệ thống của bạn .

Cú pháp của lệnh go getgo get package-import-path . package-import-path là một chuỗi xác định một gói duy nhất. Đó thường là vị trí của gói trong một repository từ xa như Github hoặc một folder trong folder $GOPATH/src/ trên máy của bạn.

Người ta thường sử dụng cờ go get với cờ -u , nó hướng dẫn go get để lấy gói và các phần phụ thuộc của nó hoặc cập nhật các phần phụ thuộc đó nếu chúng đã có trên máy.

Trong hướng dẫn này, ta sẽ cài đặt Caddy , một web server được viết bằng Go. Theo hướng dẫn của Caddy x, ta sẽ sử dụng github.com/mholt/caddy/caddy cho đường dẫn nhập gói. Sử dụng go get để tìm nạp và cài đặt Caddy:

  • go get -u github.com/mholt/caddy/caddy

Lệnh sẽ mất một chút thời gian để hoàn thành, nhưng bạn sẽ không thấy bất kỳ tiến trình nào trong khi nó tìm nạp gói và cài đặt. Không có kết quả nào thực sự cho biết lệnh được thực thi thành công.

Khi lệnh hoàn tất, bạn sẽ thấy mã nguồn của Caddy có sẵn tại $GOPATH/src/github.com/mholt/caddy . Ngoài ra, vì Caddy có file thực thi, nó được tạo tự động và lưu trữ trong folder $GOPATH/bin . Xác minh điều này bằng cách sử dụng which để in các vị trí của thực thi:

  • which caddy

Bạn sẽ thấy kết quả sau:

Output
/home/sammy/work/bin/caddy

Lưu ý : Lệnh go get cài đặt các gói từ nhánh mặc định của repository Git, trong nhiều trường hợp là nhánh master hoặc nhánh đang phát triển. Đảm bảo xem lại các hướng dẫn, thường nằm trong file README của repository , trước khi sử dụng go get .

Bạn có thể sử dụng các lệnh Git như git checkout để chọn một nhánh khác trên các nguồn thu được bằng lệnh go get . Xem lại hướng dẫn Cách sử dụng Git Nhánh để tìm hiểu thêm về cách chuyển đổi các nhánh.

Ta hãy xem xét quá trình cài đặt gói Go một cách chi tiết hơn, bắt đầu bằng việc tạo file thực thi từ các gói mà ta đã có được.

Bước 2 - Xây dựng một thực thi

Lệnh go get download nguồn và cài đặt file thực thi của Caddy cho ta trong một lệnh duy nhất. Nhưng bạn có thể cần tự mình xây dựng lại file thực thi hoặc xây dựng file thực thi từ mã của bạn . Lệnh go build dựng các file thực thi.

Mặc dù ta đã cài đặt Caddy, hãy xây dựng lại nó theo cách thủ công để ta có thể thoải mái với quá trình này. Thực thi go build và chỉ định đường dẫn nhập gói:

  • go build github.com/mholt/caddy/caddy

Như trước đây, không có kết quả nào cho biết hoạt động thành công. Tệp thực thi sẽ được tạo trong folder hiện tại của bạn, có cùng tên với folder chứa gói. Trong trường hợp này, file thực thi sẽ được đặt tên là caddy .

Nếu bạn đang ở trong folder gói, bạn có thể bỏ qua đường dẫn đến gói và chỉ cần chạy go build .

Để chỉ định một tên hoặc vị trí khác cho file thực thi, hãy sử dụng cờ -o . Hãy xây dựng một file thực thi có tên là caddy-server và đặt nó vào folder build trong folder làm việc hiện tại:

  • go build -o build/caddy-server github.com/mholt/caddy/caddy

Lệnh này tạo file thực thi và cũng tạo folder ./build nếu nó không tồn tại.

Bây giờ ta hãy xem xét cài đặt các file thực thi.

Bước 3 - Cài đặt một Executable

Xây dựng file thực thi sẽ tạo file thực thi trong folder hiện tại hoặc folder bạn chọn. Cài đặt file thực thi là quá trình tạo file thực thi và lưu trữ file đó trong $GOPATH/bin . Lệnh go install hoạt động giống như go build , nhưng go install sẽ quan tâm đến việc đặt file kết quả vào đúng nơi dành cho bạn.

Để cài đặt file thực thi, go install sử dụng go install , theo sau là đường dẫn nhập gói. , hãy sử dụng Caddy để thử điều này:

  • go install github.com/mholt/caddy/caddy

Giống như với go build , bạn sẽ không thấy kết quả nếu lệnh thành công. Và giống như trước đây, file thực thi được tạo cùng tên với folder chứa gói. Nhưng lần này, file thực thi được lưu trữ trong $GOPATH/bin . Nếu $GOPATH/bin là một phần của biến môi trường $PATH của bạn, thì file thực thi sẽ có sẵn từ mọi nơi trên hệ điều hành của bạn. Bạn có thể xác minh vị trí của nó sử dụng which lệnh:

  • which caddy

Bạn sẽ thấy kết quả sau:

Output of which
/home/sammy/work/bin/caddy

Đến đây bạn đã hiểu cách go get , go buildgo install công việc và chúng liên quan như thế nào, hãy cùng khám phá một trong những tính năng phổ biến nhất của Go: tạo file thực thi cho các nền tảng mục tiêu khác.

Bước 4 - Xây dựng các thực thi cho các kiến trúc khác nhau

Lệnh go build cho phép bạn tạo một file thực thi cho bất kỳ nền tảng đích nào được Go hỗ trợ, trên nền tảng của bạn . Điều này nghĩa là bạn có thể kiểm tra, phát hành và phân phối ứng dụng của bạn mà không cần xây dựng các file thực thi đó trên nền tảng mục tiêu mà bạn muốn sử dụng.

Việc biên dịch chéo hoạt động bằng cách cài đặt các biến môi trường bắt buộc chỉ định hệ điều hành và kiến trúc đích. Ta sử dụng GOOS biến cho hệ điều hành đích và GOARCH cho kiến trúc đích. Để xây dựng một file thực thi, lệnh sẽ có dạng sau:

  • env GOOS=target-OS GOARCH=target-architecture go build package-import-path

Lệnh env chạy chương trình trong môi trường đã sửa đổi. Điều này cho phép bạn sử dụng các biến môi trường chỉ để thực hiện lệnh hiện tại. Các biến không được đặt hoặc đặt lại sau khi lệnh thực thi.

Bảng sau đây cho thấy các kết hợp có thể có của GOOSGOARCH mà bạn có thể sử dụng:

GOOS - Hệ điều hành đích GOARCH - Nền tảng mục tiêu
android arm
darwin 386
darwin amd64
darwin arm
darwin arm64
dragonfly amd64
freebsd 386
freebsd amd64
freebsd arm
linux 386
linux amd64
linux arm
linux arm64
linux ppc64
linux ppc64le
linux mips
linux mipsle
linux mips64
linux mips64le
netbsd 386
netbsd amd64
netbsd arm
openbsd 386
openbsd amd64
openbsd arm
plan9 386
plan9 amd64
solaris amd64
windows 386
windows amd64

Cảnh báo: Các file thực thi biên dịch chéo dành cho Android yêu cầu Android NDK và một số cài đặt bổ sung nằm ngoài phạm vi của hướng dẫn này.

Sử dụng các giá trị trong bảng, ta có thể tạo Caddy cho Windows 64-bit như sau:

  • env GOOS=windows GOARCH=amd64 go build github.com/mholt/caddy/caddy

, không có kết quả nào cho biết hoạt động đã thành công. Tệp thực thi sẽ được tạo trong folder hiện tại, sử dụng tên gói làm tên của nó. Tuy nhiên, vì ta đã xây dựng file thực thi này cho Windows nên tên kết thúc bằng hậu tố .exe .

Bạn nên có file caddy.exe trong folder hiện tại của bạn , bạn có thể xác minh file này bằng ls .

  • ls caddy.exe

Bạn sẽ thấy file caddy.exe được liệt kê trong kết quả :

Output
caddy.exe

Lưu ý : Bạn có thể sử dụng cờ -o để đổi tên file thực thi hoặc đặt file đó ở một vị trí khác. Tuy nhiên, khi xây dựng file thực thi cho Windows và cung cấp một tên khác, hãy đảm bảo chỉ định rõ ràng hậu tố .exe khi đặt tên file thực thi.

Hãy xem kịch bản của quá trình này để giúp phát hành phần mềm cho nhiều môi trường mục tiêu dễ dàng hơn.

Bước 5 - Tạo tập lệnh để tự động biên dịch chéo

Quá trình tạo file thực thi cho nhiều nền tảng có thể hơi tẻ nhạt, nhưng ta có thể tạo một tập lệnh để làm mọi thứ dễ dàng hơn.

Tập lệnh sẽ lấy đường dẫn nhập gói làm đối số, lặp qua danh sách các cặp nền tảng và hệ điều hành được định nghĩa , đồng thời tạo file thực thi cho mỗi cặp, đặt kết quả vào folder hiện tại. Mỗi file thực thi sẽ được đặt tên với tên gói, theo sau là nền tảng và kiến trúc đích, ở dạng package-OS-architecture . Đây sẽ là một script phổ quát mà bạn có thể sử dụng cho bất kỳ dự án nào.

Chuyển sang folder chính của bạn và tạo một file mới có tên go-executable-build.bash trong editor của bạn:

  • cd ~
  • nano go-executable-build.bash

Ta sẽ bắt đầu kịch bản của bạn bằng một dòng shebang . Dòng này xác định trình thông dịch nào sẽ phân tích cú pháp tập lệnh này khi nó chạy dưới dạng file thực thi. Thêm dòng sau để chỉ định rằng bash sẽ thực thi tập lệnh này:

go-executable-build.bash
#!/usr/bin/env bash 

Ta muốn lấy đường dẫn nhập gói làm đối số dòng lệnh. Để làm điều này, ta sẽ sử dụng biến $ n , với n là một số không âm. Biến $0 chứa tên của tập lệnh bạn đã thực thi, trong khi $1 sẽ chứa các đối số do user cung cấp. Thêm dòng này vào tập lệnh, sẽ lấy đối số đầu tiên từ dòng lệnh và lưu trữ nó trong một biến được gọi là package :

go-executable-build.bash
... package=$1  

Tiếp theo, hãy đảm bảo user đã cung cấp giá trị này. Nếu giá trị không được cung cấp, hãy thoát khỏi tập lệnh với thông báo giải thích cách sử dụng tập lệnh:

go-executable-build.bash
...  if [[ -z "$package" ]]; then   echo "usage: $0 <package-name>"   exit 1 fi 

Câu lệnh if này kiểm tra giá trị của biến $package . Nếu nó không được đặt, ta sử dụng echo để in cách sử dụng chính xác và sau đó kết thúc tập lệnh bằng cách sử dụng exit . exit lấy giá trị trả về làm đối số, giá trị này phải là 0 cho các thực thi thành công và bất kỳ giá trị nào khác 0 cho các thực thi không thành công. Ta sử dụng 1 ở đây vì tập lệnh không thành công.

Lưu ý : Nếu bạn muốn làm cho tập lệnh này hoạt động với một gói được định nghĩa , hãy thay đổi biến package để trỏ đến đường dẫn nhập đó:

go-executable-build.bash
... package="github.com/user/hello" 

Tiếp theo, ta muốn extract tên gói từ đường dẫn. Đường dẫn nhập gói được phân cách bằng / ký tự, với tên gói nằm ở cuối đường dẫn. Đầu tiên, ta sẽ chia đường dẫn nhập gói thành một mảng, sử dụng / làm dấu phân cách:

go-executable-build.bash
package_split=(${package//\// }) 

Tên gói phải là phần tử cuối cùng của mảng $package_split mới này. Trong Bash, bạn có thể sử dụng index mảng âm để truy cập một mảng từ cuối thay vì đầu. Thêm dòng này để lấy tên gói từ mảng và lưu trữ nó trong một biến có tên là package_name :

go-executable-build.bash
... package_name=${package_split[-1]} 

Bây giờ, bạn cần phải quyết định nền tảng và kiến trúc nào bạn muốn xây dựng các file thực thi. Trong hướng dẫn này, ta sẽ xây dựng các file thực thi cho Windows 64-bit, Windows 32-bit và 64-bit macOS. Ta sẽ đặt các mục tiêu này trong một mảng có định dạng OS / Platform , vì vậy ta có thể chia từng cặp thành các biến GOOSGOARCH bằng cách sử dụng cùng một phương pháp ta đã sử dụng để extract tên gói từ đường dẫn. Thêm các nền tảng vào tập lệnh:

go-executable-build.bash
... platforms=("windows/amd64" "windows/386" "darwin/amd64") 

Tiếp theo, ta sẽ lặp qua mảng nền tảng, chia từng mục nhập nền tảng thành các giá trị cho các biến môi trường GOOSGOARCH và sử dụng chúng để xây dựng file thực thi. Ta có thể làm điều đó với vòng lặp for sau:

go-executable-build.bash
... for platform in "${platforms[@]}" do     ... done 

Biến platform sẽ chứa một mục nhập từ mảng platforms trong mỗi lần lặp. Ta cần chia platform thành hai biến - GOOSGOARCH . Thêm các dòng sau vào vòng lặp for :

go-executable-build.bash
for platform in "${platforms[@]}" do     platform_split=(${platform//\// })     GOOS=${platform_split[0]}     GOARCH=${platform_split[1]}  done 

Tiếp theo, ta sẽ tạo tên file thực thi bằng cách kết hợp tên gói với hệ điều hành và kiến trúc. Khi ta đang xây dựng cho Windows, ta cũng cần thêm hậu tố .exe vào tên file . Thêm mã này vào vòng lặp for :

go-executable-build.bash
for platform in "${platforms[@]}" do     platform_split=(${platform//\// })     GOOS=${platform_split[0]}     GOARCH=${platform_split[1]}      output_name=$package_name'-'$GOOS'-'$GOARCH      if [ $GOOS = "windows" ]; then         output_name+='.exe'     fi done  

Với các biến được đặt, ta sử dụng go build để tạo file thực thi. Thêm dòng này vào phần nội dung của vòng lặp for , ngay phía trên từ khóa done :

go-executable-build.bash
...     if [ $GOOS = "windows" ]; then         output_name+='.exe'     fi      env GOOS=$GOOS GOARCH=$GOARCH go build -o $output_name $package  done 

Cuối cùng, ta nên kiểm tra xem có lỗi khi xây dựng file thực thi hay không. Ví dụ: ta có thể gặp lỗi nếu ta cố gắng tạo một gói mà ta không có nguồn. Ta có thể kiểm tra mã trả về của lệnh go build để tìm giá trị khác 0. Biến $? chứa mã trả về từ việc thực hiện lệnh trước đó. Nếu go build trả về bất kỳ thứ gì khác 0 , thì đã xảy ra sự cố và ta sẽ muốn thoát tập lệnh. Thêm mã này vào vòng lặp for , sau lệnh go build và bên trên từ khóa done .

go-executable-build.bash
 ...      env GOOS=$GOOS GOARCH=$GOARCH go build -o $output_name $package      if [ $? -ne 0 ]; then         echo 'An error has occurred! Aborting the script execution...'         exit 1     fi 

Như vậy, bây giờ ta có một tập lệnh sẽ xây dựng nhiều file thực thi từ gói Go của ta . Đây là tập lệnh đã hoàn thành:

go-executable-build.bash
 #!/usr/bin/env bash  package=$1 if [[ -z "$package" ]]; then   echo "usage: $0 <package-name>"   exit 1 fi package_split=(${package//\// }) package_name=${package_split[-1]}  platforms=("windows/amd64" "windows/386" "darwin/amd64")  for platform in "${platforms[@]}" do     platform_split=(${platform//\// })     GOOS=${platform_split[0]}     GOARCH=${platform_split[1]}     output_name=$package_name'-'$GOOS'-'$GOARCH     if [ $GOOS = "windows" ]; then         output_name+='.exe'     fi        env GOOS=$GOOS GOARCH=$GOARCH go build -o $output_name $package     if [ $? -ne 0 ]; then         echo 'An error has occurred! Aborting the script execution...'         exit 1     fi done 

Xác minh file của bạn trùng với mã trước đó. Sau đó, lưu file và thoát khỏi editor .

Trước khi có thể sử dụng tập lệnh, ta phải làm cho nó có thể thực thi được bằng chmod :

  • chmod +x go-executable-build.bash

Cuối cùng, kiểm tra tập lệnh bằng cách xây dựng các file thực thi cho Caddy:

  • ./go-executable-build.bash github.com/mholt/caddy/caddy

Nếu mọi thứ suôn sẻ, bạn sẽ có các file thực thi trong folder hiện tại của bạn . Không có kết quả cho biết thực thi tập lệnh thành công. Bạn có thể xác minh là các file thực thi được tạo bằng ls :

  • ls caddy*

Bạn sẽ thấy cả ba version :

Example ls output
caddy-darwin-amd64 caddy-windows-386.exe caddy-windows-amd64.exe

Để thay đổi nền tảng mục tiêu, chỉ cần thay đổi biến platforms trong tập lệnh của bạn.

Kết luận

Trong hướng dẫn này, bạn đã học cách sử dụng công cụ của Go để lấy các gói từ hệ thống kiểm soát version , cũng như xây dựng và biên dịch chéo các file thực thi cho các nền tảng khác nhau.

Bạn cũng đã tạo một tập lệnh mà bạn có thể sử dụng để biên dịch chéo một gói duy nhất cho nhiều nền tảng.

Để đảm bảo ứng dụng của bạn hoạt động chính xác, bạn có thể xem thử nghiệmtích hợp liên tục như Travis-CIAppVeyor để thử nghiệm trên Windows.

Nếu bạn quan tâm đến Caddy và cách sử dụng nó, hãy xem Cách lưu trữ trang web bằng Caddy trên Ubuntu 16.04 .


Tags:

Các tin liên quan

Cách cài đặt Concourse CI trên Ubuntu 16.04
2017-05-26
Cách giám sát server và dịch vụ bằng Icinga trên Ubuntu 16.04
2017-05-05
Cách thiết lập đồng bộ hóa thời gian trên Ubuntu 16.04
2017-04-28
Cách quản lý log với Graylog 2 trên Ubuntu 16.04
2017-04-25
Cách cài đặt Webmin trên Ubuntu 16.04
2017-04-21
Cách cài đặt và cấu hình OrientDB trên Ubuntu 16.04
2017-03-24
how-to-monitor-system-metrics-with-the-tick-stack-on-ubuntu-16-04
2017-03-16
Cách cài đặt và cấu hình Postfix trên Ubuntu 16.04
2017-03-16
Cách cấu hình client FreeIPA trên Ubuntu 16.04
2017-03-08
Cách cài đặt Moodle trên Ubuntu 16.04
2017-03-02