aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--expr.go29
-rw-r--r--func.go287
-rw-r--r--main.go2
-rw-r--r--shellutil.go222
-rw-r--r--shellutil_test.go (renamed from func_test.go)0
5 files changed, 261 insertions, 279 deletions
diff --git a/expr.go b/expr.go
index 6af75f1..6af8d6e 100644
--- a/expr.go
+++ b/expr.go
@@ -584,3 +584,32 @@ func (f funcstats) Eval(w io.Writer, ev *Evaluator) {
// TODO(ukai): per functype?
traceEvent.end(te)
}
+
+type matchVarref struct{}
+
+func (m matchVarref) String() string { return "$(match-any)" }
+func (m matchVarref) Eval(w io.Writer, ev *Evaluator) { panic("not implemented") }
+func (m matchVarref) Serialize() SerializableVar { panic("not implemented") }
+func (m matchVarref) Dump(w io.Writer) { panic("not implemented") }
+
+func matchExpr(expr, pat Expr) ([]Value, bool) {
+ if len(expr) != len(pat) {
+ return nil, false
+ }
+ var mv matchVarref
+ var matches []Value
+ for i := range expr {
+ if pat[i] == mv {
+ switch expr[i].(type) {
+ case paramref, varref:
+ matches = append(matches, expr[i])
+ continue
+ }
+ return nil, false
+ }
+ if expr[i] != pat[i] {
+ return nil, false
+ }
+ }
+ return matches, true
+}
diff --git a/func.go b/func.go
index 954a1d5..3ac9404 100644
--- a/func.go
+++ b/func.go
@@ -763,290 +763,19 @@ func (f *funcShell) Compact() Value {
if !ok {
return f
}
- // hack for android
- if v, ok := matchAndroidRot13(expr); ok {
- return &funcShellAndroidRot13{
- funcShell: f,
- v: v,
- }
- }
- if dir, ok := matchAndroidFindFileInDir(expr); ok {
- androidFindCache.init(nil)
- return &funcShellAndroidFindFileInDir{
- funcShell: f,
- dir: dir,
- }
- }
- if chdir, roots, ok := matchAndroidFindJavaInDir(expr); ok {
- androidFindCache.init(nil)
- return &funcShellAndroidFindJavaInDir{
- funcShell: f,
- chdir: chdir,
- roots: roots,
- }
- }
- if dir, ok := matchAndroidFindJavaResourceFileGroup(expr); ok {
- androidFindCache.init(nil)
- return &funcShellAndroidFindJavaResourceFileGroup{
- funcShell: f,
- dir: dir,
- }
- }
- Logf("shell compact no match: %s", expr)
- return f
-}
-
-// pattern in repo/android/build/core/definisions.mk
-// rot13
-// echo $(1) | tr 'a-zA-Z' 'n-za-mN-ZA-M'
-func matchAndroidRot13(expr Expr) (Value, bool) {
- // literal: "echo "
- // paramref: 1
- // literal: " | tr 'a-zA-Z' 'n-za-mN-ZA-M'"
- if len(expr) != 3 {
- return nil, false
- }
- if expr[0] != literal("echo ") {
- return nil, false
- }
- if expr[1] != paramref(1) {
- return nil, false
- }
- if expr[2] != literal(" | tr 'a-zA-Z' 'n-za-mN-ZA-M'") {
- return nil, false
- }
- return expr[1], true
-}
-
-type funcShellAndroidRot13 struct {
- *funcShell
- v Value
-}
-
-func rot13(buf []byte) {
- for i, b := range buf {
- // tr 'a-zA-Z' 'n-za-mN-ZA-M'
- if b >= 'a' && b <= 'z' {
- b += 'n' - 'a'
- if b > 'z' {
- b -= 'z' - 'a' + 1
+ if useShellBuiltins {
+ // hack for android
+ for _, sb := range shBuiltins {
+ if v, ok := matchExpr(expr, sb.pattern); ok {
+ Logf("shell compact apply %s for %s", sb.name, expr)
+ return sb.compact(f, v)
}
- } else if b >= 'A' && b <= 'Z' {
- b += 'N' - 'A'
- if b > 'Z' {
- b -= 'Z' - 'A' + 1
- }
- }
- buf[i] = b
- }
-}
-
-func (f *funcShellAndroidRot13) Eval(w io.Writer, ev *Evaluator) {
- abuf := newBuf()
- fargs := ev.args(abuf, f.v)
- rot13(fargs[0])
- w.Write(fargs[0])
- freeBuf(abuf)
-}
-
-// pattern in repo/android/build/core/definitions.mk
-// find-subdir-assets
-// if [ -d $1 ] ; then cd $1 ; find ./ -not -name '.*' -and -type f -and -not -type l ; fi
-func matchAndroidFindFileInDir(expr Expr) (Value, bool) {
- // literal: "if [ -d "
- // paramref: 1
- // literal: " ] ; then cd "
- // paramref: 1
- // literal: " ; find ./ -not -name '.*' -and -type f -and -not -type l ; fi"
- if len(expr) != 5 {
- return nil, false
- }
- if expr[0] != literal("if [ -d ") {
- return nil, false
- }
- if expr[1] != paramref(1) {
- return nil, false
- }
- if expr[2] != literal(" ] ; then cd ") {
- return nil, false
- }
- if expr[3] != paramref(1) {
- return nil, false
- }
- if expr[4] != literal(" ; find ./ -not -name '.*' -and -type f -and -not -type l ; fi") {
- return nil, false
- }
- return paramref(1), true
-}
-
-type funcShellAndroidFindFileInDir struct {
- *funcShell
- dir Value
-}
-
-func (f *funcShellAndroidFindFileInDir) Eval(w io.Writer, ev *Evaluator) {
- abuf := newBuf()
- fargs := ev.args(abuf, f.dir)
- dir := string(trimSpaceBytes(fargs[0]))
- freeBuf(abuf)
- Logf("shellAndroidFindFileInDir %s => %s", f.dir.String(), dir)
- if strings.Contains(dir, "..") {
- Logf("shellAndroidFindFileInDir contains ..: call original shell")
- f.funcShell.Eval(w, ev)
- return
- }
- if !androidFindCache.ready() {
- Logf("shellAndroidFindFileInDir androidFindCache is not ready: call original shell")
- f.funcShell.Eval(w, ev)
- return
- }
- sw := ssvWriter{w: w}
- androidFindCache.findInDir(&sw, dir)
-}
-
-// pattern in repo/android/build/core/definitions.mk
-// all-java-files-under
-// cd ${LOCAL_PATH} ; find -L $1 -name "*.java" -and -not -name ".*"
-func matchAndroidFindJavaInDir(expr Expr) (Value, Value, bool) {
- // literal: "cd "
- // varref: xxx
- // literal: " ; find -L "
- // paramref: 1
- // literal: " -name "*.java" -and -not -name ".*"
- if len(expr) != 5 {
- return nil, nil, false
- }
- if expr[0] != literal("cd ") {
- return nil, nil, false
- }
- if _, ok := expr[1].(varref); !ok {
- return nil, nil, false
- }
- if expr[2] != literal(" ; find -L ") {
- return nil, nil, false
- }
- if expr[3] != paramref(1) {
- return nil, nil, false
- }
- if expr[4] != literal(` -name "*.java" -and -not -name ".*"`) {
- return nil, nil, false
- }
- return expr[1], paramref(1), true
-}
-
-type funcShellAndroidFindJavaInDir struct {
- *funcShell
- chdir Value
- roots Value
-}
-
-func (f *funcShellAndroidFindJavaInDir) Eval(w io.Writer, ev *Evaluator) {
- abuf := newBuf()
- fargs := ev.args(abuf, f.chdir, f.roots)
- chdir := string(trimSpaceBytes(fargs[0]))
- var roots []string
- hasDotDot := false
- ws := newWordScanner(fargs[1])
- for ws.Scan() {
- root := string(ws.Bytes())
- if strings.Contains(root, "..") {
- hasDotDot = true
- }
- roots = append(roots, string(ws.Bytes()))
- }
- freeBuf(abuf)
- Logf("shellAndroidFindJavaInDir %s,%s => %s,%s", f.chdir.String(), f.roots.String(), chdir, roots)
- if strings.Contains(chdir, "..") || hasDotDot {
- Logf("shellAndroidFindJavaInDir contains ..: call original shell")
- f.funcShell.Eval(w, ev)
- return
- }
- if !androidFindCache.ready() {
- Logf("shellAndroidFindJavaInDir androidFindCache is not ready: call original shell")
- f.funcShell.Eval(w, ev)
- return
- }
- buf := newBuf()
- sw := ssvWriter{w: buf}
- for _, root := range roots {
- if !androidFindCache.findJavaInDir(&sw, chdir, root) {
- freeBuf(buf)
- Logf("shellAndroidFindJavaInDir androidFindCache couldn't handle: call original shell")
- f.funcShell.Eval(w, ev)
- return
}
+ Logf("shell compact no match: %s", expr)
}
- w.Write(buf.Bytes())
- freeBuf(buf)
-}
-
-// pattern: in repo/android/build/core/base_rules.mk
-// java_resource_file_groups+= ...
-// cd ${TOP_DIR}${LOCAL_PATH}/${dir} && find . -type d -a -name ".svn" -prune \
-// -o -type f -a \! -name "*.java" -a \! -name "package.html" -a \! \
-// -name "overview.html" -a \! -name ".*.swp" -a \! -name ".DS_Store" \
-// -a \! -name "*~" -print )
-func matchAndroidFindJavaResourceFileGroup(expr Expr) (Value, bool) {
- // literal: "cd "
- // varref: TOP_DIR
- // varref: LOCAL_PATH
- // literal: "/"
- // varref: dir
- // literal: " && find . -type d -a name ".svn" -prune -o .."
- if len(expr) != 6 {
- return nil, false
- }
- if expr[0] != literal("cd ") {
- return nil, false
- }
- if _, ok := expr[1].(varref); !ok {
- return nil, false
- }
- if _, ok := expr[2].(varref); !ok {
- return nil, false
- }
- if expr[3] != literal("/") {
- return nil, false
- }
- if _, ok := expr[4].(varref); !ok {
- return nil, false
- }
- if expr[5] != literal(` && find . -type d -a -name ".svn" -prune -o -type f -a \! -name "*.java" -a \! -name "package.html" -a \! -name "overview.html" -a \! -name ".*.swp" -a \! -name ".DS_Store" -a \! -name "*~" -print `) {
- Logf("shell compact mismatch: expr[5]=%q", expr[5])
- return nil, false
- }
- return expr[1:5], true
-}
-
-type funcShellAndroidFindJavaResourceFileGroup struct {
- *funcShell
- dir Value
-}
-
-func (f *funcShellAndroidFindJavaResourceFileGroup) Eval(w io.Writer, ev *Evaluator) {
- abuf := newBuf()
- fargs := ev.args(abuf, f.dir)
- dir := string(trimSpaceBytes(fargs[0]))
- freeBuf(abuf)
- Logf("shellAndroidFindJavaResourceFileGroup %s => %s", f.dir.String(), dir)
- if strings.Contains(dir, "..") {
- Logf("shellAndroidFindJavaResourceFileGroup contains ..: call original shell")
- f.funcShell.Eval(w, ev)
- return
- }
- if !androidFindCache.ready() {
- Logf("shellAndroidFindJavaResourceFileGroup androidFindCache is not ready: call original shell")
- f.funcShell.Eval(w, ev)
- return
- }
- sw := ssvWriter{w: w}
- androidFindCache.findJavaResourceFileGroup(&sw, dir)
+ return f
}
-// TODO(ukai): pattern:
-//
-// echo $1 | tr 'a-zA-Z' 'n-za-mN-ZA-M'
-
// https://www.gnu.org/software/make/manual/html_node/Call-Function.html#Call-Function
type funcCall struct{ fclosure }
diff --git a/main.go b/main.go
index f414a0b..b6ccf98 100644
--- a/main.go
+++ b/main.go
@@ -53,6 +53,7 @@ var (
useFindCache bool
findCachePrunes string
useWildcardCache bool
+ useShellBuiltins bool
generateNinja bool
ignoreOptionalInclude string
gomaDir string
@@ -99,6 +100,7 @@ func parseFlags() {
flag.StringVar(&findCachePrunes, "find_cache_prunes", "",
"space separated prune directories for find cache.")
flag.BoolVar(&useWildcardCache, "use_wildcard_cache", true, "Use wildcard cache.")
+ flag.BoolVar(&useShellBuiltins, "use_shell_builtins", true, "Use shell builtins")
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.")
diff --git a/shellutil.go b/shellutil.go
new file mode 100644
index 0000000..0c0cd17
--- /dev/null
+++ b/shellutil.go
@@ -0,0 +1,222 @@
+package main
+
+import (
+ "io"
+ "strings"
+)
+
+var shBuiltins = []struct {
+ name string
+ pattern Expr
+ compact func(*funcShell, []Value) Value
+}{
+ {
+ name: "android:rot13",
+ // in repo/android/build/core/definisions.mk
+ // echo $(1) | tr 'a-zA-Z' 'n-za-mN-ZA-M'
+ pattern: Expr{
+ literal("echo "),
+ matchVarref{},
+ literal(" | tr 'a-zA-Z' 'n-za-mN-ZA-M'"),
+ },
+ compact: func(sh *funcShell, matches []Value) Value {
+ return &funcShellAndroidRot13{
+ funcShell: sh,
+ v: matches[0],
+ }
+ },
+ },
+ {
+ name: "android:find-subdir-assets",
+ // in repo/android/build/core/definitions.mk
+ // if [ -d $1 ] ; then cd $1 ; find ./ -not -name '.*' -and -type f -and -not -type l ; fi
+ pattern: Expr{
+ literal("if [ -d "),
+ matchVarref{},
+ literal(" ] ; then cd "),
+ matchVarref{},
+ literal(" ; find ./ -not -name '.*' -and -type f -and -not -type l ; fi"),
+ },
+ compact: func(sh *funcShell, v []Value) Value {
+ if v[0] != v[1] {
+ return sh
+ }
+ androidFindCache.init(nil)
+ return &funcShellAndroidFindFileInDir{
+ funcShell: sh,
+ dir: v[0],
+ }
+ },
+ },
+ {
+ name: "android:all-java-files-under",
+ // in repo/android/build/core/definitions.mk
+ // cd ${LOCAL_PATH} ; find -L $1 -name "*.java" -and -not -name ".*"
+ pattern: Expr{
+ literal("cd "),
+ matchVarref{},
+ literal(" ; find -L "),
+ matchVarref{},
+ literal(` -name "*.java" -and -not -name ".*"`),
+ },
+ compact: func(sh *funcShell, v []Value) Value {
+ androidFindCache.init(nil)
+ return &funcShellAndroidFindJavaInDir{
+ funcShell: sh,
+ chdir: v[0],
+ roots: v[1],
+ }
+ },
+ },
+ {
+ name: "android:java_resource_file_groups",
+ // in repo/android/build/core/base_rules.mk
+ // cd ${TOP_DIR}${LOCAL_PATH}/${dir} && find . -type d -a \
+ // -name ".svn" -prune -o -type f -a \! -name "*.java" \
+ // -a \! -name "package.html" -a \! -name "overview.html" \
+ // -a \! -name ".*.swp" -a \! -name ".DS_Store" \
+ // -a \! -name "*~" -print )
+ pattern: Expr{
+ literal("cd "),
+ matchVarref{},
+ matchVarref{},
+ literal("/"),
+ matchVarref{},
+ literal(` && find . -type d -a -name ".svn" -prune -o -type f -a \! -name "*.java" -a \! -name "package.html" -a \! -name "overview.html" -a \! -name ".*.swp" -a \! -name ".DS_Store" -a \! -name "*~" -print `),
+ },
+ compact: func(sh *funcShell, v []Value) Value {
+ androidFindCache.init(nil)
+ return &funcShellAndroidFindJavaResourceFileGroup{
+ funcShell: sh,
+ dir: Expr(v),
+ }
+ },
+ },
+}
+
+type funcShellAndroidRot13 struct {
+ *funcShell
+ v Value
+}
+
+func rot13(buf []byte) {
+ for i, b := range buf {
+ // tr 'a-zA-Z' 'n-za-mN-ZA-M'
+ if b >= 'a' && b <= 'z' {
+ b += 'n' - 'a'
+ if b > 'z' {
+ b -= 'z' - 'a' + 1
+ }
+ } else if b >= 'A' && b <= 'Z' {
+ b += 'N' - 'A'
+ if b > 'Z' {
+ b -= 'Z' - 'A' + 1
+ }
+ }
+ buf[i] = b
+ }
+}
+
+func (f *funcShellAndroidRot13) Eval(w io.Writer, ev *Evaluator) {
+ abuf := newBuf()
+ fargs := ev.args(abuf, f.v)
+ rot13(fargs[0])
+ w.Write(fargs[0])
+ freeBuf(abuf)
+}
+
+type funcShellAndroidFindFileInDir struct {
+ *funcShell
+ dir Value
+}
+
+func (f *funcShellAndroidFindFileInDir) Eval(w io.Writer, ev *Evaluator) {
+ abuf := newBuf()
+ fargs := ev.args(abuf, f.dir)
+ dir := string(trimSpaceBytes(fargs[0]))
+ freeBuf(abuf)
+ Logf("shellAndroidFindFileInDir %s => %s", f.dir.String(), dir)
+ if strings.Contains(dir, "..") {
+ Logf("shellAndroidFindFileInDir contains ..: call original shell")
+ f.funcShell.Eval(w, ev)
+ return
+ }
+ if !androidFindCache.ready() {
+ Logf("shellAndroidFindFileInDir androidFindCache is not ready: call original shell")
+ f.funcShell.Eval(w, ev)
+ return
+ }
+ sw := ssvWriter{w: w}
+ androidFindCache.findInDir(&sw, dir)
+}
+
+type funcShellAndroidFindJavaInDir struct {
+ *funcShell
+ chdir Value
+ roots Value
+}
+
+func (f *funcShellAndroidFindJavaInDir) Eval(w io.Writer, ev *Evaluator) {
+ abuf := newBuf()
+ fargs := ev.args(abuf, f.chdir, f.roots)
+ chdir := string(trimSpaceBytes(fargs[0]))
+ var roots []string
+ hasDotDot := false
+ ws := newWordScanner(fargs[1])
+ for ws.Scan() {
+ root := string(ws.Bytes())
+ if strings.Contains(root, "..") {
+ hasDotDot = true
+ }
+ roots = append(roots, string(ws.Bytes()))
+ }
+ freeBuf(abuf)
+ Logf("shellAndroidFindJavaInDir %s,%s => %s,%s", f.chdir.String(), f.roots.String(), chdir, roots)
+ if strings.Contains(chdir, "..") || hasDotDot {
+ Logf("shellAndroidFindJavaInDir contains ..: call original shell")
+ f.funcShell.Eval(w, ev)
+ return
+ }
+ if !androidFindCache.ready() {
+ Logf("shellAndroidFindJavaInDir androidFindCache is not ready: call original shell")
+ f.funcShell.Eval(w, ev)
+ return
+ }
+ buf := newBuf()
+ sw := ssvWriter{w: buf}
+ for _, root := range roots {
+ if !androidFindCache.findJavaInDir(&sw, chdir, root) {
+ freeBuf(buf)
+ Logf("shellAndroidFindJavaInDir androidFindCache couldn't handle: call original shell")
+ f.funcShell.Eval(w, ev)
+ return
+ }
+ }
+ w.Write(buf.Bytes())
+ freeBuf(buf)
+}
+
+type funcShellAndroidFindJavaResourceFileGroup struct {
+ *funcShell
+ dir Value
+}
+
+func (f *funcShellAndroidFindJavaResourceFileGroup) Eval(w io.Writer, ev *Evaluator) {
+ abuf := newBuf()
+ fargs := ev.args(abuf, f.dir)
+ dir := string(trimSpaceBytes(fargs[0]))
+ freeBuf(abuf)
+ Logf("shellAndroidFindJavaResourceFileGroup %s => %s", f.dir.String(), dir)
+ if strings.Contains(dir, "..") {
+ Logf("shellAndroidFindJavaResourceFileGroup contains ..: call original shell")
+ f.funcShell.Eval(w, ev)
+ return
+ }
+ if !androidFindCache.ready() {
+ Logf("shellAndroidFindJavaResourceFileGroup androidFindCache is not ready: call original shell")
+ f.funcShell.Eval(w, ev)
+ return
+ }
+ sw := ssvWriter{w: w}
+ androidFindCache.findJavaResourceFileGroup(&sw, dir)
+}
diff --git a/func_test.go b/shellutil_test.go
index e438b88..e438b88 100644
--- a/func_test.go
+++ b/shellutil_test.go