You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
261 lines
8.1 KiB
261 lines
8.1 KiB
#!/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", "samples" ] |
|
# 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; -*-
|
|
|