Add find_files builtin, use it to fix find_and_copy implementation
The macro find-and-copy finds all the files in the given source tree that
match the given filename patten and create <source>:<dest> pair with the
same relative path in the destination tree.
Bug: 193540681
Test: rbcrun build/make/tests/run.rbc
Change-Id: Ic4315ce2fab7a7791ab55dd9eed039205a1c721a
diff --git a/tools/rbcrun/README.md b/tools/rbcrun/README.md
index fb58c89..ecf8a24 100644
--- a/tools/rbcrun/README.md
+++ b/tools/rbcrun/README.md
@@ -68,6 +68,11 @@
Returns `True` if *file* exists
+#### rblf_find_files(*top*, *file-pattern*, only_files = 0)
+
+Returns all the paths under *top* whose basename matches *pattern* (which is a shell's glob pattern). If *only_files* is
+not zero, only the paths to the regular files are returned. The returned paths are relative to *top*.
+
#### rblf_wildcard(*glob*, *top* = None)
Expands *glob*. If *top* is supplied, expands "*top*/*glob*", then removes
diff --git a/tools/rbcrun/host.go b/tools/rbcrun/host.go
index b3dd499..7f4f72d 100644
--- a/tools/rbcrun/host.go
+++ b/tools/rbcrun/host.go
@@ -16,6 +16,7 @@
import (
"fmt"
+ "io/fs"
"os"
"os/exec"
"path/filepath"
@@ -170,6 +171,46 @@
return makeStringList(files), nil
}
+// find(top, pattern, only_files = 0) returns all the paths under 'top'
+// whose basename matches 'pattern' (which is a shell's glob pattern).
+// If 'only_files' is non-zero, only the paths to the regular files are
+// returned. The returned paths are relative to 'top'.
+func find(_ *starlark.Thread, b *starlark.Builtin, args starlark.Tuple,
+ kwargs []starlark.Tuple) (starlark.Value, error) {
+ var top, pattern string
+ var onlyFiles int
+ if err := starlark.UnpackArgs(b.Name(), args, kwargs,
+ "top", &top, "pattern", &pattern, "only_files?", &onlyFiles); err != nil {
+ return starlark.None, err
+ }
+ top = filepath.Clean(top)
+ pattern = filepath.Clean(pattern)
+ // Go's filepath.Walk is slow, consider using OS's find
+ var res []string
+ err := filepath.WalkDir(top, func(path string, d fs.DirEntry, err error) error {
+ if err != nil {
+ if d != nil && d.IsDir() {
+ return fs.SkipDir
+ } else {
+ return nil
+ }
+ }
+ relPath := strings.TrimPrefix(path, top)
+ if len(relPath) > 0 && relPath[0] == os.PathSeparator {
+ relPath = relPath[1:]
+ }
+ // Do not return top-level dir
+ if len(relPath) == 0 {
+ return nil
+ }
+ if matched, err := filepath.Match(pattern, d.Name()); err == nil && matched && (onlyFiles == 0 || d.Type().IsRegular()) {
+ res = append(res, relPath)
+ }
+ return nil
+ })
+ return makeStringList(res), err
+}
+
// shell(command) runs OS shell with given command and returns back
// its output the same way as Make's $(shell ) function. The end-of-lines
// ("\n" or "\r\n") are replaced with " " in the result, and the trailing
@@ -226,6 +267,8 @@
"rblf_env": structFromEnv(os.Environ()),
// To convert makefile's $(wildcard foo)
"rblf_file_exists": starlark.NewBuiltin("rblf_file_exists", fileExists),
+ // To convert find-copy-subdir and product-copy-files-by pattern
+ "rblf_find_files": starlark.NewBuiltin("rblf_find_files", find),
// To convert makefile's $(filter ...)/$(filter-out)
"rblf_regex": starlark.NewBuiltin("rblf_regex", regexMatch),
// To convert makefile's $(shell cmd)
diff --git a/tools/rbcrun/testdata/file_ops.star b/tools/rbcrun/testdata/file_ops.star
index 31631ef..50e39bf 100644
--- a/tools/rbcrun/testdata/file_ops.star
+++ b/tools/rbcrun/testdata/file_ops.star
@@ -9,11 +9,17 @@
assert.true(not rblf_file_exists("no_such_file"), "the file no_such_file does not exist")
files = rblf_wildcard("*.star")
assert.true(myname in files, "expected %s in %s" % (myname, files))
- # RBCDATADIR is set by the caller to the path where this file resides
files = rblf_wildcard("*.star", rblf_env.TEST_DATA_DIR)
assert.true(myname in files, "expected %s in %s" % (myname, files))
files = rblf_wildcard("*.xxx")
assert.true(len(files) == 0, "expansion should be empty but contains %s" % files)
-
-
+ mydir = "testdata"
+ myrelname = "%s/%s" % (mydir, myname)
+ files = rblf_find_files(rblf_env.TEST_DATA_DIR + "/../", "*")
+ assert.true(mydir in files and myrelname in files, "expected %s and %s in %s" % (mydir, myrelname, files))
+ files = rblf_find_files(rblf_env.TEST_DATA_DIR + "/../", "*", only_files=1)
+ assert.true(mydir not in files, "did not expect %s in %s" % (mydir, files))
+ assert.true(myrelname in files, "expected %s in %s" % (myrelname, files))
+ files = rblf_find_files(rblf_env.TEST_DATA_DIR + "/../", "*.star")
+ assert.true(myrelname in files, "expected %s in %s" % (myrelname, files))
test()