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