Golang中如何使用根目录下的新文件夹"pkg"

Golang中如何使用根目录下的新文件夹"pkg" 我下载了 Go 1.20 并使用 VS Code 运行了 go mod tidy。随后在根目录(项目级别)创建了一个名为“pkg”的文件夹。我想知道这是否有问题?为什么会这样出现?我应该如何使用它?

func main() {
    fmt.Println("hello world")
}
12 回复

你将所有希望公开的包都放在那里。所谓公开,是指其他人可以导入并使用它们。

更多关于Golang中如何使用根目录下的新文件夹"pkg"的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


显然,你需要使用 import 语句。除了你通过 import 明确导入的内容外,其他任何东西都不会被导入。

由其他仓库外的项目导入

这是否意味着它会自动导入?对所有项目都如此? 还是我必须使用“import…”来避免引入不需要的库?

值得说明的是:所有不在 maininternal 目录下的包都是公共包。我个人真的非常非常非常不喜欢这种 pkg 的用法。话虽如此,如果你想在你的仓库中使用它,那也没问题。

我最近几乎只用 VSCode 来编写 Go 代码。这挺有意思的。我以前从未见过这种情况。pkg 目录里有东西吗?

我能想到的另一个可能性是你的 GOPATH 变量设置得有些奇怪:go env GOPATH 的输出是什么?这个路径和你的代码所在位置有什么关系?

我个人真的非常非常非常不喜欢这种 pkg 的用法。

我别无选择。在执行 go mod tidy 时,“pkg”文件夹会自动出现。这会不会是 VS Code 的问题?我第一反应是这不对。但它可能有用。目前,它只是一个几乎为空、令人讨厌且我无法永久摆脱的文件夹。

正如 @heidi 所说,将那些可以被仓库外其他项目导入的包放在那里是一种惯例。但这只是一种惯例,众多惯例之一,而非硬性规定。

有关文件夹布局惯例的更多信息,请参见:GitHub - golang-standards/project-layout: Standard Go Project Layout

关于 /pkg 目录的部分:project-layout/pkg at master · golang-standards/project-layout · GitHub

我最近在 Go 语言开发中几乎只使用 VSCode。这很有趣。我以前从未见过这种情况。pkg 目录里有东西吗?

image

我能想到的另一件事是你的 GOPATH 变量设置得很奇怪:go env GOPATH 的输出是什么?

/Users/sibert/go

…以及这个位置与你的代码位置有什么关系?

它是包含不同项目文件夹的“项目文件夹”。GOPATH 应该是什么?

这是否意味着它会自动导入?对所有项目都如此? 还是我必须使用“import…”来避免不需要的库?

不,不会有任何自动发生的情况,导入或不导入的行为将与常规文件夹完全相同。这仅仅是一种向其他开发者表明你为该目的开发了该文件夹内包的方式。所以它只是为了向其他人传达你的意图。不会有其他事情发生,如果你不愿意,你也不必这样做。

举个例子来说明这可能如何运作:假设我开发了一个项目,该项目暴露了一个API。为了提高采用率,我也可以为该API编写一个客户端,这样如果其他开发者想要使用该API,他们就不必自己动手。他们可以直接导入我的客户端并使用它。为了表明这个客户端存在并且可供他人使用,我可以将其放在 /pkg 目录中。每当其他开发者浏览我的仓库时,他们就能快速看到这种方式是可用的。

另一个例子:我只是在创建一些个人项目,甚至不打算与他人分享。我根本不会创建 /pkg 目录,只做当下最简单的事情。

关于 golang-standards 组织和那个“标准 Go 项目布局”。正如 Russ Cox 所指出的,它根本就不是标准:

github.com/golang-standards/project-layout

这不是一个标准的 Go 项目布局

README 文件清楚地表明这不是官方的,但即使声称“它是 Go 生态系统中一组常见的历史和新兴项目布局模式”也是不准确的。

例如,Go 生态系统中绝大多数并没有将可导入的包放在 pkg 子目录中。更概括地说,这里描述的内容非常复杂,而 Go 代码仓库往往要简单得多。

不幸的是,这被当作“golang-standards”提出,而实际上并非如此。我在这里发表评论,是因为我开始看到有人说“你没有使用标准的 Go 项目布局”并链接到这个仓库。

如果它对某些人有效,那很好。但我仍然认为它给 Go 新手带来的困惑多于帮助。

我不建议将你的代码放在与 go env GOPATH 相同的位置。过去你需要这样做,但需要根据包名以一种特殊的方式进行。让我解释一下:这是过去必须采用的方式,但现在情况已经不同了……

假设 ~/go 是你的 GOPATH。你正在处理的包位于 github.com/user/repo,那么为了让 Go 工具链找到它,这个包必须放在 ~/go/src/github.com/user/repo 目录下。

这种方式在像谷歌这样的公司环境中运行良好,但对个人开发者来说就不那么友好了。因此(我暂且忽略为解决此问题而开发的一些工具),最终 Go 团队提出了模块的概念和 go mod 命令,这(以及其他功能)允许你将代码放在文件系统的几乎任何位置。

你当然可以继续使用 ~/go,但我建议将其移到其他地方,这样 pkg 目录(以及其他可能的目录)就不会出现在你的代码目录中了。

func main() {
    fmt.Println("hello world")
}

在Go 1.20及更高版本中,根目录下自动生成的pkg文件夹是正常的,这是Go模块缓存的一部分。它用于存储编译后的包文件(.a文件),以加速后续构建过程。

为什么会出现pkg文件夹?

当您运行go mod tidy或任何Go构建命令时,Go工具链会:

  1. 下载依赖到$GOPATH/pkg/mod(全局缓存)
  2. 在项目根目录创建pkg文件夹存储项目自身的编译结果

这是Go 1.20引入的构建缓存改进的一部分,称为"build cache in module mode"。

如何使用它?

您不需要手动操作这个文件夹,Go工具链会自动管理它。但了解其结构有助于调试:

示例:查看pkg内容结构

package main

import (
    "fmt"
    "io/ioutil"
    "path/filepath"
)

func main() {
    // 列出pkg目录内容(如果存在)
    files, err := ioutil.ReadDir("pkg")
    if err == nil {
        fmt.Println("pkg目录内容:")
        for _, f := range files {
            fmt.Printf("- %s\n", f.Name())
            // 如果是目录,可以进一步探索
            if f.IsDir() {
                subFiles, _ := ioutil.ReadDir(filepath.Join("pkg", f.Name()))
                for _, sf := range subFiles {
                    fmt.Printf("  └─ %s\n", sf.Name())
                }
            }
        }
    }
}

实际使用场景

当您构建项目时,Go会自动使用pkg中的缓存:

// main.go
package main

import (
    "fmt"
    "yourmodule/internal/mathutil" // 假设有本地包
)

func main() {
    result := mathutil.Add(10, 20)
    fmt.Printf("Result: %d\n", result)
}
// internal/mathutil/mathutil.go
package mathutil

func Add(a, b int) int {
    return a + b
}

第一次构建后,mathutil包的编译结果会存储在pkg中,后续构建会直接使用缓存。

注意事项

  1. 不要提交到版本控制:在.gitignore中添加:

    /pkg/
    
  2. 清理缓存:如果需要清理,可以:

    go clean -cache
    
  3. 目录结构pkg文件夹通常包含:

    pkg/
    └── mod/
        └── cache/
            └── download/    # 下载的依赖
    └── 您的平台目录/        # 如linux_amd64/
        └── *.a             # 编译的包文件
    

这个机制提高了构建速度,特别是对于大型项目或频繁的增量构建。

回到顶部