|  | #!/usr/bin/python | 
|  | # | 
|  | # Copyright (C) 2011 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. | 
|  | # | 
|  |  | 
|  | import re | 
|  | import os | 
|  | import sys | 
|  | import getopt | 
|  | import shutil | 
|  | import subprocess | 
|  | import zipfile | 
|  |  | 
|  | VERBOSE = False | 
|  | TOP_FOLDER = "src" | 
|  | _RE_PKG = re.compile("^\s*package\s+([^\s;]+)\s*;.*") | 
|  |  | 
|  | # Holds cmd-line arguments and context information | 
|  | class Params(object): | 
|  | def __init__(self): | 
|  | self.EXEC_ZIP = False | 
|  | self.DRY = False | 
|  | self.PROPS = None | 
|  | self.SRC = None | 
|  | self.DST = None | 
|  | self.CNT_USED = 0 | 
|  | self.CNT_NOPKG = 0 | 
|  | # DIR is the list of directories to scan in TOPDIR. | 
|  | self.DIR = "frameworks libcore" | 
|  | self.IGNORE_DIR = [ "hosttests" "tools" "tests" ] | 
|  | # IGNORE is a list of namespaces to ignore. Must be java | 
|  | # package definitions (e.g. "com.blah.foo.") | 
|  | self.IGNORE = [ "sun.", "libcore.", "dalvik.", | 
|  | "com.test.", "com.google.", | 
|  | "coretestutils.", "test.", "test2.", "tests." ] | 
|  | self.zipfile = None | 
|  |  | 
|  |  | 
|  | def verbose(msg, *args): | 
|  | """Prints a verbose message to stderr if --verbose is set.""" | 
|  | global VERBOSE | 
|  | if VERBOSE: | 
|  | if args: | 
|  | msg = msg % args | 
|  | print >>sys.stderr, msg | 
|  |  | 
|  |  | 
|  | # Prints a usage summary | 
|  | def usage(error=None): | 
|  | print """ | 
|  | Description: | 
|  | This script collects all framework Java sources from the current android | 
|  | source code and places them in a source.zip file that can be distributed | 
|  | by the SDK Manager. | 
|  |  | 
|  | Usage: | 
|  | %s [-n|-v|-z] <source.properties> <sources.zip> <topdir> | 
|  |  | 
|  | The source.properties file must exist and will be injected in the Zip file. | 
|  | The source directory must already exist. | 
|  | Use -v for verbose output (lists each file being picked up or ignored). | 
|  | Use -n for a dry-run (doesn't write the zip file). | 
|  | Use -z to use the system 'zip' command instead of the Python Zip module. | 
|  |  | 
|  | """ % sys.argv[0] | 
|  |  | 
|  | if error: | 
|  | print >>sys.stderr, "Error:", error | 
|  |  | 
|  |  | 
|  | # Parse command line args, returns a Params instance or sys.exit(2) on error | 
|  | # after printing the error and the usage. | 
|  | def parseArgs(argv): | 
|  | global VERBOSE | 
|  | p = Params() | 
|  | error = None | 
|  |  | 
|  | try: | 
|  | opts, args = getopt.getopt(argv[1:], | 
|  | "zvns:", | 
|  | [ "exec-zip", "verbose", "dry", "sourcedir=" ]) | 
|  | except getopt.GetoptError, e: | 
|  | error = str(e) | 
|  |  | 
|  | if error is None: | 
|  | for o, a in opts: | 
|  | if o in [ "-n", "--dry" ]: | 
|  | # Dry mode: don't copy/zip, print what would be done. | 
|  | p.DRY = True | 
|  | if o in [ "-v", "--verbose" ]: | 
|  | # Verbose mode. Display everything that's going on. | 
|  | VERBOSE = True | 
|  | elif o in [ "-s", "--sourcedir" ]: | 
|  | # The source directories to process (space separated list) | 
|  | p.DIR = a | 
|  | elif o in [ "-z", "--exec-zip" ]: | 
|  | # Don't use Python zip, instead call the 'zip' system exec. | 
|  | p.EXEC_ZIP = True | 
|  |  | 
|  | if len(args) != 3: | 
|  | error = "Missing arguments: <source> <dest>" | 
|  | else: | 
|  | p.PROPS = args[0] | 
|  | p.DST = args[1] | 
|  | p.SRC = args[2] | 
|  |  | 
|  | if not os.path.isfile(p.PROPS): | 
|  | error = "%s is not a file" % p.PROPS | 
|  | if not os.path.isdir(p.SRC): | 
|  | error = "%s is not a directory" % p.SRC | 
|  |  | 
|  | if error: | 
|  | usage(error) | 
|  | sys.exit(2) | 
|  |  | 
|  | return p | 
|  |  | 
|  |  | 
|  | # Recursively parses the given directory and processes java files found | 
|  | def parseSrcDir(p, srcdir): | 
|  | if not os.path.exists(srcdir): | 
|  | verbose("Error: Skipping unknown directory %s", srcdir) | 
|  | return | 
|  |  | 
|  | for filename in os.listdir(srcdir): | 
|  | filepath = os.path.join(srcdir, filename) | 
|  | if filename.endswith(".java") and os.path.isfile(filepath): | 
|  | pkg = checkJavaFile(filepath) | 
|  | if not pkg: | 
|  | verbose("No package found in %s", filepath) | 
|  | if pkg: | 
|  | # Should we ignore this package? | 
|  | pkg2 = pkg | 
|  | if not "." in pkg2: | 
|  | pkg2 += "." | 
|  | for ignore in p.IGNORE: | 
|  | if pkg2.startswith(ignore): | 
|  | verbose("Ignore package %s [%s]", pkg, filepath) | 
|  | pkg = None | 
|  | break | 
|  |  | 
|  | if pkg: | 
|  | pkg = pkg.replace(".", os.path.sep)  # e.g. android.view => android/view | 
|  | copy(p, filepath, pkg) | 
|  | p.CNT_USED += 1 | 
|  | else: | 
|  | p.CNT_NOPKG += 1 | 
|  | elif os.path.isdir(filepath): | 
|  | if not filename in p.IGNORE_DIR: | 
|  | parseSrcDir(p, filepath) | 
|  |  | 
|  |  | 
|  | # Check a java file to find its package declaration, if any | 
|  | def checkJavaFile(path): | 
|  | try: | 
|  | f = None | 
|  | try: | 
|  | f = file(path) | 
|  | for l in f.readlines(): | 
|  | m = _RE_PKG.match(l) | 
|  | if m: | 
|  | return m.group(1) | 
|  | finally: | 
|  | if f: f.close() | 
|  | except Exception: | 
|  | pass | 
|  |  | 
|  | return None | 
|  |  | 
|  |  | 
|  | USED_ARC_PATH = {} | 
|  |  | 
|  | # Copy the given file (given its absolute filepath) to | 
|  | # the relative desk_pkg directory in the zip file. | 
|  | def copy(p, filepath, dest_pkg): | 
|  | arc_path = os.path.join(TOP_FOLDER, dest_pkg, os.path.basename(filepath)) | 
|  | if arc_path in USED_ARC_PATH: | 
|  | verbose("Ignore duplicate archive path %s", arc_path) | 
|  | USED_ARC_PATH[arc_path] = 1 | 
|  | if p.DRY: | 
|  | print >>sys.stderr, "zip %s [%s]" % (arc_path, filepath) | 
|  | elif p.zipfile is not None: | 
|  | if p.EXEC_ZIP: | 
|  | # zipfile is a path. Copy to it. | 
|  | dest_path = os.path.join(p.zipfile, arc_path) | 
|  | dest_dir = os.path.dirname(dest_path) | 
|  | if not os.path.isdir(dest_dir): | 
|  | os.makedirs(dest_dir) | 
|  | shutil.copyfile(filepath, dest_path) | 
|  | else: | 
|  | # zipfile is a ZipFile object. Compress with it. | 
|  | p.zipfile.write(filepath, arc_path) | 
|  |  | 
|  |  | 
|  | def shellExec(*cmd): | 
|  | """ | 
|  | Executes the given system command. | 
|  |  | 
|  | The command must be split into a list (c.f. shext.split().) | 
|  |  | 
|  | This raises an exception if the command fails. | 
|  | Stdin/out/err are not being redirected. | 
|  | """ | 
|  | verbose("exec: %s", repr(cmd)) | 
|  | subprocess.check_call(cmd) | 
|  |  | 
|  |  | 
|  | def main(): | 
|  | p = parseArgs(sys.argv) | 
|  | z = None | 
|  | try: | 
|  | if not p.DRY: | 
|  | if p.EXEC_ZIP: | 
|  | p.zipfile = p.DST + "_temp_dir" | 
|  | if os.path.exists(p.zipfile): | 
|  | shutil.rmtree(p.zipfile) | 
|  | props_dest = os.path.join(p.zipfile, TOP_FOLDER + "/source.properties") | 
|  | os.makedirs(os.path.dirname(props_dest)) | 
|  | shutil.copyfile(p.PROPS, props_dest) | 
|  | else: | 
|  | p.zipfile = z = zipfile.ZipFile(p.DST, "w", zipfile.ZIP_DEFLATED) | 
|  | z.write(p.PROPS, TOP_FOLDER + "/source.properties") | 
|  | for d in p.DIR.split(): | 
|  | if d: | 
|  | parseSrcDir(p, os.path.join(p.SRC, d)) | 
|  | if p.EXEC_ZIP and not p.DRY: | 
|  | curr_dir = os.getcwd() | 
|  | os.chdir(p.zipfile) | 
|  | if os.path.exists("_temp.zip"): | 
|  | os.unlink("_temp.zip"); | 
|  | shellExec("zip", "-9r", "_temp.zip", TOP_FOLDER) | 
|  | os.chdir(curr_dir) | 
|  | shutil.move(os.path.join(p.zipfile, "_temp.zip"), p.DST) | 
|  | shutil.rmtree(p.zipfile) | 
|  | finally: | 
|  | if z is not None: | 
|  | z.close() | 
|  | print "%s: %d java files copied" % (p.DST, p.CNT_USED) | 
|  | if p.CNT_NOPKG: | 
|  | print "%s: %d java files ignored" % (p.DST, p.CNT_NOPKG) | 
|  | if p.DRY: | 
|  | print >>sys.stderr, "This was in *DRY* mode. No copies done." | 
|  |  | 
|  |  | 
|  | if __name__ == "__main__": | 
|  | main() | 
|  |  | 
|  | # For emacs: | 
|  | # -*- tab-width: 4; -*- |