go run מקמפלת ומריצה תוכנית בצעד אחד ולא שומרת קובץ בינארי. go build מקמפלת ומשאירה קובץ הרצה בתיקייה הנוכחית. go install מקמפלת ושמה את קובץ ההרצה ב-$HOME/go/bin. שלושתן מקמפלות לקוד מכונה נייטיב; אף אחת מהן לא מפרשת (interprets) כלום.
| פקודה | מקמפלת | מריצה | שומרת קובץ בינארי | לאן הקובץ הבינארי הולך |
|---|---|---|---|---|
go run . | כן | כן | לא | תיקייה זמנית, שנמחקת אחר כך |
go build | כן | לא | כן | התיקייה הנוכחית (או -o path) |
go install | כן | לא | כן | $GOBIN, אחרת $GOPATH/bin |
התוכנית הבאה היא זו שמשמשת בדוגמאות בדף הזה. העורך מריץ אותה כמו ש-go run הייתה מריצה.
go run
go run מקבלת חבילה (בדרך כלל ., התיקייה הנוכחית) או רשימה של קובצי .go:
go run .
go run main.go
go run ./cmd/server
כל מה שבא אחרי החבילה מועבר לתוכנית שלכם כארגומנטים:
go run . --port 8080 verbose
version: dev
built for: linux/amd64
args: [--port 8080 verbose]
העדיפו go run . על פני go run main.go. ציון שם קובץ מקמפל רק את הקובץ הזה, כך שברגע שיש בחבילה קובץ שני, פונקציות שמוגדרות בו מדווחות כ-undefined. הדף על חבילות ו-imports מסביר את השגיאה הזאת בפירוט.
go run יכולה גם להריץ תוכנית מרוחקת בגרסה מסוימת בלי להתקין אותה, מה שנוח למחוללי קוד:
go run golang.org/x/tools/cmd/stringer@v0.30.0 -type=Color
go build
go build מקמפלת את החבילה בתיקייה הנוכחית. עבור חבילת main היא כותבת קובץ הרצה; עבור חבילת ספרייה היא מקמפלת, מדווחת על שגיאות וזורקת את התוצאה.
go mod init example.com/hello
go build
ls
go.mod hello main.go
השם של הקובץ הבינארי נקבע לפי הכללים האלה:
| מה מריצים | שם הקובץ הבינארי |
|---|---|
go build במודול example.com/hello | hello |
go build ./cmd/server | server |
go build main.go | main (לפי שם הקובץ הראשון) |
go build -o bin/app . | bin/app |
כל אחד מהנ"ל עם GOOS=windows | אותו שם ועוד .exe |
עוד שתי צורות שתשתמשו בהן כל הזמן:
go build ./... # compile every package in the module
go vet ./... # static checks, covered below
./... פירושו "התיקייה הזאת וכל תיקייה שמתחתיה". go build ./... עם כמה חבילות main לא כותבת שום קובץ בינארי; זו בדיקה מהירה של "האם הכול מתקמפל".
דגלי בנייה שימושיים
| דגל | מה הוא עושה |
|---|---|
-o name | קובץ או תיקיית פלט |
-v | הדפסת שמות החבילות בזמן שהן מתקמפלות |
-race | בנייה עם ה-race detector (איטי יותר, קובץ גדול יותר; לבדיקות) |
-trimpath | הסרת נתיבים של מערכת הקבצים המקומית מהקובץ הבינארי, לבניות שניתנות לשחזור |
-ldflags "-s -w" | הסרת טבלת הסמלים ומידע הדיבוג DWARF, כך שהקובץ הבינארי קטן יותר |
-ldflags "-X main.version=1.4.0" | הצבת ערך למשתנה מחרוזת בזמן הקישור (link) |
-tags name | הכללת קבצים שמוגנים באילוץ //go:build name |
הדגל -X הוא הדרך שבה רוב פרויקטי Go מטביעים גרסה בקובץ הבינארי. הוא עובד רק על משתני string ברמת החבילה (לא על קבועים):
go build -o app -ldflags "-X main.version=1.4.0" .
./app
version: 1.4.0
built for: linux/amd64
args: []
הקובץ הבינארי גם רושם את גרסאות המודולים שלו, והחל מ-Go 1.18 גם את ה-commit ב-VCS שממנו הוא נבנה. go version -m ./app מדפיסה אותם, ותוכנית יכולה לקרוא אותם עם runtime/debug.ReadBuildInfo.
go install
go install בונה בדיוק כמו go build ואז מעבירה את קובץ ההרצה ל-$GOBIN, או ל-$GOPATH/bin כש-GOBIN לא מוגדר (כברירת מחדל $HOME/go/bin):
go install .
go env GOPATH
השימוש הנפוץ ביותר בה הוא התקנת כלים שכתובים ב-Go. עם @version היא מתקינה תוכנית בלי לגעת ב-go.mod שלכם:
go install golang.org/x/tools/gopls@latest
go install honnef.co/go/tools/cmd/staticcheck@latest
אם ה-shell לא מוצא כלי אחרי ההתקנה, $HOME/go/bin לא נמצא ב-PATH שלכם. הוסיפו export PATH=$PATH:$(go env GOPATH)/bin לפרופיל של ה-shell.
Cross-compile עם GOOS ו-GOARCH
Go יכולה לבנות למערכת הפעלה או למעבד אחרים מכל מכונה, בלי toolchain נוסף. הגדירו שני משתני סביבה עבור פקודת הבנייה:
GOOS=linux GOARCH=amd64 go build -o app-linux-amd64 .
GOOS=linux GOARCH=arm64 go build -o app-linux-arm64 .
GOOS=darwin GOARCH=arm64 go build -o app-macos .
GOOS=windows GOARCH=amd64 go build -o app.exe .
ב-PowerShell מגדירים משתני סביבה אחרת:
$env:GOOS = "linux"; $env:GOARCH = "amd64"; go build -o app-linux .
הצמדים הנפוצים ביותר:
| GOOS | GOARCH | יעד |
|---|---|---|
linux | amd64 | רוב השרתים והקונטיינרים |
linux | arm64 | AWS Graviton, Raspberry Pi עם מערכת הפעלה של 64 סיביות |
darwin | arm64 | מחשבי Mac עם Apple Silicon |
darwin | amd64 | מחשבי Mac עם Intel |
windows | amd64 | Windows של 64 סיביות |
js | wasm | WebAssembly בדפדפן |
go tool dist list מדפיסה כל צמד נתמך (48 ב-Go 1.24).
cross-compile קל כל כך רק לקוד Go טהור. חבילה שמשתמשת ב-cgo (קוד C, למשל חלק מהדרייברים של SQLite) צריכה cross-compiler של C עבור היעד. בזמן cross-compile, cgo מושבת כברירת מחדל, והגדרה מפורשת של CGO_ENABLED=0 נותנת גם קובץ בינארי סטטי לחלוטין ל-Linux, וזה מה שרוצים ב-image מינימלי של קונטיינר מסוג scratch או distroless.
go fmt ו-go vet
שתי בדיקות שייכות לכל תהליך עבודה, וגם ל-CI.
go fmt משכתבת קבצים בסגנון הרשמי האחד: טאבים, שדות מיושרים, ריווח אחיד. היא עוטפת את gofmt -l -w:
go fmt ./...
gofmt -l . # list files that are not formatted; empty output means clean
go vet מדווחת על קוד שמתקמפל אבל כמעט בוודאות שגוי. אי התאמה בפורמט של Printf היא המקרה הקלאסי:
package main
import "fmt"
func main() {
count := 3
fmt.Printf("%s items\n", count)
}
go vet ./...
# example.com/hello
# [example.com/hello]
./main.go:7:2: fmt.Printf format %s has arg count of wrong type int
התוכנית מתקמפלת ומדפיסה %!s(int=3) items, וזה בדיוק סוג הבאג ש-vet קיימת כדי לתפוס. בדיקות vet נוספות כוללות העתקה של sync.Mutex לפי ערך, קוד שאי אפשר להגיע אליו, struct tags עם תחביר שגוי, ו-context.CancelFunc שאף פעם לא נקראת. go test מריצה אוטומטית חלק מהבדיקות האלה; go build לא מריצה אף אחת.
פקודות go נוספות
| פקודה | מטרה |
|---|---|
go test ./... | הרצת בדיקות |
go mod tidy | הוספת תלויות חסרות והסרת תלויות שלא בשימוש |
go get pkg@version | הוספה או שינוי של תלות |
go clean -cache | ריקון ה-build cache |
go env | הדפסת הקונפיגורציה של Go |
go doc fmt.Println | הצגת תיעוד בטרמינל |
go list -m all | רשימת כל המודולים בבנייה |
למה בניות מהירות בפעם השנייה
Go שומרת חבילות מקומפלות ב-build cache (go env GOCACHE). בנייה מחדש מקמפלת רק חבילות שקוד המקור או התלויות שלהן השתנו, כך ש-go run . על פרויקט שלא השתנה עולה כמעט מיד. אם בנייה אי פעם מתנהגת מוזר אחרי החלפת גרסת Go או משתני סביבה, go clean -cache מנקה אותו; לעיתים רחוקות תצטרכו את זה.
שאלות נפוצות
מה ההבדל בין go run ל-go build?
go run מקמפלת את התוכנית לתיקייה זמנית, מריצה אותה וזורקת את הקובץ הבינארי. go build מקמפלת את התוכנית וכותבת את הקובץ הבינארי לתיקייה הנוכחית, כך שאפשר להריץ אותו שוב או להעתיק אותו למקום אחר. השתמשו ב-go run בזמן הפיתוח וב-go build כשאתם צריכים את קובץ ההרצה.
איך קובעים את שם קובץ הפלט של go build?
השתמשו ב--o: go build -o myapp . כותבת את myapp (ב-Windows, כתבו בעצמכם -o myapp.exe). בלי -o, go build קוראת לקובץ הבינארי לפי האלמנט האחרון בנתיב ה-import של החבילה, או לפי הקובץ הראשון כשמעבירים קובצי .go, ומוסיפה .exe כשבונים עבור Windows.
איך מבצעים cross-compile של Go ל-Linux או ל-Windows?
הגדירו את GOOS ואת GOARCH עבור הבנייה: GOOS=linux GOARCH=amd64 go build -o app-linux . או GOOS=windows GOARCH=amd64 go build . (שמייצרת .exe). לקוד Go טהור לא צריך שום toolchain נוסף. go tool dist list מדפיסה כל צמד נתמך.
לאן go install שמה את הקובץ הבינארי?
ב-$GOBIN אם הוא מוגדר, אחרת ב-$GOPATH/bin, שהוא $HOME/go/bin כברירת מחדל. הוסיפו את התיקייה הזאת ל-PATH שלכם כדי להריץ כלים מותקנים לפי השם שלהם. go install example.com/tool@latest מתקינה כלי בלי להוסיף אותו למודול שלכם.
האם go build מריצה את go vet?
לא. go build רק מקמפלת. go test מריצה אוטומטית חלק מהבדיקות של go vet, אבל כדי לקבל את כולן הריצו go vet ./... בעצמכם, בדרך כלל ב-CI לצד gofmt -l ..