1 /* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php 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 package com.android.ide.eclipse.adt.internal.build.builders; 18 19 import com.android.SdkConstants; 20 import com.android.annotations.NonNull; 21 import com.android.annotations.Nullable; 22 import com.android.ide.common.xml.ManifestData; 23 import com.android.ide.eclipse.adt.AdtConstants; 24 import com.android.ide.eclipse.adt.AdtPlugin; 25 import com.android.ide.eclipse.adt.internal.build.AaptParser; 26 import com.android.ide.eclipse.adt.internal.build.AidlProcessor; 27 import com.android.ide.eclipse.adt.internal.build.Messages; 28 import com.android.ide.eclipse.adt.internal.build.RenderScriptLauncher; 29 import com.android.ide.eclipse.adt.internal.build.RsSourceChangeHandler; 30 import com.android.ide.eclipse.adt.internal.build.SourceProcessor; 31 import com.android.ide.eclipse.adt.internal.build.builders.BaseBuilder.AbortBuildException; 32 import com.android.ide.eclipse.adt.internal.lint.EclipseLintClient; 33 import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs; 34 import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs.BuildVerbosity; 35 import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper; 36 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper; 37 import com.android.ide.eclipse.adt.internal.project.FixLaunchConfig; 38 import com.android.ide.eclipse.adt.internal.project.ProjectHelper; 39 import com.android.ide.eclipse.adt.internal.project.XmlErrorHandler.BasicXmlErrorListener; 40 import com.android.ide.eclipse.adt.internal.resources.manager.IdeScanningContext; 41 import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources; 42 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager; 43 import com.android.ide.eclipse.adt.internal.sdk.AdtManifestMergeCallback; 44 import com.android.ide.eclipse.adt.internal.sdk.ProjectState; 45 import com.android.ide.eclipse.adt.internal.sdk.Sdk; 46 import com.android.ide.eclipse.adt.io.IFileWrapper; 47 import com.android.ide.eclipse.adt.io.IFolderWrapper; 48 import com.android.io.StreamException; 49 import com.android.manifmerger.ManifestMerger; 50 import com.android.manifmerger.MergerLog; 51 import com.android.sdklib.AndroidVersion; 52 import com.android.sdklib.BuildToolInfo; 53 import com.android.sdklib.IAndroidTarget; 54 import com.android.sdklib.build.RenderScriptChecker; 55 import com.android.sdklib.build.RenderScriptProcessor; 56 import com.android.sdklib.internal.build.BuildConfigGenerator; 57 import com.android.sdklib.internal.build.SymbolLoader; 58 import com.android.sdklib.internal.build.SymbolWriter; 59 import com.android.sdklib.internal.project.ProjectProperties; 60 import com.android.sdklib.io.FileOp; 61 import com.android.sdklib.repository.FullRevision; 62 import com.android.utils.ILogger; 63 import com.android.utils.Pair; 64 import com.android.xml.AndroidManifest; 65 import com.google.common.collect.ArrayListMultimap; 66 import com.google.common.collect.Lists; 67 import com.google.common.collect.Multimap; 68 69 import org.eclipse.core.resources.IFile; 70 import org.eclipse.core.resources.IFolder; 71 import org.eclipse.core.resources.IMarker; 72 import org.eclipse.core.resources.IProject; 73 import org.eclipse.core.resources.IResource; 74 import org.eclipse.core.resources.IResourceDelta; 75 import org.eclipse.core.resources.IWorkspaceRoot; 76 import org.eclipse.core.resources.ResourcesPlugin; 77 import org.eclipse.core.runtime.CoreException; 78 import org.eclipse.core.runtime.IPath; 79 import org.eclipse.core.runtime.IProgressMonitor; 80 import org.eclipse.core.runtime.IStatus; 81 import org.eclipse.core.runtime.NullProgressMonitor; 82 import org.eclipse.core.runtime.Path; 83 import org.eclipse.jdt.core.IJavaProject; 84 import org.eclipse.jdt.core.JavaCore; 85 import org.xml.sax.SAXException; 86 87 import java.io.File; 88 import java.io.IOException; 89 import java.util.ArrayList; 90 import java.util.Collection; 91 import java.util.List; 92 import java.util.Map; 93 94 import javax.xml.parsers.ParserConfigurationException; 95 96 /** 97 * Pre Java Compiler. 98 * This incremental builder performs 2 tasks: 99 * <ul> 100 * <li>compiles the resources located in the res/ folder, along with the 101 * AndroidManifest.xml file into the R.java class.</li> 102 * <li>compiles any .aidl files into a corresponding java file.</li> 103 * </ul> 104 * 105 */ 106 public class PreCompilerBuilder extends BaseBuilder { 107 108 /** This ID is used in plugin.xml and in each project's .project file. 109 * It cannot be changed even if the class is renamed/moved */ 110 public static final String ID = "com.android.ide.eclipse.adt.PreCompilerBuilder"; //$NON-NLS-1$ 111 112 /** Flag to pass to PreCompiler builder that the build is a release build. 113 */ 114 public final static String RELEASE_REQUESTED = "android.releaseBuild"; //$NON-NLS-1$ 115 116 private static final String PROPERTY_PACKAGE = "manifestPackage"; //$NON-NLS-1$ 117 private static final String PROPERTY_MERGE_MANIFEST = "mergeManifest"; //$NON-NLS-1$ 118 private static final String PROPERTY_COMPILE_RESOURCES = "compileResources"; //$NON-NLS-1$ 119 private static final String PROPERTY_COMPILE_BUILDCONFIG = "createBuildConfig"; //$NON-NLS-1$ 120 private static final String PROPERTY_BUILDCONFIG_MODE = "buildConfigMode"; //$NON-NLS-1$ 121 122 private static final boolean MANIFEST_MERGER_ENABLED_DEFAULT = false; 123 private static final String MANIFEST_MERGER_PROPERTY = "manifestmerger.enabled"; //$NON-NLS-1$ 124 125 /** Merge Manifest Flag. Computed from resource delta, reset after action is taken. 126 * Stored persistently in the project. */ 127 private boolean mMustMergeManifest = false; 128 /** Resource compilation Flag. Computed from resource delta, reset after action is taken. 129 * Stored persistently in the project. */ 130 private boolean mMustCompileResources = false; 131 /** BuildConfig Flag. Computed from resource delta, reset after action is taken. 132 * Stored persistently in the project. */ 133 private boolean mMustCreateBuildConfig = false; 134 /** BuildConfig last more Flag. Computed from resource delta, reset after action is taken. 135 * Stored persistently in the project. */ 136 private boolean mLastBuildConfigMode; 137 138 /** cache of the java package defined in the manifest */ 139 private String mManifestPackage; 140 141 /** Output folder for generated Java File. Created on the Builder init 142 * @see #startupOnInitialize() 143 */ 144 private IFolder mGenFolder; 145 146 /** 147 * Progress monitor used at the end of every build to refresh the content of the 'gen' folder 148 * and set the generated files as derived. 149 */ 150 private DerivedProgressMonitor mDerivedProgressMonitor; 151 152 private AidlProcessor mAidlProcessor; 153 private RsSourceChangeHandler mRenderScriptSourceChangeHandler; 154 155 /** 156 * Progress monitor waiting the end of the process to set a persistent value 157 * in a file. This is typically used in conjunction with <code>IResource.refresh()</code>, 158 * since this call is asynchronous, and we need to wait for it to finish for the file 159 * to be known by eclipse, before we can call <code>resource.setPersistentProperty</code> on 160 * a new file. 161 */ 162 private static class DerivedProgressMonitor implements IProgressMonitor { 163 private boolean mCancelled = false; 164 private boolean mDone = false; 165 private final IFolder mGenFolder; 166 DerivedProgressMonitor(IFolder genFolder)167 public DerivedProgressMonitor(IFolder genFolder) { 168 mGenFolder = genFolder; 169 } 170 reset()171 void reset() { 172 mDone = false; 173 } 174 175 @Override beginTask(String name, int totalWork)176 public void beginTask(String name, int totalWork) { 177 } 178 179 @Override done()180 public void done() { 181 if (mDone == false) { 182 mDone = true; 183 processChildrenOf(mGenFolder); 184 } 185 } 186 processChildrenOf(IFolder folder)187 private void processChildrenOf(IFolder folder) { 188 IResource[] list; 189 try { 190 list = folder.members(); 191 } catch (CoreException e) { 192 return; 193 } 194 195 for (IResource member : list) { 196 if (member.exists()) { 197 if (member.getType() == IResource.FOLDER) { 198 processChildrenOf((IFolder) member); 199 } 200 201 try { 202 member.setDerived(true, new NullProgressMonitor()); 203 } catch (CoreException e) { 204 // This really shouldn't happen since we check that the resource 205 // exist. 206 // Worst case scenario, the resource isn't marked as derived. 207 } 208 } 209 } 210 } 211 212 @Override internalWorked(double work)213 public void internalWorked(double work) { 214 } 215 216 @Override isCanceled()217 public boolean isCanceled() { 218 return mCancelled; 219 } 220 221 @Override setCanceled(boolean value)222 public void setCanceled(boolean value) { 223 mCancelled = value; 224 } 225 226 @Override setTaskName(String name)227 public void setTaskName(String name) { 228 } 229 230 @Override subTask(String name)231 public void subTask(String name) { 232 } 233 234 @Override worked(int work)235 public void worked(int work) { 236 } 237 } 238 PreCompilerBuilder()239 public PreCompilerBuilder() { 240 super(); 241 } 242 243 // build() returns a list of project from which this project depends for future compilation. 244 @Override build( int kind, @SuppressWarnings("rawtypes") Map args, IProgressMonitor monitor)245 protected IProject[] build( 246 int kind, 247 @SuppressWarnings("rawtypes") Map args, 248 IProgressMonitor monitor) 249 throws CoreException { 250 // get a project object 251 IProject project = getProject(); 252 253 if (DEBUG_LOG) { 254 AdtPlugin.log(IStatus.INFO, "%s BUILD(PRE)", project.getName()); 255 } 256 257 // For the PreCompiler, only the library projects are considered Referenced projects, 258 // as only those projects have an impact on what is generated by this builder. 259 IProject[] result = null; 260 261 IFolder resOutFolder = null; 262 263 try { 264 assert mDerivedProgressMonitor != null; 265 266 mDerivedProgressMonitor.reset(); 267 268 // get the project info 269 ProjectState projectState = Sdk.getProjectState(project); 270 271 // this can happen if the project has no project.properties. 272 if (projectState == null) { 273 return null; 274 } 275 276 boolean isLibrary = projectState.isLibrary(); 277 278 IAndroidTarget projectTarget = projectState.getTarget(); 279 280 // get the libraries 281 List<IProject> libProjects = projectState.getFullLibraryProjects(); 282 result = libProjects.toArray(new IProject[libProjects.size()]); 283 284 IJavaProject javaProject = JavaCore.create(project); 285 286 // Top level check to make sure the build can move forward. 287 abortOnBadSetup(javaProject, projectState); 288 289 // now we need to get the classpath list 290 List<IPath> sourceFolderPathList = BaseProjectHelper.getSourceClasspaths(javaProject); 291 292 IFolder androidOutputFolder = BaseProjectHelper.getAndroidOutputFolder(project); 293 294 resOutFolder = getResOutFolder(androidOutputFolder); 295 296 setupSourceProcessors(javaProject, projectState, sourceFolderPathList, 297 androidOutputFolder); 298 299 PreCompilerDeltaVisitor dv = null; 300 String javaPackage = null; 301 String minSdkVersion = null; 302 303 if (kind == FULL_BUILD) { 304 AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project, 305 Messages.Start_Full_Pre_Compiler); 306 307 if (DEBUG_LOG) { 308 AdtPlugin.log(IStatus.INFO, "%s full build!", project.getName()); 309 } 310 311 // do some clean up. 312 doClean(project, monitor); 313 314 mMustMergeManifest = true; 315 mMustCompileResources = true; 316 mMustCreateBuildConfig = true; 317 318 mAidlProcessor.prepareFullBuild(project); 319 mRenderScriptSourceChangeHandler.prepareFullBuild(); 320 } else { 321 AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project, 322 Messages.Start_Inc_Pre_Compiler); 323 324 // Go through the resources and see if something changed. 325 // Even if the mCompileResources flag is true from a previously aborted 326 // build, we need to go through the Resource delta to get a possible 327 // list of aidl files to compile/remove. 328 IResourceDelta delta = getDelta(project); 329 if (delta == null) { 330 mMustCompileResources = true; 331 332 mAidlProcessor.prepareFullBuild(project); 333 mRenderScriptSourceChangeHandler.prepareFullBuild(); 334 } else { 335 dv = new PreCompilerDeltaVisitor(this, sourceFolderPathList, 336 mAidlProcessor.getChangeHandler(), 337 mRenderScriptSourceChangeHandler); 338 delta.accept(dv); 339 340 // Check to see if Manifest.xml, Manifest.java, or R.java have changed: 341 mMustCompileResources |= dv.getCompileResources(); 342 mMustMergeManifest |= dv.hasManifestChanged(); 343 344 // Notify the ResourceManager: 345 ResourceManager resManager = ResourceManager.getInstance(); 346 347 if (ResourceManager.isAutoBuilding()) { 348 ProjectResources projectResources = resManager.getProjectResources(project); 349 350 IdeScanningContext context = new IdeScanningContext(projectResources, 351 project, true); 352 353 boolean wasCleared = projectResources.ensureInitialized(); 354 355 if (!wasCleared) { 356 resManager.processDelta(delta, context); 357 } 358 359 // Check whether this project or its dependencies (libraries) have 360 // resources that need compilation 361 if (wasCleared || context.needsFullAapt()) { 362 mMustCompileResources = true; 363 364 // Must also call markAaptRequested on the project to not just 365 // store "aapt required" on this project, but also on any projects 366 // depending on this project if it's a library project 367 ResourceManager.markAaptRequested(project); 368 } 369 370 // Update error markers in the source editor 371 if (!mMustCompileResources) { 372 context.updateMarkers(false /* async */); 373 } 374 } // else: already processed the deltas in ResourceManager's IRawDeltaListener 375 376 mAidlProcessor.doneVisiting(project); 377 378 // get the java package from the visitor 379 javaPackage = dv.getManifestPackage(); 380 minSdkVersion = dv.getMinSdkVersion(); 381 } 382 } 383 384 // Has anyone marked this project as needing aapt? Typically done when 385 // one of the library projects this project depends on has changed 386 mMustCompileResources |= ResourceManager.isAaptRequested(project); 387 388 // if the main manifest didn't change, then we check for the library 389 // ones (will trigger manifest merging too) 390 if (libProjects.size() > 0) { 391 for (IProject libProject : libProjects) { 392 IResourceDelta delta = getDelta(libProject); 393 if (delta != null) { 394 PatternBasedDeltaVisitor visitor = new PatternBasedDeltaVisitor( 395 project, libProject, 396 "PRE:LibManifest"); //$NON-NLS-1$ 397 visitor.addSet(ChangedFileSetHelper.MANIFEST); 398 399 ChangedFileSet textSymbolCFS = null; 400 if (isLibrary == false) { 401 textSymbolCFS = ChangedFileSetHelper.getTextSymbols( 402 libProject); 403 visitor.addSet(textSymbolCFS); 404 } 405 406 delta.accept(visitor); 407 408 mMustMergeManifest |= visitor.checkSet(ChangedFileSetHelper.MANIFEST); 409 410 if (textSymbolCFS != null) { 411 mMustCompileResources |= visitor.checkSet(textSymbolCFS); 412 } 413 414 // no need to test others if we have all flags at true. 415 if (mMustMergeManifest && 416 (mMustCompileResources || textSymbolCFS == null)) { 417 break; 418 } 419 } 420 } 421 } 422 423 // store the build status in the persistent storage 424 saveProjectBooleanProperty(PROPERTY_MERGE_MANIFEST, mMustMergeManifest); 425 saveProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES, mMustCompileResources); 426 saveProjectBooleanProperty(PROPERTY_COMPILE_BUILDCONFIG, mMustCreateBuildConfig); 427 428 // if there was some XML errors, we just return w/o doing 429 // anything since we've put some markers in the files anyway. 430 if (dv != null && dv.mXmlError) { 431 AdtPlugin.printErrorToConsole(project, Messages.Xml_Error); 432 433 return result; 434 } 435 436 if (projectState.getRenderScriptSupportMode()) { 437 FullRevision minBuildToolsRev = new FullRevision(19,0,3); 438 if (mBuildToolInfo.getRevision().compareTo(minBuildToolsRev) == -1) { 439 String msg = "RenderScript support mode requires Build-Tools 19.0.3 or later."; 440 AdtPlugin.printErrorToConsole(project, msg); 441 markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR); 442 443 return result; 444 } 445 } 446 447 // get the manifest file 448 IFile manifestFile = ProjectHelper.getManifest(project); 449 450 if (manifestFile == null) { 451 String msg = String.format(Messages.s_File_Missing, 452 SdkConstants.FN_ANDROID_MANIFEST_XML); 453 AdtPlugin.printErrorToConsole(project, msg); 454 markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR); 455 456 return result; 457 458 // TODO: document whether code below that uses manifest (which is now guaranteed 459 // to be null) will actually be executed or not. 460 } 461 462 // lets check the XML of the manifest first, if that hasn't been done by the 463 // resource delta visitor yet. 464 if (dv == null || dv.getCheckedManifestXml() == false) { 465 BasicXmlErrorListener errorListener = new BasicXmlErrorListener(); 466 try { 467 ManifestData parser = AndroidManifestHelper.parseUnchecked( 468 new IFileWrapper(manifestFile), 469 true /*gather data*/, 470 errorListener); 471 472 if (errorListener.mHasXmlError == true) { 473 // There was an error in the manifest, its file has been marked 474 // by the XmlErrorHandler. The stopBuild() call below will abort 475 // this with an exception. 476 String msg = String.format(Messages.s_Contains_Xml_Error, 477 SdkConstants.FN_ANDROID_MANIFEST_XML); 478 AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project, msg); 479 markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR); 480 481 return result; 482 } 483 484 // Get the java package from the parser. 485 // This can be null if the parsing failed because the resource is out of sync, 486 // in which case the error will already have been logged anyway. 487 if (parser != null) { 488 javaPackage = parser.getPackage(); 489 minSdkVersion = parser.getMinSdkVersionString(); 490 } 491 } catch (StreamException e) { 492 handleStreamException(e); 493 494 return result; 495 } catch (ParserConfigurationException e) { 496 String msg = String.format( 497 "Bad parser configuration for %s: %s", 498 manifestFile.getFullPath(), 499 e.getMessage()); 500 501 handleException(e, msg); 502 return result; 503 504 } catch (SAXException e) { 505 String msg = String.format( 506 "Parser exception for %s: %s", 507 manifestFile.getFullPath(), 508 e.getMessage()); 509 510 handleException(e, msg); 511 return result; 512 } catch (IOException e) { 513 String msg = String.format( 514 "I/O error for %s: %s", 515 manifestFile.getFullPath(), 516 e.getMessage()); 517 518 handleException(e, msg); 519 return result; 520 } 521 } 522 523 int minSdkValue = -1; 524 525 if (minSdkVersion != null) { 526 try { 527 minSdkValue = Integer.parseInt(minSdkVersion); 528 } catch (NumberFormatException e) { 529 // it's ok, it means minSdkVersion contains a (hopefully) valid codename. 530 } 531 532 AndroidVersion targetVersion = projectTarget.getVersion(); 533 534 // remove earlier marker from the manifest 535 removeMarkersFromResource(manifestFile, AdtConstants.MARKER_ADT); 536 537 if (minSdkValue != -1) { 538 String codename = targetVersion.getCodename(); 539 if (codename != null) { 540 // integer minSdk when the target is a preview => fatal error 541 String msg = String.format( 542 "Platform %1$s is a preview and requires application manifest to set %2$s to '%1$s'", 543 codename, AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION); 544 AdtPlugin.printErrorToConsole(project, msg); 545 BaseProjectHelper.markResource(manifestFile, AdtConstants.MARKER_ADT, 546 msg, IMarker.SEVERITY_ERROR); 547 return result; 548 } else if (minSdkValue > targetVersion.getApiLevel()) { 549 // integer minSdk is too high for the target => warning 550 String msg = String.format( 551 "Attribute %1$s (%2$d) is higher than the project target API level (%3$d)", 552 AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION, 553 minSdkValue, targetVersion.getApiLevel()); 554 AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project, msg); 555 BaseProjectHelper.markResource(manifestFile, AdtConstants.MARKER_ADT, 556 msg, IMarker.SEVERITY_WARNING); 557 } 558 } else { 559 // looks like the min sdk is a codename, check it matches the codename 560 // of the platform 561 String codename = targetVersion.getCodename(); 562 if (codename == null) { 563 // platform is not a preview => fatal error 564 String msg = String.format( 565 "Manifest attribute '%1$s' is set to '%2$s'. Integer is expected.", 566 AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION, minSdkVersion); 567 AdtPlugin.printErrorToConsole(project, msg); 568 BaseProjectHelper.markResource(manifestFile, AdtConstants.MARKER_ADT, 569 msg, IMarker.SEVERITY_ERROR); 570 return result; 571 } else if (codename.equals(minSdkVersion) == false) { 572 // platform and manifest codenames don't match => fatal error. 573 String msg = String.format( 574 "Value of manifest attribute '%1$s' does not match platform codename '%2$s'", 575 AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION, codename); 576 AdtPlugin.printErrorToConsole(project, msg); 577 BaseProjectHelper.markResource(manifestFile, AdtConstants.MARKER_ADT, 578 msg, IMarker.SEVERITY_ERROR); 579 return result; 580 } 581 582 // if we get there, the minSdkVersion is a codename matching the target 583 // platform codename. In this case we set minSdkValue to the previous API 584 // level, as it's used by source processors. 585 minSdkValue = targetVersion.getApiLevel(); 586 } 587 } else if (projectTarget.getVersion().isPreview()) { 588 // else the minSdkVersion is not set but we are using a preview target. 589 // Display an error 590 String codename = projectTarget.getVersion().getCodename(); 591 String msg = String.format( 592 "Platform %1$s is a preview and requires application manifests to set %2$s to '%1$s'", 593 codename, AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION); 594 AdtPlugin.printErrorToConsole(project, msg); 595 BaseProjectHelper.markResource(manifestFile, AdtConstants.MARKER_ADT, msg, 596 IMarker.SEVERITY_ERROR); 597 return result; 598 } 599 600 if (javaPackage == null || javaPackage.length() == 0) { 601 // looks like the AndroidManifest file isn't valid. 602 String msg = String.format(Messages.s_Doesnt_Declare_Package_Error, 603 SdkConstants.FN_ANDROID_MANIFEST_XML); 604 AdtPlugin.printErrorToConsole(project, msg); 605 BaseProjectHelper.markResource(manifestFile, AdtConstants.MARKER_ADT, 606 msg, IMarker.SEVERITY_ERROR); 607 608 return result; 609 } else if (javaPackage.indexOf('.') == -1) { 610 // The application package name does not contain 2+ segments! 611 String msg = String.format( 612 "Application package '%1$s' must have a minimum of 2 segments.", 613 SdkConstants.FN_ANDROID_MANIFEST_XML); 614 AdtPlugin.printErrorToConsole(project, msg); 615 BaseProjectHelper.markResource(manifestFile, AdtConstants.MARKER_ADT, 616 msg, IMarker.SEVERITY_ERROR); 617 618 return result; 619 } 620 621 // at this point we have the java package. We need to make sure it's not a different 622 // package than the previous one that were built. 623 if (javaPackage.equals(mManifestPackage) == false) { 624 // The manifest package has changed, the user may want to update 625 // the launch configuration 626 if (mManifestPackage != null) { 627 AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project, 628 Messages.Checking_Package_Change); 629 630 FixLaunchConfig flc = new FixLaunchConfig(project, mManifestPackage, 631 javaPackage); 632 flc.start(); 633 } 634 635 // record the new manifest package, and save it. 636 mManifestPackage = javaPackage; 637 saveProjectStringProperty(PROPERTY_PACKAGE, mManifestPackage); 638 639 // force a clean 640 doClean(project, monitor); 641 mMustMergeManifest = true; 642 mMustCompileResources = true; 643 mMustCreateBuildConfig = true; 644 mAidlProcessor.prepareFullBuild(project); 645 mRenderScriptSourceChangeHandler.prepareFullBuild(); 646 647 saveProjectBooleanProperty(PROPERTY_MERGE_MANIFEST, mMustMergeManifest); 648 saveProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES, mMustCompileResources); 649 saveProjectBooleanProperty(PROPERTY_COMPILE_BUILDCONFIG, mMustCreateBuildConfig); 650 } 651 652 try { 653 handleBuildConfig(args); 654 } catch (IOException e) { 655 handleException(e, "Failed to create BuildConfig class"); 656 return result; 657 } 658 659 // merge the manifest 660 if (mMustMergeManifest) { 661 boolean enabled = MANIFEST_MERGER_ENABLED_DEFAULT; 662 String propValue = projectState.getProperty(MANIFEST_MERGER_PROPERTY); 663 if (propValue != null) { 664 enabled = Boolean.valueOf(propValue); 665 } 666 667 if (mergeManifest(androidOutputFolder, libProjects, enabled) == false) { 668 return result; 669 } 670 } 671 672 List<File> libProjectsOut = new ArrayList<File>(libProjects.size()); 673 for (IProject libProject : libProjects) { 674 libProjectsOut.add( 675 BaseProjectHelper.getAndroidOutputFolder(libProject) 676 .getLocation().toFile()); 677 } 678 679 // run the source processors 680 int processorStatus = SourceProcessor.COMPILE_STATUS_NONE; 681 682 683 try { 684 processorStatus |= mAidlProcessor.compileFiles(this, 685 project, projectTarget, sourceFolderPathList, 686 libProjectsOut, monitor); 687 } catch (Throwable t) { 688 handleException(t, "Failed to run aidl. Check workspace log for detail."); 689 return result; 690 } 691 692 try { 693 processorStatus |= compileRs(minSdkValue, projectState, androidOutputFolder, 694 resOutFolder, monitor); 695 } catch (Throwable t) { 696 handleException(t, "Failed to run renderscript. Check workspace log for detail."); 697 return result; 698 } 699 700 // if a processor created some resources file, force recompilation of the resources. 701 if ((processorStatus & SourceProcessor.COMPILE_STATUS_RES) != 0) { 702 mMustCompileResources = true; 703 // save the current state before attempting the compilation 704 saveProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES, mMustCompileResources); 705 } 706 707 // handle the resources, after the processors are run since some (renderscript) 708 // generate resources. 709 boolean compiledTheResources = mMustCompileResources; 710 if (mMustCompileResources) { 711 if (DEBUG_LOG) { 712 AdtPlugin.log(IStatus.INFO, "%s compiling resources!", project.getName()); 713 } 714 715 IFile proguardFile = null; 716 if (projectState.getProperty(ProjectProperties.PROPERTY_PROGUARD_CONFIG) != null) { 717 proguardFile = androidOutputFolder.getFile(AdtConstants.FN_AAPT_PROGUARD); 718 } 719 720 handleResources(project, javaPackage, projectTarget, manifestFile, resOutFolder, 721 libProjects, isLibrary, proguardFile); 722 } 723 724 if (processorStatus == SourceProcessor.COMPILE_STATUS_NONE && 725 compiledTheResources == false) { 726 AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project, 727 Messages.Nothing_To_Compile); 728 } 729 } catch (AbortBuildException e) { 730 return result; 731 } finally { 732 // refresh the 'gen' source folder. Once this is done with the custom progress 733 // monitor to mark all new files as derived 734 mGenFolder.refreshLocal(IResource.DEPTH_INFINITE, mDerivedProgressMonitor); 735 if (resOutFolder != null) { 736 resOutFolder.refreshLocal(IResource.DEPTH_INFINITE, mDerivedProgressMonitor); 737 } 738 } 739 740 return result; 741 } 742 getResOutFolder(IFolder androidOutputFolder)743 private IFolder getResOutFolder(IFolder androidOutputFolder) { 744 return androidOutputFolder.getFolder(AdtConstants.WS_BIN_RELATIVE_BC); 745 } 746 747 @Override clean(IProgressMonitor monitor)748 protected void clean(IProgressMonitor monitor) throws CoreException { 749 super.clean(monitor); 750 751 if (DEBUG_LOG) { 752 AdtPlugin.log(IStatus.INFO, "%s CLEAN(PRE)", getProject().getName()); 753 } 754 755 doClean(getProject(), monitor); 756 if (mGenFolder != null) { 757 mGenFolder.refreshLocal(IResource.DEPTH_INFINITE, monitor); 758 } 759 } 760 doClean(IProject project, IProgressMonitor monitor)761 private void doClean(IProject project, IProgressMonitor monitor) throws CoreException { 762 AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project, 763 Messages.Removing_Generated_Classes); 764 765 // remove all the derived resources from the 'gen' source folder. 766 if (mGenFolder != null && mGenFolder.exists()) { 767 // gen folder should not be derived, but previous version could set it to derived 768 // so we make sure this isn't the case (or it'll get deleted by the clean) 769 mGenFolder.setDerived(false, monitor); 770 771 removeDerivedResources(mGenFolder, monitor); 772 } 773 774 // Clear the project of the generic markers 775 removeMarkersFromContainer(project, AdtConstants.MARKER_AAPT_COMPILE); 776 removeMarkersFromContainer(project, AdtConstants.MARKER_XML); 777 removeMarkersFromContainer(project, AdtConstants.MARKER_AIDL); 778 removeMarkersFromContainer(project, AdtConstants.MARKER_RENDERSCRIPT); 779 removeMarkersFromContainer(project, AdtConstants.MARKER_MANIFMERGER); 780 removeMarkersFromContainer(project, AdtConstants.MARKER_ANDROID); 781 782 // Also clean up lint 783 EclipseLintClient.clearMarkers(project); 784 785 // clean the project repo 786 ProjectResources res = ResourceManager.getInstance().getProjectResources(project); 787 res.clear(); 788 } 789 790 @Override startupOnInitialize()791 protected void startupOnInitialize() { 792 try { 793 super.startupOnInitialize(); 794 795 IProject project = getProject(); 796 797 // load the previous IFolder and java package. 798 mManifestPackage = loadProjectStringProperty(PROPERTY_PACKAGE); 799 800 // get the source folder in which all the Java files are created 801 mGenFolder = project.getFolder(SdkConstants.FD_GEN_SOURCES); 802 mDerivedProgressMonitor = new DerivedProgressMonitor(mGenFolder); 803 804 // Load the current compile flags. We ask for true if not found to force a recompile. 805 mMustMergeManifest = loadProjectBooleanProperty(PROPERTY_MERGE_MANIFEST, true); 806 mMustCompileResources = loadProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES, true); 807 mMustCreateBuildConfig = loadProjectBooleanProperty(PROPERTY_COMPILE_BUILDCONFIG, true); 808 Boolean v = ProjectHelper.loadBooleanProperty(project, PROPERTY_BUILDCONFIG_MODE); 809 if (v == null) { 810 // no previous build config mode? force regenerate 811 mMustCreateBuildConfig = true; 812 } else { 813 mLastBuildConfigMode = v; 814 } 815 816 } catch (Throwable throwable) { 817 AdtPlugin.log(throwable, "Failed to finish PrecompilerBuilder#startupOnInitialize()"); 818 } 819 } 820 setupSourceProcessors(@onNull IJavaProject javaProject, @NonNull ProjectState projectState, @NonNull List<IPath> sourceFolderPathList, @NonNull IFolder androidOutputFolder)821 private void setupSourceProcessors(@NonNull IJavaProject javaProject, 822 @NonNull ProjectState projectState, 823 @NonNull List<IPath> sourceFolderPathList, 824 @NonNull IFolder androidOutputFolder) { 825 if (mAidlProcessor == null) { 826 mAidlProcessor = new AidlProcessor(javaProject, mBuildToolInfo, mGenFolder); 827 } else { 828 mAidlProcessor.setBuildToolInfo(mBuildToolInfo); 829 } 830 831 List<File> sourceFolders = Lists.newArrayListWithCapacity(sourceFolderPathList.size()); 832 IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); 833 834 for (IPath path : sourceFolderPathList) { 835 IResource resource = root.findMember(path); 836 if (resource != null && resource.exists() && resource.getType() == IResource.FOLDER) { 837 IPath fullPath = resource.getLocation(); 838 if (fullPath != null) { 839 sourceFolders.add(fullPath.toFile()); 840 } 841 } 842 } 843 844 RenderScriptChecker checker = new RenderScriptChecker(sourceFolders, 845 androidOutputFolder.getLocation().toFile()); 846 mRenderScriptSourceChangeHandler = new RsSourceChangeHandler(checker); 847 } 848 compileRs(int minSdkValue, @NonNull ProjectState projectState, @NonNull IFolder androidOutputFolder, @NonNull IFolder resOutFolder, @NonNull IProgressMonitor monitor)849 private int compileRs(int minSdkValue, 850 @NonNull ProjectState projectState, 851 @NonNull IFolder androidOutputFolder, 852 @NonNull IFolder resOutFolder, 853 @NonNull IProgressMonitor monitor) 854 throws IOException, InterruptedException { 855 if (!mRenderScriptSourceChangeHandler.mustCompile()) { 856 return SourceProcessor.COMPILE_STATUS_NONE; 857 } 858 859 RenderScriptChecker checker = mRenderScriptSourceChangeHandler.getChecker(); 860 861 List<File> inputs = checker.findInputFiles(); 862 List<File> importFolders = checker.getSourceFolders(); 863 File buildFolder = androidOutputFolder.getLocation().toFile(); 864 865 866 // get the renderscript target 867 int rsTarget = minSdkValue == -1 ? 11 : minSdkValue; 868 String rsTargetStr = projectState.getProperty(ProjectProperties.PROPERTY_RS_TARGET); 869 if (rsTargetStr != null) { 870 try { 871 rsTarget = Integer.parseInt(rsTargetStr); 872 } catch (NumberFormatException e) { 873 handleException(e, String.format( 874 "Property %s is not an integer.", 875 ProjectProperties.PROPERTY_RS_TARGET)); 876 return SourceProcessor.COMPILE_STATUS_NONE; 877 } 878 } 879 880 RenderScriptProcessor processor = new RenderScriptProcessor( 881 inputs, 882 importFolders, 883 buildFolder, 884 mGenFolder.getLocation().toFile(), 885 resOutFolder.getLocation().toFile(), 886 new File(buildFolder, SdkConstants.FD_RS_OBJ), 887 new File(buildFolder, SdkConstants.FD_RS_LIBS), 888 mBuildToolInfo, 889 rsTarget, 890 false /*debugBuild, always false for now*/, 891 3, 892 projectState.getRenderScriptSupportMode()); 893 894 // clean old dependency files fiest 895 checker.cleanDependencies(); 896 897 // then clean old output files 898 processor.cleanOldOutput(checker.getOldOutputs()); 899 900 RenderScriptLauncher launcher = new RenderScriptLauncher( 901 getProject(), 902 mGenFolder, 903 resOutFolder, 904 monitor, 905 AdtPrefs.getPrefs().getBuildVerbosity() == BuildVerbosity.VERBOSE /*verbose*/); 906 907 // and run the build 908 processor.build(launcher); 909 910 return SourceProcessor.COMPILE_STATUS_CODE | SourceProcessor.COMPILE_STATUS_RES; 911 } 912 913 @SuppressWarnings("deprecation") handleBuildConfig(@uppressWarnings"rawtypes") Map args)914 private void handleBuildConfig(@SuppressWarnings("rawtypes") Map args) 915 throws IOException, CoreException { 916 boolean debugMode = !args.containsKey(RELEASE_REQUESTED); 917 918 BuildConfigGenerator generator = new BuildConfigGenerator( 919 mGenFolder.getLocation().toOSString(), mManifestPackage, debugMode); 920 921 if (mMustCreateBuildConfig == false) { 922 // check the file is present. 923 IFolder folder = getGenManifestPackageFolder(); 924 if (folder.exists(new Path(BuildConfigGenerator.BUILD_CONFIG_NAME)) == false) { 925 mMustCreateBuildConfig = true; 926 AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, getProject(), 927 String.format("Class %1$s is missing!", 928 BuildConfigGenerator.BUILD_CONFIG_NAME)); 929 } else if (debugMode != mLastBuildConfigMode) { 930 // else if the build mode changed, force creation 931 mMustCreateBuildConfig = true; 932 AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, getProject(), 933 String.format("Different build mode, must update %1$s!", 934 BuildConfigGenerator.BUILD_CONFIG_NAME)); 935 } 936 } 937 938 if (mMustCreateBuildConfig) { 939 if (DEBUG_LOG) { 940 AdtPlugin.log(IStatus.INFO, "%s generating BuilderConfig!", getProject().getName()); 941 } 942 943 AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, getProject(), 944 String.format("Generating %1$s...", BuildConfigGenerator.BUILD_CONFIG_NAME)); 945 generator.generate(); 946 947 mMustCreateBuildConfig = false; 948 saveProjectBooleanProperty(PROPERTY_COMPILE_BUILDCONFIG, mMustCreateBuildConfig); 949 saveProjectBooleanProperty(PROPERTY_BUILDCONFIG_MODE, mLastBuildConfigMode = debugMode); 950 } 951 } 952 mergeManifest(IFolder androidOutFolder, List<IProject> libProjects, boolean enabled)953 private boolean mergeManifest(IFolder androidOutFolder, List<IProject> libProjects, 954 boolean enabled) throws CoreException { 955 if (DEBUG_LOG) { 956 AdtPlugin.log(IStatus.INFO, "%s merging manifests!", getProject().getName()); 957 } 958 959 IFile outFile = androidOutFolder.getFile(SdkConstants.FN_ANDROID_MANIFEST_XML); 960 IFile manifest = getProject().getFile(SdkConstants.FN_ANDROID_MANIFEST_XML); 961 962 // remove existing markers from the manifest. 963 // FIXME: only remove from manifest once the markers are put there. 964 removeMarkersFromResource(getProject(), AdtConstants.MARKER_MANIFMERGER); 965 966 // If the merging is not enabled or if there's no library then we simply copy the 967 // manifest over. 968 if (enabled == false || libProjects.size() == 0) { 969 try { 970 new FileOp().copyFile(manifest.getLocation().toFile(), 971 outFile.getLocation().toFile()); 972 973 outFile.refreshLocal(IResource.DEPTH_INFINITE, mDerivedProgressMonitor); 974 975 saveProjectBooleanProperty(PROPERTY_MERGE_MANIFEST, mMustMergeManifest = false); 976 } catch (IOException e) { 977 handleException(e, "Failed to copy Manifest"); 978 return false; 979 } 980 } else { 981 final ArrayList<String> errors = new ArrayList<String>(); 982 983 // TODO change MergerLog.wrapSdkLog by a custom IMergerLog that will create 984 // and maintain error markers. 985 ManifestMerger merger = new ManifestMerger( 986 MergerLog.wrapSdkLog(new ILogger() { 987 @Override 988 public void warning(@NonNull String warningFormat, Object... args) { 989 AdtPlugin.printToConsole(getProject(), String.format(warningFormat, args)); 990 } 991 992 @Override 993 public void info(@NonNull String msgFormat, Object... args) { 994 AdtPlugin.printToConsole(getProject(), String.format(msgFormat, args)); 995 } 996 997 @Override 998 public void verbose(@NonNull String msgFormat, Object... args) { 999 info(msgFormat, args); 1000 } 1001 1002 @Override 1003 public void error(@Nullable Throwable t, @Nullable String errorFormat, 1004 Object... args) { 1005 errors.add(String.format(errorFormat, args)); 1006 } 1007 }), 1008 new AdtManifestMergeCallback()); 1009 1010 File[] libManifests = new File[libProjects.size()]; 1011 int libIndex = 0; 1012 for (IProject lib : libProjects) { 1013 libManifests[libIndex++] = lib.getFile(SdkConstants.FN_ANDROID_MANIFEST_XML) 1014 .getLocation().toFile(); 1015 } 1016 1017 if (merger.process( 1018 outFile.getLocation().toFile(), 1019 manifest.getLocation().toFile(), 1020 libManifests, 1021 null /*injectAttributes*/, null /*packageOverride*/) == false) { 1022 if (errors.size() > 1) { 1023 StringBuilder sb = new StringBuilder(); 1024 for (String s : errors) { 1025 sb.append(s).append('\n'); 1026 } 1027 1028 markProject(AdtConstants.MARKER_MANIFMERGER, sb.toString(), 1029 IMarker.SEVERITY_ERROR); 1030 1031 } else if (errors.size() == 1) { 1032 markProject(AdtConstants.MARKER_MANIFMERGER, errors.get(0), 1033 IMarker.SEVERITY_ERROR); 1034 } else { 1035 markProject(AdtConstants.MARKER_MANIFMERGER, "Unknown error merging manifest", 1036 IMarker.SEVERITY_ERROR); 1037 } 1038 return false; 1039 } 1040 1041 outFile.refreshLocal(IResource.DEPTH_INFINITE, mDerivedProgressMonitor); 1042 saveProjectBooleanProperty(PROPERTY_MERGE_MANIFEST, mMustMergeManifest = false); 1043 } 1044 1045 return true; 1046 } 1047 1048 /** 1049 * Handles resource changes and regenerate whatever files need regenerating. 1050 * @param project the main project 1051 * @param javaPackage the app package for the main project 1052 * @param projectTarget the target of the main project 1053 * @param manifest the {@link IFile} representing the project manifest 1054 * @param libProjects the library dependencies 1055 * @param isLibrary if the project is a library project 1056 * @throws CoreException 1057 * @throws AbortBuildException 1058 */ handleResources(IProject project, String javaPackage, IAndroidTarget projectTarget, IFile manifest, IFolder resOutFolder, List<IProject> libProjects, boolean isLibrary, IFile proguardFile)1059 private void handleResources(IProject project, String javaPackage, IAndroidTarget projectTarget, 1060 IFile manifest, IFolder resOutFolder, List<IProject> libProjects, boolean isLibrary, 1061 IFile proguardFile) throws CoreException, AbortBuildException { 1062 // get the resource folder 1063 IFolder resFolder = project.getFolder(AdtConstants.WS_RESOURCES); 1064 1065 // get the file system path 1066 IPath outputLocation = mGenFolder.getLocation(); 1067 IPath resLocation = resFolder.getLocation(); 1068 IPath manifestLocation = manifest == null ? null : manifest.getLocation(); 1069 1070 // those locations have to exist for us to do something! 1071 if (outputLocation != null && resLocation != null 1072 && manifestLocation != null) { 1073 String osOutputPath = outputLocation.toOSString(); 1074 String osResPath = resLocation.toOSString(); 1075 String osManifestPath = manifestLocation.toOSString(); 1076 1077 // remove the aapt markers 1078 removeMarkersFromResource(manifest, AdtConstants.MARKER_AAPT_COMPILE); 1079 removeMarkersFromContainer(resFolder, AdtConstants.MARKER_AAPT_COMPILE); 1080 1081 AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project, 1082 Messages.Preparing_Generated_Files); 1083 1084 // we need to figure out where to store the R class. 1085 // get the parent folder for R.java and update mManifestPackageSourceFolder 1086 IFolder mainPackageFolder = getGenManifestPackageFolder(); 1087 1088 // handle libraries 1089 ArrayList<IFolder> libResFolders = Lists.newArrayList(); 1090 ArrayList<Pair<File, String>> libRFiles = Lists.newArrayList(); 1091 if (libProjects != null) { 1092 for (IProject lib : libProjects) { 1093 IFolder libResFolder = lib.getFolder(SdkConstants.FD_RES); 1094 if (libResFolder.exists()) { 1095 libResFolders.add(libResFolder); 1096 } 1097 1098 try { 1099 // get the package of the library, and if it's different form the 1100 // main project, generate the R class for it too. 1101 String libJavaPackage = AndroidManifest.getPackage(new IFolderWrapper(lib)); 1102 if (libJavaPackage.equals(javaPackage) == false) { 1103 1104 IFolder libOutput = BaseProjectHelper.getAndroidOutputFolder(lib); 1105 File libOutputFolder = libOutput.getLocation().toFile(); 1106 1107 libRFiles.add(Pair.of( 1108 new File(libOutputFolder, "R.txt"), 1109 libJavaPackage)); 1110 1111 } 1112 } catch (Exception e) { 1113 } 1114 } 1115 } 1116 1117 String proguardFilePath = proguardFile != null ? 1118 proguardFile.getLocation().toOSString(): null; 1119 1120 File resOutFile = resOutFolder.getLocation().toFile(); 1121 String resOutPath = resOutFile.isDirectory() ? resOutFile.getAbsolutePath() : null; 1122 1123 execAapt(project, projectTarget, osOutputPath, resOutPath, osResPath, osManifestPath, 1124 mainPackageFolder, libResFolders, libRFiles, isLibrary, proguardFilePath); 1125 } 1126 } 1127 1128 /** 1129 * Executes AAPT to generate R.java/Manifest.java 1130 * @param project the main project 1131 * @param projectTarget the main project target 1132 * @param osOutputPath the OS output path for the generated file. This is the source folder, not 1133 * the package folder. 1134 * @param osResPath the OS path to the res folder for the main project 1135 * @param osManifestPath the OS path to the manifest of the main project 1136 * @param packageFolder the IFolder that will contain the generated file. Unlike 1137 * <var>osOutputPath</var> this is the direct parent of the generated files. 1138 * If <var>customJavaPackage</var> is not null, this must match the new destination triggered 1139 * by its value. 1140 * @param libResFolders the list of res folders for the library. 1141 * @param libRFiles a list of R files for the libraries. 1142 * @param isLibrary if the project is a library project 1143 * @param proguardFile an optional path to store proguard information 1144 * @throws AbortBuildException 1145 */ 1146 @SuppressWarnings("deprecation") execAapt(IProject project, IAndroidTarget projectTarget, String osOutputPath, String osBcOutPath, String osResPath, String osManifestPath, IFolder packageFolder, ArrayList<IFolder> libResFolders, List<Pair<File, String>> libRFiles, boolean isLibrary, String proguardFile)1147 private void execAapt(IProject project, IAndroidTarget projectTarget, String osOutputPath, 1148 String osBcOutPath, String osResPath, String osManifestPath, IFolder packageFolder, 1149 ArrayList<IFolder> libResFolders, List<Pair<File, String>> libRFiles, 1150 boolean isLibrary, String proguardFile) 1151 throws AbortBuildException { 1152 1153 // We actually need to delete the manifest.java as it may become empty and 1154 // in this case aapt doesn't generate an empty one, but instead doesn't 1155 // touch it. 1156 IFile manifestJavaFile = packageFolder.getFile(SdkConstants.FN_MANIFEST_CLASS); 1157 manifestJavaFile.getLocation().toFile().delete(); 1158 1159 // launch aapt: create the command line 1160 ArrayList<String> array = new ArrayList<String>(); 1161 1162 String aaptPath = mBuildToolInfo.getPath(BuildToolInfo.PathId.AAPT); 1163 1164 array.add(aaptPath); 1165 array.add("package"); //$NON-NLS-1$ 1166 array.add("-m"); //$NON-NLS-1$ 1167 if (AdtPrefs.getPrefs().getBuildVerbosity() == BuildVerbosity.VERBOSE) { 1168 array.add("-v"); //$NON-NLS-1$ 1169 } 1170 1171 if (isLibrary) { 1172 array.add("--non-constant-id"); //$NON-NLS-1$ 1173 } 1174 1175 if (libResFolders.size() > 0) { 1176 array.add("--auto-add-overlay"); //$NON-NLS-1$ 1177 } 1178 1179 // If a library or has libraries, generate a text version of the R symbols. 1180 File outputFolder = BaseProjectHelper.getAndroidOutputFolder(project).getLocation() 1181 .toFile(); 1182 1183 if (isLibrary || !libRFiles.isEmpty()) { 1184 array.add("--output-text-symbols"); //$NON-NLS-1$ 1185 array.add(outputFolder.getAbsolutePath()); 1186 } 1187 1188 array.add("-J"); //$NON-NLS-1$ 1189 array.add(osOutputPath); 1190 array.add("-M"); //$NON-NLS-1$ 1191 array.add(osManifestPath); 1192 if (osBcOutPath != null) { 1193 array.add("-S"); //$NON-NLS-1$ 1194 array.add(osBcOutPath); 1195 } 1196 array.add("-S"); //$NON-NLS-1$ 1197 array.add(osResPath); 1198 for (IFolder libResFolder : libResFolders) { 1199 array.add("-S"); //$NON-NLS-1$ 1200 array.add(libResFolder.getLocation().toOSString()); 1201 } 1202 1203 array.add("-I"); //$NON-NLS-1$ 1204 array.add(projectTarget.getPath(IAndroidTarget.ANDROID_JAR)); 1205 1206 // use the proguard file 1207 if (proguardFile != null && proguardFile.length() > 0) { 1208 array.add("-G"); 1209 array.add(proguardFile); 1210 } 1211 1212 if (AdtPrefs.getPrefs().getBuildVerbosity() == BuildVerbosity.VERBOSE) { 1213 StringBuilder sb = new StringBuilder(); 1214 for (String c : array) { 1215 sb.append(c); 1216 sb.append(' '); 1217 } 1218 String cmd_line = sb.toString(); 1219 AdtPlugin.printToConsole(project, cmd_line); 1220 } 1221 1222 // launch 1223 try { 1224 // launch the command line process 1225 Process process = Runtime.getRuntime().exec( 1226 array.toArray(new String[array.size()])); 1227 1228 // list to store each line of stderr 1229 ArrayList<String> stdErr = new ArrayList<String>(); 1230 1231 // get the output and return code from the process 1232 int returnCode = grabProcessOutput(process, stdErr); 1233 1234 // attempt to parse the error output 1235 boolean parsingError = AaptParser.parseOutput(stdErr, project); 1236 1237 // if we couldn't parse the output we display it in the console. 1238 if (parsingError) { 1239 if (returnCode != 0) { 1240 AdtPlugin.printErrorToConsole(project, stdErr.toArray()); 1241 } else { 1242 AdtPlugin.printBuildToConsole(BuildVerbosity.NORMAL, 1243 project, stdErr.toArray()); 1244 } 1245 } 1246 1247 if (returnCode != 0) { 1248 // if the exec failed, and we couldn't parse the error output 1249 // (and therefore not all files that should have been marked, 1250 // were marked), we put a generic marker on the project and abort. 1251 if (parsingError) { 1252 markProject(AdtConstants.MARKER_ADT, 1253 Messages.Unparsed_AAPT_Errors, IMarker.SEVERITY_ERROR); 1254 } else if (stdErr.size() == 0) { 1255 // no parsing error because sdterr was empty. We still need to put 1256 // a marker otherwise there's no user visible feedback. 1257 markProject(AdtConstants.MARKER_ADT, 1258 String.format(Messages.AAPT_Exec_Error_d, returnCode), 1259 IMarker.SEVERITY_ERROR); 1260 } 1261 1262 AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project, 1263 Messages.AAPT_Error); 1264 1265 // abort if exec failed. 1266 throw new AbortBuildException(); 1267 } 1268 1269 // now if the project has libraries, R needs to be created for each libraries 1270 // unless this is a library. 1271 if (isLibrary == false && !libRFiles.isEmpty()) { 1272 File rFile = new File(outputFolder, SdkConstants.FN_RESOURCE_TEXT); 1273 // if the project has no resources, the file could not exist. 1274 if (rFile.isFile()) { 1275 // Load the full symbols from the full R.txt file. 1276 SymbolLoader fullSymbolValues = new SymbolLoader(rFile); 1277 fullSymbolValues.load(); 1278 1279 Multimap<String, SymbolLoader> libMap = ArrayListMultimap.create(); 1280 1281 // First pass processing the libraries, collecting them by packageName, 1282 // and ignoring the ones that have the same package name as the application 1283 // (since that R class was already created). 1284 1285 for (Pair<File, String> lib : libRFiles) { 1286 String libPackage = lib.getSecond(); 1287 File rText = lib.getFirst(); 1288 1289 if (rText.isFile()) { 1290 // load the lib symbols 1291 SymbolLoader libSymbols = new SymbolLoader(rText); 1292 libSymbols.load(); 1293 1294 // store these symbols by associating them with the package name. 1295 libMap.put(libPackage, libSymbols); 1296 } 1297 } 1298 1299 // now loop on all the package names, merge all the symbols to write, 1300 // and write them 1301 for (String packageName : libMap.keySet()) { 1302 Collection<SymbolLoader> symbols = libMap.get(packageName); 1303 1304 SymbolWriter writer = new SymbolWriter(osOutputPath, packageName, 1305 fullSymbolValues); 1306 for (SymbolLoader symbolLoader : symbols) { 1307 writer.addSymbolsToWrite(symbolLoader); 1308 } 1309 writer.write(); 1310 } 1311 } 1312 } 1313 1314 } catch (IOException e1) { 1315 // something happen while executing the process, 1316 // mark the project and exit 1317 String msg; 1318 String path = array.get(0); 1319 if (!new File(path).exists()) { 1320 msg = String.format(Messages.AAPT_Exec_Error_s, path); 1321 } else { 1322 String description = e1.getLocalizedMessage(); 1323 if (e1.getCause() != null && e1.getCause() != e1) { 1324 description = description + ": " + e1.getCause().getLocalizedMessage(); 1325 } 1326 msg = String.format(Messages.AAPT_Exec_Error_Other_s, description); 1327 } 1328 1329 markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR); 1330 1331 // Add workaround for the Linux problem described here: 1332 // http://developer.android.com/sdk/installing.html#troubleshooting 1333 // There are various posts on StackOverflow elsewhere where people are asking 1334 // about aapt failing to run, so even though this is documented in the 1335 // Troubleshooting section add an error message to help with this 1336 // scenario. 1337 if (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_LINUX 1338 && System.getProperty("os.arch").endsWith("64") //$NON-NLS-1$ //$NON-NLS-2$ 1339 && new File(aaptPath).exists() 1340 && new File("/usr/bin/apt-get").exists()) { //$NON-NLS-1$ 1341 markProject(AdtConstants.MARKER_ADT, 1342 "Hint: On 64-bit systems, make sure the 32-bit libraries are installed: \"sudo apt-get install ia32-libs\" or on some systems, \"sudo apt-get install lib32z1\"", 1343 IMarker.SEVERITY_ERROR); 1344 // Note - this uses SEVERITY_ERROR even though it's really SEVERITY_INFO because 1345 // we want this error message to show up adjacent to the aapt error message 1346 // (and Eclipse sorts by priority) 1347 } 1348 1349 // This interrupts the build. 1350 throw new AbortBuildException(); 1351 } catch (InterruptedException e) { 1352 // we got interrupted waiting for the process to end... 1353 // mark the project and exit 1354 String msg = String.format(Messages.AAPT_Exec_Error_s, array.get(0)); 1355 markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR); 1356 1357 // This interrupts the build. 1358 throw new AbortBuildException(); 1359 } finally { 1360 // we've at least attempted to run aapt, save the fact that we don't have to 1361 // run it again, unless there's a new resource change. 1362 saveProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES, 1363 mMustCompileResources = false); 1364 ResourceManager.clearAaptRequest(project); 1365 } 1366 } 1367 1368 /** 1369 * Creates a relative {@link IPath} from a java package. 1370 * @param javaPackageName the java package. 1371 */ getJavaPackagePath(String javaPackageName)1372 private IPath getJavaPackagePath(String javaPackageName) { 1373 // convert the java package into path 1374 String[] segments = javaPackageName.split(AdtConstants.RE_DOT); 1375 1376 StringBuilder path = new StringBuilder(); 1377 for (String s : segments) { 1378 path.append(AdtConstants.WS_SEP_CHAR); 1379 path.append(s); 1380 } 1381 1382 return new Path(path.toString()); 1383 } 1384 1385 /** 1386 * Returns an {@link IFolder} (located inside the 'gen' source folder), that matches the 1387 * package defined in the manifest. This {@link IFolder} may not actually exist 1388 * (aapt will create it anyway). 1389 * @return the {@link IFolder} that will contain the R class or null if 1390 * the folder was not found. 1391 * @throws CoreException 1392 */ getGenManifestPackageFolder()1393 private IFolder getGenManifestPackageFolder() throws CoreException { 1394 // get the path for the package 1395 IPath packagePath = getJavaPackagePath(mManifestPackage); 1396 1397 // get a folder for this path under the 'gen' source folder, and return it. 1398 // This IFolder may not reference an actual existing folder. 1399 return mGenFolder.getFolder(packagePath); 1400 } 1401 } 1402