| #!/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; -*- |