dependency_source.go 1.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. package hero
  2. import (
  3. "fmt"
  4. "os"
  5. "path/filepath"
  6. "reflect"
  7. "runtime"
  8. "strings"
  9. )
  10. // Source describes where a dependency is located at the source code itself.
  11. type Source struct {
  12. File string
  13. Line int
  14. Caller string
  15. }
  16. func newSource(fn reflect.Value) Source {
  17. var (
  18. callerFileName string
  19. callerLineNumber int
  20. callerName string
  21. )
  22. switch fn.Kind() {
  23. case reflect.Func, reflect.Chan, reflect.Map, reflect.Ptr, reflect.UnsafePointer, reflect.Slice:
  24. pc := fn.Pointer()
  25. fpc := runtime.FuncForPC(pc)
  26. if fpc != nil {
  27. callerFileName, callerLineNumber = fpc.FileLine(pc)
  28. callerName = fpc.Name()
  29. }
  30. fallthrough
  31. default:
  32. if callerFileName == "" {
  33. callerFileName, callerLineNumber = GetCaller()
  34. }
  35. }
  36. wd, _ := os.Getwd()
  37. if relFile, err := filepath.Rel(wd, callerFileName); err == nil {
  38. if !strings.HasPrefix(relFile, "..") {
  39. // Only if it's relative to this path, not parent.
  40. callerFileName = "./" + relFile
  41. }
  42. }
  43. return Source{
  44. File: filepath.ToSlash(callerFileName),
  45. Line: callerLineNumber,
  46. Caller: callerName,
  47. }
  48. }
  49. func getSource() Source {
  50. filename, line := GetCaller()
  51. return Source{
  52. File: filename,
  53. Line: line,
  54. }
  55. }
  56. func (s Source) String() string {
  57. return fmt.Sprintf("%s:%d", s.File, s.Line)
  58. }
  59. // https://golang.org/doc/go1.9#callersframes
  60. func GetCaller() (string, int) {
  61. var pcs [32]uintptr
  62. n := runtime.Callers(4, pcs[:])
  63. frames := runtime.CallersFrames(pcs[:n])
  64. for {
  65. frame, more := frames.Next()
  66. file := frame.File
  67. if strings.Contains(file, "go/src/runtime/") {
  68. continue
  69. }
  70. // funcName is something like "github.com/kataras/iris.SomeFunc"
  71. funcName := frame.Function
  72. if !strings.HasPrefix(funcName, "github.com/kataras/iris/v12") {
  73. return file, frame.Line
  74. }
  75. if !more {
  76. break
  77. }
  78. }
  79. return "???", 0
  80. }