英文:
Module XXX found, but does not contain package XXX
问题
对于不太熟悉Golang的问题,可能是我犯了一个愚蠢的错误...但是,我无论如何都无法解决它。
我有一个proto3文件(让我们称之为file.proto
),其头部如下所示:
syntax = "proto3";
package [package_name];
option go_package = "github.com/[user]/[repository]";
然后我使用protoc
:
protoc --go_out=$GOPATH/src --go-grpc_out=$GOPATH/src file.proto
到目前为止还好,我在/go/src/github.com/[user]/[repository]/
目录下得到了两个生成的文件(file.pb.go
和file_grpc.pb.go
),它们被定义在[package_name]
包中。
然后,我尝试构建的代码有以下导入语句:
import (
"github.com/[user]/[repository]/[package_name]"
)
我天真地以为它会起作用。然而,当运行go mod tidy
时,它产生了以下错误:
go: downloading github.com/[user]/[repository] v0.0.0-20211105185458-d7aab96b7629
go: finding module for package github.com/[user]/[repository]/[package_name]
example/xxx imports
github.com/[user]/[repository]/[package_name]: module github.com/[user]/[repository]@latest found (v0.0.0-20211105185458-d7aab96b7629), but does not contain package github.com/[user]/[repository]/[package_name]
你知道我在这里做错了什么吗?Go版本是go1.19 linux/amd64
,在Docker中(golang:1.19-alpine
)。
注意:我也尝试只导入github.com/[user]/[repository]
,显然是同样的问题。
更新:
好的,我从仅包含proto文件的git存储库中获取proto文件:
wget https://raw.githubusercontent.com/[user]/[repository]/file.proto
然后我使用protoc
从该文件生成go文件:
protoc --go_out=. --go-grpc_out=. file.proto
现在,在当前目录中,它看起来像这样:
- directory
| - process.go
| - file.proto
| - github.com
| - [user]
| - [repository]
| - file.pb.go
| - file_grpc.pb.go
在同一个目录中,我运行:
go mod init xxx
go mod tidy
CGO_ENABLED=0 go build process.go
process.go
中的导入指令如下:
import (
"xxx/github.com/[user]/[repository]"
)
现在它似乎找到了,但仍然出现gRPC错误,这很奇怪,因为没有任何更改。我仍然需要弄清楚它是否与上述问题有关。谢谢!
英文:
Not so familiar with Golang, it's probably a stupid mistake I made... But still, I can't for the life of me figure it out.
So, I got a proto3 file (let's call it file.proto
), whose header is as follows:
syntax = "proto3";
package [package_name];
option go_package = "github.com/[user]/[repository]";
And I use protoc
:
protoc --go_out=$GOPATH/src --go-grpc_out=$GOPATH/src file.proto
So far so good, I end up with two generated files (file.pb.go
and file_grpc.pb.go
) inside /go/src/github.com/[user]/[repository]/
, and they are defined inside the package [package_name]
.
Then, the code I'm trying to build has the following import:
import (
"github.com/[user]/[repository]/[package_name]"
)
And I naively thought it would work. However, it produces the following error when running go mod tidy
:
go: downloading github.com/[user]/[repository] v0.0.0-20211105185458-d7aab96b7629
go: finding module for package github.com/[user]/[repository]/[package_name]
example/xxx imports
github.com/[user]/[repository]/[package_name]: module github.com/[user]/[repository]@latest found (v0.0.0-20211105185458-d7aab96b7629), but does not contain package github.com/[user]/[repository]/[package_name]
Any idea what I'm doing wrong here? Go version is go1.19 linux/amd64
within Docker (golang:1.19-alpine
).
Note: I also tried to only import github.com/[user]/[repository]
, same issue obviously.
UPDATE:
OK so what I do is that I get the proto file from the git repository that only contains the proto file:
wget https://raw.githubusercontent.com/[user]/[repository]/file.proto
Then I generate go files from that file with protoc
:
protoc --go_out=. --go-grpc_out=. file.proto
Right now, in current directory, it looks like:
- directory
| - process.go
| - file.proto
| - github.com
| - [user]
| - [repository]
| - file.pb.go
| - file_grpc.pb.go
In that same directory, I run:
go mod init xxx
go mod tidy
CGO_ENABLED=0 go build process.go
The import directive in process.go
is as follows:
import (
"xxx/github.com/[user]/[repository]"
)
Now it looks like it finds it<s>, but still getting a gRPC error, which is weird because nothing changed. I still have to figure out if it comes from the issue above or not</s>. Thanks!
答案1
得分: 1
你的问题实际上包含了多个问题;我会尽量提供一些有用的信息。你最初遇到的问题是因为在一个目录中至少要有一个具有.go扩展名的文件才能被视为一个包。这是有道理的,因为如果导入的github.com/[user]/[repository]不包含任何.go文件(即go编译器无法对文件进行任何操作),那么导入就没有什么意义。
你有以下几个选择:
- 将
protoc
的输出直接复制到你的项目文件夹中,并将package
声明更改为与你的包匹配。如果你这样做,就不需要任何导入。 - 将
protoc
的输出复制(或将go_out
参数设置为protoc
)到你的项目的子文件夹中。导入路径将是你的go.mod
中module
声明的值加上包含go.mod
的文件夹的路径(这是你所做的)。 - 将文件存储在一个仓库中(在github或其他地方)。如果你“希望它是不可知的”,这个仓库不需要与你的.proto文件相同(请注意,如果生成的文件只在一个代码库中使用或者仓库对所有用户都可访问,可以将2和3结合起来)。
选项1很简单,但通常将生成的代码与其他代码分开是有益的(可以清楚地知道不应该编辑什么,并改善编辑器的自动完成等)。
选项2还可以(特别是如果protoc
直接写入文件并且你适当地设置了go_package
)。然而,当生成的文件将在多个模块中使用(例如作为客户代码的一部分)并且你的仓库是私有的时,可能会出现问题。他们需要在运行protoc
之前更改go_package
(或搜索/替换package
声明),并且导入其他.proto
文件可能无法正常工作。
选项3可能是大多数情况下最好的方法,因为它与go工具链兼容。你可以创建github.com/[user]/goproto
(或类似的)并将所有生成的代码放在那里。要使用这个,你的客户只需要import github.com/[user]/goproto
(不需要运行protoc
等)。
Go Modules/包介绍
Go规范没有详细说明导入路径的格式,而是将其留给实现:
>ImportPath的解释是依赖于具体实现的,但通常它是已安装的包的完整文件名的子字符串,并且可能相对于已安装包的存储库。
由于你正在使用go modules(现在几乎是默认的),解析包路径(导入路径的同义词)的实现规则可以总结如下:
>模块中的每个包都是在同一目录中编译在一起的一组源文件。包路径是模块路径与包所在的子目录(相对于模块根目录)的组合。例如,模块“golang.org/x/net”包含目录“html”中的一个包。该包的路径是“golang.org/x/net/html”。
因此,如果你的“模块路径”(通常是go.mod
中的顶行)设置为xxx
(go mod init xxx
),那么你将使用import xxx/github.com/[user]/[repository]
导入子文件夹github.com/[user]/[repository]
中的包(正如你所发现的)。如果你去掉中间的文件夹,并将文件放入[repository]
子文件夹(直接放在主文件夹下),那么导入语句将是import xxx/[repository]
。
你会注意到上面的示例中,我使用的module
名称是仓库的路径(而不是你在go mod init xxx
中使用的xxx
)。这是有意的,因为它允许go工具在从不同模块导入时找到包。例如,如果你使用了go mod init github.com/[user]/[repository]
和option go_package = "github.com/[user]/[repository]/myproto";
,那么生成的文件应该放在你的项目中的myproto
文件夹中,并且你可以使用import github.com/[user]/[repository]/myproto
导入它们。
虽然你不必须遵循这种方法,但我强烈推荐这样做(它会节省你很多麻烦!)。理解go的做法可能需要一些时间,但一旦你理解了,它就能很好地工作,并且非常清楚一个包托管在哪里。
英文:
Your question is really a number of questions in one; I'll try to provide some info that will help. The initial issue you had was because
>At least one file with the .go extension must be present in a directory for it to be considered a package.
This makes sense because importing github.com/[user]/[repository]
would be fairly pointless if that repository does not contain any .go
files (i.e. the go compiler could not really do anything with the files).
Your options are:
- Copy the output from
protoc
directly into your project folder and change thepackage
declarations to match your package. If you do this there is no need for any imports. - Copy (or set
go_out
argument toprotoc
) the output fromprotoc
into a subfolder of your project. The import path will then be the value of themodule
declaration in yourgo.mod
plus the path from the folder that thego.mod
is in (this is what you have done). - Store the files in a repo (on github or somewhere else). This does not need to be the same repo as your
.proto
files if you "want it to be agnostic" (note that 2 & 3 can be combined if the generated files will only be used within one code base or the repo is accessible to all users).
Option 1 is simple but its often beneficial to keep the generated code separate (makes it clear what you should not edit and improves editor autocomplete etc).
Option 2 is OK (especially if protoc
writes the files directly and you set go_package
appropriately). However issues may arise when the generated files will be used in multiple modules (e.g. as part of your customers code) and your repo is private. They will need to change go_package
before running protoc
(or search/replace the package
declarations) and importing other .proto
files may not work well.
Option 3 is probably the best approach in most situations because this works with the go tooling. You can create github.com/[user]/goproto
(or similar) and put all of your generated code in there. To use this your customers just need to import github.com/[user]/goproto
(no need to run protoc
etc).
Go Modules/package intro
The go spec does not detail the format of import paths, leaving it up to the implementation:
>The interpretation of the ImportPath is implementation-dependent but it is typically a substring of the full file name of the compiled package and may be relative to a repository of installed packages.
As you are using go modules (pretty much the default now) the implementations rules for resolving package paths (synonym of import path) can be summarised as:
>Each package within a module is a collection of source files in the same directory that are compiled together. A package path is the module path joined with the subdirectory containing the package (relative to the module root). For example, the module "golang.org/x/net" contains a package in the directory "html". That package’s path is "golang.org/x/net/html".
So if your "module path" (generally the top line in a go.mod
) is set to xxx
(go mod init xxx
) then you would import the package in subfolder github.com/[user]/[repository]
with import xxx/github.com/[user]/[repository]
(as you have found). If you got rid of the intervening folders and put the files into the [repository]
subfolder (directly off your main folder) then it would be import xxx/[repository]
You will note in the examples above that the module
names I used are paths to repo (as opposed to the xxx
you used in go mod init xxx
). This is intentional because it allows the go tooling to find the package when you import it from a different module. For example if you had used go mod init github.com/[user]/[repository]
and option go_package = "github.com/[user]/[repository]/myproto";"
then the generated files should go into the myproto
folder in your project and you import them with import github.com/[user]/[repository]/myproto
.
While you do not have to follow this approach I'd highly recommend it (it will save you from a lot of pain!). It can take a while to understand the go way of doing this, but once you do, it works well and makes it very clear where a package is hosted.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论