blob: fe1bba2932c5100b58f730f2f2b23b77c4914dee [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 "path/filepath"
Fumitoshi Ukai83410132015-06-15 14:50:07 +090023 "runtime"
Fumitoshi Ukai106fb792015-06-09 10:37:35 +090024 "sort"
Fumitoshi Ukai358c68a2015-06-08 13:12:55 +090025 "strings"
Fumitoshi Ukai106fb792015-06-09 10:37:35 +090026 "sync"
27 "time"
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +090028
29 "github.com/golang/glog"
Fumitoshi Ukai358c68a2015-06-08 13:12:55 +090030)
31
Fumitoshi Ukai9042b992015-06-23 16:10:27 +090032type wildcardCacheT struct {
Fumitoshi Ukaid81f9b92015-07-21 17:07:08 +090033 mu sync.Mutex
34 dirent map[string][]string
Fumitoshi Ukai9042b992015-06-23 16:10:27 +090035}
36
37var wildcardCache = &wildcardCacheT{
Fumitoshi Ukaid81f9b92015-07-21 17:07:08 +090038 dirent: make(map[string][]string),
39}
40
41func (w *wildcardCacheT) dirs() int {
42 w.mu.Lock()
43 defer w.mu.Unlock()
44 return len(w.dirent)
45}
46
47func (w *wildcardCacheT) files() int {
48 w.mu.Lock()
49 defer w.mu.Unlock()
50 n := 0
51 for _, names := range w.dirent {
52 n += len(names)
53 }
54 return n
55}
56
57func hasWildcardMeta(pat string) bool {
58 return strings.IndexAny(pat, "*?[") >= 0
Fumitoshi Ukai9042b992015-06-23 16:10:27 +090059}
Fumitoshi Ukai358c68a2015-06-08 13:12:55 +090060
Fumitoshi Ukaic0ded232015-07-07 11:40:04 +090061func wildcardUnescape(pat string) string {
62 var buf bytes.Buffer
63 for i := 0; i < len(pat); i++ {
64 if pat[i] == '\\' && i+1 < len(pat) {
65 switch pat[i+1] {
66 case '*', '?', '[', '\\':
Fumitoshi Ukaid81f9b92015-07-21 17:07:08 +090067 buf.WriteByte(pat[i])
Fumitoshi Ukaic0ded232015-07-07 11:40:04 +090068 }
Fumitoshi Ukaid81f9b92015-07-21 17:07:08 +090069 continue
Fumitoshi Ukaic0ded232015-07-07 11:40:04 +090070 }
Fumitoshi Ukaid81f9b92015-07-21 17:07:08 +090071 buf.WriteByte(pat[i])
Fumitoshi Ukaic0ded232015-07-07 11:40:04 +090072 }
73 return buf.String()
74}
75
Fumitoshi Ukaid81f9b92015-07-21 17:07:08 +090076func (w *wildcardCacheT) readdirnames(dir string) []string {
77 if dir == "" {
78 dir = "."
79 }
80 w.mu.Lock()
81 names, ok := w.dirent[dir]
82 w.mu.Unlock()
83 if ok {
84 return names
85 }
86 d, err := os.Open(dir)
87 if err != nil {
88 w.mu.Lock()
89 w.dirent[dir] = nil
90 w.mu.Unlock()
91 return nil
92 }
93 defer d.Close()
94 names, _ = d.Readdirnames(-1)
95 sort.Strings(names)
96 w.mu.Lock()
97 w.dirent[dir] = names
98 w.mu.Unlock()
99 return names
100}
101
102// glob searches for files matching pattern in the directory dir
103// and appends them to matches. ignore I/O errors.
104func (w *wildcardCacheT) glob(dir, pattern string, matches []string) ([]string, error) {
105 names := w.readdirnames(dir)
106 if dir != "" {
107 dir += string(filepath.Separator)
108 }
109 for _, n := range names {
110 matched, err := filepath.Match(pattern, n)
Fumitoshi Ukai9d959c32015-06-19 18:06:01 +0900111 if err != nil {
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900112 return nil, err
Fumitoshi Ukai9d959c32015-06-19 18:06:01 +0900113 }
Fumitoshi Ukaid81f9b92015-07-21 17:07:08 +0900114 if matched {
115 matches = append(matches, dir+n)
Fumitoshi Ukai9d959c32015-06-19 18:06:01 +0900116 }
Fumitoshi Ukai9d959c32015-06-19 18:06:01 +0900117 }
Fumitoshi Ukaid81f9b92015-07-21 17:07:08 +0900118 return matches, nil
119}
120
121func (w *wildcardCacheT) Glob(pat string) ([]string, error) {
122 // TODO(ukai): use find cache for glob if exists
123 // or use wildcardCache for find cache.
124 pat = wildcardUnescape(pat)
125 dir, file := filepath.Split(pat)
126 switch dir {
127 case "", string(filepath.Separator):
128 // nothing
129 default:
130 dir = dir[0 : len(dir)-1] // chop off trailing separator
131 }
132 if !hasWildcardMeta(dir) {
133 return w.glob(dir, file, nil)
134 }
135
136 m, err := w.Glob(dir)
137 if err != nil {
138 return nil, err
139 }
140 var matches []string
141 for _, d := range m {
142 matches, err = w.glob(d, file, matches)
143 if err != nil {
144 return nil, err
145 }
146 }
147 return matches, nil
Fumitoshi Ukai9d959c32015-06-19 18:06:01 +0900148}
149
Fumitoshi Ukaib44b12d2015-07-07 14:19:32 +0900150func wildcard(w evalWriter, pat string) error {
Fumitoshi Ukaid81f9b92015-07-21 17:07:08 +0900151 files, err := wildcardCache.Glob(pat)
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900152 if err != nil {
153 return err
154 }
Fumitoshi Ukai358c68a2015-06-08 13:12:55 +0900155 for _, file := range files {
Fumitoshi Ukaib44b12d2015-07-07 14:19:32 +0900156 w.writeWordString(file)
Fumitoshi Ukai358c68a2015-06-08 13:12:55 +0900157 }
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900158 return nil
Fumitoshi Ukai358c68a2015-06-08 13:12:55 +0900159}
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900160
161type fileInfo struct {
162 path string
163 mode os.FileMode
164}
165
166type androidFindCacheT struct {
167 once sync.Once
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900168 filesch chan []fileInfo
169 leavesch chan []fileInfo
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900170 files []fileInfo
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900171 leaves []fileInfo
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900172 scanTime time.Duration
173}
174
175var (
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900176 androidFindCache androidFindCacheT
177 androidDefaultLeafNames = []string{"CleanSpec.mk", "Android.mk"}
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900178)
179
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900180// AndroidFindCacheInit initializes find cache for android build.
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900181func AndroidFindCacheInit(prunes, leafNames []string) {
Fumitoshi Ukai1394d102015-07-08 16:45:49 +0900182 if !UseFindCache {
183 return
184 }
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900185 if leafNames != nil {
186 androidDefaultLeafNames = leafNames
187 }
188 androidFindCache.init(prunes)
189}
190
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900191func (c *androidFindCacheT) ready() bool {
Fumitoshi Ukai1394d102015-07-08 16:45:49 +0900192 if !UseFindCache {
193 return false
194 }
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900195 if c.files != nil {
196 return true
197 }
198 select {
199 case c.files = <-c.filesch:
200 }
201 return c.files != nil
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900202}
203
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900204func (c *androidFindCacheT) leavesReady() bool {
Fumitoshi Ukai1394d102015-07-08 16:45:49 +0900205 if !UseFindCache {
206 return false
207 }
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900208 if c.leaves != nil {
209 return true
210 }
211 select {
212 case c.leaves = <-c.leavesch:
213 }
214 return c.leaves != nil
215}
216
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900217func (c *androidFindCacheT) init(prunes []string) {
Fumitoshi Ukai1394d102015-07-08 16:45:49 +0900218 if !UseFindCache {
219 return
220 }
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900221 c.once.Do(func() {
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900222 c.filesch = make(chan []fileInfo, 1)
223 c.leavesch = make(chan []fileInfo, 1)
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900224 go c.start(prunes, androidDefaultLeafNames)
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900225 })
226}
227
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900228func (c *androidFindCacheT) start(prunes, leafNames []string) {
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900229 glog.Infof("find cache init: prunes=%q leafNames=%q", prunes, leafNames)
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900230 te := traceEvent.begin("findcache", literal("init"), traceEventFindCache)
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900231 defer func() {
Fumitoshi Ukaif543f4d2015-06-15 15:21:47 +0900232 traceEvent.end(te)
233 c.scanTime = time.Since(te.t)
Fumitoshi Ukai49599e52015-06-26 10:10:24 +0900234 logStats("android find cache scan: %v", c.scanTime)
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900235 }()
236
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900237 dirs := make(chan string, 32)
238 filech := make(chan fileInfo, 1000)
239 leafch := make(chan fileInfo, 1000)
240 var wg sync.WaitGroup
241 numWorker := runtime.NumCPU() - 1
242 wg.Add(numWorker)
243 for i := 0; i < numWorker; i++ {
244 go func() {
245 defer wg.Done()
246 for dir := range dirs {
247 err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
248 if info.IsDir() {
249 for _, prune := range prunes {
250 if info.Name() == prune {
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900251 glog.V(1).Infof("find cache prune: %s", path)
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900252 return filepath.SkipDir
253 }
254 }
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900255 }
256 filech <- fileInfo{
257 path: path,
258 mode: info.Mode(),
259 }
260 for _, leaf := range leafNames {
261 if info.Name() == leaf {
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900262 glog.V(1).Infof("find cache leaf: %s", path)
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900263 leafch <- fileInfo{
264 path: path,
265 mode: info.Mode(),
266 }
267 break
268 }
269 }
270 return nil
271 })
272 if err != nil && err != filepath.SkipDir {
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900273 glog.Warningf("error in adnroid find cache: %v", err)
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900274 close(c.filesch)
275 close(c.leavesch)
276 return
Fumitoshi Ukai0daac1f2015-06-11 11:56:50 +0900277 }
278 }
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900279 }()
280 }
281
282 go func() {
Fumitoshi Ukai040271f2015-06-18 11:04:46 +0900283 dirs := make(map[string]bool)
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900284 leavesTe := traceEvent.begin("findcache", literal("leaves"), traceEventFindCacheLeaves)
285 var leaves []fileInfo
Fumitoshi Ukai040271f2015-06-18 11:04:46 +0900286 nfiles := 0
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900287 for leaf := range leafch {
288 leaves = append(leaves, leaf)
Fumitoshi Ukai040271f2015-06-18 11:04:46 +0900289 nfiles++
290 for dir := filepath.Dir(leaf.path); dir != "."; dir = filepath.Dir(dir) {
291 if dirs[dir] {
292 break
293 }
294 leaves = append(leaves, fileInfo{
295 path: dir,
296 mode: leaf.mode | os.ModeDir,
297 })
298 dirs[dir] = true
299 }
Fumitoshi Ukai0daac1f2015-06-11 11:56:50 +0900300 }
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900301 sort.Sort(fileInfoByLeaf(leaves))
302 c.leavesch <- leaves
303 traceEvent.end(leavesTe)
Fumitoshi Ukai49599e52015-06-26 10:10:24 +0900304 logStats("%d leaves %d dirs in find cache", nfiles, len(dirs))
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900305 if !glog.V(1) {
306 return
307 }
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900308 for i, leaf := range leaves {
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900309 glog.Infof("android findleaves cache: %d: %s %v", i, leaf.path, leaf.mode)
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900310 }
311 }()
312
313 go func() {
314 filesTe := traceEvent.begin("findcache", literal("files"), traceEventFindCacheFiles)
315 var files []fileInfo
316 for file := range filech {
317 files = append(files, file)
318 }
319 sort.Sort(fileInfoByName(files))
320 c.filesch <- files
321 traceEvent.end(filesTe)
Fumitoshi Ukai49599e52015-06-26 10:10:24 +0900322 logStats("%d files in find cache", len(files))
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900323 if !glog.V(1) {
324 return
325 }
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900326 for i, fi := range files {
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900327 glog.Infof("android find cache: %d: %s %v", i, fi.path, fi.mode)
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900328 }
329 }()
330
331 curdir, err := os.Open(".")
332 if err != nil {
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900333 glog.Warningf("open . failed: %v", err)
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900334 close(c.filesch)
335 close(c.leavesch)
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900336 return
337 }
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900338 names, err := curdir.Readdirnames(-1)
339 if err != nil {
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900340 glog.Warningf("readdir . failed: %v", err)
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900341 close(c.filesch)
342 close(c.leavesch)
343 return
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900344 }
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900345 curdir.Close()
346
347 for _, name := range names {
348 dirs <- name
349 }
350 close(dirs)
351 wg.Wait()
352 close(filech)
353 close(leafch)
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900354}
355
356type fileInfoByName []fileInfo
357
358func (f fileInfoByName) Len() int { return len(f) }
359func (f fileInfoByName) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
360func (f fileInfoByName) Less(i, j int) bool {
361 return f[i].path < f[j].path
362}
363
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900364type fileInfoByLeaf []fileInfo
365
366func (f fileInfoByLeaf) Len() int { return len(f) }
367func (f fileInfoByLeaf) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
368func (f fileInfoByLeaf) Less(i, j int) bool {
369 di := strings.Count(f[i].path, "/")
370 dj := strings.Count(f[j].path, "/")
371 if di != dj {
372 return di < dj
373 }
Fumitoshi Ukaic39b1882015-06-16 16:29:08 +0900374 diri := filepath.Dir(f[i].path) + "/"
375 dirj := filepath.Dir(f[j].path) + "/"
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900376 if diri != dirj {
377 return diri < dirj
378 }
379 mdi := f[i].mode & os.ModeDir
380 mdj := f[j].mode & os.ModeDir
381 if mdi != mdj {
382 return mdi < mdj
383 }
384 return f[i].path < f[j].path
385}
386
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900387var errSkipDir = errors.New("skip dir")
388
389func (c *androidFindCacheT) walk(dir string, walkFn func(int, fileInfo) error) error {
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900390 i := sort.Search(len(c.files), func(i int) bool {
391 return c.files[i].path >= dir
392 })
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900393 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 +0900394 start := i
395 var skipdirs []string
396Loop:
397 for i := start; i < len(c.files); i++ {
398 if c.files[i].path == dir {
399 err := walkFn(i, c.files[i])
400 if err != nil {
401 return err
Fumitoshi Ukai1a77c8c2015-06-09 12:56:22 +0900402 }
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900403 continue
404 }
405 if !strings.HasPrefix(c.files[i].path, dir) {
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900406 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 +0900407 return nil
408 }
409 if !strings.HasPrefix(c.files[i].path, dir+"/") {
410 continue
411 }
412 for _, skip := range skipdirs {
413 if strings.HasPrefix(c.files[i].path, skip+"/") {
414 continue Loop
Fumitoshi Ukai1a77c8c2015-06-09 12:56:22 +0900415 }
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900416 }
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900417
418 err := walkFn(i, c.files[i])
419 if err == errSkipDir {
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900420 glog.V(1).Infof("android find in skip dir: %s", c.files[i].path)
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900421 skipdirs = append(skipdirs, c.files[i].path)
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900422 continue
423 }
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900424 if err != nil {
425 return err
426 }
427 }
428 return nil
429}
430
431// pattern in repo/android/build/core/definitions.mk
432// find-subdir-assets
433// if [ -d $1 ] ; then cd $1 ; find ./ -not -name '.*' -and -type f -and -not -type l ; fi
Fumitoshi Ukaib44b12d2015-07-07 14:19:32 +0900434func (c *androidFindCacheT) findInDir(w evalWriter, dir string) {
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900435 dir = filepath.Clean(dir)
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900436 glog.V(1).Infof("android find in dir cache: %s", dir)
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900437 c.walk(dir, func(_ int, fi fileInfo) error {
438 // -not -name '.*'
439 if strings.HasPrefix(filepath.Base(fi.path), ".") {
440 return nil
441 }
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900442 // -type f and -not -type l
443 // regular type and not symlink
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900444 if !fi.mode.IsRegular() {
445 return nil
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900446 }
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900447 name := strings.TrimPrefix(fi.path, dir+"/")
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900448 name = "./" + name
Fumitoshi Ukaib44b12d2015-07-07 14:19:32 +0900449 w.writeWordString(name)
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900450 glog.V(1).Infof("android find in dir cache: %s=> %s", dir, name)
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900451 return nil
452 })
Fumitoshi Ukai106fb792015-06-09 10:37:35 +0900453}
Fumitoshi Ukai1a77c8c2015-06-09 12:56:22 +0900454
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900455// pattern in repo/android/build/core/definitions.mk
Fumitoshi Ukaib234e272015-06-18 13:14:16 +0900456// all-java-files-under etc
457// cd ${LOCAL_PATH} ; find -L $1 -name "*<ext>" -and -not -name ".*"
Fumitoshi Ukai1a77c8c2015-06-09 12:56:22 +0900458// returns false if symlink is found.
Fumitoshi Ukaib44b12d2015-07-07 14:19:32 +0900459func (c *androidFindCacheT) findExtFilesUnder(w evalWriter, chdir, root, ext string) bool {
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900460 chdir = filepath.Clean(chdir)
Fumitoshi Ukai1a77c8c2015-06-09 12:56:22 +0900461 dir := filepath.Join(chdir, root)
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900462 glog.V(1).Infof("android find %s in dir cache: %s %s", ext, chdir, root)
Fumitoshi Ukai1a77c8c2015-06-09 12:56:22 +0900463 // check symlinks
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900464 var matches []int
465 err := c.walk(dir, func(i int, fi fileInfo) error {
466 if fi.mode&os.ModeSymlink == os.ModeSymlink {
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900467 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 +0900468 return fmt.Errorf("symlink %s", fi.path)
Fumitoshi Ukai1a77c8c2015-06-09 12:56:22 +0900469 }
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900470 matches = append(matches, i)
471 return nil
472 })
473 if err != nil {
474 return false
Fumitoshi Ukai1a77c8c2015-06-09 12:56:22 +0900475 }
Fumitoshi Ukai1a77c8c2015-06-09 12:56:22 +0900476 // no symlinks
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900477 for _, i := range matches {
478 fi := c.files[i]
479 base := filepath.Base(fi.path)
Fumitoshi Ukaib234e272015-06-18 13:14:16 +0900480 // -name "*<ext>"
481 if filepath.Ext(base) != ext {
Fumitoshi Ukai1a77c8c2015-06-09 12:56:22 +0900482 continue
483 }
484 // -not -name ".*"
485 if strings.HasPrefix(base, ".") {
486 continue
487 }
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900488 name := strings.TrimPrefix(fi.path, chdir+"/")
Fumitoshi Ukaib44b12d2015-07-07 14:19:32 +0900489 w.writeWordString(name)
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900490 glog.V(1).Infof("android find %s in dir cache: %s=> %s", ext, dir, name)
Fumitoshi Ukai1a77c8c2015-06-09 12:56:22 +0900491 }
492 return true
493}
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900494
495// pattern: in repo/android/build/core/base_rules.mk
496// java_resource_file_groups+= ...
497// cd ${TOP_DIR}${LOCAL_PATH}/${dir} && find . -type d -a -name ".svn" -prune \
498// -o -type f -a \! -name "*.java" -a \! -name "package.html" -a \! \
499// -name "overview.html" -a \! -name ".*.swp" -a \! -name ".DS_Store" \
500// -a \! -name "*~" -print )
Fumitoshi Ukaib44b12d2015-07-07 14:19:32 +0900501func (c *androidFindCacheT) findJavaResourceFileGroup(w evalWriter, dir string) {
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900502 glog.V(1).Infof("android find java resource in dir cache: %s", dir)
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900503 c.walk(filepath.Clean(dir), func(_ int, fi fileInfo) error {
504 // -type d -a -name ".svn" -prune
505 if fi.mode.IsDir() && filepath.Base(fi.path) == ".svn" {
506 return errSkipDir
507 }
508 // -type f
509 if !fi.mode.IsRegular() {
510 return nil
511 }
512 // ! -name "*.java" -a ! -name "package.html" -a
513 // ! -name "overview.html" -a ! -name ".*.swp" -a
514 // ! -name ".DS_Store" -a ! -name "*~"
515 base := filepath.Base(fi.path)
516 if filepath.Ext(base) == ".java" ||
517 base == "package.html" ||
518 base == "overview.html" ||
519 (strings.HasPrefix(base, ".") && strings.HasSuffix(base, ".swp")) ||
520 base == ".DS_Store" ||
521 strings.HasSuffix(base, "~") {
522 return nil
523 }
524 name := strings.TrimPrefix(fi.path, dir+"/")
525 name = "./" + name
Fumitoshi Ukaib44b12d2015-07-07 14:19:32 +0900526 w.writeWordString(name)
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900527 glog.V(1).Infof("android find java resource in dir cache: %s=> %s", dir, name)
Fumitoshi Ukai7adc0f52015-06-09 15:34:26 +0900528 return nil
529 })
530}
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900531
Fumitoshi Ukaib44b12d2015-07-07 14:19:32 +0900532func (c *androidFindCacheT) findleaves(w evalWriter, dir, name string, prunes []string, mindepth int) bool {
Fumitoshi Ukai40e35f52015-06-16 16:33:04 +0900533 var found []string
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900534 var dirs []string
Fumitoshi Ukai2a2deb32015-07-08 11:46:14 +0900535 dir = filepath.Clean(dir)
Fumitoshi Ukai3b6c1e82015-06-29 17:27:37 +0900536 topdepth := strings.Count(dir, "/")
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900537 dirs = append(dirs, dir)
538 for len(dirs) > 0 {
539 dir = filepath.Clean(dirs[0]) + "/"
540 dirs = dirs[1:]
541 if dir == "./" {
542 dir = ""
543 }
544 depth := strings.Count(dir, "/")
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900545 // glog.V(1).Infof("android findleaves dir=%q depth=%d dirs=%q", dir, depth, dirs)
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900546 i := sort.Search(len(c.leaves), func(i int) bool {
547 di := strings.Count(c.leaves[i].path, "/")
548 if di != depth {
549 return di >= depth
550 }
Fumitoshi Ukaic39b1882015-06-16 16:29:08 +0900551 diri := filepath.Dir(c.leaves[i].path) + "/"
552 if diri != dir {
553 return diri >= dir
554 }
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900555 return c.leaves[i].path >= dir
556 })
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900557 glog.V(1).Infof("android findleaves dir=%q i=%d/%d", dir, i, len(c.leaves))
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900558
559 Scandir:
560 for ; i < len(c.leaves); i++ {
561 if dir == "" && strings.Contains(c.leaves[i].path, "/") {
562 break
563 }
564 if !strings.HasPrefix(c.leaves[i].path, dir) {
565 break
566 }
567 if mindepth < 0 || depth >= topdepth+mindepth {
568 if !c.leaves[i].mode.IsDir() && filepath.Base(c.leaves[i].path) == name {
569 n := "./" + c.leaves[i].path
Fumitoshi Ukai40e35f52015-06-16 16:33:04 +0900570 found = append(found, n)
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900571 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 +0900572 break Scandir
573 }
574 }
575 if c.leaves[i].mode.IsDir() {
576 dirs = append(dirs, c.leaves[i].path)
577 }
578 }
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900579 // glog.V(1).Infof("android findleaves next dirs=%q", dirs)
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900580 }
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900581 glog.V(1).Infof("android findleave done")
Fumitoshi Ukai40e35f52015-06-16 16:33:04 +0900582 sort.Strings(found)
583 for _, f := range found {
Fumitoshi Ukaib44b12d2015-07-07 14:19:32 +0900584 w.writeWordString(f)
Fumitoshi Ukai40e35f52015-06-16 16:33:04 +0900585 }
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900586 return true
587}