plugins { id "com.android.application" } android { compileSdkVersion project.properties.compileSdkVersion.toInteger() ndkVersion = System.getenv("JITPACK_NDK_VERSION") ?: project.properties.ndkVersion def appVersionName = System.getenv("TERMUX_APP_VERSION_NAME") ?: "" def apkVersionTag = System.getenv("TERMUX_APK_VERSION_TAG") ?: "" def splitAPKsForDebugBuilds = System.getenv("TERMUX_SPLIT_APKS_FOR_DEBUG_BUILDS") ?: "1" def splitAPKsForReleaseBuilds = System.getenv("TERMUX_SPLIT_APKS_FOR_RELEASE_BUILDS") ?: "0" // F-Droid does not support split APKs #1904 dependencies { implementation "androidx.annotation:annotation:1.3.0" implementation "androidx.core:core:1.6.0" implementation "androidx.drawerlayout:drawerlayout:1.1.1" implementation "androidx.preference:preference:1.1.1" implementation "androidx.viewpager:viewpager:1.0.0" implementation "com.google.android.material:material:1.4.0" implementation "com.google.guava:guava:24.1-jre" implementation "io.noties.markwon:core:$markwonVersion" implementation "io.noties.markwon:ext-strikethrough:$markwonVersion" implementation "io.noties.markwon:linkify:$markwonVersion" implementation "io.noties.markwon:recycler:$markwonVersion" implementation project(":terminal-view") implementation project(":termux-shared") implementation 'com.github.termux:termux-am-library:1.0' } defaultConfig { applicationId "com.termux" minSdkVersion project.properties.minSdkVersion.toInteger() targetSdkVersion project.properties.targetSdkVersion.toInteger() versionCode 118 versionName "0.118.0" if (appVersionName) versionName = appVersionName validateVersionName(versionName) manifestPlaceholders.TERMUX_PACKAGE_NAME = "com.termux" manifestPlaceholders.TERMUX_APP_NAME = "Termux" manifestPlaceholders.TERMUX_API_APP_NAME = "Termux:API" manifestPlaceholders.TERMUX_BOOT_APP_NAME = "Termux:Boot" manifestPlaceholders.TERMUX_FLOAT_APP_NAME = "Termux:Float" manifestPlaceholders.TERMUX_STYLING_APP_NAME = "Termux:Styling" manifestPlaceholders.TERMUX_TASKER_APP_NAME = "Termux:Tasker" manifestPlaceholders.TERMUX_WIDGET_APP_NAME = "Termux:Widget" externalNativeBuild { ndkBuild { cFlags "-std=c11", "-Wall", "-Wextra", "-Werror", "-Os", "-fno-stack-protector", "-Wl,--gc-sections" } } splits { abi { enable ((gradle.startParameter.taskNames.any { it.contains("Debug") } && splitAPKsForDebugBuilds == "1") || (gradle.startParameter.taskNames.any { it.contains("Release") } && splitAPKsForReleaseBuilds == "1")) reset () include 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a' universalApk true } } } signingConfigs { debug { storeFile file('dev_keystore.jks') keyAlias 'alias' storePassword 'xrj45yWGLbsO7W0v' keyPassword 'xrj45yWGLbsO7W0v' } } buildTypes { release { minifyEnabled true shrinkResources false // Reproducible builds proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } debug { signingConfig signingConfigs.debug } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } externalNativeBuild { ndkBuild { path "src/main/cpp/Android.mk" } } lintOptions { disable 'ProtectedPermissions' } testOptions { unitTests { includeAndroidResources = true } } packagingOptions { jniLibs { useLegacyPackaging true } } applicationVariants.all { variant -> variant.outputs.all { output -> if (variant.buildType.name == "debug") { def abi = output.getFilter(com.android.build.OutputFile.ABI) outputFileName = new File("termux-app_" + (apkVersionTag ? apkVersionTag : "debug") + "_" + (abi ? abi : "universal") + ".apk") } else if (variant.buildType.name == "release") { def abi = output.getFilter(com.android.build.OutputFile.ABI) outputFileName = new File("termux-app_" + (apkVersionTag ? apkVersionTag : "release") + "_" + (abi ? abi : "universal") + ".apk") } } } } dependencies { testImplementation "junit:junit:4.13.2" testImplementation "org.robolectric:robolectric:4.4" } task versionName { doLast { print android.defaultConfig.versionName } } def validateVersionName(String versionName) { // https://semver.org/spec/v2.0.0.html#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string // ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ if (!java.util.regex.Pattern.matches("^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?\$", versionName)) throw new GradleException("The versionName '" + versionName + "' is not a valid version as per semantic version '2.0.0' spec in the format 'major.minor.patch(-prerelease)(+buildmetadata)'. https://semver.org/spec/v2.0.0.html.") } def downloadBootstrap(String arch, String expectedChecksum, String version) { def digest = java.security.MessageDigest.getInstance("SHA-256") def localUrl = "src/main/cpp/bootstrap-" + arch + ".zip" def file = new File(projectDir, localUrl) if (file.exists()) { def buffer = new byte[8192] def input = new FileInputStream(file) while (true) { def readBytes = input.read(buffer) if (readBytes < 0) break digest.update(buffer, 0, readBytes) } def checksum = new BigInteger(1, digest.digest()).toString(16) while (checksum.length() < 64) { checksum = "0" + checksum } if (checksum == expectedChecksum) { return } else { logger.quiet("Deleting old local file with wrong hash: " + localUrl) file.delete() } } def remoteUrl = "https://github.com/termux/termux-packages/releases/download/bootstrap-" + version + "/bootstrap-" + arch + ".zip" logger.quiet("Downloading " + remoteUrl + " ...") file.parentFile.mkdirs() def out = new BufferedOutputStream(new FileOutputStream(file)) def connection = new URL(remoteUrl).openConnection() connection.setInstanceFollowRedirects(true) def digestStream = new java.security.DigestInputStream(connection.inputStream, digest) out << digestStream out.close() def checksum = new BigInteger(1, digest.digest()).toString(16) while (checksum.length() < 64) { checksum = "0" + checksum } if (checksum != expectedChecksum) { file.delete() throw new GradleException("Wrong checksum for " + remoteUrl + ": expected: " + expectedChecksum + ", actual: " + checksum) } } clean { doLast { def tree = fileTree(new File(projectDir, 'src/main/cpp')) tree.include 'bootstrap-*.zip' tree.each { it.delete() } } } task downloadBootstraps() { doLast { def version = "2022.04.21-r1" downloadBootstrap("aarch64", "6ef5d51ebcc98cabec178de58b3846d192e3c47f1575f6319c0d55b4b2550ee8", version) downloadBootstrap("arm", "39f371b9b0892a5e263993eab8fc8d1f58020a7df78372d134184e8da3d5d18e", version) downloadBootstrap("i686", "2355c44da98c3990bce1fc527e36b9f9f913bb8d814d73601811fc62bb86d864", version) downloadBootstrap("x86_64", "21ced5aacde882999306a46f913191bbea134af38532fb85ef958f6822a8730c", version) } } afterEvaluate { android.applicationVariants.all { variant -> variant.javaCompileProvider.get().dependsOn(downloadBootstraps) } }