golang学习手册之build子命令(中)

build单个导入路径

单个文件

golang要求源代码文件必须以“.go”作为名字后缀,这样才可以被build等命令行工具识别。

  1. 文件名后缀不是“.go”

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    ➜  example0 cat ./example0.go
    package example0
    ➜ example0 cat ./example0.txt
    package example0
    ➜ example0 go build -n ./example0.go
    #
    # command-line-arguments
    #
    mkdir -p $WORK/b001/
    cat >$WORK/b001/importcfg << 'EOF' # internal
    # import config
    EOF
    cd /home/Admin/projects/examples/example0
    /home/Admin/.g/go/pkg/tool/linux_arm64/compile -o $WORK/b001/_pkg_.a -trimpath "$WORK/b001=>" -p command-line-arguments -complete -buildid Vw6mBaJb5SUht4kgt4T9/Vw6mBaJb5SUht4kgt4T9 -goversion go1.18 -c=4 -D _/home/Admin/projects/examples/example0 -importcfg $WORK/b001/importcfg -pack ./example0.go
    /home/Admin/.g/go/pkg/tool/linux_arm64/buildid -w $WORK/b001/_pkg_.a # internal
    ➜ example0 go build -n ./example0.txt
    cannot find package "." in:
    /home/Admin/projects/examples/example0/example0.txt

    两份文件内容是一样的,都只有一句package声明;编译go文件是正常的;但是编译txt文件会报错,说明golang的源代码文件名要以“.go”结尾。

  2. 文件名后缀是“_test.go”

    1
    2
    3
    4
    ➜  example1 cat example1_test.go
    package example1
    ➜ example1 go build -n ./example1_test.go
    ➜ example1

    加了-n参数,但是没有任何输出,说明example1_test.go文件被build忽略了;实际上,这类go文件需要go test命令处理。

  3. 源代码不属于main包
    由前文可知,当源代码不属于main包时,编译后的结果是库文件,但是默认情况下这个库文件不会被保存下来,想要保存要使用-o参数指定保存路径。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    ➜  example2 cat example2.go                 
    package example2
    ➜ example2 go build ./example2.go
    ➜ example2 ls -al
    总用量 12
    drwxr-xr-x. 2 Admin Admin 4096 6月 24 17:02 .
    drwxr-xr-x. 8 Admin Admin 4096 6月 21 09:56 ..
    -rw-r--r--. 1 Admin Admin 17 6月 24 17:02 example2.go
    ➜ example2 go build -o ./example.a ./example2.go
    ➜ example2 ls
    example2.go example.a
    ➜ example2 file example.a
    example.a: current ar archive

    可以看到,若不使用-o参数,编译后确实没有产生任何新的文件;使用了-o参数指定了保存路径后,出现了新的文件example.a,其类型为archive,是个二进制类型的文件。
    需要注意的是,编译库文件的源代码时,-o的参数值只能是文件而不能是目录,否则就会报错:

    1
    2
    ➜  example2 go build -o ./ ./example2.go      
    go: no main packages to build

    当试图将编译结果保存到当前目录时,报错了,这个错误暗示只有编译可执行文件的源代码时,才可以为-o参数指定目录。

  4. 源代码属于main包

    • 如果声明属于main包,源代码就必须存在一个main函数,否则编译报错。

      1
      2
      3
      4
      5
      ➜  example3 cat example3.go
      package main
      ➜ example3 go build -o ./example3.go
      # _/home/Admin/projects/examples/example3
      runtime.main_main·f: function main is undeclared in the main package
    • 若属于main包且包含main函数,即存在程序执行入口,那么build默认会在当前目录保存编译结果——可执行文件,文件与源代码文件同名(除去后缀),这与编译库文件的源代码有所不同。

      1
      2
      3
      4
      5
      6
      7
      8
      ➜  example3 cat example3.go
      package main

      func main() {
      }
      ➜ example3 go build ./example3.go
      ➜ example3 ls
      example3 example3.go
    • 当然也可以对这类源代码文件使用-o参数,定制可执行文件的名字。

      1
      2
      3
      ➜  example3 go build -o main ./example3.go                  
      ➜ example3 ls
      example3 example3.go main
    • 不同的是,此时-o参数的值还可以是目录,build会在指定目录下保存可执行文件,名字默认与源代码文件同名。

      1
      2
      3
      4
      ➜  example3 mkdir tmp1
      ➜ example3 go build -o ./tmp1 ./example3.go
      ➜ example3 ls tmp1
      example3

    ​ 先创建目录tmp1,然后编译的时候将其指定为保存路径,编译完成后在tmp1目录中生成了可执行文件example3。

    • 若目录不存在,还会先创建目录,需要注意指定不存在的目录时,需要在目录名末尾添加“/”,否则会被当做文件。

      1
      2
      3
      4
      5
      ➜  example3 go build -o ./tmp2/ ./example3.go
      ➜ example3 ls
      example3 example3.go main tmp1 tmp2
      ➜ example3 ls tmp2
      example3

      tmp2目录本来不存在,编译之后,先创建了tmp2目录,然后在其中生成了对应的可执行文件。

    • main函数只有在属于main包时才有意义,会被特殊对待;若不属于main包,就是一个普通函数。

      1
      2
      3
      4
      5
      6
      7
      8
      ➜  example3 cat tmp.go
      package example3

      func main() {
      }
      ➜ example3 go build ./tmp.go
      ➜ example3 ls
      example3 example3.go main tmp1 tmp2 tmp.go

​ tmp.go文件声明所在包是example3,虽然含有main函数,但是编译之后并没有生成对应的可执行文件。

单个目录

  1. 若导入路径是一个目录,则目录中必须包含golang源代码文件才可以进行编译,否则会报错。

    1
    2
    3
    4
    ➜  example4 ls
    example4.txt
    ➜ example4 go build .
    package .: no Go files in /home/Admin/projects/examples/example4

    当前目录仅有一个txt文件,编译当前目录就会报错,说找不到go文件;可想而知,如果目录是空的,也会是同样结果。

  2. build不会递归的去编译目录,也就是仅仅编译导入路径指定的目录,但是不包括其子目录;也可以理解为,build同样会忽略指定目录下类型为目录的文件。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    ➜  example4 cat tmp/tmp.go 
    package main

    func main() {
    }
    ➜ example4 go build -o main ./tmp
    ➜ example4 ls
    example4.txt main tmp
    ➜ example4 go build .
    package .: no Go files in /home/Admin/projects/examples/example4

    直接编译子目录tmp是成功的,在当前目录下生成了可执行文件;但是编译当前目录example4还是一样的报错,这说明指定目录的子目录不会影响该目录的编译结果,亦即目录不会递归编译。

  3. 导入路径指定目录下若存在多个go文件,则要求这些文件属于同一个包,否则会报错。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    ➜  example5 cat ./example5.go
    package main

    func main() {
    }
    ➜ example5 cat ./tmp.go
    package tmp

    func tmp() {
    }
    ➜ example5 go build .
    found packages main (example5.go) and tmp (tmp.go) in /home/Admin/projects/examples/example5

    两个go文件分别声明了自己的包是main和tmp,那么build它们共同所在的目录,就报错了。

  4. 导入路径指定目录下的go文件属于同一个非main包
    与编译单个非main包的go文件的表现几乎一样,区别就是指定目录下可能会有多个go文件一起被编译。

  5. 导入路径指定目录下的go文件都属于main包
    与编译单个main包的go文件几乎一样,区别就是指定目录下会有多个go文件一起被编译,可执行文件的名字默认与源代码所在目录同名。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    ➜  example6 cat main.go 
    package main

    func main() {
    }
    ➜ example6 cat example6.go
    package main

    func test() {
    }
    ➜ example6 go build .
    ➜ example6 ls
    example6 example6.go main.go