Kamil Dziedzic
Kamil Dziedzic

Reputation: 5012

How to get name of current package in go?

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

Answers (8)

ardnew
ardnew

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

gilwo
gilwo

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]
}

simple example

log example

I hope this will be useful for others as well.

Upvotes: 0

voidlogic
voidlogic

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

Mahmood
Mahmood

Reputation: 565

Better Solution

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

Ave
Ave

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

Rostepher
Rostepher

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

themue
themue

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

Marc
Marc

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

Related Questions