Reputation: 5012
Is there a way to get on runtime the name of current package?
package main
import "fmt"
func main() {
pkgName := {some magic here:)}
fmt.Println(pkgName)
}
... and the result should be "main"
Right now I'm using constant like:
package main
import "fmt"
const (
pkgName = "main"
)
func main() {
fmt.Println(pkgName)
}
but I'm curious if you can avoid this
Upvotes: 43
Views: 32426
Reputation: 2086
Although I would personally opt for something similar to the other answers using runtime
, the following is an alternative that also doesn't depend on reflect
:
import (
"go/parser"
"go/token"
_ "embed"
)
//go:embed "__FILE__.go"
var source []byte
func packageName() string {
// No need to check for error, it obviously compiles ;)
f, _ := parser.ParseFile(token.NewFileSet(), "", source, parser.PackageClauseOnly)
return f.Name.Name
}
Using the embed
package, we can bundle the source code directly into the compiled executable. So you should probably place this in a file by itself if size is a concern — and, e.g., from a func init()
, copy packageName()
to a global var.
ⓘ Remember that's not a macro, you need to replace __FILE__
with the actual name of the source file ...yes, by typing it with your fingers.
Upvotes: 0
Reputation: 1671
I want to get the name of the package in which a function is invoked,
e.g. log a line with a prefix of the package name where the log is triggered.
here is my solution:
import "runtime"
func GetCallerPackageName(skip int) string {
// alloc some space for a pc
rpc := make([]uintptr, 1)
// get pc of caller (skip as much as needed)
runtime.Callers(skip, rpc[:])
// now get the func details of the pc of the caller
callerName := runtime.FuncForPC(rpc[0]).Name()
// chop chop and get the package name
strs := strings.Split(callerName, ".")
strs = strings.Split(strs[0], "/")
return strs[len(strs)-1]
}
I hope this will be useful for others as well.
Upvotes: 0
Reputation: 6826
Here is a very low tech solution from an application of mine, this will return the package a variable is from:
func pkgName(x interface{}) string {
varType := strings.TrimPrefix(fmt.Sprintf("%T", x), "*")
return strings.TrimSuffix(varType, path.Ext(varType))
}
To get the current package declare a variable locally and pass it in.
Upvotes: 0
Reputation: 565
I actually found a better solution, in this function down here you just simply pass a function and the output is gonna be simple and straight.
package main
import (
"reflect"
"runtime"
"strings"
)
func GetPackageName(temp interface{}) string {
strs := strings.Split((runtime.FuncForPC(reflect.ValueOf(temp).Pointer()).Name()), ".")
strs = strings.Split(strs[len(strs)-2], "/")
return strs[len(strs)-1]
}
And this is an example of how you use this:
package main
import "fmt"
func main() {
fmt.Println(GetPackageName(main))
}
And this is the answer you should expect:
main
Upvotes: 3
Reputation: 1
This might help:
import (
"runtime"
"strings"
)
func Package() string {
pc, _, _, _ := runtime.Caller(1)
parts := strings.Split(runtime.FuncForPC(pc).Name(), ".")
pl := len(parts)
pkage := ""
funcName := parts[pl-1]
if parts[pl-2][0] == '(' {
funcName = parts[pl-2] + "." + funcName
pkage = strings.Join(parts[0:pl-2], ".")
} else {
pkage = strings.Join(parts[0:pl-1], ".")
}
return pkage
}
Upvotes: -1
Reputation: 201
To reliably get the package name, you can use the go compiler's parser to parse only the package clause.
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
)
func packageName(file string) (string, error) {
fset := token.NewFileSet()
// parse the go soure file, but only the package clause
astFile, err := parser.ParseFile(fset, l.path, nil, parser.PackageClauseOnly)
if err != nil {
return "", err
}
if astFile.Name == nil {
return "", fmt.Errorf("no package name found")
}
return astFile.Name.Name, nil
}
Upvotes: 6
Reputation: 7850
Here a part of my logger package. It retrieves information about the caller of the logging function to show it later in the output.
func retrieveCallInfo() *callInfo {
pc, file, line, _ := runtime.Caller(2)
_, fileName := path.Split(file)
parts := strings.Split(runtime.FuncForPC(pc).Name(), ".")
pl := len(parts)
packageName := ""
funcName := parts[pl-1]
if parts[pl-2][0] == '(' {
funcName = parts[pl-2] + "." + funcName
packageName = strings.Join(parts[0:pl-2], ".")
} else {
packageName = strings.Join(parts[0:pl-1], ".")
}
return &callInfo{
packageName: packageName,
fileName: fileName,
funcName: funcName,
line: line,
}
}
As you can see it returns the package name too.
Upvotes: 32
Reputation: 1820
There is no runtime or reflect method or function that provides the functionality that you are looking for.
The closest thing I could find is:
package main
import (
"azul3d.org/lmath.v1"
"fmt"
"reflect"
)
type Empty struct{}
func main() {
fmt.Println(reflect.TypeOf(Empty{}).PkgPath())
fmt.Println(reflect.TypeOf(lmath.Vec3{0, 0, 0}).PkgPath())
}
This would output:
main
azul3d.org/lmath.v1
You could also read the first line of the file and remove the "package" substring. (Not sure if it's the best idea)
package main
import (
"bufio"
"bytes"
"fmt"
"os"
)
func main() {
file, err := os.Open("so.go")
if err != nil {
panic(err)
}
r := bufio.NewReader(file)
line, _, err := r.ReadLine()
if err != nil {
panic(err)
}
packageName := bytes.TrimPrefix(line, []byte("package "))
fmt.Println(string(packageName))
}
Upvotes: 45