Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit dc1a728f authored by Joe Onorato's avatar Joe Onorato
Browse files

Rewrite findleaves.sh in python

This cuts the make startup time by about 30 seconds.  Python is faster
than bash in this case, and also we can now supply multiple directories
to prune, and skip the .repo directory, which is, uh, big.

This is from my mac laptop:

$ time build/tools/findleaves.sh --prune="./out" . Android.mk > /dev/null

real    0m29.186s
user    0m0.550s
sys 0m5.897s

$ time build/tools/findleaves.py --prune="./out" . Android.mk > /dev/null

real    0m4.701s
user    0m0.645s
sys 0m1.294s

$ time build/tools/findleaves.py --prune="./out" --prune="./.repo" . Android.mk > /dev/null
real    0m0.176s
user    0m0.094s
sys 0m0.080s
parent 97346fa8
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -128,7 +128,8 @@ endef
# $(1): directory to search under
# Ignores $(1)/Android.mk
define first-makefiles-under
$(shell build/tools/findleaves.sh --mindepth=2 $(1) Android.mk)
$(shell build/tools/findleaves.py --prune=out --prune=.repo --prune=.git \
        --mindepth=2 $(1) Android.mk)
endef

###########################################################
+1 −1
Original line number Diff line number Diff line
@@ -459,7 +459,7 @@ else # ONE_SHOT_MAKEFILE
# Can't use first-makefiles-under here because
# --mindepth=2 makes the prunes not work.
subdir_makefiles := \
	$(shell build/tools/findleaves.sh --prune="./out" $(subdirs) Android.mk)
	$(shell build/tools/findleaves.py --prune=out --prune=.repo --prune=.git $(subdirs) Android.mk)

include $(subdir_makefiles)
endif # ONE_SHOT_MAKEFILE

tools/findleaves.py

0 → 100755
+97 −0
Original line number Diff line number Diff line
#!/usr/bin/env python
#
# Copyright (C) 2009 The Android Open Source Project
#
# 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.
#

#
# Finds files with the specified name under a particular directory, stopping
# the search in a given subdirectory when the file is found.
#

import os
import sys

def perform_find(mindepth, prune, dirlist, filename):
  result = []
  pruneleaves = set(map(lambda x: os.path.split(x)[1], prune))
  for rootdir in dirlist:
    rootdepth = rootdir.count("/")
    for root, dirs, files in os.walk(rootdir):
      # prune
      check_prune = False
      for d in dirs:
        if d in pruneleaves:
          check_prune = True
          break
      if check_prune:
        i = 0
        while i < len(dirs):
          if os.path.join(root, dirs[i]) in prune:
            del dirs[i]
          else:
            i += 1
      # mindepth
      if mindepth > 0:
        depth = 1 + root.count("/") - rootdepth
        if depth < mindepth:
          continue
      # match
      if filename in files:
        result.append(os.path.join(root, filename))
        del dirs[:]
  return result

def usage():
  sys.stderr.write("""Usage: %(progName)s [<options>] <dirlist> <filename>
Options:
   --mindepth=<mindepth>
       Both behave in the same way as their find(1) equivalents.
   --prune=<dirname>
       Avoids returning results from inside any directory called <dirname>
       (e.g., "*/out/*"). May be used multiple times.
""" % {
      "progName": os.path.split(sys.argv[0])[1],
    })
  sys.exit(1)

def main(argv):
  mindepth = -1
  prune = []
  i=1
  while i<len(argv) and len(argv[i])>2 and argv[i][0:2] == "--":
    arg = argv[i]
    if arg.startswith("--mindepth="):
      try:
        mindepth = int(arg[len("--mindepth="):])
      except ValueError:
        usage()
    elif arg.startswith("--prune="):
      p = arg[len("--prune="):]
      if len(p) == 0:
        usage()
      prune.append(p)
    else:
      usage()
    i += 1
  if len(argv)-i < 2: # need both <dirlist> and <filename>
    usage()
  dirlist = argv[i:-1]
  filename = argv[-1]
  results = perform_find(mindepth, prune, dirlist, filename)
  for r in results:
    print r

if __name__ == "__main__":
  main(sys.argv)

tools/findleaves.sh

deleted100755 → 0
+0 −109
Original line number Diff line number Diff line
#!/bin/bash
#
# Copyright (C) 2008 The Android Open Source Project
#
# 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.
#

#
# Finds files with the specified name under a particular directory, stopping
# the search in a given subdirectory when the file is found.
#

set -o nounset  # fail when dereferencing unset variables
set -o errexit  # fail if any subcommand fails

progName=`basename $0`

function warn() {
    echo "$progName: $@" >&2
}

function trace() {
    echo "$progName: $@"
}

function usage() {
    if [[ $# > 0 ]]
    then
        warn $@
    fi
    cat <<-EOF
Usage: $progName [<options>] <dirlist> <filename>
Options:
       --mindepth=<mindepth>
       --maxdepth=<maxdepth>
       Both behave in the same way as their find(1) equivalents.
       --prune=<glob>
       Avoids returning results from any path matching the given glob-style
       pattern (e.g., "*/out/*"). May be used multiple times.
EOF
    exit 1
}

function fail() {
    warn $@
    exit 1
}

if [ $# -lt 2 ]
then
    usage
fi

findargs=""
while [[ "${1:0:2}" == "--" ]]
do
    arg=${1:2}
    name=${arg%%=*}
    value=${arg##*=}
    if [[ "$name" == "mindepth" || "$name" == "maxdepth" ]]
    then
        # Add to beginning of findargs; these must come before the expression.
        findargs="-$name $value $findargs"
    elif [[ "$name" == "prune" ]]
    then
        # Add to end of findargs; these are part of the expression.
        findargs="$findargs -path $value -prune -or"
    fi
    shift
done

nargs=$#
# The filename is the last argument
filename="${!nargs}"

# Print out all files that match, as long as the path isn't explicitly
# pruned. This will print out extraneous results from directories whose
# parents have a match. These are filtered out by the awk script below.
find -L "${@:1:$nargs-1}" $findargs -type f -name "$filename" -print 2>/dev/null |

# Only pass along the directory of each match.
sed -e 's/\/[^\/]*$/\//' |

# Sort the output, so directories appear immediately before their contents.
# If there are any duplicates, the awk script will implicitly ignore them.
# The LC_ALL=C forces sort(1) to use bytewise ordering instead of listening
# to the locale, which may do case-insensitive and/or alphanumeric-only
# sorting.
LC_ALL=C sort |

# Always print the first line, which can't possibly be covered by a
# parent directory match. After that, only print lines where the last
# line printed isn't a prefix.
awk -v "filename=$filename" '
    (NR == 1) || (index($0, last) != 1) {
        last = $0;
        printf("%s%s\n", $0, filename);
    }
'