add -shell-date flag to specify time for $(shell date)
--shell-date=ref uses go reference time. useful to check
parsed data with golden data.
diff --git a/expr.go b/expr.go
index ad05c4b..91c861b 100644
--- a/expr.go
+++ b/expr.go
@@ -19,6 +19,7 @@
"errors"
"fmt"
"io"
+ "regexp"
"strconv"
"strings"
"sync"
@@ -623,6 +624,21 @@
func (m matchVarref) Serialize() SerializableVar { panic("not implemented") }
func (m matchVarref) Dump(w io.Writer) { panic("not implemented") }
+type literalRE struct {
+ *regexp.Regexp
+}
+
+func mustLiteralRE(s string) literalRE {
+ return literalRE{
+ Regexp: regexp.MustCompile(s),
+ }
+}
+
+func (r literalRE) String() string { return r.Regexp.String() }
+func (r literalRE) Eval(w io.Writer, ev *Evaluator) { panic("not implemented") }
+func (r literalRE) Serialize() SerializableVar { panic("not implemented") }
+func (r literalRE) Dump(w io.Writer) { panic("not implemented") }
+
func matchValue(expr, pat Value) bool {
switch pat := pat.(type) {
case literal:
@@ -647,6 +663,17 @@
}
return nil, false
}
+ if patre, ok := pat[i].(literalRE); ok {
+ re := patre.Regexp
+ m := re.FindStringSubmatch(expr[i].String())
+ if m == nil {
+ return nil, false
+ }
+ for _, sm := range m[1:] {
+ matches = append(matches, literal(sm))
+ }
+ continue
+ }
if !matchValue(expr[i], pat[i]) {
return nil, false
}
diff --git a/func.go b/func.go
index a164309..364545b 100644
--- a/func.go
+++ b/func.go
@@ -774,6 +774,9 @@
shellVar := ev.LookupVar("SHELL")
// TODO: Should be Eval, not String.
cmdline := []string{shellVar.String(), "-c", arg}
+ if katiLogFlag {
+ Logf("shell %q", cmdline)
+ }
cmd := exec.Cmd{
Path: cmdline[0],
Args: cmdline,
@@ -793,13 +796,16 @@
if len(f.args)-1 < 1 {
return f
}
- if !useFindCache {
+ if !useFindCache && !useShellBuiltins {
return f
}
- expr, ok := f.args[1].(Expr)
- if !ok {
- return f
+ var expr Expr
+ switch v := f.args[1].(type) {
+ case Expr:
+ expr = v
+ default:
+ expr = Expr{v}
}
if useShellBuiltins {
// hack for android
diff --git a/main.go b/main.go
index 691c332..15ad142 100644
--- a/main.go
+++ b/main.go
@@ -29,6 +29,8 @@
"time"
)
+const shellDateTimeformat = time.RFC3339
+
var (
katiLogFlag bool
makefileFlag string
@@ -55,6 +57,7 @@
findCacheLeafNames string
useWildcardCache bool
useShellBuiltins bool
+ shellDate string
generateNinja bool
ignoreOptionalInclude string
gomaDir string
@@ -104,6 +107,7 @@
"space separated leaf names for find cache.")
flag.BoolVar(&useWildcardCache, "use_wildcard_cache", true, "Use wildcard cache.")
flag.BoolVar(&useShellBuiltins, "use_shell_builtins", true, "Use shell builtins")
+ flag.StringVar(&shellDate, "shell_date", "", "specify $(shell date) time as "+shellDateTimeformat)
flag.BoolVar(&generateNinja, "ninja", false, "Generate build.ninja.")
flag.StringVar(&ignoreOptionalInclude, "ignore_optional_include", "", "If specified, skip reading -include directives start with the specified path.")
flag.StringVar(&gomaDir, "goma_dir", "", "If specified, use goma to build C/C++ files.")
@@ -324,6 +328,17 @@
defer traceEvent.stop()
}
+ if shellDate != "" {
+ if shellDate == "ref" {
+ shellDate = shellDateTimeformat[:20] // until Z, drop 07:00
+ }
+ t, err := time.Parse(shellDateTimeformat, shellDate)
+ if err != nil {
+ panic(err)
+ }
+ shellDateTimestamp = t
+ }
+
if findCacheLeafNames != "" {
androidDefaultLeafNames = strings.Fields(findCacheLeafNames)
}
diff --git a/shellutil.go b/shellutil.go
index cf6d514..c73711d 100644
--- a/shellutil.go
+++ b/shellutil.go
@@ -15,8 +15,10 @@
package main
import (
+ "fmt"
"io"
"strings"
+ "time"
)
var androidDefaultLeafNames = []string{"CleanSpec.mk", "Android.mk"}
@@ -217,6 +219,20 @@
}
},
},
+ {
+ name: "shell-date",
+ pattern: Expr{
+ mustLiteralRE(`date \+(\S+)`),
+ },
+ compact: compactShellDate,
+ },
+ {
+ name: "shell-date-quoted",
+ pattern: Expr{
+ mustLiteralRE(`date "\+([^"]+)"`),
+ },
+ compact: compactShellDate,
+ },
}
type funcShellAndroidRot13 struct {
@@ -390,3 +406,44 @@
androidFindCache.findleaves(&sw, dir, name, prunes, f.mindepth)
}
}
+
+var (
+ shellDateTimestamp time.Time
+ shellDateFormatRef = map[string]string{
+ "%Y": "2006",
+ "%m": "01",
+ "%d": "02",
+ "%H": "15",
+ "%M": "04",
+ "%S": "05",
+ "%b": "Jan",
+ "%k": "15", // XXX
+ }
+)
+
+type funcShellDate struct {
+ *funcShell
+ format string
+}
+
+func compactShellDate(sh *funcShell, v []Value) Value {
+ if shellDateTimestamp.IsZero() {
+ return sh
+ }
+ tf, ok := v[0].(literal)
+ if !ok {
+ return sh
+ }
+ tfstr := string(tf)
+ for k, v := range shellDateFormatRef {
+ tfstr = strings.Replace(tfstr, k, v, -1)
+ }
+ return &funcShellDate{
+ funcShell: sh,
+ format: tfstr,
+ }
+}
+
+func (f *funcShellDate) Eval(w io.Writer, ev *Evaluator) {
+ fmt.Fprint(w, shellDateTimestamp.Format(f.format))
+}
diff --git a/shellutil_test.go b/shellutil_test.go
index e438b88..c54e761 100644
--- a/shellutil_test.go
+++ b/shellutil_test.go
@@ -14,7 +14,10 @@
package main
-import "testing"
+import (
+ "testing"
+ "time"
+)
func TestRot13(t *testing.T) {
for _, tc := range []struct {
@@ -37,3 +40,63 @@
}
}
}
+
+func TestShellDate(t *testing.T) {
+ ts := shellDateTimestamp
+ shellDateTimestamp = time.Now()
+ defer func() {
+ shellDateTimestamp = ts
+ }()
+ for _, tc := range []struct {
+ sharg literal
+ format string
+ }{
+ {
+ sharg: literal("date +%Y-%m-%d"),
+ format: "2006-01-02",
+ },
+ {
+ sharg: literal("date +%Y%m%d.%H%M%S"),
+ format: "20060102.150405",
+ },
+ {
+ sharg: literal(`date "+%d %b %Y %k:%M"`),
+ format: "02 Jan 2006 15:04",
+ },
+ } {
+ var matched bool
+ for _, b := range shBuiltins {
+ if b.name != "shell-date" && b.name != "shell-date-quoted" {
+ continue
+ }
+ m, ok := matchExpr(Expr{tc.sharg}, b.pattern)
+ if !ok {
+ t.Logf("%s not match with %s", b.name, tc.sharg)
+ continue
+ }
+ f := &funcShell{
+ fclosure: fclosure{
+ args: []Value{
+ literal("(shell"),
+ tc.sharg,
+ },
+ },
+ }
+ v := b.compact(f, m)
+ sd, ok := v.(*funcShellDate)
+ if !ok {
+ t.Errorf("%s: matched %s but not compacted", tc.sharg, b.name)
+ continue
+ }
+ if got, want := sd.format, tc.format; got != want {
+ t.Errorf("%s: format=%q, want=%q - %s", tc.sharg, got, want, b.name)
+ continue
+ }
+ matched = true
+ break
+ }
+ if !matched {
+ t.Errorf("%s: not matched", tc.sharg)
+ }
+ }
+}