// Copyright 2017 Google Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "errors" "flag" "fmt" "io" "io/ioutil" "log" "os" "runtime/pprof" "sort" "strings" "time" "android/soong/finder" "android/soong/finder/fs" ) var ( // configuration of what to find excludeDirs string filenamesToFind string pruneFiles string // other configuration cpuprofile string verbose bool dbPath string numIterations int ) func init() { flag.StringVar(&cpuprofile, "cpuprofile", "", "filepath of profile file to write (optional)") flag.BoolVar(&verbose, "v", false, "log additional information") flag.StringVar(&dbPath, "db", "", "filepath of cache db") flag.StringVar(&excludeDirs, "exclude-dirs", "", "comma-separated list of directory names to exclude from search") flag.StringVar(&filenamesToFind, "names", "", "comma-separated list of filenames to find") flag.StringVar(&pruneFiles, "prune-files", "", "filenames that if discovered will exclude their entire directory "+ "(including sibling files and directories)") flag.IntVar(&numIterations, "count", 1, "number of times to run. This is intended for use with --cpuprofile"+ " , to increase profile accuracy") } var usage = func() { fmt.Printf("usage: finder -name <fileName> --db <dbPath> <searchDirectory> [<searchDirectory>...]\n") flag.PrintDefaults() } func main() { err := run() if err != nil { fmt.Fprintf(os.Stderr, "%v\n", err.Error()) os.Exit(1) } } func stringToList(input string) []string { return strings.Split(input, ",") } func run() error { startTime := time.Now() flag.Parse() if cpuprofile != "" { f, err := os.Create(cpuprofile) if err != nil { return fmt.Errorf("Error opening cpuprofile: %s", err) } pprof.StartCPUProfile(f) defer f.Close() defer pprof.StopCPUProfile() } var writer io.Writer if verbose { writer = os.Stderr } else { writer = ioutil.Discard } // TODO: replace Lshortfile with Llongfile when bug 63821638 is done logger := log.New(writer, "", log.Ldate|log.Lmicroseconds|log.Lshortfile) logger.Printf("Finder starting at %v\n", startTime) rootPaths := flag.Args() if len(rootPaths) < 1 { usage() return fmt.Errorf( "Must give at least one <searchDirectory>") } workingDir, err := os.Getwd() if err != nil { return err } params := finder.CacheParams{ WorkingDirectory: workingDir, RootDirs: rootPaths, ExcludeDirs: stringToList(excludeDirs), PruneFiles: stringToList(pruneFiles), IncludeFiles: stringToList(filenamesToFind), } if dbPath == "" { usage() return errors.New("Param 'db' must be nonempty") } matches := []string{} for i := 0; i < numIterations; i++ { matches, err = runFind(params, logger) if err != nil { return err } } findDuration := time.Since(startTime) logger.Printf("Found these %v inodes in %v :\n", len(matches), findDuration) sort.Strings(matches) for _, match := range matches { fmt.Println(match) } logger.Printf("End of %v inodes\n", len(matches)) logger.Printf("Finder completed in %v\n", time.Since(startTime)) return nil } func runFind(params finder.CacheParams, logger *log.Logger) (paths []string, err error) { service, err := finder.New(params, fs.OsFs, logger, dbPath) if err != nil { return []string{}, err } defer service.Shutdown() return service.FindAll(), nil }