--- /dev/null
+package main
+
+import (
+ "path"
+ "path/filepath"
+)
+
+var TgtCache = make(map[string]*Tgt)
+
+func mustAbs(pth string) string {
+ pth, err := filepath.Abs(pth)
+ if err != nil {
+ panic(err)
+ }
+ return pth
+}
+
+func mustRel(basepath, targpath string) string {
+ pth, err := filepath.Rel(basepath, targpath)
+ if err != nil {
+ panic(err)
+ }
+ return pth
+}
+
+func cwdMustRel(paths ...string) string {
+ return mustRel(Cwd, path.Join(paths...))
+}
+
+func cwdAndTgt(tgt string) (string, string) {
+ cwd, tgt := path.Split(tgt)
+ return mustAbs(cwd), tgt
+}
+
+type Tgt struct {
+ // a/h/t resemble zsh'es :a, :h, :t modifiers
+ a string // absolute path
+ h string // head of the path, directory
+ t string // tail of the path, only name
+ rel string // relative to Cwd
+ dep string // path to dependency file
+}
+
+func NewTgt(tgt string) *Tgt {
+ a := mustAbs(tgt)
+ if TgtCache != nil {
+ if t := TgtCache[a]; t != nil {
+ return t
+ }
+ }
+ t := Tgt{a: a}
+ t.h, t.t = path.Split(t.a)
+ if len(t.h) > 1 {
+ t.h = t.h[:len(t.h)-1]
+ }
+ t.rel = mustRel(Cwd, t.a)
+ if TgtCache != nil {
+ TgtCache[a] = &t
+ }
+ return &t
+}
+
+func (tgt *Tgt) String() string {
+ return tgt.rel
+}
+
+func (tgt *Tgt) Dep() string {
+ if tgt.dep == "" {
+ tgt.dep = path.Join(tgt.h, RedoDir, tgt.t+DepSuffix)
+ }
+ return tgt.dep
+}
+
+func (tgt *Tgt) RelTo(cwd string) string {
+ return mustRel(cwd, tgt.a)
+}