diff --git a/.gitignore b/.gitignore index 69111c9abb..d4b4d6cb33 100644 --- a/.gitignore +++ b/.gitignore @@ -75,3 +75,4 @@ website/package-lock.json # Ignore test files website/.cache website/test/stubs-layout-cache/_includes/*.js +apps/android/app/release diff --git a/apps/android/app/build.gradle b/apps/android/app/build.gradle index b69479412f..60036f1b25 100644 --- a/apps/android/app/build.gradle +++ b/apps/android/app/build.gradle @@ -9,15 +9,12 @@ android { defaultConfig { applicationId "chat.simplex.app" - minSdk 29 + minSdk 26 targetSdk 32 versionCode 107 versionName "4.6" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - ndk { - abiFilters 'arm64-v8a' - } vectorDrawables { useSupportLibrary true } @@ -77,10 +74,27 @@ android { jniLibs.useLegacyPackaging = compression_level != "0" } def isRelease = gradle.getStartParameter().taskNames.find({ it.toLowerCase().contains("release") }) != null + def isBundle = gradle.getStartParameter().taskNames.find({ it.toLowerCase().contains("bundle") }) != null // if (isRelease) { // Comma separated list of languages that will be included in the apk android.defaultConfig.resConfigs("en", "cs", "de", "es", "fr", "it", "nl", "ru", "zh-rCN") // } + if (isBundle) { + defaultConfig.ndk.abiFilters 'arm64-v8a', 'armeabi-v7a' + } else { + splits { + abi { + enable true + reset() + if (isRelease) { + include 'arm64-v8a', 'armeabi-v7a' + } else { + include 'arm64-v8a', 'armeabi-v7a' + universalApk false + } + } + } + } } dependencies { @@ -196,6 +210,8 @@ tasks.register("compressApk") { if (project.properties['android.injected.signing.key.alias'] != null && buildType == 'release') { new File(outputDir, "app-release.apk").renameTo(new File(outputDir, "simplex.apk")) + new File(outputDir, "app-armeabi-v7a-release.apk").renameTo(new File(outputDir, "simplex-armv7a.apk")) + new File(outputDir, "app-arm64-v8a-release.apk").renameTo(new File(outputDir, "simplex.apk")) } // View all gradle properties set diff --git a/apps/android/app/src/main/cpp/CMakeLists.txt b/apps/android/app/src/main/cpp/CMakeLists.txt index e97f01708f..de688975c6 100644 --- a/apps/android/app/src/main/cpp/CMakeLists.txt +++ b/apps/android/app/src/main/cpp/CMakeLists.txt @@ -53,10 +53,6 @@ add_library( support SHARED IMPORTED ) set_target_properties( support PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libsupport.so) -add_library( crypto SHARED IMPORTED ) -set_target_properties( crypto PROPERTIES IMPORTED_LOCATION - ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libcrypto.so) - # Specifies libraries CMake should link to your target library. You # can link multiple libraries, such as libraries you define in this # build script, prebuilt third-party libraries, or system libraries. @@ -64,7 +60,7 @@ set_target_properties( crypto PROPERTIES IMPORTED_LOCATION target_link_libraries( # Specifies the target library. app-lib - simplex support crypto + simplex support # Links the target library to the log library # included in the NDK. diff --git a/apps/android/app/src/main/cpp/simplex-api.c b/apps/android/app/src/main/cpp/simplex-api.c index e3f1c13cf9..edd07bd63b 100644 --- a/apps/android/app/src/main/cpp/simplex-api.c +++ b/apps/android/app/src/main/cpp/simplex-api.c @@ -7,6 +7,17 @@ void hs_init(int * argc, char **argv[]); void setLineBuffering(void); int pipe_std_to_socket(const char * name); +extern void __svfscanf(void){}; +extern void __vfwscanf(void){}; +extern void __memset_chk_fail(void){}; +extern void __strcpy_chk_generic(void){}; +extern void __strcat_chk_generic(void){}; +extern void __libc_globals(void){}; +extern void __rel_iplt_start(void){}; + +// Android 9 only, not 13 +extern void reallocarray(void){}; + JNIEXPORT jint JNICALL Java_chat_simplex_app_SimplexAppKt_pipeStdOutToSocket(JNIEnv *env, __unused jclass clazz, jstring socket_name) { const char *name = (*env)->GetStringUTFChars(env, socket_name, JNI_FALSE); diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ComposeView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/ComposeView.kt index 7d0ebe38c3..d63385ee2e 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ComposeView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/ComposeView.kt @@ -7,11 +7,10 @@ import android.Manifest import android.app.Activity import android.content.* import android.content.pm.PackageManager -import android.graphics.Bitmap -import android.graphics.ImageDecoder -import android.graphics.ImageDecoder.DecodeException +import android.graphics.* import android.graphics.drawable.AnimatedImageDrawable import android.net.Uri +import android.os.Build import android.provider.MediaStore import android.util.Log import android.widget.Toast @@ -36,6 +35,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.unit.dp import androidx.core.content.ContextCompat +import androidx.core.net.toFile import chat.simplex.app.* import chat.simplex.app.R import chat.simplex.app.model.* @@ -186,10 +186,11 @@ fun ComposeView( val textStyle = remember { mutableStateOf(smallFont) } val cameraLauncher = rememberCameraLauncher { uri: Uri? -> if (uri != null) { - val source = ImageDecoder.createSource(SimplexApp.context.contentResolver, uri) - val bitmap = ImageDecoder.decodeBitmap(source) - val imagePreview = resizeImageToStrSize(bitmap, maxDataSize = 14000) - composeState.value = composeState.value.copy(preview = ComposePreview.ImagePreview(listOf(imagePreview), listOf(UploadContent.SimpleImage(uri)))) + val bitmap: Bitmap? = getBitmapFromUri(uri) + if (bitmap != null) { + val imagePreview = resizeImageToStrSize(bitmap, maxDataSize = 14000) + composeState.value = composeState.value.copy(preview = ComposePreview.ImagePreview(listOf(imagePreview), listOf(UploadContent.SimpleImage(uri)))) + } } } val cameraPermissionLauncher = rememberPermissionLauncher { isGranted: Boolean -> @@ -203,19 +204,12 @@ fun ComposeView( val content = ArrayList() val imagesPreview = ArrayList() uris.forEach { uri -> - val source = ImageDecoder.createSource(context.contentResolver, uri) - val drawable = try { - ImageDecoder.decodeDrawable(source) - } catch (e: DecodeException) { - AlertManager.shared.showAlertMsg( - title = generalGetString(R.string.image_decoding_exception_title), - text = generalGetString(R.string.image_decoding_exception_desc) - ) - Log.e(TAG, "Error while decoding drawable: ${e.stackTraceToString()}") - null - } - var bitmap: Bitmap? = if (drawable != null) ImageDecoder.decodeBitmap(source) else null - if (drawable is AnimatedImageDrawable) { + val drawable = getDrawableFromUri(uri) + var bitmap: Bitmap? = if (drawable != null) getBitmapFromUri(uri) else null + val isAnimNewApi = Build.VERSION.SDK_INT >= 28 && drawable is AnimatedImageDrawable + val isAnimOldApi = Build.VERSION.SDK_INT < 28 && + (getFileName(SimplexApp.context, uri)?.endsWith(".gif") == true || getFileName(SimplexApp.context, uri)?.endsWith(".webp") == true) + if (isAnimNewApi || isAnimOldApi) { // It's a gif or webp val fileSize = getFileSize(context, uri) if (fileSize != null && fileSize <= MAX_FILE_SIZE) { diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/SendMsgView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/SendMsgView.kt index d213296a3e..f1b3b47db5 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/SendMsgView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/SendMsgView.kt @@ -8,10 +8,12 @@ import android.content.pm.ActivityInfo import android.content.res.Configuration import android.os.Build import android.text.InputType +import android.util.Log import android.view.ViewGroup import android.view.WindowManager import android.view.inputmethod.* import android.widget.EditText +import android.widget.TextView import androidx.compose.animation.core.* import androidx.compose.foundation.* import androidx.compose.foundation.interaction.MutableInteractionSource @@ -50,6 +52,7 @@ import chat.simplex.app.views.chat.item.ItemAction import chat.simplex.app.views.helpers.* import com.google.accompanist.permissions.rememberMultiplePermissionsState import kotlinx.coroutines.* +import java.lang.reflect.Field @Composable fun SendMsgView( @@ -240,7 +243,17 @@ private fun NativeKeyboard( editText.background = drawable editText.setPadding(paddingStart, paddingTop, paddingEnd, paddingBottom) editText.setText(cs.message) - editText.textCursorDrawable?.let { DrawableCompat.setTint(it, HighOrLowlight.toArgb()) } + if (Build.VERSION.SDK_INT >= 29) { + editText.textCursorDrawable?.let { DrawableCompat.setTint(it, HighOrLowlight.toArgb()) } + } else { + try { + val f: Field = TextView::class.java.getDeclaredField("mCursorDrawableRes") + f.isAccessible = true + f.set(editText, R.drawable.edit_text_cursor) + } catch (e: Exception) { + Log.e(chat.simplex.app.TAG, e.stackTraceToString()) + } + } editText.doOnTextChanged { text, _, _, _ -> onMessageChange(text.toString()) } editText.doAfterTextChanged { text -> if (composeState.value.preview is ComposePreview.VoicePreview && text.toString() != "") editText.setText("") } editText diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/ChatItemView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/ChatItemView.kt index dee77e4a4d..432fb728fa 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/ChatItemView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/ChatItemView.kt @@ -1,5 +1,7 @@ package chat.simplex.app.views.chat.item +import android.Manifest +import android.os.Build import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape @@ -25,6 +27,7 @@ import chat.simplex.app.ui.theme.SimpleXTheme import chat.simplex.app.views.chat.ComposeContextItem import chat.simplex.app.views.chat.ComposeState import chat.simplex.app.views.helpers.* +import com.google.accompanist.permissions.rememberPermissionState import kotlinx.datetime.Clock // TODO refactor so that FramedItemView can show all CIContent items if they're deleted (see Swift code) @@ -131,9 +134,16 @@ fun ChatItemView( if (cItem.content.msgContent is MsgContent.MCImage || cItem.content.msgContent is MsgContent.MCFile || cItem.content.msgContent is MsgContent.MCVoice) { val filePath = getLoadedFilePath(context, cItem.file) if (filePath != null) { + val writePermissionState = rememberPermissionState(permission = Manifest.permission.WRITE_EXTERNAL_STORAGE) ItemAction(stringResource(R.string.save_verb), Icons.Outlined.SaveAlt, onClick = { when (cItem.content.msgContent) { - is MsgContent.MCImage -> saveImage(context, cItem.file) + is MsgContent.MCImage -> { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R || writePermissionState.hasPermission) { + saveImage(context, cItem.file) + } else { + writePermissionState.launchPermissionRequest() + } + } is MsgContent.MCFile -> saveFileLauncher.launch(cItem.file?.fileName) is MsgContent.MCVoice -> saveFileLauncher.launch(cItem.file?.fileName) else -> {} diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseView.kt index b0e2524d17..4c9bcf6b9f 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseView.kt @@ -8,7 +8,6 @@ import SectionView import android.content.Context import android.content.res.Configuration import android.net.Uri -import android.os.FileUtils import android.util.Log import android.widget.Toast import androidx.activity.compose.ManagedActivityResultLauncher @@ -40,6 +39,7 @@ import chat.simplex.app.views.helpers.* import chat.simplex.app.views.usersettings.* import kotlinx.coroutines.* import kotlinx.datetime.* +import org.apache.commons.io.IOUtils import java.io.* import java.text.SimpleDateFormat import java.util.* @@ -620,7 +620,7 @@ private fun saveArchiveFromUri(context: Context, importedArchiveUri: Uri): Strin if (inputStream != null && archiveName != null) { val archivePath = "${context.cacheDir}/$archiveName" val destFile = File(archivePath) - FileUtils.copy(inputStream, FileOutputStream(destFile)) + IOUtils.copy(inputStream, FileOutputStream(destFile)) archivePath } else { Log.e(TAG, "saveArchiveFromUri null inputStream") diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/GetImageView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/GetImageView.kt index 1ccc6803af..9356ebfa65 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/GetImageView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/GetImageView.kt @@ -6,7 +6,6 @@ import android.content.* import android.content.Intent.FLAG_ACTIVITY_NEW_TASK import android.content.pm.PackageManager import android.graphics.* -import android.graphics.ImageDecoder.DecodeException import android.net.Uri import android.provider.MediaStore import android.util.Base64 @@ -114,7 +113,7 @@ fun base64ToBitmap(base64ImageString: String): Bitmap { class CustomTakePicturePreview(var uri: Uri?, var tmpFile: File?): ActivityResultContract() { @CallSuper override fun createIntent(context: Context, input: Void?): Intent { - tmpFile = File.createTempFile("image", ".bmp", context.filesDir) + tmpFile = File.createTempFile("image", ".bmp", File(getAppFilesDirectory(SimplexApp.context))) // Since the class should return Uri, the file should be deleted somewhere else. And in order to be sure, delegate this to system tmpFile?.deleteOnExit() uri = FileProvider.getUriForFile(context, "${BuildConfig.APPLICATION_ID}.provider", tmpFile!!) @@ -205,17 +204,10 @@ fun GetImageBottomSheet( val context = LocalContext.current val processPickedImage = { uri: Uri? -> if (uri != null) { - val source = ImageDecoder.createSource(context.contentResolver, uri) - try { - val bitmap = ImageDecoder.decodeBitmap(source) + val bitmap = getBitmapFromUri(uri) + if (bitmap != null) { imageBitmap.value = uri onImageChange(bitmap) - } catch (e: DecodeException) { - Log.e(TAG, "Unable to decode the image: ${e.stackTraceToString()}") - AlertManager.shared.showAlertMsg( - title = generalGetString(R.string.image_decoding_exception_title), - text = generalGetString(R.string.image_decoding_exception_desc) - ) } } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Share.kt b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Share.kt index 4a1b8a4f82..8c05a17ecc 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Share.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Share.kt @@ -1,5 +1,6 @@ package chat.simplex.app.views.helpers +import android.Manifest import android.content.* import android.net.Uri import android.provider.MediaStore @@ -81,6 +82,7 @@ fun imageMimeType(fileName: String): String { } } +/** Before calling, make sure the user allows to write to external storage [Manifest.permission.WRITE_EXTERNAL_STORAGE] */ fun saveImage(cxt: Context, ciFile: CIFile?) { val filePath = getLoadedFilePath(cxt, ciFile) val fileName = ciFile?.fileName diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Util.kt b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Util.kt index 17a462f589..cff2b41416 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Util.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Util.kt @@ -9,6 +9,7 @@ import android.content.res.Configuration import android.content.res.Resources import android.graphics.* import android.graphics.Typeface +import android.graphics.drawable.Drawable import android.net.Uri import android.os.* import android.provider.OpenableColumns @@ -33,10 +34,12 @@ import androidx.compose.ui.unit.* import androidx.core.content.FileProvider import androidx.core.text.HtmlCompat import chat.simplex.app.* +import chat.simplex.app.R import chat.simplex.app.model.* import kotlinx.coroutines.* import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString +import org.apache.commons.io.IOUtils import java.io.* import java.text.SimpleDateFormat import java.util.* @@ -322,6 +325,14 @@ fun getFileName(context: Context, uri: Uri): String? { } } +fun getAppFilePath(context: Context, uri: Uri): String? { + return context.contentResolver.query(uri, null, null, null, null)?.use { cursor -> + val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME) + cursor.moveToFirst() + getAppFilePath(context, cursor.getString(nameIndex)) + } +} + fun getFileSize(context: Context, uri: Uri): Long? { return context.contentResolver.query(uri, null, null, null, null)?.use { cursor -> val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE) @@ -330,9 +341,48 @@ fun getFileSize(context: Context, uri: Uri): Long? { } } +fun getBitmapFromUri(uri: Uri, withAlertOnException: Boolean = true): Bitmap? { + return if (Build.VERSION.SDK_INT >= 28) { + val source = ImageDecoder.createSource(SimplexApp.context.contentResolver, uri) + try { + ImageDecoder.decodeBitmap(source) + } catch (e: android.graphics.ImageDecoder.DecodeException) { + Log.e(TAG, "Unable to decode the image: ${e.stackTraceToString()}") + if (withAlertOnException) { + AlertManager.shared.showAlertMsg( + title = generalGetString(R.string.image_decoding_exception_title), + text = generalGetString(R.string.image_decoding_exception_desc) + ) + } + null + } + } else { + BitmapFactory.decodeFile(getAppFilePath(SimplexApp.context, uri)) + } +} + +fun getDrawableFromUri(uri: Uri, withAlertOnException: Boolean = true): Drawable? { + return if (Build.VERSION.SDK_INT >= 28) { + val source = ImageDecoder.createSource(SimplexApp.context.contentResolver, uri) + try { + ImageDecoder.decodeDrawable(source) + } catch (e: android.graphics.ImageDecoder.DecodeException) { + if (withAlertOnException) { + AlertManager.shared.showAlertMsg( + title = generalGetString(R.string.image_decoding_exception_title), + text = generalGetString(R.string.image_decoding_exception_desc) + ) + } + Log.e(TAG, "Error while decoding drawable: ${e.stackTraceToString()}") + null + } + } else { + Drawable.createFromPath(getAppFilePath(SimplexApp.context, uri)) + } +} + fun saveImage(context: Context, uri: Uri): String? { - val source = ImageDecoder.createSource(SimplexApp.context.contentResolver, uri) - val bitmap = ImageDecoder.decodeBitmap(source) + val bitmap = getBitmapFromUri(uri) ?: return null return saveImage(context, bitmap) } @@ -403,7 +453,7 @@ fun saveFileFromUri(context: Context, uri: Uri): String? { if (inputStream != null && fileToSave != null) { val destFileName = uniqueCombine(context, fileToSave) val destFile = File(getAppFilePath(context, destFileName)) - FileUtils.copy(inputStream, FileOutputStream(destFile)) + IOUtils.copy(inputStream, FileOutputStream(destFile)) destFileName } else { Log.e(chat.simplex.app.TAG, "Util.kt saveFileFromUri null inputStream") diff --git a/apps/android/app/src/main/res/drawable/edit_text_cursor.xml b/apps/android/app/src/main/res/drawable/edit_text_cursor.xml new file mode 100644 index 0000000000..683c3a4dd4 --- /dev/null +++ b/apps/android/app/src/main/res/drawable/edit_text_cursor.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/android/app/src/main/res/values/colors.xml b/apps/android/app/src/main/res/values/colors.xml index 1833a6d9a3..e1a994e57f 100644 --- a/apps/android/app/src/main/res/values/colors.xml +++ b/apps/android/app/src/main/res/values/colors.xml @@ -2,5 +2,6 @@ #FF000000 #FFFFFFFF + #8b8786 #121212 \ No newline at end of file diff --git a/apps/android/build.gradle b/apps/android/build.gradle index e7be963f32..08dbb3f98d 100644 --- a/apps/android/build.gradle +++ b/apps/android/build.gradle @@ -8,6 +8,7 @@ buildscript { compose_version = localProperties['compose_version'] ?: '1.2.0-beta02' kotlin_version = localProperties['kotlin_version'] ?: '1.6.21' gradle_plugin_version = localProperties['gradle_plugin_version'] ?: '7.2.0' + abi_filter = localProperties['abi_filter'] ?: 'arm64-v8a' // Name that will be shown for debug build. By default it is from strings app_name = localProperties['app_name'] ?: "@string/app_name" diff --git a/flake.lock b/flake.lock index c628113e63..2c133a9b63 100644 --- a/flake.lock +++ b/flake.lock @@ -51,11 +51,11 @@ "cabal-34": { "flake": false, "locked": { - "lastModified": 1640353650, - "narHash": "sha256-N1t6M3/wqj90AEdRkeC8i923gQYUpzSr8b40qVOZ1Rk=", + "lastModified": 1645834128, + "narHash": "sha256-wG3d+dOt14z8+ydz4SL7pwGfe7SiimxcD/LOuPCV6xM=", "owner": "haskell", "repo": "cabal", - "rev": "942639c18c0cd8ec53e0a6f8d120091af35312cd", + "rev": "5ff598c67f53f7c4f48e31d722ba37172230c462", "type": "github" }, "original": { @@ -68,11 +68,11 @@ "cabal-36": { "flake": false, "locked": { - "lastModified": 1641652457, - "narHash": "sha256-BlFPKP4C4HRUJeAbdembX1Rms1LD380q9s0qVDeoAak=", + "lastModified": 1669081697, + "narHash": "sha256-I5or+V7LZvMxfbYgZATU4awzkicBwwok4mVoje+sGmU=", "owner": "haskell", "repo": "cabal", - "rev": "f27667f8ec360c475027dcaee0138c937477b070", + "rev": "8fd619e33d34924a94e691c5fea2c42f0fc7f144", "type": "github" }, "original": { @@ -159,15 +159,16 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1635892615, - "narHash": "sha256-harGbMZr4hzat2BWBU+Y5OYXlu+fVz7E4WeQzHi5o8A=", + "lastModified": 1672831974, + "narHash": "sha256-z9k3MfslLjWQfnjBtEtJZdq3H7kyi2kQtUThfTgdRk0=", "owner": "input-output-hk", "repo": "flake-compat", - "rev": "eca47d3377946315596da653862d341ee5341318", + "rev": "45f2638735f8cdc40fe302742b79f248d23eb368", "type": "github" }, "original": { "owner": "input-output-hk", + "ref": "hkm/gitlab-fix", "repo": "flake-compat", "type": "github" } @@ -190,11 +191,11 @@ }, "flake-utils": { "locked": { - "lastModified": 1667395993, - "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", + "lastModified": 1676283394, + "narHash": "sha256-XX2f9c3iySLCw54rJ/CZs+ZK6IQy7GXNY4nSOyu2QG4=", "owner": "numtide", "repo": "flake-utils", - "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", + "rev": "3db36a8b464d0c4532ba1c7dda728f4576d6d073", "type": "github" }, "original": { @@ -205,11 +206,11 @@ }, "flake-utils_2": { "locked": { - "lastModified": 1644229661, - "narHash": "sha256-1YdnJAsNy69bpcjuoKdOYQX0YxZBiCYZo4Twxerqv7k=", + "lastModified": 1667395993, + "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", "owner": "numtide", "repo": "flake-utils", - "rev": "3cecb5b042f7f209c56ffd8371b2711a290ec797", + "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", "type": "github" }, "original": { @@ -248,21 +249,6 @@ "type": "github" } }, - "flake-utils_5": { - "locked": { - "lastModified": 1653893745, - "narHash": "sha256-0jntwV3Z8//YwuOjzhV2sgJJPt+HY6KhU7VZUL0fKZQ=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "1ed9fb1935d260de5fe1c2f7ee0ebaae17ed2fa1", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, "ghc-8.6.5-iohk": { "flake": false, "locked": { @@ -302,11 +288,11 @@ "hackage": { "flake": false, "locked": { - "lastModified": 1672446463, - "narHash": "sha256-N5dcK1V+BLQeri5oB0ZSk2ABPs/hTGBC7jZvcY9CVLs=", + "lastModified": 1676679913, + "narHash": "sha256-nW7ApRgiA9uChV/UrW89HK75rIToLt7XtSrkodO0Nbc=", "owner": "input-output-hk", "repo": "hackage.nix", - "rev": "7289869780da23633bc193e11d2da94ecee1489d", + "rev": "c37cffd51315d8e27dd8d3faf75abf897e39c8c8", "type": "github" }, "original": { @@ -330,6 +316,7 @@ ], "hpc-coveralls": "hpc-coveralls", "hydra": "hydra", + "iserv-proxy": "iserv-proxy", "nixpkgs": [ "nixpkgs" ], @@ -337,21 +324,23 @@ "nixpkgs-2105": "nixpkgs-2105", "nixpkgs-2111": "nixpkgs-2111", "nixpkgs-2205": "nixpkgs-2205", + "nixpkgs-2211": "nixpkgs-2211", "nixpkgs-unstable": "nixpkgs-unstable", "old-ghc-nix": "old-ghc-nix", "stackage": "stackage", "tullia": "tullia" }, "locked": { - "lastModified": 1672501055, - "narHash": "sha256-Wy6KqoYqQOP1rBvfHUvM3ex8HIdBA4kwnvZj2qQ1VLU=", - "owner": "simplex-chat", + "lastModified": 1677975916, + "narHash": "sha256-dbe8lEEPyfzjdRwpePClv7J9p9lQg7BwbBqAMCw4RLw=", + "owner": "input-output-hk", "repo": "haskell.nix", - "rev": "dc719cd6dc318923c4a524d005c13f474c00d3d3", + "rev": "ab5efd87ce3fd8ade38a01d97693d29a4f1ae7e4", "type": "github" }, "original": { - "owner": "simplex-chat", + "owner": "input-output-hk", + "ref": "armv7a", "repo": "haskell.nix", "type": "github" } @@ -383,11 +372,11 @@ ] }, "locked": { - "lastModified": 1646878427, - "narHash": "sha256-KtbrofMtN8GlM7D+n90kixr7QpSlVmdN+vK5CA/aRzc=", + "lastModified": 1671755331, + "narHash": "sha256-hXsgJj0Cy0ZiCiYdW2OdBz5WmFyOMKuw4zyxKpgUKm4=", "owner": "NixOS", "repo": "hydra", - "rev": "28b682b85b7efc5cf7974065792a1f22203a5927", + "rev": "f48f00ee6d5727ae3e488cbf9ce157460853fea8", "type": "github" }, "original": { @@ -395,6 +384,46 @@ "type": "indirect" } }, + "incl": { + "inputs": { + "nixlib": [ + "haskellNix", + "tullia", + "std", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1669263024, + "narHash": "sha256-E/+23NKtxAqYG/0ydYgxlgarKnxmDbg6rCMWnOBqn9Q=", + "owner": "divnix", + "repo": "incl", + "rev": "ce7bebaee048e4cd7ebdb4cee7885e00c4e2abca", + "type": "github" + }, + "original": { + "owner": "divnix", + "repo": "incl", + "type": "github" + } + }, + "iserv-proxy": { + "flake": false, + "locked": { + "lastModified": 1670983692, + "narHash": "sha256-avLo34JnI9HNyOuauK5R69usJm+GfW3MlyGlYxZhTgY=", + "ref": "hkm/remote-iserv", + "rev": "50d0abb3317ac439a4e7495b185a64af9b7b9300", + "revCount": 10, + "type": "git", + "url": "https://gitlab.haskell.org/hamishmack/iserv-proxy.git" + }, + "original": { + "ref": "hkm/remote-iserv", + "type": "git", + "url": "https://gitlab.haskell.org/hamishmack/iserv-proxy.git" + } + }, "lowdown-src": { "flake": false, "locked": { @@ -411,25 +440,14 @@ "type": "github" } }, - "mdbook-kroki-preprocessor": { - "flake": false, - "locked": { - "lastModified": 1661755005, - "narHash": "sha256-1TJuUzfyMycWlOQH67LR63/ll2GDZz25I3JfScy/Jnw=", - "owner": "JoelCourtney", - "repo": "mdbook-kroki-preprocessor", - "rev": "93adb5716d035829efed27f65f2f0833a7d3e76f", - "type": "github" - }, - "original": { - "owner": "JoelCourtney", - "repo": "mdbook-kroki-preprocessor", - "type": "github" - } - }, "n2c": { "inputs": { - "flake-utils": "flake-utils_5", + "flake-utils": [ + "haskellNix", + "tullia", + "std", + "flake-utils" + ], "nixpkgs": [ "haskellNix", "tullia", @@ -458,16 +476,16 @@ "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1643066034, - "narHash": "sha256-xEPeMcNJVOeZtoN+d+aRwolpW8mFSEQx76HTRdlhPhg=", + "lastModified": 1661606874, + "narHash": "sha256-9+rpYzI+SmxJn+EbYxjGv68Ucp22bdFUSy/4LkHkkDQ=", "owner": "NixOS", "repo": "nix", - "rev": "a1cd7e58606a41fcf62bf8637804cf8306f17f62", + "rev": "11e45768b34fdafdcf019ddbd337afa16127ff0f", "type": "github" }, "original": { "owner": "NixOS", - "ref": "2.6.0", + "ref": "2.11.0", "repo": "nix", "type": "github" } @@ -563,17 +581,18 @@ }, "nixpkgs": { "locked": { - "lastModified": 1632864508, - "narHash": "sha256-d127FIvGR41XbVRDPVvozUPQ/uRHbHwvfyKHwEt5xFM=", + "lastModified": 1657693803, + "narHash": "sha256-G++2CJ9u0E7NNTAi9n5G8TdDmGJXcIjkJ3NF8cetQB8=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "82891b5e2c2359d7e58d08849e4c89511ab94234", + "rev": "365e1b3a859281cf11b94f87231adeabbdd878a2", "type": "github" }, "original": { - "id": "nixpkgs", - "ref": "nixos-21.05-small", - "type": "indirect" + "owner": "NixOS", + "ref": "nixos-22.05-small", + "repo": "nixpkgs", + "type": "github" } }, "nixpkgs-2003": { @@ -626,11 +645,11 @@ }, "nixpkgs-2205": { "locked": { - "lastModified": 1663981975, - "narHash": "sha256-TKaxWAVJR+a5JJauKZqibmaM5e/Pi5tBDx9s8fl/kSE=", + "lastModified": 1672580127, + "narHash": "sha256-3lW3xZslREhJogoOkjeZtlBtvFMyxHku7I/9IVehhT8=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "309faedb8338d3ae8ad8f1043b3ccf48c9cc2970", + "rev": "0874168639713f547c05947c76124f78441ea46c", "type": "github" }, "original": { @@ -640,6 +659,22 @@ "type": "github" } }, + "nixpkgs-2211": { + "locked": { + "lastModified": 1675730325, + "narHash": "sha256-uNvD7fzO5hNlltNQUAFBPlcEjNG5Gkbhl/ROiX+GZU4=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "b7ce17b1ebf600a72178f6302c77b6382d09323f", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-22.11-darwin", + "repo": "nixpkgs", + "type": "github" + } + }, "nixpkgs-regression": { "locked": { "lastModified": 1643052045, @@ -650,18 +685,19 @@ "type": "github" }, "original": { - "id": "nixpkgs", + "owner": "NixOS", + "repo": "nixpkgs", "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", - "type": "indirect" + "type": "github" } }, "nixpkgs-unstable": { "locked": { - "lastModified": 1663905476, - "narHash": "sha256-0CSwRKaYravh9v6qSlBpM0gNg0UhKT2lL7Yn6Zbx7UM=", + "lastModified": 1675758091, + "narHash": "sha256-7gFSQbSVAFUHtGCNHPF7mPc5CcqDk9M2+inlVPZSneg=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "e14f9fb57315f0d4abde222364f19f88c77d2b79", + "rev": "747927516efcb5e31ba03b7ff32f61f6d47e7d87", "type": "github" }, "original": { @@ -720,20 +756,35 @@ }, "nixpkgs_5": { "locked": { - "lastModified": 1669833724, - "narHash": "sha256-/HEZNyGbnQecrgJnfE8d0WC5c1xuPSD2LUpB6YXlg4c=", - "owner": "nixos", + "lastModified": 1676726892, + "narHash": "sha256-M7OYVR6dKmzmlebIjybFf3l18S2uur8lMyWWnHQooLY=", + "owner": "angerman", "repo": "nixpkgs", - "rev": "4d2b37a84fad1091b9de401eb450aae66f1a741e", + "rev": "729469087592bdea58b360de59dadf6d58714c42", "type": "github" }, "original": { - "owner": "nixos", - "ref": "22.11", + "owner": "angerman", + "ref": "release-22.11", "repo": "nixpkgs", "type": "github" } }, + "nosys": { + "locked": { + "lastModified": 1667881534, + "narHash": "sha256-FhwJ15uPLRsvaxtt/bNuqE/ykMpNAPF0upozFKhTtXM=", + "owner": "divnix", + "repo": "nosys", + "rev": "2d0d5207f6a230e9d0f660903f8db9807b54814f", + "type": "github" + }, + "original": { + "owner": "divnix", + "repo": "nosys", + "type": "github" + } + }, "old-ghc-nix": { "flake": false, "locked": { @@ -762,11 +813,11 @@ "stackage": { "flake": false, "locked": { - "lastModified": 1669598217, - "narHash": "sha256-UioviNyxA3fexeguXLQpgMR6uWL9Q/wulipCbET3C8w=", + "lastModified": 1677888571, + "narHash": "sha256-YkhRNOaN6QVagZo1cfykYV8KqkI8/q6r2F5+jypOma4=", "owner": "input-output-hk", "repo": "stackage.nix", - "rev": "8400280d894949e26354123aebc20801a2165182", + "rev": "cb50e6fabdfb2d7e655059039012ad0623f06a27", "type": "github" }, "original": { @@ -777,17 +828,23 @@ }, "std": { "inputs": { + "arion": [ + "haskellNix", + "tullia", + "std", + "blank" + ], "blank": "blank", "devshell": "devshell", "dmerge": "dmerge", "flake-utils": "flake-utils_4", + "incl": "incl", "makes": [ "haskellNix", "tullia", "std", "blank" ], - "mdbook-kroki-preprocessor": "mdbook-kroki-preprocessor", "microvm": [ "haskellNix", "tullia", @@ -797,14 +854,15 @@ "n2c": "n2c", "nixago": "nixago", "nixpkgs": "nixpkgs_4", + "nosys": "nosys", "yants": "yants" }, "locked": { - "lastModified": 1665513321, - "narHash": "sha256-D6Pacw9yf/HMs84KYuCxHXnNDL7v43gtcka5URagFqE=", + "lastModified": 1674526466, + "narHash": "sha256-tMTaS0bqLx6VJ+K+ZT6xqsXNpzvSXJTmogkraBGzymg=", "owner": "divnix", "repo": "std", - "rev": "94a90eedb9cfc115b12ae8f6622d9904788559e4", + "rev": "516387e3d8d059b50e742a2ff1909ed3c8f82826", "type": "github" }, "original": { @@ -824,11 +882,11 @@ "std": "std" }, "locked": { - "lastModified": 1666200256, - "narHash": "sha256-cJPS8zBu30SMhxMe7I8DWutwqMuhPsEez87y9gxMKc4=", + "lastModified": 1675695930, + "narHash": "sha256-B7rEZ/DBUMlK1AcJ9ajnAPPxqXY6zW2SBX+51bZV0Ac=", "owner": "input-output-hk", "repo": "tullia", - "rev": "575362c2244498e8d2c97f72861510fa72e75d44", + "rev": "621365f2c725608f381b3ad5b57afef389fd4c31", "type": "github" }, "original": { @@ -862,11 +920,11 @@ ] }, "locked": { - "lastModified": 1660507851, - "narHash": "sha256-BKjq7JnVuUR/xDtcv6Vm9GYGKAblisXrAgybor9hT/s=", + "lastModified": 1667096281, + "narHash": "sha256-wRRec6ze0gJHmGn6m57/zhz/Kdvp9HS4Nl5fkQ+uIuA=", "owner": "divnix", "repo": "yants", - "rev": "0b895ca02a8fa72bad50b454cb3e7d8a66407c96", + "rev": "d18f356ec25cb94dc9c275870c3a7927a10f8c3c", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 5279db48e8..f7e86fbb6f 100644 --- a/flake.nix +++ b/flake.nix @@ -1,7 +1,7 @@ { description = "nix flake for simplex-chat"; - inputs.nixpkgs.url = "github:nixos/nixpkgs/22.11"; - inputs.haskellNix.url = "github:simplex-chat/haskell.nix"; + inputs.nixpkgs.url = "github:angerman/nixpkgs/release-22.11"; + inputs.haskellNix.url = "github:input-output-hk/haskell.nix/armv7a"; inputs.haskellNix.inputs.nixpkgs.follows = "nixpkgs"; inputs.hackage = { url = "github:input-output-hk/hackage.nix"; @@ -12,7 +12,23 @@ outputs = { self, haskellNix, nixpkgs, flake-utils, ... }: let systems = [ "x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin" ]; in flake-utils.lib.eachSystem systems (system: - let pkgs = haskellNix.legacyPackages.${system}; in + # this android26 overlay makes the pkgsCross.{aarch64-android,armv7a-android-prebuilt} to set stdVer to 26 (Android 8). + let android26 = final: prev: { + pkgsCross = prev.pkgsCross // { + aarch64-android = import prev.path { + inherit system; + inherit (prev) overlays; + crossSystem = prev.lib.systems.examples.aarch64-android // { sdkVer = "26"; }; + }; + armv7a-android-prebuilt = import prev.path { + inherit system; + inherit (prev) overlays; + crossSystem = prev.lib.systems.examples.armv7a-android-prebuilt // { sdkVer = "26"; }; + }; + }; + }; in + # `appendOverlays` with a singleton is identical to `extend`. + let pkgs = haskellNix.legacyPackages.${system}.appendOverlays [android26]; in let drv' = { extra-modules, pkgs', ... }: pkgs'.haskell-nix.project { compiler-nix-name = "ghc8107"; index-state = "2022-06-20T00:00:00Z"; @@ -81,6 +97,7 @@ "x86_64-linux" = let androidPkgs = pkgs.pkgsCross.aarch64-android; + android32Pkgs = pkgs.pkgsCross.armv7a-android-prebuilt; # For some reason building libiconv with nixpgks android setup produces # LANGINFO_CODESET to be found, which is not compatible with android sdk 23; # so we'll patch up iconv to not include that. @@ -96,11 +113,32 @@ androidFFI = androidPkgs.libffi.overrideAttrs (old: { dontDisableStatic = true; hardeningDisable = [ "fortify" ]; - postConfigure = '' - echo "#undef HAVE_MEMFD_CREATE" >> aarch64-unknown-linux-android/fficonfig.h - ''; + }); + android32FFI = android32Pkgs.libffi.overrideAttrs (old: { + dontDisableStatic = true; + hardeningDisable = [ "fortify" ]; } );in { + "${pkgs.pkgsCross.musl64.hostPlatform.system}-static:exe:simplex-chat" = (drv pkgs.pkgsCross.musl64).simplex-chat.components.exes.simplex-chat; + "${pkgs.pkgsCross.musl32.hostPlatform.system}-static:exe:simplex-chat" = (drv pkgs.pkgsCross.musl32).simplex-chat.components.exes.simplex-chat; + # "${pkgs.pkgsCross.muslpi.hostPlatform.system}-static:exe:simplex-chat" = (drv pkgs.pkgsCross.muslpi).simplex-chat.components.exes.simplex-chat; + "${pkgs.pkgsCross.aarch64-multiplatform-musl.hostPlatform.system}-static:exe:simplex-chat" = (drv pkgs.pkgsCross.aarch64-multiplatform-musl).simplex-chat.components.exes.simplex-chat; + "armv7a-android:lib:support" = (drv android32Pkgs).android-support.components.library.override { + smallAddressSpace = true; enableShared = false; + setupBuildFlags = map (x: "--ghc-option=${x}") [ "-shared" "-o" "libsupport.so" ]; + postInstall = '' + + mkdir -p $out/_pkg + cp libsupport.so $out/_pkg + ${pkgs.patchelf}/bin/patchelf --remove-needed libunwind.so.1 $out/_pkg/libsupport.so + (cd $out/_pkg; ${pkgs.zip}/bin/zip -r -9 $out/pkg-armv7a-android-libsupport.zip *) + rm -fR $out/_pkg + + mkdir -p $out/nix-support + echo "file binary-dist \"$(echo $out/*.zip)\"" \ + > $out/nix-support/hydra-build-products + ''; + }; "aarch64-android:lib:support" = (drv androidPkgs).android-support.components.library.override { smallAddressSpace = true; enableShared = false; setupBuildFlags = map (x: "--ghc-option=${x}") [ "-shared" "-o" "libsupport.so" ]; @@ -117,6 +155,67 @@ > $out/nix-support/hydra-build-products ''; }; + "armv7a-android:lib:simplex-chat" = (drv' { + pkgs' = android32Pkgs; + extra-modules = [{ + packages.direct-sqlcipher.flags.openssl = true; + packages.direct-sqlcipher.components.library.libs = pkgs.lib.mkForce [ + (android32Pkgs.openssl.override { static = true; enableKTLS = false; }) + ]; + packages.direct-sqlcipher.patches = [ + ./scripts/nix/direct-sqlcipher-android-log.patch + ]; + }]; + }).simplex-chat.components.library.override { + smallAddressSpace = true; enableShared = false; + # for android we build a shared library, passing these arguments is a bit tricky, as + # we want only the threaded rts (HSrts_thr) and ffi to be linked, but not fed into iserv for + # template haskell cross compilation. Thus we just pass them as linker options (-optl). + setupBuildFlags = map (x: "--ghc-option=${x}") [ "-shared" "-o" "libsimplex.so" "-optl-lHSrts_thr" "-optl-lffi"]; + postInstall = '' + set -x + ${pkgs.tree}/bin/tree $out + mkdir -p $out/_pkg + # copy over includes, we might want those, but maybe not. + # cp -r $out/lib/*/*/include $out/_pkg/ + # find the libHS...ghc-X.Y.Z.a static library; this is the + # rolled up one with all dependencies included. + cp libsimplex.so $out/_pkg + # find ./dist -name "lib*.so" -exec cp {} $out/_pkg \; + # find ./dist -name "libHS*-ghc*.a" -exec cp {} $out/_pkg \; + # find ${android32FFI}/lib -name "*.a" -exec cp {} $out/_pkg \; + # find ${android32Pkgs.gmp6.override { withStatic = true; }}/lib -name "*.a" -exec cp {} $out/_pkg \; + # find ${androidIconv}/lib -name "*.a" -exec cp {} $out/_pkg \; + # find ${android32Pkgs.stdenv.cc.libc}/lib -name "*.a" -exec cp {} $out/_pkg \; + echo ${android32Pkgs.openssl.override { enableKTLS = false; }} + find ${(android32Pkgs.openssl.override { enableKTLS = false; }).out}/lib -name "*.so" -exec cp {} $out/_pkg \; + + # remove the .1 and other version suffixes from .so's. Androids linker + # doesn't play nice with them. + for lib in $out/_pkg/*.so; do + for dep in $(${pkgs.patchelf}/bin/patchelf --print-needed "$lib"); do + if [[ "''${dep##*.so}" ]]; then + echo "$lib : $dep -> ''${dep%%.so*}.so" + chmod +w "$lib" + ${pkgs.patchelf}/bin/patchelf --replace-needed "$dep" "''${dep%%.so*}.so" "$lib" + fi + done + done + + for lib in $out/_pkg/*.so; do + chmod +w "$lib" + ${pkgs.patchelf}/bin/patchelf --remove-needed libunwind.so "$lib" + [[ "$lib" != *libsimplex.so ]] && ${pkgs.patchelf}/bin/patchelf --set-soname "$(basename -a $lib)" "$lib" + done + + ${pkgs.tree}/bin/tree $out/_pkg + (cd $out/_pkg; ${pkgs.zip}/bin/zip -r -9 $out/pkg-armv7a-android-libsimplex.zip *) + rm -fR $out/_pkg + mkdir -p $out/nix-support + echo "file binary-dist \"$(echo $out/*.zip)\"" \ + > $out/nix-support/hydra-build-products + ''; + }; "aarch64-android:lib:simplex-chat" = (drv' { pkgs' = androidPkgs; extra-modules = [{ @@ -178,110 +277,6 @@ > $out/nix-support/hydra-build-products ''; }; - "x86_64-android:lib:support" = (drv androidPkgs).android-support.components.library.override { - smallAddressSpace = true; enableShared = false; - setupBuildFlags = map (x: "--ghc-option=${x}") [ "-shared" "-o" "libsupport.so" ]; - postInstall = '' - - mkdir -p $out/_pkg - cp libsupport.so $out/_pkg - ${pkgs.patchelf}/bin/patchelf --remove-needed libunwind.so.1 $out/_pkg/libsupport.so - (cd $out/_pkg; ${pkgs.zip}/bin/zip -r -9 $out/pkg-x86_64-android-libsupport.zip *) - rm -fR $out/_pkg - - mkdir -p $out/nix-support - echo "file binary-dist \"$(echo $out/*.zip)\"" \ - > $out/nix-support/hydra-build-products - ''; - }; - "x86_64-android:lib:simplex-chat" = (drv' { - pkgs' = androidPkgs; - extra-modules = [{ - packages.direct-sqlcipher.flags.openssl = true; - }]; - }).simplex-chat.components.library.override { - smallAddressSpace = true; enableShared = false; - # for android we build a shared library, passing these arguments is a bit tricky, as - # we want only the threaded rts (HSrts_thr) and ffi to be linked, but not fed into iserv for - # template haskell cross compilation. Thus we just pass them as linker options (-optl). - setupBuildFlags = map (x: "--ghc-option=${x}") [ "-shared" "-o" "libsimplex.so" "-optl-lHSrts_thr" "-optl-lffi"]; - postInstall = '' - ${pkgs.tree}/bin/tree $out - mkdir -p $out/_pkg - # copy over includes, we might want those, but maybe not. - # cp -r $out/lib/*/*/include $out/_pkg/ - # find the libHS...ghc-X.Y.Z.a static library; this is the - # rolled up one with all dependencies included. - cp libsimplex.so $out/_pkg - # find ./dist -name "lib*.so" -exec cp {} $out/_pkg \; - # find ./dist -name "libHS*-ghc*.a" -exec cp {} $out/_pkg \; - # find ${androidFFI}/lib -name "*.a" -exec cp {} $out/_pkg \; - # find ${androidPkgs.gmp6.override { withStatic = true; }}/lib -name "*.a" -exec cp {} $out/_pkg \; - # find ${androidIconv}/lib -name "*.a" -exec cp {} $out/_pkg \; - # find ${androidPkgs.stdenv.cc.libc}/lib -name "*.a" -exec cp {} $out/_pkg \; - - ${pkgs.patchelf}/bin/patchelf --remove-needed libunwind.so.1 $out/_pkg/libsimplex.so - - ${pkgs.tree}/bin/tree $out/_pkg - (cd $out/_pkg; ${pkgs.zip}/bin/zip -r -9 $out/pkg-x86_64-android-libsimplex.zip *) - rm -fR $out/_pkg - mkdir -p $out/nix-support - echo "file binary-dist \"$(echo $out/*.zip)\"" \ - > $out/nix-support/hydra-build-products - ''; - }; - "x86_64-linux:lib:support" = (drv androidPkgs).android-support.components.library.override { - smallAddressSpace = true; enableShared = false; - setupBuildFlags = map (x: "--ghc-option=${x}") [ "-shared" "-o" "libsupport.so" ]; - postInstall = '' - - mkdir -p $out/_pkg - cp libsupport.so $out/_pkg - ${pkgs.patchelf}/bin/patchelf --remove-needed libunwind.so.1 $out/_pkg/libsupport.so - (cd $out/_pkg; ${pkgs.zip}/bin/zip -r -9 $out/pkg-x86_64-linux-libsupport.zip *) - rm -fR $out/_pkg - - mkdir -p $out/nix-support - echo "file binary-dist \"$(echo $out/*.zip)\"" \ - > $out/nix-support/hydra-build-products - ''; - }; - "x86_64-linux:lib:simplex-chat" = (drv' { - pkgs' = androidPkgs; - extra-modules = [{ - packages.direct-sqlcipher.flags.openssl = true; - }]; - }).simplex-chat.components.library.override { - smallAddressSpace = true; enableShared = false; - # for android we build a shared library, passing these arguments is a bit tricky, as - # we want only the threaded rts (HSrts_thr) and ffi to be linked, but not fed into iserv for - # template haskell cross compilation. Thus we just pass them as linker options (-optl). - setupBuildFlags = map (x: "--ghc-option=${x}") [ "-shared" "-o" "libsimplex.so" "-optl-lHSrts_thr" "-optl-lffi"]; - postInstall = '' - ${pkgs.tree}/bin/tree $out - mkdir -p $out/_pkg - # copy over includes, we might want those, but maybe not. - # cp -r $out/lib/*/*/include $out/_pkg/ - # find the libHS...ghc-X.Y.Z.a static library; this is the - # rolled up one with all dependencies included. - cp libsimplex.so $out/_pkg - # find ./dist -name "lib*.so" -exec cp {} $out/_pkg \; - # find ./dist -name "libHS*-ghc*.a" -exec cp {} $out/_pkg \; - # find ${androidFFI}/lib -name "*.a" -exec cp {} $out/_pkg \; - # find ${androidPkgs.gmp6.override { withStatic = true; }}/lib -name "*.a" -exec cp {} $out/_pkg \; - # find ${androidIconv}/lib -name "*.a" -exec cp {} $out/_pkg \; - # find ${androidPkgs.stdenv.cc.libc}/lib -name "*.a" -exec cp {} $out/_pkg \; - - ${pkgs.patchelf}/bin/patchelf --remove-needed libunwind.so.1 $out/_pkg/libsimplex.so - - ${pkgs.tree}/bin/tree $out/_pkg - (cd $out/_pkg; ${pkgs.zip}/bin/zip -r -9 $out/pkg-x86_64-linux-libsimplex.zip *) - rm -fR $out/_pkg - mkdir -p $out/nix-support - echo "file binary-dist \"$(echo $out/*.zip)\"" \ - > $out/nix-support/hydra-build-products - ''; - }; }; # builds for iOS and iOS simulator diff --git a/scripts/android/build-android.sh b/scripts/android/build-android.sh index 9928a07366..6ff2f6e446 100755 --- a/scripts/android/build-android.sh +++ b/scripts/android/build-android.sh @@ -12,9 +12,9 @@ nix_install() { [ ! -d /nix ] && sudo sh -c "mkdir -p /nix && chown -R $u /nix" # Install nix - nix_ver="nix-2.11.1" + nix_ver="nix-2.14.1" nix_url="https://releases.nixos.org/nix/$nix_ver/install" - nix_hash="4569a01dc5f62056f29f3195673bc3242fc70bf2474927fb5d8549c4d997402d" + nix_hash="565974057264f0536f600c68d59395927cd73e9fc5a60f33c1906e8f7bc33fcf" curl -sSf "$nix_url" -o "$tmp/nix-install" printf "%s %s" "$nix_hash" "$tmp/nix-install" | sha256sum -c @@ -29,11 +29,16 @@ nix_setup() { } git_setup() { + [ "$folder" != "." ] && { + git clone --depth=1 https://github.com/simplex-chat/simplex-chat "$folder" + } + # Switch to nix-android branch git -C "$folder" checkout "$commit" # Create missing folders mkdir -p "$folder/apps/android/app/src/main/cpp/libs/arm64-v8a" + mkdir -p "$folder/apps/android/app/src/main/cpp/libs/armeabi-v7a" } checks() { @@ -55,10 +60,6 @@ checks() { esac done - [ "$folder" != "." ] && { - git clone https://github.com/simplex-chat/simplex-chat "$folder" - } - if [ -n "$commands_failed" ]; then commands_failed=${commands_failed% *} printf "%s is not found in your \$PATH. Please install them and re-run the script.\n" "$commands_failed" @@ -72,21 +73,31 @@ build() { # Build simplex lib nix build "$folder#hydraJobs.aarch64-android:lib:simplex-chat.x86_64-linux" unzip -o "$PWD/result/pkg-aarch64-android-libsimplex.zip" -d "$folder/apps/android/app/src/main/cpp/libs/arm64-v8a" + + nix build "$folder#hydraJobs.armv7a-android:lib:simplex-chat.x86_64-linux" + unzip -o "$PWD/result/pkg-armv7a-android-libsimplex.zip" -d "$folder/apps/android/app/src/main/cpp/libs/armeabi-v7a" # Build android suppprt lib nix build "$folder#hydraJobs.aarch64-android:lib:support.x86_64-linux" unzip -o "$PWD/result/pkg-aarch64-android-libsupport.zip" -d "$folder/apps/android/app/src/main/cpp/libs/arm64-v8a" + nix build "$folder#hydraJobs.armv7a-android:lib:support.x86_64-linux" + unzip -o "$PWD/result/pkg-armv7a-android-libsupport.zip" -d "$folder/apps/android/app/src/main/cpp/libs/armeabi-v7a" + sed -i.bak 's/${extract_native_libs}/true/' "$folder/apps/android/app/src/main/AndroidManifest.xml" + sed -i.bak '/android {/a lint {abortOnError false}' "$folder/apps/android/app/build.gradle" gradle -p "$folder/apps/android/" clean build assembleRelease - mkdir -p "$tmp/android" - unzip -oqd "$tmp/android/" "$folder/apps/android/app/build/outputs/apk/release/app-release-unsigned.apk" + mkdir -p "$tmp/android-aarch64" + unzip -oqd "$tmp/android-aarch64/" "$folder/apps/android/app/build/outputs/apk/release/app-arm64-v8a-release-unsigned.apk" + (cd "$tmp/android-aarch64" && zip -rq5 "$tmp/simplex-chat-aarch64.apk" . && zip -rq0 "$tmp/simplex-chat-aarch64.apk" resources.arsc res) + zipalign -p -f 4 "$tmp/simplex-chat-aarch64.apk" "$PWD/simplex-chat-aarch64.apk" - (cd "$tmp/android" && zip -rq5 "$tmp/simplex-chat.apk" . && zip -rq0 "$tmp/simplex-chat.apk" resources.arsc res) - - zipalign -p -f 4 "$tmp/simplex-chat.apk" "$PWD/simplex-chat.apk" + mkdir -p "$tmp/android-armv7" + unzip -oqd "$tmp/android-armv7/" "$folder/apps/android/app/build/outputs/apk/release/app-armeabi-v7a-release-unsigned.apk" + (cd "$tmp/android-armv7" && zip -rq5 "$tmp/simplex-chat-armv7.apk" . && zip -rq0 "$tmp/simplex-chat-armv7.apk" resources.arsc res) + zipalign -p -f 4 "$tmp/simplex-chat-armv7.apk" "$PWD/simplex-chat-armv7.apk" } final() { diff --git a/scripts/android/compress-and-sign-apk.sh b/scripts/android/compress-and-sign-apk.sh index 1bc904af01..694dcade8d 100755 --- a/scripts/android/compress-and-sign-apk.sh +++ b/scripts/android/compress-and-sign-apk.sh @@ -18,29 +18,31 @@ fi cd $apk_parent_dir -ORIG_NAME=$(echo app*.apk) -unzip -o -q -d apk $ORIG_NAME +ORIG_NAMES=( $(echo app*.apk) ) +for ORIG_NAME in "${ORIG_NAMES[@]}"; do + unzip -o -q -d apk $ORIG_NAME -rm $ORIG_NAME + rm $ORIG_NAME -(cd apk && zip -r -q -$level ../$ORIG_NAME .) -# Shouldn't be compressed because of Android requirement -(cd apk && zip -r -q -0 ../$ORIG_NAME resources.arsc) -(cd apk && zip -r -q -0 ../$ORIG_NAME res) -#(cd apk && 7z a -r -mx=$level -tzip -x!resources.arsc ../$ORIG_NAME .) -#(cd apk && 7z a -r -mx=0 -tzip ../$ORIG_NAME resources.arsc) + (cd apk && zip -r -q -$level ../$ORIG_NAME .) + # Shouldn't be compressed because of Android requirement + (cd apk && zip -r -q -0 ../$ORIG_NAME resources.arsc) + (cd apk && zip -r -q -0 ../$ORIG_NAME res) + #(cd apk && 7z a -r -mx=$level -tzip -x!resources.arsc ../$ORIG_NAME .) + #(cd apk && 7z a -r -mx=0 -tzip ../$ORIG_NAME resources.arsc) -ALL_TOOLS=($sdk_dir/build-tools/*/) -BIN_DIR="${ALL_TOOLS[1]}" + ALL_TOOLS=($sdk_dir/build-tools/*/) + BIN_DIR="${ALL_TOOLS[1]}" -$BIN_DIR/zipalign -p -f 4 $ORIG_NAME $ORIG_NAME-2 + $BIN_DIR/zipalign -p -f 4 $ORIG_NAME $ORIG_NAME-2 -mv $ORIG_NAME{-2,} + mv $ORIG_NAME{-2,} -$BIN_DIR/apksigner sign \ - --ks "$store_file" --ks-key-alias "$key_alias" --ks-pass "pass:$store_password" \ - --key-pass "pass:$key_password" $ORIG_NAME + $BIN_DIR/apksigner sign \ + --ks "$store_file" --ks-key-alias "$key_alias" --ks-pass "pass:$store_password" \ + --key-pass "pass:$key_password" $ORIG_NAME -# cleanup -rm -rf apk || true -rm ${ORIG_NAME}.idsig 2> /dev/null || true + # cleanup + rm -rf apk || true + rm ${ORIG_NAME}.idsig 2> /dev/null || true +done \ No newline at end of file diff --git a/scripts/android/download-libs.sh b/scripts/android/download-libs.sh new file mode 100755 index 0000000000..dd1b1fb2d1 --- /dev/null +++ b/scripts/android/download-libs.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +set -e + +function readlink() { + echo "$(cd "$(dirname "$1")"; pwd -P)" +} + +if [ -z "${1}" ]; then + echo "Job repo is unset. Provide it via first argument like: $(readlink "$0")/download_libs.sh https://something.com/job/something/{master,stable}" + exit 1 +fi + +job_repo=$1 +default_arch=$2 + +arches=("aarch64" "armv7a") +output_arches=("arm64-v8a" "armeabi-v7a") + +if [ -z "${default_arch}" ]; then + # No custom architectures were specified, using defaults + echo "Libs for all supported architectures will be downloaded. To use single arch, pass one of the following values to the end of command: ${arches[*]}" +else + for ((i = 0 ; i < ${#output_arches[@]}; i++)); do + if [ "${arches[$i]}" == "$default_arch" ]; then + output_arches=("${output_arches[$i]}") + fi + done + arches=("$default_arch") +fi + +root_dir="$(dirname "$(dirname "$(readlink "$0")")")" +for ((i = 0 ; i < ${#arches[@]}; i++)); do + arch="${arches[$i]}" + output_arch="${output_arches[$i]}" + output_dir="$root_dir/apps/android/app/src/main/cpp/libs/$output_arch/" + + mkdir -p "$output_dir" 2> /dev/null + + curl --location -o libsupport.zip $job_repo/$arch-android:lib:support.x86_64-linux/latest/download/1 && \ + unzip -o libsupport.zip && \ + mv libsupport.so "$output_dir" && \ + rm libsupport.zip + + curl --location -o libsimplex.zip "$job_repo"/"$arch"-android:lib:simplex-chat.x86_64-linux/latest/download/1 && \ + unzip -o libsimplex.zip && \ + mv libsimplex.so "$output_dir" && \ + rm libsimplex.zip +done diff --git a/scripts/android/download_libs_aarch64.sh b/scripts/android/download_libs_aarch64.sh deleted file mode 100755 index d54a6fd23d..0000000000 --- a/scripts/android/download_libs_aarch64.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash - -set -e - -function readlink() { - echo $(cd $(dirname $1); pwd -P) -} - -if [ -z ${1} ]; then - echo "Job repo is unset. Provide it via first argument like: $(readlink $0)/download_libs_aarch64.sh https://something.com/job/something/{master,stable}" - exit 1 -fi - -job_repo=$1 -arch="aarch64" -#arch="x86_64" -output_arch="arm64-v8a" -#output_arch="x86_64" - -root_dir="$(dirname $(dirname $(readlink $0)))" -output_dir="$root_dir/apps/android/app/src/main/cpp/libs/$output_arch/" - -mkdir -p "$output_dir" 2> /dev/null - -curl --location -o libsupport.zip $job_repo/$arch-android:lib:support.x86_64-linux/latest/download/1 && \ -unzip -o libsupport.zip && \ -mv libsupport.so "$output_dir" && \ -rm libsupport.zip - -curl --location -o libsimplex.zip $job_repo/$arch-android:lib:simplex-chat.x86_64-linux/latest/download/1 && \ -unzip -o libsimplex.zip && \ -mv libsimplex.so "$output_dir" && \ -rm libsimplex.zip diff --git a/scripts/android/prepare.sh b/scripts/android/prepare.sh index 5fbe70aa6b..4947d0c2b3 100755 --- a/scripts/android/prepare.sh +++ b/scripts/android/prepare.sh @@ -1,8 +1,12 @@ #!/bin/sh -# libsimplex.so and libsupport.so binaries should be in ~/Downloads folder +# libsimplex.so and libsupport.so binaries should be in ~/Downloads folder in their directories based on archive name mkdir -p ./apps/android/app/src/main/cpp/libs/arm64-v8a/ rm ./apps/android/app/src/main/cpp/libs/arm64-v8a/* -cp ~/Downloads/libsupport.so ./apps/android/app/src/main/cpp/libs/arm64-v8a/ -cp ~/Downloads/pkg-aarch64-android-libsimplex/libsimplex.so ./apps/android/app/src/main/cpp/libs/arm64-v8a/ -cp ~/Downloads/pkg-aarch64-android-libsimplex/libcrypto.so ./apps/android/app/src/main/cpp/libs/arm64-v8a/ +unzip -o ~/Downloads/pkg-aarch64-android-libsupport.zip -d ./apps/android/app/src/main/cpp/libs/arm64-v8a +unzip -o ~/Downloads/pkg-aarch64-android-libsimplex.zip -d ./apps/android/app/src/main/cpp/libs/arm64-v8a/ + +mkdir -p ./apps/android/app/src/main/cpp/libs/armeabi-v7a/ +rm ./apps/android/app/src/main/cpp/libs/armeabi-v7a/* +unzip -o ~/Downloads/pkg-armv7a-android-libsupport.zip -d ./apps/android/app/src/main/cpp/libs/armeabi-v7a/ +unzip -o ~/Downloads/pkg-armv7a-android-libsimplex.zip -d ./apps/android/app/src/main/cpp/libs/armeabi-v7a/ diff --git a/scripts/ios/download-libs.sh b/scripts/ios/download-libs.sh new file mode 100755 index 0000000000..9d7e388870 --- /dev/null +++ b/scripts/ios/download-libs.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +set -e + +function readlink() { + echo "$(cd "$(dirname "$1")"; pwd -P)" +} + +if [ -z "${1}" ]; then + echo "Job repo is unset. Provide it via first argument like: $(readlink "$0")/download_libs.sh https://something.com/job/something/{master,stable}" + exit 1 +fi + +job_repo=$1 +default_arch=$2 + +arches=("aarch64" "x86_64") +output_arches=("aarch64" "x86_64") + +if [ -z "${default_arch}" ]; then + # No custom architectures were specified, using defaults + echo "Libs for all supported architectures will be downloaded. To use single arch, pass one of the following values to the end of command: ${arches[*]}" +else + for ((i = 0 ; i < ${#output_arches[@]}; i++)); do + if [ "${arches[$i]}" == "$default_arch" ]; then + output_arches=("${output_arches[$i]}") + fi + done + arches=("$default_arch") +fi + +root_dir="$(dirname "$(dirname "$(readlink "$0")")")" +for ((i = 0 ; i < ${#arches[@]}; i++)); do + arch="${arches[$i]}" + output_arch="${output_arches[$i]}" + output_dir="$HOME/Downloads" + + curl --location -o "$output_dir"/pkg-ios-"$arch"-swift-json.zip "$job_repo"/"$arch"-darwin-ios:lib:simplex-chat."$arch"-darwin/latest/download/1 && \ + unzip -o "$output_dir"/pkg-ios-"$output_arch"-swift-json.zip -d ~/Downloads/pkg-ios-"$output_arch"-swift-json +done +sh "$root_dir"/scripts/ios/prepare-x86_64.sh diff --git a/scripts/ios/download_libs.sh b/scripts/ios/download_libs.sh deleted file mode 100755 index b6d253a9c4..0000000000 --- a/scripts/ios/download_libs.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -set -e - -function readlink() { - echo $(cd $(dirname $1); pwd -P) -} - -if [ -z ${1} ]; then - echo "Job repo is unset. Provide it via first argument like: $(readlink $0)/download_libs_aarch64.sh https://something.com/job/something/{master,stable}" - exit 1 -fi - -job_repo=$1 - -root_dir="$(dirname $(dirname $(readlink $0)))" - -curl --location -o ~/Downloads/pkg-ios-aarch64-swift-json.zip $job_repo/aarch64-darwin-ios:lib:simplex-chat.aarch64-darwin/latest/download/1 && \ -unzip -o ~/Downloads/pkg-ios-aarch64-swift-json.zip -d ~/Downloads/pkg-ios-aarch64-swift-json - -curl --location -o ~/Downloads/pkg-ios-x86_64-swift-json.zip $job_repo/x86_64-darwin-ios:lib:simplex-chat.x86_64-darwin/latest/download/1 && \ -unzip -o ~/Downloads/pkg-ios-x86_64-swift-json.zip -d ~/Downloads/pkg-ios-x86_64-swift-json - -sh $root_dir/scripts/ios/prepare-x86_64.sh