Skip to content

Commit 94bbe32

Browse files
author
Workbench
committed
chore: clean up build system, remove kotlin-android plugin, drop apt-android-5, fix AndroidManifest
1 parent b8b0e6c commit 94bbe32

6 files changed

Lines changed: 204 additions & 220 deletions

File tree

.github/workflows/attach_debug_apks_to_release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
strategy:
1212
fail-fast: false
1313
matrix:
14-
package_variant: [ apt-android-7, apt-android-5 ]
14+
package_variant: [ apt-android-7 ]
1515
env:
1616
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
1717

.github/workflows/debug_build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
strategy:
1818
fail-fast: false
1919
matrix:
20-
package_variant: [ apt-android-7, apt-android-5 ]
20+
package_variant: [ apt-android-7 ]
2121

2222
steps:
2323
- name: Clone repository

app/build.gradle

Lines changed: 100 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
1-
import java.security.DigestInputStream
2-
import java.util.zip.ZipEntry
3-
import java.util.zip.ZipInputStream
4-
5-
61
plugins {
72
id "com.android.application"
83
}
9-
apply plugin: 'kotlin-android'
4+
5+
ext {
6+
// The packageVariant defines the bootstrap variant that will be included in the app APK.
7+
// This must be supported by com.termux.shared.termux.TermuxBootstrap.PackageVariant or app will
8+
// crash at startup.
9+
// Bootstrap of a different variant must not be manually installed by the user after app installation
10+
// by replacing $PREFIX since app code is dependant on the variant used to build the APK.
11+
// Currently supported values are: [ "apt-android-7" "apt-android-5" ]
12+
packageVariant = System.getenv("TERMUX_PACKAGE_VARIANT") ?: "apt-android-7" // Default: "apt-android-7"
13+
}
1014

1115
android {
1216
namespace "com.termux"
@@ -37,20 +41,39 @@ android {
3741
}
3842

3943
defaultConfig {
40-
applicationId "com.termux"
41-
minSdkVersion 21
42-
targetSdkVersion 28
43-
versionCode 83
44-
versionName "0.83"
44+
minSdkVersion project.properties.minSdkVersion.toInteger()
45+
targetSdkVersion project.properties.targetSdkVersion.toInteger()
46+
versionCode 118
47+
versionName "0.118.0"
48+
49+
if (appVersionName) versionName = appVersionName
50+
validateVersionName(versionName)
51+
52+
buildConfigField "String", "TERMUX_PACKAGE_VARIANT", "\"" + project.ext.packageVariant + "\"" // Used by TermuxApplication class
53+
54+
manifestPlaceholders.TERMUX_PACKAGE_NAME = "com.termux"
55+
manifestPlaceholders.TERMUX_APP_NAME = "Termux"
56+
manifestPlaceholders.TERMUX_API_APP_NAME = "Termux:API"
57+
manifestPlaceholders.TERMUX_BOOT_APP_NAME = "Termux:Boot"
58+
manifestPlaceholders.TERMUX_FLOAT_APP_NAME = "Termux:Float"
59+
manifestPlaceholders.TERMUX_STYLING_APP_NAME = "Termux:Styling"
60+
manifestPlaceholders.TERMUX_TASKER_APP_NAME = "Termux:Tasker"
61+
manifestPlaceholders.TERMUX_WIDGET_APP_NAME = "Termux:Widget"
4562

4663
externalNativeBuild {
4764
ndkBuild {
4865
cFlags "-std=c11", "-Wall", "-Wextra", "-Werror", "-Os", "-fno-stack-protector", "-Wl,--gc-sections"
4966
}
5067
}
5168

52-
ndk {
53-
abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
69+
splits {
70+
abi {
71+
enable ((gradle.startParameter.taskNames.any { it.contains("Debug") } && splitAPKsForDebugBuilds == "1") ||
72+
(gradle.startParameter.taskNames.any { it.contains("Release") } && splitAPKsForReleaseBuilds == "1"))
73+
reset ()
74+
include 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
75+
universalApk true
76+
}
5477
}
5578
}
5679

@@ -82,8 +105,11 @@ android {
82105
sourceCompatibility JavaVersion.VERSION_17
83106
targetCompatibility JavaVersion.VERSION_17
84107
}
85-
kotlinOptions {
86-
jvmTarget = JavaVersion.VERSION_1_8
108+
109+
externalNativeBuild {
110+
ndkBuild {
111+
path "src/main/cpp/Android.mk"
112+
}
87113
}
88114

89115
lint {
@@ -120,15 +146,9 @@ android {
120146
}
121147

122148
dependencies {
123-
testImplementation 'junit:junit:4.13'
124-
testImplementation 'org.robolectric:robolectric:4.3.1'
125-
126-
//kotlin
127-
implementation "androidx.core:core-ktx:1.3.2"
128-
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
129-
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.8'
130-
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.8'
131-
149+
testImplementation "junit:junit:4.13.2"
150+
testImplementation "org.robolectric:robolectric:4.10"
151+
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:1.1.5"
132152
}
133153

134154
task versionName {
@@ -137,117 +157,87 @@ task versionName {
137157
}
138158
}
139159

140-
def setupBootstrap(String arch, String expectedChecksum, int version) {
141-
def digest = java.security.MessageDigest.getInstance("SHA-256")
160+
def validateVersionName(String versionName) {
161+
// https://semver.org/spec/v2.0.0.html#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
162+
// ^(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-]+)*))?$
163+
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))
164+
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.")
165+
}
142166

143-
def zipDownloadFile = new File(project.buildDir, "./gradle/bootstrap-" + arch + "-" + version + ".zip")
167+
def downloadBootstrap(String arch, String expectedChecksum, String version) {
168+
def digest = java.security.MessageDigest.getInstance("SHA-256")
144169

145-
if (zipDownloadFile.exists()) {
170+
def localUrl = "src/main/cpp/bootstrap-" + arch + ".zip"
171+
def file = new File(projectDir, localUrl)
172+
if (file.exists()) {
146173
def buffer = new byte[8192]
147-
def input = new FileInputStream(zipDownloadFile)
174+
def input = new FileInputStream(file)
148175
while (true) {
149176
def readBytes = input.read(buffer)
150177
if (readBytes < 0) break
151178
digest.update(buffer, 0, readBytes)
152179
}
153180
def checksum = new BigInteger(1, digest.digest()).toString(16)
154-
if (checksum != expectedChecksum) {
155-
logger.quiet("Deleting old local file with wrong hash: " + zipDownloadFile.getAbsolutePath())
156-
zipDownloadFile.delete()
181+
while (checksum.length() < 64) { checksum = "0" + checksum }
182+
if (checksum == expectedChecksum) {
183+
return
184+
} else {
185+
logger.quiet("Deleting old local file with wrong hash: " + localUrl + ": expected: " + expectedChecksum + ", actual: " + checksum)
186+
file.delete()
157187
}
158188
}
159189

160-
if (!zipDownloadFile.exists()) {
161-
def remoteUrl = "https://bintray.com/termux/bootstrap/download_file?file_path=android10-v" + version + "-bootstrap-" + arch + ".zip"
162-
logger.quiet("Downloading " + remoteUrl + " ...")
190+
def remoteUrl = "https://github.com/termux/termux-packages/releases/download/bootstrap-" + version + "/bootstrap-" + arch + ".zip"
191+
logger.quiet("Downloading " + remoteUrl + " ...")
163192

164-
zipDownloadFile.parentFile.mkdirs()
165-
def out = new BufferedOutputStream(new FileOutputStream(zipDownloadFile))
193+
file.parentFile.mkdirs()
194+
def out = new BufferedOutputStream(new FileOutputStream(file))
166195

167-
def connection = new URL(remoteUrl).openConnection()
168-
connection.setInstanceFollowRedirects(true)
169-
def digestStream = new DigestInputStream(connection.inputStream, digest)
170-
out << digestStream
171-
out.close()
196+
def connection = new URL(remoteUrl).openConnection()
197+
connection.setInstanceFollowRedirects(true)
198+
def digestStream = new java.security.DigestInputStream(connection.inputStream, digest)
199+
out << digestStream
200+
out.close()
172201

173-
def checksum = new BigInteger(1, digest.digest()).toString(16)
174-
if (checksum != expectedChecksum) {
175-
zipDownloadFile.delete()
176-
throw new GradleException("Wrong checksum for " + remoteUrl + ": expected: " + expectedChecksum + ", actual: " + checksum)
177-
}
202+
def checksum = new BigInteger(1, digest.digest()).toString(16)
203+
while (checksum.length() < 64) { checksum = "0" + checksum }
204+
if (checksum != expectedChecksum) {
205+
file.delete()
206+
throw new GradleException("Wrong checksum for " + remoteUrl + ": expected: " + expectedChecksum + ", actual: " + checksum)
178207
}
208+
}
179209

180-
def doneMarkerFile = new File(zipDownloadFile.getAbsolutePath() + "." + expectedChecksum + ".done")
181-
182-
if (doneMarkerFile.exists()) return
183-
184-
def archDirName
185-
if (arch == "aarch64") archDirName = "arm64-v8a";
186-
if (arch == "arm") archDirName = "armeabi-v7a";
187-
if (arch == "i686") archDirName = "x86";
188-
if (arch == "x86_64") archDirName = "x86_64";
189-
190-
def outputPath = project.getRootDir().getAbsolutePath() + "/app/src/main/jniLibs/" + archDirName + "/"
191-
def outputDir = new File(outputPath).getAbsoluteFile()
192-
if (!outputDir.exists()) outputDir.mkdirs()
193-
194-
def symlinksFile = new File(outputDir, "libsymlinks.so").getAbsoluteFile()
195-
if (symlinksFile.exists()) symlinksFile.delete();
196-
197-
def mappingsFile = new File(outputDir, "libfiles.so").getAbsoluteFile()
198-
if (mappingsFile.exists()) mappingsFile.delete()
199-
mappingsFile.createNewFile()
200-
def mappingsFileWriter = new BufferedWriter(new FileWriter(mappingsFile))
201-
202-
def counter = 100
203-
new ZipInputStream(new FileInputStream(zipDownloadFile)).withCloseable { zipInput ->
204-
ZipEntry zipEntry
205-
while ((zipEntry = zipInput.getNextEntry()) != null) {
206-
if (zipEntry.getName() == "SYMLINKS.txt") {
207-
zipInput.transferTo(new FileOutputStream(symlinksFile))
208-
} else if (!zipEntry.isDirectory()) {
209-
def soName = "lib" + counter + ".so"
210-
def targetFile = new File(outputDir, soName).getAbsoluteFile()
211-
212-
println "target file path is ${targetFile}"
213-
214-
try {
215-
zipInput.transferTo(new FileOutputStream(targetFile))
216-
} catch (Exception e) {
217-
println "Error ${e}"
218-
}
219-
220-
221-
if (zipEntry.getName().endsWith("/pkg")) {
222-
def pkgScript = new FileInputStream(project.getRootDir().getAbsolutePath() + "/pkg.sh")
223-
pkgScript.transferTo(new FileOutputStream(targetFile))
224-
}
225-
226-
mappingsFileWriter.writeLine(soName + "" + zipEntry.getName())
227-
counter++
228-
}
229-
}
210+
clean {
211+
doLast {
212+
def tree = fileTree(new File(projectDir, 'src/main/cpp'))
213+
tree.include 'bootstrap-*.zip'
214+
tree.each { it.delete() }
230215
}
231-
232-
mappingsFileWriter.close()
233-
doneMarkerFile.createNewFile()
234216
}
235217

236-
task setupBootstraps() {
218+
task downloadBootstraps() {
237219
doLast {
238-
def version = 12
239-
setupBootstrap("aarch64", "5e07239cad78050f56a28f9f88a0b485cead45864c6c00e1a654c728152b0244", version)
240-
setupBootstrap("arm", "fc72279c480c1eea46b6f0fcf78dc57599116c16dcf3b2b970a9ef828f0ec30b", version)
241-
setupBootstrap("i686", "895680fc967aecfa4ed77b9dc03aab95d86345be69df48402c63bfc0178337f6", version)
242-
setupBootstrap("x86_64", "8714ab8a5ff4e1f5f3ec01e7d0294776bfcffb187c84fa95270ec67ede8f682e", version)
220+
def packageVariant = project.ext.packageVariant
221+
if (packageVariant == "apt-android-7") {
222+
def version = "2026.02.12-r1" + "%2B" + "apt.android-7"
223+
downloadBootstrap("aarch64", "ea2aeba8819e517db711f8c32369e89e7c52cee73e07930ff91185e1ab93f4f3", version)
224+
downloadBootstrap("arm", "a38f4d3b2f735f83be2bf54eff463e86dc32a3e2f9f861c1557c4378d249c018", version)
225+
downloadBootstrap("i686", "f5bc0b025b9f3b420b5fcaeefc064f888f5f22a0d6fd7090f4aac0c33eb3555b", version)
226+
downloadBootstrap("x86_64", "b7fd0f2e3a4de534be3144f9f91acc768630fc463eaf134ab2e64c545e834f7a", version)
227+
} else if (packageVariant == "apt-android-5") {
228+
def version = "2022.04.28-r6" + "+" + packageVariant
229+
downloadBootstrap("aarch64", "913609d439415c828c5640be1b0561467e539cb1c7080662decaaca2fb4820e7", version)
230+
downloadBootstrap("arm", "26bfb45304c946170db69108e5eb6e3641aad751406ce106c80df80cad2eccf8", version)
231+
downloadBootstrap("i686", "46dcfeb5eef67ba765498db9fe4c50dc4690805139aa0dd141a9d8ee0693cd27", version)
232+
downloadBootstrap("x86_64", "615b590679ee6cd885b7fd2ff9473c845e920f9b422f790bb158c63fe42b8481", version)
233+
} else {
234+
throw new GradleException("Unsupported TERMUX_PACKAGE_VARIANT \"" + packageVariant + "\"")
235+
}
243236
}
244237
}
245238

246239
afterEvaluate {
247240
android.applicationVariants.all { variant ->
248-
variant.javaCompileProvider.get().dependsOn(setupBootstraps)
241+
variant.javaCompileProvider.get().dependsOn(downloadBootstraps)
249242
}
250243
}
251-
repositories {
252-
mavenCentral()
253-
}

app/src/main/AndroidManifest.xml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,9 @@
3636
android:maxSdkVersion="29" />
3737

3838
<application
39-
android:extractNativeLibs="true"
4039
android:allowBackup="false"
4140
android:icon="@drawable/ic_launcher"
4241
android:banner="@drawable/banner"
43-
android:extractNativeLibs="true"
44-
android:icon="@drawable/ic_launcher"
4542
android:label="@string/application_name"
4643
android:supportsRtl="false"
4744
android:theme="@style/Theme.Termux"

build.gradle

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
1-
2-
31
buildscript {
4-
ext.kotlin_version = '1.4.0'
52
repositories {
63
mavenCentral()
74
google()
85
}
96
dependencies {
10-
classpath 'com.android.tools.build:gradle:3.5.2'
7+
classpath "com.android.tools.build:gradle:8.13.2"
118
}
129
}
1310

14-
allprojects {
11+
allprojects{
1512
repositories {
1613
google()
1714
mavenCentral()

0 commit comments

Comments
 (0)