如何可靠地获取Go项目的根目录?

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

How to reliably get the projects root in go?

问题

现在我使用runtime.Caller(0)path.Dirfilepath.Abs结合使用,以获取当前执行文件的路径,并相对于该路径获取项目根目录。

假设我有以下文件夹结构:

$GOPATH/src/example.org/myproject
$GOPATH/src/example.org/myproject/main.go
$GOPATH/src/example.org/myproject/path
$GOPATH/src/example.org/myproject/path/loader.go

如果我想要获取项目根目录,我调用loader.go,然后使用runtime.Caller(0)获取其路径,然后向上一级文件夹到达项目根目录。

问题是,当使用go test -cover时,执行的文件不再位于正常位置,而是位于一个特殊的子目录中,用于测试和覆盖分析。
runtime.Caller(0)给我返回以下结果:

example.org/myproject/path/_test/_obj_/loader.go

通过path.Dirfilepath.Abs处理后,我得到:

$GOPATH/src/example.org/myproject/path/example.org/myproject/path/_test/_obj_

从那里向上一级,我将无法到达项目根目录,而是显然到达了完全不同的位置。所以我的问题是:
有没有可靠的方法来获取项目根目录?

英文:

For now I used runtime.Caller(0) in combination with path.Dir and filepath.Abs to get the path to the currently executed file and get the project root relative to it.

So lets say I've a folder structure like this:

$GOPATH/src/example.org/myproject
$GOPATH/src/example.org/myproject/main.go
$GOPATH/src/example.org/myproject/path
$GOPATH/src/example.org/myproject/path/loader.go

If I want my project root I call the loader.go which in turn gets its path with runtime.Caller(0) and then goes up one folder to reach the project root.

The problem is when using go test -cover the executed file is not in its normal place anymore but in a special sub directory for the testing and coverage analysis.
runtime.Caller(0) gives me the following:

example.org/myproject/path/_test/_obj_/loader.go

Running it through path.Dir and filepath.Abs will give me:

$GOPATH/src/example.org/myproject/path/example.org/myproject/path/_test/_obj_

When I go up from there I won't reach the project root but something totally different obviously. So my questions stands:
Is there any reliable way of getting the project root?

答案1

得分: 3

你可以使用$GOPATH环境变量来构建它:

gp := os.Getenv("GOPATH")
ap := path.Join(gp, "src/example.org/myproject")
fmt.Println(ap)

这将返回你的项目目录的绝对路径:

/path/to/gopath/src/example.org/myproject

显然,这只在设置了GOPATH时有效,也就是在你的开发机器上。在生产环境中,你需要通过配置文件提供目录。

英文:

You can build it from the $GOPATH env variable:

gp := os.Getenv("GOPATH")
ap := path.Join(gp, "src/example.org/myproject")
fmt.Println(ap)

That will yield the absolute path to your paroject dir:

/path/to/gopath/src/example.org/myproject

This obviously only works when GOPATH is set. aka. on your dev machine. On production you need to supply directories via a config file.

答案2

得分: 2

请看这个答案。如果你使用的是Go 1.8及以上版本,func Executable() (string, error)是一个我在需要时偶然发现的选项。我简单测试了它与go test -cover的交互方式,似乎工作正常:

> func Executable() (string, error)
>
> Executable返回启动当前进程的可执行文件的路径名。不能保证路径仍然指向正确的可执行文件。如果使用符号链接启动进程,根据操作系统的不同,结果可能是符号链接或它指向的路径。如果需要稳定的结果,可以使用path/filepath.EvalSymlinks。

package main

import (
    "fmt"
    "os"
    "path"
)

func main() {
    e, err := os.Executable()
    if err != nil {
        panic(err)
    }
    path := path.Dir(e)
    fmt.Println(path)
}

测试代码:

binpath.go:

package binpath

import (
    "os"
    "path"
)

func getBinPath() string {
    e, err := os.Executable()
    if err != nil {
        panic(err)
    }
    path := path.Dir(e)
    return path
}

binpath_test.go:

package binpath

import (
    "fmt"
    "testing"
)

func TestGetBinPath(t *testing.T) {
    fmt.Println(getBinPath())
}

结果类似于/tmp/go-build465775039/github.com/tworabbits/binpath/_test

英文:

See this answer. In case you are using go ~ 1.8, func Executable() (string, error) is an option I stumbled over when I needed it. I tested briefly how it interacts with go test -cover and it seems to work fine:

> func Executable() (string, error)
>
> Executable returns the path name for the executable that started the current process. There is no guarantee that the path is still pointing to the correct executable. If a symlink was used to start the process, depending on the operating system, the result might be the symlink or the path it pointed to. If a stable result is needed, path/filepath.EvalSymlinks might help.

package main

import (
    "fmt"
    "os"
    "path"
)

func main() {
    e, err := os.Executable()
    if err != nil {
        panic(err)
    }
    path := path.Dir(e)
    fmt.Println(path)
}

The test:

binpath.go:

package binpath

import (
    "os"
    "path"
)

func getBinPath() string {
    e, err := os.Executable()
    if err != nil {
        panic(err)
    }
    path := path.Dir(e)
    return path
}

binpath_test.go:

package binpath

import (
    "fmt"
    "testing"
)

func TestGetBinPath(t *testing.T) {
    fmt.Println(getBinPath())
}

Results in something like /tmp/go-build465775039/github.com/tworabbits/binpath/_test

答案3

得分: 0

Abs 返回路径的绝对表示。如果路径不是绝对路径,则会与当前工作目录连接,将其转换为绝对路径。

absPath, err := filepath.Abs()
if err != nil {
    panic("Ooops")
}
英文:

Abs returns an absolute representation of path. If the path is not absolute it will be joined with the current working directory to turn it into an absolute path

absPath, err := filepath.Abs()
if err != nil {
	panic("Ooops)
}

huangapple
  • 本文由 发表于 2017年7月21日 17:37:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/45234252.html
匿名

发表评论

匿名网友

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

确定