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