/// <summary> /// Explodes a single aar file. This is done by calling the /// JDK "jar" command, then moving the classes.jar file. /// </summary> /// <param name="dir">The directory to unpack / explode the AAR to. If antProject is true /// the ant project will be located in Path.Combine(dir, Path.GetFileName(aarFile)).</param> /// <param name="aarFile">Aar file to explode.</param> /// <param name="antProject">true to explode into an Ant style project or false /// to repack the processed AAR as a new AAR.</param> /// <param name="abis">ABIs in the AAR or null if it's universal.</param> /// <returns>true if successful, false otherwise.</returns> internal virtual bool ProcessAar(string dir, string aarFile, bool antProject, out AndroidAbis abis) { PlayServicesResolver.Log(String.Format("ProcessAar {0} {1} antProject={2}", dir, aarFile, antProject), level: LogLevel.Verbose); abis = null; string workingDir = Path.Combine(dir, Path.GetFileNameWithoutExtension(aarFile)); FileUtils.DeleteExistingFileOrDirectory(workingDir); Directory.CreateDirectory(workingDir); if (!PlayServicesResolver.ExtractZip(aarFile, null, workingDir)) { return(false); } PlayServicesResolver.ReplaceVariablesInAndroidManifest( Path.Combine(workingDir, "AndroidManifest.xml"), UnityCompat.ApplicationId, new Dictionary <string, string>()); string nativeLibsDir = null; if (antProject) { // Create the libs directory to store the classes.jar and non-Java shared // libraries. string libDir = Path.Combine(workingDir, "libs"); nativeLibsDir = libDir; Directory.CreateDirectory(libDir); // Move the classes.jar file to libs. string classesFile = Path.Combine(workingDir, "classes.jar"); string targetClassesFile = Path.Combine(libDir, Path.GetFileName(classesFile)); if (File.Exists(targetClassesFile)) { File.Delete(targetClassesFile); } if (File.Exists(classesFile)) { File.Move(classesFile, targetClassesFile); } else { // Generate an empty classes.jar file. string temporaryDirectory = CreateTemporaryDirectory(); if (temporaryDirectory == null) { return(false); } if (!ArchiveAar(targetClassesFile, temporaryDirectory)) { return(false); } } } // Copy non-Java shared libraries (.so) files from the "jni" directory into the // lib directory so that Unity's legacy (Ant-like) build system includes them in the // built APK. string jniLibDir = Path.Combine(workingDir, "jni"); nativeLibsDir = nativeLibsDir ?? jniLibDir; if (Directory.Exists(jniLibDir)) { var abisInArchive = AarDirectoryFindAbis(workingDir); if (jniLibDir != nativeLibsDir) { FileUtils.CopyDirectory(jniLibDir, nativeLibsDir); FileUtils.DeleteExistingFileOrDirectory(jniLibDir); } if (abisInArchive != null) { // Remove shared libraries for all ABIs that are not required for the selected // ABIs. var activeAbisSet = AndroidAbis.Current.ToSet(); var abisInArchiveSet = abisInArchive.ToSet(); var abisInArchiveToRemoveSet = new HashSet <string>(abisInArchiveSet); abisInArchiveToRemoveSet.ExceptWith(activeAbisSet); Func <IEnumerable <string>, string> setToString = (setToConvert) => { return(String.Join(", ", (new List <string>(setToConvert)).ToArray())); }; PlayServicesResolver.Log( String.Format( "Target ABIs [{0}], ABIs [{1}] in {2}, will remove [{3}] ABIs", setToString(activeAbisSet), setToString(abisInArchiveSet), aarFile, setToString(abisInArchiveToRemoveSet)), level: LogLevel.Verbose); foreach (var abiToRemove in abisInArchiveToRemoveSet) { abisInArchiveSet.Remove(abiToRemove); FileUtils.DeleteExistingFileOrDirectory(Path.Combine(nativeLibsDir, abiToRemove)); } abis = new AndroidAbis(abisInArchiveSet); } } if (antProject) { // Create the project.properties file which indicates to // Unity that this directory is a plugin. string projectProperties = Path.Combine(workingDir, "project.properties"); if (!File.Exists(projectProperties)) { File.WriteAllLines(projectProperties, new [] { "# Project target.", "target=android-9", "android.library=true" }); } PlayServicesResolver.Log(String.Format("Replacing {0} with {1}", aarFile, workingDir), level: LogLevel.Verbose); // Clean up the aar file. FileUtils.DeleteExistingFileOrDirectory(Path.GetFullPath(aarFile)); // Add a tracking label to the exploded files. PlayServicesResolver.LabelAssets(new [] { workingDir }); } else { // Add a tracking label to the exploded files just in-case packaging fails. PlayServicesResolver.LabelAssets(new [] { workingDir }); PlayServicesResolver.Log(String.Format("Repacking {0} from {1}", aarFile, workingDir), level: LogLevel.Verbose); // Create a new AAR file. FileUtils.DeleteExistingFileOrDirectory(Path.GetFullPath(aarFile)); if (!ArchiveAar(aarFile, workingDir)) { return(false); } // Clean up the exploded directory. FileUtils.DeleteExistingFileOrDirectory(workingDir); } return(true); }
// Private method to avoid too deeply nested code in "DoResolution". private void GradleResolve(AndroidSdkPackageCollection packages, PlayServicesSupport svcSupport, string destinationDirectory, System.Action resolutionComplete) { string errorOutro = "make sure you have the latest version of this plugin and if you " + "still get this error, report it in a a bug here:\n" + "https://github.com/googlesamples/unity-jar-resolver/issues\n"; string errorIntro = null; int targetSdkVersion = UnityCompat.GetAndroidTargetSDKVersion(); string buildToolsVersion = null; if (targetSdkVersion < 0) { // A value of -1 means the targetSDK Version enum returned "Auto" // instead of an actual version, so it's up to us to actually figure it out. targetSdkVersion = GetLatestInstalledAndroidPlatformVersion(packages); PlayServicesResolver.Log( String.Format("TargetSDK is set to Auto-detect, and the latest Platform has " + "been detected as: android-{0}", targetSdkVersion), level: LogLevel.Verbose); errorIntro = String.Format("The Target SDK is set to automatically pick the highest " + "installed platform in the Android Player Settings, which appears to be " + "\"android-{0}\". This requires build-tools with at least the same version, " + "however ", targetSdkVersion); } else { errorIntro = String.Format("The target SDK version is set in the Android Player " + "Settings to \"android-{0}\" which requires build tools with " + "at least the same version, however ", targetSdkVersion); } // You can use a higher version of the build-tools than your compileSdkVersion, in order // to pick up new/better compiler while not changing what you build your app against. --Xav // https://stackoverflow.com/a/24523113 // Implicitly Xav is also saying, you can't use a build tool version less than the // platform level you're building. This is backed up from testing. if (targetSdkVersion > TESTED_BUILD_TOOLS_VERSION_MAJOR) { buildToolsVersion = GetLatestMinorBuildToolsVersion(packages, targetSdkVersion); if (buildToolsVersion == null) { PlayServicesResolver.Log( errorIntro + String.Format("no build-tools are available " + "at this level in the sdk manager. This plugin has been tested with " + "platforms up to android-{0} using build-tools {0}.{1}.{2}. You can try " + "selecting a lower targetSdkVersion in the Android Player Settings. Please ", TESTED_BUILD_TOOLS_VERSION_MAJOR, TESTED_BUILD_TOOLS_VERSION_MINOR, TESTED_BUILD_TOOLS_VERSION_REV) + errorOutro, level: LogLevel.Error); return; } else { PlayServicesResolver.Log( errorIntro + String.Format("this plugin has only been " + "tested with build-tools up to version {0}.{1}.{2}. Corresponding " + "build-tools version {3} will be used, however this is untested with this " + "plugin and MAY NOT WORK! If you have trouble, please select a target SDK " + "version at or below \"android-{0}\". If you need to get this working with " + "the latest platform, please ", TESTED_BUILD_TOOLS_VERSION_MAJOR, TESTED_BUILD_TOOLS_VERSION_MINOR, TESTED_BUILD_TOOLS_VERSION_REV, buildToolsVersion) + errorOutro, level: LogLevel.Warning); } } if (buildToolsVersion == null) { // Use the tested build tools version, which we know will be able to handle // this targetSDK version. buildToolsVersion = String.Format("{0}.{1}.{2}", TESTED_BUILD_TOOLS_VERSION_MAJOR, TESTED_BUILD_TOOLS_VERSION_MINOR, TESTED_BUILD_TOOLS_VERSION_REV); // We don't have to bother with checking if it's installed because gradle actually // does that for us. } string minSdkVersion = UnityCompat.GetAndroidMinSDKVersion().ToString(); var config = new Dictionary <string, string>() { { "app_id", UnityCompat.ApplicationId }, { "sdk_version", targetSdkVersion.ToString() }, { "min_sdk_version", minSdkVersion }, { "build_tools_version", buildToolsVersion }, { "android_sdk_dir", svcSupport.SDK } }; // This creates an enumerable of strings with the json lines for each dep like this: // "[\"namespace\", \"package\", \"version\"]" var dependencies = PlayServicesSupport.GetAllDependencies(); var depLines = from d in dependencies select "[" + ToJSONList(DepsVersionAsArray(d.Value)) + "]"; // Get a flattened list of dependencies, excluding any with the "$SDK" path variable, // since those will automatically be included in the gradle build. var repoLines = new HashSet <string>( dependencies.SelectMany(d => d.Value.Repositories) .Where(s => !s.Contains(PlayServicesSupport.SdkVariable))); var proguard_config_paths = new List <string>() { Path.Combine(GRADLE_SCRIPT_LOCATION, PROGUARD_UNITY_CONFIG), Path.Combine(GRADLE_SCRIPT_LOCATION, PROGUARD_MSG_FIX_CONFIG) }; // Build the full json config as a string. string json_config = @"{{ ""config"": {{ {0} }}, ""project_deps"": [ {1} ], ""extra_m2repositories"": [ {2} ], ""extra_proguard_configs"": [ {3} ] }}"; json_config = String.Format(json_config, ToJSONDictionary(config), ToJSONList(depLines, ",\n", 4, true), ToJSONList(repoLines, ",\n", 4), ToJSONList(proguard_config_paths, ",\n", 4)); // Escape any literal backslashes (such as those from paths on windows), since we want to // preserve them when reading the config as backslashes and not interpret them // as escape characters. json_config = json_config.Replace(@"\", @"\\"); System.IO.File.WriteAllText(GENERATE_CONFIG_PATH, json_config); var outDir = Path.Combine(destinationDirectory, GENERATE_GRADLE_OUTPUT_DIR); RunGenGradleScript( " -c \"" + GENERATE_CONFIG_PATH + "\"" + " -b \"" + GENERATE_GRADLE_BUILD_PATH + "\"" + " -o \"" + outDir + "\"", (result) => { if (result.exitCode == 0) { var currentAbi = PlayServicesResolver.AndroidTargetDeviceAbi; var activeAbis = GetSelectedABIDirs(currentAbi); var libsDir = Path.Combine(outDir, "libs"); if (Directory.Exists(libsDir)) { foreach (var directory in Directory.GetDirectories(libsDir)) { var abiDir = Path.GetFileName(directory).ToLower(); if (!activeAbis.Contains(abiDir)) { FileUtils.DeleteExistingFileOrDirectory(directory); } } } if (Directory.Exists(outDir)) { PlayServicesResolver.LabelAssets(new [] { outDir }, true, true); } AssetDatabase.Refresh(); resolutionComplete(); } }); }
/// <summary> /// Copy srcaar files to aar files that are excluded from Unity's build process. /// </summary> /// <param name="dependencies">Dependencies to inject.</param> /// <returns>true if successful, false otherwise.</returns> private static bool CopySrcAars(ICollection <Dependency> dependencies) { bool succeeded = true; var aarFiles = new List <string>(); // Copy each .srcaar file to .aar while configuring the plugin importer to ignore the // file. foreach (var aar in LocalMavenRepository.FindAarsInLocalRepos(dependencies)) { var dir = Path.GetDirectoryName(aar); var filename = Path.GetFileNameWithoutExtension(aar); var targetFilename = Path.Combine(dir, filename + ".aar"); bool configuredAar = File.Exists(targetFilename); if (!configuredAar) { bool copiedAndLabeledAar = AssetDatabase.CopyAsset(aar, targetFilename); if (copiedAndLabeledAar) { var unlabeledAssets = new HashSet <string>(); PlayServicesResolver.LabelAssets( new [] { targetFilename }, complete: (unlabeled) => { unlabeledAssets.UnionWith(unlabeled); }); copiedAndLabeledAar = unlabeledAssets.Count == 0; } if (copiedAndLabeledAar) { try { PluginImporter importer = (PluginImporter)AssetImporter.GetAtPath( targetFilename); importer.SetCompatibleWithAnyPlatform(false); importer.SetCompatibleWithPlatform(BuildTarget.Android, false); configuredAar = true; } catch (Exception ex) { PlayServicesResolver.Log(String.Format( "Failed to disable {0} from being included by Unity's " + "internal build. {0} has been deleted and will not be " + "included in Gradle builds. ({1})", aar, ex), level: LogLevel.Error); } } else { PlayServicesResolver.Log(String.Format( "Unable to copy {0} to {1}. {1} will not be included in Gradle " + "builds.", aar, targetFilename), level: LogLevel.Error); } if (configuredAar) { aarFiles.Add(targetFilename); // Some versions of Unity do not mark the asset database as dirty when // plugin importer settings change so reimport the asset to synchronize // the state. AssetDatabase.ImportAsset(targetFilename, ImportAssetOptions.ForceUpdate); } else { if (File.Exists(targetFilename)) { AssetDatabase.DeleteAsset(targetFilename); } succeeded = false; } } } foreach (var aar in aarFiles) { if (!LocalMavenRepository.PatchPomFile(aar)) { succeeded = false; } } return(succeeded); }
/// <summary> /// Explodes a single aar file. This is done by calling the /// JDK "jar" command, then moving the classes.jar file. /// </summary> /// <param name="dir">the parent directory of the plugin.</param> /// <param name="aarFile">Aar file to explode.</param> /// <param name="antProject">true to explode into an Ant style project or false /// to repack the processed AAR as a new AAR.</param> /// <param name="abi">ABI of the AAR or null if it's universal.</param> /// <returns>true if successful, false otherwise.</returns> internal virtual bool ProcessAar(string dir, string aarFile, bool antProject, out string abi) { PlayServicesResolver.Log(String.Format("ProcessAar {0} {1} antProject={2}", dir, aarFile, antProject), level: LogLevel.Verbose); abi = null; string workingDir = Path.Combine(dir, Path.GetFileNameWithoutExtension(aarFile)); FileUtils.DeleteExistingFileOrDirectory(workingDir); Directory.CreateDirectory(workingDir); if (!ExtractAar(aarFile, null, workingDir)) { return(false); } PlayServicesResolver.ReplaceVariablesInAndroidManifest( Path.Combine(workingDir, "AndroidManifest.xml"), UnityCompat.ApplicationId, new Dictionary <string, string>()); string nativeLibsDir = null; if (antProject) { // Create the libs directory to store the classes.jar and non-Java shared // libraries. string libDir = Path.Combine(workingDir, "libs"); nativeLibsDir = libDir; Directory.CreateDirectory(libDir); // Move the classes.jar file to libs. string classesFile = Path.Combine(workingDir, "classes.jar"); string targetClassesFile = Path.Combine(libDir, Path.GetFileName(classesFile)); if (File.Exists(targetClassesFile)) { File.Delete(targetClassesFile); } if (File.Exists(classesFile)) { File.Move(classesFile, targetClassesFile); } else { // Generate an empty classes.jar file. string temporaryDirectory = CreateTemporaryDirectory(); if (temporaryDirectory == null) { return(false); } ArchiveAar(targetClassesFile, temporaryDirectory); } } // Copy non-Java shared libraries (.so) files from the "jni" directory into the // lib directory so that Unity's legacy (Ant-like) build system includes them in the // built APK. string jniLibDir = Path.Combine(workingDir, "jni"); nativeLibsDir = nativeLibsDir ?? jniLibDir; if (Directory.Exists(jniLibDir)) { if (jniLibDir != nativeLibsDir) { FileUtils.CopyDirectory(jniLibDir, nativeLibsDir); FileUtils.DeleteExistingFileOrDirectory(jniLibDir); } // Remove shared libraries for all ABIs that are not required for the selected // target ABI. var currentAbi = PlayServicesResolver.AndroidTargetDeviceAbi; var activeAbis = GetSelectedABIDirs(currentAbi); foreach (var directory in Directory.GetDirectories(nativeLibsDir)) { var abiDir = Path.GetFileName(directory); if (!activeAbis.Contains(abiDir)) { FileUtils.DeleteExistingFileOrDirectory(directory); } } abi = currentAbi; } if (antProject) { // Create the project.properties file which indicates to // Unity that this directory is a plugin. string projectProperties = Path.Combine(workingDir, "project.properties"); if (!File.Exists(projectProperties)) { File.WriteAllLines(projectProperties, new [] { "# Project target.", "target=android-9", "android.library=true" }); } // Clean up the aar file. FileUtils.DeleteExistingFileOrDirectory(Path.GetFullPath(aarFile)); // Add a tracking label to the exploded files. PlayServicesResolver.LabelAssets(new [] { workingDir }); } else { // Add a tracking label to the exploded files just in-case packaging fails. PlayServicesResolver.LabelAssets(new [] { workingDir }); // Create a new AAR file. FileUtils.DeleteExistingFileOrDirectory(Path.GetFullPath(aarFile)); if (!ArchiveAar(aarFile, workingDir)) { return(false); } // Clean up the exploded directory. FileUtils.DeleteExistingFileOrDirectory(workingDir); } return(true); }
/// <summary> /// Explodes a single aar file. This is done by calling the /// JDK "jar" command, then moving the classes.jar file. /// </summary> /// <param name="dir">The directory to unpack / explode the AAR to. If antProject is true /// the ant project will be located in Path.Combine(dir, Path.GetFileName(aarFile)).</param> /// <param name="aarFile">Aar file to explode.</param> /// <param name="antProject">true to explode into an Ant style project or false /// to repack the processed AAR as a new AAR.</param> /// <param name="abis">ABIs in the AAR or null if it's universal.</param> /// <returns>true if successful, false otherwise.</returns> internal virtual bool ProcessAar(string dir, string aarFile, bool antProject, out AndroidAbis abis) { PlayServicesResolver.Log(String.Format("ProcessAar {0} {1} antProject={2}", dir, aarFile, antProject), level: LogLevel.Verbose); abis = null; string aarDirName = Path.GetFileNameWithoutExtension(aarFile); // Output directory for the contents of the AAR / JAR. string outputDir = Path.Combine(dir, aarDirName); string stagingDir = FileUtils.CreateTemporaryDirectory(); if (stagingDir == null) { PlayServicesResolver.Log(String.Format( "Unable to create temporary directory to process AAR {0}", aarFile), level: LogLevel.Error); return(false); } try { string workingDir = Path.Combine(stagingDir, aarDirName); FileUtils.DeleteExistingFileOrDirectory(workingDir); Directory.CreateDirectory(workingDir); if (!PlayServicesResolver.ExtractZip(aarFile, null, workingDir)) { return(false); } PlayServicesResolver.ReplaceVariablesInAndroidManifest( Path.Combine(workingDir, "AndroidManifest.xml"), PlayServicesResolver.GetAndroidApplicationId(), new Dictionary <string, string>()); string nativeLibsDir = null; if (antProject) { // Create the libs directory to store the classes.jar and non-Java shared // libraries. string libDir = Path.Combine(workingDir, "libs"); nativeLibsDir = libDir; Directory.CreateDirectory(libDir); // Move the classes.jar file to libs. string classesFile = Path.Combine(workingDir, "classes.jar"); string targetClassesFile = Path.Combine(libDir, Path.GetFileName(classesFile)); if (File.Exists(targetClassesFile)) { File.Delete(targetClassesFile); } if (File.Exists(classesFile)) { FileUtils.MoveFile(classesFile, targetClassesFile); } else { // Some libraries publish AARs that are poorly formatted (e.g missing // a classes.jar file). Firebase's license AARs at certain versions are // examples of this. When Unity's internal build system detects an Ant // project or AAR without a classes.jar, the build is aborted. This // generates an empty classes.jar file to workaround the issue. string emptyClassesDir = Path.Combine(stagingDir, "empty_classes_jar"); if (!ArchiveAar(targetClassesFile, emptyClassesDir)) { return(false); } } } // Copy non-Java shared libraries (.so) files from the "jni" directory into the // lib directory so that Unity's legacy (Ant-like) build system includes them in the // built APK. string jniLibDir = Path.Combine(workingDir, "jni"); nativeLibsDir = nativeLibsDir ?? jniLibDir; if (Directory.Exists(jniLibDir)) { var abisInArchive = AarDirectoryFindAbis(workingDir); if (jniLibDir != nativeLibsDir) { FileUtils.CopyDirectory(jniLibDir, nativeLibsDir); FileUtils.DeleteExistingFileOrDirectory(jniLibDir); } if (abisInArchive != null) { // Remove shared libraries for all ABIs that are not required for the // selected ABIs. var activeAbisSet = AndroidAbis.Current.ToSet(); var abisInArchiveSet = abisInArchive.ToSet(); var abisInArchiveToRemoveSet = new HashSet <string>(abisInArchiveSet); abisInArchiveToRemoveSet.ExceptWith(activeAbisSet); Func <IEnumerable <string>, string> setToString = (setToConvert) => { return(String.Join(", ", (new List <string>(setToConvert)).ToArray())); }; PlayServicesResolver.Log( String.Format( "Target ABIs [{0}], ABIs [{1}] in {2}, will remove [{3}] ABIs", setToString(activeAbisSet), setToString(abisInArchiveSet), aarFile, setToString(abisInArchiveToRemoveSet)), level: LogLevel.Verbose); foreach (var abiToRemove in abisInArchiveToRemoveSet) { abisInArchiveSet.Remove(abiToRemove); FileUtils.DeleteExistingFileOrDirectory(Path.Combine(nativeLibsDir, abiToRemove)); } abis = new AndroidAbis(abisInArchiveSet); } } if (antProject) { // Create the project.properties file which indicates to Unity that this // directory is a plugin. string projectProperties = Path.Combine(workingDir, "project.properties"); if (!File.Exists(projectProperties)) { File.WriteAllLines(projectProperties, new [] { "# Project target.", "target=android-9", "android.library=true" }); } PlayServicesResolver.Log( String.Format("Creating Ant project: Replacing {0} with {1}", aarFile, outputDir), level: LogLevel.Verbose); // Clean up the aar file. FileUtils.DeleteExistingFileOrDirectory(Path.GetFullPath(aarFile)); // Create the output directory. FileUtils.MoveDirectory(workingDir, outputDir); // Add a tracking label to the exploded files. PlayServicesResolver.LabelAssets(new [] { outputDir }); } else { // Add a tracking label to the exploded files just in-case packaging fails. PlayServicesResolver.Log(String.Format("Repacking {0} from {1}", aarFile, workingDir), level: LogLevel.Verbose); // Create a new AAR file. FileUtils.DeleteExistingFileOrDirectory(Path.GetFullPath(aarFile)); if (!ArchiveAar(aarFile, workingDir)) { return(false); } PlayServicesResolver.LabelAssets(new [] { aarFile }); } } catch (Exception e) { PlayServicesResolver.Log(String.Format("Failed to process AAR {0} ({1}", aarFile, e), level: LogLevel.Error); } finally { // Clean up the temporary directory. FileUtils.DeleteExistingFileOrDirectory(stagingDir); } return(true); }
/// <summary> /// Explodes a single aar file. This is done by calling the /// JDK "jar" command, then moving the classes.jar file. /// </summary> /// <param name="dir">the parent directory of the plugin.</param> /// <param name="aarFile">Aar file to explode.</param> /// <param name="antProject">true to explode into an Ant style project or false /// to repack the processed AAR as a new AAR.</param> /// <param name="abi">ABI of the AAR or null if it's universal.</param> /// <returns>true if successful, false otherwise.</returns> internal virtual bool ProcessAar(string dir, string aarFile, bool antProject, out string abi) { abi = null; string workingDir = Path.Combine(dir, Path.GetFileNameWithoutExtension(aarFile)); PlayServicesSupport.DeleteExistingFileOrDirectory(workingDir, includeMetaFiles: true); Directory.CreateDirectory(workingDir); if (!ExtractAar(aarFile, null, workingDir)) { return(false); } ReplaceVariables(workingDir); string nativeLibsDir = null; if (antProject) { // Create the libs directory to store the classes.jar and non-Java shared // libraries. string libDir = Path.Combine(workingDir, "libs"); nativeLibsDir = libDir; Directory.CreateDirectory(libDir); // Move the classes.jar file to libs. string classesFile = Path.Combine(workingDir, "classes.jar"); if (File.Exists(classesFile)) { string targetClassesFile = Path.Combine(libDir, Path.GetFileName(classesFile)); if (File.Exists(targetClassesFile)) { File.Delete(targetClassesFile); } File.Move(classesFile, targetClassesFile); } } // Copy non-Java shared libraries (.so) files from the "jni" directory into the // lib directory so that Unity's legacy (Ant-like) build system includes them in the // built APK. string jniLibDir = Path.Combine(workingDir, "jni"); nativeLibsDir = nativeLibsDir ?? jniLibDir; if (Directory.Exists(jniLibDir)) { if (jniLibDir != nativeLibsDir) { PlayServicesSupport.CopyDirectory(jniLibDir, nativeLibsDir); PlayServicesSupport.DeleteExistingFileOrDirectory(jniLibDir, includeMetaFiles: true); } // Remove shared libraries for all ABIs that are not required for the selected // target ABI. var activeAbis = new HashSet <string>(); var currentAbi = PlayServicesResolver.AndroidTargetDeviceAbi.ToLower(); foreach (var kv in UNITY_ABI_TO_NATIVE_LIBRARY_ABI_DIRECTORY) { if (currentAbi == kv.Key) { activeAbis.Add(kv.Value); } } if (activeAbis.Count == 0) { activeAbis.UnionWith(UNITY_ABI_TO_NATIVE_LIBRARY_ABI_DIRECTORY.Values); } foreach (var directory in Directory.GetDirectories(nativeLibsDir)) { var abiDir = Path.GetFileName(directory); if (!activeAbis.Contains(abiDir)) { PlayServicesSupport.DeleteExistingFileOrDirectory( directory, includeMetaFiles: true); } } abi = currentAbi; } if (antProject) { // Create the project.properties file which indicates to // Unity that this directory is a plugin. string projectProperties = Path.Combine(workingDir, "project.properties"); if (!File.Exists(projectProperties)) { File.WriteAllLines(projectProperties, new [] { "# Project target.", "target=android-9", "android.library=true" }); } // Clean up the aar file. PlayServicesSupport.DeleteExistingFileOrDirectory(Path.GetFullPath(aarFile), includeMetaFiles: true); // Add a tracking label to the exploded files. PlayServicesResolver.LabelAssets(new [] { workingDir }); } else { // Add a tracking label to the exploded files just in-case packaging fails. PlayServicesResolver.LabelAssets(new [] { workingDir }); // Create a new AAR file. PlayServicesSupport.DeleteExistingFileOrDirectory(Path.GetFullPath(aarFile), includeMetaFiles: true); if (!ArchiveAar(aarFile, workingDir)) { return(false); } // Clean up the exploded directory. PlayServicesSupport.DeleteExistingFileOrDirectory(workingDir, includeMetaFiles: true); } return(true); }