Jack Wang | 2abd80a | 2009-06-29 18:47:03 -0700 | [diff] [blame] | 1 | #!/usr/bin/python2.4 |
| 2 | # |
| 3 | # |
| 4 | # Copyright 2009, The Android Open Source Project |
| 5 | # |
| 6 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 7 | # you may not use this file except in compliance with the License. |
| 8 | # You may obtain a copy of the License at |
| 9 | # |
| 10 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 11 | # |
| 12 | # Unless required by applicable law or agreed to in writing, software |
| 13 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 15 | # See the License for the specific language governing permissions and |
| 16 | # limitations under the License. |
| 17 | |
| 18 | """Utility to create Android project files for tests.""" |
| 19 | |
| 20 | # python imports |
| 21 | import datetime |
| 22 | import optparse |
| 23 | import os |
| 24 | import string |
| 25 | import sys |
| 26 | |
| 27 | # local imports |
| 28 | import android_mk |
| 29 | import android_manifest |
| 30 | |
| 31 | |
| 32 | class TestsConsts(object): |
| 33 | """Constants for test Android.mk and AndroidManifest.xml creation.""" |
| 34 | |
| 35 | MK_BUILD_INCLUDE = "call all-makefiles-under,$(LOCAL_PATH)" |
| 36 | MK_BUILD_STRING = "\ninclude $(%s)\n" % MK_BUILD_INCLUDE |
| 37 | TEST_MANIFEST_TEMPLATE = """<?xml version="1.0" encoding="utf-8"?> |
| 38 | <!-- Copyright (C) $YEAR The Android Open Source Project |
| 39 | |
| 40 | Licensed under the Apache License, Version 2.0 (the "License"); |
| 41 | you may not use this file except in compliance with the License. |
| 42 | You may obtain a copy of the License at |
| 43 | |
| 44 | http://www.apache.org/licenses/LICENSE-2.0 |
| 45 | |
| 46 | Unless required by applicable law or agreed to in writing, software |
| 47 | distributed under the License is distributed on an "AS IS" BASIS, |
| 48 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 49 | See the License for the specific language governing permissions and |
| 50 | limitations under the License. |
| 51 | --> |
| 52 | |
| 53 | <manifest xmlns:android="http://schemas.android.com/apk/res/android" |
| 54 | package="$PACKAGE_NAME.tests"> |
| 55 | |
| 56 | <application> |
| 57 | <uses-library android:name="android.test.runner" /> |
| 58 | </application> |
| 59 | |
| 60 | <instrumentation android:name="android.test.InstrumentationTestRunner" |
| 61 | android:targetPackage="$PACKAGE_NAME" |
| 62 | android:label="Tests for $MODULE_NAME"> |
| 63 | </instrumentation> |
| 64 | </manifest> |
| 65 | """ |
| 66 | TEST_MK_TEMPLATE = """LOCAL_PATH := $$(call my-dir) |
| 67 | include $$(CLEAR_VARS) |
| 68 | |
| 69 | LOCAL_MODULE_TAGS := tests |
| 70 | |
| 71 | LOCAL_JAVA_LIBRARIES := android.test.runner |
| 72 | |
| 73 | LOCAL_SRC_FILES := $$(call all-java-files-under, src) |
| 74 | |
| 75 | LOCAL_PACKAGE_NAME := ${MODULE_NAME}Tests${CERTIFICATE} |
| 76 | |
| 77 | LOCAL_INSTRUMENTATION_FOR := ${MODULE_NAME} |
| 78 | |
| 79 | LOCAL_SDK_VERSION := current |
| 80 | |
| 81 | include $$(BUILD_PACKAGE) |
| 82 | """ |
| 83 | TESTS_FOLDER = "tests" |
| 84 | |
| 85 | |
| 86 | def _GenerateTestManifest(manifest, module_name, mapping=None): |
| 87 | """Create and populate tests/AndroidManifest.xml with variable values from |
| 88 | Android.mk and AndroidManifest.xml. |
| 89 | |
| 90 | Does nothing if tests/AndroidManifest.xml already exists. |
| 91 | |
| 92 | Args: |
| 93 | manifest: AndroidManifest object for application manifest |
| 94 | module_name: module name used for labelling |
| 95 | mapping: optional user defined mapping of variable values, replaces values |
| 96 | extracted from AndroidManifest.xml |
| 97 | Raises: |
| 98 | IOError: tests/AndroidManifest.xml cannot be opened for writing |
| 99 | """ |
| 100 | # skip if file already exists |
| 101 | tests_path = "%s/%s" % (manifest.app_path, TestsConsts.TESTS_FOLDER) |
| 102 | tests_manifest_path = "%s/%s" % (tests_path, manifest.FILENAME) |
| 103 | if os.path.exists(tests_manifest_path): |
| 104 | _PrintMessage("%s already exists, not overwritten" % tests_manifest_path) |
| 105 | return |
| 106 | |
| 107 | if not mapping: |
| 108 | package_name = manifest.GetPackageName() |
| 109 | mapping = {"PACKAGE_NAME":package_name, "MODULE_NAME":module_name, |
| 110 | "YEAR":datetime.date.today().year} |
| 111 | output = string.Template(TestsConsts.TEST_MANIFEST_TEMPLATE).substitute(mapping) |
| 112 | |
| 113 | # create tests folder if not existent |
| 114 | if not os.path.exists(tests_path): |
| 115 | os.mkdir(tests_path) |
| 116 | |
| 117 | # write tests/AndroidManifest.xml |
| 118 | tests_manifest = open(tests_manifest_path, mode="w") |
| 119 | tests_manifest.write(output) |
| 120 | tests_manifest.close() |
| 121 | _PrintMessage("Created %s" % tests_manifest_path) |
| 122 | |
| 123 | |
| 124 | def _GenerateTestMK(mk, mapping=None): |
| 125 | """Create and populate tests/Android.mk with variable values from Android.mk. |
| 126 | |
| 127 | Does nothing if tests/Android.mk already exists. |
| 128 | |
| 129 | Args: |
| 130 | mk: AndroidMK object for application makefile |
| 131 | mapping: optional user defined mapping of variable values, replaces |
| 132 | values stored in mk |
| 133 | Raises: |
| 134 | IOError: tests/Android.mk cannot be opened for writing |
| 135 | """ |
| 136 | # skip if file already exists |
| 137 | tests_path = "%s/%s" % (mk.app_path, TestsConsts.TESTS_FOLDER) |
| 138 | tests_mk_path = "%s/%s" % (tests_path, mk.FILENAME) |
| 139 | if os.path.exists(tests_mk_path): |
| 140 | _PrintMessage("%s already exists, not overwritten" % tests_mk_path) |
| 141 | return |
| 142 | |
| 143 | # append test build if not existent in makefile |
| 144 | if not mk.HasInclude(TestsConsts.MK_BUILD_INCLUDE): |
| 145 | mk_path = "%s/%s" % (mk.app_path, mk.FILENAME) |
| 146 | mk_file = open(mk_path, mode="a") |
| 147 | mk_file.write(TestsConsts.MK_BUILD_STRING) |
| 148 | mk_file.close() |
| 149 | |
| 150 | # construct tests/Android.mk |
| 151 | # include certificate definition if existent in makefile |
| 152 | certificate = mk.GetVariable(mk.CERTIFICATE) |
| 153 | if certificate: |
| 154 | cert_definition = ("\n%s := %s" % (mk.CERTIFICATE, certificate)) |
| 155 | else: |
| 156 | cert_definition = "" |
| 157 | if not mapping: |
| 158 | module_name = mk.GetVariable(mk.PACKAGE_NAME) |
| 159 | mapping = {"MODULE_NAME":module_name, "CERTIFICATE":cert_definition} |
| 160 | output = string.Template(TestsConsts.TEST_MK_TEMPLATE).substitute(mapping) |
| 161 | |
| 162 | # create tests folder if not existent |
| 163 | if not os.path.exists(tests_path): |
| 164 | os.mkdir(tests_path) |
| 165 | |
| 166 | # write tests/Android.mk to disk |
| 167 | tests_mk = open(tests_mk_path, mode="w") |
| 168 | tests_mk.write(output) |
| 169 | tests_mk.close() |
| 170 | _PrintMessage("Created %s" % tests_mk_path) |
| 171 | |
| 172 | |
| 173 | def _ParseArgs(argv): |
| 174 | """Parse the command line arguments. |
| 175 | |
| 176 | Args: |
| 177 | argv: the list of command line arguments |
| 178 | Returns: |
| 179 | a tuple of options and individual command line arguments. |
| 180 | """ |
| 181 | parser = optparse.OptionParser(usage="%s <app_path>" % sys.argv[0]) |
| 182 | options, args = parser.parse_args(argv) |
| 183 | if len(args) < 1: |
| 184 | _PrintError("Error: Incorrect syntax") |
| 185 | parser.print_usage() |
| 186 | sys.exit() |
| 187 | return (options, args) |
| 188 | |
| 189 | |
| 190 | def _PrintMessage(msg): |
| 191 | print >> sys.stdout, msg |
| 192 | |
| 193 | |
| 194 | def _PrintError(msg): |
| 195 | print >> sys.stderr, msg |
| 196 | |
| 197 | |
| 198 | def _ValidateInputFiles(mk, manifest): |
| 199 | """Verify that required variables are defined in input files. |
| 200 | |
| 201 | Args: |
| 202 | mk: AndroidMK object for application makefile |
| 203 | manifest: AndroidManifest object for application manifest |
| 204 | Raises: |
| 205 | RuntimeError: mk does not define LOCAL_PACKAGE_NAME or |
| 206 | manifest does not define package variable |
| 207 | """ |
| 208 | module_name = mk.GetVariable(mk.PACKAGE_NAME) |
| 209 | if not module_name: |
| 210 | raise RuntimeError("Variable %s missing from %s" % |
| 211 | (mk.PACKAGE_NAME, mk.FILENAME)) |
| 212 | |
| 213 | package_name = manifest.GetPackageName() |
| 214 | if not package_name: |
| 215 | raise RuntimeError("Variable package missing from %s" % manifest.FILENAME) |
| 216 | |
| 217 | |
| 218 | def main(argv): |
| 219 | options, args = _ParseArgs(argv) |
| 220 | app_path = args[0]; |
| 221 | |
| 222 | if not os.path.exists(app_path): |
| 223 | _PrintError("Error: Application path %s not found" % app_path) |
| 224 | sys.exit() |
| 225 | |
| 226 | try: |
| 227 | mk = android_mk.AndroidMK(app_path=app_path) |
| 228 | manifest = android_manifest.AndroidManifest(app_path=app_path) |
| 229 | _ValidateInputFiles(mk, manifest) |
| 230 | |
| 231 | module_name = mk.GetVariable(mk.PACKAGE_NAME) |
| 232 | _GenerateTestMK(mk) |
| 233 | _GenerateTestManifest(manifest, module_name) |
| 234 | except Exception, e: |
| 235 | _PrintError("Error: %s" % e) |
| 236 | _PrintError("Error encountered, script aborted") |
| 237 | sys.exit() |
| 238 | |
| 239 | src_path = app_path + "/tests/src" |
| 240 | if not os.path.exists(src_path): |
| 241 | os.mkdir(src_path) |
| 242 | |
| 243 | |
| 244 | if __name__ == "__main__": |
| 245 | main(sys.argv[1:]) |