1 /* 2 * Copyright (C) 2013 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.wizards.exportgradle; 18 19 import static com.android.SdkConstants.GRADLE_LATEST_VERSION; 20 import static com.android.SdkConstants.GRADLE_PLUGIN_LATEST_VERSION; 21 import static com.android.SdkConstants.GRADLE_PLUGIN_NAME; 22 23 import com.android.SdkConstants; 24 import com.android.annotations.NonNull; 25 import com.android.annotations.Nullable; 26 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper; 27 import com.android.ide.eclipse.adt.internal.sdk.ProjectState; 28 import com.android.ide.eclipse.adt.internal.sdk.Sdk; 29 import com.android.ide.eclipse.adt.io.IFolderWrapper; 30 import com.android.io.IAbstractFile; 31 import com.android.sdklib.io.FileOp; 32 import com.android.xml.AndroidManifest; 33 import com.google.common.base.Charsets; 34 import com.google.common.base.Joiner; 35 import com.google.common.collect.Lists; 36 import com.google.common.io.Closeables; 37 import com.google.common.io.Files; 38 39 import org.eclipse.core.resources.IFile; 40 import org.eclipse.core.resources.IProject; 41 import org.eclipse.core.resources.IWorkspaceRoot; 42 import org.eclipse.core.resources.ResourcesPlugin; 43 import org.eclipse.core.runtime.CoreException; 44 import org.eclipse.core.runtime.IPath; 45 import org.eclipse.core.runtime.IProgressMonitor; 46 import org.eclipse.core.runtime.IStatus; 47 import org.eclipse.core.runtime.Path; 48 import org.eclipse.core.runtime.SubMonitor; 49 import org.eclipse.jdt.core.IClasspathEntry; 50 import org.eclipse.jdt.core.IJavaProject; 51 import org.eclipse.jdt.core.JavaCore; 52 import org.eclipse.osgi.util.NLS; 53 import org.eclipse.swt.widgets.Shell; 54 55 import java.io.ByteArrayInputStream; 56 import java.io.File; 57 import java.io.FileInputStream; 58 import java.io.FileOutputStream; 59 import java.io.IOException; 60 import java.io.InputStream; 61 import java.util.ArrayList; 62 import java.util.Collection; 63 import java.util.Comparator; 64 import java.util.List; 65 import java.util.Properties; 66 import java.util.Set; 67 import java.util.TreeSet; 68 69 /** 70 * Creates build.gradle and settings.gradle files for a set of projects. 71 * <p> 72 * Based on {@link org.eclipse.ant.internal.ui.datatransfer.BuildFileCreator} 73 */ 74 public class BuildFileCreator { 75 static final String BUILD_FILE = "build.gradle"; //$NON-NLS-1$ 76 static final String SETTINGS_FILE = "settings.gradle"; //$NON-NLS-1$ 77 private static final String NEWLINE = System.getProperty("line.separator"); //$NON-NLS-1$ 78 private static final String GRADLE_WRAPPER_LOCATION = 79 "tools/templates/gradle/wrapper"; //$NON-NLS-1$ 80 static final String PLUGIN_CLASSPATH = 81 "classpath '" + GRADLE_PLUGIN_NAME + GRADLE_PLUGIN_LATEST_VERSION + "'"; //$NON-NLS-1$ 82 static final String MAVEN_REPOSITORY = "mavenCentral()"; //$NON-NLS-1$ 83 84 private static final String[] GRADLE_WRAPPER_FILES = new String[] { 85 "gradlew", //$NON-NLS-1$ 86 "gradlew.bat", //$NON-NLS-1$ 87 "gradle/wrapper/gradle-wrapper.jar", //$NON-NLS-1$ 88 "gradle/wrapper/gradle-wrapper.properties" //$NON-NLS-1$ 89 }; 90 91 private static final Comparator<IFile> FILE_COMPARATOR = new Comparator<IFile>() { 92 @Override 93 public int compare(IFile o1, IFile o2) { 94 return o1.toString().compareTo(o2.toString()); 95 } 96 }; 97 98 private final GradleModule mModule; 99 private final StringBuilder mBuildFile = new StringBuilder(); 100 101 /** 102 * Create buildfile for the projects. 103 * 104 * @param shell parent instance for dialogs 105 * @return project names for which buildfiles were created 106 * @throws InterruptedException thrown when user cancels task 107 */ createBuildFiles( @onNull ProjectSetupBuilder builder, @NonNull Shell shell, @NonNull IProgressMonitor pm)108 public static void createBuildFiles( 109 @NonNull ProjectSetupBuilder builder, 110 @NonNull Shell shell, 111 @NonNull IProgressMonitor pm) { 112 113 File gradleLocation = new File(Sdk.getCurrent().getSdkOsLocation(), GRADLE_WRAPPER_LOCATION); 114 SubMonitor localmonitor = null; 115 116 try { 117 // See if we have a Gradle wrapper in the SDK templates directory. If so, we can copy 118 // it over. 119 boolean hasGradleWrapper = true; 120 for (File wrapperFile : getGradleWrapperFiles(gradleLocation)) { 121 if (!wrapperFile.exists()) { 122 hasGradleWrapper = false; 123 } 124 } 125 126 Collection<GradleModule> modules = builder.getModules(); 127 boolean multiModules = modules.size() > 1; 128 129 // determine files to create/change 130 List<IFile> files = new ArrayList<IFile>(); 131 132 // add the build.gradle file for all modules. 133 for (GradleModule module : modules) { 134 // build.gradle file 135 IFile file = module.getProject().getFile(BuildFileCreator.BUILD_FILE); 136 files.add(file); 137 } 138 139 // get the commonRoot for all modules. If only one module, this returns the path 140 // of the project. 141 IPath commonRoot = builder.getCommonRoot(); 142 143 IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); 144 IPath workspaceLocation = workspaceRoot.getLocation(); 145 146 IPath relativePath = commonRoot.makeRelativeTo(workspaceLocation); 147 // if makeRelativePath to returns the same path, then commonRoot is not in the 148 // workspace. 149 boolean rootInWorkspace = !relativePath.equals(commonRoot); 150 // we only care if the root is a workspace project. if it's the workspace folder itself, 151 // then the files won't be handled by the workspace. 152 rootInWorkspace = rootInWorkspace && relativePath.segmentCount() > 0; 153 154 File settingsFile = new File(commonRoot.toFile(), SETTINGS_FILE); 155 156 // more than one modules -> generate settings.gradle 157 if (multiModules && rootInWorkspace) { 158 159 // Locate the settings.gradle file and add it to the changed files list 160 IPath settingsGradle = Path.fromOSString(settingsFile.getAbsolutePath()); 161 162 // different path, means commonRoot is inside the workspace, which means we have 163 // to add settings.gradle and wrapper files to the list of files to add. 164 IFile iFile = workspaceRoot.getFile(settingsGradle); 165 if (iFile != null) { 166 files.add(iFile); 167 } 168 } 169 170 // Gradle wrapper files 171 if (hasGradleWrapper && rootInWorkspace) { 172 // See if there already wrapper files there and only mark nonexistent ones for 173 // creation. 174 for (File wrapperFile : getGradleWrapperFiles(commonRoot.toFile())) { 175 if (!wrapperFile.exists()) { 176 IPath path = Path.fromOSString(wrapperFile.getAbsolutePath()); 177 IFile file = workspaceRoot.getFile(path); 178 files.add(file); 179 } 180 } 181 } 182 183 ExportStatus status = new ExportStatus(); 184 builder.setStatus(status); 185 186 // Trigger checkout of changed files 187 Set<IFile> confirmedFiles = validateEdit(files, status, shell); 188 189 if (status.hasError()) { 190 return; 191 } 192 193 // Now iterate over all the modules and generate the build files. 194 localmonitor = SubMonitor.convert(pm, ExportMessages.PageTitle, 195 confirmedFiles.size()); 196 List<String> projectSettingsPath = Lists.newArrayList(); 197 for (GradleModule currentModule : modules) { 198 IProject moduleProject = currentModule.getProject(); 199 200 IFile file = moduleProject.getFile(BuildFileCreator.BUILD_FILE); 201 if (!confirmedFiles.contains(file)) { 202 continue; 203 } 204 205 localmonitor.setTaskName(NLS.bind(ExportMessages.FileStatusMessage, 206 moduleProject.getName())); 207 208 ProjectState projectState = Sdk.getProjectState(moduleProject); 209 BuildFileCreator instance = new BuildFileCreator(currentModule, shell); 210 if (projectState != null) { 211 // This is an Android project 212 if (!multiModules) { 213 instance.appendBuildScript(); 214 } 215 instance.appendHeader(projectState.isLibrary()); 216 instance.appendDependencies(); 217 instance.startAndroidTask(projectState); 218 //instance.appendDefaultConfig(); 219 instance.createAndroidSourceSets(); 220 instance.finishAndroidTask(); 221 } else { 222 // This is a plain Java project 223 instance.appendJavaHeader(); 224 instance.createJavaSourceSets(); 225 } 226 227 try { 228 // Write the build file 229 String buildfile = instance.mBuildFile.toString(); 230 InputStream is = 231 new ByteArrayInputStream(buildfile.getBytes("UTF-8")); //$NON-NLS-1$ 232 if (file.exists()) { 233 file.setContents(is, true, true, null); 234 } else { 235 file.create(is, true, null); 236 } 237 } catch (Exception e) { 238 status.addFileStatus(ExportStatus.FileStatus.IO_FAILURE, 239 file.getLocation().toFile()); 240 status.setErrorMessage(e.getMessage()); 241 return; 242 } 243 244 if (localmonitor.isCanceled()) { 245 return; 246 } 247 localmonitor.worked(1); 248 249 // get the project path to add it to the settings.gradle. 250 projectSettingsPath.add(currentModule.getPath()); 251 } 252 253 // write the settings file. 254 if (multiModules) { 255 try { 256 writeGradleSettingsFile(settingsFile, projectSettingsPath); 257 } catch (IOException e) { 258 status.addFileStatus(ExportStatus.FileStatus.IO_FAILURE, settingsFile); 259 status.setErrorMessage(e.getMessage()); 260 return; 261 } 262 File mainBuildFile = new File(commonRoot.toFile(), BUILD_FILE); 263 try { 264 writeRootBuildGradle(mainBuildFile); 265 } catch (IOException e) { 266 status.addFileStatus(ExportStatus.FileStatus.IO_FAILURE, mainBuildFile); 267 status.setErrorMessage(e.getMessage()); 268 return; 269 } 270 } 271 272 // finally write the wrapper 273 // TODO check we can based on where it is 274 if (hasGradleWrapper) { 275 copyGradleWrapper(gradleLocation, commonRoot.toFile(), status); 276 if (status.hasError()) { 277 return; 278 } 279 } 280 281 } finally { 282 if (localmonitor != null && !localmonitor.isCanceled()) { 283 localmonitor.done(); 284 } 285 if (pm != null) { 286 pm.done(); 287 } 288 } 289 } 290 291 /** 292 * @param GradleModule create buildfile for this project 293 * @param shell parent instance for dialogs 294 */ BuildFileCreator(GradleModule module, Shell shell)295 private BuildFileCreator(GradleModule module, Shell shell) { 296 mModule = module; 297 } 298 299 /** 300 * Return the files that comprise the Gradle wrapper as a collection of {@link File} instances. 301 * @param root 302 * @return 303 */ getGradleWrapperFiles(File root)304 private static List<File> getGradleWrapperFiles(File root) { 305 List<File> files = new ArrayList<File>(GRADLE_WRAPPER_FILES.length); 306 for (String file : GRADLE_WRAPPER_FILES) { 307 files.add(new File(root, file)); 308 } 309 return files; 310 } 311 312 /** 313 * Copy the Gradle wrapper files from one directory to another. 314 */ copyGradleWrapper(File from, File to, ExportStatus status)315 private static void copyGradleWrapper(File from, File to, ExportStatus status) { 316 for (String file : GRADLE_WRAPPER_FILES) { 317 File dest = new File(to, file); 318 try { 319 File src = new File(from, file); 320 dest.getParentFile().mkdirs(); 321 new FileOp().copyFile(src, dest); 322 323 if (src.getName().equals(GRADLE_PROPERTIES)) { 324 updateGradleDistributionUrl(GRADLE_LATEST_VERSION, dest); 325 } 326 dest.setExecutable(src.canExecute()); 327 status.addFileStatus(ExportStatus.FileStatus.OK, dest); 328 } catch (IOException e) { 329 status.addFileStatus(ExportStatus.FileStatus.IO_FAILURE, dest); 330 return; 331 } 332 } 333 } 334 335 /** 336 * Outputs boilerplate buildscript information common to all Gradle build files. 337 */ appendBuildScript()338 private void appendBuildScript() { 339 appendBuildScript(mBuildFile); 340 } 341 342 /** 343 * Outputs boilerplate header information common to all Gradle build files. 344 */ appendBuildScript(StringBuilder builder)345 private static void appendBuildScript(StringBuilder builder) { 346 builder.append("buildscript {\n"); //$NON-NLS-1$ 347 builder.append(" repositories {\n"); //$NON-NLS-1$ 348 builder.append(" " + MAVEN_REPOSITORY + "\n"); //$NON-NLS-1$ 349 builder.append(" }\n"); //$NON-NLS-1$ 350 builder.append(" dependencies {\n"); //$NON-NLS-1$ 351 builder.append(" " + PLUGIN_CLASSPATH + "\n"); //$NON-NLS-1$ 352 builder.append(" }\n"); //$NON-NLS-1$ 353 builder.append("}\n"); //$NON-NLS-1$ 354 } 355 356 /** 357 * Outputs boilerplate header information common to all Gradle build files. 358 */ appendHeader(boolean isLibrary)359 private void appendHeader(boolean isLibrary) { 360 if (isLibrary) { 361 mBuildFile.append("apply plugin: 'android-library'\n"); //$NON-NLS-1$ 362 } else { 363 mBuildFile.append("apply plugin: 'android'\n"); //$NON-NLS-1$ 364 } 365 mBuildFile.append("\n"); //$NON-NLS-1$ 366 } 367 368 /** 369 * Outputs a block which sets up library and project dependencies. 370 */ appendDependencies()371 private void appendDependencies() { 372 mBuildFile.append("dependencies {\n"); //$NON-NLS-1$ 373 374 // first the local jars. 375 // TODO: Fix 376 mBuildFile.append(" compile fileTree(dir: 'libs', include: '*.jar')\n"); //$NON-NLS-1$ 377 378 for (GradleModule dep : mModule.getDependencies()) { 379 mBuildFile.append(" compile project('" + dep.getPath() + "')\n"); //$NON-NLS-1$ //$NON-NLS-2$ 380 } 381 382 mBuildFile.append("}\n"); //$NON-NLS-1$ 383 mBuildFile.append("\n"); //$NON-NLS-1$ 384 } 385 386 /** 387 * Outputs the beginning of an Android task in the build file. 388 */ startAndroidTask(ProjectState projectState)389 private void startAndroidTask(ProjectState projectState) { 390 int buildApi = projectState.getTarget().getVersion().getApiLevel(); 391 String toolsVersion = projectState.getTarget().getBuildToolInfo().getRevision().toString(); 392 mBuildFile.append("android {\n"); //$NON-NLS-1$ 393 mBuildFile.append(" compileSdkVersion " + buildApi + "\n"); //$NON-NLS-1$ 394 mBuildFile.append(" buildToolsVersion \"" + toolsVersion + "\"\n"); //$NON-NLS-1$ 395 mBuildFile.append("\n"); //$NON-NLS-1$ 396 397 try { 398 IJavaProject javaProject = BaseProjectHelper.getJavaProject(projectState.getProject()); 399 // otherwise we check source compatibility 400 String source = javaProject.getOption(JavaCore.COMPILER_SOURCE, true); 401 if (JavaCore.VERSION_1_7.equals(source)) { 402 mBuildFile.append( 403 " compileOptions {\n" + //$NON-NLS-1$ 404 " sourceCompatibility JavaVersion.VERSION_1_7\n" + //$NON-NLS-1$ 405 " targetCompatibility JavaVersion.VERSION_1_7\n" + //$NON-NLS-1$ 406 " }\n" + //$NON-NLS-1$ 407 "\n"); //$NON-NLS-1$ 408 } 409 } catch (CoreException e) { 410 // Ignore compliance level, go with default 411 } 412 } 413 414 /** 415 * Outputs a sourceSets block to the Android task that locates all of the various source 416 * subdirectories in the project. 417 */ createAndroidSourceSets()418 private void createAndroidSourceSets() { 419 IFolderWrapper projectFolder = new IFolderWrapper(mModule.getProject()); 420 IAbstractFile mManifestFile = AndroidManifest.getManifest(projectFolder); 421 if (mManifestFile == null) { 422 return; 423 } 424 List<String> srcDirs = new ArrayList<String>(); 425 for (IClasspathEntry entry : mModule.getJavaProject().readRawClasspath()) { 426 if (entry.getEntryKind() != IClasspathEntry.CPE_SOURCE || 427 SdkConstants.FD_GEN_SOURCES.equals(entry.getPath().lastSegment())) { 428 continue; 429 } 430 IPath path = entry.getPath().removeFirstSegments(1); 431 srcDirs.add("'" + path.toOSString() + "'"); //$NON-NLS-1$ 432 } 433 434 String srcPaths = Joiner.on(",").join(srcDirs); 435 436 mBuildFile.append(" sourceSets {\n"); //$NON-NLS-1$ 437 mBuildFile.append(" main {\n"); //$NON-NLS-1$ 438 mBuildFile.append(" manifest.srcFile '" + SdkConstants.FN_ANDROID_MANIFEST_XML + "'\n"); //$NON-NLS-1$ 439 mBuildFile.append(" java.srcDirs = [" + srcPaths + "]\n"); //$NON-NLS-1$ 440 mBuildFile.append(" resources.srcDirs = [" + srcPaths + "]\n"); //$NON-NLS-1$ 441 mBuildFile.append(" aidl.srcDirs = [" + srcPaths + "]\n"); //$NON-NLS-1$ 442 mBuildFile.append(" renderscript.srcDirs = [" + srcPaths + "]\n"); //$NON-NLS-1$ 443 mBuildFile.append(" res.srcDirs = ['res']\n"); //$NON-NLS-1$ 444 mBuildFile.append(" assets.srcDirs = ['assets']\n"); //$NON-NLS-1$ 445 mBuildFile.append(" }\n"); //$NON-NLS-1$ 446 mBuildFile.append("\n"); //$NON-NLS-1$ 447 mBuildFile.append(" // Move the tests to tests/java, tests/res, etc...\n"); //$NON-NLS-1$ 448 mBuildFile.append(" instrumentTest.setRoot('tests')\n"); //$NON-NLS-1$ 449 if (srcDirs.contains("'src'")) { 450 mBuildFile.append("\n"); //$NON-NLS-1$ 451 mBuildFile.append(" // Move the build types to build-types/<type>\n"); //$NON-NLS-1$ 452 mBuildFile.append(" // For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...\n"); //$NON-NLS-1$ 453 mBuildFile.append(" // This moves them out of them default location under src/<type>/... which would\n"); //$NON-NLS-1$ 454 mBuildFile.append(" // conflict with src/ being used by the main source set.\n"); //$NON-NLS-1$ 455 mBuildFile.append(" // Adding new build types or product flavors should be accompanied\n"); //$NON-NLS-1$ 456 mBuildFile.append(" // by a similar customization.\n"); //$NON-NLS-1$ 457 mBuildFile.append(" debug.setRoot('build-types/debug')\n"); //$NON-NLS-1$ 458 mBuildFile.append(" release.setRoot('build-types/release')\n"); //$NON-NLS-1$ 459 } 460 mBuildFile.append(" }\n"); //$NON-NLS-1$ 461 } 462 463 /** 464 * Outputs the completion of the Android task in the build file. 465 */ finishAndroidTask()466 private void finishAndroidTask() { 467 mBuildFile.append("}\n"); //$NON-NLS-1$ 468 } 469 470 /** 471 * Outputs a boilerplate header for non-Android projects 472 */ appendJavaHeader()473 private void appendJavaHeader() { 474 mBuildFile.append("apply plugin: 'java'\n"); //$NON-NLS-1$ 475 } 476 477 /** 478 * Outputs a sourceSets block for non-Android projects to locate the source directories. 479 */ createJavaSourceSets()480 private void createJavaSourceSets() { 481 List<String> dirs = new ArrayList<String>(); 482 for (IClasspathEntry entry : mModule.getJavaProject().readRawClasspath()) { 483 if (entry.getEntryKind() != IClasspathEntry.CPE_SOURCE) { 484 continue; 485 } 486 IPath path = entry.getPath().removeFirstSegments(1); 487 dirs.add("'" + path.toOSString() + "'"); //$NON-NLS-1$ 488 } 489 490 String srcPaths = Joiner.on(",").join(dirs); 491 492 mBuildFile.append("sourceSets {\n"); //$NON-NLS-1$ 493 mBuildFile.append(" main.java.srcDirs = [" + srcPaths + "]\n"); //$NON-NLS-1$ 494 mBuildFile.append(" main.resources.srcDirs = [" + srcPaths + "]\n"); //$NON-NLS-1$ 495 mBuildFile.append(" test.java.srcDirs = ['tests/java']\n"); //$NON-NLS-1$ 496 mBuildFile.append(" test.resources.srcDirs = ['tests/resources']\n"); //$NON-NLS-1$ 497 mBuildFile.append("}\n"); //$NON-NLS-1$ 498 } 499 500 /** 501 * Merges the new subproject dependencies into the settings.gradle file if it already exists, 502 * and creates one if it does not. 503 * @throws IOException 504 */ writeGradleSettingsFile(File settingsFile, List<String> projectPaths)505 private static void writeGradleSettingsFile(File settingsFile, List<String> projectPaths) 506 throws IOException { 507 StringBuilder contents = new StringBuilder(); 508 for (String path : projectPaths) { 509 contents.append("include '").append(path).append("'\n"); //$NON-NLS-1$ //$NON-NLS-2$ 510 } 511 512 Files.write(contents.toString(), settingsFile, Charsets.UTF_8); 513 } 514 writeRootBuildGradle(File buildFile)515 private static void writeRootBuildGradle(File buildFile) throws IOException { 516 StringBuilder sb = new StringBuilder( 517 "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n"); 518 519 appendBuildScript(sb); 520 521 Files.write(sb.toString(), buildFile, Charsets.UTF_8); 522 } 523 524 /** 525 * Request write access to given files. Depending on the version control 526 * plug-in opens a confirm checkout dialog. 527 * 528 * @param shell 529 * parent instance for dialogs 530 * @return <code>IFile</code> objects for which user confirmed checkout 531 * @throws CoreException 532 * thrown if project is under version control, but not connected 533 */ validateEdit( @onNull List<IFile> files, @NonNull ExportStatus exportStatus, @NonNull Shell shell)534 static Set<IFile> validateEdit( 535 @NonNull List<IFile> files, 536 @NonNull ExportStatus exportStatus, 537 @NonNull Shell shell) { 538 Set<IFile> confirmedFiles = new TreeSet<IFile>(FILE_COMPARATOR); 539 if (files.size() == 0) { 540 return confirmedFiles; 541 } 542 IStatus status = (files.get(0)).getWorkspace().validateEdit( 543 files.toArray(new IFile[files.size()]), shell); 544 if (status.isMultiStatus() && status.getChildren().length > 0) { 545 for (int i = 0; i < status.getChildren().length; i++) { 546 IStatus statusChild = status.getChildren()[i]; 547 if (statusChild.isOK()) { 548 confirmedFiles.add(files.get(i)); 549 } else { 550 exportStatus.addFileStatus( 551 ExportStatus.FileStatus.VCS_FAILURE, 552 files.get(i).getLocation().toFile()); 553 } 554 } 555 } else if (status.isOK()) { 556 confirmedFiles.addAll(files); 557 } 558 if (status.getSeverity() == IStatus.ERROR) { 559 // not possible to checkout files: not connected to version 560 // control plugin or hijacked files and made read-only, so 561 // collect error messages provided by validator and re-throw 562 StringBuffer message = new StringBuffer(status.getPlugin() + ": " //$NON-NLS-1$ 563 + status.getMessage() + NEWLINE); 564 if (status.isMultiStatus()) { 565 for (int i = 0; i < status.getChildren().length; i++) { 566 IStatus statusChild = status.getChildren()[i]; 567 message.append(statusChild.getMessage() + NEWLINE); 568 } 569 } 570 String s = message.toString(); 571 exportStatus.setErrorMessage(s); 572 } 573 574 return confirmedFiles; 575 } 576 577 // ------------------------------------------------------------------------------- 578 // Fix gradle wrapper version. This code is from GradleUtil in the Studio plugin: 579 // ------------------------------------------------------------------------------- 580 581 private static final String GRADLE_PROPERTIES = "gradle-wrapper.properties"; 582 private static final String GRADLEW_PROPERTIES_PATH = 583 "gradle" + File.separator + "wrapper" + File.separator + GRADLE_PROPERTIES; 584 private static final String GRADLEW_DISTRIBUTION_URL_PROPERTY_NAME = "distributionUrl"; 585 586 @NonNull getGradleWrapperPropertiesFilePath(@onNull File projectRootDir)587 private static File getGradleWrapperPropertiesFilePath(@NonNull File projectRootDir) { 588 return new File(projectRootDir, GRADLEW_PROPERTIES_PATH); 589 } 590 591 @Nullable findWrapperPropertiesFile(@onNull File projectRootDir)592 public static File findWrapperPropertiesFile(@NonNull File projectRootDir) { 593 File wrapperPropertiesFile = getGradleWrapperPropertiesFilePath(projectRootDir); 594 return wrapperPropertiesFile.isFile() ? wrapperPropertiesFile : null; 595 } 596 updateGradleDistributionUrl( @onNull String gradleVersion, @NonNull File propertiesFile)597 private static boolean updateGradleDistributionUrl( 598 @NonNull String gradleVersion, 599 @NonNull File propertiesFile) throws IOException { 600 Properties properties = loadGradleWrapperProperties(propertiesFile); 601 String gradleDistributionUrl = getGradleDistributionUrl(gradleVersion, false); 602 String property = properties.getProperty(GRADLEW_DISTRIBUTION_URL_PROPERTY_NAME); 603 if (property != null 604 && (property.equals(gradleDistributionUrl) || property 605 .equals(getGradleDistributionUrl(gradleVersion, true)))) { 606 return false; 607 } 608 properties.setProperty(GRADLEW_DISTRIBUTION_URL_PROPERTY_NAME, gradleDistributionUrl); 609 FileOutputStream out = null; 610 try { 611 out = new FileOutputStream(propertiesFile); 612 properties.store(out, null); 613 return true; 614 } finally { 615 Closeables.close(out, true); 616 } 617 } 618 619 @NonNull loadGradleWrapperProperties(@onNull File propertiesFile)620 private static Properties loadGradleWrapperProperties(@NonNull File propertiesFile) 621 throws IOException { 622 Properties properties = new Properties(); 623 FileInputStream fileInputStream = null; 624 try { 625 fileInputStream = new FileInputStream(propertiesFile); 626 properties.load(fileInputStream); 627 return properties; 628 } finally { 629 Closeables.close(fileInputStream, true); 630 } 631 } 632 633 @NonNull getGradleDistributionUrl(@onNull String gradleVersion, boolean binOnly)634 private static String getGradleDistributionUrl(@NonNull String gradleVersion, 635 boolean binOnly) { 636 String suffix = binOnly ? "bin" : "all"; 637 return String.format("http://services.gradle.org/distributions/gradle-%1$s-" + suffix 638 + ".zip", gradleVersion); 639 } 640 } 641