blob: 3d4c582857e2953ccfa7a4183c6c0076a17c59b6 [file] [log] [blame]
Raphael Mollda9eabd2011-09-28 11:18:34 -07001#!/usr/bin/python
2#
3# Copyright (C) 2011 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18import re
19import os
20import sys
21import getopt
22import zipfile
23
24VERBOSE = False
25TOP_FOLDER = "src"
26_RE_PKG = re.compile("^\s*package\s+([^\s;]+)\s*;.*")
27
28# Holds cmd-line arguments and context information
29class Params(object):
30 def __init__(self):
31 self.DRY = False
32 self.PROPS = None
33 self.SRC = None
34 self.DST = None
35 self.CNT_USED = 0
36 self.CNT_NOPKG = 0
37 # DIR is the list of directories to scan in TOPDIR.
38 self.DIR = "frameworks libcore"
39 # IGNORE is a list of namespaces to ignore. Must be java
Raphael Mollfdefc022011-10-01 23:42:04 -070040 # package definitions (e.g. "com.blah.foo.")
41 self.IGNORE = [ "sun.", "libcore.", "dalvik.",
42 "com.test.", "com.google.",
Raphael Mollda9eabd2011-09-28 11:18:34 -070043 "coretestutils.", "test.", "test2.", "tests." ]
44 self.zipfile = None
45
46
47def verbose(msg, *args):
48 """Prints a verbose message to stderr if --verbose is set."""
49 global VERBOSE
50 if VERBOSE:
51 if args:
52 msg = msg % args
53 print >>sys.stderr, msg
54
55
56# Prints a usage summary
57def usage(error=None):
58 print """
59 Description:
60 This script collects all framework Java sources from the current android
61 source code and places them in a source.zip file that can be distributed
62 by the SDK Manager.
63
64 Usage:
65 %s [-n|-v] <source.properties> <sources.zip> <topdir>
66
67 The source.properties file must exist and will be injected in the Zip file.
68 The source directory must already exist.
69 Use -v for verbose output (lists each file being picked up or ignored).
70 Use -n for a dry-run (doesn't write the zip file).
71
72""" % sys.argv[0]
73
74 if error:
75 print >>sys.stderr, "Error:", error
76
77
78# Parse command line args, returns a Params instance or sys.exit(2) on error
79# after printing the error and the usage.
80def parseArgs(argv):
81 global VERBOSE
82 p = Params()
83 error = None
84
85 try:
86 opts, args = getopt.getopt(argv[1:],
87 "vns:",
88 [ "--verbose", "--dry", "--sourcedir=" ])
89 except getopt.GetoptError, e:
90 error = str(e)
91
92 if error is None:
93 for o, a in opts:
94 if o in [ "-n", "--dry" ]:
95 p.DRY = True
96 if o in [ "-v", "--verbose" ]:
97 VERBOSE = True
98 elif o in [ "-s", "--sourcedir" ]:
99 p.DIR = a
100
101 if len(args) != 3:
102 error = "Missing arguments: <source> <dest>"
103 else:
104 p.PROPS = args[0]
105 p.DST = args[1]
106 p.SRC = args[2]
107
108 if not os.path.isfile(p.PROPS):
109 error = "%s is not a file" % p.PROPS
110 if not os.path.isdir(p.SRC):
111 error = "%s is not a directory" % p.SRC
112
113 if error:
114 usage(error)
115 sys.exit(2)
116
117 return p
118
119
120# Recursively parses the given directory and processes java files found
121def parseSrcDir(p, srcdir):
122 if not os.path.exists(srcdir):
123 verbose("Error: Skipping unknown directory %s", srcdir)
124 return
125
126 for filename in os.listdir(srcdir):
127 filepath = os.path.join(srcdir, filename)
128 if filename.endswith(".java") and os.path.isfile(filepath):
129 pkg = checkJavaFile(filepath)
130 if not pkg:
131 verbose("No package found in %s", filepath)
132 if pkg:
133 # Should we ignore this package?
134 for ignore in p.IGNORE:
135 if pkg.startswith(ignore):
136 verbose("Ignore package %s [%s]", pkg, filepath)
137 pkg = None
138 break
139
140 if pkg:
141 pkg = pkg.replace(".", os.path.sep) # e.g. android.view => android/view
142 copy(p, filepath, pkg)
143 p.CNT_USED += 1
144 else:
145 p.CNT_NOPKG += 1
146 elif os.path.isdir(filepath):
147 parseSrcDir(p, filepath)
148
149
150# Check a java file to find its package declaration, if any
151def checkJavaFile(path):
152 try:
153 f = None
154 try:
155 f = file(path)
156 for l in f.readlines():
157 m = _RE_PKG.match(l)
158 if m:
159 return m.group(1)
160 finally:
161 if f: f.close()
162 except Exception:
163 pass
164
165 return None
166
167
168# Copy the given file (given its absolute filepath) to
169# the relative desk_pkg directory in the zip file.
170def copy(p, filepath, dest_pkg):
171 arc_path = os.path.join(TOP_FOLDER, dest_pkg, os.path.basename(filepath))
172 if p.DRY:
173 print >>sys.stderr, "zip %s [%s]" % (arc_path, filepath)
174 elif p.zipfile is not None:
175 p.zipfile.write(filepath, arc_path)
176
177
178def main():
179 p = parseArgs(sys.argv)
180 z = None
181 try:
182 if not p.DRY:
183 p.zipfile = z = zipfile.ZipFile(p.DST, "w", zipfile.ZIP_DEFLATED)
184 z.write(p.PROPS, TOP_FOLDER + "/source.properties")
185 for d in p.DIR.split():
186 if d:
187 parseSrcDir(p, os.path.join(p.SRC, d))
188 finally:
189 if z is not None:
190 z.close()
191 print "%s: %d java files copied" % (p.DST, p.CNT_USED)
192 if p.CNT_NOPKG:
193 print "%s: %d java files ignored" % (p.DST, p.CNT_NOPKG)
194 if p.DRY:
195 print >>sys.stderr, "This was in *DRY* mode. No copies done."
196
197
198if __name__ == "__main__":
199 main()
200
201# For emacs:
202# -*- tab-width: 4; -*-