| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * Copyright (C) 2016 The Android Open Source Project | 
 | 3 |  * | 
 | 4 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 | 5 |  * you may not use this file except in compliance with the License. | 
 | 6 |  * You may obtain a copy of the License at | 
 | 7 |  * | 
 | 8 |  *      http://www.apache.org/licenses/LICENSE-2.0 | 
 | 9 |  * | 
 | 10 |  * Unless required by applicable law or agreed to in writing, software | 
 | 11 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 | 12 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | 13 |  * See the License for the specific language governing permissions and | 
 | 14 |  * limitations under the License. | 
 | 15 |  */ | 
 | 16 |  | 
 | 17 | #include "aapt.h" | 
 | 18 | #include "adb.h" | 
 | 19 | #include "make.h" | 
 | 20 | #include "print.h" | 
 | 21 | #include "util.h" | 
 | 22 |  | 
 | 23 | #include <sstream> | 
 | 24 | #include <string> | 
 | 25 | #include <vector> | 
 | 26 |  | 
 | 27 | #include <stdio.h> | 
 | 28 | #include <stdlib.h> | 
 | 29 | #include <string.h> | 
 | 30 | #include <unistd.h> | 
 | 31 |  | 
 | 32 | #include <google/protobuf/stubs/common.h> | 
 | 33 |  | 
 | 34 | using namespace std; | 
 | 35 |  | 
| Joe Onorato | 6c97f49 | 2019-02-27 20:42:37 -0500 | [diff] [blame] | 36 | #define NATIVE_TESTS "NATIVE_TESTS" | 
 | 37 |  | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 38 | /** | 
 | 39 |  * An entry from the command line for something that will be built, installed, | 
 | 40 |  * and/or tested. | 
 | 41 |  */ | 
 | 42 | struct Target { | 
 | 43 |     bool build; | 
 | 44 |     bool install; | 
 | 45 |     bool test; | 
 | 46 |     string pattern; | 
 | 47 |     string name; | 
 | 48 |     vector<string> actions; | 
 | 49 |     Module module; | 
 | 50 |  | 
 | 51 |     int testActionCount; | 
 | 52 |  | 
 | 53 |     int testPassCount; | 
 | 54 |     int testFailCount; | 
| Makoto Onuki | 6fb2c97 | 2017-08-02 14:40:12 -0700 | [diff] [blame] | 55 |     int unknownFailureCount; // unknown failure == "Process crashed", etc. | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 56 |     bool actionsWithNoTests; | 
 | 57 |  | 
 | 58 |     Target(bool b, bool i, bool t, const string& p); | 
 | 59 | }; | 
 | 60 |  | 
 | 61 | Target::Target(bool b, bool i, bool t, const string& p) | 
 | 62 |     :build(b), | 
 | 63 |      install(i), | 
 | 64 |      test(t), | 
 | 65 |      pattern(p), | 
 | 66 |      testActionCount(0), | 
 | 67 |      testPassCount(0), | 
 | 68 |      testFailCount(0), | 
| Makoto Onuki | 6fb2c97 | 2017-08-02 14:40:12 -0700 | [diff] [blame] | 69 |      unknownFailureCount(0), | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 70 |      actionsWithNoTests(false) | 
 | 71 | { | 
 | 72 | } | 
 | 73 |  | 
 | 74 | /** | 
 | 75 |  * Command line options. | 
 | 76 |  */ | 
 | 77 | struct Options { | 
 | 78 |     // For help | 
 | 79 |     bool runHelp; | 
 | 80 |  | 
 | 81 |     // For tab completion | 
 | 82 |     bool runTab; | 
 | 83 |     string tabPattern; | 
 | 84 |  | 
 | 85 |     // For build/install/test | 
| Joe Onorato | 6592c3c | 2016-11-12 16:34:25 -0800 | [diff] [blame] | 86 |     bool noRestart; | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 87 |     bool reboot; | 
 | 88 |     vector<Target*> targets; | 
 | 89 |  | 
 | 90 |     Options(); | 
 | 91 |     ~Options(); | 
 | 92 | }; | 
 | 93 |  | 
 | 94 | Options::Options() | 
 | 95 |     :runHelp(false), | 
 | 96 |      runTab(false), | 
| Joe Onorato | 6592c3c | 2016-11-12 16:34:25 -0800 | [diff] [blame] | 97 |      noRestart(false), | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 98 |      reboot(false), | 
 | 99 |      targets() | 
 | 100 | { | 
 | 101 | } | 
 | 102 |  | 
 | 103 | Options::~Options() | 
 | 104 | { | 
 | 105 | } | 
 | 106 |  | 
 | 107 | struct InstallApk | 
 | 108 | { | 
 | 109 |     TrackedFile file; | 
 | 110 |     bool alwaysInstall; | 
 | 111 |     bool installed; | 
 | 112 |  | 
 | 113 |     InstallApk(); | 
 | 114 |     InstallApk(const InstallApk& that); | 
 | 115 |     InstallApk(const string& filename, bool always); | 
 | 116 |     ~InstallApk() {}; | 
 | 117 | }; | 
 | 118 |  | 
 | 119 | InstallApk::InstallApk() | 
 | 120 | { | 
 | 121 | } | 
 | 122 |  | 
 | 123 | InstallApk::InstallApk(const InstallApk& that) | 
 | 124 |     :file(that.file), | 
 | 125 |      alwaysInstall(that.alwaysInstall), | 
 | 126 |      installed(that.installed) | 
 | 127 | { | 
 | 128 | } | 
 | 129 |  | 
 | 130 | InstallApk::InstallApk(const string& filename, bool always) | 
 | 131 |     :file(filename), | 
 | 132 |      alwaysInstall(always), | 
 | 133 |      installed(false) | 
 | 134 | { | 
 | 135 | } | 
 | 136 |  | 
| Joe Onorato | 6c97f49 | 2019-02-27 20:42:37 -0500 | [diff] [blame] | 137 | struct PushedFile | 
 | 138 | { | 
 | 139 |     TrackedFile file; | 
 | 140 |     string dest; | 
 | 141 |  | 
 | 142 |     PushedFile(); | 
 | 143 |     PushedFile(const PushedFile& that); | 
 | 144 |     PushedFile(const string& filename, const string& dest); | 
 | 145 |     ~PushedFile() {}; | 
 | 146 | }; | 
 | 147 |  | 
 | 148 | PushedFile::PushedFile() | 
 | 149 | { | 
 | 150 | } | 
 | 151 |  | 
 | 152 | PushedFile::PushedFile(const PushedFile& that) | 
 | 153 |     :file(that.file), | 
 | 154 |      dest(that.dest) | 
 | 155 | { | 
 | 156 | } | 
 | 157 |  | 
 | 158 | PushedFile::PushedFile(const string& f, const string& d) | 
 | 159 |     :file(f), | 
 | 160 |      dest(d) | 
 | 161 | { | 
 | 162 | } | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 163 |  | 
 | 164 | /** | 
 | 165 |  * Record for an test that is going to be launched. | 
 | 166 |  */ | 
 | 167 | struct TestAction { | 
 | 168 |     TestAction(); | 
 | 169 |  | 
 | 170 |     // The package name from the apk | 
 | 171 |     string packageName; | 
 | 172 |  | 
 | 173 |     // The test runner class | 
 | 174 |     string runner; | 
 | 175 |  | 
 | 176 |     // The test class, or none if all tests should be run | 
 | 177 |     string className; | 
 | 178 |  | 
 | 179 |     // The original target that requested this action | 
 | 180 |     Target* target; | 
 | 181 |  | 
 | 182 |     // The number of tests that passed | 
 | 183 |     int passCount; | 
 | 184 |  | 
 | 185 |     // The number of tests that failed | 
 | 186 |     int failCount; | 
 | 187 | }; | 
 | 188 |  | 
 | 189 | TestAction::TestAction() | 
 | 190 |     :passCount(0), | 
 | 191 |      failCount(0) | 
 | 192 | { | 
 | 193 | } | 
 | 194 |  | 
 | 195 | /** | 
 | 196 |  * Record for an activity that is going to be launched. | 
 | 197 |  */ | 
 | 198 | struct ActivityAction { | 
 | 199 |     // The package name from the apk | 
 | 200 |     string packageName; | 
 | 201 |  | 
 | 202 |     // The test class, or none if all tests should be run | 
 | 203 |     string className; | 
 | 204 | }; | 
 | 205 |  | 
 | 206 | /** | 
 | 207 |  * Callback class for the am instrument command. | 
 | 208 |  */ | 
 | 209 | class TestResults: public InstrumentationCallbacks | 
 | 210 | { | 
 | 211 | public: | 
 | 212 |     virtual void OnTestStatus(TestStatus& status); | 
 | 213 |     virtual void OnSessionStatus(SessionStatus& status); | 
 | 214 |  | 
 | 215 |     /** | 
 | 216 |      * Set the TestAction that the tests are for. | 
 | 217 |      * It will be updated with statistics as the tests run. | 
 | 218 |      */ | 
 | 219 |     void SetCurrentAction(TestAction* action); | 
 | 220 |  | 
| Makoto Onuki | 6fb2c97 | 2017-08-02 14:40:12 -0700 | [diff] [blame] | 221 |     bool IsSuccess(); | 
 | 222 |  | 
 | 223 |     string GetErrorMessage(); | 
 | 224 |  | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 225 | private: | 
 | 226 |     TestAction* m_currentAction; | 
| Makoto Onuki | 6fb2c97 | 2017-08-02 14:40:12 -0700 | [diff] [blame] | 227 |     SessionStatus m_sessionStatus; | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 228 | }; | 
 | 229 |  | 
 | 230 | void | 
 | 231 | TestResults::OnTestStatus(TestStatus& status) | 
 | 232 | { | 
 | 233 |     bool found; | 
 | 234 | //    printf("OnTestStatus\n"); | 
 | 235 | //    status.PrintDebugString(); | 
 | 236 |     int32_t resultCode = status.has_results() ? status.result_code() : 0; | 
 | 237 |  | 
 | 238 |     if (!status.has_results()) { | 
 | 239 |         return; | 
 | 240 |     } | 
 | 241 |     const ResultsBundle &results = status.results(); | 
 | 242 |  | 
 | 243 |     int32_t currentTestNum = get_bundle_int(results, &found, "current", NULL); | 
 | 244 |     if (!found) { | 
 | 245 |         currentTestNum = -1; | 
 | 246 |     } | 
 | 247 |  | 
 | 248 |     int32_t testCount = get_bundle_int(results, &found, "numtests", NULL); | 
 | 249 |     if (!found) { | 
 | 250 |         testCount = -1; | 
 | 251 |     } | 
 | 252 |  | 
 | 253 |     string className = get_bundle_string(results, &found, "class", NULL); | 
 | 254 |     if (!found) { | 
 | 255 |         return; | 
 | 256 |     } | 
 | 257 |  | 
 | 258 |     string testName = get_bundle_string(results, &found, "test", NULL); | 
 | 259 |     if (!found) { | 
 | 260 |         return; | 
 | 261 |     } | 
 | 262 |  | 
 | 263 |     if (resultCode == 0) { | 
 | 264 |         // test passed | 
 | 265 |         m_currentAction->passCount++; | 
 | 266 |         m_currentAction->target->testPassCount++; | 
 | 267 |     } else if (resultCode == 1) { | 
 | 268 |         // test starting | 
 | 269 |         ostringstream line; | 
 | 270 |         line << "Running"; | 
 | 271 |         if (currentTestNum > 0) { | 
 | 272 |             line << ": " << currentTestNum; | 
 | 273 |             if (testCount > 0) { | 
 | 274 |                 line << " of " << testCount; | 
 | 275 |             } | 
 | 276 |         } | 
 | 277 |         line << ": " << m_currentAction->target->name << ':' << className << "\\#" << testName; | 
 | 278 |         print_one_line("%s", line.str().c_str()); | 
| Makoto Onuki | 6fb2c97 | 2017-08-02 14:40:12 -0700 | [diff] [blame] | 279 |     } else if ((resultCode == -1) || (resultCode == -2)) { | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 280 |         // test failed | 
| Makoto Onuki | 6fb2c97 | 2017-08-02 14:40:12 -0700 | [diff] [blame] | 281 |         // Note -2 means an assertion failure, and -1 means other exceptions.  We just treat them | 
 | 282 |         // all as "failures". | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 283 |         m_currentAction->failCount++; | 
 | 284 |         m_currentAction->target->testFailCount++; | 
 | 285 |         printf("%s\n%sFailed: %s:%s\\#%s%s\n", g_escapeClearLine, g_escapeRedBold, | 
 | 286 |                 m_currentAction->target->name.c_str(), className.c_str(), | 
 | 287 |                 testName.c_str(), g_escapeEndColor); | 
 | 288 |  | 
 | 289 |         string stack = get_bundle_string(results, &found, "stack", NULL); | 
 | 290 |         if (found) { | 
 | 291 |             printf("%s\n", stack.c_str()); | 
 | 292 |         } | 
 | 293 |     } | 
 | 294 | } | 
 | 295 |  | 
 | 296 | void | 
| Makoto Onuki | 6fb2c97 | 2017-08-02 14:40:12 -0700 | [diff] [blame] | 297 | TestResults::OnSessionStatus(SessionStatus& status) | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 298 | { | 
 | 299 |     //status.PrintDebugString(); | 
| Makoto Onuki | 6fb2c97 | 2017-08-02 14:40:12 -0700 | [diff] [blame] | 300 |     m_sessionStatus = status; | 
 | 301 |     if (m_currentAction && !IsSuccess()) { | 
 | 302 |         m_currentAction->target->unknownFailureCount++; | 
 | 303 |     } | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 304 | } | 
 | 305 |  | 
 | 306 | void | 
 | 307 | TestResults::SetCurrentAction(TestAction* action) | 
 | 308 | { | 
 | 309 |     m_currentAction = action; | 
 | 310 | } | 
 | 311 |  | 
| Makoto Onuki | 6fb2c97 | 2017-08-02 14:40:12 -0700 | [diff] [blame] | 312 | bool | 
 | 313 | TestResults::IsSuccess() | 
 | 314 | { | 
 | 315 |     return m_sessionStatus.result_code() == -1; // Activity.RESULT_OK. | 
 | 316 | } | 
 | 317 |  | 
 | 318 | string | 
 | 319 | TestResults::GetErrorMessage() | 
 | 320 | { | 
 | 321 |     bool found; | 
 | 322 |     string shortMsg = get_bundle_string(m_sessionStatus.results(), &found, "shortMsg", NULL); | 
 | 323 |     if (!found) { | 
 | 324 |         return IsSuccess() ? "" : "Unknown failure"; | 
 | 325 |     } | 
 | 326 |     return shortMsg; | 
 | 327 | } | 
 | 328 |  | 
 | 329 |  | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 330 | /** | 
 | 331 |  * Prints the usage statement / help text. | 
 | 332 |  */ | 
 | 333 | static void | 
 | 334 | print_usage(FILE* out) { | 
 | 335 |     fprintf(out, "usage: bit OPTIONS PATTERN\n"); | 
 | 336 |     fprintf(out, "\n"); | 
 | 337 |     fprintf(out, "  Build, sync and test android code.\n"); | 
 | 338 |     fprintf(out, "\n"); | 
 | 339 |     fprintf(out, "  The -b -i and -t options allow you to specify which phases\n"); | 
 | 340 |     fprintf(out, "  you want to run. If none of those options are given, then\n"); | 
 | 341 |     fprintf(out, "  all phases are run. If any of these options are provided\n"); | 
 | 342 |     fprintf(out, "  then only the listed phases are run.\n"); | 
 | 343 |     fprintf(out, "\n"); | 
 | 344 |     fprintf(out, "  OPTIONS\n"); | 
 | 345 |     fprintf(out, "  -b     Run a build\n"); | 
 | 346 |     fprintf(out, "  -i     Install the targets\n"); | 
 | 347 |     fprintf(out, "  -t     Run the tests\n"); | 
 | 348 |     fprintf(out, "\n"); | 
| Joe Onorato | 6592c3c | 2016-11-12 16:34:25 -0800 | [diff] [blame] | 349 |     fprintf(out, "  -n     Don't reboot or restart\n"); | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 350 |     fprintf(out, "  -r     If the runtime needs to be restarted, do a full reboot\n"); | 
 | 351 |     fprintf(out, "         instead\n"); | 
 | 352 |     fprintf(out, "\n"); | 
 | 353 |     fprintf(out, "  PATTERN\n"); | 
 | 354 |     fprintf(out, "  One or more targets to build, install and test. The target\n"); | 
 | 355 |     fprintf(out, "  names are the names that appear in the LOCAL_MODULE or\n"); | 
 | 356 |     fprintf(out, "  LOCAL_PACKAGE_NAME variables in Android.mk or Android.bp files.\n"); | 
 | 357 |     fprintf(out, "\n"); | 
 | 358 |     fprintf(out, "  Building and installing\n"); | 
 | 359 |     fprintf(out, "  -----------------------\n"); | 
 | 360 |     fprintf(out, "  The modules specified will be built and then installed. If the\n"); | 
 | 361 |     fprintf(out, "  files are on the system partition, they will be synced and the\n"); | 
 | 362 |     fprintf(out, "  attached device rebooted. If they are APKs that aren't on the\n"); | 
 | 363 |     fprintf(out, "  system partition they are installed with adb install.\n"); | 
 | 364 |     fprintf(out, "\n"); | 
 | 365 |     fprintf(out, "  For example:\n"); | 
 | 366 |     fprintf(out, "    bit framework\n"); | 
 | 367 |     fprintf(out, "      Builds framework.jar, syncs the system partition and reboots.\n"); | 
 | 368 |     fprintf(out, "\n"); | 
 | 369 |     fprintf(out, "    bit SystemUI\n"); | 
 | 370 |     fprintf(out, "      Builds SystemUI.apk, syncs the system partition and reboots.\n"); | 
 | 371 |     fprintf(out, "\n"); | 
 | 372 |     fprintf(out, "    bit CtsProtoTestCases\n"); | 
 | 373 |     fprintf(out, "      Builds this CTS apk, adb installs it, but does not run any\n"); | 
 | 374 |     fprintf(out, "      tests.\n"); | 
 | 375 |     fprintf(out, "\n"); | 
 | 376 |     fprintf(out, "  Running Unit Tests\n"); | 
 | 377 |     fprintf(out, "  ------------------\n"); | 
 | 378 |     fprintf(out, "  To run a unit test, list the test class names and optionally the\n"); | 
 | 379 |     fprintf(out, "  test method after the module.\n"); | 
 | 380 |     fprintf(out, "\n"); | 
 | 381 |     fprintf(out, "  For example:\n"); | 
 | 382 |     fprintf(out, "    bit CtsProtoTestCases:*\n"); | 
 | 383 |     fprintf(out, "      Builds this CTS apk, adb installs it, and runs all the tests\n"); | 
 | 384 |     fprintf(out, "      contained in that apk.\n"); | 
 | 385 |     fprintf(out, "\n"); | 
 | 386 |     fprintf(out, "    bit framework CtsProtoTestCases:*\n"); | 
 | 387 |     fprintf(out, "      Builds the framework and the apk, syncs and reboots, then\n"); | 
 | 388 |     fprintf(out, "      adb installs CtsProtoTestCases.apk, and runs all tests \n"); | 
 | 389 |     fprintf(out, "      contained in that apk.\n"); | 
 | 390 |     fprintf(out, "\n"); | 
 | 391 |     fprintf(out, "    bit CtsProtoTestCases:.ProtoOutputStreamBoolTest\n"); | 
 | 392 |     fprintf(out, "    bit CtsProtoTestCases:android.util.proto.cts.ProtoOutputStreamBoolTest\n"); | 
 | 393 |     fprintf(out, "      Builds and installs CtsProtoTestCases.apk, and runs all the\n"); | 
 | 394 |     fprintf(out, "      tests in the ProtoOutputStreamBoolTest class.\n"); | 
 | 395 |     fprintf(out, "\n"); | 
 | 396 |     fprintf(out, "    bit CtsProtoTestCases:.ProtoOutputStreamBoolTest\\#testWrite\n"); | 
 | 397 |     fprintf(out, "      Builds and installs CtsProtoTestCases.apk, and runs the testWrite\n"); | 
 | 398 |     fprintf(out, "      test method on that class.\n"); | 
 | 399 |     fprintf(out, "\n"); | 
 | 400 |     fprintf(out, "    bit CtsProtoTestCases:.ProtoOutputStreamBoolTest\\#testWrite,.ProtoOutputStreamBoolTest\\#testRepeated\n"); | 
 | 401 |     fprintf(out, "      Builds and installs CtsProtoTestCases.apk, and runs the testWrite\n"); | 
 | 402 |     fprintf(out, "      and testRepeated test methods on that class.\n"); | 
 | 403 |     fprintf(out, "\n"); | 
| Makoto Onuki | 164e796 | 2017-07-06 16:20:11 -0700 | [diff] [blame] | 404 |     fprintf(out, "    bit CtsProtoTestCases:android.util.proto.cts.\n"); | 
 | 405 |     fprintf(out, "      Builds and installs CtsProtoTestCases.apk, and runs the tests in the java package\n"); | 
 | 406 |     fprintf(out, "      \"android.util.proto.cts\".\n"); | 
 | 407 |     fprintf(out, "\n"); | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 408 |     fprintf(out, "  Launching an Activity\n"); | 
 | 409 |     fprintf(out, "  ---------------------\n"); | 
 | 410 |     fprintf(out, "  To launch an activity, specify the activity class name after\n"); | 
 | 411 |     fprintf(out, "  the module name.\n"); | 
 | 412 |     fprintf(out, "\n"); | 
 | 413 |     fprintf(out, "  For example:\n"); | 
 | 414 |     fprintf(out, "    bit StatusBarTest:NotificationBuilderTest\n"); | 
 | 415 |     fprintf(out, "    bit StatusBarTest:.NotificationBuilderTest\n"); | 
 | 416 |     fprintf(out, "    bit StatusBarTest:com.android.statusbartest.NotificationBuilderTest\n"); | 
 | 417 |     fprintf(out, "      Builds and installs StatusBarTest.apk, launches the\n"); | 
 | 418 |     fprintf(out, "      com.android.statusbartest/.NotificationBuilderTest activity.\n"); | 
 | 419 |     fprintf(out, "\n"); | 
 | 420 |     fprintf(out, "\n"); | 
 | 421 |     fprintf(out, "usage: bit --tab ...\n"); | 
 | 422 |     fprintf(out, "\n"); | 
 | 423 |     fprintf(out, "  Lists the targets in a format for tab completion. To get tab\n"); | 
 | 424 |     fprintf(out, "  completion, add this to your bash environment:\n"); | 
 | 425 |     fprintf(out, "\n"); | 
 | 426 |     fprintf(out, "     complete -C \"bit --tab\" bit\n"); | 
 | 427 |     fprintf(out, "\n"); | 
 | 428 |     fprintf(out, "  Sourcing android's build/envsetup.sh will do this for you\n"); | 
 | 429 |     fprintf(out, "  automatically.\n"); | 
 | 430 |     fprintf(out, "\n"); | 
 | 431 |     fprintf(out, "\n"); | 
 | 432 |     fprintf(out, "usage: bit --help\n"); | 
 | 433 |     fprintf(out, "usage: bit -h\n"); | 
 | 434 |     fprintf(out, "\n"); | 
 | 435 |     fprintf(out, "  Print this help message\n"); | 
 | 436 |     fprintf(out, "\n"); | 
 | 437 | } | 
 | 438 |  | 
 | 439 |  | 
 | 440 | /** | 
 | 441 |  * Sets the appropriate flag* variables. If there is a problem with the | 
 | 442 |  * commandline arguments, prints the help message and exits with an error. | 
 | 443 |  */ | 
 | 444 | static void | 
 | 445 | parse_args(Options* options, int argc, const char** argv) | 
 | 446 | { | 
 | 447 |     // Help | 
 | 448 |     if (argc == 2 && (strcmp(argv[1],  "-h") == 0 || strcmp(argv[1], "--help") == 0)) { | 
 | 449 |         options->runHelp = true; | 
 | 450 |         return; | 
 | 451 |     } | 
 | 452 |  | 
 | 453 |     // Tab | 
 | 454 |     if (argc >= 4 && strcmp(argv[1], "--tab") == 0) { | 
 | 455 |         options->runTab = true; | 
 | 456 |         options->tabPattern = argv[3]; | 
 | 457 |         return; | 
 | 458 |     } | 
 | 459 |  | 
 | 460 |     // Normal usage | 
 | 461 |     bool anyPhases = false; | 
 | 462 |     bool gotPattern = false; | 
 | 463 |     bool flagBuild = false; | 
 | 464 |     bool flagInstall = false; | 
 | 465 |     bool flagTest = false; | 
 | 466 |     for (int i=1; i < argc; i++) { | 
 | 467 |         string arg(argv[i]); | 
 | 468 |         if (arg[0] == '-') { | 
 | 469 |             for (size_t j=1; j<arg.size(); j++) { | 
 | 470 |                 switch (arg[j]) { | 
 | 471 |                     case '-': | 
 | 472 |                         break; | 
 | 473 |                     case 'b': | 
 | 474 |                         if (gotPattern) { | 
 | 475 |                             gotPattern = false; | 
 | 476 |                             flagInstall = false; | 
 | 477 |                             flagTest = false; | 
 | 478 |                         } | 
 | 479 |                         flagBuild = true; | 
 | 480 |                         anyPhases = true; | 
 | 481 |                         break; | 
 | 482 |                     case 'i': | 
 | 483 |                         if (gotPattern) { | 
 | 484 |                             gotPattern = false; | 
 | 485 |                             flagBuild = false; | 
 | 486 |                             flagTest = false; | 
 | 487 |                         } | 
 | 488 |                         flagInstall = true; | 
 | 489 |                         anyPhases = true; | 
 | 490 |                         break; | 
 | 491 |                     case 't': | 
 | 492 |                         if (gotPattern) { | 
 | 493 |                             gotPattern = false; | 
 | 494 |                             flagBuild = false; | 
 | 495 |                             flagInstall = false; | 
 | 496 |                         } | 
 | 497 |                         flagTest = true; | 
 | 498 |                         anyPhases = true; | 
 | 499 |                         break; | 
| Joe Onorato | 6592c3c | 2016-11-12 16:34:25 -0800 | [diff] [blame] | 500 |                     case 'n': | 
 | 501 |                         options->noRestart = true; | 
 | 502 |                         break; | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 503 |                     case 'r': | 
 | 504 |                         options->reboot = true; | 
 | 505 |                         break; | 
 | 506 |                     default: | 
 | 507 |                         fprintf(stderr, "Unrecognized option '%c'\n", arg[j]); | 
 | 508 |                         print_usage(stderr); | 
 | 509 |                         exit(1); | 
 | 510 |                         break; | 
 | 511 |                 } | 
 | 512 |             } | 
 | 513 |         } else { | 
 | 514 |             Target* target = new Target(flagBuild || !anyPhases, flagInstall || !anyPhases, | 
 | 515 |                     flagTest || !anyPhases, arg); | 
 | 516 |             size_t colonPos = arg.find(':'); | 
 | 517 |             if (colonPos == 0) { | 
 | 518 |                 fprintf(stderr, "Test / activity supplied without a module to build: %s\n", | 
 | 519 |                         arg.c_str()); | 
 | 520 |                 print_usage(stderr); | 
| Yunlian Jiang | 2cfa849 | 2016-12-06 16:08:39 -0800 | [diff] [blame] | 521 |                 delete target; | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 522 |                 exit(1); | 
 | 523 |             } else if (colonPos == string::npos) { | 
 | 524 |                 target->name = arg; | 
 | 525 |             } else { | 
 | 526 |                 target->name.assign(arg, 0, colonPos); | 
 | 527 |                 size_t beginPos = colonPos+1; | 
 | 528 |                 size_t commaPos; | 
 | 529 |                 while (true) { | 
 | 530 |                     commaPos = arg.find(',', beginPos); | 
 | 531 |                     if (commaPos == string::npos) { | 
 | 532 |                         if (beginPos != arg.size()) { | 
 | 533 |                             target->actions.push_back(string(arg, beginPos, commaPos)); | 
 | 534 |                         } | 
 | 535 |                         break; | 
 | 536 |                     } else { | 
 | 537 |                         if (commaPos != beginPos) { | 
 | 538 |                             target->actions.push_back(string(arg, beginPos, commaPos-beginPos)); | 
 | 539 |                         } | 
 | 540 |                         beginPos = commaPos+1; | 
 | 541 |                     } | 
 | 542 |                 } | 
 | 543 |             } | 
 | 544 |             options->targets.push_back(target); | 
 | 545 |             gotPattern = true; | 
 | 546 |         } | 
 | 547 |     } | 
 | 548 |     // If no pattern was supplied, give an error | 
 | 549 |     if (options->targets.size() == 0) { | 
 | 550 |         fprintf(stderr, "No PATTERN supplied.\n\n"); | 
 | 551 |         print_usage(stderr); | 
 | 552 |         exit(1); | 
 | 553 |     } | 
 | 554 | } | 
 | 555 |  | 
 | 556 | /** | 
 | 557 |  * Get an environment variable. | 
 | 558 |  * Exits with an error if it is unset or the empty string. | 
 | 559 |  */ | 
 | 560 | static string | 
 | 561 | get_required_env(const char* name, bool quiet) | 
 | 562 | { | 
 | 563 |     const char* value = getenv(name); | 
 | 564 |     if (value == NULL || value[0] == '\0') { | 
 | 565 |         if (!quiet) { | 
 | 566 |             fprintf(stderr, "%s not set. Did you source build/envsetup.sh," | 
 | 567 |                     " run lunch and do a build?\n", name); | 
 | 568 |         } | 
 | 569 |         exit(1); | 
 | 570 |     } | 
 | 571 |     return string(value); | 
 | 572 | } | 
 | 573 |  | 
 | 574 | /** | 
 | 575 |  * Get the out directory. | 
 | 576 |  * | 
 | 577 |  * This duplicates the logic in build/make/core/envsetup.mk (which hasn't changed since 2011) | 
 | 578 |  * so that we don't have to wait for get_build_var make invocation. | 
 | 579 |  */ | 
 | 580 | string | 
 | 581 | get_out_dir() | 
 | 582 | { | 
 | 583 |     const char* out_dir = getenv("OUT_DIR"); | 
 | 584 |     if (out_dir == NULL || out_dir[0] == '\0') { | 
 | 585 |         const char* common_base = getenv("OUT_DIR_COMMON_BASE"); | 
 | 586 |         if (common_base == NULL || common_base[0] == '\0') { | 
 | 587 |             // We don't prefix with buildTop because we cd there and it | 
 | 588 |             // makes all the filenames long when being pretty printed. | 
 | 589 |             return "out"; | 
 | 590 |         } else { | 
| Joe Onorato | 8a5bb63 | 2016-10-21 14:31:42 -0700 | [diff] [blame] | 591 |             char pwd[PATH_MAX]; | 
 | 592 |             if (getcwd(pwd, PATH_MAX) == NULL) { | 
 | 593 |                 fprintf(stderr, "Your pwd is too long.\n"); | 
 | 594 |                 exit(1); | 
 | 595 |             } | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 596 |             const char* slash = strrchr(pwd, '/'); | 
 | 597 |             if (slash == NULL) { | 
 | 598 |                 slash = ""; | 
 | 599 |             } | 
 | 600 |             string result(common_base); | 
 | 601 |             result += slash; | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 602 |             return result; | 
 | 603 |         } | 
 | 604 |     } | 
 | 605 |     return string(out_dir); | 
 | 606 | } | 
 | 607 |  | 
 | 608 | /** | 
 | 609 |  * Check that a system property on the device matches the expected value. | 
 | 610 |  * Exits with an error if they don't. | 
 | 611 |  */ | 
 | 612 | static void | 
 | 613 | check_device_property(const string& property, const string& expected) | 
 | 614 | { | 
 | 615 |     int err; | 
 | 616 |     string deviceValue = get_system_property(property, &err); | 
 | 617 |     check_error(err); | 
 | 618 |     if (deviceValue != expected) { | 
 | 619 |         print_error("There is a mismatch between the build you just did and the device you"); | 
 | 620 |         print_error("are trying to sync it to in the %s system property", property.c_str()); | 
 | 621 |         print_error("   build:  %s", expected.c_str()); | 
 | 622 |         print_error("   device: %s", deviceValue.c_str()); | 
 | 623 |         exit(1); | 
 | 624 |     } | 
 | 625 | } | 
 | 626 |  | 
| Chih-Hung Hsieh | c7edf07 | 2017-10-03 09:57:55 -0700 | [diff] [blame] | 627 | static void | 
 | 628 | chdir_or_exit(const char *path) { | 
 | 629 |     // TODO: print_command("cd", path); | 
 | 630 |     if (0 != chdir(path)) { | 
 | 631 |         print_error("Error: Could not chdir: %s", path); | 
 | 632 |         exit(1); | 
 | 633 |     } | 
 | 634 | } | 
 | 635 |  | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 636 | /** | 
 | 637 |  * Run the build, install, and test actions. | 
 | 638 |  */ | 
| Makoto Onuki | 6fb2c97 | 2017-08-02 14:40:12 -0700 | [diff] [blame] | 639 | bool | 
| Joe Onorato | 6592c3c | 2016-11-12 16:34:25 -0800 | [diff] [blame] | 640 | run_phases(vector<Target*> targets, const Options& options) | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 641 | { | 
 | 642 |     int err = 0; | 
 | 643 |  | 
 | 644 |     // | 
 | 645 |     // Initialization | 
 | 646 |     // | 
 | 647 |  | 
 | 648 |     print_status("Initializing"); | 
 | 649 |  | 
 | 650 |     const string buildTop = get_required_env("ANDROID_BUILD_TOP", false); | 
 | 651 |     const string buildProduct = get_required_env("TARGET_PRODUCT", false); | 
 | 652 |     const string buildVariant = get_required_env("TARGET_BUILD_VARIANT", false); | 
 | 653 |     const string buildType = get_required_env("TARGET_BUILD_TYPE", false); | 
| Joe Onorato | ce0bd06 | 2019-01-14 15:30:05 -0800 | [diff] [blame] | 654 |     const string buildOut = get_out_dir(); | 
| Chih-Hung Hsieh | c7edf07 | 2017-10-03 09:57:55 -0700 | [diff] [blame] | 655 |     chdir_or_exit(buildTop.c_str()); | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 656 |  | 
| Joe Onorato | ce0bd06 | 2019-01-14 15:30:05 -0800 | [diff] [blame] | 657 |     BuildVars buildVars(buildOut, buildProduct, buildVariant, buildType); | 
 | 658 |  | 
 | 659 |     const string buildDevice = buildVars.GetBuildVar("TARGET_DEVICE", false); | 
 | 660 |     const string buildId = buildVars.GetBuildVar("BUILD_ID", false); | 
| Dan Willemsen | a40118d | 2017-10-17 17:46:41 -0700 | [diff] [blame] | 661 |  | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 662 |     // Get the modules for the targets | 
 | 663 |     map<string,Module> modules; | 
 | 664 |     read_modules(buildOut, buildDevice, &modules, false); | 
 | 665 |     for (size_t i=0; i<targets.size(); i++) { | 
 | 666 |         Target* target = targets[i]; | 
 | 667 |         map<string,Module>::iterator mod = modules.find(target->name); | 
 | 668 |         if (mod != modules.end()) { | 
 | 669 |             target->module = mod->second; | 
 | 670 |         } else { | 
 | 671 |             print_error("Error: Could not find module: %s", target->name.c_str()); | 
 | 672 |             err = 1; | 
 | 673 |         } | 
 | 674 |     } | 
 | 675 |     if (err != 0) { | 
 | 676 |         exit(1); | 
 | 677 |     } | 
 | 678 |  | 
 | 679 |     // Choose the goals | 
 | 680 |     vector<string> goals; | 
 | 681 |     for (size_t i=0; i<targets.size(); i++) { | 
 | 682 |         Target* target = targets[i]; | 
 | 683 |         if (target->build) { | 
 | 684 |             goals.push_back(target->name); | 
 | 685 |         } | 
 | 686 |     } | 
 | 687 |  | 
 | 688 |     // Figure out whether we need to sync the system and which apks to install | 
| Joe Onorato | 6c97f49 | 2019-02-27 20:42:37 -0500 | [diff] [blame] | 689 |     string deviceTargetPath = buildOut + "/target/product/" + buildDevice; | 
 | 690 |     string systemPath = deviceTargetPath + "/system/"; | 
 | 691 |     string dataPath = deviceTargetPath + "/data/"; | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 692 |     bool syncSystem = false; | 
 | 693 |     bool alwaysSyncSystem = false; | 
| Joe Onorato | 70edfa8 | 2018-12-14 15:46:27 -0800 | [diff] [blame] | 694 |     vector<string> systemFiles; | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 695 |     vector<InstallApk> installApks; | 
| Joe Onorato | 6c97f49 | 2019-02-27 20:42:37 -0500 | [diff] [blame] | 696 |     vector<PushedFile> pushedFiles; | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 697 |     for (size_t i=0; i<targets.size(); i++) { | 
 | 698 |         Target* target = targets[i]; | 
 | 699 |         if (target->install) { | 
 | 700 |             for (size_t j=0; j<target->module.installed.size(); j++) { | 
 | 701 |                 const string& file = target->module.installed[j]; | 
 | 702 |                 // System partition | 
 | 703 |                 if (starts_with(file, systemPath)) { | 
 | 704 |                     syncSystem = true; | 
| Joe Onorato | 70edfa8 | 2018-12-14 15:46:27 -0800 | [diff] [blame] | 705 |                     systemFiles.push_back(file); | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 706 |                     if (!target->build) { | 
 | 707 |                         // If a system partition target didn't get built then | 
 | 708 |                         // it won't change we will always need to do adb sync | 
 | 709 |                         alwaysSyncSystem = true; | 
 | 710 |                     } | 
 | 711 |                     continue; | 
 | 712 |                 } | 
 | 713 |                 // Apk in the data partition | 
 | 714 |                 if (starts_with(file, dataPath) && ends_with(file, ".apk")) { | 
 | 715 |                     // Always install it if we didn't build it because otherwise | 
 | 716 |                     // it will never have changed. | 
 | 717 |                     installApks.push_back(InstallApk(file, !target->build)); | 
 | 718 |                     continue; | 
 | 719 |                 } | 
| Joe Onorato | 6c97f49 | 2019-02-27 20:42:37 -0500 | [diff] [blame] | 720 |                 // If it's a native test module, push it. | 
 | 721 |                 if (target->module.HasClass(NATIVE_TESTS) && starts_with(file, dataPath)) { | 
 | 722 |                     string installedPath(file.c_str() + deviceTargetPath.length()); | 
 | 723 |                     pushedFiles.push_back(PushedFile(file, installedPath)); | 
 | 724 |                 } | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 725 |             } | 
 | 726 |         } | 
 | 727 |     } | 
 | 728 |     map<string,FileInfo> systemFilesBefore; | 
 | 729 |     if (syncSystem && !alwaysSyncSystem) { | 
 | 730 |         get_directory_contents(systemPath, &systemFilesBefore); | 
 | 731 |     } | 
 | 732 |  | 
| Joe Onorato | 70edfa8 | 2018-12-14 15:46:27 -0800 | [diff] [blame] | 733 |     if (systemFiles.size() > 0){ | 
 | 734 |         print_info("System files:"); | 
 | 735 |         for (size_t i=0; i<systemFiles.size(); i++) { | 
 | 736 |             printf("  %s\n", systemFiles[i].c_str()); | 
 | 737 |         } | 
 | 738 |     } | 
| Joe Onorato | 6c97f49 | 2019-02-27 20:42:37 -0500 | [diff] [blame] | 739 |     if (pushedFiles.size() > 0){ | 
 | 740 |         print_info("Files to push:"); | 
 | 741 |         for (size_t i=0; i<pushedFiles.size(); i++) { | 
 | 742 |             printf("  %s\n", pushedFiles[i].file.filename.c_str()); | 
 | 743 |             printf("    --> %s\n", pushedFiles[i].dest.c_str()); | 
 | 744 |         } | 
 | 745 |     } | 
| Joe Onorato | 70edfa8 | 2018-12-14 15:46:27 -0800 | [diff] [blame] | 746 |     if (installApks.size() > 0){ | 
 | 747 |         print_info("APKs to install:"); | 
 | 748 |         for (size_t i=0; i<installApks.size(); i++) { | 
 | 749 |             printf("  %s\n", installApks[i].file.filename.c_str()); | 
 | 750 |         } | 
 | 751 |     } | 
 | 752 |  | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 753 |     // | 
 | 754 |     // Build | 
 | 755 |     // | 
 | 756 |  | 
 | 757 |     // Run the build | 
 | 758 |     if (goals.size() > 0) { | 
 | 759 |         print_status("Building"); | 
 | 760 |         err = build_goals(goals); | 
 | 761 |         check_error(err); | 
 | 762 |     } | 
 | 763 |  | 
 | 764 |     // | 
 | 765 |     // Install | 
 | 766 |     // | 
 | 767 |  | 
 | 768 |     // Sync the system partition and reboot | 
 | 769 |     bool skipSync = false; | 
 | 770 |     if (syncSystem) { | 
 | 771 |         print_status("Syncing /system"); | 
 | 772 |  | 
 | 773 |         if (!alwaysSyncSystem) { | 
 | 774 |             // If nothing changed and we weren't forced to sync, skip the reboot for speed. | 
 | 775 |             map<string,FileInfo> systemFilesAfter; | 
 | 776 |             get_directory_contents(systemPath, &systemFilesAfter); | 
 | 777 |             skipSync = !directory_contents_differ(systemFilesBefore, systemFilesAfter); | 
 | 778 |         } | 
 | 779 |         if (skipSync) { | 
 | 780 |             printf("Skipping sync because no files changed.\n"); | 
 | 781 |         } else { | 
 | 782 |             // Do some sanity checks | 
 | 783 |             check_device_property("ro.build.product", buildProduct); | 
 | 784 |             check_device_property("ro.build.type", buildVariant); | 
 | 785 |             check_device_property("ro.build.id", buildId); | 
 | 786 |  | 
 | 787 |             // Stop & Sync | 
| Joe Onorato | 6592c3c | 2016-11-12 16:34:25 -0800 | [diff] [blame] | 788 |             if (!options.noRestart) { | 
 | 789 |                 err = run_adb("shell", "stop", NULL); | 
 | 790 |                 check_error(err); | 
 | 791 |             } | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 792 |             err = run_adb("remount", NULL); | 
 | 793 |             check_error(err); | 
 | 794 |             err = run_adb("sync", "system", NULL); | 
 | 795 |             check_error(err); | 
 | 796 |  | 
| Joe Onorato | 6592c3c | 2016-11-12 16:34:25 -0800 | [diff] [blame] | 797 |             if (!options.noRestart) { | 
 | 798 |                 if (options.reboot) { | 
 | 799 |                     print_status("Rebooting"); | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 800 |  | 
| Joe Onorato | 6592c3c | 2016-11-12 16:34:25 -0800 | [diff] [blame] | 801 |                     err = run_adb("reboot", NULL); | 
 | 802 |                     check_error(err); | 
 | 803 |                     err = run_adb("wait-for-device", NULL); | 
 | 804 |                     check_error(err); | 
 | 805 |                 } else { | 
 | 806 |                     print_status("Restarting the runtime"); | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 807 |  | 
| Joe Onorato | 6592c3c | 2016-11-12 16:34:25 -0800 | [diff] [blame] | 808 |                     err = run_adb("shell", "setprop", "sys.boot_completed", "0", NULL); | 
 | 809 |                     check_error(err); | 
 | 810 |                     err = run_adb("shell", "start", NULL); | 
 | 811 |                     check_error(err); | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 812 |                 } | 
| Joe Onorato | 6592c3c | 2016-11-12 16:34:25 -0800 | [diff] [blame] | 813 |  | 
 | 814 |                 while (true) { | 
 | 815 |                     string completed = get_system_property("sys.boot_completed", &err); | 
 | 816 |                     check_error(err); | 
 | 817 |                     if (completed == "1") { | 
 | 818 |                         break; | 
 | 819 |                     } | 
 | 820 |                     sleep(2); | 
 | 821 |                 } | 
 | 822 |                 sleep(1); | 
 | 823 |                 err = run_adb("shell", "wm", "dismiss-keyguard", NULL); | 
 | 824 |                 check_error(err); | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 825 |             } | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 826 |         } | 
 | 827 |     } | 
 | 828 |  | 
| Joe Onorato | 6c97f49 | 2019-02-27 20:42:37 -0500 | [diff] [blame] | 829 |     // Push files | 
 | 830 |     if (pushedFiles.size() > 0) { | 
 | 831 |         print_status("Pushing files"); | 
 | 832 |         for (size_t i=0; i<pushedFiles.size(); i++) { | 
 | 833 |             const PushedFile& pushed = pushedFiles[i]; | 
 | 834 |             string dir = dirname(pushed.dest); | 
 | 835 |             if (dir.length() == 0 || dir == "/") { | 
 | 836 |                 // This isn't really a file inside the data directory. Just skip it. | 
 | 837 |                 continue; | 
 | 838 |             } | 
 | 839 |             // TODO: if (!apk.file.fileInfo.exists || apk.file.HasChanged()) | 
 | 840 |             err = run_adb("shell", "mkdir", "-p", dir.c_str(), NULL); | 
 | 841 |             check_error(err); | 
 | 842 |             err = run_adb("push", pushed.file.filename.c_str(), pushed.dest.c_str()); | 
 | 843 |             check_error(err); | 
 | 844 |             // pushed.installed = true; | 
 | 845 |         } | 
 | 846 |     } | 
 | 847 |  | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 848 |     // Install APKs | 
 | 849 |     if (installApks.size() > 0) { | 
 | 850 |         print_status("Installing APKs"); | 
 | 851 |         for (size_t i=0; i<installApks.size(); i++) { | 
 | 852 |             InstallApk& apk = installApks[i]; | 
 | 853 |             if (!apk.file.fileInfo.exists || apk.file.HasChanged()) { | 
 | 854 |                 // It didn't exist before or it changed, so int needs install | 
| Jeff Sharkey | 5f9dc42 | 2017-07-06 12:13:42 -0600 | [diff] [blame] | 855 |                 err = run_adb("install", "-r", "-g", apk.file.filename.c_str(), NULL); | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 856 |                 check_error(err); | 
 | 857 |                 apk.installed = true; | 
 | 858 |             } else { | 
 | 859 |                 printf("APK didn't change. Skipping install of %s\n", apk.file.filename.c_str()); | 
 | 860 |             } | 
 | 861 |         } | 
 | 862 |     } | 
 | 863 |  | 
 | 864 |     // | 
 | 865 |     // Actions | 
 | 866 |     // | 
 | 867 |  | 
| Joe Onorato | 6c97f49 | 2019-02-27 20:42:37 -0500 | [diff] [blame] | 868 |     // Whether there have been any tests run, so we can print a summary. | 
 | 869 |     bool testsRun = false; | 
 | 870 |  | 
 | 871 |     // Run the native tests. | 
 | 872 |     // TODO: We don't have a good way of running these and capturing the output of | 
 | 873 |     // them live.  It'll take some work.  On the other hand, if they're gtest tests, | 
 | 874 |     // the output of gtest is not completely insane like the text output of the | 
 | 875 |     // instrumentation tests.  So for now, we'll just live with that. | 
 | 876 |     for (size_t i=0; i<targets.size(); i++) { | 
 | 877 |         Target* target = targets[i]; | 
 | 878 |         if (target->test && target->module.HasClass(NATIVE_TESTS)) { | 
 | 879 |             // We don't have a clear signal from the build system which of the installed | 
 | 880 |             // files is actually the test, so we guess by looking for one with the same | 
 | 881 |             // leaf name as the module that is executable. | 
 | 882 |             for (size_t j=0; j<target->module.installed.size(); j++) { | 
 | 883 |                 string filename = target->module.installed[j]; | 
 | 884 |                 if (!starts_with(filename, dataPath)) { | 
 | 885 |                     // Native tests go into the data directory. | 
 | 886 |                     continue; | 
 | 887 |                 } | 
 | 888 |                 if (leafname(filename) != target->module.name) { | 
 | 889 |                     // This isn't the test executable. | 
 | 890 |                     continue; | 
 | 891 |                 } | 
 | 892 |                 if (!is_executable(filename)) { | 
 | 893 |                     continue; | 
 | 894 |                 } | 
 | 895 |                 string installedPath(filename.c_str() + deviceTargetPath.length()); | 
 | 896 |                 printf("the magic one is: %s\n", filename.c_str()); | 
 | 897 |                 printf("  and it's installed at: %s\n", installedPath.c_str()); | 
 | 898 |  | 
 | 899 |                 // Convert bit-style actions to gtest test filter arguments | 
 | 900 |                 if (target->actions.size() > 0) { | 
 | 901 |                     testsRun = true; | 
 | 902 |                     target->testActionCount++; | 
 | 903 |                     bool runAll = false; | 
 | 904 |                     string filterArg("--gtest_filter="); | 
 | 905 |                     for (size_t k=0; k<target->actions.size(); k++) { | 
 | 906 |                         string actionString = target->actions[k]; | 
 | 907 |                         if (actionString == "*") { | 
 | 908 |                             runAll = true; | 
 | 909 |                         } else { | 
 | 910 |                             filterArg += actionString; | 
 | 911 |                             if (k != target->actions.size()-1) { | 
 | 912 |                                 // We would otherwise have to worry about this condition | 
 | 913 |                                 // being true, and appending an extra ':', but we know that | 
 | 914 |                                 // if the extra action is "*", then we'll just run all and | 
 | 915 |                                 // won't use filterArg anyway, so just keep this condition | 
 | 916 |                                 // simple. | 
 | 917 |                                 filterArg += ':'; | 
 | 918 |                             } | 
 | 919 |                         } | 
 | 920 |                     } | 
 | 921 |                     if (runAll) { | 
 | 922 |                         err = run_adb("shell", installedPath.c_str(), NULL); | 
 | 923 |                     } else { | 
 | 924 |                         err = run_adb("shell", installedPath.c_str(), filterArg.c_str(), NULL); | 
 | 925 |                     } | 
 | 926 |                     if (err == 0) { | 
 | 927 |                         target->testPassCount++; | 
 | 928 |                     } else { | 
 | 929 |                         target->testFailCount++; | 
 | 930 |                     } | 
 | 931 |                 } | 
 | 932 |             } | 
 | 933 |         } | 
 | 934 |     } | 
 | 935 |  | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 936 |     // Inspect the apks, and figure out what is an activity and what needs a test runner | 
 | 937 |     bool printedInspecting = false; | 
 | 938 |     vector<TestAction> testActions; | 
 | 939 |     vector<ActivityAction> activityActions; | 
 | 940 |     for (size_t i=0; i<targets.size(); i++) { | 
 | 941 |         Target* target = targets[i]; | 
 | 942 |         if (target->test) { | 
 | 943 |             for (size_t j=0; j<target->module.installed.size(); j++) { | 
 | 944 |                 string filename = target->module.installed[j]; | 
 | 945 |  | 
| Joe Onorato | 70edfa8 | 2018-12-14 15:46:27 -0800 | [diff] [blame] | 946 |                 // Apk in the data partition | 
 | 947 |                 if (!starts_with(filename, dataPath) || !ends_with(filename, ".apk")) { | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 948 |                     continue; | 
 | 949 |                 } | 
 | 950 |  | 
 | 951 |                 if (!printedInspecting) { | 
 | 952 |                     printedInspecting = true; | 
 | 953 |                     print_status("Inspecting APKs"); | 
 | 954 |                 } | 
 | 955 |  | 
 | 956 |                 Apk apk; | 
 | 957 |                 err = inspect_apk(&apk, filename); | 
 | 958 |                 check_error(err); | 
 | 959 |  | 
 | 960 |                 for (size_t k=0; k<target->actions.size(); k++) { | 
 | 961 |                     string actionString = target->actions[k]; | 
 | 962 |                     if (actionString == "*") { | 
 | 963 |                         if (apk.runner.length() == 0) { | 
 | 964 |                             print_error("Error: Test requested for apk that doesn't" | 
 | 965 |                                     " have an <instrumentation> tag: %s\n", | 
 | 966 |                                     target->module.name.c_str()); | 
 | 967 |                             exit(1); | 
 | 968 |                         } | 
 | 969 |                         TestAction action; | 
 | 970 |                         action.packageName = apk.package; | 
 | 971 |                         action.runner = apk.runner; | 
 | 972 |                         action.target = target; | 
 | 973 |                         testActions.push_back(action); | 
 | 974 |                         target->testActionCount++; | 
 | 975 |                     } else if (apk.HasActivity(actionString)) { | 
 | 976 |                         ActivityAction action; | 
 | 977 |                         action.packageName = apk.package; | 
 | 978 |                         action.className = full_class_name(apk.package, actionString); | 
 | 979 |                         activityActions.push_back(action); | 
 | 980 |                     } else { | 
 | 981 |                         if (apk.runner.length() == 0) { | 
 | 982 |                             print_error("Error: Test requested for apk that doesn't" | 
 | 983 |                                     " have an <instrumentation> tag: %s\n", | 
 | 984 |                                     target->module.name.c_str()); | 
 | 985 |                             exit(1); | 
 | 986 |                         } | 
 | 987 |                         TestAction action; | 
 | 988 |                         action.packageName = apk.package; | 
 | 989 |                         action.runner = apk.runner; | 
 | 990 |                         action.className = full_class_name(apk.package, actionString); | 
 | 991 |                         action.target = target; | 
 | 992 |                         testActions.push_back(action); | 
 | 993 |                         target->testActionCount++; | 
 | 994 |                     } | 
 | 995 |                 } | 
 | 996 |             } | 
 | 997 |         } | 
 | 998 |     } | 
 | 999 |  | 
 | 1000 |     // Run the instrumentation tests | 
 | 1001 |     TestResults testResults; | 
 | 1002 |     if (testActions.size() > 0) { | 
 | 1003 |         print_status("Running tests"); | 
| Joe Onorato | 6c97f49 | 2019-02-27 20:42:37 -0500 | [diff] [blame] | 1004 |         testsRun = true; | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 1005 |         for (size_t i=0; i<testActions.size(); i++) { | 
 | 1006 |             TestAction& action = testActions[i]; | 
 | 1007 |             testResults.SetCurrentAction(&action); | 
 | 1008 |             err = run_instrumentation_test(action.packageName, action.runner, action.className, | 
 | 1009 |                     &testResults); | 
 | 1010 |             check_error(err); | 
 | 1011 |             if (action.passCount == 0 && action.failCount == 0) { | 
 | 1012 |                 action.target->actionsWithNoTests = true; | 
 | 1013 |             } | 
 | 1014 |             int total = action.passCount + action.failCount; | 
 | 1015 |             printf("%sRan %d test%s for %s. ", g_escapeClearLine, | 
 | 1016 |                     total, total > 1 ? "s" : "", action.target->name.c_str()); | 
 | 1017 |             if (action.passCount == 0 && action.failCount == 0) { | 
 | 1018 |                 printf("%s%d passed, %d failed%s\n", g_escapeYellowBold, action.passCount, | 
 | 1019 |                         action.failCount, g_escapeEndColor); | 
 | 1020 |             } else if (action.failCount >  0) { | 
 | 1021 |                 printf("%d passed, %s%d failed%s\n", action.passCount, g_escapeRedBold, | 
 | 1022 |                         action.failCount, g_escapeEndColor); | 
 | 1023 |             } else { | 
 | 1024 |                 printf("%s%d passed%s, %d failed\n", g_escapeGreenBold, action.passCount, | 
 | 1025 |                         g_escapeEndColor, action.failCount); | 
 | 1026 |             } | 
| Makoto Onuki | 6fb2c97 | 2017-08-02 14:40:12 -0700 | [diff] [blame] | 1027 |             if (!testResults.IsSuccess()) { | 
 | 1028 |                 printf("\n%sTest didn't finish successfully: %s%s\n", g_escapeRedBold, | 
 | 1029 |                         testResults.GetErrorMessage().c_str(), g_escapeEndColor); | 
 | 1030 |             } | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 1031 |         } | 
 | 1032 |     } | 
 | 1033 |  | 
 | 1034 |     // Launch the activity | 
 | 1035 |     if (activityActions.size() > 0) { | 
 | 1036 |         print_status("Starting activity"); | 
 | 1037 |  | 
 | 1038 |         if (activityActions.size() > 1) { | 
 | 1039 |             print_warning("Multiple activities specified.  Will only start the first one:"); | 
 | 1040 |             for (size_t i=0; i<activityActions.size(); i++) { | 
 | 1041 |                 ActivityAction& action = activityActions[i]; | 
 | 1042 |                 print_warning("   %s", | 
 | 1043 |                         pretty_component_name(action.packageName, action.className).c_str()); | 
 | 1044 |             } | 
 | 1045 |         } | 
 | 1046 |  | 
 | 1047 |         const ActivityAction& action = activityActions[0]; | 
 | 1048 |         string componentName = action.packageName + "/" + action.className; | 
 | 1049 |         err = run_adb("shell", "am", "start", componentName.c_str(), NULL); | 
 | 1050 |         check_error(err); | 
 | 1051 |     } | 
 | 1052 |  | 
 | 1053 |     // | 
 | 1054 |     // Print summary | 
 | 1055 |     // | 
 | 1056 |  | 
 | 1057 |     printf("\n%s--------------------------------------------%s\n", g_escapeBold, g_escapeEndColor); | 
 | 1058 |  | 
 | 1059 |     // Build | 
 | 1060 |     if (goals.size() > 0) { | 
 | 1061 |         printf("%sBuilt:%s\n", g_escapeBold, g_escapeEndColor); | 
 | 1062 |         for (size_t i=0; i<goals.size(); i++) { | 
 | 1063 |             printf("   %s\n", goals[i].c_str()); | 
 | 1064 |         } | 
 | 1065 |     } | 
 | 1066 |  | 
 | 1067 |     // Install | 
 | 1068 |     if (syncSystem) { | 
 | 1069 |         if (skipSync) { | 
 | 1070 |             printf("%sSkipped syncing /system partition%s\n", g_escapeBold, g_escapeEndColor); | 
 | 1071 |         } else { | 
 | 1072 |             printf("%sSynced /system partition%s\n", g_escapeBold, g_escapeEndColor); | 
 | 1073 |         } | 
 | 1074 |     } | 
 | 1075 |     if (installApks.size() > 0) { | 
 | 1076 |         bool printedTitle = false; | 
 | 1077 |         for (size_t i=0; i<installApks.size(); i++) { | 
 | 1078 |             const InstallApk& apk = installApks[i]; | 
 | 1079 |             if (apk.installed) { | 
 | 1080 |                 if (!printedTitle) { | 
 | 1081 |                     printf("%sInstalled:%s\n", g_escapeBold, g_escapeEndColor); | 
 | 1082 |                     printedTitle = true; | 
 | 1083 |                 } | 
 | 1084 |                 printf("   %s\n", apk.file.filename.c_str()); | 
 | 1085 |             } | 
 | 1086 |         } | 
 | 1087 |         printedTitle = false; | 
 | 1088 |         for (size_t i=0; i<installApks.size(); i++) { | 
 | 1089 |             const InstallApk& apk = installApks[i]; | 
 | 1090 |             if (!apk.installed) { | 
 | 1091 |                 if (!printedTitle) { | 
 | 1092 |                     printf("%sSkipped install:%s\n", g_escapeBold, g_escapeEndColor); | 
 | 1093 |                     printedTitle = true; | 
 | 1094 |                 } | 
 | 1095 |                 printf("   %s\n", apk.file.filename.c_str()); | 
 | 1096 |             } | 
 | 1097 |         } | 
 | 1098 |     } | 
 | 1099 |  | 
 | 1100 |     // Tests | 
| Makoto Onuki | 6fb2c97 | 2017-08-02 14:40:12 -0700 | [diff] [blame] | 1101 |     bool hasErrors = false; | 
| Joe Onorato | 6c97f49 | 2019-02-27 20:42:37 -0500 | [diff] [blame] | 1102 |     if (testsRun) { | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 1103 |         printf("%sRan tests:%s\n", g_escapeBold, g_escapeEndColor); | 
 | 1104 |         size_t maxNameLength = 0; | 
 | 1105 |         for (size_t i=0; i<targets.size(); i++) { | 
 | 1106 |             Target* target = targets[i]; | 
 | 1107 |             if (target->test) { | 
 | 1108 |                 size_t len = target->name.length(); | 
 | 1109 |                 if (len > maxNameLength) { | 
 | 1110 |                     maxNameLength = len; | 
 | 1111 |                 } | 
 | 1112 |             } | 
 | 1113 |         } | 
 | 1114 |         string padding(maxNameLength, ' '); | 
 | 1115 |         for (size_t i=0; i<targets.size(); i++) { | 
 | 1116 |             Target* target = targets[i]; | 
 | 1117 |             if (target->testActionCount > 0) { | 
 | 1118 |                 printf("   %s%s", target->name.c_str(), padding.c_str() + target->name.length()); | 
| Makoto Onuki | 6fb2c97 | 2017-08-02 14:40:12 -0700 | [diff] [blame] | 1119 |                 if (target->unknownFailureCount > 0) { | 
 | 1120 |                     printf("     %sUnknown failure, see above message.%s\n", | 
 | 1121 |                             g_escapeRedBold, g_escapeEndColor); | 
 | 1122 |                     hasErrors = true; | 
 | 1123 |                 } else if (target->actionsWithNoTests) { | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 1124 |                     printf("     %s%d passed, %d failed%s\n", g_escapeYellowBold, | 
 | 1125 |                             target->testPassCount, target->testFailCount, g_escapeEndColor); | 
| Makoto Onuki | 6fb2c97 | 2017-08-02 14:40:12 -0700 | [diff] [blame] | 1126 |                     hasErrors = true; | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 1127 |                 } else if (target->testFailCount > 0) { | 
 | 1128 |                     printf("     %d passed, %s%d failed%s\n", target->testPassCount, | 
 | 1129 |                             g_escapeRedBold, target->testFailCount, g_escapeEndColor); | 
| Makoto Onuki | 6fb2c97 | 2017-08-02 14:40:12 -0700 | [diff] [blame] | 1130 |                     hasErrors = true; | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 1131 |                 } else { | 
 | 1132 |                     printf("     %s%d passed%s, %d failed\n", g_escapeGreenBold, | 
 | 1133 |                             target->testPassCount, g_escapeEndColor, target->testFailCount); | 
 | 1134 |                 } | 
 | 1135 |             } | 
 | 1136 |         } | 
 | 1137 |     } | 
 | 1138 |     if (activityActions.size() > 1) { | 
 | 1139 |         printf("%sStarted Activity:%s\n", g_escapeBold, g_escapeEndColor); | 
 | 1140 |         const ActivityAction& action = activityActions[0]; | 
 | 1141 |         printf("   %s\n", pretty_component_name(action.packageName, action.className).c_str()); | 
 | 1142 |     } | 
 | 1143 |  | 
 | 1144 |     printf("%s--------------------------------------------%s\n", g_escapeBold, g_escapeEndColor); | 
| Makoto Onuki | 6fb2c97 | 2017-08-02 14:40:12 -0700 | [diff] [blame] | 1145 |     return !hasErrors; | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 1146 | } | 
 | 1147 |  | 
 | 1148 | /** | 
 | 1149 |  * Implement tab completion of the target names from the all modules file. | 
 | 1150 |  */ | 
 | 1151 | void | 
 | 1152 | run_tab_completion(const string& word) | 
 | 1153 | { | 
| Joe Onorato | ce0bd06 | 2019-01-14 15:30:05 -0800 | [diff] [blame] | 1154 |     const string buildTop = get_required_env("ANDROID_BUILD_TOP", false); | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 1155 |     const string buildProduct = get_required_env("TARGET_PRODUCT", false); | 
| Joe Onorato | ce0bd06 | 2019-01-14 15:30:05 -0800 | [diff] [blame] | 1156 |     const string buildVariant = get_required_env("TARGET_BUILD_VARIANT", false); | 
 | 1157 |     const string buildType = get_required_env("TARGET_BUILD_TYPE", false); | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 1158 |     const string buildOut = get_out_dir(); | 
| Chih-Hung Hsieh | c7edf07 | 2017-10-03 09:57:55 -0700 | [diff] [blame] | 1159 |     chdir_or_exit(buildTop.c_str()); | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 1160 |  | 
| Joe Onorato | ce0bd06 | 2019-01-14 15:30:05 -0800 | [diff] [blame] | 1161 |     BuildVars buildVars(buildOut, buildProduct, buildVariant, buildType); | 
 | 1162 |  | 
 | 1163 |     string buildDevice = buildVars.GetBuildVar("TARGET_DEVICE", false); | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 1164 |  | 
 | 1165 |     map<string,Module> modules; | 
 | 1166 |     read_modules(buildOut, buildDevice, &modules, true); | 
 | 1167 |  | 
 | 1168 |     for (map<string,Module>::const_iterator it = modules.begin(); it != modules.end(); it++) { | 
 | 1169 |         if (starts_with(it->first, word)) { | 
 | 1170 |             printf("%s\n", it->first.c_str()); | 
 | 1171 |         } | 
 | 1172 |     } | 
 | 1173 | } | 
 | 1174 |  | 
 | 1175 | /** | 
 | 1176 |  * Main entry point. | 
 | 1177 |  */ | 
 | 1178 | int | 
 | 1179 | main(int argc, const char** argv) | 
 | 1180 | { | 
 | 1181 |     GOOGLE_PROTOBUF_VERIFY_VERSION; | 
 | 1182 |     init_print(); | 
 | 1183 |  | 
 | 1184 |     Options options; | 
 | 1185 |     parse_args(&options, argc, argv); | 
 | 1186 |  | 
 | 1187 |     if (options.runHelp) { | 
 | 1188 |         // Help | 
 | 1189 |         print_usage(stdout); | 
 | 1190 |         exit(0); | 
 | 1191 |     } else if (options.runTab) { | 
 | 1192 |         run_tab_completion(options.tabPattern); | 
 | 1193 |         exit(0); | 
 | 1194 |     } else { | 
 | 1195 |         // Normal run | 
| Makoto Onuki | 6fb2c97 | 2017-08-02 14:40:12 -0700 | [diff] [blame] | 1196 |         exit(run_phases(options.targets, options) ? 0 : 1); | 
| Joe Onorato | 0578cbc | 2016-10-19 17:03:06 -0700 | [diff] [blame] | 1197 |     } | 
 | 1198 |  | 
 | 1199 |     return 0; | 
 | 1200 | } | 
 | 1201 |  |