模块 XXX 找到了,但不包含包 XXX。

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

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.gofile_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编译器无法对文件进行任何操作),那么导入就没有什么意义。

你有以下几个选择:

  1. protoc的输出直接复制到你的项目文件夹中,并将package声明更改为与你的包匹配。如果你这样做,就不需要任何导入。
  2. protoc的输出复制(或将go_out参数设置为protoc)到你的项目的子文件夹中。导入路径将是你的go.modmodule声明的值加上包含go.mod的文件夹的路径(这是你所做的)。
  3. 将文件存储在一个仓库中(在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中的顶行)设置为xxxgo 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:

  1. Copy the output from protoc directly into your project folder and change the package declarations to match your package. If you do this there is no need for any imports.
  2. Copy (or set go_out argument to protoc) the output from protoc into a subfolder of your project. The import path will then be the value of the module declaration in your go.mod plus the path from the folder that the go.mod is in (this is what you have done).
  3. 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 = &quot;github.com/[user]/[repository]/myproto&quot;;&quot; 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.

huangapple
  • 本文由 发表于 2022年8月19日 07:57:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/73410563.html
匿名

发表评论

匿名网友

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

确定