public override BuiltNativeProgram DeployTo(NPath targetDirectory, Dictionary <IDeployable, IDeployable> alreadyDeployed = null) { // This is complementary target, library should be deployed to the corresponding folder of the main target // see comment in https://github.com/Unity-Technologies/dots/blob/master/TinySamples/Packages/com.unity.dots.runtime/bee%7E/BuildProgramSources/DotsConfigs.cs // DotsConfigs.MakeConfigs() method for details. var gradleProjectPath = AndroidApkToolchain.ExportProject ? targetDirectory.Combine(m_gameName) : Path.Parent.Parent.Combine("gradle"); var libDirectory = gradleProjectPath.Combine("src/main/jniLibs").Combine(m_libPath); // Deployables with type DeployableFile are deployed with main target Deployables = Deployables.Where(f => !(f is DeployableFile)).ToArray(); var result = base.DeployTo(libDirectory, alreadyDeployed); // Required to make sure that main target Gradle project depends on this lib and this lib is deployed before packaging step Backend.Current.AddDependency(gradleProjectPath.Combine("build.gradle"), result.Path); return(result); }
private void GenerateGradleProject(NPath gradleProjectPath) { var gradleSrcPath = AsmDefConfigFile.AsmDefDescriptionFor("Unity.Build.Android.DotsRuntime").Path.Parent.Combine("AndroidProjectTemplate~/"); var hasGradleDependencies = false; var gradleDependencies = new StringBuilder(); gradleDependencies.AppendLine(" dependencies {"); var hasKotlin = false; foreach (var d in Deployables.Where(d => (d is DeployableFile))) { var f = d as DeployableFile; if (f.Path.Extension == "aar" || f.Path.Extension == "jar") { gradleDependencies.AppendLine($" compile(name:'{f.Path.FileNameWithoutExtension}', ext:'{f.Path.Extension}')"); hasGradleDependencies = true; } else if (f.Path.Extension == "kt") { hasKotlin = true; } } if (hasGradleDependencies) { gradleDependencies.AppendLine(" }"); } else { gradleDependencies.Clear(); } var kotlinClassPath = hasKotlin ? " classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.11'" : ""; var kotlinPlugin = hasKotlin ? "apply plugin: 'kotlin-android'" : ""; var loadLibraries = new StringBuilder(); bool useStaticLib = Deployables.FirstOrDefault(l => l.ToString().Contains("lib_unity_tiny_android.so")) == default(IDeployable); if (useStaticLib) { loadLibraries.AppendLine($" System.loadLibrary(\"{m_gameName}\");"); } else { var rx = new Regex(@".*lib([\w\d_]+)\.so", RegexOptions.Compiled); foreach (var l in Deployables) { var match = rx.Match(l.ToString()); if (match.Success) { loadLibraries.AppendLine($" System.loadLibrary(\"{match.Groups[1].Value}\");"); } } } String abiFilters = ""; if (AndroidApkToolchain.Config.Architectures.Architectures == AndroidArchitecture.ARM64) { abiFilters = "'arm64-v8a'"; } else if (AndroidApkToolchain.Config.Architectures.Architectures == AndroidArchitecture.ARMv7) { abiFilters = "'armeabi-v7a'"; } else if (AndroidApkToolchain.IsFatApk) { abiFilters = "'armeabi-v7a', 'arm64-v8a'"; } else // shouldn't happen { Console.WriteLine($"Tiny android toolchain doesn't support {AndroidApkToolchain.Config.Architectures.Architectures.ToString()} architectures"); } // Android docs say "density" value was added in API level 17, but it doesn't compile with target SDK level lower than 24. string configChanges = ((int)AndroidApkToolchain.Config.APILevels.ResolvedTargetAPILevel > 23) ? AndroidConfigChanges + "|density" : AndroidConfigChanges; var useKeystore = BuildConfiguration.HasComponent <AndroidKeystore>(); var renderOutsideSafeArea = BuildConfiguration.HasComponent <AndroidRenderOutsideSafeArea>(); var icons = AndroidApkToolchain.Config.Icons; var hasBackground = icons.Icons.Any(i => !String.IsNullOrEmpty(i.Background)); var hasCustomIcons = hasBackground || icons.Icons.Any(i => !String.IsNullOrEmpty(i.Foreground) || !String.IsNullOrEmpty(i.Legacy)); var version = AndroidApkToolchain.Config.Settings.Version; var versionFieldCount = version.Revision > 0 ? 4 : 3; var maxRatio = AndroidApkToolchain.Config.AspectRatio.GetMaxAspectRatio(AndroidApkToolchain.Config.APILevels.ResolvedTargetAPILevel); var additionalApplicationMetadata = ""; var additionalPermissions = ""; var additionalFeatures = ""; if (!String.IsNullOrEmpty(maxRatio)) { additionalApplicationMetadata += GetMetaDataString("android.max_aspect", maxRatio); } if (BuildConfiguration.HasComponent <ARCoreSettings>()) { additionalPermissions += GetPermissionString("android.permission.CAMERA"); if (AndroidApkToolchain.Config.ARCore.Requirement == Requirement.Optional) { additionalApplicationMetadata += "\n" + GetMetaDataString("com.google.ar.core", "optional"); } else { additionalApplicationMetadata += "\n" + GetMetaDataString("com.google.ar.core", "required"); additionalFeatures += GetFeatureString("android.hardware.camera.ar", true); } if (AndroidApkToolchain.Config.ARCore.DepthSupport == Requirement.Required) { additionalFeatures += "\n" + GetFeatureString("com.google.ar.core.depth", true); } } var templateStrings = new Dictionary <string, string> { { "**LOADLIBRARIES**", loadLibraries.ToString() }, { "**PACKAGENAME**", AndroidApkToolchain.Config.Identifier.PackageName }, { "**PRODUCTNAME**", AndroidApkToolchain.Config.Settings.ProductName }, { "**VERSIONNAME**", version.ToString(versionFieldCount) }, { "**VERSIONCODE**", AndroidApkToolchain.Config.VersionCode.VersionCode.ToString() }, { "**ORIENTATION**", GetOrientationAttr() }, { "**INSTALLLOCATION**", AndroidApkToolchain.Config.InstallLocation?.PreferredInstallLocationAsString() }, { "**CUTOUTMODE**", AndroidRenderOutsideSafeArea.CutoutMode(renderOutsideSafeArea) }, { "**NOTCHCONFIG**", AndroidRenderOutsideSafeArea.NotchConfig(renderOutsideSafeArea) }, { "**NOTCHSUPPORT**", AndroidRenderOutsideSafeArea.NotchSupport(renderOutsideSafeArea) }, { "**GAMENAME**", m_gameName }, { "**MINSDKVERSION**", ((int)AndroidApkToolchain.Config.APILevels.MinAPILevel).ToString() }, { "**TARGETSDKVERSION**", ((int)AndroidApkToolchain.Config.APILevels.ResolvedTargetAPILevel).ToString() }, { "**CONFIGCHANGES**", configChanges }, { "**ACTIVITY_ASPECT**", String.IsNullOrEmpty(maxRatio) ? "" : $"android:maxAspectRatio=\"{maxRatio}\"" }, { "**ADDITIONAL_APPLICATION_METADATA**", additionalApplicationMetadata }, { "**ADDITIONAL_PERMISSIONS**", additionalPermissions }, { "**ADDITIONAL_FEATURES**", additionalFeatures }, { "**ABIFILTERS**", abiFilters }, { "**SIGN**", AndroidApkToolchain.Config.Keystore.GetSigningConfigs(useKeystore) }, { "**SIGNCONFIG**", AndroidApkToolchain.Config.Keystore.GetSigningConfig(useKeystore) }, { "**DEPENDENCIES**", gradleDependencies.ToString() }, { "**KOTLINCLASSPATH**", kotlinClassPath }, { "**KOTLINPLUGIN**", kotlinPlugin }, { "**ALLOWED_PORTRAIT**", AndroidApkToolchain.AllowedOrientationPortrait ? "true" : "false" }, { "**ALLOWED_REVERSE_PORTRAIT**", AndroidApkToolchain.AllowedOrientationReversePortrait ? "true" : "false" }, { "**ALLOWED_LANDSCAPE**", AndroidApkToolchain.AllowedOrientationLandscape ? "true" : "false" }, { "**ALLOWED_REVERSE_LANDSCAPE**", AndroidApkToolchain.AllowedOrientationReverseLandscape ? "true" : "false" }, { "**BACKGROUND_PATH**", hasBackground ? "mipmap" : "drawable" } }; // copy icon files if (hasCustomIcons) { for (int i = 0; i < icons.Icons.Length; ++i) { var dpi = ((ScreenDPI)i).ToString().ToLower(); if (AndroidApkToolchain.Config.APILevels.TargetSDKSupportsAdaptiveIcons) { CopyIcon(gradleProjectPath, dpi, "ic_launcher_foreground.png", icons.Icons[i].Foreground); CopyIcon(gradleProjectPath, dpi, "ic_launcher_background.png", icons.Icons[i].Background); } CopyIcon(gradleProjectPath, dpi, "app_icon.png", icons.Icons[i].Legacy); } } // copy and patch project files var apiRx = new Regex(@".+res[\\|\/].+-v([0-9]+)$", RegexOptions.Compiled); foreach (var r in gradleSrcPath.Files(true)) { if ((hasCustomIcons && r.HasDirectory("mipmap-mdpi")) || (hasBackground && r.HasDirectory("drawable"))) // skipping icons files if there are custom ones { continue; } if (!AndroidApkToolchain.Config.APILevels.TargetSDKSupportsAdaptiveIcons && r.FileName.StartsWith("ic_launcher_")) { continue; } var match = apiRx.Match(r.Parent.ToString()); if (match.Success) { var api = Int32.Parse(match.Groups[1].Value); if (api > (int)AndroidApkToolchain.Config.APILevels.ResolvedTargetAPILevel) { continue; } } var destPath = gradleProjectPath.Combine(r.RelativeTo(gradleSrcPath)); if (r.Extension == "template") { destPath = destPath.ChangeExtension(""); var code = r.ReadAllText(); foreach (var t in templateStrings) { if (code.IndexOf(t.Key) != -1) { code = code.Replace(t.Key, t.Value); } } Backend.Current.AddWriteTextAction(destPath, code); } else { destPath = CopyTool.Instance().Setup(destPath, r); } m_projectFiles.Add(destPath); } var localProperties = new StringBuilder(); localProperties.AppendLine($"sdk.dir={new NPath(AndroidApkToolchain.Config.ExternalTools.SdkPath).ToString()}"); localProperties.AppendLine($"ndk.dir={new NPath(AndroidApkToolchain.Config.ExternalTools.NdkPath).ToString()}"); var localPropertiesPath = gradleProjectPath.Combine("local.properties"); Backend.Current.AddWriteTextAction(localPropertiesPath, localProperties.ToString()); m_projectFiles.Add(localPropertiesPath); }