Home | History | Annotate | Download | only in cmd
      1 // Copyright 2017 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 
     15 package main
     16 
     17 import (
     18 	"errors"
     19 	"flag"
     20 	"fmt"
     21 	"io"
     22 	"io/ioutil"
     23 	"log"
     24 	"os"
     25 	"runtime/pprof"
     26 	"sort"
     27 	"strings"
     28 	"time"
     29 
     30 	"android/soong/finder"
     31 	"android/soong/finder/fs"
     32 )
     33 
     34 var (
     35 	// configuration of what to find
     36 	excludeDirs     string
     37 	filenamesToFind string
     38 	pruneFiles      string
     39 
     40 	// other configuration
     41 	cpuprofile    string
     42 	verbose       bool
     43 	dbPath        string
     44 	numIterations int
     45 )
     46 
     47 func init() {
     48 	flag.StringVar(&cpuprofile, "cpuprofile", "",
     49 		"filepath of profile file to write (optional)")
     50 	flag.BoolVar(&verbose, "v", false, "log additional information")
     51 	flag.StringVar(&dbPath, "db", "", "filepath of cache db")
     52 
     53 	flag.StringVar(&excludeDirs, "exclude-dirs", "",
     54 		"comma-separated list of directory names to exclude from search")
     55 	flag.StringVar(&filenamesToFind, "names", "",
     56 		"comma-separated list of filenames to find")
     57 	flag.StringVar(&pruneFiles, "prune-files", "",
     58 		"filenames that if discovered will exclude their entire directory "+
     59 			"(including sibling files and directories)")
     60 	flag.IntVar(&numIterations, "count", 1,
     61 		"number of times to run. This is intended for use with --cpuprofile"+
     62 			" , to increase profile accuracy")
     63 }
     64 
     65 var usage = func() {
     66 	fmt.Printf("usage: finder -name <fileName> --db <dbPath> <searchDirectory> [<searchDirectory>...]\n")
     67 	flag.PrintDefaults()
     68 }
     69 
     70 func main() {
     71 	err := run()
     72 	if err != nil {
     73 		fmt.Fprintf(os.Stderr, "%v\n", err.Error())
     74 		os.Exit(1)
     75 	}
     76 }
     77 
     78 func stringToList(input string) []string {
     79 	return strings.Split(input, ",")
     80 }
     81 
     82 func run() error {
     83 	startTime := time.Now()
     84 	flag.Parse()
     85 
     86 	if cpuprofile != "" {
     87 		f, err := os.Create(cpuprofile)
     88 		if err != nil {
     89 			return fmt.Errorf("Error opening cpuprofile: %s", err)
     90 		}
     91 		pprof.StartCPUProfile(f)
     92 		defer f.Close()
     93 		defer pprof.StopCPUProfile()
     94 	}
     95 
     96 	var writer io.Writer
     97 	if verbose {
     98 		writer = os.Stderr
     99 	} else {
    100 		writer = ioutil.Discard
    101 	}
    102 
    103 	// TODO: replace Lshortfile with Llongfile when bug 63821638 is done
    104 	logger := log.New(writer, "", log.Ldate|log.Lmicroseconds|log.Lshortfile)
    105 
    106 	logger.Printf("Finder starting at %v\n", startTime)
    107 
    108 	rootPaths := flag.Args()
    109 	if len(rootPaths) < 1 {
    110 		usage()
    111 		return fmt.Errorf(
    112 			"Must give at least one <searchDirectory>")
    113 	}
    114 
    115 	workingDir, err := os.Getwd()
    116 	if err != nil {
    117 		return err
    118 	}
    119 	params := finder.CacheParams{
    120 		WorkingDirectory: workingDir,
    121 		RootDirs:         rootPaths,
    122 		ExcludeDirs:      stringToList(excludeDirs),
    123 		PruneFiles:       stringToList(pruneFiles),
    124 		IncludeFiles:     stringToList(filenamesToFind),
    125 	}
    126 	if dbPath == "" {
    127 		usage()
    128 		return errors.New("Param 'db' must be nonempty")
    129 	}
    130 
    131 	matches := []string{}
    132 	for i := 0; i < numIterations; i++ {
    133 		matches, err = runFind(params, logger)
    134 		if err != nil {
    135 			return err
    136 		}
    137 	}
    138 	findDuration := time.Since(startTime)
    139 	logger.Printf("Found these %v inodes in %v :\n", len(matches), findDuration)
    140 	sort.Strings(matches)
    141 	for _, match := range matches {
    142 		fmt.Println(match)
    143 	}
    144 	logger.Printf("End of %v inodes\n", len(matches))
    145 	logger.Printf("Finder completed in %v\n", time.Since(startTime))
    146 	return nil
    147 }
    148 
    149 func runFind(params finder.CacheParams, logger *log.Logger) (paths []string, err error) {
    150 	service, err := finder.New(params, fs.OsFs, logger, dbPath)
    151 	if err != nil {
    152 		return []string{}, err
    153 	}
    154 	defer service.Shutdown()
    155 	return service.FindAll(), nil
    156 }
    157