为什么“go run”命令在主包中找不到第二个文件?

huangapple go评论84阅读模式
英文:

Why does the "go run" command fail to find a second file in the main package?

问题

我遇到了一个问题,当我运行go run main.go时,会出现以下错误:

# command-line-arguments
./main.go:9: undefined: test

然而,命令go build && ./goruntest可以正常编译和运行程序。

输出结果为:

> Hi from test()
>
> Hi from sameFileTest()
>
> Hi from pkgtest.Test()
>
> Hi from pkgtest.Test1()

我的目录结构如下:

go/src/github.com/username/goruntest/
    pkgtest/
        pkgtest.go
        pkgtest1.go
    main.go
    test2.go

以下是代码。

main.go

package main

import (
    "fmt"
    "github.com/username/goruntest/pkgtest"
)

func main() {
    fmt.Println(test())           // main.go:9
    fmt.Println(sameFileTest())
    fmt.Println(pkgtest.Test())
    fmt.Println(pkgtest.Test1())
}

func sameFileTest() string {
    return "Hi from sameFileTest()"
}

gotest1.go

package main

func test() string {
    return "Hi from test()"
}

pkgtest/pkgtest.go

package pkgtest

func Test() string {
    return "Hi from pkgtest.Test()"
}

pkgtest/pkgtest1.go

package pkgtest

func Test1() string {
    return "Hi from pkgtest.Test1()"
}

我理解问题出在package main的第二个文件上,我也理解在main中没有真正需要第二个文件的原因。

**我的问题是:**为什么go run无法处理这样的设置,但是构建和运行可执行文件却可以正常工作?

编辑

pkgtest中添加了第二个文件

我也理解命令go run main.go gotest1.go可以工作,但为什么我需要指定gotest1.go

我最初为了简洁起见省略了这些细节,但现在我看到它们对问题很重要。

英文:

I am experiencing an issue where go run main.go produces the error:

# command-line-arguments
./main.go:9: undefined: test

However the commands go build && ./goruntest compile and run the program just fine.

The output is:

> Hi from test()
>
> Hi from sameFileTest()
>
> Hi from pkgtest.Test()
>
> Hi from pkgtest.Test1()

I have the directory set-up like so:

go/src/github.com/username/goruntest/
    pkgtest/
        pkgtest.go
        pkgtest1.go
    main.go
    test2.go

Here is the code.

main.go

package main

import (
    "fmt"
    "github.com/username/goruntest/pkgtest"
)

func main() {
    fmt.Println(test())           // main.go:9
    fmt.Println(sameFileTest())
    fmt.Println(pkgtest.Test())
    fmt.Println(pkgtest.Test1())
}

func sameFileTest() string {
    return "Hi from sameFileTest()"
}

gotest1.go

package main

func test() string {
    return "Hi from test()"
}

pkgtest/pkgtest.go

package pkgtest

func Test() string {
    return "Hi from pkgtest.Test()"
}

pkgtest/pkgtest1.go

package pkgtest

func Test1() string {
    return "Hi from pkgtest.Test1()"
}

I understand that the problem is the second file as part of package main and I also understand that there is no real reason to have a second file in main.

My question is: Why is go run unable to handle this set-up but building and running the executable works just fine?

EDIT

Included a second file in pkgtest

I also understand that the command go run main.go gotest1.go works but why do I need to specify gotest1.go?

I originally omitted these details for the sake of brevity. But now I see they are important to the question.

答案1

得分: 11

尝试将所有相关文件提供给go run命令。

$ go help run
用法:go run [构建标志] [-exec xprog] gofiles... [arguments...]

`go run`命令编译并运行由指定的Go源文件组成的主包。
Go源文件的定义是以字面上的“.go”后缀结尾的文件。
英文:

Try providing all relevant files to go run

$ go help run
usage: go run [build flags] [-exec xprog] gofiles... [arguments...]

Run compiles and runs the main package comprising the named Go source files.
A Go source file is defined to be a file ending in a literal ".go" suffix.

答案2

得分: 1

我想在这里分享一些有用的资源供参考,这些资源是我从另一个类似的已删除帖子中学到的。这看起来是一个简单的命令,但你可能还不完全了解它。

  1. 如果你要向go run提供一个源文件列表,那么你将创建一个单一的合成包,并且构建约束将被忽略。所以,与其列出go文件,不如使用导入路径或文件系统路径,例如go run .。可以通过go help packages来检查它:
作为一个特例,如果包列表是来自单个目录的一系列.go文件,那么该命令将应用于一个由这些文件组成的合成包,忽略这些文件中的任何构建约束,并忽略目录中的任何其他文件。
  1. 可执行目标的名称取自第一个源文件的名称。在go run命令的实现中可以查看这一点这里(第80行和125行)和这里(第2506行):
// GoFilesPackage 为构建一组Go文件(通常在命令行上命名)创建一个包。
// 对于包p,目标名称为p.a;对于main包,目标名称为第一个Go文件的名称。
func GoFilesPackage(ctx context.Context, gofiles []string) *Package {
...
  1. 为了避免在包初始化过程中出现潜在问题,建议按照词法文件名顺序提供go文件列表。在规范中可以查看这一点:
在多个文件中声明的变量的声明顺序由将文件呈现给编译器的顺序决定:在第一个文件中声明的变量在第二个文件中声明的任何变量之前声明,依此类推。
...
一个没有导入的包通过为其所有的包级变量分配初始值,然后按照它们在源代码中出现的顺序调用所有的init函数(可能在多个文件中)来进行初始化。
...
为了确保可重现的初始化行为,构建系统鼓励按照词法文件名顺序将属于同一个包的多个文件呈现给编译器。
  1. 关于go run有一些历史性的讨论,可以在这里这里查看。
英文:

I want to share some useful resources here for reference, which I learned from another similar deleted post. It looks like a simple command but yet you may not fully know it.

  1. If you're supplying a list of source files to go run, you are creating a single synthesized package, and build constraints are ignored. So instead of listing go files, it's better to use an import path or a file system path, e.g. go run .. Check it with go help packages:
As a special case, if the package list is a list of .go files from a
single directory, the command is applied to a single synthesized
package made up of exactly those files, ignoring any build constraints
in those files and ignoring any other files in the directory.
  1. The executable target is named after the first source file. Check it in implementations for go run command here(on line 80 and 125) and here(on line 2506):
// GoFilesPackage creates a package for building a collection of Go files
// (typically named on the command line). The target is named p.a for
// package p or named after the first Go file for package main.
func GoFilesPackage(ctx context.Context, gofiles []string) *Package {
...
  1. To avoid potential problems during package initialization, you're encouraged to supply the list of go files in lexical file name order. Check it in the specs:
The declaration order of variables declared in multiple files is determined by the order in which the files are presented to the compiler: Variables declared in the first file are declared before any of the variables declared in the second file, and so on.
...
A package with no imports is initialized by assigning initial values to all its package-level variables followed by calling all init functions in the order they appear in the source, possibly in multiple files, as presented to the compiler.
...
To ensure reproducible initialization behavior, build systems are encouraged to present multiple files belonging to the same package in lexical file name order to a compiler. 
  1. There are historical discussions about go run, check it here and here.

答案3

得分: 0

从当前包(目录)运行所有*.go文件的最简单方法是:

go run .

如上所述,这种方法允许您在一个目录中使用类似脚本的*.go文件,并且可以运行其中任何一个而不受其他文件的干扰。

英文:

The easiest way to run all *.go-files from the current package (directory) is

go run .

As mentioned above, this approach allows you to use script-like *.go-files in one directory and run any of them without interference from the others.

huangapple
  • 本文由 发表于 2014年11月14日 08:25:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/26920969.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定