Skip to content

سطر الأوامر

أوامر Go تحتوي على مجموعة كاملة من سلسلة الأدوات، هذه الأوامر تغطي التوثيق، التنسيق، فحص الكود، التجميع، الاختبار، إدارة التبعيات وغيرها من الجوانب، يمكن القول أنها تتعلق بجميع جوانب تطوير Go.

text
bug         الإبلاغ عن الأخطاء
build       تجميع الحزم والتبعيات
clean       مسح الملفات الكائنة
doc         عرض التوثيق في الكود المصدري
env         عرض معلومات متغيرات بيئة Go
fix         إصلاح مشاكل التوافق مع API الناتجة عن تغييرات إصدار go
fmt         تنسيق الكود المصدري
generate    توليد الكود
get         إضافة التبعيات
install     تثبيت وتجميع الحزم
list        أمر قائمة الحزم/الوحدات
mod         أمر صيانة الوحدات
work        أمر صيانة مساحة العمل
run         تجميع وتشغيل
test        اختبار
tool        تشغيل أداة go المحددة
version     عرض معلومات إصدار go
vet         فحص وإخراج المشاكل المحتملة في الكود المصدري

هذه المقالة هي مجرد وصف و introduction بسيط لاستخدامها، جميع المحتويات مأخوذة من الوثائق الرسمية، لمزيد من التفاصيل يمكنك زيارة cmd/go.

help

أول أمر يجب معرفته هو أمر help، من خلاله يمكنك قراءة كيفية استخدام الأوامر. هناك طريقتان للاستخدام، إذا كنت تريد الحصول على معلومات استخدام مختصرة، يمكنك إضافة علامة -h بعد الأمر المحدد، مثلاً

sh
$ go env -h
usage: go env [-json] [-u] [-w] [var ...]
Run 'go help env' for details.

سيعرض go باختصار طريقة استخدام هذا الأمر، كما أنه يلمح إلى أنه للحصول على معلومات أكثر تفصيلاً تحتاج إلى استخدام أمر help

sh
$ go help env
usage: go env [-json] [-u] [-w] [var ...]

Env prints Go environment information.

By default env prints information as a shell script
(on Windows, a batch file). If one or more variable
names is given as arguments, env prints the value of
each named variable on its own line.

The -json flag prints the environment in JSON format
instead of as a shell script.

The -u flag requires one or more arguments and unsets
the default setting for the named environment variables,
if one has been set with 'go env -w'.

The -w flag requires one or more arguments of the
form NAME=VALUE and changes the default settings
of the named environment variables to the given values.

For more about environment variables, see 'go help environment'.

استخدم أمر help بشكل جيد، من خلاله يمكنك الحصول على الكثير من المعلومات حول الأوامر.

doc

sh
$ go doc -h
Usage of [go] doc:
        go doc
        go doc <pkg>
        go doc <sym>[.<methodOrField>]
        go doc [<pkg>.]<sym>[.<methodOrField>]
        go doc [<pkg>.][<sym>.]<methodOrField>
        go doc <pkg> <sym>[.<methodOrField>]
For more information run
        go help doc

Flags:
  -C dir
        change to dir before running command
  -all
        show all documentation for package
  -c    symbol matching honors case (paths not affected)
  -cmd
        show symbols with package docs even if package is a command
  -short
        one-line representation for each symbol
  -src
        show source code for symbol
  -u    show unexported symbols as well as exported

أمر doc سيخرج تعليقات التوثيق للحزمة، الثابت، الدالة، النوع، المتغير، الطريقة وحتى حقول الهيكل المحددة. بدون أي معاملات، سيخرج تعليقات الحزمة الحالية

sh
$ go doc

يمكنك أيضاً تحديد حزمة معينة للعرض، مثل عرض تعليقات توثيق حزمة runtime

sh
$ go doc runtime
package runtime // import "runtime"

Package runtime contains operations that interact with Go's runtime system,
such as functions to control goroutines. It also includes the low-level type
information used by the reflect package; see reflect's documentation for the
programmable interface to the run-time type system.
......

أو نوع معين

sh
$ go doc unsafe.Pointer
package unsafe // import "unsafe"

type Pointer *ArbitraryType
    Pointer represents a pointer to an arbitrary type. There are four special
    operations available for type Pointer that are not available for other
    types:
      - A pointer value of any type can be converted to a Pointer.
      - A Pointer can be converted to a pointer value of any type.
      - A uintptr can be converted to a Pointer.
      - A Pointer can be converted to a uintptr.
      ...

أو دالة معينة

sh
$ go doc runtime.GC
package runtime // import "runtime"

func GC()
    GC runs a garbage collection and blocks the caller until the garbage
    collection is complete. It may also block the entire program.

له العلامات التالية شائعة الاستخدام

  • -u: عرض الأنواع الخاصة
  • -all: عرض كل توثيق الحزمة المحددة
  • -short: سطر واحد فقط من الوصف المختصر
  • -src: إخراج الكود المصدري
  • -cmd: للحزم التي تنتمي إلى أوامر go، إخراج توثيق الكود داخل الحزمة أيضاً.

مثل عرض متغير runtime.inf، هذا متغير غير مكشوف للخارج

sh
$ go doc -u runtime.inf
package runtime // import "runtime"

var inf = float64frombits(0x7FF0000000000000)

استخدام أمر doc بشكل جيد يمكن أن يساعدك في قراءة التوثيق بشكل أكثر راحة.

طريقة أخرى لقراءة توثيق الأوامر هي قراءة الكود المصدري، لأن توثيق بعض الأوامر لا يُكتب بالتفصيل، بل على العكس يكون هناك وصف أكثر تفصيلاً في الكود المصدري. بما أن هذه الأوامر كلها مكتوبة بـ go، فإن قراءتها تكون مريحة جداً. هذه الأوامر كلها موجودة في حزمة src/cmd، كل حزمة فرعية هي أمر منفصل، نقطة الدخول في ملف cmd/go/main.go

go
func init() {
    base.Go.Commands = []*base.Command{
       bug.CmdBug,
       work.CmdBuild,
       clean.CmdClean,
       doc.CmdDoc,
       envcmd.CmdEnv,
       fix.CmdFix,
       fmtcmd.CmdFmt,
       generate.CmdGenerate,
       modget.CmdGet,
       work.CmdInstall,
       list.CmdList,
       modcmd.CmdMod,
       workcmd.CmdWork,
       run.CmdRun,
       test.CmdTest,
       tool.CmdTool,
       version.CmdVersion,
       vet.CmdVet,

       help.HelpBuildConstraint,
       help.HelpBuildmode,
       help.HelpC,
       help.HelpCache,
       help.HelpEnvironment,
       help.HelpFileType,
       modload.HelpGoMod,
       help.HelpGopath,
       get.HelpGopathGet,
       modfetch.HelpGoproxy,
       help.HelpImportPath,
       modload.HelpModules,
       modget.HelpModuleGet,
       modfetch.HelpModuleAuth,
       help.HelpPackages,
       modfetch.HelpPrivate,
       test.HelpTestflag,
       test.HelpTestfunc,
       modget.HelpVCS,
    }
}

هنا ستجد جميع الأوامر الفرعية لـ go، ومعلومات توثيق المساعدة الخاصة بها.

bug

sh
$ go help bug
usage: go bug

Bug opens the default browser and starts a new bug report.
The report includes useful system information.

هذا الأمر ليس له أي معاملات أو علامات، سيفتح متصفحك الافتراضي ويزور واجهة issue في مستودع github.com/golang/go، لتسهيل الإبلاغ عن الأخطاء، ليس له أي استخدام آخر.

version

من خلال أمر version يمكنك عرض معلومات إصدار go الحالي.

go
$ go version -h
usage: go version [-m] [-v] [file ...]

عند التنفيذ بدون أي معاملات، سيخرج إصدار لغة go الحالي

sh
$ go version
go version go1.21.0 windows/amd64

كما أنه يقبل مسار ملف كمعامل، سيخرج إصدار go المستخدم عند تجميع جميع الملفات الثنائية القابلة للتعرف في هذا المسار.

sh
$ go version -v ./
buf.exe: go1.20.2
cobra-cli.exe: go1.21.0
dlv.exe: go1.20.2
goctl.exe: go1.20.2
goimports.exe: go1.20.2
golangci-lint.exe: go1.20.2
gopls.exe: go1.19.3
kratos.exe: go1.20.2
main.exe: go1.19.1
protoc-gen-go-grpc.exe: go1.20.2
protoc-gen-go-http.exe: go1.20.2
protoc-gen-go.exe: go1.20.2
protoc-gen-openapi.exe: go1.20.2
swag.exe: go1.21.0
wire.exe: go1.21.0

حيث المعامل -v يحدد لأمر version محاولة إخراج إصدار go للملفات غير القابلة للتعرف، والمعامل -m يخرج معلومات الوحدة للملف الثنائي، وبعض معاملات التجميع، إليك مثال بسيط.

sh
$ go version -v -m wire.exe
wire.exe: go1.21.0
        path    github.com/google/wire/cmd/wire
        mod     github.com/google/wire  v0.5.0  h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8=
        dep     github.com/google/subcommands   v1.0.1  h1:/eqq+otEXm5vhfBrbREPCSVQbvofip6kIz+mX5TUH7k=
        dep     github.com/pmezard/go-difflib   v1.0.0  h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
        dep     golang.org/x/tools      v0.0.0-20190422233926-fe54fb35175b      h1:NVD8gBK33xpdqCaZVVtd6OFJp+3dxkXuz7+U7KaVN6s=
        build   -buildmode=exe
        build   -compiler=gc
        build   DefaultGODEBUG=panicnil=1
        build   CGO_ENABLED=1
        build   CGO_CFLAGS=
        build   CGO_CPPFLAGS=
        build   CGO_CXXFLAGS=
        build   CGO_LDFLAGS=
        build   GOARCH=amd64
        build   GOOS=windows
        build   GOAMD64=v1

go نفسه هو ملف ثنائي، في الواقع بدون أي معاملات، go version يخرج إصدار لغة go للملف الثنائي نفسه، لأن جميع سلسلة أدوات cmd/go مُنفذة بلغة go نفسها.

env

من خلال أمر env يمكنك عرض معلومات جميع متغيرات بيئة go، تعديل هذه المتغيرات البيئية سيؤثر على سلوك سلسلة أدوات go.

sh
$ go env -h
usage: go env [-json] [-u] [-w] [var ...]
Run 'go help env' for details.

تنفيذ هذا الأمر بدون أي معاملات سيخرج قيم جميع متغيرات بيئة go

sh
$ go env
set GO111MODULE=on
set GOARCH=amd64
...

وضع اسم متغير بيئي معين كمعامل لإخراج قيمة هذا المتغير فقط

sh
$ go env GO111MODULE
on

إضافة -json يمكن إخراجها بصيغة json

sh
$ go env -json
{
        "AR": "ar",
        "CC": "gcc",
    ......
}

من خلال علامة -w، وبصيغة var=value كمعامل، سيتم تعديل قيمة متغير معين بشكل دائم

sh
$ go env -w GO111MODULE=on

استخدام علامة -u يمكن إعادة متغير معين إلى قيمته الافتراضية

sh
$ go env -u GO111MODULE

تنفيذ go help environment يمكن عرض مقدمة لكل متغير بيئي

sh
$ go help environment
The go command and the tools it invokes consult environment variables
for configuration. If an environment variable is unset or empty, the go
command uses a sensible default setting. To see the effective setting of
the variable <NAME>, run 'go env <NAME>'. To change the default setting,
run 'go env -w <NAME>=<VALUE>'. Defaults changed using 'go env -w'
are recorded in a Go environment configuration file stored in the
per-user configuration directory, as reported by os.UserConfigDir.
The location of the configuration file can be changed by setting
the environment variable GOENV, and 'go env GOENV' prints the
effective location, but 'go env -w' cannot change the default location.
See 'go help env' for details.

General-purpose environment variables:

        GO111MODULE
                Controls whether the go command runs in module-aware mode or GOPATH mode.
                May be "off", "on", or "auto".
                See https://golang.org/ref/mod#mod-commands.
        GCCGO
                The gccgo command to run for 'go build -compiler=gccgo'.
        GOARCH
                The architecture, or processor, for which to compile code.
                Examples are amd64, 386, arm, ppc64.
        GOBIN
                The directory where 'go install' will install a command.
        GOCACHE
                The directory where the go command will store cached
                information for reuse in future builds.
    ......

فيما يلي مقدمة لبعض متغيرات البيئة شائعة الاستخدام

GOVERSION

قيمة هذا المتغير البيئي تعتمد على إصدار لغة go، ورقم الإصدار مأخوذ من ملف $GOROOT/VERSION، هذا الملف يسجل رقم إصدار go الحالي ووقت البناء.

sh
$ cat $GOROOT/VERSION
go1.21.3
time 2023-10-09T17:04:35Z

قيمة متغير runtime.Version هي نفسها قيمة GOVERSION، وهذا المتغير البيئي لا يمكن تعديله.

GOENV

في دليل $GOROOT يوجد ملف تكوين افتراضي اسمه go.env

sh
$ cat $GOROOT/go.env
# This file contains the initial defaults for go command configuration.
# Values set by 'go env -w' and written to the user's go/env file override these.
# The environment overrides everything else.

# Use the Go module mirror and checksum database by default.
# See https://proxy.golang.org for details.
GOPROXY=https://proxy.golang.org,direct
GOSUMDB=sum.golang.org

# Automatically download newer toolchains as directed by go.mod files.
# See https://go.dev/doc/toolchain for details.
GOTOOLCHAIN=auto

تنسيقه هو ببساطة صورة key=value، قيم المتغيرات البيئية المعدلة من خلال أمر go env -w key=value ستُكتب في ملف التكوين. لكن يمكن أيضاً عدم استخدام ملف التكوين الافتراضي، المتغير البيئي GOENV يمكن تحديد عنوان ملف تكوين env يدوياً، وقيمة المتغير البيئي GOENV يمكن فقط أن تُغطى بواسطة متغيرات البيئة لنظام التشغيل، ولا يمكن تعديلها بأمر go env -w.

GOHOSTARCH

يمثل بنية CPU للجهاز، للعرض فقط، قيمة هذا المتغير البيئي ليست مقروءة من ملف التكوين، ولا يمكن تعديلها.

GOHOSTOS

يمثل نظام التشغيل للجهاز، للعرض فقط، قيمة هذا المتغير البيئي ليست مقروءة من ملف التكوين، ولا يمكن تعديلها.

GOOS

عند التجميع، قيمة GOOS ستحدد نظام الهدف الذي سيُجمّع إليه الكود المصدري، القيمة الافتراضية هي GOHOSTOS، أي نظام التشغيل للجهاز، لديه الخيارات التالية

  • linux
  • darwin
  • windows
  • netbsd
  • aix
  • android

الأنظمة المدعومة فعلياً لا تقتصر على هذه، استخدم الأمر go tool dist list لعرض جميع القيم المدعومة

sh
$ go tool dist list | awk -F '/' '{print $1}' | awk '!seen[$0]++'
aix
android
darwin
dragonfly
freebsd
illumos
ios
js
linux
netbsd
openbsd
plan9
solaris
wasip1
windows

GOARCH

عند التجميع، قيمة GOARCH ستحدد تعليمات بنية CPU المستخدمة عند التجميع، القيمة الافتراضية هي GOHOSTARCH، أي بنية CPU للجهاز، لديه الخيارات التالية

  • amd64
  • 386
  • arm
  • ppc64

البُنى المدعومة فعلياً أكثر من هذه، استخدم أمر go tool dist list لعرض جميع القيم المدعومة

sh
$ go tool dist list | awk -F '/' '{print $2}' | awk '!seen[$0]++'
ppc64
386
amd64
arm
arm64
riscv64
wasm
loong64
mips
mips64
mips64le
mipsle
ppc64le
s390x

يجب الانتباه إلى أن GOOS و GOARCH لا يمكن دمجهما بشكل عشوائي، بعض أنظمة التشغيل تدعم فقط بنى CPU معينة.

GOROOT

GOROOT يمثل الدليل الجذر لموقع تثبيت لغة go، قيمة GOROOT لا يمكن تعديلها مباشرة، ويمكن فقط أن تُغطى بواسطة متغيرات بيئة نظام التشغيل.

sh
$ ls $GOROOT -1
api
bin
codereview.cfg
CONTRIBUTING.md
doc
go.env
lib
LICENSE
misc
PATENTS
pkg
README.md
SECURITY.md
src
test
VERSION

في الدليل الجذر توجد المجلدات أو الملفات المهمة التالية

  • lib، يخزن بعض التبعيات، حالياً مكتبة واحدة فقط تحتوي على معلومات المنطقة الزمنية لجميع دول العالم، موجودة في $GOROOT/lib/time، الملف الثنائي المُجمّع لن يحتوي على هذه المعلومات الزمنية.

  • pkg، يخزن بعض مكتبات الأدوات والملفات الرأسية، مثل أمر go tool سيبحث عن الملفات الثنائية لسلسلة أدوات go في دليل $GOROOT/pkg/tool

  • bin، يخزن الملفات الثنائية، افتراضياً يوجد فقط go و gofmt كملفات قابلة للتنفيذ، يجب إضافة $GOROOT/bin إلى متغيرات النظام، وإلا لن تتمكن من استخدام أوامر go.

  • src، يخزن الكود المصدري لـ go

  • VERSION، هذا الملف يخزن معلومات إصدار لغة go

  • go.env، هذا الملف هو ملف تكوين env الافتراضي

GOPATH

القيمة الافتراضية لـ GOPATH هي $HOME/go، قيمة هذا المتغير البيئي تحدد أين البحث عن الملفات المستوردة عند تحليل جمل import. في الأيام الأولى قبل وجود gomod، كان GOPATH مخصصاً لتخزين المكتبات الخارجية المختلفة، وهيكله كالتالي

sh
GOPATH=/home/user/go

/home/user/go/
    src/
        foo/
            bar/               (go code in package bar)
                x.go
            quux/              (go code in package main)
                y.go
    bin/
        quux                   (installed command)
    pkg/
        linux_amd64/
            foo/
                bar.a          (installed package object)

بعد ظهور gomod، أصبح GOPATH مجرد مكان لتخزين التبعيات التي ينزلها go get، والملفات الثنائية التي ينزلها ويُجمّعها go install. يجب الانتباه إلى أن موقع GOPATH لا يمكن أن يكون نفس GOROOT، وإلا لن يكون له أي تأثير.

sh
$ go env GOBIN
warning: GOPATH set to GOROOT (/home/user/go) has no effect

حتى وقت كتابة هذا المقال، وصل إصدار لغة go إلى go1.21.3، وباستثناء المشاريع القديمة جداً، لا أحد يستخدم gopath لإدارة التبعيات بعد.

GOBIN

GOBIN يُستخدم لتخزين الملفات الثنائية القابلة للتنفيذ الخارجية التي ينزلها ويُجمّعها go install، قيمته الافتراضية هي $GOPATH/bin. مثل $GOROOT/bin، يجب إضافة هذا الدليل إلى متغيرات بيئة نظام التشغيل، وإلا لن تتمكن من استخدام الملفات الثنائية في دليل GOBIN.

GOMODCACHE

GOMODCACHE يمثل موقع تخزين التبعيات التي ينزلها go get، قيمته الافتراضية هي $GOPATH/pkg/mod. هيكل تخزينه كالتالي

$GOMODCACHE/domain/username/project@verion

في نفس مستوى الدليل سيكون هناك مجلد اسمه sumdb، لتخزين المعلومات المتعلقة بقاعدة بيانات المجموع الاختباري للتبعيات.

GOCACHE

يخزن معلومات الذاكرة المؤقتة للتجميع، قيمته الافتراضية هي $HOME/.cache/go-build، سيُنشأ ملف README في هذا الدليل.

sh
$ cat $(go env GOCACHE)/README
This directory holds cached build artifacts from the Go build system.
Run "go clean -cache" if the directory is getting too large.
Run "go clean -fuzzcache" to delete the fuzz cache.
See golang.org to learn more about Go.

كل build تُنتج العديد من الملفات، سيقوم go بتخزين هذه الملفات مؤقتاً لإعادة استخدامها في التجميع التالي.

GOTEMPDIR

للملفات المؤقتة المنتجة أثناء التجميع، مثل الملف الثنائي المؤقت الذي سيشغله go run. قيمته الافتراضية هي الدليل المؤقت المحدد بواسطة نظام التشغيل، على mac أو linux يكون /tmp، وعلى windows يكون %TEMP%، ويمكن أيضاً تعديله إلى موقع يحدده المستخدم.

GO111MODULE

هذا المتغير البيئي يشير إلى الطريقة المستخدمة لإدارة تبعيات مشروع go، لديه ثلاث قيم متاحة

  • off، إيقاف gomod، استخدام gopath، وتجاهل جميع ملفات go.mod
  • on، استخدام gomod، عدم استخدام gopath (الافتراضي).
  • auto، الاستشعار التلقائي، إذا كان ملف المشروع يحتوي على go.mod سيستخدم gomod للإدارة

TIP

لماذا يُسمى GO111MODULE وليس GOMODULE مباشرة، لأن gomod طُرح لأول مرة في إصدار go1.11.

GOPROXY

وكيل وحدة go، القيمة الافتراضية هي https://proxy.golang.org,direct، عناوين URL مفصولة بفواصل، direct تعني استخدام VCS مباشرة وتخطي وكيل الوحدة، سيتم تنفيذ الأخيرة فقط عندما يتعذر الوصول إلى الأولى، وهناك خيار متاح وهو off، يعني حظر تنزيل أي وحدة. بالإضافة إلى ذلك، يمكن أن يكون GOPROXY عنوان ملف، مثل

GOPROXY=file://$(go env GOMODCACHE)/cache/download

من خلال go get -x يمكنك عرض الأوامر المنفذة أثناء عملية تنزيل التبعيات، لمعرفة ما إذا كان قد مر بالوكيل.

sh
$ go get -x github.com/spf13/cast
# get https://goproxy.cn/github.com/@v/list
# get https://goproxy.cn/github.com/spf13/cast/@v/list
# get https://goproxy.cn/github.com/spf13/@v/list
# get https://goproxy.cn/github.com/spf13/@v/list: 404 Not Found (0.118s)
# get https://goproxy.cn/github.com/@v/list: 404 Not Found (0.197s)
# get https://goproxy.cn/github.com/spf13/cast/@v/list: 200 OK (0.257s)
# get https://goproxy.cn/github.com/spf13/cast/@v/v1.5.1.info
# get https://goproxy.cn/github.com/spf13/cast/@v/v1.5.1.info: 200 OK (0.013s)
# get https://goproxy.cn/github.com/spf13/cast/@v/v1.5.1.mod
# get https://goproxy.cn/github.com/spf13/cast/@v/v1.5.1.mod: 200 OK (0.015s)
# get https://goproxy.cn/sumdb/sum.golang.org/supported
# get https://goproxy.cn/sumdb/sum.golang.org/supported: 200 OK (0.064s)
# get https://goproxy.cn/sumdb/sum.golang.org/lookup/github.com/spf13/cast@v1.5.1
# get https://goproxy.cn/sumdb/sum.golang.org/lookup/github.com/spf13/cast@v1.5.1: 200 OK (0.014s)
# get https://goproxy.cn/sumdb/sum.golang.org/tile/8/0/x079/736
# get https://goproxy.cn/sumdb/sum.golang.org/tile/8/0/x079/736: 200 OK (0.016s)
# get https://goproxy.cn/sumdb/sum.golang.org/tile/8/0/x068/334
# get https://goproxy.cn/sumdb/sum.golang.org/tile/8/1/266
# get https://goproxy.cn/sumdb/sum.golang.org/tile/8/0/x068/334: 200 OK (0.023s)
# get https://goproxy.cn/sumdb/sum.golang.org/tile/8/1/266: 200 OK (0.028s)
go: downloading github.com/spf13/cast v1.5.1
# get https://goproxy.cn/github.com/spf13/cast/@v/v1.5.1.zip
# get https://goproxy.cn/github.com/spf13/cast/@v/v1.5.1.zip: 200 OK (0.024s)
go: added github.com/spf13/cast v1.5.1

استخدام وكيل الوحدة يمكن أن يحسن بفعالية سرعة تنزيل الوحدات، للمستخدمين في الصين لا يمكن الوصول إلى الوكيل الرسمي الافتراضي بدون وكيل، حالياً وكلاء الوحدات الخارجيين الموثوقين المتاحين كالتالي

  • https://proxy.golang.com.cn، مفتوح المصدر ويوفر نسخة المؤسسات
  • https://goproxy.cn، توفره Qiniu Cloud ومفتوح المصدر

بالطبع هناك أيضاً حلول مفتوحة المصدر لبناء وكيل وحدات ذاتي: goproxy

GOSUMDB

GOSUMDB يُستخدم لتعيين عنوان قاعدة بيانات فحص المجموع الاختباري للمكتبات التابعة، الافتراضي هو sum.golang.org، عند تعيينك لوكيل، سيصل go إلى قاعدة بيانات الفحص من خلال الوكيل.

GOPRIVATE

المتغير البيئي GOPRIVATE يُستخدم لتعيين المكتبات الخاصة، المكتبات المتطابقة لن تُفحص من خلال sumdb، ولن تمر بالوكيل، بل ستُنزل التبعيات مباشرة من خلال VCS. يدعم هذا الإعداد أحرف البدل، مفصولة بفواصل، كما يلي، جميع التبعيات التي تنتهي بـ corp.example.com واسمها github.com/gohper/myproject لن تمر بالوكيل و sumdb.

GOPRIVATE=*.corp.example.com,github.com/gohper/myproject

يمكن أيضاً تعيين مستخدم أو منظمة معينة مباشرة

GOPRIVATE=github.com/gopher,github.com/myorganization

GONOPROXY

يشير إلى التبعيات التي لا تحتاج للمرور بالوكيل، القواعد نفسها GOPRIVATE، وستغطي GOPRIVATE.

GONOSUMDB

يشير إلى التبعيات التي لا تحتاج للمرور بقاعدة بيانات الفحص، القواعد نفسها GOPRIVATE، وستغطي GOPRIVATE.

GOINSECURE

يشير إلى التبعيات التي تُنزل مباشرة باستخدام VCS، القواعد نفسها GOPRIVATE، وستُغطى بواسطة GONOPROXY و GONOSUMDB.

GOVCS

تعيين نظام التحكم في الإصدار لإدارة الوحدات، الافتراضي public:git|hg,private:all. يمكن أيضاً تحديد VCS لنطاقات معينة، مثلاً

GOVCS=github.com:git,evil.com:off,*:git|hg

في القيود أعلاه، github يمكنه استخدام git فقط، evil.com غير مسموح له، استخدام | يمكن أن يمثل عدة VCS. إذا لم تضع أي قيود، يمكن الإعداد كالتالي

GOVCS=*:all

إذا لم يُسمح بأي VCS، يمكن الإعداد كالتالي

GOVCS=*:off

GOWORK

تعيين ما إذا كان سيتم تفعيل مساحة العمل، الافتراضي فارغ أي مفعّل، إذا عُيّن إلى off فلن يُفعّل، وسيتم تجاهل جميع ملفات go.work.

GOTOOLDIR

تعيين موقع سلسلة أدوات go المستخدمة، الافتراضي هو $GOROOT/pkg/tool، سلسلة الأدوات الافتراضية مخزنة أيضاً في هذا الموقع.

GODEBUG

تعيين خيارات التصحيح، على شكل أزواج مفتاح-قيمة للتحكم في بعض سلوكيات تنفيذ برنامج go، مثلاً

GODEBUG=http2client=0,http2server=0

هذه الإعدادات لتسهيل الرجوع إلى السلوك القديم عند حدوث تغييرات غير متوافقة أثناء عملية تحديث الإصدار، مثلاً في 1.21 لم يعد مسموحاً بـ panic(nil)، لهذا سجّل go الرسمي خصيصاً GODEBUG History، انتقل إلى GODEBUG لمعرفة المزيد من التفاصيل.

CGO_ENABLED

يشير إلى ما إذا كان سيتم تفعيل cgo، الافتراضي 1، أي مفعّل، تعيينه إلى 0 يلغيه.

هذه المتغيرات البيئية كلها شائعة الاستخدام، أما对于那些不那么常用的 لا يوجد مقدمة كثيرة، مثل CGO، WASM وما شابه، إذا كنت مهتماً يمكنك التعرف عليها بنفسك.

build

go يدعم نوعين من المجمعات، gccgo و gc. gcc هو مجمع C/C++ عريق، يدعم لغات متعددة بما فيها go، أما gc الثاني فلا يشير إلى جمع المهملات، بل يشير إلى go compiler، في go1.5 أكملت go الاعتماد الذاتي، gc هو مجمع مكتوب بالكامل بلغة go، الكود المصدري له موجود في حزمة cmd/compile، وبما أنه مُنفذ بالكامل بلغة go، فمن السهل جداً فهم وتعلم آلياته الداخلية. بشكل افتراضي، يستخدم المجمع gc للتجميع. وبالمناسبة، مصحح أخطاء go أيضاً نوعان، gdb و dlv، الأول هو مصحح C/C++ عريق، يدعم لغات متعددة بما فيها go، والثاني هو مصحح أخطاء مكتوب بلغة go، دعمه للغة go أكثر ودية، وهو أيضاً مفتوح المصدر، يُنصح باستخدام الأخير.

وظيفة أمر build هي تجميع ملفات go المصدرية إلى ملفات ثنائية قابلة للتنفيذ، ستختبر تجربة تجميع سريعة جداً، وهذه أيضاً إحدى خصائص لغة go.

sh
$ go build -h
usage: go build [-o output] [build flags] [packages]
Run 'go help build' for details.

يقبل ثلاثة معاملات، واحد هو مسار إخراج الملف المشار إليه بعلامة -o، وواحد هو علامات البناء build flags لتعريف سلوك التجميع، والأخير هو الحزمة المراد تجميعها، يجب أن يكون هذا المعامل في النهاية. إليك مثال بسيط، بدون استخدام علامات البناء.

sh
# Windows
$ go build -o .\bin\golearn.exe golearn

# macOS / Linux
$ go build -o ./bin/golearn golearn

./bin/golearn.exe يمثل مسار الإخراج، golearn يمثل الوحدة المراد تجميعها، ويمكن أن يكون أيضاً ملف مدخل، أو مجلد. مثل المثال البسيط التالي الذي يأخذ ملف main.go كملف مدخل للتجول.

sh
# Windows
$ go build -o .\bin\golearn.exe main.go

# macOS / Linux
$ go build -o ./bin/golearn main.go

عند التجميع سيتم تجاهل جميع الملفات التي تنتهي بـ _test.go، لأن هذه الملفات حسب الاصطلاح هي ملفات اختبار.

بالإضافة إلى ذلك، يدعم أمر build عدداً كبيراً من علامات البناء للتحكم في بعض السلوكيات أثناء التجميع.

  • -x: إخراج التعليمات التفصيلية أثناء عملية التجميع
  • -n: مشابه لـ -x، لكن الفرق أنه فقط يخرج هذه التعليمات، لكن لا ينفذها فعلياً.
  • -v: إخراج الحزم المُجمّعة
  • -p: عدد التزامن أثناء عملية التجميع
  • -a: فرض إعادة البناء، حتى لو كان أحدث إصدار.
  • -compiler: تحديد المجمع المستخدم، gccgo أو gc، الأخير هو مجمع مكتوب بـ go.
  • -race: تفعيل اكتشاف التسابق
  • -msan: تفعيل تحليل الذاكرة
  • -asan: تفعيل تحليل العناوين
  • -cover: تفعيل اكتشاف تغطية الكود
  • -buildmode: تحديد وضع التجميع، الخيارات المتاحة هي archive، c-archive، c-shared، default، shared، exe، pie، plugin.
  • -pgo، تحديد ملف pgo
  • -trimpath: إزالة بادئة مسار الملف المصدر، مثل المسار النسبي /var/lib/go/src/main.go، بعد الإزالة سيكون اسم الملف الذي يمكن الحصول عليه من خلال runtime أثناء التشغيل هو فقط المسار النسبي بالنسبة لمسار الوحدة /main.go، بعد تفعيل هذا، سيزداد وقت التجميع بشكل ملحوظ، حوالي 20-40%، حسب عدد الملفات.
  • -toolexec، بعض أوامر go المنفذة قبل التجميع، التنسيق هو -toolexec 'cmd args'.
  • -gcflags: تحديد بعض علامات gc للمجمع
  • -gccgoflags: تحديد بعض علامات gccgo للمجمع
  • -ldflags: تحديد بعض علامات أداة link

للحصول على بعض المعاملات المارة مثل ldflags، يمكنك تمرير "-help" للحصول على قيمه المحتملة، مثل

sh
$ go build -ldflags -help
usage: link [options] main.o
  -B note
        add an ELF NT_GNU_BUILD_ID note when using ELF
  -E entry
        set entry symbol name
......

هذه هي الأكثر استخداماً، أما غيرها غير شائعة الاستخدام فيمكنك التعرف عليها بنفسك.

gcflags

من خلال gcflags يمكنك تمرير بعض المعاملات للمجمع gc للتحكم في سلوكيات محددة، تنسيق استخدامه هو -gcflags="pattern=args list"، args list هي قائمة المعاملات، pattern هو نطاق التأثير، له القيم المتاحة التالية

  • main، مسار الحزمة العليا حيث يوجد ملف المدخل
  • all، الوحدة الحالية وجميع تبعيات الوضع الحالي
  • std، المكتبة القياسية
  • cmd، يؤثر على جميع الملفات المصدرية في حزمة cmd
  • أحرف البدل، مثل .، ./...، cmd/....

قواعد pattern هذه تنطبق على جميع العلامات التي تدعم هذا التنسيق، مثل ldflags. من خلال الأمر التالي لعرض قيم معاملاته المتاحة

sh
$ go build -gcflags -help
用法:compile [选项] file.go...
  -%    调试非静态初始化器
  -+    编译运行时
  -B    禁用边界检查
  -C    禁用错误消息中的列号打印
  -D path
        设置本地导入的相对路径
  -E    调试符号导出
  -I directory
        添加目录到导入搜索路径
  -K    调试缺失的行号
  -L    对于受 //line 指令影响的错误位置,同时显示实际源文件名
  -N    禁用优化
  -S    打印汇编列表
  -V    打印版本并退出
  -W    类型检查后调试解析树
  ......

فيما يلي مقدمة لبعض المعاملات شائعة الاستخدام

  • -S: إخراج صورة التجميع للكود
  • -N: إيقاف تحسينات التجميع
  • -m: إخراج قرارات التحسين
  • -l: إيقاف الدمج التلقائي للدوال
  • -c: عدد التزامن في التجميع
  • -dwarf: توليد علامات DWARF

مثلاً إذا كنت تريد عرض صورة التجميع للكود، يمكنك استخدام المعامل -S، ويجب أيضاً إيقاف التحسين والدمج التلقائي، حتى تتم استعادة شكله الأصلي، كالتالي

sh
$ go build -trimpath -gcflags="-N -l -S" main.go
main.main STEXT size=171 args=0x0 locals=0x58 funcid=0x0 align=0x0
        0x0000 00000 (./main.go:9)      TEXT    main.main(SB), ABIInternal, $88-0
        0x0000 00000 (./main.go:9)      CMPQ    SP, 16(R14)
        0x0004 00004 (./main.go:9)      PCDATA  $0, $-2
        0x0004 00004 (./main.go:9)      JLS     161
        0x000a 00010 (./main.go:9)      PCDATA  $0, $-1
        0x000a 00010 (./main.go:9)      PUSHQ   BP
        0x000b 00011 (./main.go:9)      MOVQ    SP, BP
        0x000e 00014 (./main.go:9)      SUBQ    $80, SP
        0x0012 00018 (./main.go:9)      FUNCDATA        $0, gclocals·J5F+7Qw7O7ve2QcWC7DpeQ==(SB)
        0x0012 00018 (./main.go:9)      FUNCDATA        $1, gclocals·bDfKCdmtOiGIuJz/x+yQyQ==(SB)
        0x0012 00018 (./main.go:9)      FUNCDATA        $2, main.main.stkobj(SB)
        0x0012 00018 (./main.go:10)     MOVUPS  X15, main..autotmp_0+40(SP)
        0x0018 00024 (./main.go:10)     LEAQ    main..autotmp_0+40(SP), CX
        0x001d 00029 (./main.go:10)     MOVQ    CX, main..autotmp_2+32(SP)

ldflags

من خلال ldflags يمكنك تمرير بعض المعاملات للرابط للتحكم في سلوكيات محددة، من خلال الأمر التالي لعرض جميع القيم المتاحة لـ ldflags، تقريباً عشرين أو ثلاثين.

sh
$ go build -ldflags -help
usage: link [options] main.o
  -B note
        add an ELF NT_GNU_BUILD_ID note when using ELF
  -E entry
        set entry symbol name
  -H type
        set header type
  -I linker
        use linker as ELF dynamic linker
  -L directory
        add specified directory to library path
  -R quantum
        set address rounding quantum (default -1)
  -T int
        set the start address of text symbols (default -1)
  -V    print version and exit
  -X definition
        add string value definition of the form importpath.name=value
  -a    no-op (deprecated)
  .....

المعامل -X في ldflags هو وظيفة عملية جداً، يمكنه تعريف قيم متغيرات السلسلة في حزمة محددة أثناء الربط. من خلال هذه الوظيفة، يمكننا بسهولة حقن بعض المعلومات الوصفية أثناء التجميع. وبما أنه مجرد متغير، فمن السهل أيضاً الحصول عليه أثناء التشغيل، إليك مثال بسيط.

go
package main

import "fmt"

var (
  Version string
)

func main() {
  fmt.Println(Version)
}

تنفيذ الأمر

sh
go build -ldflags "-X main.Version=$(git describe --always)" main.go

بعد التشغيل سيتم إخراج المجموع الاختباري sha1 لـ git commit.

5e3fd7a

هناك أيضاً بعض المعاملات العملية الأخرى مثل

  • -w: عدم توليد DWARF، هذه معلومات لتسهيل تصحيح الكود المصدري.
  • -s: تعطيل جدول الرموز

هذان يستخدمان معاً عادةً، يمكن تقليل حجم الملف الثنائي المُجمّع بشكل ملحوظ، حوالي 40%-50%، والعيب واضح أيضاً، لا يمكن التصحيح، إليك مثال.

sh
$ go build -ldflags="-w -s" main.go

التجميع المتقاطع

تجميع لغة go له خاصيتان رئيسيتان، الأولى هي السرعة، والخاصية الكبيرة الأخرى هي التجميع المتقاطع، التجميع المتقاطع يعني القدرة على التجميع محلياً إلى كود هدف لأنظمة أخرى، مثلاً على windows التجميع إلى ملفات ثنائية لـ linux أو darwin، والعكس صحيح. التجميع المتقاطع يدعم العديد من اللغات، هذا ليس شيئاً نادراً، لكن التجميع المتقاطع في go بسيط جداً، يحتاج فقط الخطوات التالية

  1. تعيين متغير البيئة GOOS، اختيار نظام التشغيل الهدف
  2. تعيين متغير البيئة GOARCH، اختيار بنية CPU الهدف
  3. استخدام go build للتجميع كالمعتاد

العملية برمتها قصيرة جداً، لا تحتاج استخدام أدوات أو إعدادات إضافية، والسرعة سريعة كالمعتاد. كالتالي

makefile
build_linux:
  SET CGO_ENABLED=0
  SET GOOS="linux"
  SET GOARCH="amd64"
  go build -o golearn  main.go

build_mac:
  SET CGO_ENABLED=0
  SET GOOS="darwin"
  SET GOARCH="amd64"
  go build -o golearn main.go

build_win:
  SET CGO_ENABLED=0
  SET GOOS="win"
  SET GOARCH="amd64"
  go build -o golearn.exe main.go

.PHONY: build_linux \
    build_mac \
    build_win

الخطوة الأولى SET CGO_ENABLED=0 تعطل cgo، بمجرد استخدام cgo في كودك، لن تتمكن من استخدام التجميع المتقاطع بشكل طبيعي. الخطوة الثانية SET GOOS تعيين النظام الهدف، الخيارات المتاحة هي linux، darwin، windows، netbsd. الخطوة الثالثة تعيين بنية CPU، SET GOARCH، الخيارات المتاحة هي amd64، 386، arm، ppc64. الخطوة الأخيرة هي التجميع كالمعتاد.

التحكم في التجميع

أمر build يمكنه التحكم في تأثير التجميع من خلال tags، وهو موجود في الكود المصدري على شكل توجيه، انظر المثال، ملف product.go

go
// +build product

package main

import "fmt"

func main() {
  fmt.Println("product")
}

ملف debug.go

go
// +build debug

package main

import "fmt"

func main() {
  fmt.Println("debug")
}

كلاهما لديه توجيه // +build، يشير إلى الحالات التي سيُجمّع فيها. تنسيقه الأساسي هو

go
// +build tag1 tag2

package pkg_name

هناك عدة قواعد يجب اتباعها

  1. يجب أن يكون هناك مسافة بين // و +build
  2. يجب أن يكون أعلى إعلان الحزمة
  3. يجب أن يُفصل عن إعلان الحزمة بسطر فارغ

بالإضافة إلى ذلك، يمكن تحقيق التحكم المنطقي من خلال تباعد بسيط، المسافة تعني OR، الفاصلة تعني AND، ! تعني NOT. مثل المثال التالي

go
// +build windows linux

package pkg_name

يعني أنه في نظام windows أو linux سيُجمّع الملف الحالي.

go
// +build windows,amd64,!cgo linux,i386,cgo

package pkg_name

هذا المثال يعني أنه في نظام windows بنية amd64 وبدون تفعيل cgo أو نظام linux بنية i386 وتفعيل cgo سيُجمّع. إذا كنت تريد ببساطة عدم مشاركة ملف معين في التجميع، يمكنك استخدام ignore.

go
// +build ignore

package pkg_name

يمكن أيضاً وجود عدة أسطر من التوجيهات

go
// +build windows
// +build amd64

package pkg_name

التوجيهات متعددة الأسطر تُعامل بطريقة AND. بالنسبة لعلامات المنصة والبنية هذه، سيتم تمريرها تلقائياً من go أثناء التجميع، يمكننا أيضاً تمرير علامات مخصصة، لنأخذ الملفين السابقين كمثال

sh
$ go build -tags="debug" . && ./golearn.exe
debug

$ go build -tags="product" . && ./golearn.exe
product

يمكنك أن ترى أن الإخراج مختلف عند تمرير علامات مختلفة، وبذلك يكون الهدف من التحكم في التجميع قد تحقق.

run

أمر run وأمر build كلاهما سيُجمّع الكود المصدري، الفرق هو أن أمر run سيشغله مباشرة بعد اكتمال التجميع. أمر run لتسريع سرعة التجميع، لا يُنشئ معلومات التصحيح أثناء عملية التجميع، لذلك لا يدعم التصحيح، ويُنشئ فقط ملفاً ثنائياً مؤقتاً، يُخزن عادةً في دليل GOTMEPDIR، مثل /temp/go-build2822241271/b001/exe/main.exe.

sh
$ go run -h
usage: go run [build flags] [-exec xprog] package [arguments...]
Run 'go help run' for details.

يدعم أيضاً علامات البناء لأمر build، ويوفر معاملاً -exec لتحديد البرنامج الذي سيشغل الملف الثنائي، [arguments...] يشير إلى معاملات تشغيل البرنامج. إليك مثال

go
package main

import (
  "fmt"
  "os"
)

var (
  Version string
)

func main() {
  fmt.Println(Version)
  fmt.Println(os.Args[1:])
}

استخدام go run للتشغيل

sh
$ go run -ldflags="-X main.Version=$(git describe --always)" main.go hello
5e3fd7a
[hello]

بشكل عام استخدامه مشابه جداً لـ go build، لذلك لن نقدم الكثير من التفاصيل.

tool

أمر tool نفسه ليس له أي وظيفة، وظيفته هي استدعاء الأدوات في دليل cmd/ مباشرةً، مثل cmd/compile هو المجمع المدمج. من خلال go tool يمكن استدعاء هذه الأدوات مباشرةً، دون الحاجة لتنفيذ الملفات الثنائية لهذه الأدوات يدوياً.

sh
$ go tool -h
usage: go tool [-n] command [args...]

استخدام المعامل -n لطباعة جميع معاملات الأوامر المدعومة

sh
$ go tool -n
addr2line
asm
buildid
cgo
compile
covdata
cover
doc
fix
link
nm
objdump
pack
pprof
test2json
trace
vet

هذه الأدوات مخزنة في دليل GOROOT/pkg/tool، ومُجمّعة حسب نظام التشغيل وبنية CPU، كالتالي.

sh
$ ls $GOROOT/pkg/tool/windows_amd64/ -1
addr2line.exe*
asm.exe*
buildid.exe*
cgo.exe*
compile.exe*
covdata.exe*
cover.exe*
doc.exe*
fix.exe*
link.exe*
nm.exe*
objdump.exe*
pack.exe*
pprof.exe*
test2json.exe*
trace.exe*
vet.exe*

استخدام go doc cmd/command لعرض استخدام كل أمر، مثل

sh
$ go doc cmd/compile
Usage:

    go tool compile [flags] file...

The specified files must be Go source files and all part of the same package.
The same compiler is used for all target operating systems and architectures.
The GOOS and GOARCH environment variables set the desired target.

Flags:

    -D path
        Set relative path for local imports.
    -I dir1 -I dir2
        Search for imported packages in dir1, dir2, etc,
        after consulting $GOROOT/pkg/$GOOS_$GOARCH.
    -L
        Show complete file path in error messages.
...

العلامات التي يدعمها cmd/compile هي نفسها العلامات التي يدعمها gcflags المذكور سابقاً. الفرق بين go tool compile و go build هو أن الأول مسؤول فقط عن التجميع، ويمكنه فقط أخذ الملفات كمعاملات، بينما الثاني يمكنه أخذ المجلدات، الحزم، الملفات كمعاملات، ولا يقوم فقط بتجميع الكود المصدري، بل مسؤول أيضاً عن ربط الملفات، مسح الملفات غير المفيدة وغيرها، الأول جزء من الأخير. يمكننا طباعة الأوامر المنفذة أثناء عملية build

sh
$ go build -n main.go

#
# internal/goarch
#

mkdir -p $WORK\b004\
cat >$WORK\b004\importcfg << 'EOF' # internal
# import config
EOF
"/golang/pkg/tool/windows_amd64/compile.exe" -o "$WORK/b004/_pkg_.a" -trimpath "$WORK/b004=>" -p internal/goarch -std -+ -complete -buildid 3gunEkUExGdhOPa2rFsh/3gunEkUExGdhOPa2rFsh -goversion go1.21.0 -c=4 -nolocalimports -importcfg "$WORK/b004/importcfg" -pack "/golang/src/internal/goarch/goarch.go" "/golang/src/internal/goarch/goarch_amd64.go" "/golang/src/internal/goarch/zgoarch_amd64.go"
"/golang/pkg/tool/windows_amd64/buildid.exe" -w "$WORK/b004/_pkg_.a" # internal
...

في العملية يمكنك رؤية هذا الجزء /golang/pkg/tool/windows_amd64/compile.exe، وهو استدعاء المجمع. بالإضافة إلى compile، هناك العديد من الأدوات الأخرى التي يمكن استدعاؤها، والعديد من أوامر go في الواقع هي أسماء مستعارة لها.

clean

أمر clean يُستخدم لمسح الملفات الكائنة المنتجة أثناء عملية التجميع

sh
$ go clean -h
usage: go clean [clean flags] [build flags] [packages]
Run 'go help clean' for details.

يدعم العلامات التالية

  • -i: مسح الملفات الأرشيفية أو الملفات الثنائية المقابلة
  • -n: طباعة الأوامر التي سيتم تنفيذها أثناء عملية المسح لكن بدون تنفيذها فعلياً
  • -x: طباعة الأوامر التي سيتم تنفيذها أثناء عملية المسح وتنفيذها
  • -r: المسح بشكل متكرر من خلال import path
  • -cache، مسح جميع الذاكرات المؤقتة الناتجة عن go build
  • -testcache: مسح جميع ذاكرات الاختبار المؤقتة الناتجة
  • -modcache: مسح جميع ذاكرات الوحدات المنزلة
  • -fuzzcache: مسح الذاكرات المؤقتة الناتجة عن fuzz test.

عند استخدام go tool compile، يتم استدعاء أمر المجمع مباشرةً، على عكس go build الذي يقوم بالكثير من المعالجات اللاحقة، لذلك ستُنتج ملفات كائنة. مثل تنفيذ الأمر التالي

sh
go tool compile -N -S -l main.go

سيُنتج ملفاً اسمه main.o، استخدم أمر go clean لمسحه. أو استخدام المعامل -n لطباعة الأوامر التي سيتم تنفيذها.

sh
$ go clean -n
rm -f golearn golearn.exe golearn golearn.exe golearn.test golearn.test.exe golearn.test golearn.test.exe api api.exe main main.exe

مسح ذاكرة التجميع المؤقتة، سيحذف الذاكرة المؤقتة للتجول المنتجة في دليل GOCACHE

sh
$ go clean -cache -n
rm -r /cache/00 /cache/01 /cache/02

مسح الذاكرة المؤقتة لـ fuzz test، هذه الذواكر مخزنة افتراضياً في دليل GOCACHE/fuzz/

sh
$ go clean -fuzzcache -n
rm -rf /cache/fuzz

fix

لغة go حتى وقت كتابة هذا المقال لها عشر سنوات، خلال عملية التحديث والتعديل المستمرة للغة، من المحتم ظهور بعض عدم التوافق بسبب تغييرات API، أمر fix وُجد لهذا الغرض، سيكتشف APIs القديمة في الملفات المصدرية ويستبدلها بـ APIs الجديدة.

sh
$ go fix -h
usage: go fix [-fix list] [packages]
Run 'go help fix' for details.

يدعم المجلدات، أسماء الملفات، الدلائل كمعاملات، يقبل علامة -fix لتمرير معاملات للإشارة إلى نوع التعديل، يمكن من خلال أمر got tool fix -help عرض القيم المتاحة

sh
$ go tool fix -help
usage: go tool fix [-diff] [-r fixname,...] [-force fixname,...] [path ...]
  -diff
        display diffs instead of rewriting files
  -force string
        force these fixes to run even if the code looks updated
  -go string
        go language version for files
  -r string
        restrict the rewrites to this comma-separated list

Available rewrites are:

buildtag
        Remove +build comments from modules using Go 1.18 or later

cftype
        Fixes initializers and casts of C.*Ref and JNI types

context
        Change imports of golang.org/x/net/context to context

egl
        Fixes initializers of EGLDisplay

eglconf
        Fixes initializers of EGLConfig

gotypes
        Change imports of golang.org/x/tools/go/{exact,types} to go/{constant,types}

jni
        Fixes initializers of JNI's jobject and subtypes

netipv6zone
        Adapt element key to IPAddr, UDPAddr or TCPAddr composite literals.

        https://codereview.appspot.com/6849045/

printerconfig
        Add element keys to Config composite literals.

إليك مثالاً، الكود المصدري يستخدم حزمة golang.org/x/net/context

sh
package main

import (
  "fmt"
  "golang.org/x/net/context"
)

func main() {
  background := context.Background()
  fmt.Println(background.Err())
}

استخدام go fix للتصحيح، واستبدالها بحزمة context في المكتبة القياسية، يمكننا استخدام الأمر التالي للقيام بالاستبدال

sh
$ go fix -fix context main.go

يمكن أيضاً عدم الاستبدال، ومشاهدة التغييرات قبل وبعد الملف.

sh
$ go tool fix -r context -diff  main.go
main.go: fixed context
diff main.go fixed/main.go
--- main.go
+++ fixed/main.go
@@ -1,8 +1,8 @@
 package main

 import (
+       "context"
        "fmt"
-       "golang.org/x/net/context"
 )

 func main() {

لغة go وُجدت لأكثر من عشر سنوات ولديها فقط تسعة معاملات استبدال متاحة، مما يدل على أن التوافق محفوظ بشكل جيد.

fmt

أمر fmt هو أداة التنسيق المدمجة في لغة go، تُستخدم لتنسيق ملفات go المصدرية.

sh
$ go fmt -h
usage: go fmt [-n] [-x] [packages]
Run 'go help fmt' for details.

من خلال أمر go doc gofmt لعرض توثيقه التفصيلي

sh
$ go doc cmd/gofmt
Gofmt formats Go programs. It uses tabs for indentation and blanks for
alignment. Alignment assumes that an editor is using a fixed-width font.

Usage:

    gofmt [flags] [path ...]

The flags are:

    -d
        Do not print reformatted sources to standard output.
        If a file's formatting is different than gofmt's, print diffs
        to standard output.
    -e
        Print all (including spurious) errors.
    -l
        Do not print reformatted sources to standard output.
        If a file's formatting is different from gofmt's, print its name
        to standard output.
    -r rule
        Apply the rewrite rule to the source before reformatting.
    -s
        Try to simplify code (after applying the rewrite rule, if any).
    -w
        Do not print reformatted sources to standard output.
        If a file's formatting is different from gofmt's, overwrite it
        with gofmt's version. If an error occurred during overwriting,
        the original file is restored from an automatic backup.

gofmt يستخدم tab للمسافة البادئة، ومسافات للمحاذاة، افتراضياً سيُخرج الكود المنسق إلى المخرج القياسي، وليس الكتابة فوق الملف الأصلي. أمر go fmt في الواقع يستخدم أمر gofmt، وهو ملف ثنائي مستقل، موجود في دليل GOROOT/bin.

sh
$ ls $GOROOT/bin -1
go.exe*
gofmt.exe*

أضف علامة -n لأمر go fmt لمعرفة التعليمات التي سيتم تنفيذها.

sh
$ go fmt main.go
/golang/bin/gofmt.exe -l -w main.go

يمكن ملاحظة أن go fmt هو في الواقع اسم مستعار لـ gofmt -l -w، أمر gofmt لديه المعاملات التالية

  • -d: إخراج اختلافات الملف قبل وبعد التنسيق
  • -e: إخراج جميع الأخطاء
  • -l: إخراج أسماء الملفات المتغيرة
  • -r: تطبيق قواعد التنسيق
  • -s: محاولة تبسيط الكود
  • -w: الكتابة فوق الملف المصدر، إذا حدث خطأ سيتم استعادة النسخة الاحتياطية

لنفترض أن لدينا الآن الملف المصدر التالي

go
$ cat main.go
package main

import "fmt"

func main() {
fmt.Println("hello world!")}

من خلال المعامل -d يمكن معاينة التغييرات

sh
$ gofmt -d main.go
diff main.go.orig main.go
--- main.go.orig
+++ main.go
@@ -3,5 +3,5 @@
 import "fmt"

 func main() {
-fmt.Println("hello world!")}
-
+       fmt.Println("hello world!")
+}

المعامل -l سيُخرج اسم الملف الذي سيتم تعديله

$ gofmt -l .
main.go

إذا كانت هناك أخطاء نحوية، المعامل -e يمكن أن يُخرج بمزيد من التفاصيل

sh
$ gofmt -d -e main.go
main.go:6:27: missing ',' in argument list
main.go:6:28: expected operand, found newline
main.go:7:2: expected ')', found 'EOF'
main.go:7:2: expected ';', found 'EOF'
main.go:7:2: expected ';', found 'EOF'
main.go:7:2: expected '}', found 'EOF'
main.go:7:2: missing ',' in argument list

-w سيُطبّق التعديلات على الملف المصدر

sh
$ gofmt -l -w .
main.go

$ cat main.go
package main

import "fmt"

func main() {
        fmt.Println("hello world!")
}

يمكنك أن تكتشف أن كأداة تنسيق، gofmt لا يوفر تماماً أي تكوين مخصص، بينما أداة التنسيق prettify المصممة لتجميل كود js توفر الكثير من التكوينات لتنسيق الكود، هذا يعكس موقف go الرسمي، لا تفكر في التخصيص، الأفضل أن يكون نمط كود الجميع متسقاً، على الأقل هناك فائدة واحدة وهي عدم الحاجة للتكيف مع عادات الآخرين عند قراءة الكود. لكن في الواقع هي لا تزال تحتفظ بخيار تخصيص واحد، وهو قواعد استبدال تنسيق الكود، القواعد يمكن تخصيصها، التنسيق كالتالي

pattern -> replacement

مثل إزالة الأقواس الزائدة

(a) -> a

عرض تغييرات الملف

sh
$ gofmt -r "(a) -> a" -d -l .
main.go
diff main.go.orig main.go
--- main.go.orig
+++ main.go
@@ -3,5 +3,5 @@
 import "fmt"

 func main() {
-       fmt.Println(("hello world!"))
+       fmt.Println("hello world!")
 }

يمكنك أن ترى أن gofmt سيحذف الأقواس الزائدة.

get

أمر get هو بالتأكيد الأكثر استخداماً في تطوير go، وظيفته هي تنزيل الكود المصدري لعنوان الحزمة المحدد إلى الدليل المقابل لـ GOMODCACHE.

sh
$ go get -h
usage: go get [-t] [-u] [-v] [build flags] [packages]
Run 'go help get' for details.
  • -u: محاولة تحديث الإصدار الفرعي وإصدار التصحيح للحزمة، إذا كان هناك تغيير في الإصدار الرئيسي، مثل v1->v2، لن يتم التحديث.
  • -t: تحديث إصدارات التبعيات في الاختبارات
  • -v: إخراج الحزم المُجمّعة، في الواقع ينتمي إلى معاملات build flags

في الأيام القديمة، كانت وظيفة go get مشابهة لـ go install، كانت تنزل وتُجمّع هذه الحزم، ومع ظهور ونضوج وحدات go، تم إهمال هذا الجزء من الوظيفة تدريجياً، الآن الاستخدام الأكثر شيوعاً لأمر get هو تنزيل وتحليل التبعيات لوحدات go، لذلك يمكنك أن ترى أن أمر go get يدعم أيضاً build flags، وإذا حاولت استخدامه مثل go install خارج الوحدة، سيُذكّرك أن هذا الاستخدام تم إهماله.

sh
$ go get github.com/wire/wire
go: go.mod file not found in current directory or any parent directory.
        'go get' is no longer supported outside a module.
        To build and install a command, use 'go install' with a version,
        like 'go install example.com/cmd@latest'
        For more information, see https://golang.org/doc/go-get-install-deprecation
        or run 'go help get' or 'go help install'.

أما لماذا لا يزال محفوظاً في وصف التوثيق فغير معروف، بالرجوع إلى الكود المصدري لأمر get، ستجد أيضاً أنه يحتفظ بتلك العلامات القديمة.

go
var (
  getD        = CmdGet.Flag.Bool("d", true, "")
  getF        = CmdGet.Flag.Bool("f", false, "")
  getFix      = CmdGet.Flag.Bool("fix", false, "")
  getM        = CmdGet.Flag.Bool("m", false, "")
  getT        = CmdGet.Flag.Bool("t", false, "")
  getU        upgradeFlag
  getInsecure = CmdGet.Flag.Bool("insecure", false, "")
  // -v is cfg.BuildV
)

بالعودة إلى الموضوع، أمر get سيُنزّل الكود المصدري للحزمة المحددة إلى دليل التبعيات العالمي المحلي، أي الدليل المقابل لـ GOCACHE، ثم يسجل المعلومات في ملفي go.mod و go.sum، الأول مسؤول عن تسجيل الإصدار، والثاني مسؤول عن تسجيل المجموع الاختباري sha1 لضمان الأمان. أمر get في الواقع مبني على VCS، أي نظام التحكم في الإصدار المحلي، يدعم الإجمالي ما يلي

  • git
  • hg (Mercurial)
  • bzr (Bazaar)
  • svn
  • fossil

من بينها، الافتراضي يدعم فقط git و hg، يمكن التكوين في GOVCS، التنسيق كالتالي

GOVCS=github.com:git,example.com:hg,*:git|hg,*:all

GOVCS يدعم فقط git و hg كـ VCS، الثلاثة الآخرون يحتاجون للتكوين في GOPRIVATE.

go get لديه عدة طرق استخدام إجمالاً، يمكن تمرير عنوان التبعية مباشرة كمعامل

sh
$ go get golang.org/x/net

يمكن أيضاً تحديد الإصدار

sh
$ go get golang.org/x/net@0.17.0

تحديد أحدث إصدار

sh
$ go get golang.org/x/net@latest

محاولة تحديث الإصدار

sh
$ go get -u golang.org/x/net

إزالة تبعية معينة

sh
$ go get golang.org/x/net@none

هذه الاستخدامات لإدارة التبعيات العادية، يمكن أيضاً استخدامه لإدارة التبعيات غير العادية، مثل تحديث إصدار لغة go

sh
$ go get go@latest
go: updating go.mod requires go >= 1.21.3; switching to go1.21.3
go: downloading go1.21.3 (windows/amd64)
go: upgraded go 1.21.0 => 1.21.3

يمكن حتى استخدامه لتحديث إصدار سلسلة أدوات go

sh
$ go get toolchain@latest

عند استخدام go get لتحديث إصدارات go وسلسلة الأدوات، سيتم تثبيت الإصدار الجديد من go في دليل GOMODCACHE/golang.org/

sh
$ ls $(go env GOMODCACHE)/golang.org -1
'toolchain@v0.0.1-go1.21.3.windows-amd64'/
x/

في هذه الحالة يمكنك تعديل GOROOT يدوياً للتبديل إلى الإصدار المحدد.

install

أمر install مشابه لأمر get، كلاهما يُستخدم لتنزيل التبعيات الخارجية، لكن الفرق هو أن أمر get ينزل الكود المصدري، بينما أمر install سيُجمّع الكود المصدري إلى ملف ثنائي قابل للتنفيذ على الجهاز، مسار تخزين الملف الثنائي أولاً في دليل GOBIN، ثم في GOPATH/bin. الوظيفة الرئيسية لهذا الأمر هي تنزيل بعض أدوات سطر الأوامر الخارجية المفتوحة، وبفضل سرعة تجميع لغة go وقابليتها للنقل، لا تحتاج لتنزيل الملفات الثنائية، بل تنزيل الكود المصدري مباشرة ثم تجميعه محلياً.

sh
$ go install -h
usage: go install [build flags] [packages]
Run 'go help install' for details.

أمر install يقبل علامات البناء واسم الحزمة كمعاملات، في حالة تفعيل gomod، يجب أن يحمل اسم الحزمة رقم الإصدار. مثلاً تنزيل مصحح الأخطاء delve

sh
$ go install -x github.com/go-delve/delve/cmd/dlv@latest
# get https://goproxy.cn/github.com/@v/list
# get https://goproxy.cn/github.com/go-delve/delve/cmd/@v/list
# get https://goproxy.cn/github.com/go-delve/delve/cmd/dlv/@v/list
# get https://goproxy.cn/github.com/go-delve/delve/@v/list
# get https://goproxy.cn/github.com/go-delve/@v/list
# get https://goproxy.cn/github.com/@v/list: 404 Not Found (0.014s)
# get https://goproxy.cn/github.com/go-delve/delve/cmd/@v/list: 404 Not Found (0.027s)
# get https://goproxy.cn/github.com/go-delve/delve/cmd/dlv/@v/list: 404 Not Found (0.027s)
# get https://goproxy.cn/github.com/go-delve/delve/@v/list: 200 OK (0.027s)
# get https://goproxy.cn/github.com/go-delve/@v/list: 404 Not Found (0.027s)
WORK=/home/user/tmp/go-build2033992495
mkdir -p $WORK/b001/
cat >/home/user/tmp/go-build2033992495/b001/importcfg.link << 'EOF' # internal
packagefile github.com/go-delve/delve/cmd/dlv=/home/user/.cache/go-build/f1/f11d552287458c0fce625abe50bf928c487064c36bbb1251ad8b1968772c3e4b-d
......
......
mkdir -p /home/wyh/gomod/bin/
mv $WORK/b001/exe/a.out /home/wyh/gomod/bin/dlv
rm -r $WORK/b001/

أولاً سيُنزّل الكود المصدري إلى المسار المخزن في GOMODCACHE، وهذا مشابه لأمر get، ثم ينتقل إلى دليل العمل المؤقت، ويُجمّعه، بعد اكتمال التجميع ينقل الملف الثنائي إلى دليل GOPATH/bin، وأخيراً يحذف المجلد المؤقت. أمر install له قيد آخر وهو أن الحزمة المنزلة يجب أن تكون حزمة مدخل المشروع، أي يجب أن تحتوي على ملف مدخل main.go، وإلا سيُذكّرك أنه لا يمكن تثبيتها. مثلاً، استخدام go install لتنزيل gin

sh
$ go install -x github.com/gin-gonic/gin@latest
# get https://goproxy.cn/github.com/@v/list
# get https://goproxy.cn/github.com/gin-gonic/gin/@v/list
# get https://goproxy.cn/github.com/gin-gonic/@v/list
# get https://goproxy.cn/github.com/@v/list: 404 Not Found (0.022s)
# get https://goproxy.cn/github.com/gin-gonic/gin/@v/list: 200 OK (0.027s)
# get https://goproxy.cn/github.com/gin-gonic/@v/list: 404 Not Found (0.028s)
package github.com/gin-gonic/gin is not a main package

gin هو مكتبة تبعية إطار عمل web، وليس أداة سطر أوامر، وبطبيعة الحال ليس لديه ملف مدخل، لذا سيفشل التثبيت.

list

أمر list سيُخرج قائمة الحزم في الموقع المحدد، واحدة في كل سطر، ويدعم تنسيق الإخراج المخصص، ويدعم العديد من المعاملات، شرط استخدامه هو أن تكون في مشروع يدعم gomod.

sh
$ go list -h
usage: go list [-f format] [-json] [-m] [list flags] [build flags] [packages]
Run 'go help list' for details.

يدعم المعاملات التالية

  • -f: معامل التنسيق
  • -json: إخراج بصيغة json
  • -compiled: عرض جميع الحزم التي سيُجمّعها المجمع
  • -deps: عرض اسم كل حزمة وكل تبعياتها
  • -test: عرض حزمة الاختبار لكل حزمة
  • -e: الإخراج بشكل طبيعي عند مواجهة حزم بها أخطاء
  • -find: عدم تحليل علاقات التبعية لهذه الحزم
  • -export: عند استخدام هذا المعامل، تعيين قيمة حقل Package.Export للهيكل إلى الملف الذي يحتوي على أحدث معلومات تصدير للحزمة المحددة، وتعيين قيمة حقل Package.BuildID إلى BuildID للحزمة، يستخدم بشكل أساسي لتنسيق الإخراج.

معاملات معلومات الوحدة،

  • -m: إخراج الوحدة بدلاً من إخراج الحزمة

  • -versions: عرض جميع المعلومات المتاحة لوحدة

  • -retracted: عرض الإصدار المسحوب لوحدة

المعامل [packages] يمكن أن يكون اسم حزمة محدد، أو مجلد، أو all، يعني أي مكان، عند استخدام المعامل -m، يعني all جميع التبعيات المُشار إليها في الوحدة الحالية.

مثلاً، الملف الحالي يحتوي فقط على ملف main.go، وكود واحد فقط يُخرج "hello world"، بعد تنفيذ go list -deps .، سيُخرج جميع الحزم من المشروع الحالي إلى fmt وجميع التبعيات المُشار إليها.

bash
$ ls
go.mod  go.sum  main.go

$ cat main.go
package main

import "fmt"

func main() {
        fmt.Println("hello world")
}

$ go list -deps .
internal/goarch
unsafe
internal/abi
internal/unsafeheader
internal/cpu
internal/bytealg
internal/coverage/rtcov
internal/godebugs
internal/goexperiment
internal/goos
runtime/internal/atomic
runtime/internal/math
runtime/internal/sys
runtime
......
......
path
io/fs
os
fmt
golearn

أو إخراج جميع تبعيات الوحدة في المشروع الحالي

bash
$ go list -m all
golearn
cloud.google.com/go v0.26.0
github.com/246859/containers v0.0.1
github.com/246859/river v0.1.0 => D:\WorkSpace\Code\riverdb
github.com/BurntSushi/toml v0.3.1
github.com/Jleagle/steam-go v0.0.0-20230725082712-1053b441b1f2
github.com/Jleagle/unmarshal-go v0.0.0-20210227002040-694f544f9265
github.com/KyleBanks/depth v1.2.1
github.com/Microsoft/go-winio v0.6.1
github.com/PuerkitoBio/purell v1.1.1
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578
github.com/andeya/ameda v1.5.3
github.com/andeya/goutil v1.0.1
...

format

إخراج أمر list يكون على شكل أسطر، كل سطر هو حزمة. يوفر المسؤول معاملاً f- يمكننا من تخصيص تنسيق إخراج السطر، القيمة التي يقبلها هي صيغة القالب المحددة بواسطة محرك القوالب template/text، مثل المثال التالي

sh
-f "package {{ .Dir }} {{ .Name }}"

كل حزمة مُكرّر عليها ستُمرّر على شكل الهيكل التالي، جميع الحقول في هذا الهيكل يمكن استخدامها كمعاملات للقالب.

go
type Package struct {
    Dir            string   // directory containing package sources
    ImportPath     string   // import path of package in dir
    ImportComment  string   // path in import comment on package statement
    Name           string   // package name
    Doc            string   // package documentation string
    Target         string   // install path
    Shlib          string   // the shared library that contains this package (only set when -linkshared)
    Goroot         bool     // is this package in the Go root?
    Standard       bool     // is this package part of the standard Go library?
    Stale          bool     // would 'go install' do anything for this package?
    StaleReason    string   // explanation for Stale==true
    Root           string   // Go root or Go path dir containing this package
    ConflictDir    string   // this directory shadows Dir in $GOPATH
    BinaryOnly     bool     // binary-only package (no longer supported)
    ForTest        string   // package is only for use in named test
    Export         string   // file containing export data (when using -export)
    BuildID        string   // build ID of the compiled package (when using -export)
    Module         *Module  // info about package's containing module, if any (can be nil)
    Match          []string // command-line patterns matching this package
    DepOnly        bool     // package is only a dependency, not explicitly listed
    DefaultGODEBUG string  // default GODEBUG setting, for main packages

    // Source files
    GoFiles           []string   // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
    CgoFiles          []string   // .go source files that import "C"
    CompiledGoFiles   []string   // .go files presented to compiler (when using -compiled)
    IgnoredGoFiles    []string   // .go source files ignored due to build constraints
    IgnoredOtherFiles []string // non-.go source files ignored due to build constraints
    CFiles            []string   // .c source files
    CXXFiles          []string   // .cc, .cxx and .cpp source files
    MFiles            []string   // .m source files
    HFiles            []string   // .h, .hh, .hpp and .hxx source files
    FFiles            []string   // .f, .F, .for and .f90 Fortran source files
    SFiles            []string   // .s source files
    SwigFiles         []string   // .swig files
    SwigCXXFiles      []string   // .swigcxx files
    SysoFiles         []string   // .syso object files to add to archive
    TestGoFiles       []string   // _test.go files in package
    XTestGoFiles      []string   // _test.go files outside package

    // Embedded files
    EmbedPatterns      []string // //go:embed patterns
    EmbedFiles         []string // files matched by EmbedPatterns
    TestEmbedPatterns  []string // //go:embed patterns in TestGoFiles
    TestEmbedFiles     []string // files matched by TestEmbedPatterns
    XTestEmbedPatterns []string // //go:embed patterns in XTestGoFiles
    XTestEmbedFiles    []string // files matched by XTestEmbedPatterns

    // Cgo directives
    CgoCFLAGS    []string // cgo: flags for C compiler
    CgoCPPFLAGS  []string // cgo: flags for C preprocessor
    CgoCXXFLAGS  []string // cgo: flags for C++ compiler
    CgoFFLAGS    []string // cgo: flags for Fortran compiler
    CgoLDFLAGS   []string // cgo: flags for linker
    CgoPkgConfig []string // cgo: pkg-config names

    // Dependency information
    Imports      []string          // import paths used by this package
    ImportMap    map[string]string // map from source import to ImportPath (identity entries omitted)
    Deps         []string          // all (recursively) imported dependencies
    TestImports  []string          // imports from TestGoFiles
    XTestImports []string          // imports from XTestGoFiles

    // Error information
    Incomplete bool            // this package or a dependency has an error
    Error      *PackageError   // error loading package
    DepsErrors []*PackageError // errors loading dependencies
}

type PackageError struct {
    ImportStack   []string // shortest path from package named on command line to this one
    Pos           string   // position of error (if present, file:line:col)
    Err           string   // the error itself
}

إذا كان المُكرّر عليه هو وحدة، فسيُمرّر على شكل الهيكل التالي، جميع حقوله يمكن أيضاً استخدامها كمعاملات للقالب.

go
type Module struct {
    Path       string        // module path
    Query      string        // version query corresponding to this version
    Version    string        // module version
    Versions   []string      // available module versions
    Replace    *Module       // replaced by this module
    Time       *time.Time    // time version was created
    Update     *Module       // available update (with -u)
    Main       bool          // is this the main module?
    Indirect   bool          // module is only indirectly needed by main module
    Dir        string        // directory holding local copy of files, if any
    GoMod      string        // path to go.mod file describing module, if any
    GoVersion  string        // go version used in module
    Retracted  []string      // retraction information, if any (with -retracted or -u)
    Deprecated string        // deprecation message, if any (with -u)
    Error      *ModuleError  // error loading module
    Origin     any           // provenance of module
    Reuse      bool          // reuse of old module info is safe
}

type ModuleError struct {
    Err string // the error itself
}

عرض جميع الحزم

bash
$ go list -f "package {{.Dir}} {{.Name}}" ./...
package /golearn main
package /golearn/app cmd
package /golearn/cmd cmd
package /golearn/docs docs
package /golearn/tool tool
package /golearn/tool_test tool

عرض الوحدات

bash
$ go list -m -f "mod {{.Path}} {{.Version}} {{.GoVersion}} {{.GoMod}}"
mod golearn  1.21.3 /golearn/go.mod

mod

go mod هو أمر مخصص لإدارة وحدات go.

sh
$ go mod help
Go mod provides access to operations on modules.

Note that support for modules is built into all the go commands,
not just 'go mod'. For example, day-to-day adding, removing, upgrading,
and downgrading of dependencies should be done using 'go get'.
See 'go help modules' for an overview of module functionality.

Usage:

        go mod <command> [arguments]

The commands are:

        download    download modules to local cache
        edit        edit go.mod from tools or scripts
        graph       print module requirement graph
        init        initialize new module in current directory
        tidy        add missing and remove unused modules
        vendor      make vendored copy of dependencies
        verify      verify dependencies have expected content
        why         explain why packages or modules are needed

Use "go help mod <command>" for more information about a command.

له الأوامر الفرعية التالية

  • download: تنزيل جميع التبعيات المُشار إليها في ملف go.mod إلى الذاكرة المؤقتة المحلية
  • edit: تعديل ملف go.mod، الواجهة التي يوفرها سطر الأوامر مخصصة بشكل أساسي للأدوات أو النصوص البرمجية الأخرى لاستدعائها.
  • init: تهيئة مشروع gomod في الدليل الحالي
  • tidy: تنزيل التبعيات المفقودة، حذف التبعيات غير المستخدمة
  • graph: إخراج رسم التبعيات
  • verify: التحقق من التبعيات المحلية
  • why: شرح سبب الحاجة إلى هذه الوحدات
  • vendor: تصدير تبعيات المشروع إلى دليل vendor

init

sh
$ go help mod init
usage: go mod init [module-path]

أمر init يُستخدم لتهيئة مشروع gomod، معامله الوحيد هو مسار الوحدة، الذي سيكون الأساس للآخرين لتنزيل تبعياتك في المستقبل. قواعد تسميته عموماً هي

domain_name/user_name/repo_name

مثل أن الناس عموماً يضعون مشاريعهم على github، لذا يمكن أن يكون

github.com/jack/gotour

لا يُنصح باستخدام رموز خاصة في مسار الوحدة. إليك حالة استخدام

sh
$ mkdir gotour

$ cd gotour

$ go mod init "github.com/jack/gotour"
go: creating new go.mod: module github.com/jack/gotour

tidy

$ go help mod tidy
usage: go mod tidy [-e] [-v] [-x] [-go=version] [-compat=version]

أمر tidy سيُنظّف التبعيات غير المفيدة في go.mod، أي التبعيات غير المُشار إليها، وسيُنزّل التبعيات المُشار إليها لكن غير الموجودة. يدعم المعاملات التالية

  • -v، إخراج تبعيات الوحدات المحذوفة
  • -e، إذا حدث خطأ أثناء العملية تجاهله واستمر في التنفيذ
  • -x، إخراج عملية التنفيذ
  • -go=version، تحديث إصدار go في ملف go.mod
  • -compact=version، الاحتفاظ بأي مجاميع اختبارية إضافية مطلوبة من إصدار Go الرئيسي المحدد لتحميل رسم الوحدة بنجاح، وإذا كان إصدار go سيحمّل أي حزم مستوردة من إصدارات وحدة مختلفة، فسيؤدي ذلك إلى خطأ في tidy. عادةً ما يُستخدم هذا المعامل نادراً، وعموماً يحدث خطأ فقط عند تغيير الإصدار، يمكنك الذهاب إلى stackoverflow لرؤية هذا الإجابة go modules - go mod tidy error message: "but go 1.16 would select" - Stack Overflow

انظر مثال استخدام

sh
$ go mod tidy -v
unused github.com/246859/containers
unused github.com/246859/river
unused github.com/Jleagle/steam-go
unused github.com/Jleagle/unmarshal-go
unused github.com/KyleBanks/depth
unused github.com/Microsoft/go-winio
unused github.com/PuerkitoBio/purell
unused github.com/PuerkitoBio/urlesc
unused github.com/andeya/ameda
unused github.com/andeya/goutil
unused github.com/asaskevich/govalidator
unused github.com/buger/jsonparser
unused github.com/bwmarrin/snowflake
unused github.com/bytedance/go-tagexpr/v2
unused github.com/bytedance/sonic
unused github.com/cespare/xxhash/v2
unused github.com/chenzhuoyu/base64x
......

download

sh
$ go help mod download
usage: go mod download [-x] [-json] [-reuse=old.json] [modules]

اسم أمر download وإن كان يعني التنزيل عند الترجمة، لكنه فقط يُنزّل التبعيات إلى الذاكرة المؤقتة المحلية للملفات، ولا يُعدّل ملف go.mod، وظيفته هي التنزيل المسبق للتبعيات إلى الذاكرة المؤقتة المحلية للملفات، إذا كنت تريد تنزيل تبعية معينة، يُنصح باستخدام go get أو go mod tidy.

فيما يلي بعض أمثلة الاستخدام

sh
$ go mod download -x gorm.io/gorm
# get https://goproxy.cn/gorm.io/gorm/@v/list
# get https://goproxy.cn/gorm.io/gorm/@v/list: 200 OK (0.084s)

إذا تم تنفيذه بدون أي معاملات، سيُنزّل جميع التبعيات الموجودة في ملف go.mod وغير الموجودة في الذاكرة المؤقتة المحلية للتبعيات، إذا لم يكن هناك ما يحتاج للتنزيل سيُخرج

go: no module dependencies to download

edit

sh
$ go help mod edit
usage: go mod edit [editing flags] [-fmt|-print|-json] [go.mod]

edit هو واجهة سطر أوامر لتعديل ملف go.mod، عادةً ما تكون مخصصة للاستخدام من قبل برامج أخرى، بعض المحررات IDE توفر دعم gomod باستخدام هذه الأوامر. يدعم المعاملات التالية

  • -module، تعديل مسار الوحدة
  • -go=version، تعديل إصدار go المتوقع
  • -require=path@version، إضافة تبعية جديدة
  • -droprequire=path@version، حذف تبعية
  • -exclude=path@version، إضافة تبعية مستثناة
  • -dropexclude=path@version، حذف تبعية مستثناة
  • -replace=old@version=new@version، إضافة تبعية بديلة
  • -dropreplace=old@version، حذف تبعية بديلة
  • -retract=version، إضافة عنصر تراجع عن إصدار
  • -dropretract=version، حذف عنصر تراجع عن إصدار

هناك أيضاً بعض المعاملات الأخرى للعرض

  • -print، إخراج محتوى الملف
  • -json، الإخراج بصيغة json

مثل المثال التالي

$ go mod edit -print
module golearn

go 1.21.3

require (
        github.com/dstgo/task v1.2.0
        github.com/spf13/cast v1.5.1
        github.com/swaggo/swag v1.16.2
        golang.org/x/net v0.19.0
        gorm.io/gorm v1.25.5
)

graph

sh
$ go help mod graph
usage: go mod graph [-go=version] [-x]

أمر graph سيُخرج رسم التبعيات في المشروع الحالي، قابلية قراءته ضعيفة، ومعظم الأوقات ليس للبشر قراءته، نتائجه عادةً ما تُعالج ثم تُعرض بشكل مرئي. كل سطر هو تبعية، تنسيقه كالتالي

المرجع المُشار إليه المُشار إليه

مثل

golearn go@1.21.3

يدعم أيضاً معاملين

  • -go=version، تحميل رسم التبعيات باستخدام إصدار go المحدد، قيمته لا يمكن أن تكون أقل من الإصدار في ملف go.mod.
  • -x، عرض الأوامر المنفذة أثناء العملية.

انظر مثال استخدام بسيط

sh
$ go mod graph
golearn github.com/246859/containers@v0.0.1
golearn github.com/246859/river@v0.1.0
golearn github.com/Jleagle/steam-go@v0.0.0-20230725082712-1053b441b1f2
golearn github.com/Jleagle/unmarshal-go@v0.0.0-20210227002040-694f544f9265
golearn github.com/KyleBanks/depth@v1.2.1
golearn github.com/Microsoft/go-winio@v0.6.1
golearn github.com/PuerkitoBio/purell@v1.1.1
golearn github.com/PuerkitoBio/urlesc@v0.0.0-20170810143723-de5bf2ad4578
golearn github.com/andeya/ameda@v1.5.3
golearn github.com/andeya/goutil@v1.0.1
golearn github.com/asaskevich/govalidator@v0.0.0-20230301143203-a9d515a09cc2
golearn github.com/buger/jsonparser@v1.1.1
golearn github.com/bwmarrin/snowflake@v0.3.0
golearn github.com/bytedance/go-tagexpr/v2@v2.9.11
......

vendor

sh
$ go help mod vendor
usage: go mod vendor [-e] [-v] [-o outdir]

vendor هو حل بديل لـ gopath في الأيام الأولى قبل إصدار gomod، كل مشروع go كان لديه دليل vendor، يُخزن كل تبعية المشروع بشكل منفصل بتنسيق domain/user/project، مثل node_module المنتفخ في nodeJs المجاور، كل مشروع تبعياته مُخزنة بشكل منفصل، طريقة إدارة التبعيات هذه تبدو غبية الآن، لكن في ذلك الوقت لم يكن هناك حل أفضل، السبب في الاحتفاظ بـ vendor هو أن go يلتزم بوعد التوافق مع الإصدارات السابقة، بعض المشاريع القديمة بما فيها الكود المصدري لـ go قد لا تزال تستخدم vendor.

بالعودة إلى الموضوع، vendor هو أمر فرعي لـ go mod، يمكنه تصدير التبعيات العالمية المُشار إليها في الوحدة الحالية إلى دليل vendor.

sh
$ go mod vendor -h
usage: go mod vendor [-e] [-v] [-o outdir]
Run 'go help mod vendor' for details.

له المعاملات التالية

  • -o: تحديد مسار مجلد الإخراج
  • -v: إخراج كل تبعية
  • -e: الاستمرار عند حدوث خطأ

انظر مثالاً، أولاً استخدم go list -m all لعرض التبعيات المُشار إليها في المشروع الحالي

sh
$ go list -m all
github.com/dstgo/task
github.com/davecgh/go-spew v1.1.1
github.com/pkg/errors v0.9.1
github.com/pmezard/go-difflib v1.0.0
github.com/stretchr/objx v0.5.0
github.com/stretchr/testify v1.8.4
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405
gopkg.in/yaml.v3 v3.0.1

التصدير إلى دليل vendor الحالي

sh
$ go mod vendor -v -e -o vendor
# github.com/davecgh/go-spew v1.1.1
## explicit
github.com/davecgh/go-spew/spew
# github.com/pkg/errors v0.9.1
## explicit
github.com/pkg/errors
# github.com/pmezard/go-difflib v1.0.0
## explicit
github.com/pmezard/go-difflib/difflib
# github.com/stretchr/testify v1.8.4
## explicit; go 1.20
github.com/stretchr/testify/assert
# gopkg.in/yaml.v3 v3.0.1
## explicit
gopkg.in/yaml.v3

هيكل الدليل بعد التصدير كالتالي

└─vendor
    ├─github.com
    │  ├─davecgh
    │  │  └─go-spew
    │  │      └─spew
    │  ├─pkg
    │  │  └─errors
    │  ├─pmezard
    │  │  └─go-difflib
    │  │      └─difflib
    │  └─stretchr
    │      └─testify
    │          └─assert
    └─gopkg.in
    |    └─yaml.v3
    |
    |--modules.txt

من بينها modules.txt هو ملف يصف جميع التبعيات، مشابه لـ go.mod الحالي.

verify

sh
$ go help mod verify
usage: go mod verify

هذا الأمر سيتحقق مما إذا كانت تبعيات المشروع قد تم تعديلها منذ تنزيلها إلى المحلي. مثل، إذا لم تكن هناك مشاكل سيُخرج all modules verified

sh
$ go mod verify
all modules verified

وإلا سيُبلغ عن أين حدث التغيير، وينتهي الأمر بحالة غير طبيعية. مثل

sh
$ go mod verify
gorm.io/gorm v1.25.5: dir has been modified (/go/mod/libs/gorm.io/gorm@v1.25.5)

why

$ go help mod why
usage: go mod why [-m] [-vendor] packages...

يشرح سبب الحاجة إلى هذه الحزمة، في الواقع يُخرج رسم التبعيات المتعلق بها. مثل

sh
$ go mod why gorm.io/gorm
# gorm.io/gorm
golearn
gorm.io/gorm

الافتراضي هو فقط تحليل الاستيراد من main، إضافة المعامل -m يمكن تحليل حالة استيراد كل حزمة.

work

أمر work هو أداة تطوير محلية لإدارة وحدات go المتعددة

bash
$ go work help
Work provides access to operations on workspaces.

Note that support for workspaces is built into many other commands, not
just 'go work'.

The commands are:

        edit        edit go.work from tools or scripts
        init        initialize workspace file
        sync        sync workspace build list to modules
        use         add modules to workspace file
        vendor      make vendored copy of dependencies

Use "go help work <command>" for more information about a command.

init

الأمر الفرعي init يُستخدم لتهيئة مساحة عمل، هذا الأمر سيُنشئ ملفاً اسمه go.work

bash
$ go work init -h
usage: go work init [moddirs]
Run 'go help work init' for details.

يقبل المعامل [moddirs] لتحديد أدلة الوحدات التي ستُدرج في الإدارة، مثل

bash
$ go work init ./service ./api

use

الأمر الفرعي use يُستخدم لإضافة أدلة الوحدات المُدرجة في الإدارة إلى go.work

bash
$ go help work use
usage: go work use [-r] [moddirs]

Use provides a command-line interface for adding
directories, optionally recursively, to a go.work file.

يقبل [moddirs] كمعامل، وهناك أيضاً -r يعني البحث المتكرر عن الوحدات في مسار [moddirs]، مثل

bash
$ go work use -r ./oss-api ./multi_modules

edit

وظيفة الأمر الفرعي edit هي نفسها go mod edit، كلاهما يترك واجهة سطر أوامر للأدوات والنصوص البرمجية الأخرى للعمل عليها.

$ go help work edit
usage: go work edit [editing flags] [go.work]

Edit provides a command-line interface for editing go.work,
for use primarily by tools or scripts. It only reads go.work;
it does not look up information about the modules involved.
If no file is specified, Edit looks for a go.work file in the current
directory and its parent directories

المعاملات كالتالي

  • -fmt، تنسيق ملف go.work

  • -use، -dropuse، إضافة وإزالة مسارات الوحدات

  • -replace=old[@v]=new[@v]، -dropreplace=old[@v]=new[@v]، لإضافة وإزالة الوحدات المراد استبدالها

  • -go، -toolchain=name، تحديد إصدار go، وتحديد سلسلة الأدوات المراد استخدامها

  • -print، طباعة التعديلات النهائية، لا تُكتب إلى الملف

  • -json، الإخراج بصيغة json، لا يمكن أن يتواجد مع -print، النوع المقابل كالتالي

    go
    type GoWork struct {
            Go        string
            Toolchain string
            Use       []Use
            Replace   []Replace
    }
    
    type Use struct {
            DiskPath   string
            ModulePath string
    }
    
    type Replace struct {
            Old Module
            New Module
    }
    
    type Module struct {
            Path    string
            Version string
    }

بعض أمثلة الاستخدام كالتالي، تنسيق الإخراج

bash
$ go work edit -fmt -print
go 1.22.0

use (
        ./ab/cd
        ./auth
        ./user
)

إخراج json

bash
$ go work edit -fmt -json
{
        "Go": "1.22.0",
        "Use": [
                {
                        "DiskPath": "./ab/cd"
                },
                {
                        "DiskPath": "./auth"
                },
                {
                        "DiskPath": "./user"
                }
        ],
        "Replace": null
}

sync

الأمر الفرعي sync يُستخدم لإعادة قائمة بناء workspace في go.work إلى الوحدات المختلفة في workspace.

bash
$ go help work sync
usage: go work sync

Sync syncs the workspace's build list back to the
workspace's modules

هذه العملية تحدث بشكل رئيسي بعد اكتمال التطوير المحلي، عندما اكتملت أعمال النشر لكل وحدة، في هذه الحالة استخدام sync، سيُحدّث تبعيات go.mod لجميع الوحدات في worksapce بناءً على علاقات التبعية بين الوحدات، وبذلك لا نحتاج للتحديث يدوياً.

vendor

أمر vendor سيُنشئ نسخة من جميع مكتبات التبعيات للوحدات في workspace في دليل vendor.

bash
$ go work help vendor
usage: go work vendor [-e] [-v] [-o outdir]

وظيفته نفس go mod vendor، لن نقدم الكثير من التفاصيل.

vet

أمر vet هو أداة فحص أخطاء ثابتة للكود المصدري للغة go، مثل أدوات lint في اللغات الأخرى، مثل Eslint.

sh
$ go vet -h
usage: go vet [build flags] [-vettool prog] [vet flags] [packages]
Run 'go help vet' for details.
Run 'go tool vet help' for a full list of flags and analyzers.
Run 'go tool vet -help' for an overview.

لنبدأ بمثال بسيط، لدينا الكود المصدري التالي

sh
$ cat main.go
package main

import "fmt"

func main(){
        fmt.Println("hello world!"
}

تنفيذ go vet بدون أي معاملات في نفس الدليل

sh
$ go vet
vet: ./main.go:6:28: missing ',' before newline in argument list (and 1 more errors)

vet سيُبلغ عن أي ملف وأي سطر به مشكلة. يدعم علامات البناء كمعاملات، مثل -n و -x، ويدعم الحزم، المجلدات، أسماء الملفات كمعاملات.

sh
$ go vet .
$ go vet main.go
$ go vet ./cmd
$ go vet runtime

من خلال الأمر التالي لعرض معاملاته وتفسيراته الأكثر تفصيلاً.

sh
$ go tool vet help
vet is a tool for static analysis of Go programs.

vet examines Go source code and reports suspicious constructs,
such as Printf calls whose arguments do not align with the format
string. It uses heuristics that do not guarantee all reports are
genuine problems, but it can find errors not caught by the compilers.

Registered analyzers:

    asmdecl      report mismatches between assembly files and Go declarations
    assign       check for useless assignments
    atomic       check for common mistakes using the sync/atomic package
    bools        check for common mistakes involving boolean operators
    buildtag     check //go:build and // +build directives
    ......

أمر go tool vet لا يمكن استخدامه مباشرة لفحص الكود، يجب استخدام go vet. المعامل [vet flag] في go vet يدعم إعداد محلل الكود، القيم المتاحة كالتالي

asmdecl      检查汇编文件是否与go声明不匹配
assign       检查是否有无用的变量
atomic       检查使用sync/atomic时是否破坏了原子性
bools        检查是否错误使用逻辑运算符
buildtag     检查build tag
cgocall      检查违反cgao指针传递规则的行为
composites   检查未初始化的复合结构,比如map,chan
copylocks    检查是否发生了锁的值复制
directive    检查go工具链指令
errorsas     检查是否向errors.As传递非指针类型或非error类型的值
framepointer 检查编译优化后的汇编代码是否在保存帧指针之前对其进行清除
httpresponse 检查是否错误使用httpresponse
ifaceassert  检查接口到接口的类型断言
loopclosure  循环变量的引用问题
lostcancel   context.WithCancel没有使用cancel函数
nilfunc      检查函数和nil之间是否存在无用的比较
printf       检查printf的格式化参数是否正确
shift        检查是否有等于或超过整数宽度的移位
sigchanyzer  检查无缓冲的chan os.Signal
slog         检查不合法的结构化日志调用
stdmethods   检查已知接口方法的签名是否正确
stringintconv 检查字符串整型转换
structtag    检查结构体tag是否正确
testinggoroutine 检查是否在测试中使用协程调用testing.Fatal
tests        检查测试和示例的常见错误用法
timeformat   使用(time.Time).Format 或 time.Parse的时间格式是否正确
unmarshal    向unmarshal传递非指针或非接口类型
unreachable  检查不可到达的代码
unsafeptr    检查uintptr到unsafe.Pointer不正确转换
unusedresult 检查未使用的函数返回值

جميعها محللات تحلل نقطة معينة، مثل محلل timeformat يفحص ما إذا كان تنسيق استدعاء time.Format يتوافق مع الصيغة الصحيحة. افتراضياً سيتم تفعيل جميع المحللات المذكورة أعلاه، لتفعيل محلل منفصل يمكن استخدام التنسيق التالي

sh
$ go vet -timeformat main.go

للتعطيل المنفرد

sh
$ go vet -timeformat=false main.go

الكود المصدري لهذه المحللات موجود في cmd/vendor/golang.org/x/tools/go/analysis/passes، كل محلل هو فخ شائع في لغة go، لذا يُنصح بشدة باستخدام أمر vet لفحص الكود الخاص بك. بالإضافة إلى هؤلاء، يدعم أيضاً بعض علامات المعاملات الأخرى

  • -V، فقط طباعة الإصدار ثم الخروج
  • -json، الإخراج بصيغة json
  • -c=n، عرض عدد محدد من أسطر التعارض في السياق (يبدو أنه لا فائدة منه)

هناك أيضاً بعض المحللات الخارجية، مثل shadows، المسؤولة عن اكتشاف مشاكل إخفاء المتغيرات المسماة بمتغيرات قصيرة، وبما أنها خارجية فتحتاج للتنزيل باستخدام go install

sh
$ go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow@latest

تنسيق الاستخدام كالتالي

sh
$ go vet -vettool=$(which shadow)

test

sh
$ go test -h
usage: go test [build/test flags] [packages] [build/test flags & test binary flags]
Run 'go help test' and 'go help testflag' for details.

أمر test هو أمر يوفر وظيفة الاختبار في سلسلة أدوات لغة go، هذه الوظيفة مهمة جداً، للبرامج، الاختبار الكامل ضروري. هنا مجرد مقدمة بسيطة لكيفية استخدام أمر test، لمعرفة المزيد عن الاختبار، انتقل إلى: الاختبار

بالإضافة إلى دعم معاملات التجميع لأمر build، يدعم test أيضاً المعاملات التالية

  • -args، معاملات مدخل البرنامج
  • -c، تجميع الملف الثنائي للاختبار للحزمة الحالية إلى الدليل الحالي لكن بدون تنفيذه، يُسمى pkg.test
  • -exec، تنفيذ بعض الأوامر الأخرى قبل بدء الاختبار
  • -json، نمط إخراج الاختبار يصبح json
  • -o، تحديد اسم مسار الملف الثنائي للاختبار

يدعم أيضاً العديد من testflag ، استخدم أمر help لعرض جميع testflag

sh
$ go help testflag
`go test` 命令既接受作用于 `go test` 本身的标志,
也接受作用于生成的测试二进制文件的标志。

`go test` 命令识别以下标志,并用于控制任何测试的执行:
        -bench regexp
        -benchtime t
        -count n
    ......

مقدمة لبعض المعاملات شائعة الاستخدام

  • -v، إخراج نتيجة اختبار كل حالة.
  • -timeout duration، مهلة تنفيذ الاختبار
  • -skip regexp، تخطي حالات الاختبار المحددة
  • -short، تقصير وقت تشغيل حالات الاختبار التي تستغرق وقتاً طويلاً
  • -shuffle، خلط ترتيب تنفيذ جميع حالات الاختبار
  • -run regexp، تشغيل حالات الاختبار المحددة
  • -list regexp،列出每一个测试用例
  • -cpu 1,2,4، تحديد عدد cpu
  • -count n، تحديد عدد مرات تنفيذ كل حالة اختبار

أبسط استخدام هو، بدون أي معاملات، سيُنفذ جميع حالات الاختبار في الحزمة الحالية، ويُخرج النتائج.

sh
$ ls *_test.go
hello_test.go

$ go test
PASS
ok      golearn 0.522s

تحديد ملف اختبار معين

sh
$ go test hello_test.go
ok      command-line-arguments  0.041s

إضافة المعامل -v يمكن عرض إخراج أكثر تفصيلاً، إنه شائع جداً.

sh
$ go test -v hello_test.go
=== RUN   TestHello
    hello_test.go:6: hello world!
--- PASS: TestHello (0.00s)
PASS
ok      command-line-arguments  0.041s

تحديد حالة اختبار معينة

sh
$ go test -v -run TestHello
=== RUN   TestHello
    hello_test.go:6: hello world!
--- PASS: TestHello (0.00s)
PASS
ok      golearn 0.028s

أثناء الاختبار، أمر test ينقسم إلى وضعين، أولاً وضع المجلد، عند تنفيذ أمر test بدون معامل package، سيُنفذ الاختبار في وضع المجلد، مثل الأوامر التالية

sh
$ go test
$ go test -v

في هذا الوضع، يتم تعطيل ذاكرة الاختبار المؤقتة. الوضع الآخر هو وضع القائمة، عندما لا يكون معامل package فارغاً، سيتم الاختبار في وضع القائمة، والفرق بينه وبين السابق هو ما إذا كان سيتم تفعيل ذاكرة الاختبار المؤقتة. مثل التالي

sh
$ go test -v .
$ go test -v ./...
$ go test .
$ go test -v net/http

في وضع القائمة، سيقوم go بتجميع ملفات اختبار كل حزمة في الحزمة المحددة إلى ملفات ثنائية وتنفيذها، لتجنب تشغيل الاختبارات بشكل متكرر، سيقوم go افتراضياً بتخزين النتائج مؤقتاً، ولن يعيد التجميع عند التشغيل للمرة الثانية. استخدام المعاملات التالية سيُفعّل الذاكرة المؤقتة افتراضياً

  • -benchtime
  • -cpu
  • -list
  • -parallel
  • -run
  • short
  • -timeout
  • -failfast
  • -v

استخدام معاملات أخرى غير هذه يمكن إيقاف الذاكرة المؤقتة، الطريقة التي يدعو إليها المسؤول هي استخدام -count=1 لتعطيل الذاكرة المؤقتة. مثل

sh
$ go test -v -count=1 ./...

التوجيهات

بخلاف الأوامر، توجيهات go موجودة في الملفات المصدرية بشكل ثابت، ولها اسم آخر أكثر اصطلاحية: توجيهات الترجمة (pragma directives).

المجمع والرابط سيغيران سلوكهما بسببها للوصول إلى التحكم في التجميع، يشبه إلى حد ما الماكرو في لغة C، وبالطبع ليست كل التوجيهات تُستخدم للتأثير على التجميع، بعضها لوظائف أخرى، مثل توجيه generate يُستخدم عادةً لوظيفة توليد الكود. هذه التوجيهات موجودة عادةً على شكل تعليقات، وبادئتها //go:، ولا يمكن أن تحتوي على أي مسافات في المنتصف، مثل توجيه //go:generate. جميع أنواع التوجيهات تنقسم إلى نوعين

  • توجيهات وظيفية، هذه توجيهات وظيفية يوفرها go يمكن استخدامها بحرية، مثل generate، embed، build.
  • توجيهات المجمع، هذه التوجيهات يجب استخدامها بحذر، الاستخدام العشوائي قد يؤدي إلى نتائج غير متوقعة.

باستثناء التوجيهات الوظيفية، معظم التوجيهات يمكنها فقط العمل على توقيعات الدوال. للتوجيهات المتعلقة بالمجمع يمكنك تنفيذ الأمر go doc compile لعرض توجيهاته. لجميع التوجيهات، يمكن العثور على معلومات عنها في cmd/compile/internal/ir/node.go: 440.

generate

sh
$ go help generate
usage: go generate [-run regexp] [-n] [-v] [-x] [build flags] [file.go... | packages]

توجيه generate كما يوحي اسمه له علاقة بالتوليد، عادةً وظيفته هي تنفيذ الأوامر التي تولد الكود وتُحدّث الكود المصدري، لكن في الواقع يمكنه تنفيذ أي أمر. بالإضافة إلى ذلك، توجيه generate يختلف عن التوجيهات الأخرى، له أمر مخصص يمكن استخدامه لتنفيذ جميع توجيهات generate الموجودة في الملفات المصدرية. يمكنه أخذ اسم ملف أو اسم حزمة كمعامل إدخال للإشارة إلى الملفات التي سيتم تنفيذ توجيهات generate فيها، فيما يلي معاملاته الأخرى.

  • -run=regex ، تشغيل توجيه generate المحدد
  • -skip=regex، تخطي توجيه generate المحدد
  • -n، طباعة الأوامر التي سيتم تنفيذها
  • -x، طباعة الأوامر المنفذة أثناء العملية
  • -v، إخراج الملفات المُعالجة

بالإضافة إلى ذلك، الأوامر المنفذة في توجيه generate تدعم أيضاً المعاملات المدمجة التالية

  • $GOARCH، بنية cpu
  • $GOOS، نظام التشغيل
  • $GOFILE، اسم الملف
  • $GOLINE، رقم السطر
  • $GOPACKAGE، اسم الحزمة
  • $GOROOT، go root
  • $DOLLAR، علامة الدولار
  • $PATH، متغير بيئة path

انظر مثالاً، لا يوجد كود فقط سطر تعليق واحد

go
package main

//go:generate echo "hello world!"

تنفيذ الأمر

$ go generate .
hello world!

هذا المثال هو تنفيذ أمر go

go
package main

//go:generate go version

تنفيذ الأمر

sh
$ go generate .
go version go1.21.3 windows/amd64

توجيه generate يمكن استخدامه لتنفيذ أي أمر، مثل swagger لتوليد وثائق API، أو Wire لتوليد كود IOC. لكن هذا التوجيه غير مناسب لتنفيذ أوامر معقدة جداً، مناسب لتنفيذ أوامر قصيرة، إذا كانت هناك احتياجات معقدة يمكن استخدام نصوص برمجية أو makefile كبديل.

embed

توجيه embed أُضيف في 1.16، وظيفته هي يمكنه حزم الملفات الثابتة معاً في الملف الثنائي، مثل قوالب HTML. تنسيقه كالتالي

go
//go:embed pattern

pattern يمكن أن يكون تعبيراً نمطياً glob، أو مجلداً أو ملفاً محدداً. انظر مثالاً

go
package main

import "embed"

//go:embed *
var static embed.FS

توجيه embed يتطلب أن يكون فوق متغير عام من نوع embed.Fs، انتبه يجب أن يكون متغيراً عاماً، ويجب استيراد حزمة embed لاستخدامه، في هذا المثال، * يعني أن جميع الملفات في المجلد الحالي سيتم حزمها في الملف الثنائي، لكنه لن يسمح بوجود مجلدات تبدأ بـ ..

المثال التالي يوضح قراءة المحتوى من الملفات المدمجة

go
package main

import (
  "embed"
  "fmt"
)

//go:embed *.txt
var static embed.FS

func main() {
  bytes, err := static.ReadFile("hello.txt")
  if err != nil {
    panic(err)
  }
  fmt.Println(string(bytes))
}

له ثلاث طرق فقط، استخدامه مشابه جداً لنظام الملفات المعتاد، وبما أنه يُنفذ واجهة io/Fs، يمكن أيضاً تمريره ككائن Fs.

go
func (f FS) Open(name string) (fs.File, error)
func (f FS) ReadFile(name string) ([]byte, error)
func (f FS) ReadDir(name string) ([]fs.DirEntry, error)

المثال التالي يوضح تضمين ملف html من خلال توجيه embed، والوصول إليه من خلال خدمة http.

go
package main

import (
  "embed"
  "net/http"
)

//go:embed index.html
var htmlFs embed.FS

func main() {
  http.Handle("/", http.FileServer(http.FS(htmlFs)))
  http.ListenAndServe(":8080", http.DefaultServeMux)
}

نتيجة الوصول كالتالي

sh
$ curl -s -GET 127.0.0.1:8080
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>hello world!</title>
</head>
<body>
<H1>Hello World!</H1>
<script>
    alert("hello world!");
</script>
</body>
</html>

توجيه embed يدعم أيضاً أن يكون نوع المتغير العام []byte، مثل المثال التالي

go
package main

import (
  _ "embed"
  "net/http"
)

//go:embed index.html
var rawdata []byte

func main() {
  http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
    writer.Write(rawdata)
  })
  http.ListenAndServe(":8080", http.DefaultServeMux)
}

التأثير الذي يُحققه مشابه جداً للمثال السابق.

sh
$ curl -s -GET 127.0.0.1:8080
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>hello world!</title>
</head>
<body>
<H1>Hello World!</H1>
<script>
    alert("hello world!");
</script>
</body>
</html>

build

في قسم build-التحكم في التجميع، تحدثنا عن استخدام توجيه // +build للتحكم في سلوك التجميع. وتوجيه //go:build أُضيف في 1.17، والهدف هو استبدال التوجيه السابق، لكن الآن في 1.21 لم يتم الاستبدال بعد، ويُقدر أنهما سيتواجدان معاً في المستقبل، حول هذا التوجيه الجديد، الوثائق الرسمية لديها أيضاً مقدمة: build constraints. وظيفته نفس السابق، لكن صياغته أكثر صرامة، ويدعم التعبيرات المنطقية، انظر مثالاً

go
//go:build (linux && 386) || (darwin && !cgo)

package pkg_name

هذه الطريقة أكثر قابلية للقراءة من الطريقة القديمة.

line

توجيه line سيؤثر على رقم السطر التالي له، ورقم العمود، واسم الملف، وظيفته تقتصر على هذا، معظم الأوقات قد يُستخدم لتصحيح الأخطاء. مثل عند حدوث خطأ، سيغير المعلومات التي يُخرجها المجمع.

go
package main

var a undefinedType

func main() {

}

في الحالة الطبيعية، سيُخرج المجمع

.\main.go:3:7: undefined: undefinedType

لكن إذا استُخدم توجيه line، فسيكون مختلفاً

go
package main

//line abc.go:10:100
var a undefinedType

func main() {

}

فسيكون إخراجه

abc.go:10:106: undefined: undefinedType

وبسبب الأسباب التاريخية، توجيه line هو أيضاً التوجيه الوحيد الذي يختلف استخدامه عن التوجيهات الأخرى. تنسيقه هو

go
//line filename:line:column

يمكنك أن ترى أنه لا يحتاج إلى go: كبادئة.

linkname

هذا التوجيه يمكن استخدامه لربط دوال أو متغيرات عامة من حزم أخرى، حتى لو كانت أنواعاً خاصة، هذه العملية تحدث كثيراً في المكتبة القياسية وخاصة runtime، هناك بعض الدوال ليس لها جسم دالة يتم تنفيذها بهذه الطريقة، والجزء الآخر من الدوال التي لها جسم دالة فارغ يتم تنفيذها بالتجميع. لنرَ استخدامه، تنسيق الاستخدام كالتالي

go
//go:linkname اسم نوع الربط النوع المرتبط

وقبل الاستخدام، يجب استيراد حزمة unsafe. انظر مثالاً بسيطاً لربط نوع خاص في المكتبة القياسية

go
import (
  "fmt"
  "unsafe"
)

//go:linkname memhash runtime.memhash
func memhash(p unsafe.Pointer, h, s uintptr) uintptr

func MemHash(data []byte) uint64 {
  ptr := unsafe.Pointer(unsafe.SliceData(data))
  return uint64(memhash(ptr, 0, uintptr(len(data))))
}

func main() {
  fmt.Println(MemHash([]byte("hello")))
}

الإخراج

15395306441938000233

يربط الدالة الخاصة runtime.memhash بالدالة التي أعلنا عنها بأنفسنا، هذه الدالة ليس لها جسم دالة فقط توقيع، وتلعب دور حامل فقط. وظيفة memhash هي إعطاء مؤشر، وبذرة تجزئة، وإزاحة ذاكرة، وحساب قيمة التجزئة بناءً على الذاكرة. عملية الربط هذه تتم أثناء التجميع،

إذا لم تكن مكتبة قياسية، فالوضع مختلف قليلاً، مثل وجود دالة test في حزمة example، قبل الربط يجب أولاً استيراد هذه الحزمة بشكل مجهول.

go
package example

// نوع خاص، لا يمكن الوصول إليه من الخارج.
func test() string {
  return "a"
}
go
package main

import (
  "fmt"
  _ "golearn/example"
  _ "unsafe"
)

//go:linkname test golearn/example.test
func test() string

func main() {
  fmt.Println(test())
}

الإخراج

a

يمكنك أن ترى أن الربط قد نجح، هذه الطريقة يمكنها تجاوز نظام وحدات go والقيام بما تريد، لكن لا يُنصح باستخدامها على نطاق واسع، إلا إذا كنت تعرف ما تفعله.

noinline

توجيه noinline يشير إلى أن دالة ممنوعة من تحسين الدمج، حتى لو كانت بسيطة جداً. انظر مثالاً بسيطاً

go
package main

func val() string {
  return "val"
}

func main() {
  var c = val()
  _ = c
}

val دالة بسيطة جداً، وظيفتها هي إرجاع حرفي سلسلة، وبسبب بساطتها الشديدة وأن نتيجتها يمكن التنبؤ بها دائماً، فأثناء التجميع سيتم تحسينها إلى الشكل التالي

go
package main

func main() {
  var c = "val"
  _ = c
}

لنرَ شكلها المُجمّع، يمكننا أن نرى أنه لم يتم العثور على استدعاء دالة val.

TEXT    main.val(SB), NOSPLIT|NOFRAME|ABIInternal, $0-0
FUNCDATA        $0, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
FUNCDATA        $1, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
LEAQ    go:string."val"(SB), AX
MOVL    $3, BX

FUNCDATA        $0, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
RET

الآن لنضف توجيه noinline

go
package main

//go:noinline
func val() string {
  return "val"
}

func main() {
  var c = val()
  _ = c
}

لنرَ شكلها المُجمّع مرة أخرى

CMPQ    SP, 16(R14)
PCDATA  $0, $-2
JLS     17
PCDATA  $0, $-1
PUSHQ   BP
MOVQ    SP, BP
FUNCDATA        $0, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
FUNCDATA        $1, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
PCDATA  $1, $0
CALL    main.val(SB)
POPQ    BP
RET

هذه المرة يمكننا أن نرى بوضوح استدعاء main.val، وهذا بالضبط ما يفعله توجيه noinline، منع المجمع من دمج الدوال أثناء التحسين.

nospilit

توجيه nospilit وظيفته هي تخطي فحص تجاوز المكدس. بما أن نموذج الجدولة المتزامنة في go هو جدولة استباقية، إذا كانت دالة ستشغل كوداً منخفض المستوى جداً، فلا يُناسب أن تُستبعد goroutines الأخرى عند استدعاء هذه الدالة، يمكن استخدام هذا التوجيه للإشارة إلى ذلك.

go
//go:nosplit
func nospilitFn()

بعد استخدام هذا التوجيه، لن يتم إجراء زيادة المكدس.

noescape

noescape، من اسمه يمكنك بسهولة تخمين أنه له علاقة بالهروب، وظيفته هي الإشارة إلى أن الدالة الحالية لن تحدث فيها سلوكيات هروب للذاكرة، وبعد انتهاء التنفيذ سيتم استعادة جميع الموارد، ويجب أن يكون لهذه الدالة فقط توقيع وليس جسم دالة، في هذه الحالة عموماً يتم تنفيذ الدالة بالتجميع.

مثل memhash المستخدم سابقاً سيستخدم هذا التوجيه

go
//go:noescape
//go:linkname memhash runtime.memhash
func memhash(p unsafe.Pointer, h, s uintptr) uintptr

بهذه الطريقة، لن يقوم المجمع بإجراء تحليل الهروب عليها، بشرط أن تضمن أنها لن تحدث هروباً، إذا حدث، فلا يُعرف ما ستكون العواقب.

uintptrescapes

توجيه uintptrescapes يشير إلى أن معاملات النوع uinptr في هذه الدالة تم تحويلها إلى قيم مؤشر وهربت إلى الكومة، ويجب أن تبقى حية. هذا التوجيه يُستخدم عموماً لبعض استدعاءات النظام منخفضة المستوى، معظم الحالات لا تحتاج لمعرفته.

go
//go:uintptrescapes
func nospilit(ptr uintptr) uintptr

في السابق كان يجب أن يكون هناك توجيه notinheaps للإشارة إلى أن نوعاً ما لا يُسمح له بتخصيص الذاكرة على الكومة، لا أعرف في أي إصدار تم حذفه.

norace

توجيه norace يشير إلى أن وصول الذاكرة لدالة ما لم يعد يحتاج لتحليل التسابق، عادةً ما يُستخدم عند تشغيل كود منخفض المستوى غير مناسب لتحليل التسابق.

go
//go:norace
func low_level_code(ptr uintptr) uintptr

TIP

هناك أيضاً بعض التوجيهات التي تُقيد استخدامها على حزمة runtime فقط، لا يمكن استخدامها من الخارج، تتعلق بأشياء أعمق، لمعرفة المزيد يمكنك الانتقال إلى Runtime-only compiler directives للاطلاع على مقدمة عنها.

Golang تم تحريره بواسطة www.golangdev.cn