diff --git a/Makefile b/Makefile deleted file mode 100644 index 68ac34a879..0000000000 --- a/Makefile +++ /dev/null @@ -1,159 +0,0 @@ -.PHONY : kill_xcode clean bazel_app_debug_arm64 bazel_app_debug_sim_arm64 bazel_app_arm64 bazel_app_armv7 bazel_app check_sandbox_debug_build bazel_project bazel_project_noextensions - -APP_VERSION="7.3" -CORE_COUNT=$(shell sysctl -n hw.logicalcpu) -CORE_COUNT_MINUS_ONE=$(shell expr ${CORE_COUNT} \- 1) - -BAZEL=$(shell which bazel) - -ifneq ($(BAZEL_HTTP_CACHE_URL),) - export BAZEL_CACHE_FLAGS=\ - --remote_cache="$(BAZEL_HTTP_CACHE_URL)" --experimental_remote_downloader="$(BAZEL_HTTP_CACHE_URL)" -else ifneq ($(BAZEL_CACHE_DIR),) - export BAZEL_CACHE_FLAGS=\ - --disk_cache="${BAZEL_CACHE_DIR}" -endif - -ifneq ($(BAZEL_KEEP_GOING),) - export BAZEL_KEEP_GOING_FLAGS=\ - -k -else ifneq ($(BAZEL_CACHE_DIR),) - export BAZEL_KEEP_GOING_FLAGS= -endif - -BAZEL_COMMON_FLAGS=\ - --announce_rc \ - --features=swift.use_global_module_cache \ - --features=swift.split_derived_files_generation \ - --features=swift.skip_function_bodies_for_derived_files \ - --jobs=${CORE_COUNT} \ - ${BAZEL_KEEP_GOING_FLAGS} \ - -BAZEL_DEBUG_FLAGS=\ - --features=swift.enable_batch_mode \ - --swiftcopt=-j${CORE_COUNT_MINUS_ONE} \ - --experimental_guard_against_concurrent_changes \ - -BAZEL_SANDBOX_FLAGS=\ - --strategy=Genrule=sandboxed \ - --spawn_strategy=sandboxed \ - --strategy=SwiftCompile=sandboxed \ - -# --num-threads 0 forces swiftc to generate one object file per module; it: -# 1. resolves issues with the linker caused by swift-objc mixing. -# 2. makes the resulting binaries significantly smaller (up to 9% for this project). -BAZEL_OPT_FLAGS=\ - --features=swift.opt_uses_wmo \ - --features=swift.opt_uses_osize \ - --swiftcopt='-num-threads' --swiftcopt='0' \ - --features=dead_strip \ - --objc_enable_binary_stripping \ - --apple_bitcode=watchos=embedded \ - -kill_xcode: - killall Xcode || true - -clean: - "${BAZEL}" clean --expunge - -bazel_app_debug_arm64: - APP_VERSION="${APP_VERSION}" \ - BAZEL_CACHE_DIR="${BAZEL_CACHE_DIR}" \ - BAZEL_HTTP_CACHE_URL="${BAZEL_HTTP_CACHE_URL}" \ - TELEGRAM_DISABLE_EXTENSIONS="0" \ - build-system/prepare-build.sh Telegram distribution - "${BAZEL}" build Telegram/Telegram ${BAZEL_CACHE_FLAGS} ${BAZEL_COMMON_FLAGS} ${BAZEL_DEBUG_FLAGS} \ - -c dbg \ - --ios_multi_cpus=arm64 \ - --watchos_cpus=armv7k,arm64_32 \ - --verbose_failures - -bazel_app_debug_sim_arm64: - APP_VERSION="${APP_VERSION}" \ - BAZEL_CACHE_DIR="${BAZEL_CACHE_DIR}" \ - BAZEL_HTTP_CACHE_URL="${BAZEL_HTTP_CACHE_URL}" \ - TELEGRAM_DISABLE_EXTENSIONS="0" \ - build-system/prepare-build.sh Telegram distribution - "${BAZEL}" build Telegram/Telegram ${BAZEL_CACHE_FLAGS} ${BAZEL_COMMON_FLAGS} ${BAZEL_DEBUG_FLAGS} \ - -c dbg \ - --ios_multi_cpus=sim_arm64 \ - --watchos_cpus=armv7k,arm64_32 \ - --verbose_failures - -bazel_app_arm64: - APP_VERSION="${APP_VERSION}" \ - BAZEL_CACHE_DIR="${BAZEL_CACHE_DIR}" \ - BAZEL_HTTP_CACHE_URL="${BAZEL_HTTP_CACHE_URL}" \ - TELEGRAM_DISABLE_EXTENSIONS="0" \ - build-system/prepare-build.sh Telegram distribution - "${BAZEL}" build Telegram/Telegram ${BAZEL_CACHE_FLAGS} ${BAZEL_COMMON_FLAGS} ${BAZEL_OPT_FLAGS} \ - -c opt \ - --ios_multi_cpus=arm64 \ - --watchos_cpus=armv7k,arm64_32 \ - --apple_generate_dsym \ - --output_groups=+dsyms \ - --verbose_failures - -bazel_app_armv7: - APP_VERSION="${APP_VERSION}" \ - BAZEL_CACHE_DIR="${BAZEL_CACHE_DIR}" \ - BAZEL_HTTP_CACHE_URL="${BAZEL_HTTP_CACHE_URL}" \ - TELEGRAM_DISABLE_EXTENSIONS="0" \ - build-system/prepare-build.sh Telegram distribution - "${BAZEL}" build Telegram/Telegram ${BAZEL_CACHE_FLAGS} ${BAZEL_COMMON_FLAGS} ${BAZEL_OPT_FLAGS} \ - -c opt \ - --ios_multi_cpus=armv7 \ - --watchos_cpus=armv7k,arm64_32 \ - --apple_generate_dsym \ - --output_groups=+dsyms \ - --verbose_failures - -bazel_app: - APP_VERSION="${APP_VERSION}" \ - BAZEL_CACHE_DIR="${BAZEL_CACHE_DIR}" \ - BAZEL_HTTP_CACHE_URL="${BAZEL_HTTP_CACHE_URL}" \ - TELEGRAM_DISABLE_EXTENSIONS="0" \ - build-system/prepare-build.sh Telegram distribution - "${BAZEL}" build Telegram/Telegram ${BAZEL_CACHE_FLAGS} ${BAZEL_COMMON_FLAGS} ${BAZEL_OPT_FLAGS} \ - -c opt \ - --ios_multi_cpus=armv7,arm64 \ - --watchos_cpus=armv7k,arm64_32 \ - --apple_generate_dsym \ - --output_groups=+dsyms \ - --verbose_failures - -check_sandbox_debug_build: - APP_VERSION="${APP_VERSION}" \ - BAZEL_CACHE_DIR="${BAZEL_CACHE_DIR}" \ - BAZEL_HTTP_CACHE_URL="${BAZEL_HTTP_CACHE_URL}" \ - TELEGRAM_DISABLE_EXTENSIONS="0" \ - build-system/prepare-build.sh Telegram distribution - "${BAZEL}" build Telegram/Telegram ${BAZEL_CACHE_FLAGS} ${BAZEL_COMMON_FLAGS} ${BAZEL_DEBUG_FLAGS} ${BAZEL_SANDBOX_FLAGS} \ - -c opt \ - --ios_multi_cpus=arm64 \ - --watchos_cpus=armv7k,arm64_32 \ - --apple_generate_dsym \ - --output_groups=+dsyms \ - --verbose_failures - -bazel_project: kill_xcode - APP_VERSION="${APP_VERSION}" \ - BAZEL_CACHE_DIR="${BAZEL_CACHE_DIR}" \ - BAZEL_HTTP_CACHE_URL="${BAZEL_HTTP_CACHE_URL}" \ - TELEGRAM_DISABLE_EXTENSIONS="0" \ - build-system/prepare-build.sh Telegram development - APP_VERSION="${APP_VERSION}" \ - BAZEL_CACHE_DIR="${BAZEL_CACHE_DIR}" \ - BAZEL_HTTP_CACHE_URL="${BAZEL_HTTP_CACHE_URL}" \ - build-system/generate-xcode-project.sh Telegram - -bazel_project_noextensions: kill_xcode - APP_VERSION="${APP_VERSION}" \ - BAZEL_CACHE_DIR="${BAZEL_CACHE_DIR}" \ - BAZEL_HTTP_CACHE_URL="${BAZEL_HTTP_CACHE_URL}" \ - TELEGRAM_DISABLE_EXTENSIONS="1" \ - build-system/prepare-build.sh Telegram development - APP_VERSION="${APP_VERSION}" \ - BAZEL_CACHE_DIR="${BAZEL_CACHE_DIR}" \ - BAZEL_HTTP_CACHE_URL="${BAZEL_HTTP_CACHE_URL}" \ - build-system/generate-xcode-project.sh Telegram diff --git a/README.md b/README.md index 621c3cef60..344a62325c 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,96 @@ # Telegram iOS Source Code Compilation Guide -1. Install the brew package manager, if you havenโ€™t already. -2. Install the packages yasm, cmake: -``` -brew install yasm cmake -``` -3. Clone the project from GitHub: +We welcome all developers to use our API and source code to create applications on our platform. +There are several things we require from **all developers** for the moment. + +# Creating your Telegram Application + +1. [**Obtain your own api_id**](https://core.telegram.org/api/obtaining_api_id) for your application. +2. Please **do not** use the name Telegram for your app โ€” or make sure your users understand that it is unofficial. +3. Kindly **do not** use our standard logo (white paper plane in a blue circle) as your app's logo. +3. Please study our [**security guidelines**](https://core.telegram.org/mtproto/security_guidelines) and take good care of your users' data and privacy. +4. Please remember to publish **your** code too in order to comply with the licences. + +# Compilation Guide + +1. Install Xcode (directly from https://developer.apple.com/download/more or using the App Store). +2. Clone the project from GitHub: ``` git clone --recursive https://github.com/TelegramMessenger/Telegram-iOS.git ``` -4. Open Telegram-iOS.workspace. -5. Open the Telegram-iOS-Fork scheme. -6. Start the compilation process. -7. To run the app on your device, you will need to set the correct values for the signature, .entitlements files and package IDs in accordance with your developer account values. + +3. Download Bazel 3.7.0 + +``` +mkdir -p $HOME/bazel-dist +cd $HOME/bazel-dist +curl -O -L https://github.com/bazelbuild/bazel/releases/download/3.7.0/bazel-3.7.0-darwin-x86_64 +mv bazel-3.7.0* bazel +``` + +Verify that it's working + +``` +chmod +x bazel +./bazel --version +``` + +4. Adjust configuration parameters + +``` +mkdir -p $HOME/telegram-configuration +cp -R build-system/example-configuration/* $HOME/telegram-configuration/ +``` + +- Modify the values in `variables.bzl` +- Replace the provisioning profiles in `provisioning` with valid files + +5. (Optional) Create a build cache directory to speed up rebuilds + +``` +mkdir -p "$HOME/telegram-bazel-cache" +``` + +5. Build the app + +``` +python3 build-system/Make/Make.py \ + --bazel="$HOME/bazel-dist/bazel" \ + --cacheDir="$HOME/telegram-bazel-cache" \ + build \ + --configurationPath="$HOME/telegram-configuration" \ + --buildNumber=100001 \ + --configuration=release_universal +``` + +6. (Optional) Generate an Xcode project + +``` +python3 build-system/Make/Make.py \ + --bazel="$HOME/bazel-dist/bazel" \ + --cacheDir="$HOME/telegram-bazel-cache" \ + generateProject \ + --configurationPath="$HOME/telegram-configuration" \ + --disableExtensions +``` + +Tip: use `--disableExtensions` when developing to speed up development by not building application extensions. + + +# Tips + +Bazel is used to build the app. To simplify the development setup a helper script is provided (`build-system/Make/Make.py`). See help: + +``` +python3 build-system/Make/Make.py --help +python3 build-system/Make/Make.py build --help +python3 build-system/Make/Make.py generateProject --help +``` + +Each release is built using specific Xcode and Bazel versions (see `versions.json`). The helper script checks the versions of installed software and reports an error if they don't match the ones specified in `versions.json`. There are flags that allow to bypass these checks: + +``` +python3 build-system/Make/Make.py --overrideBazelVersion build ... # Don't check the version of Bazel +python3 build-system/Make/Make.py --overrideXcodeVersion build ... # Don't check the version of Xcode +``` diff --git a/Telegram/BUILD b/Telegram/BUILD index d570c88f26..415ee8cc3f 100644 --- a/Telegram/BUILD +++ b/Telegram/BUILD @@ -1,3 +1,7 @@ +load("@bazel_skylib//rules:common_settings.bzl", + "bool_flag", +) + load("@build_bazel_rules_apple//apple:ios.bzl", "ios_application", "ios_extension", @@ -18,13 +22,10 @@ load("//build-system/bazel-utils:plist_fragment.bzl", ) load( - "//build-input/data:variables.bzl", - "telegram_build_number", - "telegram_version", + "@build_configuration//:variables.bzl", "telegram_bundle_id", "telegram_aps_environment", "telegram_team_id", - "telegram_disable_extensions", ) config_setting( @@ -34,6 +35,19 @@ config_setting( }, ) +bool_flag( + name = "disableExtensions", + build_setting_default = False, + visibility = ["//visibility:public"], +) + +config_setting( + name = "disableExtensionsSetting", + flag_values = { + ":disableExtensions": "True", + }, +) + genrule( name = "empty", outs = ["empty.swift"], @@ -190,14 +204,20 @@ swift_library( ) plist_fragment( - name = "AdditionalInfoPlist", + name = "BuildNumberInfoPlist", extension = "plist", template = """ - CFBundleShortVersionString - {telegram_version} CFBundleVersion - {telegram_build_number} + {buildNumber} + """ +) + +plist_fragment( + name = "UrlTypesInfoPlist", + extension = "plist", + template = + """ CFBundleURLTypes @@ -210,16 +230,6 @@ plist_fragment( telegram - - CFBundleTypeRole - Viewer - CFBundleURLName - {telegram_bundle_id}.ton - CFBundleURLSchemes - - ton - - CFBundleTypeRole Viewer @@ -232,8 +242,6 @@ plist_fragment( """.format( - telegram_version = telegram_version, - telegram_build_number = telegram_build_number, telegram_bundle_id = telegram_bundle_id, ) ) @@ -371,13 +379,8 @@ plist_fragment( template = """ CFBundleShortVersionString - {telegram_version} - CFBundleVersion - {telegram_build_number} - """.format( - telegram_version = telegram_version, - telegram_build_number = telegram_build_number, - ) + {telegramVersion} + """ ) plist_fragment( @@ -478,11 +481,12 @@ watchos_extension( infoplists = [ ":WatchExtensionInfoPlist", ":VersionInfoPlist", + ":BuildNumberInfoPlist", ":AppNameInfoPlist", ":WatchExtensionNSExtensionInfoPlist", ], minimum_os_version = "5.0", - provisioning_profile = "//build-input/data/provisioning-profiles:WatchExtension.mobileprovision", + provisioning_profile = "@build_configuration//provisioning:WatchExtension.mobileprovision", resources = [ ":TelegramWatchExtensionResources", ], @@ -505,11 +509,12 @@ watchos_application( infoplists = [ ":WatchAppInfoPlist", ":VersionInfoPlist", + "BuildNumberInfoPlist", ":AppNameInfoPlist", ":WatchAppCompanionInfoPlist", ], minimum_os_version = "5.0", - provisioning_profile = "//build-input/data/provisioning-profiles:WatchApp.mobileprovision", + provisioning_profile = "@build_configuration//provisioning:WatchApp.mobileprovision", resources = [ ":TelegramWatchAppResources", ":TelegramWatchAppAssets", @@ -528,20 +533,14 @@ plist_fragment( """ CFBundleIdentifier {telegram_bundle_id}.MtProtoKit - CFBundleVersion - {telegram_build_number} CFBundleDevelopmentRegion en CFBundleName MtProtoKit - CFBundleShortVersionString - {telegram_version} CFBundlePackageType FMWK """.format( telegram_bundle_id = telegram_bundle_id, - telegram_version = telegram_version, - telegram_build_number = telegram_build_number, ) ) @@ -556,6 +555,8 @@ ios_framework( ], infoplists = [ ":MtProtoKitInfoPlist", + ":BuildNumberInfoPlist", + ":VersionInfoPlist", ], minimum_os_version = "9.0", ipa_post_processor = strip_framework, @@ -571,20 +572,14 @@ plist_fragment( """ CFBundleIdentifier {telegram_bundle_id}.SwiftSignalKit - CFBundleVersion - {telegram_build_number} CFBundleDevelopmentRegion en CFBundleName SwiftSignalKit - CFBundleShortVersionString - {telegram_version} CFBundlePackageType FMWK """.format( telegram_bundle_id = telegram_bundle_id, - telegram_version = telegram_version, - telegram_build_number = telegram_build_number, ) ) @@ -599,6 +594,8 @@ ios_framework( ], infoplists = [ ":SwiftSignalKitInfoPlist", + ":BuildNumberInfoPlist", + ":VersionInfoPlist", ], minimum_os_version = "9.0", ipa_post_processor = strip_framework, @@ -614,20 +611,14 @@ plist_fragment( """ CFBundleIdentifier {telegram_bundle_id}.Postbox - CFBundleVersion - {telegram_build_number} CFBundleDevelopmentRegion en CFBundleName Postbox - CFBundleShortVersionString - {telegram_version} CFBundlePackageType FMWK """.format( telegram_bundle_id = telegram_bundle_id, - telegram_version = telegram_version, - telegram_build_number = telegram_build_number, ) ) @@ -642,6 +633,8 @@ ios_framework( ], infoplists = [ ":PostboxInfoPlist", + ":BuildNumberInfoPlist", + ":VersionInfoPlist", ], frameworks = [ ":SwiftSignalKitFramework", @@ -660,20 +653,14 @@ plist_fragment( """ CFBundleIdentifier {telegram_bundle_id}.TelegramApi - CFBundleVersion - {telegram_build_number} CFBundleDevelopmentRegion en CFBundleName TelegramApi - CFBundleShortVersionString - {telegram_version} CFBundlePackageType FMWK """.format( telegram_bundle_id = telegram_bundle_id, - telegram_version = telegram_version, - telegram_build_number = telegram_build_number, ) ) @@ -688,6 +675,8 @@ ios_framework( ], infoplists = [ ":TelegramApiInfoPlist", + ":BuildNumberInfoPlist", + ":VersionInfoPlist", ], minimum_os_version = "9.0", ipa_post_processor = strip_framework, @@ -703,20 +692,14 @@ plist_fragment( """ CFBundleIdentifier {telegram_bundle_id}.SyncCore - CFBundleVersion - {telegram_build_number} CFBundleDevelopmentRegion en CFBundleName SyncCore - CFBundleShortVersionString - {telegram_version} CFBundlePackageType FMWK """.format( telegram_bundle_id = telegram_bundle_id, - telegram_version = telegram_version, - telegram_build_number = telegram_build_number, ) ) @@ -731,6 +714,8 @@ ios_framework( ], infoplists = [ ":SyncCoreInfoPlist", + ":BuildNumberInfoPlist", + ":VersionInfoPlist", ], frameworks = [ ":SwiftSignalKitFramework", @@ -750,20 +735,14 @@ plist_fragment( """ CFBundleIdentifier {telegram_bundle_id}.TelegramCore - CFBundleVersion - {telegram_build_number} CFBundleDevelopmentRegion en CFBundleName TelegramCore - CFBundleShortVersionString - {telegram_version} CFBundlePackageType FMWK """.format( telegram_bundle_id = telegram_bundle_id, - telegram_version = telegram_version, - telegram_build_number = telegram_build_number, ) ) @@ -778,13 +757,14 @@ ios_framework( ], infoplists = [ ":TelegramCoreInfoPlist", + ":BuildNumberInfoPlist", + ":VersionInfoPlist", ], frameworks = [ ":MtProtoKitFramework", ":SwiftSignalKitFramework", ":PostboxFramework", ":SyncCoreFramework", - #":TelegramApiFramework", ], minimum_os_version = "9.0", ipa_post_processor = strip_framework, @@ -800,20 +780,14 @@ plist_fragment( """ CFBundleIdentifier {telegram_bundle_id}.AsyncDisplayKit - CFBundleVersion - {telegram_build_number} CFBundleDevelopmentRegion en CFBundleName AsyncDisplayKit - CFBundleShortVersionString - {telegram_version} CFBundlePackageType FMWK """.format( telegram_bundle_id = telegram_bundle_id, - telegram_version = telegram_version, - telegram_build_number = telegram_build_number, ) ) @@ -828,6 +802,8 @@ ios_framework( ], infoplists = [ ":AsyncDisplayKitInfoPlist", + ":BuildNumberInfoPlist", + ":VersionInfoPlist", ], minimum_os_version = "9.0", ipa_post_processor = strip_framework, @@ -843,20 +819,14 @@ plist_fragment( """ CFBundleIdentifier {telegram_bundle_id}.Display - CFBundleVersion - {telegram_build_number} CFBundleDevelopmentRegion en CFBundleName Display - CFBundleShortVersionString - {telegram_version} CFBundlePackageType FMWK """.format( telegram_bundle_id = telegram_bundle_id, - telegram_version = telegram_version, - telegram_build_number = telegram_build_number, ) ) @@ -914,6 +884,8 @@ ios_framework( ], infoplists = [ ":DisplayInfoPlist", + ":BuildNumberInfoPlist", + ":VersionInfoPlist", ], frameworks = [ ":SwiftSignalKitFramework", @@ -933,20 +905,14 @@ plist_fragment( """ CFBundleIdentifier {telegram_bundle_id}.TelegramUI - CFBundleVersion - {telegram_build_number} CFBundleDevelopmentRegion en CFBundleName TelegramUI - CFBundleShortVersionString - {telegram_version} CFBundlePackageType FMWK """.format( telegram_bundle_id = telegram_bundle_id, - telegram_version = telegram_version, - telegram_build_number = telegram_build_number, ) ) @@ -961,12 +927,13 @@ ios_framework( ], infoplists = [ ":TelegramUIInfoPlist", + ":BuildNumberInfoPlist", + ":VersionInfoPlist", ], frameworks = [ ":MtProtoKitFramework", ":SwiftSignalKitFramework", ":PostboxFramework", - #":TelegramApiFramework", ":SyncCoreFramework", ":TelegramCoreFramework", ":AsyncDisplayKitFramework", @@ -1052,10 +1019,11 @@ ios_extension( infoplists = [ ":ShareInfoPlist", ":VersionInfoPlist", + ":BuildNumberInfoPlist", ":AppNameInfoPlist", ], minimum_os_version = "9.0", - provisioning_profile = "//build-input/data/provisioning-profiles:Share.mobileprovision", + provisioning_profile = "@build_configuration//provisioning:Share.mobileprovision", deps = [":ShareExtensionLib"], frameworks = [ ":TelegramUIFramework" @@ -1120,10 +1088,11 @@ ios_extension( infoplists = [ ":NotificationContentInfoPlist", ":VersionInfoPlist", + ":BuildNumberInfoPlist", ":AppNameInfoPlist", ], minimum_os_version = "10.0", - provisioning_profile = "//build-input/data/provisioning-profiles:NotificationContent.mobileprovision", + provisioning_profile = "@build_configuration//provisioning:NotificationContent.mobileprovision", deps = [":NotificationContentExtensionLib"], frameworks = [ ":TelegramUIFramework" @@ -1157,25 +1126,35 @@ plist_fragment( ) ) +swift_library( + name = "GeneratedSources", + module_name = "GeneratedSources", + srcs = glob([ + "Generated/**/*.swift", + ]), + visibility = ["//visibility:public"], +) + swift_library( name = "WidgetExtensionLib", module_name = "WidgetExtensionLib", srcs = glob([ "WidgetKitWidget/**/*.swift", - "Generated/**/*.swift", ]), data = [ "SiriIntents/Intents.intentdefinition", ], deps = [ "//submodules/BuildConfig:BuildConfig", - "//submodules/WidgetItems:WidgetItems", + "//submodules/WidgetItems:WidgetItems_iOS14", + "//submodules/WidgetItemsUtils:WidgetItemsUtils", "//submodules/AppLockState:AppLockState", "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/Postbox:Postbox", "//submodules/TelegramCore:TelegramCore", "//submodules/SyncCore:SyncCore", "//submodules/OpenSSLEncryptionProvider:OpenSSLEncryptionProvider", + ":GeneratedSources", ], ) @@ -1191,11 +1170,12 @@ ios_extension( infoplists = [ ":WidgetInfoPlist", ":VersionInfoPlist", + ":BuildNumberInfoPlist", ":AppNameInfoPlist", ], minimum_os_version = "14.0", provides_main = True, - provisioning_profile = "//build-input/data/provisioning-profiles:Widget.mobileprovision", + provisioning_profile = "@build_configuration//provisioning:Widget.mobileprovision", deps = [":WidgetExtensionLib"], frameworks = [ ":SwiftSignalKitFramework", @@ -1251,7 +1231,6 @@ swift_library( module_name = "IntentsExtensionLib", srcs = glob([ "SiriIntents/**/*.swift", - "Generated/**/*.swift", ]), data = [ "SiriIntents/Intents.intentdefinition", @@ -1265,6 +1244,7 @@ swift_library( "//submodules/BuildConfig:BuildConfig", "//submodules/OpenSSLEncryptionProvider:OpenSSLEncryptionProvider", "//submodules/AppLockState:AppLockState", + ":GeneratedSources", ], ) @@ -1280,16 +1260,16 @@ ios_extension( infoplists = [ ":IntentsInfoPlist", ":VersionInfoPlist", + ":BuildNumberInfoPlist", ":AppNameInfoPlist", ], minimum_os_version = "10.0", - provisioning_profile = "//build-input/data/provisioning-profiles:Intents.mobileprovision", + provisioning_profile = "@build_configuration//provisioning:Intents.mobileprovision", deps = [":IntentsExtensionLib"], frameworks = [ ":SwiftSignalKitFramework", ":PostboxFramework", ":TelegramCoreFramework", - #":TelegramApiFramework", ":SyncCoreFramework", ], ) @@ -1331,17 +1311,16 @@ ios_extension( infoplists = [ ":NotificationServiceInfoPlist", ":VersionInfoPlist", + ":BuildNumberInfoPlist", ":AppNameInfoPlist", ], minimum_os_version = "10.0", - provisioning_profile = "//build-input/data/provisioning-profiles:NotificationService.mobileprovision", + provisioning_profile = "@build_configuration//provisioning:NotificationService.mobileprovision", deps = ["//Telegram/NotificationService:NotificationServiceExtensionLib"], frameworks = [ ":MtProtoKitFramework", ":SwiftSignalKitFramework", ":PostboxFramework", - #":TelegramApiFramework", - #":SyncCoreFramework", ], ) @@ -1522,11 +1501,13 @@ ios_application( ), families = ["iphone", "ipad"], minimum_os_version = "9.0", - provisioning_profile = "//build-input/data/provisioning-profiles:Telegram.mobileprovision", + provisioning_profile = "@build_configuration//provisioning:Telegram.mobileprovision", entitlements = ":TelegramEntitlements.entitlements", infoplists = [ ":TelegramInfoPlist", - ":AdditionalInfoPlist", + ":BuildNumberInfoPlist", + ":VersionInfoPlist", + ":UrlTypesInfoPlist", ], ipa_post_processor = ":AddAlternateIcons", resources = [ @@ -1547,14 +1528,16 @@ ios_application( strings = [ ":AppStringResources", ], - extensions = [ - ] if telegram_disable_extensions else [ - ":ShareExtension", - ":NotificationContentExtension", - ":NotificationServiceExtension", - ":IntentsExtension", - ":WidgetExtension", - ], + extensions = select({ + ":disableExtensionsSetting": [], + "//conditions:default": [ + ":ShareExtension", + ":NotificationContentExtension", + ":NotificationServiceExtension", + ":IntentsExtension", + ":WidgetExtension", + ], + }), watch_application = ":TelegramWatchApp", deps = [ ":Main", diff --git a/Telegram/SiriIntents/IntentHandler.swift b/Telegram/SiriIntents/IntentHandler.swift index 41ac71b129..8ef09e3458 100644 --- a/Telegram/SiriIntents/IntentHandler.swift +++ b/Telegram/SiriIntents/IntentHandler.swift @@ -9,6 +9,7 @@ import Contacts import OpenSSLEncryptionProvider import AppLockState import UIKit +import GeneratedSources private var accountCache: Account? diff --git a/Telegram/WidgetKitWidget/TodayViewController.swift b/Telegram/WidgetKitWidget/TodayViewController.swift index 1afe3bb85a..2a02c2d21c 100644 --- a/Telegram/WidgetKitWidget/TodayViewController.swift +++ b/Telegram/WidgetKitWidget/TodayViewController.swift @@ -13,6 +13,9 @@ import Postbox import SyncCore import TelegramCore import OpenSSLEncryptionProvider +import WidgetItemsUtils + +import GeneratedSources private var installedSharedLogger = false @@ -156,9 +159,16 @@ struct Provider: IntentTimelineProvider { ) } + var mappedMessage: WidgetDataPeer.Message? + if let index = transaction.getTopPeerMessageIndex(peerId: peer.id) { + if let message = transaction.getMessage(index.id) { + mappedMessage = WidgetDataPeer.Message(message: message) + } + } + peers.append(WidgetDataPeer(id: peer.id.toInt64(), name: name, lastName: lastName, letters: peer.displayLetters, avatarPath: smallestImageRepresentation(peer.profileImageRepresentations).flatMap { representation in return postbox.mediaBox.resourcePath(representation.resource) - }, badge: badge)) + }, badge: badge, message: mappedMessage)) } } return WidgetDataPeers(accountPeerId: widgetPeers.accountPeerId, peers: peers) @@ -195,12 +205,13 @@ struct AvatarItemView: View { var accountPeerId: Int64 var peer: WidgetDataPeer var itemSize: CGFloat + var displayBadge: Bool = true var body: some View { return ZStack { Image(uiImage: avatarImage(accountPeerId: accountPeerId, peer: peer, size: CGSize(width: itemSize, height: itemSize))) .clipShape(Circle()) - if let badge = peer.badge, badge.count > 0 { + if displayBadge, let badge = peer.badge, badge.count > 0 { Text("\(badge.count)") .font(Font.system(size: 16.0)) .multilineTextAlignment(.center) @@ -219,6 +230,7 @@ struct AvatarItemView: View { struct WidgetView: View { @Environment(\.widgetFamily) private var widgetFamily + @Environment(\.colorScheme) private var colorScheme let data: PeersWidgetData func placeholder(geometry: GeometryProxy) -> some View { @@ -329,12 +341,150 @@ struct WidgetView: View { } } - var body: some View { + var body1: some View { ZStack { peerViews() } .padding(0.0) } + + func chatTopLine(_ peer: WidgetDataPeer) -> some View { + let dateText: String + if let message = peer.message { + dateText = DateFormatter.localizedString(from: Date(timeIntervalSince1970: Double(message.timestamp)), dateStyle: .none, timeStyle: .short) + } else { + dateText = "" + } + return HStack(alignment: .center, spacing: 0.0, content: { + Text(peer.name).font(Font.system(size: 16.0, weight: .medium, design: .default)).foregroundColor(.primary) + Spacer() + Text(dateText).font(Font.system(size: 14.0, weight: .regular, design: .default)).foregroundColor(.secondary) + }) + } + + func chatBottomLine(_ peer: WidgetDataPeer) -> some View { + var text = peer.message?.text ?? "" + if let message = peer.message { + //TODO:localize + switch message.content { + case .text: + break + case .image: + text = "๐Ÿ–ผ Photo" + case .video: + text = "๐Ÿ“น Video" + case .gif: + text = "Gif" + case let .file(file): + text = "๐Ÿ“Ž \(file.name)" + case let .music(music): + if !music.title.isEmpty && !music.artist.isEmpty { + text = "\(music.artist) โ€” \(music.title)" + } else if !music.title.isEmpty { + text = music.title + } else if !music.artist.isEmpty { + text = music.artist + } else { + text = "Music" + } + case .voiceMessage: + text = "๐ŸŽค Voice Message" + case .videoMessage: + text = "Video Message" + case let .sticker(sticker): + text = "\(sticker.altText) Sticker" + case let .call(call): + if call.isVideo { + text = "Video Call" + } else { + text = "Voice Call" + } + case .mapLocation: + text = "Location" + case let .game(game): + text = "๐ŸŽฎ \(game.title)" + case let .poll(poll): + text = "๐Ÿ“Š \(poll.title)" + } + } + + var hasBadge = false + if let badge = peer.badge, badge.count > 0 { + hasBadge = true + } + + return HStack(alignment: .center, spacing: hasBadge ? 6.0 : 0.0, content: { + Text(text).lineLimit(nil).font(Font.system(size: 15.0, weight: .regular, design: .default)).foregroundColor(.secondary).multilineTextAlignment(.leading).frame(maxHeight: .infinity, alignment: .topLeading) + Spacer() + if let badge = peer.badge, badge.count > 0 { + VStack { + Spacer() + Text("\(badge.count)") + .font(Font.system(size: 14.0)) + .multilineTextAlignment(.center) + .foregroundColor(.white) + .padding(.horizontal, 4.0) + .background( + RoundedRectangle(cornerRadius: 10) + .fill(badge.isMuted ? Color.gray : Color.blue) + .frame(minWidth: 20, idealWidth: 20, maxWidth: .infinity, minHeight: 20, idealHeight: 20, maxHeight: 20.0, alignment: .center) + ) + .padding(EdgeInsets(top: 0.0, leading: 0.0, bottom: 6.0, trailing: 3.0)) + } + } + }) + } + + func chatContent(_ peer: WidgetDataPeer) -> some View { + return VStack(alignment: .leading, spacing: 2.0, content: { + chatTopLine(peer) + chatBottomLine(peer).frame(maxHeight: .infinity) + }) + } + + func chatContentView(_ index: Int) -> AnyView { + let peers: WidgetDataPeers + switch data { + case let .peers(peersValue): + peers = peersValue + if peers.peers.count <= index { + return AnyView(Spacer()) + } + default: + return AnyView(Spacer()) + } + + return AnyView( + Link(destination: URL(string: linkForPeer(id: peers.peers[index].id))!, label: { + HStack(alignment: .center, spacing: 0.0, content: { + AvatarItemView(accountPeerId: peers.accountPeerId, peer: peers.peers[index], itemSize: 60.0, displayBadge: false).frame(width: 60.0, height: 60.0, alignment: .leading).padding(EdgeInsets(top: 0.0, leading: 10.0, bottom: 0.0, trailing: 10.0)) + chatContent(peers.peers[index]).frame(maxWidth: .infinity).padding(EdgeInsets(top: 10.0, leading: 0.0, bottom: 10.0, trailing: 10.0)) + }) + }) + ) + } + + func getSeparatorColor() -> Color { + switch colorScheme { + case .light: + return Color(.sRGB, red: 200.0 / 255.0, green: 199.0 / 255.0, blue: 204.0 / 255.0, opacity: 1.0) + case .dark: + return Color(.sRGB, red: 61.0 / 255.0, green: 61.0 / 255.0, blue: 64.0 / 255.0, opacity: 1.0) + @unknown default: + return .secondary + } + } + + var body: some View { + GeometryReader(content: { geometry in + ZStack { + chatContentView(0).position(x: geometry.size.width / 2.0, y: geometry.size.height / 4.0).frame(width: geometry.size.width, height: geometry.size.height / 2.0, alignment: .leading) + chatContentView(1).position(x: geometry.size.width / 2.0, y: geometry.size.height / 2.0 + geometry.size.height / 4.0).frame(width: geometry.size.width, height: geometry.size.height / 2.0, alignment: .leading) + Rectangle().foregroundColor(getSeparatorColor()).position(x: geometry.size.width / 2.0, y: geometry.size.height / 4.0).frame(width: geometry.size.width, height: 0.33, alignment: .leading) + } + }) + .padding(0.0) + } } private let buildConfig: BuildConfig = { @@ -429,7 +579,7 @@ struct Static_Widget: Widget { return IntentConfiguration(kind: kind, intent: SelectFriendsIntent.self, provider: Provider(), content: { entry in WidgetView(data: getWidgetData(contents: entry.contents)) }) - .supportedFamilies([.systemSmall, .systemMedium]) + .supportedFamilies([.systemMedium]) .configurationDisplayName(presentationData.widgetGalleryTitle) .description(presentationData.widgetGalleryDescription) } diff --git a/build-system/Make/BuildEnvironment.py b/build-system/Make/BuildEnvironment.py new file mode 100644 index 0000000000..aa889367f2 --- /dev/null +++ b/build-system/Make/BuildEnvironment.py @@ -0,0 +1,148 @@ +import json +import os +import platform +import subprocess + + +def is_apple_silicon(): + if platform.processor() == 'arm': + return True + else: + return False + + +def get_clean_env(): + clean_env = os.environ.copy() + clean_env['PATH'] = '/usr/bin:/bin:/usr/sbin:/sbin' + return clean_env + + +def resolve_executable(program): + def is_executable(fpath): + return os.path.isfile(fpath) and os.access(fpath, os.X_OK) + + for path in get_clean_env()["PATH"].split(os.pathsep): + executable_file = os.path.join(path, program) + if is_executable(executable_file): + return executable_file + return None + + +def run_executable_with_output(path, arguments): + executable_path = resolve_executable(path) + if executable_path is None: + raise Exception('Could not resolve {} to a valid executable file'.format(path)) + + process = subprocess.Popen( + [executable_path] + arguments, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + env=get_clean_env() + ) + output_data, _ = process.communicate() + output_string = output_data.decode('utf-8') + return output_string + + +def call_executable(arguments, use_clean_environment=True, check_result=True): + executable_path = resolve_executable(arguments[0]) + if executable_path is None: + raise Exception('Could not resolve {} to a valid executable file'.format(arguments[0])) + + if use_clean_environment: + resolved_env = get_clean_env() + else: + resolved_env = os.environ + + resolved_arguments = [executable_path] + arguments[1:] + + if check_result: + subprocess.check_call(resolved_arguments, env=resolved_env) + else: + subprocess.call(resolved_arguments, env=resolved_env) + + +def get_bazel_version(bazel_path): + command_result = run_executable_with_output(bazel_path, ['--version']).strip('\n') + if not command_result.startswith('bazel '): + raise Exception('{} is not a valid bazel binary'.format(bazel_path)) + command_result = command_result.replace('bazel ', '') + return command_result + + +def get_xcode_version(): + xcode_path = run_executable_with_output('xcode-select', ['-p']).strip('\n') + if not os.path.isdir(xcode_path): + print('The path reported by \'xcode-select -p\' does not exist') + exit(1) + + plist_path = '{}/../Info.plist'.format(xcode_path) + + info_plist_lines = run_executable_with_output('plutil', [ + '-p', plist_path + ]).split('\n') + + pattern = 'CFBundleShortVersionString" => ' + for line in info_plist_lines: + index = line.find(pattern) + if index != -1: + version = line[index + len(pattern):].strip('"') + return version + + print('Could not parse the Xcode version from {}'.format(plist_path)) + exit(1) + + +class BuildEnvironment: + def __init__( + self, + base_path, + bazel_path, + bazel_x86_64_path, + override_bazel_version, + override_xcode_version + ): + self.base_path = os.path.expanduser(base_path) + self.bazel_path = os.path.expanduser(bazel_path) + if bazel_x86_64_path is not None: + self.bazel_x86_64_path = os.path.expanduser(bazel_x86_64_path) + else: + self.bazel_x86_64_path = None + + configuration_path = os.path.join(self.base_path, 'versions.json') + with open(configuration_path) as file: + configuration_dict = json.load(file) + if configuration_dict['app'] is None: + raise Exception('Missing app version in {}'.format(configuration_path)) + else: + self.app_version = configuration_dict['app'] + if configuration_dict['bazel'] is None: + raise Exception('Missing bazel version in {}'.format(configuration_path)) + else: + self.bazel_version = configuration_dict['bazel'] + if configuration_dict['xcode'] is None: + raise Exception('Missing xcode version in {}'.format(configuration_path)) + else: + self.xcode_version = configuration_dict['xcode'] + + actual_bazel_version = get_bazel_version(self.bazel_path) + if actual_bazel_version != self.bazel_version: + if override_bazel_version: + print('Overriding the required bazel version {} with {} as reported by {}'.format( + self.bazel_version, actual_bazel_version, self.bazel_path)) + self.bazel_version = actual_bazel_version + else: + print('Required bazel version is "{}", but "{}"" is reported by {}'.format( + self.bazel_version, actual_bazel_version, self.bazel_path)) + exit(1) + + actual_xcode_version = get_xcode_version() + if actual_xcode_version != self.xcode_version: + if override_xcode_version: + print('Overriding the required Xcode version {} with {} as reported by \'xcode-select -p\''.format( + self.xcode_version, actual_xcode_version, self.bazel_path)) + self.xcode_version = actual_xcode_version + else: + print('Required Xcode version is {}, but {} is reported by \'xcode-select -p\''.format( + self.xcode_version, actual_xcode_version, self.bazel_path)) + exit(1) diff --git a/build-system/Make/Make.py b/build-system/Make/Make.py new file mode 100644 index 0000000000..8c12101cfd --- /dev/null +++ b/build-system/Make/Make.py @@ -0,0 +1,484 @@ +#!/bin/python3 + +import argparse +import os +import shlex +import sys +import tempfile +import subprocess + +from BuildEnvironment import is_apple_silicon, resolve_executable, call_executable, BuildEnvironment +from ProjectGeneration import generate + + +class BazelCommandLine: + def __init__(self, bazel_path, bazel_x86_64_path, override_bazel_version, override_xcode_version): + self.build_environment = BuildEnvironment( + base_path=os.getcwd(), + bazel_path=bazel_path, + bazel_x86_64_path=bazel_x86_64_path, + override_bazel_version=override_bazel_version, + override_xcode_version=override_xcode_version + ) + self.remote_cache = None + self.cache_dir = None + self.additional_args = None + self.build_number = None + self.configuration_args = None + self.configuration_path = None + + self.common_args = [ + # https://docs.bazel.build/versions/master/command-line-reference.html + # Ask bazel to print the actual resolved command line options. + '--announce_rc', + + # https://github.com/bazelbuild/rules_swift + # If enabled, Swift compilation actions will use the same global Clang module + # cache used by Objective-C compilation actions. This is disabled by default + # because under some circumstances Clang module cache corruption can cause the + # Swift compiler to crash (sometimes when switching configurations or syncing a + # repository), but disabling it also causes a noticeable build time regression + # so it can be explicitly re-enabled by users who are not affected by those + # crashes. + '--features=swift.use_global_module_cache', + + # https://docs.bazel.build/versions/master/command-line-reference.html + # Print the subcommand details in case of failure. + '--verbose_failures', + ] + + self.common_build_args = [ + # https://github.com/bazelbuild/rules_swift + # If enabled and whole module optimisation is being used, the `*.swiftdoc`, + # `*.swiftmodule` and `*-Swift.h` are generated with a separate action + # rather than as part of the compilation. + '--features=swift.split_derived_files_generation', + + # https://github.com/bazelbuild/rules_swift + # If enabled the skip function bodies frontend flag is passed when using derived + # files generation. + '--features=swift.skip_function_bodies_for_derived_files', + + # Set the number of parallel processes to match the available CPU core count. + '--jobs={}'.format(os.cpu_count()), + ] + + self.common_debug_args = [ + # https://github.com/bazelbuild/rules_swift + # If enabled, Swift compilation actions will use batch mode by passing + # `-enable-batch-mode` to `swiftc`. This is a new compilation mode as of + # Swift 4.2 that is intended to speed up non-incremental non-WMO builds by + # invoking a smaller number of frontend processes and passing them batches of + # source files. + '--features=swift.enable_batch_mode', + + # https://docs.bazel.build/versions/master/command-line-reference.html + # Set the number of parallel jobs per module to saturate the available CPU resources. + '--swiftcopt=-j{}'.format(os.cpu_count() - 1), + ] + + self.common_release_args = [ + # https://github.com/bazelbuild/rules_swift + # Enable whole module optimization. + '--features=swift.opt_uses_wmo', + + # https://github.com/bazelbuild/rules_swift + # Use -Osize instead of -O when building swift modules. + '--features=swift.opt_uses_osize', + + # --num-threads 0 forces swiftc to generate one object file per module; it: + # 1. resolves issues with the linker caused by the swift-objc mixing. + # 2. makes the resulting binaries significantly smaller (up to 9% for this project). + '--swiftcopt=-num-threads', '--swiftcopt=0', + + # Strip unsused code. + '--features=dead_strip', + '--objc_enable_binary_stripping', + + # Always embed bitcode into Watch binaries. This is required by the App Store. + '--apple_bitcode=watchos=embedded', + ] + + def add_remote_cache(self, host): + self.remote_cache = host + + def add_cache_dir(self, path): + self.cache_dir = path + + def add_additional_args(self, additional_args): + self.additional_args = additional_args + + def set_build_number(self, build_number): + self.build_number = build_number + + def set_configuration_path(self, path): + self.configuration_path = path + + def set_configuration(self, configuration): + if configuration == 'debug_arm64': + self.configuration_args = [ + # bazel debug build configuration + '-c', 'dbg', + + # Build single-architecture binaries. It is almost 2 times faster is 32-bit support is not required. + '--ios_multi_cpus=arm64', + + # Always build universal Watch binaries. + '--watchos_cpus=armv7k,arm64_32' + ] + self.common_debug_args + elif configuration == 'release_arm64': + self.configuration_args = [ + # bazel optimized build configuration + '-c', 'opt', + + # Build single-architecture binaries. It is almost 2 times faster is 32-bit support is not required. + '--ios_multi_cpus=arm64', + + # Always build universal Watch binaries. + '--watchos_cpus=armv7k,arm64_32', + + # Generate DSYM files when building. + '--apple_generate_dsym', + + # Require DSYM files as build output. + '--output_groups=+dsyms' + ] + self.common_release_args + elif configuration == 'release_universal': + self.configuration_args = [ + # bazel optimized build configuration + '-c', 'opt', + + # Build universal binaries. + '--ios_multi_cpus=armv7,arm64', + + # Always build universal Watch binaries. + '--watchos_cpus=armv7k,arm64_32', + + # Generate DSYM files when building. + '--apple_generate_dsym', + + # Require DSYM files as build output. + '--output_groups=+dsyms' + ] + self.common_release_args + else: + raise Exception('Unknown configuration {}'.format(configuration)) + + def invoke_clean(self): + combined_arguments = [ + self.build_environment.bazel_path, + 'clean', + '--expunge' + ] + + print('TelegramBuild: running {}'.format(combined_arguments)) + call_executable(combined_arguments) + + def get_define_arguments(self): + return [ + '--define=buildNumber={}'.format(self.build_number), + '--define=telegramVersion={}'.format(self.build_environment.app_version) + ] + + def get_project_generation_arguments(self): + combined_arguments = [] + combined_arguments += self.common_args + combined_arguments += self.common_debug_args + combined_arguments += self.get_define_arguments() + + if self.remote_cache is not None: + combined_arguments += [ + '--remote_cache={}'.format(self.remote_cache), + '--experimental_remote_downloader={}'.format(self.remote_cache) + ] + elif self.cache_dir is not None: + combined_arguments += [ + '--disk_cache={path}'.format(path=self.cache_dir) + ] + + return combined_arguments + + def invoke_build(self): + combined_arguments = [ + self.build_environment.bazel_path, + 'build', + 'Telegram/Telegram' + ] + + if self.configuration_path is None: + raise Exception('configuration_path is not defined') + + combined_arguments += [ + '--override_repository=build_configuration={}'.format(self.configuration_path) + ] + + combined_arguments += self.common_args + combined_arguments += self.common_build_args + combined_arguments += self.get_define_arguments() + + if self.remote_cache is not None: + combined_arguments += [ + '--remote_cache={}'.format(self.remote_cache), + '--experimental_remote_downloader={}'.format(self.remote_cache) + ] + elif self.cache_dir is not None: + combined_arguments += [ + '--disk_cache={path}'.format(path=self.cache_dir) + ] + + combined_arguments += self.configuration_args + + print('TelegramBuild: running') + print(subprocess.list2cmdline(combined_arguments)) + call_executable(combined_arguments) + + +def clean(arguments): + bazel_command_line = BazelCommandLine( + bazel_path=arguments.bazel, + bazel_x86_64_path=None, + override_bazel_version=arguments.overrideBazelVersion, + override_xcode_version=arguments.overrideXcodeVersion + ) + + bazel_command_line.invoke_clean() + + +def resolve_configuration(bazel_command_line: BazelCommandLine, arguments): + if arguments.configurationGenerator is not None: + configuration_generator_arguments = shlex.split(arguments.configurationGenerator) + + configuration_generator_executable = resolve_executable(configuration_generator_arguments[0]) + + if configuration_generator_executable is None: + print('{} is not a valid executable'.format(configuration_generator_arguments[0])) + exit(1) + + temp_configuration_path = tempfile.mkdtemp() + + resolved_configuration_generator_arguments = [configuration_generator_executable] + resolved_configuration_generator_arguments += configuration_generator_arguments[1:] + resolved_configuration_generator_arguments += [temp_configuration_path] + + call_executable(resolved_configuration_generator_arguments, use_clean_environment=False) + + print('TelegramBuild: using generated configuration in {}'.format(temp_configuration_path)) + bazel_command_line.set_configuration_path(temp_configuration_path) + elif arguments.configurationPath is not None: + absolute_configuration_path = os.path.abspath(arguments.configurationPath) + if not os.path.isdir(absolute_configuration_path): + print('Error: {} does not exist'.format(absolute_configuration_path)) + exit(1) + bazel_command_line.set_configuration_path(absolute_configuration_path) + else: + raise Exception('Neither configurationPath nor configurationGenerator are set') + + +def generate_project(arguments): + bazel_x86_64_path = None + if is_apple_silicon(): + bazel_x86_64_path = arguments.bazel_x86_64 + + bazel_command_line = BazelCommandLine( + bazel_path=arguments.bazel, + bazel_x86_64_path=bazel_x86_64_path, + override_bazel_version=arguments.overrideBazelVersion, + override_xcode_version=arguments.overrideXcodeVersion + ) + + if arguments.cacheDir is not None: + bazel_command_line.add_cache_dir(arguments.cacheDir) + elif arguments.cacheHost is not None: + bazel_command_line.add_remote_cache(arguments.cacheHost) + + resolve_configuration(bazel_command_line, arguments) + + bazel_command_line.set_build_number(arguments.buildNumber) + + disable_extensions = False + if arguments.disableExtensions is not None: + disable_extensions = arguments.disableExtensions + + call_executable(['killall', 'Xcode'], check_result=False) + + generate( + build_environment=bazel_command_line.build_environment, + disable_extensions=disable_extensions, + configuration_path=bazel_command_line.configuration_path, + bazel_app_arguments=bazel_command_line.get_project_generation_arguments() + ) + + +def build(arguments): + bazel_command_line = BazelCommandLine( + bazel_path=arguments.bazel, + bazel_x86_64_path=None, + override_bazel_version=arguments.overrideBazelVersion, + override_xcode_version=arguments.overrideXcodeVersion + ) + + if arguments.cacheDir is not None: + bazel_command_line.add_cache_dir(arguments.cacheDir) + elif arguments.cacheHost is not None: + bazel_command_line.add_remote_cache(arguments.cacheHost) + + resolve_configuration(bazel_command_line, arguments) + + bazel_command_line.set_configuration(arguments.configuration) + bazel_command_line.set_build_number(arguments.buildNumber) + + bazel_command_line.invoke_build() + + +def add_project_and_build_common_arguments(current_parser: argparse.ArgumentParser): + group = current_parser.add_mutually_exclusive_group(required=True) + group.add_argument( + '--configurationPath', + help=''' + Path to a folder containing build configuration and provisioning profiles. + See build-system/example-configuration for an example. + ''', + metavar='path' + ) + group.add_argument( + '--configurationGenerator', + help=''' + A command line invocation that will dynamically generate the configuration data + (project constants and provisioning profiles). + The expression will be parsed according to the shell parsing rules into program and arguments parts. + The program will be then invoked with the given arguments plus the path to the output directory. + See build-system/generate-configuration.sh for an example. + Example: --configurationGenerator="sh ~/my_script.sh argument1" + ''', + metavar='command' + ) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(prog='Make') + + parser.add_argument( + '--verbose', + action='store_true', + default=False, + help='Print debug info' + ) + + parser.add_argument( + '--bazel', + required=True, + help='Use custom bazel binary', + metavar='path' + ) + + parser.add_argument( + '--overrideBazelVersion', + action='store_true', + help='Override bazel version with the actual version reported by the bazel binary' + ) + + parser.add_argument( + '--overrideXcodeVersion', + action='store_true', + help='Override xcode version with the actual version reported by \'xcode-select -p\'' + ) + + parser.add_argument( + '--bazelArguments', + required=False, + help='Add additional arguments to all bazel invocations.', + metavar='arguments' + ) + + cacheTypeGroup = parser.add_mutually_exclusive_group() + cacheTypeGroup.add_argument( + '--cacheHost', + required=False, + help='Use remote build artifact cache to speed up rebuilds (See https://github.com/buchgr/bazel-remote).', + metavar='http://host:9092' + ) + cacheTypeGroup.add_argument( + '--cacheDir', + required=False, + help='Cache build artifacts in a local directory to speed up rebuilds.', + metavar='path' + ) + + subparsers = parser.add_subparsers(dest='commandName', help='Commands') + + cleanParser = subparsers.add_parser( + 'clean', help=''' + Clean local bazel cache. Does not affect files cached remotely (via --cacheHost=...) or + locally in an external directory ('--cacheDir=...') + ''' + ) + + generateProjectParser = subparsers.add_parser('generateProject', help='Generate Xcode project') + if is_apple_silicon(): + generateProjectParser.add_argument( + '--bazel_x86_64', + required=True, + help='A standalone bazel x86_64 binary is required to generate a project on Apple Silicon.', + metavar='path' + ) + generateProjectParser.add_argument( + '--buildNumber', + required=False, + type=int, + default=10000, + help='Build number.', + metavar='number' + ) + add_project_and_build_common_arguments(generateProjectParser) + generateProjectParser.add_argument( + '--disableExtensions', + action='store_true', + default=False, + help=''' + The generated project will not include app extensions. + This allows Xcode to properly index the source code. + ''' + ) + + buildParser = subparsers.add_parser('build', help='Build the app') + buildParser.add_argument( + '--buildNumber', + required=True, + type=int, + help='Build number.', + metavar='number' + ) + add_project_and_build_common_arguments(buildParser) + buildParser.add_argument( + '--configuration', + choices=[ + 'debug_arm64', + 'release_arm64', + 'release_universal' + ], + required=True, + help='Build configuration' + ) + + if len(sys.argv) < 2: + parser.print_help() + sys.exit(1) + + args = parser.parse_args() + + if args.verbose: + print(args) + + if args.commandName is None: + exit(0) + + try: + if args.commandName == 'clean': + clean(arguments=args) + elif args.commandName == 'generateProject': + generate_project(arguments=args) + elif args.commandName == 'build': + build(arguments=args) + else: + raise Exception('Unknown command') + except KeyboardInterrupt: + pass diff --git a/build-system/Make/ProjectGeneration.py b/build-system/Make/ProjectGeneration.py new file mode 100644 index 0000000000..dab2520e34 --- /dev/null +++ b/build-system/Make/ProjectGeneration.py @@ -0,0 +1,155 @@ +import json +import os +import shutil + +from BuildEnvironment import is_apple_silicon, call_executable, BuildEnvironment + + +def remove_directory(path): + if os.path.isdir(path): + shutil.rmtree(path) + + +def generate(build_environment: BuildEnvironment, disable_extensions, configuration_path, bazel_app_arguments): + project_path = os.path.join(build_environment.base_path, 'build-input/gen/project') + app_target = 'Telegram' + + ''' + TULSI_APP="build-input/gen/project/Tulsi.app" + TULSI="$TULSI_APP/Contents/MacOS/Tulsi" + + rm -rf "$GEN_DIRECTORY/${APP_TARGET}.tulsiproj" + rm -rf "$TULSI_APP" + ''' + + os.makedirs(project_path, exist_ok=True) + remove_directory('{}/Tulsi.app'.format(project_path)) + remove_directory('{project}/{target}.tulsiproj'.format(project=project_path, target=app_target)) + + tulsi_path = os.path.join(project_path, 'Tulsi.app/Contents/MacOS/Tulsi') + + if is_apple_silicon(): + tulsi_build_bazel_path = build_environment.bazel_x86_64_path + if tulsi_build_bazel_path is None or not os.path.isfile(tulsi_build_bazel_path): + print('Could not find a valid bazel x86_64 binary at {}'.format(tulsi_build_bazel_path)) + exit(1) + else: + tulsi_build_bazel_path = build_environment.bazel_path + + current_dir = os.getcwd() + os.chdir(os.path.join(build_environment.base_path, 'build-system/tulsi')) + call_executable([ + tulsi_build_bazel_path, + 'build', '//:tulsi', + '--xcode_version={}'.format(build_environment.xcode_version), + '--use_top_level_targets_for_symlinks', + '--verbose_failures' + ]) + os.chdir(current_dir) + + bazel_wrapper_path = os.path.abspath('build-input/gen/project/bazel') + + bazel_wrapper_arguments = [] + bazel_wrapper_arguments += ['--override_repository=build_configuration={}'.format(configuration_path)] + if disable_extensions and False: + bazel_wrapper_arguments += ['--//Telegram:disableExtensions'] + + with open(bazel_wrapper_path, 'wb') as bazel_wrapper: + bazel_wrapper.write('''#!/bin/sh +{bazel} "$@" {arguments} +'''.format( + bazel=build_environment.bazel_path, + arguments=' '.join(bazel_wrapper_arguments) + ).encode('utf-8')) + + call_executable(['chmod', '+x', bazel_wrapper_path]) + + call_executable([ + 'unzip', '-oq', + 'build-system/tulsi/bazel-bin/tulsi.zip', + '-d', project_path + ]) + + user_defaults_path = os.path.expanduser('~/Library/Preferences/com.google.Tulsi.plist') + if os.path.isfile(user_defaults_path): + os.unlink(user_defaults_path) + + with open(user_defaults_path, 'wb') as user_defaults: + user_defaults.write(''' + + + + + defaultBazelURL + {} + + +'''.format(bazel_wrapper_path).encode('utf-8')) + + bazel_build_arguments = [] + bazel_build_arguments += ['--override_repository=build_configuration={}'.format(configuration_path)] + if disable_extensions: + bazel_build_arguments += ['--//Telegram:disableExtensions'] + + call_executable([ + tulsi_path, + '--', + '--verbose', + '--create-tulsiproj', app_target, + '--workspaceroot', './', + '--bazel', bazel_wrapper_path, + '--outputfolder', project_path, + '--target', '{target}:{target}'.format(target=app_target), + '--build-options', ' '.join(bazel_build_arguments) + ]) + + additional_arguments = [] + additional_arguments += ['--override_repository=build_configuration={}'.format(configuration_path)] + additional_arguments += bazel_app_arguments + if disable_extensions: + additional_arguments += ['--//Telegram:disableExtensions'] + + additional_arguments_string = ' '.join(additional_arguments) + + tulsi_config_path = 'build-input/gen/project/{target}.tulsiproj/Configs/{target}.tulsigen'.format(target=app_target) + with open(tulsi_config_path, 'rb') as tulsi_config: + tulsi_config_json = json.load(tulsi_config) + for category in ['BazelBuildOptionsDebug', 'BazelBuildOptionsRelease']: + tulsi_config_json['optionSet'][category]['p'] += ' {}'.format(additional_arguments_string) + tulsi_config_json['sourceFilters'] = [ + 'Telegram/...', + 'submodules/...', + 'third-party/...' + ] + with open(tulsi_config_path, 'wb') as tulsi_config: + tulsi_config.write(json.dumps(tulsi_config_json, indent=2).encode('utf-8')) + + call_executable([ + tulsi_path, + '--', + '--verbose', + '--genconfig', '{project}/{target}.tulsiproj:{target}'.format(project=project_path, target=app_target), + '--bazel', bazel_wrapper_path, + '--outputfolder', project_path, + '--no-open-xcode' + ]) + + xcodeproj_path = '{project}/{target}.xcodeproj'.format(project=project_path, target=app_target) + + bazel_build_settings_path = '{}/.tulsi/Scripts/bazel_build_settings.py'.format(xcodeproj_path) + + with open(bazel_build_settings_path, 'rb') as bazel_build_settings: + bazel_build_settings_contents = bazel_build_settings.read().decode('utf-8') + bazel_build_settings_contents = bazel_build_settings_contents.replace( + 'BUILD_SETTINGS = BazelBuildSettings(', + 'import os\nBUILD_SETTINGS = BazelBuildSettings(' + ) + bazel_build_settings_contents = bazel_build_settings_contents.replace( + '\'--cpu=ios_arm64\'', + '\'--cpu=ios_arm64\'.replace(\'ios_arm64\', \'ios_sim_arm64\' if os.environ.get(\'EFFECTIVE_PLATFORM_NAME\') ' + '== \'-iphonesimulator\' else \'ios_arm64\')' + ) + with open(bazel_build_settings_path, 'wb') as bazel_build_settings: + bazel_build_settings.write(bazel_build_settings_contents.encode('utf-8')) + + call_executable(['open', xcodeproj_path]) diff --git a/build-system/bazel_version b/build-system/bazel_version deleted file mode 100644 index 7c69a55dbb..0000000000 --- a/build-system/bazel_version +++ /dev/null @@ -1 +0,0 @@ -3.7.0 diff --git a/build-input/BUILD b/build-system/example-configuration/BUILD similarity index 100% rename from build-input/BUILD rename to build-system/example-configuration/BUILD diff --git a/build-system/example-configuration/WORKSPACE b/build-system/example-configuration/WORKSPACE new file mode 100644 index 0000000000..e69de29bb2 diff --git a/build-system/example-configuration/provisioning/BUILD b/build-system/example-configuration/provisioning/BUILD new file mode 100644 index 0000000000..9abb88a19d --- /dev/null +++ b/build-system/example-configuration/provisioning/BUILD @@ -0,0 +1,11 @@ + +exports_files([ + "Intents.mobileprovision", + "NotificationContent.mobileprovision", + "NotificationService.mobileprovision", + "Share.mobileprovision", + "Telegram.mobileprovision", + "WatchApp.mobileprovision", + "WatchExtension.mobileprovision", + "Widget.mobileprovision", +]) diff --git a/build-system/example-configuration/provisioning/Intents.mobileprovision b/build-system/example-configuration/provisioning/Intents.mobileprovision new file mode 100644 index 0000000000..8cb484f943 Binary files /dev/null and b/build-system/example-configuration/provisioning/Intents.mobileprovision differ diff --git a/build-system/example-configuration/provisioning/NotificationContent.mobileprovision b/build-system/example-configuration/provisioning/NotificationContent.mobileprovision new file mode 100644 index 0000000000..bd054573e9 Binary files /dev/null and b/build-system/example-configuration/provisioning/NotificationContent.mobileprovision differ diff --git a/build-system/example-configuration/provisioning/NotificationService.mobileprovision b/build-system/example-configuration/provisioning/NotificationService.mobileprovision new file mode 100644 index 0000000000..663bd68b69 Binary files /dev/null and b/build-system/example-configuration/provisioning/NotificationService.mobileprovision differ diff --git a/build-system/example-configuration/provisioning/Share.mobileprovision b/build-system/example-configuration/provisioning/Share.mobileprovision new file mode 100644 index 0000000000..e49de2d713 Binary files /dev/null and b/build-system/example-configuration/provisioning/Share.mobileprovision differ diff --git a/build-system/example-configuration/provisioning/Telegram.mobileprovision b/build-system/example-configuration/provisioning/Telegram.mobileprovision new file mode 100644 index 0000000000..1592674339 Binary files /dev/null and b/build-system/example-configuration/provisioning/Telegram.mobileprovision differ diff --git a/build-system/example-configuration/provisioning/WatchApp.mobileprovision b/build-system/example-configuration/provisioning/WatchApp.mobileprovision new file mode 100644 index 0000000000..2b0558a240 Binary files /dev/null and b/build-system/example-configuration/provisioning/WatchApp.mobileprovision differ diff --git a/build-system/example-configuration/provisioning/WatchExtension.mobileprovision b/build-system/example-configuration/provisioning/WatchExtension.mobileprovision new file mode 100644 index 0000000000..ba9ce392ff Binary files /dev/null and b/build-system/example-configuration/provisioning/WatchExtension.mobileprovision differ diff --git a/build-system/example-configuration/provisioning/Widget.mobileprovision b/build-system/example-configuration/provisioning/Widget.mobileprovision new file mode 100644 index 0000000000..37de9fe5aa Binary files /dev/null and b/build-system/example-configuration/provisioning/Widget.mobileprovision differ diff --git a/build-system/example-configuration/variables.bzl b/build-system/example-configuration/variables.bzl new file mode 100644 index 0000000000..a89c790a17 --- /dev/null +++ b/build-system/example-configuration/variables.bzl @@ -0,0 +1,10 @@ +telegram_bundle_id = "ph.telegra.Telegraph" +telegram_api_id = "8" +telegram_api_hash = "7245de8e747a0d6fbe11f7cc14fcc0bb" +telegram_team_id = "C67CF9S4VU" +telegram_app_center_id = "0" +telegram_is_internal_build = "false" +telegram_is_appstore_build = "true" +telegram_appstore_id = "0" +telegram_app_specific_url_scheme = "tg" +telegram_aps_environment = "production" diff --git a/build-system/example-generate-configuration b/build-system/example-generate-configuration new file mode 100755 index 0000000000..79217d6514 --- /dev/null +++ b/build-system/example-generate-configuration @@ -0,0 +1,7 @@ +#!/bin/sh + +if [ ! -d "$1" ]; then + exit 1 +fi + +cp -R build-system/example-configuration/* "$1/" diff --git a/build-system/manage-developer-portal-app.sh b/build-system/manage-developer-portal-app.sh deleted file mode 100644 index 60e21c1381..0000000000 --- a/build-system/manage-developer-portal-app.sh +++ /dev/null @@ -1,129 +0,0 @@ -#!/bin/bash - -set -e - -FASTLANE="$(which fastlane)" - -EXPECTED_VARIABLES=(\ - APPLE_ID \ - BASE_BUNDLE_ID \ - APP_NAME \ - TEAM_ID \ - PROVISIONING_DIRECTORY \ -) - -MISSING_VARIABLES="0" -for VARIABLE_NAME in ${EXPECTED_VARIABLES[@]}; do - if [ "${!VARIABLE_NAME}" = "" ]; then - echo "$VARIABLE_NAME not defined" - MISSING_VARIABLES="1" - fi -done -if [ "$MISSING_VARIABLES" == "1" ]; then - exit 1 -fi - -if [ ! -d "$PROVISIONING_DIRECTORY" ]; then - echo "Directory $PROVISIONING_DIRECTORY does not exist" - exit 1 -fi - -BASE_DIR=$(mktemp -d) -FASTLANE_DIR="$BASE_DIR/fastlane" -mkdir "$FASTLANE_DIR" -FASTFILE="$FASTLANE_DIR/Fastfile" - -touch "$FASTFILE" - -CREDENTIALS=(\ - --username "$APPLE_ID" \ - --team_id "$TEAM_ID" \ -) -export FASTLANE_SKIP_UPDATE_CHECK=1 - -APP_EXTENSIONS=(\ - Share \ - SiriIntents \ - NotificationContent \ - NotificationService \ - Widget \ -) - -echo "lane :manage_app do" >> "$FASTFILE" -echo " produce(" >> "$FASTFILE" -echo " username: '$APPLE_ID'," >> "$FASTFILE" -echo " app_identifier: '${BASE_BUNDLE_ID}'," >> "$FASTFILE" -echo " app_name: '$APP_NAME'," >> "$FASTFILE" -echo " language: 'English'," >> "$FASTFILE" -echo " app_version: '1.0'," >> "$FASTFILE" -echo " team_id: '$TEAM_ID'," >> "$FASTFILE" -echo " skip_itc: true," >> "$FASTFILE" -echo " )" >> "$FASTFILE" - -echo " produce(" >> "$FASTFILE" -echo " username: '$APPLE_ID'," >> "$FASTFILE" -echo " app_identifier: '${BASE_BUNDLE_ID}.watchkitapp'," >> "$FASTFILE" -echo " app_name: '$APP_NAME Watch App'," >> "$FASTFILE" -echo " language: 'English'," >> "$FASTFILE" -echo " app_version: '1.0'," >> "$FASTFILE" -echo " team_id: '$TEAM_ID'," >> "$FASTFILE" -echo " skip_itc: true," >> "$FASTFILE" -echo " )" >> "$FASTFILE" - -echo " produce(" >> "$FASTFILE" -echo " username: '$APPLE_ID'," >> "$FASTFILE" -echo " app_identifier: '${BASE_BUNDLE_ID}.watchkitapp.watchkitextension'," >> "$FASTFILE" -echo " app_name: '$APP_NAME Watch App Extension'," >> "$FASTFILE" -echo " language: 'English'," >> "$FASTFILE" -echo " app_version: '1.0'," >> "$FASTFILE" -echo " team_id: '$TEAM_ID'," >> "$FASTFILE" -echo " skip_itc: true," >> "$FASTFILE" -echo " )" >> "$FASTFILE" - -for EXTENSION in ${APP_EXTENSIONS[@]}; do - echo " produce(" >> "$FASTFILE" - echo " username: '$APPLE_ID'," >> "$FASTFILE" - echo " app_identifier: '${BASE_BUNDLE_ID}.${EXTENSION}'," >> "$FASTFILE" - echo " app_name: '${APP_NAME} ${EXTENSION}'," >> "$FASTFILE" - echo " language: 'English'," >> "$FASTFILE" - echo " app_version: '1.0'," >> "$FASTFILE" - echo " team_id: '$TEAM_ID'," >> "$FASTFILE" - echo " skip_itc: true," >> "$FASTFILE" - echo " )" >> "$FASTFILE" -done - -echo "end" >> "$FASTFILE" - -pushd "$BASE_DIR" - -fastlane cert ${CREDENTIALS[@]} --development - -fastlane manage_app - -fastlane produce group -g "group.$BASE_BUNDLE_ID" -n "$APP_NAME Group" ${CREDENTIALS[@]} - -fastlane produce enable_services -a "$BASE_BUNDLE_ID" ${CREDENTIALS[@]} \ - --app-group \ - --push-notification \ - --sirikit - -fastlane produce associate_group -a "$BASE_BUNDLE_ID" "group.$BASE_BUNDLE_ID" ${CREDENTIALS[@]} -for EXTENSION in ${APP_EXTENSIONS[@]}; do - fastlane produce enable_services -a "${BASE_BUNDLE_ID}.${EXTENSION}" ${CREDENTIALS[@]} \ - --app-group - - fastlane produce associate_group -a "${BASE_BUNDLE_ID}.${EXTENSION}" "group.$BASE_BUNDLE_ID" ${CREDENTIALS[@]} -done - -for DEVELOPMENT_FLAG in "--development"; do - fastlane sigh -a "$BASE_BUNDLE_ID" ${CREDENTIALS[@]} -o "$PROVISIONING_DIRECTORY" $DEVELOPMENT_FLAG \ - --skip_install - for EXTENSION in ${APP_EXTENSIONS[@]}; do - fastlane sigh -a "${BASE_BUNDLE_ID}.${EXTENSION}" ${CREDENTIALS[@]} -o "$PROVISIONING_DIRECTORY" $DEVELOPMENT_FLAG \ - --skip_install - done -done - -popd - -rm -rf "$BASE_DIR" diff --git a/build-system/tulsi b/build-system/tulsi index b965114f31..734518e85d 160000 --- a/build-system/tulsi +++ b/build-system/tulsi @@ -1 +1 @@ -Subproject commit b965114f31a464185318c71ef3d0c1538d0c52de +Subproject commit 734518e85d769de070b5a78b234080d9580ae625 diff --git a/build-system/xcode_version b/build-system/xcode_version deleted file mode 100644 index 5807e59269..0000000000 --- a/build-system/xcode_version +++ /dev/null @@ -1 +0,0 @@ -12.2 diff --git a/buildbox/build-telegram.sh b/buildbox/build-telegram.sh index 38a7bf6fd8..c1e4905153 100644 --- a/buildbox/build-telegram.sh +++ b/buildbox/build-telegram.sh @@ -5,7 +5,7 @@ set -e BUILD_TELEGRAM_VERSION="1" MACOS_VERSION="10.15" -XCODE_VERSION="12.2" +XCODE_VERSION="12.3" GUEST_SHELL="bash" VM_BASE_NAME="macos$(echo $MACOS_VERSION | sed -e 's/\.'/_/g)_Xcode$(echo $XCODE_VERSION | sed -e 's/\.'/_/g)" @@ -58,7 +58,7 @@ cp "$BAZEL" "tools/bazel" BUILD_CONFIGURATION="$1" if [ "$BUILD_CONFIGURATION" == "hockeyapp" ] || [ "$BUILD_CONFIGURATION" == "appcenter-experimental" ] || [ "$BUILD_CONFIGURATION" == "appcenter-experimental-2" ]; then - CODESIGNING_SUBPATH="transient-data/codesigning" + CODESIGNING_SUBPATH="transient-data/telegram-codesigning/codesigning" CODESIGNING_TEAMS_SUBPATH="transient-data/teams" elif [ "$BUILD_CONFIGURATION" == "appstore" ]; then CODESIGNING_SUBPATH="transient-data/codesigning" @@ -90,28 +90,25 @@ fi BASE_DIR=$(pwd) if [ "$BUILD_CONFIGURATION" == "hockeyapp" ] || [ "$BUILD_CONFIGURATION" == "appcenter-experimental" ] || [ "$BUILD_CONFIGURATION" == "appcenter-experimental-2" ] || [ "$BUILD_CONFIGURATION" == "appstore" ]; then - if [ ! `which setup-telegram-build.sh` ]; then - echo "setup-telegram-build.sh not found in PATH $PATH" + if [ ! `which generate-configuration.sh` ]; then + echo "generate-configuration.sh not found in PATH $PATH" exit 1 fi - if [ ! `which setup-codesigning.sh` ]; then - echo "setup-codesigning.sh not found in PATH $PATH" - exit 1 - fi - source `which setup-telegram-build.sh` - setup_telegram_build "$BUILD_CONFIGURATION" "$BASE_DIR/$BUILDBOX_DIR/transient-data" - source `which setup-codesigning.sh` - - CODESIGNING_CONFIGURATION="$BUILD_CONFIGURATION" - if [ "$BUILD_CONFIGURATION" == "appcenter-experimental" ] || [ "$BUILD_CONFIGURATION" == "appcenter-experimental-2" ]; then - CODESIGNING_CONFIGURATION="hockeyapp" - fi - setup_codesigning "$CODESIGNING_CONFIGURATION" "$BASE_DIR/$BUILDBOX_DIR/transient-data" - if [ "$SETUP_TELEGRAM_BUILD_VERSION" != "$BUILD_TELEGRAM_VERSION" ]; then - echo "setup-telegram-build.sh script version doesn't match" - exit 1 - fi + mkdir -p "$BASE_DIR/$BUILDBOX_DIR/transient-data/telegram-codesigning" + mkdir -p "$BASE_DIR/$BUILDBOX_DIR/transient-data/build-configuration" + + case "$BUILD_CONFIGURATION" in + "hockeyapp") + generate-configuration.sh internal release "$BASE_DIR/$BUILDBOX_DIR/transient-data/telegram-codesigning" "$BASE_DIR/$BUILDBOX_DIR/transient-data/build-configuration" + ;; + + *) + echo "Unknown build configuration $BUILD_CONFIGURATION" + exit 1 + ;; + esac + if [ "$BUILD_CONFIGURATION" == "appstore" ]; then if [ -z "$TELEGRAM_BUILD_APPSTORE_PASSWORD" ]; then echo "TELEGRAM_BUILD_APPSTORE_PASSWORD is not set" @@ -186,13 +183,14 @@ elif [ "$BUILD_MACHINE" == "macOS" ]; then fi scp -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -pr "$BUILDBOX_DIR/$CODESIGNING_SUBPATH" telegram@"$VM_IP":codesigning_data -scp -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -pr "$BUILDBOX_DIR/$CODESIGNING_TEAMS_SUBPATH" telegram@"$VM_IP":codesigning_teams +scp -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -pr "$BASE_DIR/$BUILDBOX_DIR/transient-data/build-configuration" telegram@"$VM_IP":telegram-configuration +#scp -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -pr "$BUILDBOX_DIR/$CODESIGNING_TEAMS_SUBPATH" telegram@"$VM_IP":codesigning_teams -if [ "$BUILD_CONFIGURATION" == "verify" ]; then - ssh -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null telegram@"$VM_IP" -o ServerAliveInterval=60 -t "mkdir -p telegram-ios-shared/fastlane; echo '' > telegram-ios-shared/fastlane/Fastfile" -else - scp -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -pr "$BUILDBOX_DIR/transient-data/telegram-ios-shared" telegram@"$VM_IP":telegram-ios-shared -fi +#if [ "$BUILD_CONFIGURATION" == "verify" ]; then +# ssh -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null telegram@"$VM_IP" -o ServerAliveInterval=60 -t "mkdir -p telegram-ios-shared/fastlane; echo '' > telegram-ios-shared/fastlane/Fastfile" +#else +# scp -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -pr "$BUILDBOX_DIR/transient-data/telegram-ios-shared" telegram@"$VM_IP":telegram-ios-shared +#fi scp -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -pr "$BUILDBOX_DIR/guest-build-telegram.sh" "$BUILDBOX_DIR/transient-data/source.tar" telegram@"$VM_IP": ssh -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null telegram@"$VM_IP" -o ServerAliveInterval=60 -t "export TELEGRAM_BUILD_APPSTORE_PASSWORD=\"$TELEGRAM_BUILD_APPSTORE_PASSWORD\"; export TELEGRAM_BUILD_APPSTORE_TEAM_NAME=\"$TELEGRAM_BUILD_APPSTORE_TEAM_NAME\"; export TELEGRAM_BUILD_APPSTORE_USERNAME=\"$TELEGRAM_BUILD_APPSTORE_USERNAME\"; export BUILD_NUMBER=\"$BUILD_NUMBER\"; export COMMIT_ID=\"$COMMIT_ID\"; export COMMIT_AUTHOR=\"$COMMIT_AUTHOR\"; export BAZEL_HTTP_CACHE_URL=\"$BAZEL_HTTP_CACHE_URL\"; $GUEST_SHELL -l guest-build-telegram.sh $BUILD_CONFIGURATION" || true diff --git a/buildbox/guest-build-telegram.sh b/buildbox/guest-build-telegram.sh index 3e4681311c..d540abab4d 100644 --- a/buildbox/guest-build-telegram.sh +++ b/buildbox/guest-build-telegram.sh @@ -13,8 +13,8 @@ if [ -z "COMMIT_ID" ]; then fi if [ "$1" == "hockeyapp" ] || [ "$1" == "appcenter-experimental" ] || [ "$1" == "appcenter-experimental-2" ] || [ "$1" == "testinghockeyapp" ]; then - CERTS_PATH="$HOME/codesigning_data/certs" - PROFILES_PATH="$HOME/codesigning_data/profiles" + CERTS_PATH="$HOME/codesigning_data/certs/enterprise" + #PROFILES_PATH="$HOME/codesigning_data/profiles" elif [ "$1" == "testinghockeyapp-local" ]; then CERTS_PATH="$HOME/codesigning_data/certs" PROFILES_PATH="$HOME/codesigning_data/profiles" @@ -79,7 +79,7 @@ echo "Unpacking files..." mkdir -p "$SOURCE_PATH/buildbox" mkdir -p "$SOURCE_PATH/buildbox/transient-data" -cp -r "$HOME/codesigning_teams" "$SOURCE_PATH/buildbox/transient-data/teams" +#cp -r "$HOME/codesigning_teams" "$SOURCE_PATH/buildbox/transient-data/teams" BASE_DIR=$(pwd) cd "$SOURCE_PATH" @@ -95,17 +95,18 @@ done security set-key-partition-list -S apple-tool:,apple: -k "$MY_KEYCHAIN_PASSWORD" "$MY_KEYCHAIN" -mkdir -p "$HOME/Library/MobileDevice/Provisioning Profiles" +#mkdir -p "$HOME/Library/MobileDevice/Provisioning Profiles" -for f in $(ls "$PROFILES_PATH"); do - PROFILE_PATH="$PROFILES_PATH/$f" - uuid=`grep UUID -A1 -a "$PROFILE_PATH" | grep -io "[-A-F0-9]\{36\}"` - cp -f "$PROFILE_PATH" "$HOME/Library/MobileDevice/Provisioning Profiles/$uuid.mobileprovision" -done +#for f in $(ls "$PROFILES_PATH"); do +# PROFILE_PATH="$PROFILES_PATH/$f" +# uuid=`grep UUID -A1 -a "$PROFILE_PATH" | grep -io "[-A-F0-9]\{36\}"` +# cp -f "$PROFILE_PATH" "$HOME/Library/MobileDevice/Provisioning Profiles/$uuid.mobileprovision" +#done if [ "$1" == "hockeyapp" ] || [ "$1" == "appcenter-experimental" ] || [ "$1" == "appcenter-experimental-2" ]; then - BUILD_ENV_SCRIPT="../telegram-ios-shared/buildbox/bin/internal.sh" - APP_TARGET="bazel_app_arm64" + #BUILD_ENV_SCRIPT="../telegram-ios-shared/buildbox/bin/internal.sh" + #APP_TARGET="bazel_app_arm64" + echo "" >> /dev/null elif [ "$1" == "appstore" ]; then BUILD_ENV_SCRIPT="../telegram-ios-shared/buildbox/bin/appstore.sh" APP_TARGET="bazel_app" @@ -126,7 +127,15 @@ elif [ "$1" == "appcenter-experimental-2" ]; then export APP_CENTER_ID="$APP_CENTER_EXPERIMENTAL_2_ID" fi -PATH="$PATH:$(pwd)/tools" BAZEL_HTTP_CACHE_URL="$BAZEL_HTTP_CACHE_URL" LOCAL_CODESIGNING=1 sh "$BUILD_ENV_SCRIPT" make "$APP_TARGET" +#PATH="$PATH:$(pwd)/tools" BAZEL_HTTP_CACHE_URL="$BAZEL_HTTP_CACHE_URL" LOCAL_CODESIGNING=1 sh "$BUILD_ENV_SCRIPT" make "$APP_TARGET" + +python3 build-system/Make/Make.py \ + --bazel="$(pwd)/tools/bazel" \ + --cacheHost="$BAZEL_HTTP_CACHE_URL" \ + build \ + --configurationPath="$HOME/telegram-configuration" \ + --buildNumber="$BUILD_NUMBER" \ + --configuration=release_arm64 OUTPUT_PATH="build/artifacts" rm -rf "$OUTPUT_PATH" diff --git a/submodules/BuildConfig/BUILD b/submodules/BuildConfig/BUILD index 43c0ca2b29..d3f9c56360 100644 --- a/submodules/BuildConfig/BUILD +++ b/submodules/BuildConfig/BUILD @@ -1,5 +1,5 @@ load( - "//build-input/data:variables.bzl", + "@build_configuration//:variables.bzl", "telegram_api_id", "telegram_api_hash", "telegram_app_center_id", diff --git a/submodules/Postbox/Sources/TopChatMessageView.swift b/submodules/Postbox/Sources/TopChatMessageView.swift new file mode 100644 index 0000000000..be48793c97 --- /dev/null +++ b/submodules/Postbox/Sources/TopChatMessageView.swift @@ -0,0 +1,44 @@ +import Foundation + +final class MutableTopChatMessageView: MutablePostboxView { + private let peerIds: Set + fileprivate var messages: [PeerId: Message] = [:] + + init(postbox: Postbox, peerIds: Set) { + self.peerIds = peerIds + + for peerId in self.peerIds { + if let index = postbox.chatListIndexTable.get(peerId: peerId).topMessageIndex { + self.messages[peerId] = postbox.getMessage(index.id) + } + } + } + + func replay(postbox: Postbox, transaction: PostboxTransaction) -> Bool { + var updated = false + for peerId in self.peerIds { + if transaction.currentOperationsByPeerId[peerId] != nil { + if let index = postbox.chatListIndexTable.get(peerId: peerId).topMessageIndex { + self.messages[peerId] = postbox.getMessage(index.id) + } else { + self.messages.removeValue(forKey: peerId) + } + updated = true + } + } + + return updated + } + + func immutableView() -> PostboxView { + return TopChatMessageView(self) + } +} + +public final class TopChatMessageView: PostboxView { + public let messages: [PeerId: Message] + + init(_ view: MutableTopChatMessageView) { + self.messages = view.messages + } +} diff --git a/submodules/Postbox/Sources/Views.swift b/submodules/Postbox/Sources/Views.swift index 3202e76e23..255489a3fe 100644 --- a/submodules/Postbox/Sources/Views.swift +++ b/submodules/Postbox/Sources/Views.swift @@ -30,6 +30,7 @@ public enum PostboxViewKey: Hashable { case basicPeer(PeerId) case allChatListHoles(PeerGroupId) case historyTagInfo(peerId: PeerId, tag: MessageTags) + case topChatMessage(peerIds: [PeerId]) public var hashValue: Int { switch self { @@ -91,6 +92,8 @@ public enum PostboxViewKey: Hashable { return groupId.hashValue case let .historyTagInfo(peerId, tag): return peerId.hashValue ^ tag.hashValue + case let .topChatMessage(peerIds): + return peerIds.hashValue } } @@ -270,6 +273,12 @@ public enum PostboxViewKey: Hashable { } else { return false } + case let .topChatMessage(peerIds): + if case .topChatMessage(peerIds) = rhs { + return true + } else { + return false + } } } } @@ -334,5 +343,7 @@ func postboxViewForKey(postbox: Postbox, key: PostboxViewKey) -> MutablePostboxV return MutableAllChatListHolesView(postbox: postbox, groupId: groupId) case let .historyTagInfo(peerId, tag): return MutableHistoryTagInfoView(postbox: postbox, peerId: peerId, tag: tag) + case let .topChatMessage(peerIds): + return MutableTopChatMessageView(postbox: postbox, peerIds: Set(peerIds)) } } diff --git a/submodules/TelegramCore/Sources/InvitationLinks.swift b/submodules/TelegramCore/Sources/InvitationLinks.swift index 04a9c3d1d9..8fb5e28bc2 100644 --- a/submodules/TelegramCore/Sources/InvitationLinks.swift +++ b/submodules/TelegramCore/Sources/InvitationLinks.swift @@ -12,7 +12,7 @@ public func ensuredExistingPeerExportedInvitation(account: Account, peerId: Peer let flags: Int32 = (1 << 2) if let _ = peer as? TelegramChannel { if let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData, cachedData.exportedInvitation != nil && !revokeExisted { - return .complete() + return .single(cachedData.exportedInvitation) } else { return account.network.request(Api.functions.messages.exportChatInvite(flags: flags, peer: inputPeer, expireDate: nil, usageLimit: nil)) |> retryRequest @@ -99,10 +99,13 @@ public func peerExportedInvitations(account: Account, peerId: PeerId, revoked: B return account.postbox.transaction { transaction -> Signal in if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) { var flags: Int32 = 0 + if let _ = offsetLink { + flags |= (1 << 2) + } if revoked { flags |= (1 << 3) } - return account.network.request(Api.functions.messages.getExportedChatInvites(flags: flags, peer: inputPeer, adminId: nil, offsetLink: nil, limit: 50)) + return account.network.request(Api.functions.messages.getExportedChatInvites(flags: flags, peer: inputPeer, adminId: nil, offsetLink: offsetLink, limit: 50)) |> map(Optional.init) |> `catch` { _ -> Signal in return .single(nil) diff --git a/submodules/TelegramUI/BUILD b/submodules/TelegramUI/BUILD index 112616c6f1..63e2395943 100644 --- a/submodules/TelegramUI/BUILD +++ b/submodules/TelegramUI/BUILD @@ -187,6 +187,7 @@ swift_library( "//submodules/Markdown:Markdown", "//submodules/SearchPeerMembers:SearchPeerMembers", "//submodules/WidgetItems:WidgetItems", + "//submodules/WidgetItemsUtils:WidgetItemsUtils", "//submodules/OpenSSLEncryptionProvider:OpenSSLEncryptionProvider", "//submodules/PhoneNumberFormat:PhoneNumberFormat", "//submodules/AppLock:AppLock", @@ -211,6 +212,7 @@ swift_library( "//submodules/SlotMachineAnimationNode:SlotMachineAnimationNode", "//submodules/AnimatedNavigationStripeNode:AnimatedNavigationStripeNode", "//submodules/AudioBlob:AudioBlob", + "//Telegram:GeneratedSources", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Sources/WidgetDataContext.swift b/submodules/TelegramUI/Sources/WidgetDataContext.swift index 1822576ce5..f962dbc4f2 100644 --- a/submodules/TelegramUI/Sources/WidgetDataContext.swift +++ b/submodules/TelegramUI/Sources/WidgetDataContext.swift @@ -8,6 +8,9 @@ import TelegramPresentationData import NotificationsPresentationData import WidgetKit import TelegramUIPreferences +import WidgetItemsUtils + +import GeneratedSources final class WidgetDataContext { private var currentAccount: Account? @@ -32,9 +35,45 @@ final class WidgetDataContext { } case disabled - case peers(peers: [Peer], unread: [PeerId: Unread]) + case peers(peers: [Peer], unread: [PeerId: Unread], messages: [PeerId: WidgetDataPeer.Message]) } + let updatedAdditionalPeerIds: Signal, NoError> = Signal { subscriber in + if #available(iOSApplicationExtension 14.0, iOS 14.0, *) { + #if arch(arm64) || arch(i386) || arch(x86_64) + WidgetCenter.shared.getCurrentConfigurations({ result in + var peerIds = Set() + if case let .success(infos) = result { + for info in infos { + if let configuration = info.configuration as? SelectFriendsIntent { + if let items = configuration.friends { + for item in items { + guard let identifier = item.identifier, let peerIdValue = Int64(identifier) else { + continue + } + peerIds.insert(PeerId(peerIdValue)) + } + } + } + } + } + + subscriber.putNext(peerIds) + subscriber.putCompletion() + }) + #else + subscriber.putNext(Set()) + subscriber.putCompletion() + #endif + } else { + subscriber.putNext(Set()) + subscriber.putCompletion() + } + + return EmptyDisposable + } + |> runOn(.mainQueue()) + let preferencesKey: PostboxViewKey = .preferences(keys: Set([ ApplicationSpecificPreferencesKeys.widgetSettings ])) @@ -69,13 +108,24 @@ final class WidgetDataContext { case .disabled: return .single(.disabled) case let .peers(peers): - return combineLatest(queue: .mainQueue(), peers.filter { !$0.isDeleted }.map { account.postbox.peerView(id: $0.id)}) |> mapToSignal { peerViews -> Signal in - return account.postbox.unreadMessageCountsView(items: peerViews.map { - .peer($0.peerId) - }) - |> map { values -> CombinedRecentPeers in + return combineLatest(queue: .mainQueue(), peers.filter { !$0.isDeleted }.map { account.postbox.peerView(id: $0.id)}) + |> mapToSignal { peerViews -> Signal in + let topMessagesKey: PostboxViewKey = .topChatMessage(peerIds: peerViews.map { + $0.peerId + }) + return combineLatest(queue: .mainQueue(), + account.postbox.unreadMessageCountsView(items: peerViews.map { + .peer($0.peerId) + }), + account.postbox.combinedView(keys: [topMessagesKey]) + ) + |> map { values, combinedView -> CombinedRecentPeers in var peers: [Peer] = [] var unread: [PeerId: CombinedRecentPeers.Unread] = [:] + var messages: [PeerId: WidgetDataPeer.Message] = [:] + + let topMessages = combinedView.views[topMessagesKey] as! TopChatMessageView + for peerView in peerViews { if let peer = peerViewMainPeer(peerView) { var isMuted: Bool = false @@ -93,21 +143,25 @@ final class WidgetDataContext { unread[peerView.peerId] = CombinedRecentPeers.Unread(count: Int32(unreadCount), isMuted: isMuted) } + if let message = topMessages.messages[peerView.peerId] { + messages[peerView.peerId] = WidgetDataPeer.Message(message: message) + } + peers.append(peer) } } - return .peers(peers: peers, unread: unread) + return .peers(peers: peers, unread: unread, messages: messages) } } } } - return recent + let processedRecent = recent |> map { result -> WidgetData in switch result { case .disabled: return WidgetData(accountId: account.id.int64, content: .disabled) - case let .peers(peers, unread): + case let .peers(peers, unread, messages): return WidgetData(accountId: account.id.int64, content: .peers(WidgetDataPeers(accountPeerId: account.peerId.toInt64(), peers: peers.compactMap { peer -> WidgetDataPeer? in var name: String = "" var lastName: String? @@ -133,13 +187,110 @@ final class WidgetDataContext { ) } + let message = messages[peer.id] + return WidgetDataPeer(id: peer.id.toInt64(), name: name, lastName: lastName, letters: peer.displayLetters, avatarPath: smallestImageRepresentation(peer.profileImageRepresentations).flatMap { representation in return account.postbox.mediaBox.resourcePath(representation.resource) - }, badge: badge) + }, badge: badge, message: message) }))) } } |> distinctUntilChanged + + let additionalPeerIds = Signal, NoError>.single(Set()) |> then(updatedAdditionalPeerIds) + let processedCustom: Signal = additionalPeerIds + |> distinctUntilChanged + |> mapToSignal { additionalPeerIds -> Signal in + return combineLatest(queue: .mainQueue(), additionalPeerIds.map { account.postbox.peerView(id: $0) }) + |> mapToSignal { peerViews -> Signal in + let topMessagesKey: PostboxViewKey = .topChatMessage(peerIds: peerViews.map { + $0.peerId + }) + return combineLatest(queue: .mainQueue(), + account.postbox.unreadMessageCountsView(items: peerViews.map { + .peer($0.peerId) + }), + account.postbox.combinedView(keys: [topMessagesKey]) + ) + |> map { values, combinedView -> CombinedRecentPeers in + var peers: [Peer] = [] + var unread: [PeerId: CombinedRecentPeers.Unread] = [:] + var messages: [PeerId: WidgetDataPeer.Message] = [:] + + let topMessages = combinedView.views[topMessagesKey] as! TopChatMessageView + + for peerView in peerViews { + if let peer = peerViewMainPeer(peerView) { + var isMuted: Bool = false + if let notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings { + switch notificationSettings.muteState { + case .muted: + isMuted = true + default: + break + } + } + + let unreadCount = values.count(for: .peer(peerView.peerId)) + if let unreadCount = unreadCount, unreadCount > 0 { + unread[peerView.peerId] = CombinedRecentPeers.Unread(count: Int32(unreadCount), isMuted: isMuted) + } + + if let message = topMessages.messages[peerView.peerId] { + messages[peerView.peerId] = WidgetDataPeer.Message(message: message) + } + + peers.append(peer) + } + } + return .peers(peers: peers, unread: unread, messages: messages) + } + } + } + |> map { result -> WidgetData in + switch result { + case .disabled: + return WidgetData(accountId: account.id.int64, content: .disabled) + case let .peers(peers, unread, messages): + return WidgetData(accountId: account.id.int64, content: .peers(WidgetDataPeers(accountPeerId: account.peerId.toInt64(), peers: peers.compactMap { peer -> WidgetDataPeer? in + var name: String = "" + var lastName: String? + + if let user = peer as? TelegramUser { + if let firstName = user.firstName { + name = firstName + lastName = user.lastName + } else if let lastName = user.lastName { + name = lastName + } else if let phone = user.phone, !phone.isEmpty { + name = phone + } + } else { + name = peer.debugDisplayTitle + } + + var badge: WidgetDataPeer.Badge? + if let unreadValue = unread[peer.id], unreadValue.count > 0 { + badge = WidgetDataPeer.Badge( + count: Int(unreadValue.count), + isMuted: unreadValue.isMuted + ) + } + + let message = messages[peer.id] + + return WidgetDataPeer(id: peer.id.toInt64(), name: name, lastName: lastName, letters: peer.displayLetters, avatarPath: smallestImageRepresentation(peer.profileImageRepresentations).flatMap { representation in + return account.postbox.mediaBox.resourcePath(representation.resource) + }, badge: badge, message: message) + }))) + } + } + |> distinctUntilChanged + + return combineLatest(processedRecent, processedCustom) + |> map { processedRecent, _ -> WidgetData in + return processedRecent + } }).start(next: { widgetData in let path = basePath + "/widget-data" if let data = try? JSONEncoder().encode(widgetData) { diff --git a/submodules/WidgetItems/BUILD b/submodules/WidgetItems/BUILD index 0da6915f24..1a1cff6024 100644 --- a/submodules/WidgetItems/BUILD +++ b/submodules/WidgetItems/BUILD @@ -10,3 +10,14 @@ swift_library( "//visibility:public", ], ) + +swift_library( + name = "WidgetItems_iOS14", + module_name = "WidgetItems_iOS14", + srcs = glob([ + "Sources/**/*.swift", + ]), + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/WidgetItems/Sources/WidgetItems.swift b/submodules/WidgetItems/Sources/WidgetItems.swift index 703299b475..7b0bb61830 100644 --- a/submodules/WidgetItems/Sources/WidgetItems.swift +++ b/submodules/WidgetItems/Sources/WidgetItems.swift @@ -15,20 +15,223 @@ public struct WidgetDataPeer: Codable, Equatable { } } + public struct Message: Codable, Equatable { + public enum Content: Codable, Equatable { + public enum DecodingError: Error { + case generic + } + + public struct Image: Codable, Equatable { + public init() { + } + } + + public struct Video: Codable, Equatable { + public init() { + } + } + + public struct File: Codable, Equatable { + public var name: String + + public init(name: String) { + self.name = name + } + } + + public struct Gif: Codable, Equatable { + public init() { + } + } + + public struct Music: Codable, Equatable { + public var artist: String + public var title: String + public var duration: Int32 + + public init(artist: String, title: String, duration: Int32) { + self.artist = artist + self.title = title + self.duration = duration + } + } + + public struct VoiceMessage: Codable, Equatable { + public var duration: Int32 + + public init(duration: Int32) { + self.duration = duration + } + } + + public struct VideoMessage: Codable, Equatable { + public var duration: Int32 + + public init(duration: Int32) { + self.duration = duration + } + } + + public struct Sticker: Codable, Equatable { + public var altText: String + + public init(altText: String) { + self.altText = altText + } + } + + public struct Call: Codable, Equatable { + public var isVideo: Bool + + public init(isVideo: Bool) { + self.isVideo = isVideo + } + } + + public struct MapLocation: Codable, Equatable { + public init() { + } + } + + public struct Game: Codable, Equatable { + public var title: String + + public init(title: String) { + self.title = title + } + } + + public struct Poll: Codable, Equatable { + public var title: String + + public init(title: String) { + self.title = title + } + } + + enum CodingKeys: String, CodingKey { + case text + case image + case video + case file + case gif + case music + case voiceMessage + case videoMessage + case sticker + case call + case mapLocation + case game + case poll + } + + case text + case image(Image) + case video(Video) + case file(File) + case gif(Gif) + case music(Music) + case voiceMessage(VoiceMessage) + case videoMessage(VideoMessage) + case sticker(Sticker) + case call(Call) + case mapLocation(MapLocation) + case game(Game) + case poll(Poll) + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + if let _ = try? container.decode(String.self, forKey: .text) { + self = .text + } else if let image = try? container.decode(Image.self, forKey: .image) { + self = .image(image) + } else if let video = try? container.decode(Video.self, forKey: .video) { + self = .video(video) + } else if let gif = try? container.decode(Gif.self, forKey: .gif) { + self = .gif(gif) + } else if let file = try? container.decode(File.self, forKey: .file) { + self = .file(file) + } else if let music = try? container.decode(Music.self, forKey: .voiceMessage) { + self = .music(music) + } else if let voiceMessage = try? container.decode(VoiceMessage.self, forKey: .voiceMessage) { + self = .voiceMessage(voiceMessage) + } else if let videoMessage = try? container.decode(VideoMessage.self, forKey: .videoMessage) { + self = .videoMessage(videoMessage) + } else if let sticker = try? container.decode(Sticker.self, forKey: .sticker) { + self = .sticker(sticker) + } else if let call = try? container.decode(Call.self, forKey: .call) { + self = .call(call) + } else if let mapLocation = try? container.decode(MapLocation.self, forKey: .mapLocation) { + self = .mapLocation(mapLocation) + } else if let game = try? container.decode(Game.self, forKey: .game) { + self = .game(game) + } else if let poll = try? container.decode(Poll.self, forKey: .poll) { + self = .poll(poll) + } else { + throw DecodingError.generic + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + switch self { + case .text: + try container.encode("", forKey: .text) + case let .image(image): + try container.encode(image, forKey: .image) + case let .video(video): + try container.encode(video, forKey: .video) + case let .file(file): + try container.encode(file, forKey: .file) + case let .gif(gif): + try container.encode(gif, forKey: .gif) + case let .music(music): + try container.encode(music, forKey: .music) + case let .voiceMessage(voiceMessage): + try container.encode(voiceMessage, forKey: .voiceMessage) + case let .videoMessage(videoMessage): + try container.encode(videoMessage, forKey: .videoMessage) + case let .sticker(sticker): + try container.encode(sticker, forKey: .sticker) + case let .call(call): + try container.encode(call, forKey: .call) + case let .mapLocation(mapLocation): + try container.encode(mapLocation, forKey: .mapLocation) + case let .game(game): + try container.encode(game, forKey: .game) + case let .poll(poll): + try container.encode(poll, forKey: .poll) + } + } + } + + public var text: String + public var content: Content + public var timestamp: Int32 + + public init(text: String, content: Content, timestamp: Int32) { + self.text = text + self.content = content + self.timestamp = timestamp + } + } + public var id: Int64 public var name: String public var lastName: String? public var letters: [String] public var avatarPath: String? public var badge: Badge? + public var message: Message? - public init(id: Int64, name: String, lastName: String?, letters: [String], avatarPath: String?, badge: Badge?) { + public init(id: Int64, name: String, lastName: String?, letters: [String], avatarPath: String?, badge: Badge?, message: Message?) { self.id = id self.name = name self.lastName = lastName self.letters = letters self.avatarPath = avatarPath self.badge = badge + self.message = message } } diff --git a/submodules/WidgetItemsUtils/BUILD b/submodules/WidgetItemsUtils/BUILD new file mode 100644 index 0000000000..a17c9cfcbc --- /dev/null +++ b/submodules/WidgetItemsUtils/BUILD @@ -0,0 +1,19 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "WidgetItemsUtils", + module_name = "WidgetItemsUtils", + srcs = glob([ + "Sources/**/*.swift", + ]), + deps = [ + "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", + "//submodules/Postbox:Postbox", + "//submodules/SyncCore:SyncCore", + "//submodules/TelegramCore:TelegramCore", + "//submodules/WidgetItems:WidgetItems", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/WidgetItemsUtils/Sources/WidgetItemsUtils.swift b/submodules/WidgetItemsUtils/Sources/WidgetItemsUtils.swift new file mode 100644 index 0000000000..75c64372c0 --- /dev/null +++ b/submodules/WidgetItemsUtils/Sources/WidgetItemsUtils.swift @@ -0,0 +1,59 @@ +import Foundation + +import Postbox +import SyncCore +import TelegramCore +import WidgetItems + +public extension WidgetDataPeer.Message { + init(message: Message) { + var content: WidgetDataPeer.Message.Content = .text + for media in message.media { + switch media { + case _ as TelegramMediaImage: + content = .image(WidgetDataPeer.Message.Content.Image()) + case let file as TelegramMediaFile: + var fileName = "file" + for attribute in file.attributes { + if case let .FileName(value) = attribute { + fileName = value + break + } + } + content = .file(WidgetDataPeer.Message.Content.File(name: fileName)) + for attribute in file.attributes { + switch attribute { + case let .Sticker(altText, _, _): + content = .sticker(WidgetDataPeer.Message.Content.Sticker(altText: altText)) + case let .Video(duration, _, flags): + if flags.contains(.instantRoundVideo) { + content = .videoMessage(WidgetDataPeer.Message.Content.VideoMessage(duration: Int32(duration))) + } else { + content = .video(WidgetDataPeer.Message.Content.Video()) + } + case let .Audio(isVoice, duration, title, performer, _): + if isVoice { + content = .voiceMessage(WidgetDataPeer.Message.Content.VoiceMessage(duration: Int32(duration))) + } else { + content = .music(WidgetDataPeer.Message.Content.Music(artist: performer ?? "", title: title ?? "", duration: Int32(duration))) + } + default: + break + } + } + case let action as TelegramMediaAction: + switch action.action { + case let .phoneCall(_, _, _, isVideo): + content = .call(WidgetDataPeer.Message.Content.Call(isVideo: isVideo)) + default: + break + } + case _ as TelegramMediaMap: + content = .mapLocation(WidgetDataPeer.Message.Content.MapLocation()) + default: + break + } + } + self.init(text: message.text, content: content, timestamp: message.timestamp) + } +} diff --git a/third-party/libvpx/BUILD b/third-party/libvpx/BUILD index b1c435b166..8ee1bef895 100644 --- a/third-party/libvpx/BUILD +++ b/third-party/libvpx/BUILD @@ -55,6 +55,12 @@ genrule( rm -rf "$$BUILD_DIR" mkdir -p "$$BUILD_DIR" + YASM_DIR="$$BUILD_DIR/yasm" + rm -rf "$$YASM_DIR" + mkdir -p "$$YASM_DIR" + tar -xf "$(location //third-party/yasm:yasm.tar)" -C "$$YASM_DIR" + ABS_YASM_DIR="$$(pwd)/$$(dirname $$YASM_DIR)/$$(basename $$YASM_DIR)" + cp $(location :build-libvpx-bazel.sh) "$$BUILD_DIR/" cp $(location :0001-Add-support-for-arm64-iphonesimulator-gcc.patch) "$$BUILD_DIR/" @@ -81,6 +87,9 @@ genrule( outs = ["Public/vpx/" + x for x in headers] + ["Public/vpx/vpx_config.h"] + ["Public/vpx/lib{}.a".format(x) for x in libs], + tools = [ + "//third-party/yasm:yasm.tar", + ], visibility = [ "//visibility:public", ] diff --git a/third-party/mozjpeg/mozjpeg/src/jconfig.h b/third-party/mozjpeg/mozjpeg/src/jconfig.h new file mode 100644 index 0000000000..beccc163e6 --- /dev/null +++ b/third-party/mozjpeg/mozjpeg/src/jconfig.h @@ -0,0 +1,73 @@ +/* Version ID for the JPEG library. + * Might be useful for tests like "#if JPEG_LIB_VERSION >= 60". + */ +#define JPEG_LIB_VERSION 80 + +/* libjpeg-turbo version */ +#define LIBJPEG_TURBO_VERSION 4.0.0 + +/* libjpeg-turbo version in integer form */ +#define LIBJPEG_TURBO_VERSION_NUMBER 4000000 + +/* Support arithmetic encoding */ +#define C_ARITH_CODING_SUPPORTED 1 + +/* Support arithmetic decoding */ +#define D_ARITH_CODING_SUPPORTED 1 + +/* Support in-memory source/destination managers */ +/* #undef MEM_SRCDST_SUPPORTED */ + +/* Use accelerated SIMD routines. */ +#define WITH_SIMD 1 + +/* + * Define BITS_IN_JSAMPLE as either + * 8 for 8-bit sample values (the usual setting) + * 12 for 12-bit sample values + * Only 8 and 12 are legal data precisions for lossy JPEG according to the + * JPEG standard, and the IJG code does not support anything else! + * We do not support run-time selection of data precision, sorry. + */ + +#define BITS_IN_JSAMPLE 8 /* use 8 or 12 */ + +/* Define to 1 if you have the header file. */ +#define HAVE_LOCALE_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDDEF_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define if you need to include to get size_t. */ +#define NEED_SYS_TYPES_H 1 + +/* Define if you have BSD-like bzero and bcopy in rather than + memset/memcpy in . */ +/* #undef NEED_BSD_STRINGS */ + +/* Define to 1 if the system has the type `unsigned char'. */ +#define HAVE_UNSIGNED_CHAR 1 + +/* Define to 1 if the system has the type `unsigned short'. */ +#define HAVE_UNSIGNED_SHORT 1 + +/* Compiler does not support pointers to undefined structures. */ +/* #undef INCOMPLETE_TYPES_BROKEN */ + +/* Define if your (broken) compiler shifts signed values as if they were + unsigned. */ +/* #undef RIGHT_SHIFT_IS_UNSIGNED */ + +/* Define to 1 if type `char' is unsigned and you are not using gcc. */ +#ifndef __CHAR_UNSIGNED__ +/* #undef __CHAR_UNSIGNED__ */ +#endif + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `unsigned int' if does not define. */ +/* #undef size_t */ diff --git a/third-party/mozjpeg/mozjpeg/src/jconfigint.h b/third-party/mozjpeg/mozjpeg/src/jconfigint.h new file mode 100644 index 0000000000..4d0059156a --- /dev/null +++ b/third-party/mozjpeg/mozjpeg/src/jconfigint.h @@ -0,0 +1,31 @@ +/* libjpeg-turbo build number */ +#define BUILD "20201129" + +/* Compiler's inline keyword */ +#undef inline + +/* How to obtain function inlining. */ +#define INLINE __inline__ __attribute__((always_inline)) + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "mozjpeg" + +/* Version number of package */ +#define VERSION "4.0.0" + +/* The size of `size_t', as computed by sizeof. */ +#define SIZEOF_SIZE_T 8 + +/* Define if your compiler has __builtin_ctzl() and sizeof(unsigned long) == sizeof(size_t). */ +#define HAVE_BUILTIN_CTZL + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_INTRIN_H */ + +#if defined(_MSC_VER) && defined(HAVE_INTRIN_H) +#if (SIZEOF_SIZE_T == 8) +#define HAVE_BITSCANFORWARD64 +#elif (SIZEOF_SIZE_T == 4) +#define HAVE_BITSCANFORWARD +#endif +#endif diff --git a/versions.json b/versions.json new file mode 100644 index 0000000000..a99715704d --- /dev/null +++ b/versions.json @@ -0,0 +1,5 @@ +{ + "app": "7.3.1", + "bazel": "3.7.0", + "xcode": "12.3" +}