1*9e94795aSAndroid Build Coastguard Worker#!/usr/bin/env -S python3 -u 2*9e94795aSAndroid Build Coastguard Worker 3*9e94795aSAndroid Build Coastguard Worker""" 4*9e94795aSAndroid Build Coastguard WorkerThis script helps find various build behaviors that make builds less hermetic 5*9e94795aSAndroid Build Coastguard Workerand repeatable. Depending on the flags, it runs a sequence of builds and looks 6*9e94795aSAndroid Build Coastguard Workerfor files that have changed or have been improperly regenerated, updating 7*9e94795aSAndroid Build Coastguard Workertheir timestamps incorrectly. It also looks for changes that the build has 8*9e94795aSAndroid Build Coastguard Workerdone to the source tree, and for files whose contents are dependent on the 9*9e94795aSAndroid Build Coastguard Workerlocation of the out directory. 10*9e94795aSAndroid Build Coastguard Worker 11*9e94795aSAndroid Build Coastguard WorkerThis utility has two major modes, full and incremental. By default, this tool 12*9e94795aSAndroid Build Coastguard Workerruns in full mode. To run in incremental mode, pass the --incremental flag. 13*9e94795aSAndroid Build Coastguard Worker 14*9e94795aSAndroid Build Coastguard Worker 15*9e94795aSAndroid Build Coastguard WorkerFULL MODE 16*9e94795aSAndroid Build Coastguard Worker 17*9e94795aSAndroid Build Coastguard WorkerIn full mode, this tool helps verify BUILD CORRECTNESS by examining its 18*9e94795aSAndroid Build Coastguard WorkerREPEATABILITY. In full mode, this tool runs two complete builds in different 19*9e94795aSAndroid Build Coastguard Workerdirectories and compares the CONTENTS of the two directories. Lists of any 20*9e94795aSAndroid Build Coastguard Workerfiles that are added, removed or changed are printed, sorted by the timestamp 21*9e94795aSAndroid Build Coastguard Workerof that file, to aid finding which dependencies trigger the rebuilding of 22*9e94795aSAndroid Build Coastguard Workerother files. 23*9e94795aSAndroid Build Coastguard Worker 24*9e94795aSAndroid Build Coastguard Worker 25*9e94795aSAndroid Build Coastguard WorkerINCREMENTAL MODE 26*9e94795aSAndroid Build Coastguard Worker 27*9e94795aSAndroid Build Coastguard WorkerIn incremental mode, this tool helps verfiy the SPEED of the build. It runs two 28*9e94795aSAndroid Build Coastguard Workerbuilds and looks at the TIMESTAMPS of the generated files, and reports files 29*9e94795aSAndroid Build Coastguard Workerthat were changed by the second build. In theory, an incremental build with no 30*9e94795aSAndroid Build Coastguard Workersource files touched should not have any generated targets changed. As in full 31*9e94795aSAndroid Build Coastguard Workerbuilds, the file list is returned sorted by timestamp. 32*9e94795aSAndroid Build Coastguard Worker 33*9e94795aSAndroid Build Coastguard Worker 34*9e94795aSAndroid Build Coastguard WorkerOTHER CHECKS 35*9e94795aSAndroid Build Coastguard Worker 36*9e94795aSAndroid Build Coastguard WorkerIn both full and incremental mode, this tool looks at the timestamps of all 37*9e94795aSAndroid Build Coastguard Workersource files in the tree, and reports on files that have been touched. In the 38*9e94795aSAndroid Build Coastguard Workeroutput, these are labeled with the header "Source files touched after start of 39*9e94795aSAndroid Build Coastguard Workerbuild." 40*9e94795aSAndroid Build Coastguard Worker 41*9e94795aSAndroid Build Coastguard WorkerIn addition, by default, this tool sets the OUT_DIR environment variable to 42*9e94795aSAndroid Build Coastguard Workersomething other than "out" in order to find build rules that are not respecting 43*9e94795aSAndroid Build Coastguard Workerthe OUT_DIR. If you see these, you should fix them, but if your build can not 44*9e94795aSAndroid Build Coastguard Workercomplete for some reason because of this, you can pass the --no-check-out-dir 45*9e94795aSAndroid Build Coastguard Workerflag to suppress this check. 46*9e94795aSAndroid Build Coastguard Worker 47*9e94795aSAndroid Build Coastguard Worker 48*9e94795aSAndroid Build Coastguard WorkerOTHER FLAGS 49*9e94795aSAndroid Build Coastguard Worker 50*9e94795aSAndroid Build Coastguard WorkerIn full mode, the --detect-embedded-paths flag does the two builds in different 51*9e94795aSAndroid Build Coastguard Workerdirectories, to help in finding rules that embed the out directory path into 52*9e94795aSAndroid Build Coastguard Workerthe targets. 53*9e94795aSAndroid Build Coastguard Worker 54*9e94795aSAndroid Build Coastguard WorkerThe --hide-build-output flag hides the output of successful bulds, to make 55*9e94795aSAndroid Build Coastguard Workerscript output cleaner. The output of builds that fail is still shown. 56*9e94795aSAndroid Build Coastguard Worker 57*9e94795aSAndroid Build Coastguard WorkerThe --no-build flag is useful if you have already done a build and would 58*9e94795aSAndroid Build Coastguard Workerjust like to re-run the analysis. 59*9e94795aSAndroid Build Coastguard Worker 60*9e94795aSAndroid Build Coastguard WorkerThe --target flag lets you specify a build target other than the default 61*9e94795aSAndroid Build Coastguard Workerfull build (droid). You can pass "nothing" as in the example below, or a 62*9e94795aSAndroid Build Coastguard Workerspecific target, to reduce the scope of the checks performed. 63*9e94795aSAndroid Build Coastguard Worker 64*9e94795aSAndroid Build Coastguard WorkerThe --touch flag lets you specify a list of source files to touch between 65*9e94795aSAndroid Build Coastguard Workerthe builds, to examine the consequences of editing a particular file. 66*9e94795aSAndroid Build Coastguard Worker 67*9e94795aSAndroid Build Coastguard Worker 68*9e94795aSAndroid Build Coastguard WorkerEXAMPLE COMMANDLINES 69*9e94795aSAndroid Build Coastguard Worker 70*9e94795aSAndroid Build Coastguard WorkerPlease run build/make/tools/compare_builds.py --help for a full listing 71*9e94795aSAndroid Build Coastguard Workerof the commandline flags. Here are a sampling of useful combinations. 72*9e94795aSAndroid Build Coastguard Worker 73*9e94795aSAndroid Build Coastguard Worker 1. Find files changed during an incremental build that doesn't build 74*9e94795aSAndroid Build Coastguard Worker any targets. 75*9e94795aSAndroid Build Coastguard Worker 76*9e94795aSAndroid Build Coastguard Worker build/make/tools/compare_builds.py --incremental --target nothing 77*9e94795aSAndroid Build Coastguard Worker 78*9e94795aSAndroid Build Coastguard Worker Long incremental build times, or consecutive builds that re-run build actions 79*9e94795aSAndroid Build Coastguard Worker are usually caused by files being touched as part of loading the makefiles. 80*9e94795aSAndroid Build Coastguard Worker 81*9e94795aSAndroid Build Coastguard Worker The nothing build (m nothing) loads the make and blueprint files, generates 82*9e94795aSAndroid Build Coastguard Worker the dependency graph, but then doesn't actually build any targets. Checking 83*9e94795aSAndroid Build Coastguard Worker against this build is the fastest and easiest way to find files that are 84*9e94795aSAndroid Build Coastguard Worker modified while makefiles are read, for example with $(shell) invocations. 85*9e94795aSAndroid Build Coastguard Worker 86*9e94795aSAndroid Build Coastguard Worker 2. Find packaging targets that are different, ignoring intermediate files. 87*9e94795aSAndroid Build Coastguard Worker 88*9e94795aSAndroid Build Coastguard Worker build/make/tools/compare_builds.py --subdirs --detect-embedded-paths 89*9e94795aSAndroid Build Coastguard Worker 90*9e94795aSAndroid Build Coastguard Worker These flags will compare the final staging directories for partitions, 91*9e94795aSAndroid Build Coastguard Worker as well as the APKs, apexes, testcases, and the like (the full directory 92*9e94795aSAndroid Build Coastguard Worker list is in the DEFAULT_DIRS variable below). Since these are the files 93*9e94795aSAndroid Build Coastguard Worker that are ultimately released, it is more important that these files be 94*9e94795aSAndroid Build Coastguard Worker replicable, even if the intermediates that went into them are not (for 95*9e94795aSAndroid Build Coastguard Worker example, when debugging symbols are stripped). 96*9e94795aSAndroid Build Coastguard Worker 97*9e94795aSAndroid Build Coastguard Worker 3. Check that all targets are repeatable. 98*9e94795aSAndroid Build Coastguard Worker 99*9e94795aSAndroid Build Coastguard Worker build/make/tools/compare_builds.py --detect-embedded-paths 100*9e94795aSAndroid Build Coastguard Worker 101*9e94795aSAndroid Build Coastguard Worker This check will list all of the differences in built targets that it can 102*9e94795aSAndroid Build Coastguard Worker find. Be aware that the AOSP tree still has quite a few targets that 103*9e94795aSAndroid Build Coastguard Worker are flagged by this check, so OEM changes might be lost in that list. 104*9e94795aSAndroid Build Coastguard Worker That said, each file shown here is a potential blocker for a repeatable 105*9e94795aSAndroid Build Coastguard Worker build. 106*9e94795aSAndroid Build Coastguard Worker 107*9e94795aSAndroid Build Coastguard Worker 4. See what targets are rebuilt when a file is touched between builds. 108*9e94795aSAndroid Build Coastguard Worker 109*9e94795aSAndroid Build Coastguard Worker build/make/tools/compare_builds.py --incremental \ 110*9e94795aSAndroid Build Coastguard Worker --touch frameworks/base/core/java/android/app/Activity.java 111*9e94795aSAndroid Build Coastguard Worker 112*9e94795aSAndroid Build Coastguard Worker This check simulates the common engineer workflow of touching a single 113*9e94795aSAndroid Build Coastguard Worker file and rebuilding the whole system. To see a restricted view, consider 114*9e94795aSAndroid Build Coastguard Worker also passing a --target option for a common use case. For example: 115*9e94795aSAndroid Build Coastguard Worker 116*9e94795aSAndroid Build Coastguard Worker build/make/tools/compare_builds.py --incremental --target framework \ 117*9e94795aSAndroid Build Coastguard Worker --touch frameworks/base/core/java/android/app/Activity.java 118*9e94795aSAndroid Build Coastguard Worker""" 119*9e94795aSAndroid Build Coastguard Worker 120*9e94795aSAndroid Build Coastguard Workerimport argparse 121*9e94795aSAndroid Build Coastguard Workerimport itertools 122*9e94795aSAndroid Build Coastguard Workerimport os 123*9e94795aSAndroid Build Coastguard Workerimport shutil 124*9e94795aSAndroid Build Coastguard Workerimport stat 125*9e94795aSAndroid Build Coastguard Workerimport subprocess 126*9e94795aSAndroid Build Coastguard Workerimport sys 127*9e94795aSAndroid Build Coastguard Worker 128*9e94795aSAndroid Build Coastguard Worker 129*9e94795aSAndroid Build Coastguard Worker# Soong 130*9e94795aSAndroid Build Coastguard WorkerSOONG_UI = "build/soong/soong_ui.bash" 131*9e94795aSAndroid Build Coastguard Worker 132*9e94795aSAndroid Build Coastguard Worker 133*9e94795aSAndroid Build Coastguard Worker# Which directories to use if no --subdirs is supplied without explicit directories. 134*9e94795aSAndroid Build Coastguard WorkerDEFAULT_DIRS = ( 135*9e94795aSAndroid Build Coastguard Worker "apex", 136*9e94795aSAndroid Build Coastguard Worker "data", 137*9e94795aSAndroid Build Coastguard Worker "product", 138*9e94795aSAndroid Build Coastguard Worker "ramdisk", 139*9e94795aSAndroid Build Coastguard Worker "recovery", 140*9e94795aSAndroid Build Coastguard Worker "root", 141*9e94795aSAndroid Build Coastguard Worker "system", 142*9e94795aSAndroid Build Coastguard Worker "system_ext", 143*9e94795aSAndroid Build Coastguard Worker "system_other", 144*9e94795aSAndroid Build Coastguard Worker "testcases", 145*9e94795aSAndroid Build Coastguard Worker "vendor", 146*9e94795aSAndroid Build Coastguard Worker) 147*9e94795aSAndroid Build Coastguard Worker 148*9e94795aSAndroid Build Coastguard Worker 149*9e94795aSAndroid Build Coastguard Worker# Files to skip for incremental timestamp checking 150*9e94795aSAndroid Build Coastguard WorkerBUILD_INTERNALS_PREFIX_SKIP = ( 151*9e94795aSAndroid Build Coastguard Worker "soong/.glob/", 152*9e94795aSAndroid Build Coastguard Worker ".path/", 153*9e94795aSAndroid Build Coastguard Worker) 154*9e94795aSAndroid Build Coastguard Worker 155*9e94795aSAndroid Build Coastguard Worker 156*9e94795aSAndroid Build Coastguard WorkerBUILD_INTERNALS_SUFFIX_SKIP = ( 157*9e94795aSAndroid Build Coastguard Worker "/soong/soong_build_metrics.pb", 158*9e94795aSAndroid Build Coastguard Worker "/.installable_test_files", 159*9e94795aSAndroid Build Coastguard Worker "/files.db", 160*9e94795aSAndroid Build Coastguard Worker "/.blueprint.bootstrap", 161*9e94795aSAndroid Build Coastguard Worker "/build_number.txt", 162*9e94795aSAndroid Build Coastguard Worker "/build.ninja", 163*9e94795aSAndroid Build Coastguard Worker "/.out-dir", 164*9e94795aSAndroid Build Coastguard Worker "/build_fingerprint.txt", 165*9e94795aSAndroid Build Coastguard Worker "/build_thumbprint.txt", 166*9e94795aSAndroid Build Coastguard Worker "/.copied_headers_list", 167*9e94795aSAndroid Build Coastguard Worker "/.installable_files", 168*9e94795aSAndroid Build Coastguard Worker) 169*9e94795aSAndroid Build Coastguard Worker 170*9e94795aSAndroid Build Coastguard Worker 171*9e94795aSAndroid Build Coastguard Workerclass DiffType(object): 172*9e94795aSAndroid Build Coastguard Worker def __init__(self, code, message): 173*9e94795aSAndroid Build Coastguard Worker self.code = code 174*9e94795aSAndroid Build Coastguard Worker self.message = message 175*9e94795aSAndroid Build Coastguard Worker 176*9e94795aSAndroid Build Coastguard WorkerDIFF_NONE = DiffType("DIFF_NONE", "Files are the same") 177*9e94795aSAndroid Build Coastguard WorkerDIFF_MODE = DiffType("DIFF_MODE", "Stat mode bits differ") 178*9e94795aSAndroid Build Coastguard WorkerDIFF_SIZE = DiffType("DIFF_SIZE", "File size differs") 179*9e94795aSAndroid Build Coastguard WorkerDIFF_SYMLINK = DiffType("DIFF_SYMLINK", "Symlinks point to different locations") 180*9e94795aSAndroid Build Coastguard WorkerDIFF_CONTENTS = DiffType("DIFF_CONTENTS", "File contents differ") 181*9e94795aSAndroid Build Coastguard Worker 182*9e94795aSAndroid Build Coastguard Worker 183*9e94795aSAndroid Build Coastguard Workerdef main(): 184*9e94795aSAndroid Build Coastguard Worker argparser = argparse.ArgumentParser(description="Diff build outputs from two builds.", 185*9e94795aSAndroid Build Coastguard Worker epilog="Run this command from the root of the tree." 186*9e94795aSAndroid Build Coastguard Worker + " Before running this command, the build environment" 187*9e94795aSAndroid Build Coastguard Worker + " must be set up, including sourcing build/envsetup.sh" 188*9e94795aSAndroid Build Coastguard Worker + " and running lunch.") 189*9e94795aSAndroid Build Coastguard Worker argparser.add_argument("--detect-embedded-paths", action="store_true", 190*9e94795aSAndroid Build Coastguard Worker help="Use unique out dirs to detect paths embedded in binaries.") 191*9e94795aSAndroid Build Coastguard Worker argparser.add_argument("--incremental", action="store_true", 192*9e94795aSAndroid Build Coastguard Worker help="Compare which files are touched in two consecutive builds without a clean in between.") 193*9e94795aSAndroid Build Coastguard Worker argparser.add_argument("--hide-build-output", action="store_true", 194*9e94795aSAndroid Build Coastguard Worker help="Don't print the build output for successful builds") 195*9e94795aSAndroid Build Coastguard Worker argparser.add_argument("--no-build", dest="run_build", action="store_false", 196*9e94795aSAndroid Build Coastguard Worker help="Don't build or clean, but do everything else.") 197*9e94795aSAndroid Build Coastguard Worker argparser.add_argument("--no-check-out-dir", dest="check_out_dir", action="store_false", 198*9e94795aSAndroid Build Coastguard Worker help="Don't check for rules not honoring movable out directories.") 199*9e94795aSAndroid Build Coastguard Worker argparser.add_argument("--subdirs", nargs="*", 200*9e94795aSAndroid Build Coastguard Worker help="Only scan these subdirs of $PRODUCT_OUT instead of the whole out directory." 201*9e94795aSAndroid Build Coastguard Worker + " The --subdirs argument with no listed directories will give a default list.") 202*9e94795aSAndroid Build Coastguard Worker argparser.add_argument("--target", default="droid", 203*9e94795aSAndroid Build Coastguard Worker help="Make target to run. The default is droid") 204*9e94795aSAndroid Build Coastguard Worker argparser.add_argument("--touch", nargs="+", default=[], 205*9e94795aSAndroid Build Coastguard Worker help="Files to touch between builds. Must pair with --incremental.") 206*9e94795aSAndroid Build Coastguard Worker args = argparser.parse_args(sys.argv[1:]) 207*9e94795aSAndroid Build Coastguard Worker 208*9e94795aSAndroid Build Coastguard Worker if args.detect_embedded_paths and args.incremental: 209*9e94795aSAndroid Build Coastguard Worker sys.stderr.write("Can't pass --detect-embedded-paths and --incremental together.\n") 210*9e94795aSAndroid Build Coastguard Worker sys.exit(1) 211*9e94795aSAndroid Build Coastguard Worker if args.detect_embedded_paths and not args.check_out_dir: 212*9e94795aSAndroid Build Coastguard Worker sys.stderr.write("Can't pass --detect-embedded-paths and --no-check-out-dir together.\n") 213*9e94795aSAndroid Build Coastguard Worker sys.exit(1) 214*9e94795aSAndroid Build Coastguard Worker if args.touch and not args.incremental: 215*9e94795aSAndroid Build Coastguard Worker sys.stderr.write("The --incremental flag is required if the --touch flag is passed.") 216*9e94795aSAndroid Build Coastguard Worker sys.exit(1) 217*9e94795aSAndroid Build Coastguard Worker 218*9e94795aSAndroid Build Coastguard Worker AssertAtTop() 219*9e94795aSAndroid Build Coastguard Worker RequireEnvVar("TARGET_PRODUCT") 220*9e94795aSAndroid Build Coastguard Worker RequireEnvVar("TARGET_BUILD_VARIANT") 221*9e94795aSAndroid Build Coastguard Worker 222*9e94795aSAndroid Build Coastguard Worker # Out dir file names: 223*9e94795aSAndroid Build Coastguard Worker # - dir_prefix - The directory we'll put everything in (except for maybe the top level 224*9e94795aSAndroid Build Coastguard Worker # out/ dir). 225*9e94795aSAndroid Build Coastguard Worker # - *work_dir - The directory that we will build directly into. This is in dir_prefix 226*9e94795aSAndroid Build Coastguard Worker # unless --no-check-out-dir is set. 227*9e94795aSAndroid Build Coastguard Worker # - *out_dir - After building, if work_dir is different from out_dir, we move the out 228*9e94795aSAndroid Build Coastguard Worker # directory to here so we can do the comparisions. 229*9e94795aSAndroid Build Coastguard Worker # - timestamp_* - Files we touch so we know the various phases between the builds, so we 230*9e94795aSAndroid Build Coastguard Worker # can compare timestamps of files. 231*9e94795aSAndroid Build Coastguard Worker if args.incremental: 232*9e94795aSAndroid Build Coastguard Worker dir_prefix = "out_incremental" 233*9e94795aSAndroid Build Coastguard Worker if args.check_out_dir: 234*9e94795aSAndroid Build Coastguard Worker first_work_dir = first_out_dir = dir_prefix + "/out" 235*9e94795aSAndroid Build Coastguard Worker second_work_dir = second_out_dir = dir_prefix + "/out" 236*9e94795aSAndroid Build Coastguard Worker else: 237*9e94795aSAndroid Build Coastguard Worker first_work_dir = first_out_dir = "out" 238*9e94795aSAndroid Build Coastguard Worker second_work_dir = second_out_dir = "out" 239*9e94795aSAndroid Build Coastguard Worker else: 240*9e94795aSAndroid Build Coastguard Worker dir_prefix = "out_full" 241*9e94795aSAndroid Build Coastguard Worker first_out_dir = dir_prefix + "/out_1" 242*9e94795aSAndroid Build Coastguard Worker second_out_dir = dir_prefix + "/out_2" 243*9e94795aSAndroid Build Coastguard Worker if not args.check_out_dir: 244*9e94795aSAndroid Build Coastguard Worker first_work_dir = second_work_dir = "out" 245*9e94795aSAndroid Build Coastguard Worker elif args.detect_embedded_paths: 246*9e94795aSAndroid Build Coastguard Worker first_work_dir = first_out_dir 247*9e94795aSAndroid Build Coastguard Worker second_work_dir = second_out_dir 248*9e94795aSAndroid Build Coastguard Worker else: 249*9e94795aSAndroid Build Coastguard Worker first_work_dir = dir_prefix + "/work" 250*9e94795aSAndroid Build Coastguard Worker second_work_dir = dir_prefix + "/work" 251*9e94795aSAndroid Build Coastguard Worker timestamp_start = dir_prefix + "/timestamp_start" 252*9e94795aSAndroid Build Coastguard Worker timestamp_between = dir_prefix + "/timestamp_between" 253*9e94795aSAndroid Build Coastguard Worker timestamp_end = dir_prefix + "/timestamp_end" 254*9e94795aSAndroid Build Coastguard Worker 255*9e94795aSAndroid Build Coastguard Worker if args.run_build: 256*9e94795aSAndroid Build Coastguard Worker # Initial clean, if necessary 257*9e94795aSAndroid Build Coastguard Worker print("Cleaning " + dir_prefix + "/") 258*9e94795aSAndroid Build Coastguard Worker Clean(dir_prefix) 259*9e94795aSAndroid Build Coastguard Worker print("Cleaning out/") 260*9e94795aSAndroid Build Coastguard Worker Clean("out") 261*9e94795aSAndroid Build Coastguard Worker CreateEmptyFile(timestamp_start) 262*9e94795aSAndroid Build Coastguard Worker print("Running the first build in " + first_work_dir) 263*9e94795aSAndroid Build Coastguard Worker RunBuild(first_work_dir, first_out_dir, args.target, args.hide_build_output) 264*9e94795aSAndroid Build Coastguard Worker for f in args.touch: 265*9e94795aSAndroid Build Coastguard Worker print("Touching " + f) 266*9e94795aSAndroid Build Coastguard Worker TouchFile(f) 267*9e94795aSAndroid Build Coastguard Worker CreateEmptyFile(timestamp_between) 268*9e94795aSAndroid Build Coastguard Worker print("Running the second build in " + second_work_dir) 269*9e94795aSAndroid Build Coastguard Worker RunBuild(second_work_dir, second_out_dir, args.target, args.hide_build_output) 270*9e94795aSAndroid Build Coastguard Worker CreateEmptyFile(timestamp_end) 271*9e94795aSAndroid Build Coastguard Worker print("Done building") 272*9e94795aSAndroid Build Coastguard Worker print() 273*9e94795aSAndroid Build Coastguard Worker 274*9e94795aSAndroid Build Coastguard Worker # Which out directories to scan 275*9e94795aSAndroid Build Coastguard Worker if args.subdirs is not None: 276*9e94795aSAndroid Build Coastguard Worker if args.subdirs: 277*9e94795aSAndroid Build Coastguard Worker subdirs = args.subdirs 278*9e94795aSAndroid Build Coastguard Worker else: 279*9e94795aSAndroid Build Coastguard Worker subdirs = DEFAULT_DIRS 280*9e94795aSAndroid Build Coastguard Worker first_files = ProductFiles(RequireBuildVar(first_out_dir, "PRODUCT_OUT"), subdirs) 281*9e94795aSAndroid Build Coastguard Worker second_files = ProductFiles(RequireBuildVar(second_out_dir, "PRODUCT_OUT"), subdirs) 282*9e94795aSAndroid Build Coastguard Worker else: 283*9e94795aSAndroid Build Coastguard Worker first_files = OutFiles(first_out_dir) 284*9e94795aSAndroid Build Coastguard Worker second_files = OutFiles(second_out_dir) 285*9e94795aSAndroid Build Coastguard Worker 286*9e94795aSAndroid Build Coastguard Worker printer = Printer() 287*9e94795aSAndroid Build Coastguard Worker 288*9e94795aSAndroid Build Coastguard Worker if args.incremental: 289*9e94795aSAndroid Build Coastguard Worker # Find files that were rebuilt unnecessarily 290*9e94795aSAndroid Build Coastguard Worker touched_incrementally = FindOutFilesTouchedAfter(first_files, 291*9e94795aSAndroid Build Coastguard Worker GetFileTimestamp(timestamp_between)) 292*9e94795aSAndroid Build Coastguard Worker printer.PrintList("Touched in incremental build", touched_incrementally) 293*9e94795aSAndroid Build Coastguard Worker else: 294*9e94795aSAndroid Build Coastguard Worker # Compare the two out dirs 295*9e94795aSAndroid Build Coastguard Worker added, removed, changed = DiffFileList(first_files, second_files) 296*9e94795aSAndroid Build Coastguard Worker printer.PrintList("Added", added) 297*9e94795aSAndroid Build Coastguard Worker printer.PrintList("Removed", removed) 298*9e94795aSAndroid Build Coastguard Worker printer.PrintList("Changed", changed, "%s %s") 299*9e94795aSAndroid Build Coastguard Worker 300*9e94795aSAndroid Build Coastguard Worker # Find files in the source tree that were touched 301*9e94795aSAndroid Build Coastguard Worker touched_during = FindSourceFilesTouchedAfter(GetFileTimestamp(timestamp_start)) 302*9e94795aSAndroid Build Coastguard Worker printer.PrintList("Source files touched after start of build", touched_during) 303*9e94795aSAndroid Build Coastguard Worker 304*9e94795aSAndroid Build Coastguard Worker # Find files and dirs that were output to "out" and didn't respect $OUT_DIR 305*9e94795aSAndroid Build Coastguard Worker if args.check_out_dir: 306*9e94795aSAndroid Build Coastguard Worker bad_out_dir_contents = FindFilesAndDirectories("out") 307*9e94795aSAndroid Build Coastguard Worker printer.PrintList("Files and directories created by rules that didn't respect $OUT_DIR", 308*9e94795aSAndroid Build Coastguard Worker bad_out_dir_contents) 309*9e94795aSAndroid Build Coastguard Worker 310*9e94795aSAndroid Build Coastguard Worker # If we didn't find anything, print success message 311*9e94795aSAndroid Build Coastguard Worker if not printer.printed_anything: 312*9e94795aSAndroid Build Coastguard Worker print("No bad behaviors found.") 313*9e94795aSAndroid Build Coastguard Worker 314*9e94795aSAndroid Build Coastguard Worker 315*9e94795aSAndroid Build Coastguard Workerdef AssertAtTop(): 316*9e94795aSAndroid Build Coastguard Worker """If the current directory is not the top of an android source tree, print an error 317*9e94795aSAndroid Build Coastguard Worker message and exit.""" 318*9e94795aSAndroid Build Coastguard Worker if not os.access(SOONG_UI, os.X_OK): 319*9e94795aSAndroid Build Coastguard Worker sys.stderr.write("FAILED: Please run from the root of the tree.\n") 320*9e94795aSAndroid Build Coastguard Worker sys.exit(1) 321*9e94795aSAndroid Build Coastguard Worker 322*9e94795aSAndroid Build Coastguard Worker 323*9e94795aSAndroid Build Coastguard Workerdef RequireEnvVar(name): 324*9e94795aSAndroid Build Coastguard Worker """Gets an environment variable. If that fails, then print an error message and exit.""" 325*9e94795aSAndroid Build Coastguard Worker result = os.environ.get(name) 326*9e94795aSAndroid Build Coastguard Worker if not result: 327*9e94795aSAndroid Build Coastguard Worker sys.stderr.write("error: Can't determine %s. Please run lunch first.\n" % name) 328*9e94795aSAndroid Build Coastguard Worker sys.exit(1) 329*9e94795aSAndroid Build Coastguard Worker return result 330*9e94795aSAndroid Build Coastguard Worker 331*9e94795aSAndroid Build Coastguard Worker 332*9e94795aSAndroid Build Coastguard Workerdef RunSoong(out_dir, args, capture_output): 333*9e94795aSAndroid Build Coastguard Worker env = dict(os.environ) 334*9e94795aSAndroid Build Coastguard Worker env["OUT_DIR"] = out_dir 335*9e94795aSAndroid Build Coastguard Worker args = [SOONG_UI,] + args 336*9e94795aSAndroid Build Coastguard Worker if capture_output: 337*9e94795aSAndroid Build Coastguard Worker proc = subprocess.Popen(args, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 338*9e94795aSAndroid Build Coastguard Worker combined_output, none = proc.communicate() 339*9e94795aSAndroid Build Coastguard Worker return proc.returncode, combined_output 340*9e94795aSAndroid Build Coastguard Worker else: 341*9e94795aSAndroid Build Coastguard Worker result = subprocess.run(args, env=env) 342*9e94795aSAndroid Build Coastguard Worker return result.returncode, None 343*9e94795aSAndroid Build Coastguard Worker 344*9e94795aSAndroid Build Coastguard Worker 345*9e94795aSAndroid Build Coastguard Workerdef GetBuildVar(out_dir, name): 346*9e94795aSAndroid Build Coastguard Worker """Gets a variable from the build system.""" 347*9e94795aSAndroid Build Coastguard Worker returncode, output = RunSoong(out_dir, ["--dumpvar-mode", name], True) 348*9e94795aSAndroid Build Coastguard Worker if returncode != 0: 349*9e94795aSAndroid Build Coastguard Worker return None 350*9e94795aSAndroid Build Coastguard Worker else: 351*9e94795aSAndroid Build Coastguard Worker return output.decode("utf-8").strip() 352*9e94795aSAndroid Build Coastguard Worker 353*9e94795aSAndroid Build Coastguard Worker 354*9e94795aSAndroid Build Coastguard Workerdef RequireBuildVar(out_dir, name): 355*9e94795aSAndroid Build Coastguard Worker """Gets a variable from the builds system. If that fails, then print an error 356*9e94795aSAndroid Build Coastguard Worker message and exit.""" 357*9e94795aSAndroid Build Coastguard Worker value = GetBuildVar(out_dir, name) 358*9e94795aSAndroid Build Coastguard Worker if not value: 359*9e94795aSAndroid Build Coastguard Worker sys.stderr.write("error: Can't determine %s. Please run lunch first.\n" % name) 360*9e94795aSAndroid Build Coastguard Worker sys.exit(1) 361*9e94795aSAndroid Build Coastguard Worker return value 362*9e94795aSAndroid Build Coastguard Worker 363*9e94795aSAndroid Build Coastguard Worker 364*9e94795aSAndroid Build Coastguard Workerdef Clean(directory): 365*9e94795aSAndroid Build Coastguard Worker """"Deletes the supplied directory.""" 366*9e94795aSAndroid Build Coastguard Worker try: 367*9e94795aSAndroid Build Coastguard Worker shutil.rmtree(directory) 368*9e94795aSAndroid Build Coastguard Worker except FileNotFoundError: 369*9e94795aSAndroid Build Coastguard Worker pass 370*9e94795aSAndroid Build Coastguard Worker 371*9e94795aSAndroid Build Coastguard Worker 372*9e94795aSAndroid Build Coastguard Workerdef RunBuild(work_dir, out_dir, target, hide_build_output): 373*9e94795aSAndroid Build Coastguard Worker """Runs a build. If the build fails, prints a message and exits.""" 374*9e94795aSAndroid Build Coastguard Worker returncode, output = RunSoong(work_dir, 375*9e94795aSAndroid Build Coastguard Worker ["--build-mode", "--all-modules", "--dir=" + os.getcwd(), target], 376*9e94795aSAndroid Build Coastguard Worker hide_build_output) 377*9e94795aSAndroid Build Coastguard Worker if work_dir != out_dir: 378*9e94795aSAndroid Build Coastguard Worker os.replace(work_dir, out_dir) 379*9e94795aSAndroid Build Coastguard Worker if returncode != 0: 380*9e94795aSAndroid Build Coastguard Worker if hide_build_output: 381*9e94795aSAndroid Build Coastguard Worker # The build output was hidden, so print it now for debugging 382*9e94795aSAndroid Build Coastguard Worker sys.stderr.buffer.write(output) 383*9e94795aSAndroid Build Coastguard Worker sys.stderr.write("FAILED: Build failed. Stopping.\n") 384*9e94795aSAndroid Build Coastguard Worker sys.exit(1) 385*9e94795aSAndroid Build Coastguard Worker 386*9e94795aSAndroid Build Coastguard Worker 387*9e94795aSAndroid Build Coastguard Workerdef DiffFileList(first_files, second_files): 388*9e94795aSAndroid Build Coastguard Worker """Examines the files. 389*9e94795aSAndroid Build Coastguard Worker 390*9e94795aSAndroid Build Coastguard Worker Returns: 391*9e94795aSAndroid Build Coastguard Worker Filenames of files in first_filelist but not second_filelist (added files) 392*9e94795aSAndroid Build Coastguard Worker Filenames of files in second_filelist but not first_filelist (removed files) 393*9e94795aSAndroid Build Coastguard Worker 2-Tuple of filenames for the files that are in both but are different (changed files) 394*9e94795aSAndroid Build Coastguard Worker """ 395*9e94795aSAndroid Build Coastguard Worker # List of files, relative to their respective PRODUCT_OUT directories 396*9e94795aSAndroid Build Coastguard Worker first_filelist = sorted([x for x in first_files], key=lambda x: x[1]) 397*9e94795aSAndroid Build Coastguard Worker second_filelist = sorted([x for x in second_files], key=lambda x: x[1]) 398*9e94795aSAndroid Build Coastguard Worker 399*9e94795aSAndroid Build Coastguard Worker added = [] 400*9e94795aSAndroid Build Coastguard Worker removed = [] 401*9e94795aSAndroid Build Coastguard Worker changed = [] 402*9e94795aSAndroid Build Coastguard Worker 403*9e94795aSAndroid Build Coastguard Worker first_index = 0 404*9e94795aSAndroid Build Coastguard Worker second_index = 0 405*9e94795aSAndroid Build Coastguard Worker 406*9e94795aSAndroid Build Coastguard Worker while first_index < len(first_filelist) and second_index < len(second_filelist): 407*9e94795aSAndroid Build Coastguard Worker # Path relative to source root and path relative to PRODUCT_OUT 408*9e94795aSAndroid Build Coastguard Worker first_full_filename, first_relative_filename = first_filelist[first_index] 409*9e94795aSAndroid Build Coastguard Worker second_full_filename, second_relative_filename = second_filelist[second_index] 410*9e94795aSAndroid Build Coastguard Worker 411*9e94795aSAndroid Build Coastguard Worker if first_relative_filename < second_relative_filename: 412*9e94795aSAndroid Build Coastguard Worker # Removed 413*9e94795aSAndroid Build Coastguard Worker removed.append(first_full_filename) 414*9e94795aSAndroid Build Coastguard Worker first_index += 1 415*9e94795aSAndroid Build Coastguard Worker elif first_relative_filename > second_relative_filename: 416*9e94795aSAndroid Build Coastguard Worker # Added 417*9e94795aSAndroid Build Coastguard Worker added.append(second_full_filename) 418*9e94795aSAndroid Build Coastguard Worker second_index += 1 419*9e94795aSAndroid Build Coastguard Worker else: 420*9e94795aSAndroid Build Coastguard Worker # Both present 421*9e94795aSAndroid Build Coastguard Worker diff_type = DiffFiles(first_full_filename, second_full_filename) 422*9e94795aSAndroid Build Coastguard Worker if diff_type != DIFF_NONE: 423*9e94795aSAndroid Build Coastguard Worker changed.append((first_full_filename, second_full_filename)) 424*9e94795aSAndroid Build Coastguard Worker first_index += 1 425*9e94795aSAndroid Build Coastguard Worker second_index += 1 426*9e94795aSAndroid Build Coastguard Worker 427*9e94795aSAndroid Build Coastguard Worker while first_index < len(first_filelist): 428*9e94795aSAndroid Build Coastguard Worker first_full_filename, first_relative_filename = first_filelist[first_index] 429*9e94795aSAndroid Build Coastguard Worker removed.append(first_full_filename) 430*9e94795aSAndroid Build Coastguard Worker first_index += 1 431*9e94795aSAndroid Build Coastguard Worker 432*9e94795aSAndroid Build Coastguard Worker while second_index < len(second_filelist): 433*9e94795aSAndroid Build Coastguard Worker second_full_filename, second_relative_filename = second_filelist[second_index] 434*9e94795aSAndroid Build Coastguard Worker added.append(second_full_filename) 435*9e94795aSAndroid Build Coastguard Worker second_index += 1 436*9e94795aSAndroid Build Coastguard Worker 437*9e94795aSAndroid Build Coastguard Worker return (SortByTimestamp(added), 438*9e94795aSAndroid Build Coastguard Worker SortByTimestamp(removed), 439*9e94795aSAndroid Build Coastguard Worker SortByTimestamp(changed, key=lambda item: item[1])) 440*9e94795aSAndroid Build Coastguard Worker 441*9e94795aSAndroid Build Coastguard Worker 442*9e94795aSAndroid Build Coastguard Workerdef FindOutFilesTouchedAfter(files, timestamp): 443*9e94795aSAndroid Build Coastguard Worker """Find files in the given file iterator that were touched after timestamp.""" 444*9e94795aSAndroid Build Coastguard Worker result = [] 445*9e94795aSAndroid Build Coastguard Worker for full, relative in files: 446*9e94795aSAndroid Build Coastguard Worker ts = GetFileTimestamp(full) 447*9e94795aSAndroid Build Coastguard Worker if ts > timestamp: 448*9e94795aSAndroid Build Coastguard Worker result.append(TouchedFile(full, ts)) 449*9e94795aSAndroid Build Coastguard Worker return [f.filename for f in sorted(result, key=lambda f: f.timestamp)] 450*9e94795aSAndroid Build Coastguard Worker 451*9e94795aSAndroid Build Coastguard Worker 452*9e94795aSAndroid Build Coastguard Workerdef GetFileTimestamp(filename): 453*9e94795aSAndroid Build Coastguard Worker """Get timestamp for a file (just wraps stat).""" 454*9e94795aSAndroid Build Coastguard Worker st = os.stat(filename, follow_symlinks=False) 455*9e94795aSAndroid Build Coastguard Worker return st.st_mtime 456*9e94795aSAndroid Build Coastguard Worker 457*9e94795aSAndroid Build Coastguard Worker 458*9e94795aSAndroid Build Coastguard Workerdef SortByTimestamp(items, key=lambda item: item): 459*9e94795aSAndroid Build Coastguard Worker """Sort the list by timestamp of files. 460*9e94795aSAndroid Build Coastguard Worker Args: 461*9e94795aSAndroid Build Coastguard Worker items - the list of items to sort 462*9e94795aSAndroid Build Coastguard Worker key - a function to extract a filename from each element in items 463*9e94795aSAndroid Build Coastguard Worker """ 464*9e94795aSAndroid Build Coastguard Worker return [x[0] for x in sorted([(item, GetFileTimestamp(key(item))) for item in items], 465*9e94795aSAndroid Build Coastguard Worker key=lambda y: y[1])] 466*9e94795aSAndroid Build Coastguard Worker 467*9e94795aSAndroid Build Coastguard Worker 468*9e94795aSAndroid Build Coastguard Workerdef FindSourceFilesTouchedAfter(timestamp): 469*9e94795aSAndroid Build Coastguard Worker """Find files in the source tree that have changed after timestamp. Ignores 470*9e94795aSAndroid Build Coastguard Worker the out directory.""" 471*9e94795aSAndroid Build Coastguard Worker result = [] 472*9e94795aSAndroid Build Coastguard Worker for root, dirs, files in os.walk(".", followlinks=False): 473*9e94795aSAndroid Build Coastguard Worker if root == ".": 474*9e94795aSAndroid Build Coastguard Worker RemoveItemsFromList(dirs, (".repo", "out", "out_full", "out_incremental")) 475*9e94795aSAndroid Build Coastguard Worker for f in files: 476*9e94795aSAndroid Build Coastguard Worker full = os.path.sep.join((root, f))[2:] 477*9e94795aSAndroid Build Coastguard Worker ts = GetFileTimestamp(full) 478*9e94795aSAndroid Build Coastguard Worker if ts > timestamp: 479*9e94795aSAndroid Build Coastguard Worker result.append(TouchedFile(full, ts)) 480*9e94795aSAndroid Build Coastguard Worker return [f.filename for f in sorted(result, key=lambda f: f.timestamp)] 481*9e94795aSAndroid Build Coastguard Worker 482*9e94795aSAndroid Build Coastguard Worker 483*9e94795aSAndroid Build Coastguard Workerdef FindFilesAndDirectories(directory): 484*9e94795aSAndroid Build Coastguard Worker """Finds all files and directories inside a directory.""" 485*9e94795aSAndroid Build Coastguard Worker result = [] 486*9e94795aSAndroid Build Coastguard Worker for root, dirs, files in os.walk(directory, followlinks=False): 487*9e94795aSAndroid Build Coastguard Worker result += [os.path.sep.join((root, x, "")) for x in dirs] 488*9e94795aSAndroid Build Coastguard Worker result += [os.path.sep.join((root, x)) for x in files] 489*9e94795aSAndroid Build Coastguard Worker return result 490*9e94795aSAndroid Build Coastguard Worker 491*9e94795aSAndroid Build Coastguard Worker 492*9e94795aSAndroid Build Coastguard Workerdef CreateEmptyFile(filename): 493*9e94795aSAndroid Build Coastguard Worker """Create an empty file with now as the timestamp at filename.""" 494*9e94795aSAndroid Build Coastguard Worker try: 495*9e94795aSAndroid Build Coastguard Worker os.makedirs(os.path.dirname(filename)) 496*9e94795aSAndroid Build Coastguard Worker except FileExistsError: 497*9e94795aSAndroid Build Coastguard Worker pass 498*9e94795aSAndroid Build Coastguard Worker open(filename, "w").close() 499*9e94795aSAndroid Build Coastguard Worker os.utime(filename) 500*9e94795aSAndroid Build Coastguard Worker 501*9e94795aSAndroid Build Coastguard Worker 502*9e94795aSAndroid Build Coastguard Workerdef TouchFile(filename): 503*9e94795aSAndroid Build Coastguard Worker os.utime(filename) 504*9e94795aSAndroid Build Coastguard Worker 505*9e94795aSAndroid Build Coastguard Worker 506*9e94795aSAndroid Build Coastguard Workerdef DiffFiles(first_filename, second_filename): 507*9e94795aSAndroid Build Coastguard Worker def AreFileContentsSame(remaining, first_filename, second_filename): 508*9e94795aSAndroid Build Coastguard Worker """Compare the file contents. They must be known to be the same size.""" 509*9e94795aSAndroid Build Coastguard Worker CHUNK_SIZE = 32*1024 510*9e94795aSAndroid Build Coastguard Worker with open(first_filename, "rb") as first_file: 511*9e94795aSAndroid Build Coastguard Worker with open(second_filename, "rb") as second_file: 512*9e94795aSAndroid Build Coastguard Worker while remaining > 0: 513*9e94795aSAndroid Build Coastguard Worker size = min(CHUNK_SIZE, remaining) 514*9e94795aSAndroid Build Coastguard Worker if first_file.read(CHUNK_SIZE) != second_file.read(CHUNK_SIZE): 515*9e94795aSAndroid Build Coastguard Worker return False 516*9e94795aSAndroid Build Coastguard Worker remaining -= size 517*9e94795aSAndroid Build Coastguard Worker return True 518*9e94795aSAndroid Build Coastguard Worker 519*9e94795aSAndroid Build Coastguard Worker first_stat = os.stat(first_filename, follow_symlinks=False) 520*9e94795aSAndroid Build Coastguard Worker second_stat = os.stat(first_filename, follow_symlinks=False) 521*9e94795aSAndroid Build Coastguard Worker 522*9e94795aSAndroid Build Coastguard Worker # Mode bits 523*9e94795aSAndroid Build Coastguard Worker if first_stat.st_mode != second_stat.st_mode: 524*9e94795aSAndroid Build Coastguard Worker return DIFF_MODE 525*9e94795aSAndroid Build Coastguard Worker 526*9e94795aSAndroid Build Coastguard Worker # File size 527*9e94795aSAndroid Build Coastguard Worker if first_stat.st_size != second_stat.st_size: 528*9e94795aSAndroid Build Coastguard Worker return DIFF_SIZE 529*9e94795aSAndroid Build Coastguard Worker 530*9e94795aSAndroid Build Coastguard Worker # Contents 531*9e94795aSAndroid Build Coastguard Worker if stat.S_ISLNK(first_stat.st_mode): 532*9e94795aSAndroid Build Coastguard Worker if os.readlink(first_filename) != os.readlink(second_filename): 533*9e94795aSAndroid Build Coastguard Worker return DIFF_SYMLINK 534*9e94795aSAndroid Build Coastguard Worker elif stat.S_ISREG(first_stat.st_mode): 535*9e94795aSAndroid Build Coastguard Worker if not AreFileContentsSame(first_stat.st_size, first_filename, second_filename): 536*9e94795aSAndroid Build Coastguard Worker return DIFF_CONTENTS 537*9e94795aSAndroid Build Coastguard Worker 538*9e94795aSAndroid Build Coastguard Worker return DIFF_NONE 539*9e94795aSAndroid Build Coastguard Worker 540*9e94795aSAndroid Build Coastguard Worker 541*9e94795aSAndroid Build Coastguard Workerclass FileIterator(object): 542*9e94795aSAndroid Build Coastguard Worker """Object that produces an iterator containing all files in a given directory. 543*9e94795aSAndroid Build Coastguard Worker 544*9e94795aSAndroid Build Coastguard Worker Each iteration yields a tuple containing: 545*9e94795aSAndroid Build Coastguard Worker 546*9e94795aSAndroid Build Coastguard Worker [0] (full) Path to file relative to source tree. 547*9e94795aSAndroid Build Coastguard Worker [1] (relative) Path to the file relative to the base directory given in the 548*9e94795aSAndroid Build Coastguard Worker constructor. 549*9e94795aSAndroid Build Coastguard Worker """ 550*9e94795aSAndroid Build Coastguard Worker 551*9e94795aSAndroid Build Coastguard Worker def __init__(self, base_dir): 552*9e94795aSAndroid Build Coastguard Worker self._base_dir = base_dir 553*9e94795aSAndroid Build Coastguard Worker 554*9e94795aSAndroid Build Coastguard Worker def __iter__(self): 555*9e94795aSAndroid Build Coastguard Worker return self._Iterator(self, self._base_dir) 556*9e94795aSAndroid Build Coastguard Worker 557*9e94795aSAndroid Build Coastguard Worker def ShouldIncludeFile(self, root, path): 558*9e94795aSAndroid Build Coastguard Worker return False 559*9e94795aSAndroid Build Coastguard Worker 560*9e94795aSAndroid Build Coastguard Worker class _Iterator(object): 561*9e94795aSAndroid Build Coastguard Worker def __init__(self, parent, base_dir): 562*9e94795aSAndroid Build Coastguard Worker self._parent = parent 563*9e94795aSAndroid Build Coastguard Worker self._base_dir = base_dir 564*9e94795aSAndroid Build Coastguard Worker self._walker = os.walk(base_dir, followlinks=False) 565*9e94795aSAndroid Build Coastguard Worker self._current_index = 0 566*9e94795aSAndroid Build Coastguard Worker self._current_dir = [] 567*9e94795aSAndroid Build Coastguard Worker 568*9e94795aSAndroid Build Coastguard Worker def __iter__(self): 569*9e94795aSAndroid Build Coastguard Worker return self 570*9e94795aSAndroid Build Coastguard Worker 571*9e94795aSAndroid Build Coastguard Worker def __next__(self): 572*9e94795aSAndroid Build Coastguard Worker # os.walk's iterator will eventually terminate by raising StopIteration 573*9e94795aSAndroid Build Coastguard Worker while True: 574*9e94795aSAndroid Build Coastguard Worker if self._current_index >= len(self._current_dir): 575*9e94795aSAndroid Build Coastguard Worker root, dirs, files = self._walker.__next__() 576*9e94795aSAndroid Build Coastguard Worker full_paths = [os.path.sep.join((root, f)) for f in files] 577*9e94795aSAndroid Build Coastguard Worker pairs = [(f, f[len(self._base_dir)+1:]) for f in full_paths] 578*9e94795aSAndroid Build Coastguard Worker self._current_dir = [(full, relative) for full, relative in pairs 579*9e94795aSAndroid Build Coastguard Worker if self._parent.ShouldIncludeFile(root, relative)] 580*9e94795aSAndroid Build Coastguard Worker self._current_index = 0 581*9e94795aSAndroid Build Coastguard Worker if not self._current_dir: 582*9e94795aSAndroid Build Coastguard Worker continue 583*9e94795aSAndroid Build Coastguard Worker index = self._current_index 584*9e94795aSAndroid Build Coastguard Worker self._current_index += 1 585*9e94795aSAndroid Build Coastguard Worker return self._current_dir[index] 586*9e94795aSAndroid Build Coastguard Worker 587*9e94795aSAndroid Build Coastguard Worker 588*9e94795aSAndroid Build Coastguard Workerclass OutFiles(FileIterator): 589*9e94795aSAndroid Build Coastguard Worker """Object that produces an iterator containing all files in a given out directory, 590*9e94795aSAndroid Build Coastguard Worker except for files which are known to be touched as part of build setup. 591*9e94795aSAndroid Build Coastguard Worker """ 592*9e94795aSAndroid Build Coastguard Worker def __init__(self, out_dir): 593*9e94795aSAndroid Build Coastguard Worker super().__init__(out_dir) 594*9e94795aSAndroid Build Coastguard Worker self._out_dir = out_dir 595*9e94795aSAndroid Build Coastguard Worker 596*9e94795aSAndroid Build Coastguard Worker def ShouldIncludeFile(self, root, relative): 597*9e94795aSAndroid Build Coastguard Worker # Skip files in root, although note that this could actually skip 598*9e94795aSAndroid Build Coastguard Worker # files that are sadly generated directly into that directory. 599*9e94795aSAndroid Build Coastguard Worker if root == self._out_dir: 600*9e94795aSAndroid Build Coastguard Worker return False 601*9e94795aSAndroid Build Coastguard Worker # Skiplist 602*9e94795aSAndroid Build Coastguard Worker for skip in BUILD_INTERNALS_PREFIX_SKIP: 603*9e94795aSAndroid Build Coastguard Worker if relative.startswith(skip): 604*9e94795aSAndroid Build Coastguard Worker return False 605*9e94795aSAndroid Build Coastguard Worker for skip in BUILD_INTERNALS_SUFFIX_SKIP: 606*9e94795aSAndroid Build Coastguard Worker if relative.endswith(skip): 607*9e94795aSAndroid Build Coastguard Worker return False 608*9e94795aSAndroid Build Coastguard Worker return True 609*9e94795aSAndroid Build Coastguard Worker 610*9e94795aSAndroid Build Coastguard Worker 611*9e94795aSAndroid Build Coastguard Workerclass ProductFiles(FileIterator): 612*9e94795aSAndroid Build Coastguard Worker """Object that produces an iterator containing files in listed subdirectories of $PRODUCT_OUT. 613*9e94795aSAndroid Build Coastguard Worker """ 614*9e94795aSAndroid Build Coastguard Worker def __init__(self, product_out, subdirs): 615*9e94795aSAndroid Build Coastguard Worker super().__init__(product_out) 616*9e94795aSAndroid Build Coastguard Worker self._subdirs = subdirs 617*9e94795aSAndroid Build Coastguard Worker 618*9e94795aSAndroid Build Coastguard Worker def ShouldIncludeFile(self, root, relative): 619*9e94795aSAndroid Build Coastguard Worker for subdir in self._subdirs: 620*9e94795aSAndroid Build Coastguard Worker if relative.startswith(subdir): 621*9e94795aSAndroid Build Coastguard Worker return True 622*9e94795aSAndroid Build Coastguard Worker return False 623*9e94795aSAndroid Build Coastguard Worker 624*9e94795aSAndroid Build Coastguard Worker 625*9e94795aSAndroid Build Coastguard Workerclass TouchedFile(object): 626*9e94795aSAndroid Build Coastguard Worker """A file in the out directory with a timestamp.""" 627*9e94795aSAndroid Build Coastguard Worker def __init__(self, filename, timestamp): 628*9e94795aSAndroid Build Coastguard Worker self.filename = filename 629*9e94795aSAndroid Build Coastguard Worker self.timestamp = timestamp 630*9e94795aSAndroid Build Coastguard Worker 631*9e94795aSAndroid Build Coastguard Worker 632*9e94795aSAndroid Build Coastguard Workerdef RemoveItemsFromList(haystack, needles): 633*9e94795aSAndroid Build Coastguard Worker for needle in needles: 634*9e94795aSAndroid Build Coastguard Worker try: 635*9e94795aSAndroid Build Coastguard Worker haystack.remove(needle) 636*9e94795aSAndroid Build Coastguard Worker except ValueError: 637*9e94795aSAndroid Build Coastguard Worker pass 638*9e94795aSAndroid Build Coastguard Worker 639*9e94795aSAndroid Build Coastguard Worker 640*9e94795aSAndroid Build Coastguard Workerclass Printer(object): 641*9e94795aSAndroid Build Coastguard Worker def __init__(self): 642*9e94795aSAndroid Build Coastguard Worker self.printed_anything = False 643*9e94795aSAndroid Build Coastguard Worker 644*9e94795aSAndroid Build Coastguard Worker def PrintList(self, title, items, fmt="%s"): 645*9e94795aSAndroid Build Coastguard Worker if items: 646*9e94795aSAndroid Build Coastguard Worker if self.printed_anything: 647*9e94795aSAndroid Build Coastguard Worker sys.stdout.write("\n") 648*9e94795aSAndroid Build Coastguard Worker sys.stdout.write("%s:\n" % title) 649*9e94795aSAndroid Build Coastguard Worker for item in items: 650*9e94795aSAndroid Build Coastguard Worker sys.stdout.write(" %s\n" % fmt % item) 651*9e94795aSAndroid Build Coastguard Worker self.printed_anything = True 652*9e94795aSAndroid Build Coastguard Worker 653*9e94795aSAndroid Build Coastguard Worker 654*9e94795aSAndroid Build Coastguard Workerif __name__ == "__main__": 655*9e94795aSAndroid Build Coastguard Worker try: 656*9e94795aSAndroid Build Coastguard Worker main() 657*9e94795aSAndroid Build Coastguard Worker except KeyboardInterrupt: 658*9e94795aSAndroid Build Coastguard Worker pass 659*9e94795aSAndroid Build Coastguard Worker 660*9e94795aSAndroid Build Coastguard Worker 661*9e94795aSAndroid Build Coastguard Worker# vim: ts=2 sw=2 sts=2 nocindent 662