我可以在多个源代码目录中开发 Go 包吗?

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

Can I develop a go package in multiple source directories?

问题

我正在开发一个Go包,它有点复杂,因此我想将源代码组织成多个目录。

然而,我不希望包的用户必须使用过长的导入路径。无论如何,包的内部结构并不是他们关心的。

因此,我的包结构如下:

subDir1
  subSubDir1
  subSubDir2
subDir2
  subSubDir3

...以此类推。它们都有自己的导出函数。

我希望避免用户必须导入以下内容:

import (
  "mypackage/subDir1"
  "mypackage/subDir1/subSubDir2"
)

...以此类推。

我只希望,如果他们想要使用我包中的一个导出函数,他们只需导入mypackage即可访问所有函数。

我尝试在所有.go文件中声明package mypackage。因此,我在不同目录下有源文件,但是它们都有相同的包声明。

在这种情况下,我遇到的问题是我根本无法从同一个包中导入多个目录。它会显示以下错误:

./src1.go:6:15: error: redefinition of ‘mypackage’
   "mypackage/mysubdir1"
               ^
./src1.go:4:10: note: previous definition of ‘mypackage’ was here
   "mypackage"
          ^
./src1.go:5:15: error: redefinition of ‘mypackage’
   "mypackage/mysubdir2"
               ^
./src1.go:4:10: note: previous definition of ‘mypackage’ was here
   "mypackage"
          ^

有没有办法解决这个问题?

英文:

I am developing a go package, which is a little bit complex and thus I want to organize the source code into multiple directories.

However, I don't want the users of the package to have to use too long imports. Anyways, the internal structure of the package isn't their concern.

Thus, my package structure looks so:

subDir1
  subSubDir1
  subSubDir2
subDir2
  subSubDir3

...and so on. All of them have their exported calls.

I would like to avoid that my users have to import

import (
  "mypackage/subDir1"
  "mypackage/subDir1/subSubDir2"
)

...and so on.

I only want, if they want to use an exported function from my package, they should have access all of them by simply importing mypackage.

I tried that I declare package mypackage in all of the .go files. Thus, I had source files in different directories, but with the same package declaration.

In this case, the problem what I've confronted was that I simply couldn't import multiple directories from the same package. It said:

./src1.go:6:15: error: redefinition of ‘mypackage’
   "mypackage/mysubdir1"
               ^
./src1.go:4:10: note: previous definition of ‘mypackage’ was here
   "mypackage"
          ^
./src1.go:5:15: error: redefinition of ‘mypackage’
   "mypackage/mysubdir2"
               ^
./src1.go:4:10: note: previous definition of ‘mypackage’ was here
   "mypackage"
          ^

Is it somehow possible?

答案1

得分: 19

在任何情况下,你都不应该这样做,因为语言规范允许编译器实现拒绝这种结构。引用自规范:包声明

具有相同 PackageName 的一组文件构成了一个包的实现。实现可能要求一个包的所有源文件位于同一个目录中

相反,你可以将文件名的结构模拟成文件夹结构;例如,不要使用以下文件:

foo/foo1.go
foo/bar/bar1.go
foo/bar/bar2.go

而是可以使用:

foo/foo1.go
foo/bar-bar1.go
foo/bar-bar2.go

此外,如果你的包非常大,以至于需要多个文件夹来“托管”包的实现文件,那么你应该考虑将其拆分为多个包。

还要注意,Go 1.5 引入了内部包。如果在包文件夹内创建一个特殊的 internal 子文件夹,你可以在其中创建任意数量的子包(甚至使用多级)。你的包将能够导入和使用它们(或者更准确地说,所有以你的包文件夹为根的包),但外部的其他人将无法这样做,这将导致编译时错误。

例如,你可以创建一个 foo 包,有一个 foo/foo.go 文件和一个 foo/internal/bar 包。foo 将能够导入 foo/internal/bar,但 boo 不行。同样,foo/baz 也将能够导入和使用 foo/internal/bar,因为它是以 foo/ 为根的。

因此,你可以使用内部包将大型包拆分为较小的包,有效地将源文件分组到多个文件夹中。唯一需要注意的是将包要导出的内容放入包中,而不是放入内部包中(因为内部包无法从“外部”导入/可见)。

英文:

You should not do this in any case, as the language spec allows a compiler implementation to reject such constructs. Quoting from Spec: Package clause:

> A set of files sharing the same PackageName form the implementation of a package. An implementation may require that all source files for a package inhabit the same directory.

Instead "structure" your file names to mimic the folder structure; e.g. instead of files of

foo/foo1.go
foo/bar/bar1.go
foo/bar/bar2.go

You could simply use:

foo/foo1.go
foo/bar-bar1.go
foo/bar-bar2.go

Also if your package is so big that you would need multiple folders to even "host" the files of the package implementation, you should really consider not implementing it as a single package, but break it into multiple packages.

Also note that Go 1.5 introduced internal packages. If you create a special internal subfolder inside your package folder, you may create any number of subpackages inside that (even using multiple levels). Your package will be able to import and use them (or to be more precise all packages rooted at your package folder), but no one else outside will be able to do so, it would be a compile time error.

E.g. you may create a foo package, have a foo/foo.go file, and foo/internal/bar package. foo will be able to import foo/internal/bar, but e.g. boo won't. Also foo/baz will also be able to import and use foo/internal/bar because it's rooted at foo/.

So you may use internal packages to break down your big package into smaller ones, effectively grouping your source files into multiple folders. Only thing you have to pay attention to is to put everything your package wants to export into the package and not into the internal packages (as those are not importable / visible from the "outside").

答案2

得分: 0

在你的包源代码中,你需要通过重命名导入来区分你的源目录。你可以在所有的源文件中声明相同的package mypackage(即使它们位于不同的目录中)。

然而,在导入它们时,你应该给目录分配一个独立的名称。在你的源文件src1.go中,以以下方式导入其他目录:

import (
  "mypackage"
  submodule1 "mypackage/mySubDir"
)

这样,你就可以通过mypackage.AnyThing()访问在mypackage中定义的API,通过submodule1.AnyThing()访问在mySubDir中定义的API。

外部世界(即你的包的用户)将在myPackage.AnyThing()中看到所有导出的实体。

避免命名空间冲突,并使用更易理解、直观的命名,就像示例中一样。

英文:

Inside your package source code, you have to differentiate your source directories by renamed imports. You can declare the same package mypackage in all of your source files (even if they are in different directories).

However, while you import them, you should give an induvidual names to the directories. In your source src1.go, import the other directories on this way:

import (
  "mypackage"
  submodule1 "mypackage/mySubDir"
)

And you will be able to reach the API defined in "mypackage" as mypackage.AnyThing(), and the API defined in mySubDir as submodule1.AnyThing().

The external world (i.e. the users of your package) will see all exported entities in myPackage.AnyThing().

Avoid namespace collisions. And use better understable, intuitive naming as in the example.

答案3

得分: 0

是的,这是可以做到的,没有任何问题,只需手动调用Go编译器,而不是通过go工具。

但是最好的建议是:不要这样做。这样做很丑陋且不必要地复杂。只需正确设计你的包。

补充说明(因为有时真正的意图可能会被忽略,也许因为讽刺太微妙):不要这样做!这是一个非常愚蠢的想法!不要与工具作对!如果你这样做,每个人都会理所当然地讨厌你!没有人能理解你的代码或编译它!仅仅因为理论上可以做到并不意味着这是一个明智的想法。即使是为了“学习目的”也不行!你可能甚至不知道如何手动调用Go编译器,如果你弄清楚了,那将是一件非常麻烦的事情。

英文:

Yes, this is doable without any problems, just invoke the Go compiler by hand, that is not via the go tool.

But the best advice is: Don't do that. It's ugly and unnecessarily complicated. Just design your package properly.

Addendum (because the real intention of this answer seems to get lost sometimes, maybe because irony is too subtle): Don't do that!! This is an incredible stupid idea! Stop fighting the tools! Everybody will rightfully hate you if you do that! Nobody will understand your code or be able to compile it! Just because something is doable in theory doesn't mean this is a sensible idea in any way. Not even for "learning purpose"! You probably even don't know how to invoke the Go compiler by hand and if you figure it out it will be a major pita.

huangapple
  • 本文由 发表于 2017年8月27日 04:00:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/45899203.html
匿名

发表评论

匿名网友

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

确定