blob: 1653b3e572dfec51220874394522b1e7e234ac27 [file] [log] [blame]
Shinichiro Hamajib69bf8a2015-06-10 14:52:06 +09001// Copyright 2015 Google Inc. All rights reserved
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +090015package kati
Fumitoshi Ukai358c68a2015-06-08 13:12:55 +090016
17import (
Fumitoshi Ukaic0ded232015-07-07 11:40:04 +090018 "bytes"
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +090019 "errors"
20 "fmt"
Fumitoshi Ukai106fb792015-06-09 10:37:35 +090021 "os"
Fumitoshi Ukai358c68a2015-06-08 13:12:55 +090022 "os/exec"
23 "path/filepath"
Fumitoshi Ukai83410132015-06-15 14:50:07 +090024 "runtime"
Fumitoshi Ukai106fb792015-06-09 10:37:35 +090025 "sort"
Fumitoshi Ukai358c68a2015-06-08 13:12:55 +090026 "strings"
Fumitoshi Ukai106fb792015-06-09 10:37:35 +090027 "sync"
28 "time"
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +090029
30 "github.com/golang/glog"
Fumitoshi Ukai358c68a2015-06-08 13:12:55 +090031)
32
Fumitoshi Ukai9042b992015-06-23 16:10:27 +090033type wildcardCacheT struct {
34 mu sync.Mutex
35 m map[string][]string
36}
37
38var wildcardCache = &wildcardCacheT{
39 m: make(map[string][]string),
40}
Fumitoshi Ukai358c68a2015-06-08 13:12:55 +090041
Fumitoshi Ukaic0ded232015-07-07 11:40:04 +090042func wildcardUnescape(pat string) string {
43 var buf bytes.Buffer
44 for i := 0; i < len(pat); i++ {
45 if pat[i] == '\\' && i+1 < len(pat) {
46 switch pat[i+1] {
47 case '*', '?', '[', '\\':
48 writeByte(&buf, pat[i])
49 default:
50 i++
51 }
52 }
53 writeByte(&buf, pat[i])
54 }
55 return buf.String()
56}
57
Fumitoshi Ukai65c72332015-06-26 21:32:50 +090058func wildcardGlob(pat string) ([]string, error) {
Fumitoshi Ukai9d959c32015-06-19 18:06:01 +090059 // TODO(ukai): use find cache for glob if exists.
Fumitoshi Ukaic0ded232015-07-07 11:40:04 +090060 pat = wildcardUnescape(pat)
Fumitoshi Ukai9d959c32015-06-19 18:06:01 +090061 pattern := filepath.Clean(pat)
62 if pattern != pat {
63 // For some reason, go's Glob normalizes
64 // foo/../bar to bar.
65 i := strings.IndexAny(pattern, "*?[")
66 if i < 0 {
67 // no wildcard. if any files matched with pattern,
68 // return pat.
69 _, err := os.Stat(pat)
70 if err != nil {
Fumitoshi Ukai65c72332015-06-26 21:32:50 +090071 return nil, nil
Fumitoshi Ukai9d959c32015-06-19 18:06:01 +090072 }
Fumitoshi Ukai65c72332015-06-26 21:32:50 +090073 return []string{pat}, nil
Fumitoshi Ukai9d959c32015-06-19 18:06:01 +090074 }
75 if strings.Contains(pattern[i+1:], "..") {
76 // We ask shell to expand a glob to avoid this.
77 cmdline := []string{"/bin/sh", "-c", "/bin/ls -d " + pat}
78 cmd := exec.Cmd{
79 Path: cmdline[0],
80 Args: cmdline,
81 }
82 // Ignore errors.
83 out, _ := cmd.Output()
84 ws := newWordScanner(out)
85 var files []string
86 for ws.Scan() {
87 files = append(files, string(ws.Bytes()))
88 }
Fumitoshi Ukai65c72332015-06-26 21:32:50 +090089 return files, nil
Fumitoshi Ukai9d959c32015-06-19 18:06:01 +090090 }
91 // prefix + meta + suffix, and suffix doesn't have '..'
92 prefix := pattern[:i]
93 i = strings.IndexAny(pat, "*?[")
94 if i < 0 {
Fumitoshi Ukai65c72332015-06-26 21:32:50 +090095 return nil, fmt.Errorf("wildcard metachar mismatch? pattern=%q pat=%q", pattern, pat)
Fumitoshi Ukai9d959c32015-06-19 18:06:01 +090096 }
97 oprefix := pat[:i]
98 matched, err := filepath.Glob(pattern)
99 if err != nil {
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900100 return nil, err
Fumitoshi Ukai9d959c32015-06-19 18:06:01 +0900101 }
102 var files []string
103 for _, m := range matched {
104 file := oprefix + strings.TrimPrefix(m, prefix)
105 _, err := os.Stat(file)
106 if err != nil {
107 continue
108 }
109 files = append(files, file)
110 }
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900111 return files, nil
Fumitoshi Ukai9d959c32015-06-19 18:06:01 +0900112 }
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900113 return filepath.Glob(pat)
Fumitoshi Ukai9d959c32015-06-19 18:06:01 +0900114}
115
Fumitoshi Ukaib44b12d2015-07-07 14:19:32 +0900116func wildcard(w evalWriter, pat string) error {
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900117 if UseWildcardCache {
Fumitoshi Ukai358c68a2015-06-08 13:12:55 +0900118 // TODO(ukai): make sure it didn't chdir?
Fumitoshi Ukai9042b992015-06-23 16:10:27 +0900119 wildcardCache.mu.Lock()
120 files, ok := wildcardCache.m[pat]
121 wildcardCache.mu.Unlock()
122 if ok {
Fumitoshi Ukai358c68a2015-06-08 13:12:55 +0900123 for _, file := range files {
Fumitoshi Ukaib44b12d2015-07-07 14:19:32 +0900124 w.writeWordString(file)
Fumitoshi Ukai358c68a2015-06-08 13:12:55 +0900125 }
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900126 return nil
Fumitoshi Ukai358c68a2015-06-08 13:12:55 +0900127 }
128 }
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900129 files, err := wildcardGlob(pat)
130 if err != nil {
131 return err
132 }
Fumitoshi Ukai358c68a2015-06-08 13:12:55 +0900133 for _, file := range files {
Fumitoshi Ukaib44b12d2015-07-07 14:19:32 +0900134 w.writeWordString(file)
Fumitoshi Ukai358c68a2015-06-08 13:12:55 +0900135 }
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900136 if UseWildcardCache {
Fumitoshi Ukai9042b992015-06-23 16:10:27 +0900137 wildcardCache.mu.Lock()
138 wildcardCache.m[pat] = files
139 wildcardCache.mu.Unlock()
Fumitoshi Ukai358c68a2015-06-08 13:12:55 +0900140 }
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900141 return nil
Fumitoshi Ukai358c68a2015-06-08 13:12:55 +0900142}
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900143
144type fileInfo struct {
145 path string
146 mode os.FileMode
147}
148
149type androidFindCacheT struct {
150 once sync.Once
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900151 filesch chan []fileInfo
152 leavesch chan []fileInfo
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900153 files []fileInfo
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900154 leaves []fileInfo
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900155 scanTime time.Duration
156}
157
158var (
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900159 androidFindCache androidFindCacheT
160 androidDefaultLeafNames = []string{"CleanSpec.mk", "Android.mk"}
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900161)
162
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900163// AndroidFindCacheInit initializes find cache for android build.
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900164func AndroidFindCacheInit(prunes, leafNames []string) {
Fumitoshi Ukai1394d102015-07-08 16:45:49 +0900165 if !UseFindCache {
166 return
167 }
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900168 if leafNames != nil {
169 androidDefaultLeafNames = leafNames
170 }
171 androidFindCache.init(prunes)
172}
173
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900174func (c *androidFindCacheT) ready() bool {
Fumitoshi Ukai1394d102015-07-08 16:45:49 +0900175 if !UseFindCache {
176 return false
177 }
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900178 if c.files != nil {
179 return true
180 }
181 select {
182 case c.files = <-c.filesch:
183 }
184 return c.files != nil
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900185}
186
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900187func (c *androidFindCacheT) leavesReady() bool {
Fumitoshi Ukai1394d102015-07-08 16:45:49 +0900188 if !UseFindCache {
189 return false
190 }
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900191 if c.leaves != nil {
192 return true
193 }
194 select {
195 case c.leaves = <-c.leavesch:
196 }
197 return c.leaves != nil
198}
199
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900200func (c *androidFindCacheT) init(prunes []string) {
Fumitoshi Ukai1394d102015-07-08 16:45:49 +0900201 if !UseFindCache {
202 return
203 }
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900204 c.once.Do(func() {
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900205 c.filesch = make(chan []fileInfo, 1)
206 c.leavesch = make(chan []fileInfo, 1)
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900207 go c.start(prunes, androidDefaultLeafNames)
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900208 })
209}
210
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900211func (c *androidFindCacheT) start(prunes, leafNames []string) {
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900212 glog.Infof("find cache init: prunes=%q leafNames=%q", prunes, leafNames)
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900213 te := traceEvent.begin("findcache", literal("init"), traceEventFindCache)
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900214 defer func() {
Fumitoshi Ukaif543f4d2015-06-15 15:21:47 +0900215 traceEvent.end(te)
216 c.scanTime = time.Since(te.t)
Fumitoshi Ukai49599e52015-06-26 10:10:24 +0900217 logStats("android find cache scan: %v", c.scanTime)
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900218 }()
219
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900220 dirs := make(chan string, 32)
221 filech := make(chan fileInfo, 1000)
222 leafch := make(chan fileInfo, 1000)
223 var wg sync.WaitGroup
224 numWorker := runtime.NumCPU() - 1
225 wg.Add(numWorker)
226 for i := 0; i < numWorker; i++ {
227 go func() {
228 defer wg.Done()
229 for dir := range dirs {
230 err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
231 if info.IsDir() {
232 for _, prune := range prunes {
233 if info.Name() == prune {
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900234 glog.V(1).Infof("find cache prune: %s", path)
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900235 return filepath.SkipDir
236 }
237 }
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900238 }
239 filech <- fileInfo{
240 path: path,
241 mode: info.Mode(),
242 }
243 for _, leaf := range leafNames {
244 if info.Name() == leaf {
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900245 glog.V(1).Infof("find cache leaf: %s", path)
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900246 leafch <- fileInfo{
247 path: path,
248 mode: info.Mode(),
249 }
250 break
251 }
252 }
253 return nil
254 })
255 if err != nil && err != filepath.SkipDir {
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900256 glog.Warningf("error in adnroid find cache: %v", err)
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900257 close(c.filesch)
258 close(c.leavesch)
259 return
Fumitoshi Ukai0daac1f2015-06-11 11:56:50 +0900260 }
261 }
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900262 }()
263 }
264
265 go func() {
Fumitoshi Ukai040271f2015-06-18 11:04:46 +0900266 dirs := make(map[string]bool)
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900267 leavesTe := traceEvent.begin("findcache", literal("leaves"), traceEventFindCacheLeaves)
268 var leaves []fileInfo
Fumitoshi Ukai040271f2015-06-18 11:04:46 +0900269 nfiles := 0
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900270 for leaf := range leafch {
271 leaves = append(leaves, leaf)
Fumitoshi Ukai040271f2015-06-18 11:04:46 +0900272 nfiles++
273 for dir := filepath.Dir(leaf.path); dir != "."; dir = filepath.Dir(dir) {
274 if dirs[dir] {
275 break
276 }
277 leaves = append(leaves, fileInfo{
278 path: dir,
279 mode: leaf.mode | os.ModeDir,
280 })
281 dirs[dir] = true
282 }
Fumitoshi Ukai0daac1f2015-06-11 11:56:50 +0900283 }
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900284 sort.Sort(fileInfoByLeaf(leaves))
285 c.leavesch <- leaves
286 traceEvent.end(leavesTe)
Fumitoshi Ukai49599e52015-06-26 10:10:24 +0900287 logStats("%d leaves %d dirs in find cache", nfiles, len(dirs))
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900288 if !glog.V(1) {
289 return
290 }
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900291 for i, leaf := range leaves {
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900292 glog.Infof("android findleaves cache: %d: %s %v", i, leaf.path, leaf.mode)
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900293 }
294 }()
295
296 go func() {
297 filesTe := traceEvent.begin("findcache", literal("files"), traceEventFindCacheFiles)
298 var files []fileInfo
299 for file := range filech {
300 files = append(files, file)
301 }
302 sort.Sort(fileInfoByName(files))
303 c.filesch <- files
304 traceEvent.end(filesTe)
Fumitoshi Ukai49599e52015-06-26 10:10:24 +0900305 logStats("%d files in find cache", len(files))
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900306 if !glog.V(1) {
307 return
308 }
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900309 for i, fi := range files {
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900310 glog.Infof("android find cache: %d: %s %v", i, fi.path, fi.mode)
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900311 }
312 }()
313
314 curdir, err := os.Open(".")
315 if err != nil {
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900316 glog.Warningf("open . failed: %v", err)
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900317 close(c.filesch)
318 close(c.leavesch)
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900319 return
320 }
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900321 names, err := curdir.Readdirnames(-1)
322 if err != nil {
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900323 glog.Warningf("readdir . failed: %v", err)
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900324 close(c.filesch)
325 close(c.leavesch)
326 return
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900327 }
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900328 curdir.Close()
329
330 for _, name := range names {
331 dirs <- name
332 }
333 close(dirs)
334 wg.Wait()
335 close(filech)
336 close(leafch)
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900337}
338
339type fileInfoByName []fileInfo
340
341func (f fileInfoByName) Len() int { return len(f) }
342func (f fileInfoByName) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
343func (f fileInfoByName) Less(i, j int) bool {
344 return f[i].path < f[j].path
345}
346
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900347type fileInfoByLeaf []fileInfo
348
349func (f fileInfoByLeaf) Len() int { return len(f) }
350func (f fileInfoByLeaf) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
351func (f fileInfoByLeaf) Less(i, j int) bool {
352 di := strings.Count(f[i].path, "/")
353 dj := strings.Count(f[j].path, "/")
354 if di != dj {
355 return di < dj
356 }
Fumitoshi Ukaic39b1882015-06-16 16:29:08 +0900357 diri := filepath.Dir(f[i].path) + "/"
358 dirj := filepath.Dir(f[j].path) + "/"
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900359 if diri != dirj {
360 return diri < dirj
361 }
362 mdi := f[i].mode & os.ModeDir
363 mdj := f[j].mode & os.ModeDir
364 if mdi != mdj {
365 return mdi < mdj
366 }
367 return f[i].path < f[j].path
368}
369
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900370var errSkipDir = errors.New("skip dir")
371
372func (c *androidFindCacheT) walk(dir string, walkFn func(int, fileInfo) error) error {
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900373 i := sort.Search(len(c.files), func(i int) bool {
374 return c.files[i].path >= dir
375 })
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900376 glog.V(1).Infof("android find in dir cache: %s i=%d/%d", dir, i, len(c.files))
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900377 start := i
378 var skipdirs []string
379Loop:
380 for i := start; i < len(c.files); i++ {
381 if c.files[i].path == dir {
382 err := walkFn(i, c.files[i])
383 if err != nil {
384 return err
Fumitoshi Ukai1a77c8c2015-06-09 12:56:22 +0900385 }
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900386 continue
387 }
388 if !strings.HasPrefix(c.files[i].path, dir) {
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900389 glog.V(1).Infof("android find in dir cache: %s end=%d/%d", dir, i, len(c.files))
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900390 return nil
391 }
392 if !strings.HasPrefix(c.files[i].path, dir+"/") {
393 continue
394 }
395 for _, skip := range skipdirs {
396 if strings.HasPrefix(c.files[i].path, skip+"/") {
397 continue Loop
Fumitoshi Ukai1a77c8c2015-06-09 12:56:22 +0900398 }
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900399 }
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900400
401 err := walkFn(i, c.files[i])
402 if err == errSkipDir {
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900403 glog.V(1).Infof("android find in skip dir: %s", c.files[i].path)
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900404 skipdirs = append(skipdirs, c.files[i].path)
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900405 continue
406 }
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900407 if err != nil {
408 return err
409 }
410 }
411 return nil
412}
413
414// pattern in repo/android/build/core/definitions.mk
415// find-subdir-assets
416// if [ -d $1 ] ; then cd $1 ; find ./ -not -name '.*' -and -type f -and -not -type l ; fi
Fumitoshi Ukaib44b12d2015-07-07 14:19:32 +0900417func (c *androidFindCacheT) findInDir(w evalWriter, dir string) {
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900418 dir = filepath.Clean(dir)
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900419 glog.V(1).Infof("android find in dir cache: %s", dir)
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900420 c.walk(dir, func(_ int, fi fileInfo) error {
421 // -not -name '.*'
422 if strings.HasPrefix(filepath.Base(fi.path), ".") {
423 return nil
424 }
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900425 // -type f and -not -type l
426 // regular type and not symlink
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900427 if !fi.mode.IsRegular() {
428 return nil
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900429 }
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900430 name := strings.TrimPrefix(fi.path, dir+"/")
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900431 name = "./" + name
Fumitoshi Ukaib44b12d2015-07-07 14:19:32 +0900432 w.writeWordString(name)
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900433 glog.V(1).Infof("android find in dir cache: %s=> %s", dir, name)
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900434 return nil
435 })
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900436}
Fumitoshi Ukai1a77c8c2015-06-09 12:56:22 +0900437
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900438// pattern in repo/android/build/core/definitions.mk
Fumitoshi Ukaib234e272015-06-18 13:14:16 +0900439// all-java-files-under etc
440// cd ${LOCAL_PATH} ; find -L $1 -name "*<ext>" -and -not -name ".*"
Fumitoshi Ukai1a77c8c2015-06-09 12:56:22 +0900441// returns false if symlink is found.
Fumitoshi Ukaib44b12d2015-07-07 14:19:32 +0900442func (c *androidFindCacheT) findExtFilesUnder(w evalWriter, chdir, root, ext string) bool {
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900443 chdir = filepath.Clean(chdir)
Fumitoshi Ukai1a77c8c2015-06-09 12:56:22 +0900444 dir := filepath.Join(chdir, root)
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900445 glog.V(1).Infof("android find %s in dir cache: %s %s", ext, chdir, root)
Fumitoshi Ukai1a77c8c2015-06-09 12:56:22 +0900446 // check symlinks
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900447 var matches []int
448 err := c.walk(dir, func(i int, fi fileInfo) error {
449 if fi.mode&os.ModeSymlink == os.ModeSymlink {
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900450 glog.Warningf("android find %s in dir cache: detect symlink %s %v", ext, c.files[i].path, c.files[i].mode)
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900451 return fmt.Errorf("symlink %s", fi.path)
Fumitoshi Ukai1a77c8c2015-06-09 12:56:22 +0900452 }
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900453 matches = append(matches, i)
454 return nil
455 })
456 if err != nil {
457 return false
Fumitoshi Ukai1a77c8c2015-06-09 12:56:22 +0900458 }
Fumitoshi Ukai1a77c8c2015-06-09 12:56:22 +0900459 // no symlinks
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900460 for _, i := range matches {
461 fi := c.files[i]
462 base := filepath.Base(fi.path)
Fumitoshi Ukaib234e272015-06-18 13:14:16 +0900463 // -name "*<ext>"
464 if filepath.Ext(base) != ext {
Fumitoshi Ukai1a77c8c2015-06-09 12:56:22 +0900465 continue
466 }
467 // -not -name ".*"
468 if strings.HasPrefix(base, ".") {
469 continue
470 }
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900471 name := strings.TrimPrefix(fi.path, chdir+"/")
Fumitoshi Ukaib44b12d2015-07-07 14:19:32 +0900472 w.writeWordString(name)
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900473 glog.V(1).Infof("android find %s in dir cache: %s=> %s", ext, dir, name)
Fumitoshi Ukai1a77c8c2015-06-09 12:56:22 +0900474 }
475 return true
476}
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900477
478// pattern: in repo/android/build/core/base_rules.mk
479// java_resource_file_groups+= ...
480// cd ${TOP_DIR}${LOCAL_PATH}/${dir} && find . -type d -a -name ".svn" -prune \
481// -o -type f -a \! -name "*.java" -a \! -name "package.html" -a \! \
482// -name "overview.html" -a \! -name ".*.swp" -a \! -name ".DS_Store" \
483// -a \! -name "*~" -print )
Fumitoshi Ukaib44b12d2015-07-07 14:19:32 +0900484func (c *androidFindCacheT) findJavaResourceFileGroup(w evalWriter, dir string) {
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900485 glog.V(1).Infof("android find java resource in dir cache: %s", dir)
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900486 c.walk(filepath.Clean(dir), func(_ int, fi fileInfo) error {
487 // -type d -a -name ".svn" -prune
488 if fi.mode.IsDir() && filepath.Base(fi.path) == ".svn" {
489 return errSkipDir
490 }
491 // -type f
492 if !fi.mode.IsRegular() {
493 return nil
494 }
495 // ! -name "*.java" -a ! -name "package.html" -a
496 // ! -name "overview.html" -a ! -name ".*.swp" -a
497 // ! -name ".DS_Store" -a ! -name "*~"
498 base := filepath.Base(fi.path)
499 if filepath.Ext(base) == ".java" ||
500 base == "package.html" ||
501 base == "overview.html" ||
502 (strings.HasPrefix(base, ".") && strings.HasSuffix(base, ".swp")) ||
503 base == ".DS_Store" ||
504 strings.HasSuffix(base, "~") {
505 return nil
506 }
507 name := strings.TrimPrefix(fi.path, dir+"/")
508 name = "./" + name
Fumitoshi Ukaib44b12d2015-07-07 14:19:32 +0900509 w.writeWordString(name)
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900510 glog.V(1).Infof("android find java resource in dir cache: %s=> %s", dir, name)
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900511 return nil
512 })
513}
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900514
Fumitoshi Ukaib44b12d2015-07-07 14:19:32 +0900515func (c *androidFindCacheT) findleaves(w evalWriter, dir, name string, prunes []string, mindepth int) bool {
Fumitoshi Ukai40e35f52015-06-16 16:33:04 +0900516 var found []string
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900517 var dirs []string
Fumitoshi Ukai2a2deb32015-07-08 11:46:14 +0900518 dir = filepath.Clean(dir)
Fumitoshi Ukai3b6c1e82015-06-29 17:27:37 +0900519 topdepth := strings.Count(dir, "/")
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900520 dirs = append(dirs, dir)
521 for len(dirs) > 0 {
522 dir = filepath.Clean(dirs[0]) + "/"
523 dirs = dirs[1:]
524 if dir == "./" {
525 dir = ""
526 }
527 depth := strings.Count(dir, "/")
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900528 // glog.V(1).Infof("android findleaves dir=%q depth=%d dirs=%q", dir, depth, dirs)
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900529 i := sort.Search(len(c.leaves), func(i int) bool {
530 di := strings.Count(c.leaves[i].path, "/")
531 if di != depth {
532 return di >= depth
533 }
Fumitoshi Ukaic39b1882015-06-16 16:29:08 +0900534 diri := filepath.Dir(c.leaves[i].path) + "/"
535 if diri != dir {
536 return diri >= dir
537 }
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900538 return c.leaves[i].path >= dir
539 })
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900540 glog.V(1).Infof("android findleaves dir=%q i=%d/%d", dir, i, len(c.leaves))
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900541
542 Scandir:
543 for ; i < len(c.leaves); i++ {
544 if dir == "" && strings.Contains(c.leaves[i].path, "/") {
545 break
546 }
547 if !strings.HasPrefix(c.leaves[i].path, dir) {
548 break
549 }
550 if mindepth < 0 || depth >= topdepth+mindepth {
551 if !c.leaves[i].mode.IsDir() && filepath.Base(c.leaves[i].path) == name {
552 n := "./" + c.leaves[i].path
Fumitoshi Ukai40e35f52015-06-16 16:33:04 +0900553 found = append(found, n)
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900554 glog.V(1).Infof("android findleaves name=%s=> %s (depth=%d topdepth=%d mindepth=%d)", name, n, depth, topdepth, mindepth)
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900555 break Scandir
556 }
557 }
558 if c.leaves[i].mode.IsDir() {
559 dirs = append(dirs, c.leaves[i].path)
560 }
561 }
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900562 // glog.V(1).Infof("android findleaves next dirs=%q", dirs)
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900563 }
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900564 glog.V(1).Infof("android findleave done")
Fumitoshi Ukai40e35f52015-06-16 16:33:04 +0900565 sort.Strings(found)
566 for _, f := range found {
Fumitoshi Ukaib44b12d2015-07-07 14:19:32 +0900567 w.writeWordString(f)
Fumitoshi Ukai40e35f52015-06-16 16:33:04 +0900568 }
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900569 return true
570}