1package org.robolectric.internal; 2 3import java.io.File; 4import java.net.URL; 5import java.nio.file.Files; 6import java.nio.file.Path; 7import java.nio.file.Paths; 8import org.robolectric.annotation.Config; 9import org.robolectric.res.FileFsFile; 10import org.robolectric.util.Logger; 11import org.robolectric.util.ReflectionHelpers; 12 13public class GradleManifestFactory implements ManifestFactory { 14 @Override 15 public ManifestIdentifier identify(Config config) { 16 if (config.constants() == Void.class) { 17 Logger.error("Field 'constants' not specified in @Config annotation"); 18 Logger.error("This is required when using Robolectric with Gradle!"); 19 throw new RuntimeException("No 'constants' field in @Config annotation!"); 20 } 21 22 final String buildOutputDir = getBuildOutputDir(config); 23 final String type = getType(config); 24 final String flavor = getFlavor(config); 25 final String abiSplit = getAbiSplit(config); 26 final String packageName = config.packageName().isEmpty() 27 ? config.constants().getPackage().getName() 28 : config.packageName(); 29 30 final FileFsFile res; 31 final FileFsFile assets; 32 final FileFsFile manifest; 33 34 if (FileFsFile.from(buildOutputDir, "data-binding-layout-out").exists()) { 35 // Android gradle plugin 1.5.0+ puts the merged layouts in data-binding-layout-out. 36 // https://github.com/robolectric/robolectric/issues/2143 37 res = FileFsFile.from(buildOutputDir, "data-binding-layout-out", flavor, type); 38 } else if (FileFsFile.from(buildOutputDir, "res", "merged").exists()) { 39 // res/merged added in Android Gradle plugin 1.3-beta1 40 res = FileFsFile.from(buildOutputDir, "res", "merged", flavor, type); 41 } else if (FileFsFile.from(buildOutputDir, "res").exists()) { 42 res = FileFsFile.from(buildOutputDir, "res", flavor, type); 43 } else { 44 res = FileFsFile.from(buildOutputDir, "bundles", flavor, type, "res"); 45 } 46 47 if (!Config.DEFAULT_ASSET_FOLDER.equals(config.assetDir()) 48 && FileFsFile.from(buildOutputDir, config.assetDir()).exists()) { 49 assets = FileFsFile.from(buildOutputDir, config.assetDir()); 50 } else if (FileFsFile.from(buildOutputDir, "assets").exists()) { 51 assets = FileFsFile.from(buildOutputDir, "assets", flavor, type); 52 } else { 53 assets = FileFsFile.from(buildOutputDir, "bundles", flavor, type, "assets"); 54 } 55 56 String manifestName = config.manifest(); 57 URL manifestUrl = getClass().getClassLoader().getResource(manifestName); 58 if (manifestUrl != null && manifestUrl.getProtocol().equals("file")) { 59 manifest = FileFsFile.from(manifestUrl.getPath()); 60 } else if (FileFsFile.from(buildOutputDir, "manifests", "full").exists()) { 61 manifest = FileFsFile.from(buildOutputDir, "manifests", "full", flavor, abiSplit, type, manifestName); 62 } else if (FileFsFile.from(buildOutputDir, "manifests", "aapt").exists()) { 63 // Android gradle plugin 2.2.0+ can put library manifest files inside of "aapt" instead of "full" 64 manifest = FileFsFile.from(buildOutputDir, "manifests", "aapt", flavor, abiSplit, type, manifestName); 65 } else { 66 manifest = FileFsFile.from(buildOutputDir, "bundles", flavor, abiSplit, type, manifestName); 67 } 68 69 return new ManifestIdentifier(manifest, res, assets, packageName, null); 70 } 71 72 private static String getBuildOutputDir(Config config) { 73 Path buildDir = Paths.get(config.buildDir(), "intermediates"); 74 if (!Files.exists(buildDir)) { 75 // By default build dir is a relative path. However, the build dir lookup may fail if the 76 // working directory of the test configuration in Android Studio is not set to the module 77 // root directory (e.g it is set to the entire project root directory). Attempt to locate it 78 // relative to the constants class, which is generated in the build output directory. 79 String moduleRoot = config.constants().getResource("").toString().replace("file:", ""); 80 int idx = moduleRoot.lastIndexOf(File.separator + "intermediates"); 81 if (idx > 0) { 82 buildDir = Paths.get(moduleRoot.substring(0, idx), "intermediates"); 83 } else { 84 Logger.error("Failed to locate build dir"); 85 } 86 } 87 return buildDir.toString(); 88 } 89 90 private static String getType(Config config) { 91 try { 92 return ReflectionHelpers.getStaticField(config.constants(), "BUILD_TYPE"); 93 } catch (Throwable e) { 94 return null; 95 } 96 } 97 98 private static String getFlavor(Config config) { 99 try { 100 return ReflectionHelpers.getStaticField(config.constants(), "FLAVOR"); 101 } catch (Throwable e) { 102 return null; 103 } 104 } 105 106 private static String getAbiSplit(Config config) { 107 try { 108 return config.abiSplit(); 109 } catch (Throwable e) { 110 return null; 111 } 112 } 113} 114