From b7562a5152a9fe9c826e257e3987776adec65e44 Mon Sep 17 00:00:00 2001 From: Vlad Shcherban Date: Thu, 27 Feb 2020 18:45:56 +0800 Subject: [PATCH 001/101] Offline Builds initial draft --- .gitignore | 3 +- platform/android/app/build.gradle.kts | 384 +++++--------- platform/android/build.gradle.kts | 1 - platform/iphone/mks3upload | 11 +- platform/mac/AndroidAppBuildController.mm | 29 +- platform/mac/AppBuildController.mm | 7 +- platform/mac/AppDelegate.h | 2 + platform/mac/AppDelegate.mm | 32 +- .../CoronaBuilder.xcodeproj/project.pbxproj | 8 + .../xcschemes/CoronaBuilder.xcscheme | 22 +- platform/mac/LinuxAppBuildController.mm | 2 +- platform/mac/OSXAppBuildController.mm | 2 +- platform/mac/Rtt_IOSAppPackager.mm | 150 +++++- platform/mac/Rtt_MacAuthorizationDelegate.mm | 9 +- platform/mac/Rtt_MacPlatformServices.mm | 1 + platform/mac/Rtt_MacSimulator.mm | 2 +- platform/mac/Rtt_TVOSAppPackager.mm | 150 +++++- platform/mac/WebAppBuildController.mm | 2 +- .../mac/ratatouille.xcodeproj/project.pbxproj | 16 + .../CoronaBuilderPluginCollector.lua | 328 ++++++++++++ .../resources/CoronaOfflineiOSPackager.lua | 156 ++++++ platform/resources/iPhonePackageApp.lua | 2 +- platform/resources/iostemplate/README.md | 5 + platform/resources/tvosPackageApp.lua | 3 +- platform/shared/Rtt_Authorization.cpp | 7 +- platform/shared/Rtt_Authorization.h | 1 + platform/shared/Rtt_WebServicesSession.cpp | 6 + platform/shared/Rtt_WebServicesSession.h | 2 + platform/tvos/mks3upload | 14 +- .../CoronaBuilder/BuilderPluginDownloader.lua | 142 ++--- tools/CoronaBuilder/Rtt_CoronaBuilder.cpp | 38 +- tools/CoronaBuilder/Rtt_CoronaBuilder.h | 1 + .../CoronaBuilder/Rtt_DownloadPluginsMain.cpp | 33 +- tools/buildsys-ios/libtemplate/Builder.lua | 488 ++++++++++++++++++ .../buildsys-ios/libtemplate/BuilderUtils.lua | 124 +++++ tools/buildsys-ios/libtemplate/Defaults.lua | 299 +++++++++++ tools/buildsys-ios/libtemplate/build.lua | 17 + .../buildsys-ios/libtemplate/build_output.sh | 164 ++++++ 38 files changed, 2262 insertions(+), 401 deletions(-) create mode 100644 platform/resources/CoronaBuilderPluginCollector.lua create mode 100644 platform/resources/CoronaOfflineiOSPackager.lua create mode 100644 platform/resources/iostemplate/README.md create mode 100644 tools/buildsys-ios/libtemplate/Builder.lua create mode 100644 tools/buildsys-ios/libtemplate/BuilderUtils.lua create mode 100644 tools/buildsys-ios/libtemplate/Defaults.lua create mode 100644 tools/buildsys-ios/libtemplate/build.lua create mode 100755 tools/buildsys-ios/libtemplate/build_output.sh diff --git a/.gitignore b/.gitignore index c16db4575..d70e98ecb 100644 --- a/.gitignore +++ b/.gitignore @@ -267,5 +267,6 @@ external/facebook-ios-sdk external/facebook-android-sdk plugins/ads-inmobi plugins/ads-inneractive -tools/buildsys-ios commit-email.sh +platform/resources/iostemplate +platform/tvos/license.ccdata diff --git a/platform/android/app/build.gradle.kts b/platform/android/app/build.gradle.kts index 4f434b165..16813c132 100644 --- a/platform/android/app/build.gradle.kts +++ b/platform/android/app/build.gradle.kts @@ -3,7 +3,7 @@ import com.beust.klaxon.JsonArray import com.beust.klaxon.JsonObject import com.beust.klaxon.Klaxon import com.beust.klaxon.Parser -import de.undercouch.gradle.tasks.download.DownloadAction +import com.beust.klaxon.lookup import org.apache.commons.io.output.ByteArrayOutputStream import org.apache.tools.ant.filters.StringInputStream @@ -24,6 +24,7 @@ val coronaKeyAlias: String? by project val coronaKeyAliasPassword: String? by project val configureCoronaPlugins: String? by project val coronaBuild: String? by project +val (dailyBuildYear, dailyBuildRevision) = coronaBuild?.split('.') ?: listOf(null, null) val coronaBuildData: String? by project val coronaExpansionFileName: String? by project val coronaCustomHome: String? by project @@ -43,12 +44,14 @@ val nativeDir = if (windows) { val resourceDir = coronaResourcesDir?.let { file("$it/../Native/").absolutePath }?.takeIf { file(it).exists() } (resourceDir ?: System.getenv("CORONA_ROOT")).replace("\\", "/") } else { - val resourceDir = coronaResourcesDir?.let { file("$it/../../../Native//").absolutePath }?.takeIf { file(it).exists() } + val resourceDir = coronaResourcesDir?.let { file("$it/../../../Native/").absolutePath }?.takeIf { file(it).exists() } resourceDir ?: "${System.getenv("HOME")}/Library/Application Support/Corona/Native/" } val windowsPathHelper = "${System.getenv("PATH")}${File.pathSeparator}${System.getenv("CORONA_PATH")}" val coronaPlugins = file("$buildDir/corona-plugins") +val luaCmd = "$nativeDir/Corona/$shortOsName/bin/lua" +val isSimulatorBuild = coronaTmpDir != null fun checkCoronaNativeInstallation() { if (file("$nativeDir/Corona/android/resource/android-template.zip").exists()) @@ -78,67 +81,67 @@ val buildToolsDir = "$projectDir/buildTools".takeIf { file(it).exists() } val generatedPluginsOutput = "$buildDir/generated/corona_plugins" val generatedPluginAssetsDir = "$generatedPluginsOutput/assets" val generatedPluginNativeLibsDir = "$generatedPluginsOutput/native" -val generatedControlPath = "$generatedPluginsOutput/control" val generatedBuildIdPath = "$generatedPluginsOutput/build" val generatedPluginMegaJar = "$generatedPluginsOutput/plugins.jar" val generatedMainIconsAndBannersDir = "$buildDir/generated/corona_icons" - -var buildSettings: JsonObject? = null -var fakeBuildData: String? = null -coronaTmpDir?.let { srcDir -> - file("$srcDir/build.properties").takeIf { it.exists() }?.let { f -> - buildSettings = Parser.default().parse(f.absolutePath) as? JsonObject - } -} -parseBuildSettingsFile() -val coronaMinSdkVersion = try { - buildSettings?.obj("buildSettings")?.obj("android")?.let { - try { - return@let it.string("minSdkVersion")?.toIntOrNull() - } catch (ignore: Throwable) { +val parsedBuildProperties: JsonObject = run { + coronaTmpDir?.let { srcDir -> + file("$srcDir/build.properties").takeIf { it.exists() }?.let { f -> + return@run Parser.default().parse(f.absolutePath) as JsonObject } - try { - return@let it.int("minSdkVersion") - } catch (ignore: Throwable) { - } - null } -} catch (ignore: Throwable) { - null -} ?: 15 -val coronaVersionName = try { - buildSettings?.obj("buildSettings")?.obj("android")?.let { - try { - return@let it.string("versionName") - } catch (ignore: Throwable) { - } - try { - return@let it.int("versionName")?.toString() - } catch (ignore: Throwable) { - } - null + val buildSettingsFile = file("$coronaSrcDir/build.settings") + if (!buildSettingsFile.exists()) { + return@run JsonObject(mapOf("buildSettings" to JsonObject(), "packageName" to coronaAppPackage, "targetedAppStore" to coronaTargetStore)) } -} catch (ignore: Throwable) { - null -} ?: project.findProperty("coronaVersionName") as? String ?: "1.0" -val coronaVersionCode: Int = try { - buildSettings?.obj("buildSettings")?.obj("android")?.let { - try { - return@let it.string("versionCode")?.toIntOrNull() - } catch (ignore: Throwable) { - } - try { - return@let it.int("versionCode") - } catch (ignore: Throwable) { - } - null + val output = ByteArrayOutputStream() + val execResult = exec { + setWorkingDir("$nativeDir/Corona/$shortOsName/bin") + commandLine(luaCmd, + "-e", + "package.path='$nativeDir/Corona/shared/resource/?.lua;'..package.path", + "-e", + """ + dofile('${buildSettingsFile.path.replace("\\", "\\\\")}') + if type(settings) == 'table' then + print(require('json').encode(settings)) + else + print('{}') + end + """.trimIndent() + ) + errorOutput = output + standardOutput = output + isIgnoreExitValue = true + if (windows) environment["PATH"] = windowsPathHelper } -} catch (ignore: Throwable) { - null -} ?: (project.findProperty("coronaVersionCode") as? String)?.toIntOrNull() ?: 1 + if (execResult.exitValue != 0) { + throw InvalidUserDataException("Build.settings file could not be parsed: ${output.toString().replace(luaCmd, "")}") + } + val parsedBuildSettingsFile = Parser.default().parse(StringBuilder(output.toString())) as? JsonObject + return@run JsonObject(mapOf("buildSettings" to parsedBuildSettingsFile, "packageName" to coronaAppPackage, "targetedAppStore" to coronaTargetStore)) +} + +val coronaMinSdkVersion = parsedBuildProperties.lookup("buildSettings.android.minSdkVersion").firstOrNull()?.toString()?.toIntOrNull() + ?: 15 + +val coronaBuilder = if (windows) { + "$nativeDir/Corona/win/bin/CoronaBuilder.exe" +} else { + "$nativeDir/Corona/$shortOsName/bin/CoronaBuilder.app/Contents/MacOS/CoronaBuilder" +} + + +val coronaVersionName = + parsedBuildProperties.lookup("buildSettings.android.versionName").firstOrNull()?.toString() + ?: project.findProperty("coronaVersionName") as? String ?: "1.0" + +val coronaVersionCode: Int = + parsedBuildProperties.lookup("buildSettings.android.versionCode").firstOrNull()?.toString()?.toIntOrNull() + ?: (project.findProperty("coronaVersionCode") as? String)?.toIntOrNull() ?: 1 val androidDestPluginPlatform = if (coronaTargetStore.equals("amazon", ignoreCase = true)) { "android-kindle" @@ -155,8 +158,6 @@ val coronaAndroidPluginsCache = file(if (windows) { } else { "${System.getenv("HOME")}/Library/Application Support/Corona/build cache/$androidDestPluginPlatform" }) -val eTagFileName = "${coronaAndroidPluginsCache.parent}/CoronaETags.txt" - val pluginDisabledMetadata = mutableSetOf() val pluginDisabledDependencies = mutableSetOf() @@ -314,18 +315,13 @@ fun coronaAssetsCopySpec(spec: CopySpec) { file("$coronaTmpDir/excludesfile.properties").takeIf { it.exists() }?.readLines()?.forEach { exclude(it) } - if (coronaTmpDir == null) { - parseBuildSettingsFile() - try { - buildSettings?.obj("buildSettings")?.obj("excludeFiles")?.let { - it.array("all")?.forEach { excludeEntry -> - exclude("**/$excludeEntry") - } - it.array("android")?.forEach { excludeEntry -> - exclude("**/$excludeEntry") - } - } - } catch (ignore: Throwable) { + if (!isSimulatorBuild) { + // use build.settings properties only if this is not simulator build + parsedBuildProperties.lookup>("buildSettings.excludeFiles.all").firstOrNull()?.forEach { + exclude("**/$it") + } + parsedBuildProperties.lookup>("buildSettings.excludeFiles.android").firstOrNull()?.forEach { + exclude("**/$it") } } exclude("**/Icon\r") @@ -347,11 +343,6 @@ android.applicationVariants.all { val compileLuaTask = tasks.create("compileLua${baseName.capitalize()}") { description = "If required, compiles Lua and archives it into resource.car" val luac = "$nativeDir/Corona/$shortOsName/bin/luac" - val coronaBuilder = if (windows) { - "$nativeDir/Corona/win/bin/CoronaBuilder.exe" - } else { - "$nativeDir/Corona/$shortOsName/bin/CoronaBuilder.app/Contents/MacOS/CoronaBuilder" - } val srcLuaFiles = fileTree(coronaSrcDir) { include("**/*.lua") @@ -368,10 +359,10 @@ android.applicationVariants.all { val compiledDir = "$buildDir/intermediates/compiled_lua/$baseName" delete(compiledDir) mkdir(compiledDir) - val luaFiles = if (coronaTmpDir == null) { - srcLuaFiles + pluginLuaFiles - } else { + val luaFiles = if (isSimulatorBuild) { pluginLuaFiles + } else { + srcLuaFiles + pluginLuaFiles } val outputsList = luaFiles.map { val compiled = when { @@ -397,7 +388,7 @@ android.applicationVariants.all { commandLine(luac, *additionalArguments.toTypedArray(), "-o", "$compiledDir/$compiled", "--", it) } compiled - } + if (coronaTmpDir != null) { + } + if (isSimulatorBuild) { fileTree(coronaTmpDir!!) { include("*.lu") } @@ -407,7 +398,7 @@ android.applicationVariants.all { val buildId = file(generatedBuildIdPath) .takeIf { it.exists() }?.let { file(generatedBuildIdPath).readText().trim() - } ?: "unknown" + } ?: coronaBuild ?: "unknown" val metadataLuaStr = """ if not application or type( application ) ~= "table" then application = {} @@ -481,74 +472,8 @@ android.applicationVariants.all { android.sourceSets[name].assets.srcDirs(generatedAssetsDir) } - -fun readETagMap(): Map { - return file(eTagFileName).takeIf { it.exists() }?.readLines()?.map { s -> - val (k, v) = s.split("\t") - k to v - }?.toMap() ?: mapOf() -} - -fun downloadPluginsBasedOnBuilderOutput(builderOutput: ByteArrayOutputStream, eTagMap: Map, newETagMap: MutableMap): Int { - coronaAndroidPluginsCache.mkdirs() - mkdir(generatedPluginsOutput) - val builderOutputStr = builderOutput.toString() - builderOutputStr.lines() - .filter { it.startsWith("SPLASH\t") } - .map { it.removePrefix("SPLASH\t").trim() } - .lastOrNull()?.let { - file(generatedControlPath).writeText(it) - } - builderOutputStr.lines() - .filter { it.startsWith("BUILD\t") } - .map { it.removePrefix("BUILD\t").trim() } - .lastOrNull()?.let { - file(generatedBuildIdPath).writeText(it) - } - val pluginUrls = builderOutputStr - .lines() - .filter { it.startsWith("plugin\t") } - .map { it.trim().removePrefix("plugin\t").split("\t") } - pluginUrls.forEach { (plugin, url) -> - val eTagKey = "$androidDestPluginPlatform/$plugin" - val existingTag = eTagMap[eTagKey] - try { - val outputFile = with(DownloadAction(project)) { - src(url) - dest("$coronaAndroidPluginsCache/$plugin") - val outputFile = outputFiles.first() - if (existingTag != null && outputFile.exists()) { - header("If-None-Match", existingTag) - } - responseInterceptor { response, _ -> - val eTag = response.getFirstHeader("ETag") - if (eTag != null) { - newETagMap[eTagKey] = eTag.value - } - } - execute() - outputFile - } - copy { - from(tarTree(outputFile)) - into("$coronaPlugins/${outputFile.nameWithoutExtension}") - } - } catch (ex: Exception) { - if (ex.message?.equals("Not Found", ignoreCase = true) == true) { - logger.error("WARNING: plugin '${plugin.removeSuffix(".tgz")}' was not found for current platform. Consider disabling it with 'supportedPlatforms' field.") - } else { - logger.error("ERROR: There was a problem downloading plugin '${plugin.removeSuffix(".tgz")}'. Please, try again.") - throw ex - } - } - } - return pluginUrls.count() -} - fun downloadAndProcessCoronaPlugins(reDownloadPlugins: Boolean = true) { - parseBuildSettingsFile() - val luaVerbosityPlug = if (!logger.isLifecycleEnabled) { arrayOf("-e", "printError=print;print=function()end") } else { @@ -560,76 +485,40 @@ fun downloadAndProcessCoronaPlugins(reDownloadPlugins: Boolean = true) { if (reDownloadPlugins) { delete(coronaPlugins) - - val coronaBuilder = if (windows) { - "$nativeDir/Corona/win/bin/CoronaBuilder.exe" - } else { - "$nativeDir/Corona/$shortOsName/bin/CoronaBuilder.app/Contents/MacOS/CoronaBuilder" + file(coronaPlugins).mkdirs() + + val buildDataStr = coronaBuildData?.let { file(it).readText() } ?: run { + val fakeBuildData = (parsedBuildProperties.obj("buildSettings") ?: JsonObject()) + fakeBuildData["targetAppStore"] = coronaTargetStore + fakeBuildData["dailyBuildYear"] = dailyBuildYear + fakeBuildData["dailyBuildRevision"] = dailyBuildRevision + fakeBuildData["appName"] = coronaAppFileName ?: "Corona App" + fakeBuildData.toJsonString() } - val eTagMap = readETagMap() - val newETagMap = mutableMapOf() - - run { - val buildPropsFile = file("$coronaTmpDir/build.properties") - val inputSettingsFile = if (buildPropsFile.exists()) { - buildPropsFile - } else { - file("$coronaSrcDir/build.settings") - } - - val builderOutput = ByteArrayOutputStream() - val execResult = exec { - val buildData = coronaBuildData?.let { file(it).readText() } ?: fakeBuildData - ?: throw InvalidModelException("Unable to retrieve build.data: '$coronaBuildData', '${file(coronaBuildData - ?: "null")}'") - commandLine(coronaBuilder, "plugins", "download", androidDestPluginPlatform, "--build", "$coronaBuild", inputSettingsFile, "--android-build", "--build-data") - if (windows) environment["PATH"] = windowsPathHelper - standardInput = StringInputStream(buildData) - standardOutput = builderOutput - isIgnoreExitValue = true - } - if (execResult.exitValue != 0) { - logger.error("Error while fetching plugins") - logger.error(builderOutput.toString()) - execResult.rethrowFailure() - throw InvalidPluginException("Error while fetching plugins") - } - logger.lifecycle("Downloading plugins") - delete(generatedControlPath) - downloadPluginsBasedOnBuilderOutput(builderOutput, eTagMap, newETagMap) + val buildParams = JsonObject(mapOf( + "appName" to coronaAppFileName, + "appPackage" to coronaAppPackage, + "build" to dailyBuildRevision, + "buildData" to buildDataStr, + "modernPlatform" to "android", + "platform" to "android", + "pluginPlatform" to androidDestPluginPlatform, + "destinationDirectory" to coronaPlugins.absolutePath + )).toJsonString() + + val builderOutput = ByteArrayOutputStream() + val execResult = exec { + commandLine(coronaBuilder, "plugins", "download", "--android-offline-plugins") + if (windows) environment["PATH"] = windowsPathHelper + standardInput = StringInputStream(buildParams) + standardOutput = builderOutput + isIgnoreExitValue = true } - - logger.lifecycle("Fetching plugin dependencies") - var didDownloadSomething: Boolean - do { - processPluginGradleScripts() - val pluginDirectoriesSet = file(coronaPlugins) - .walk() - .maxDepth(1) - .filter { it.isDirectory && it != coronaPlugins } - .map { it.relativeTo(coronaPlugins).path } - .toSet() - val pluginDirectories = (pluginDirectoriesSet - pluginDisabledDependencies).toTypedArray() - val builderOutput = ByteArrayOutputStream() - val execResult = exec { - commandLine(coronaBuilder, "plugins", "download", androidDestPluginPlatform, "--build", "$coronaBuild", "--fetch-dependencies", coronaPlugins, *pluginDirectories) - if (windows) environment["PATH"] = windowsPathHelper - standardOutput = builderOutput - isIgnoreExitValue = true - } - if (execResult.exitValue != 0) { - logger.error("Error while fetching plugin dependencies") - logger.error(builderOutput.toString()) - execResult.rethrowFailure() - throw InvalidPluginException("Error while fetching plugin dependencies") - } - didDownloadSomething = downloadPluginsBasedOnBuilderOutput(builderOutput, eTagMap, newETagMap) > 0 - } while (didDownloadSomething) - - if (newETagMap.count() > 0) { - val combinedETags = readETagMap() + newETagMap - file(eTagFileName).writeText(combinedETags.map { it.key + "\t" + it.value }.joinToString("\n")) + if (execResult.exitValue != 0) { + logger.error("Error while fetching plugins: $builderOutput") + throw InvalidPluginException("Error while fetching plugins: $builderOutput") } + logger.lifecycle("Downloading plugins") } processPluginGradleScripts() @@ -708,7 +597,7 @@ fun downloadAndProcessCoronaPlugins(reDownloadPlugins: Boolean = true) { }.map { it.absolutePath } exec { if (windows) environment["PATH"] = windowsPathHelper - commandLine("$nativeDir/Corona/$shortOsName/bin/lua" + commandLine(luaCmd , "-e" , "package.path='$nativeDir/Corona/shared/resource/?.lua;'..package.path" , *luaVerbosityPlug @@ -727,7 +616,7 @@ fun downloadAndProcessCoronaPlugins(reDownloadPlugins: Boolean = true) { buildPropsFile } else { val buildPropsOut = file("$buildDir/intermediates/corona.build.props") - buildPropsOut.writeText(buildSettings!!.toJsonString()) + buildPropsOut.writeText(parsedBuildProperties.toJsonString()) buildPropsOut } @@ -735,7 +624,7 @@ fun downloadAndProcessCoronaPlugins(reDownloadPlugins: Boolean = true) { file(manifestGenDir).mkdirs() exec { if (windows) environment["PATH"] = windowsPathHelper - commandLine("$nativeDir/Corona/$shortOsName/bin/lua" + commandLine(luaCmd , "-e" , "package.path='$nativeDir/Corona/shared/resource/?.lua;'..package.path" , *luaVerbosityPlug @@ -832,46 +721,6 @@ tasks.register("processPluginsNoDownload") { } } -tasks.register("parseBuildSettings") { - doLast { - parseBuildSettingsFile() - } -} - - -fun parseBuildSettingsFile() { - if (buildSettings != null) { - return - } - val buildSettingsFile = file("$coronaSrcDir/build.settings") - if (!buildSettingsFile.exists()) { - return - } - - val output = ByteArrayOutputStream() - exec { - setWorkingDir("$nativeDir/Corona/$shortOsName/bin") - commandLine("$nativeDir/Corona/$shortOsName/bin/lua", - "-e", - "package.path='$nativeDir/Corona/shared/resource/?.lua;'..package.path", - "-e", - """ - pcall( function() dofile('${buildSettingsFile.path.replace("\\", "\\\\")}') end ) - if type(settings) == 'table' then - print(require('json').encode(settings)) - else - print('{}') - end - """.trimIndent() - ) - standardOutput = output - if (windows) environment["PATH"] = windowsPathHelper - } - fakeBuildData = output.toString() - val parsedBuildSettingsFile = Parser.default().parse(StringBuilder(fakeBuildData)) as? JsonObject - buildSettings = JsonObject(mapOf("buildSettings" to parsedBuildSettingsFile, "packageName" to coronaAppPackage, "targetedAppStore" to coronaTargetStore)) -} - // @@ -940,7 +789,7 @@ tasks.register("exportToNativeAppTemplate") { }) } doLast { - println("Copied to ${file(templateDir).absolutePath}") + logger.lifecycle("Copied to ${file(templateDir).absolutePath}") } } @@ -1149,16 +998,25 @@ tasks.create("copySplashScreen") { copyCoronaIconFiles.dependsOn(this) dependsOn(cleanupIconsDir) - val control = file(generatedControlPath).takeIf { it.exists() }?.readText()?.trim() - if (control == null) { - from(projectDir) { - include("_corona_splash_screen.png") - } - } else if (control != "nil") { - from(coronaSrcDir) { - include(control) + val enableSplash: Boolean = parsedBuildProperties.lookup("buildSettings.splashScreen.android.enable").firstOrNull() + ?: parsedBuildProperties.lookup("buildSettings.splashScreen.enable").firstOrNull() + ?: true + val image: String? = parsedBuildProperties.lookup("buildSettings.splashScreen.android.image").firstOrNull() + ?: parsedBuildProperties.lookup("buildSettings.splashScreen.image").firstOrNull() + + logger.info("Configured Splash Screen enable: $enableSplash, image: $image.") + if (enableSplash) { + if (image != null) { + from(coronaSrcDir) { + include(image) + } + if (inputs.sourceFiles.isEmpty) throw InvalidUserDataException("Custom Splash Screen file '$image' not found!") + } else { + from(projectDir) { + include("_corona_splash_screen.png") + } + if (inputs.sourceFiles.isEmpty) throw InvalidUserDataException("Splash screen was not disabled in build.settings but '$projectDir/_corona_splash_screen.png' was not found!") } - if (inputs.sourceFiles.isEmpty) throw InvalidUserDataException("Custom Splash Screen file '$control' not found!") } into("$generatedMainIconsAndBannersDir/drawable") diff --git a/platform/android/build.gradle.kts b/platform/android/build.gradle.kts index 7bd05c59e..0dc1c0103 100644 --- a/platform/android/build.gradle.kts +++ b/platform/android/build.gradle.kts @@ -5,7 +5,6 @@ buildscript { } dependencies { classpath("com.android.tools.build:gradle:3.5.3") - classpath("de.undercouch:gradle-download-task:3.4.3") classpath("com.beust:klaxon:5.0.1") } } diff --git a/platform/iphone/mks3upload b/platform/iphone/mks3upload index d8b44007c..5b91d7110 100755 --- a/platform/iphone/mks3upload +++ b/platform/iphone/mks3upload @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/bash -e # # mks3upload # @@ -85,3 +85,12 @@ if [ -d $AS2BACKUP ] then mv $AS2BACKUP $ASSETS2 fi + +for IOS_VER in template/iphone/* +do + IOS_VER=$(basename "${IOS_VER}") + + cp "2100.9999_template_iphone_${IOS_VER}_basic.tar.bz" "../resources/iostemplate/iphoneos_${IOS_VER}.tar.bz" + cp "2100.9999_template_iphone-sim_${IOS_VER}_basic.tar.bz" "../resources/iostemplate/iphonesimulator_${IOS_VER}.tar.bz" + +done diff --git a/platform/mac/AndroidAppBuildController.mm b/platform/mac/AndroidAppBuildController.mm index 80e4b8d5b..e0aebc5b7 100644 --- a/platform/mac/AndroidAppBuildController.mm +++ b/platform/mac/AndroidAppBuildController.mm @@ -120,21 +120,26 @@ - (void) showWindow:(id)sender; { // Generate a default package id based on the user's email address + the app name const Rtt::AuthorizationTicket *ticket = [appDelegate ticket]; - NSString *username = [NSString stringWithExternalString:ticket->GetUsername()]; - NSArray *nameComponents = [username componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]]; + NSString *tmpPackageName; + if(ticket) { + NSString *username = [NSString stringWithExternalString:ticket->GetUsername()]; + NSArray *nameComponents = [username componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]]; - NSMutableString *tmpPackageName = [[[NSMutableString alloc] init] autorelease]; + tmpPackageName = [[[NSMutableString alloc] init] autorelease]; - // Reverse the order of the components of the email address and concatenate them together - // separated by periods for something like: com.coronalabs.perry.MyNewApp - for (id component in [nameComponents reverseObjectEnumerator]) - { - [tmpPackageName appendString:component]; - [tmpPackageName appendString:@"."]; - } + // Reverse the order of the components of the email address and concatenate them together + // separated by periods for something like: com.coronalabs.perry.MyNewApp + for (id component in [nameComponents reverseObjectEnumerator]) + { + [tmpPackageName appendString:component]; + [tmpPackageName appendString:@"."]; + } - // Add the appname having replaced any non-alphanumerics with underscores - [tmpPackageName appendString:[[self.appName componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]] componentsJoinedByString:@"_"]]; + // Add the appname having replaced any non-alphanumerics with underscores + [tmpPackageName appendString:[[self.appName componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]] componentsJoinedByString:@"_"]]; + } else { + tmpPackageName = [@"com.coronalabs." stringByAppendingString:[[self.appName componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]] componentsJoinedByString:@"_"]]; + } self.androidAppPackage = tmpPackageName; } diff --git a/platform/mac/AppBuildController.mm b/platform/mac/AppBuildController.mm index cd13dead6..90271d921 100644 --- a/platform/mac/AppBuildController.mm +++ b/platform/mac/AppBuildController.mm @@ -1115,9 +1115,12 @@ - (void) restoreBuildPreferences - (BOOL) loginSession:(WebServicesSession *)session services:(MacPlatformServices *)services ticket:(const AuthorizationTicket *)ticket message:(NSString **)message { - Rtt_ASSERT(session); + Rtt_ASSERT(session); Rtt_ASSERT(services); - Rtt_ASSERT(ticket); + + if(!ticket && services) { + return session && session->IsOfflineSession(); + } const char* usr = ticket->GetUsername(); Rtt::String encryptedPassword; diff --git a/platform/mac/AppDelegate.h b/platform/mac/AppDelegate.h index 7dfd7fe56..dafab9d68 100644 --- a/platform/mac/AppDelegate.h +++ b/platform/mac/AppDelegate.h @@ -178,6 +178,8 @@ namespace Rtt @property (nonatomic, readwrite) BOOL respondsToBackKey; @property (nonatomic, readonly, getter=analytics) Rtt::SimulatorAnalytics *fAnalytics; ++(BOOL)offlineModeAllowed; + -(BOOL)isRunning; -(IBAction)showHelp:(id)sender; -(void)signin; diff --git a/platform/mac/AppDelegate.mm b/platform/mac/AppDelegate.mm index f502103bd..3b4026b6d 100755 --- a/platform/mac/AppDelegate.mm +++ b/platform/mac/AppDelegate.mm @@ -595,6 +595,11 @@ @implementation AppDelegate @synthesize _currentLocation; @synthesize fAnalytics; ++(BOOL)offlineModeAllowed { + static BOOL allowed = [[NSUserDefaults standardUserDefaults] boolForKey:@"allowOfflineMode"]; + return allowed; +} + -(id)init { self = [super init]; @@ -855,16 +860,25 @@ -(void)signin if ( ! authorizer.Initialize(true) ) { - NSRunAlertPanel( @"Could not authorize this computer to use Corona Simulator", @"An Internet connection is required to authorize first time use.", nil, nil, nil ); - - [[NSApplication sharedApplication] terminate:self]; + if(![AppDelegate offlineModeAllowed]) + { - return; + NSRunAlertPanel( @"Could not authorize this computer to use Corona Simulator", @"An Internet connection is required to authorize first time use.", nil, nil, nil ); + + [[NSApplication sharedApplication] terminate:self]; + + return; + } } ticket = authorizer.GetTicket(); + + if(!ticket && [AppDelegate offlineModeAllowed]) { + authorizedToLaunch = YES; + return; + } - if ( ! authorizer.VerifyTicket() ) + if ( ticket && ! authorizer.VerifyTicket() ) { NSRunAlertPanel( @"Could not launch Corona Simulator", @"Invalid registration.", nil, nil, nil ); @@ -1587,6 +1601,7 @@ -(NSArray*)GetRecentDocuments // I believe this is generally safe enough because awakeFromNib gets called before applicationWillFinishLaunching. -(void)applicationWillFinishLaunching:(NSNotification*)aNotification { + // Set up the ticket subsystem fServices = new Rtt::MacPlatformServices( *fConsolePlatform ); fAuthorizerDelegate = new Rtt::MacAuthorizationDelegate; @@ -1848,7 +1863,7 @@ - (void)applicationWillResignActive:(NSNotification *)aNotification static bool IsAppAllowedToRun( const Rtt::AuthorizationTicket* t ) { - return t && t->IsAppAllowedToRun(); + return [AppDelegate offlineModeAllowed] || (t && t->IsAppAllowedToRun()); } -(BOOL)isRunnable @@ -2099,7 +2114,7 @@ - (NSString*) preferencesUserName const Rtt::AuthorizationTicket* ticket = [self ticket]; if ( NULL == ticket ) { - return nil; + return @""; } const char* label = ticket->GetUsername(); @@ -2127,7 +2142,7 @@ -(void)deauthorize:(id)sender // If we're connected to the internet, try to deauth this computer with the server so we don't burn one // of their (many) authorization slots. We don't bug the user if this doesn't work. - if ( services.IsInternetAvailable() ) + if ( services.IsInternetAvailable() && ticket ) { const char *usr = ticket->GetUsername(); Rtt::String encryptedPassword; @@ -2149,6 +2164,7 @@ -(void)deauthorize:(id)sender services.SetPreference( Authorization::kTicketKey, NULL ); services.SetPreference( Authorization::kSuppressFeedbackKey, NULL ); services.SetPreference( Authorization::kUsernameKey, NULL ); + services.SetLibraryPreference( Authorization::kOfflineModeConfirmed, NULL ); if (ticket != NULL) { diff --git a/platform/mac/CoronaBuilder.xcodeproj/project.pbxproj b/platform/mac/CoronaBuilder.xcodeproj/project.pbxproj index 2f7ec2a37..68cc527f0 100644 --- a/platform/mac/CoronaBuilder.xcodeproj/project.pbxproj +++ b/platform/mac/CoronaBuilder.xcodeproj/project.pbxproj @@ -135,8 +135,10 @@ C2CF9BA01AE5BFFC00A9A64A /* AndroidValidation.lua in CopyFiles */ = {isa = PBXBuildFile; fileRef = C2C5359D1AE5B8A200BF7A6E /* AndroidValidation.lua */; }; C2F98DF41BB34C4600BDE58B /* XMLRPC.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = C2F98DEC1BB34B8100BDE58B /* XMLRPC.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; C2F98DF51BB34C4C00BDE58B /* XMLRPC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C2F98DEC1BB34B8100BDE58B /* XMLRPC.framework */; }; + F53C329F2404E30900BC2BED /* CoronaBuilderPluginCollector.lua in Sources */ = {isa = PBXBuildFile; fileRef = F53C328E2404E2C000BC2BED /* CoronaBuilderPluginCollector.lua */; }; F547C7F61ED28A2700C4ED92 /* Rtt_DownloadPluginsMain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F547C7F41ED28A2700C4ED92 /* Rtt_DownloadPluginsMain.cpp */; }; F547C7F71ED28BA600C4ED92 /* BuilderPluginDownloader.lua in Sources */ = {isa = PBXBuildFile; fileRef = F547C7ED1ED289EA00C4ED92 /* BuilderPluginDownloader.lua */; }; + F5BEA4B823FFE98200206A6A /* CoronaOfflineiOSPackager.lua in Sources */ = {isa = PBXBuildFile; fileRef = F5BEA4A723FFE97300206A6A /* CoronaOfflineiOSPackager.lua */; }; F5DB5B312113702800EC5CBC /* linuxPackageApp.lua in Sources */ = {isa = PBXBuildFile; fileRef = F5DB5B202113701300EC5CBC /* linuxPackageApp.lua */; }; F5FBD1CE20696145009A9D32 /* Rtt_WebAppPackager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5FBD1CC20696145009A9D32 /* Rtt_WebAppPackager.cpp */; }; F5FBD1DB206961A4009A9D32 /* libluasocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C221F5231D48156700F45E77 /* libluasocket.a */; }; @@ -634,9 +636,11 @@ C2F1BAF71EF48EF500D77BD7 /* CoronaShell.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = CoronaShell.xcodeproj; path = CoronaShell/CoronaShell.xcodeproj; sourceTree = ""; }; C2F1BAFD1EF48F2F00D77BD7 /* ratatouille.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = ratatouille.xcodeproj; sourceTree = ""; }; C2F98DE61BB34B8000BDE58B /* XMLRPC.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = XMLRPC.xcodeproj; path = "../../external/osx-xmlrpc/XMLRPC/XMLRPC.xcodeproj"; sourceTree = ""; }; + F53C328E2404E2C000BC2BED /* CoronaBuilderPluginCollector.lua */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = CoronaBuilderPluginCollector.lua; path = ../resources/CoronaBuilderPluginCollector.lua; sourceTree = ""; }; F547C7ED1ED289EA00C4ED92 /* BuilderPluginDownloader.lua */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = BuilderPluginDownloader.lua; path = ../../tools/CoronaBuilder/BuilderPluginDownloader.lua; sourceTree = ""; }; F547C7F41ED28A2700C4ED92 /* Rtt_DownloadPluginsMain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Rtt_DownloadPluginsMain.cpp; path = ../../tools/CoronaBuilder/Rtt_DownloadPluginsMain.cpp; sourceTree = ""; }; F547C7F51ED28A2700C4ED92 /* Rtt_DownloadPluginsMain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Rtt_DownloadPluginsMain.h; path = ../../tools/CoronaBuilder/Rtt_DownloadPluginsMain.h; sourceTree = ""; }; + F5BEA4A723FFE97300206A6A /* CoronaOfflineiOSPackager.lua */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = CoronaOfflineiOSPackager.lua; path = ../resources/CoronaOfflineiOSPackager.lua; sourceTree = ""; }; F5DB5B202113701300EC5CBC /* linuxPackageApp.lua */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = linuxPackageApp.lua; path = ../resources/linuxPackageApp.lua; sourceTree = ""; }; F5FBD1CC20696145009A9D32 /* Rtt_WebAppPackager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Rtt_WebAppPackager.cpp; path = ../shared/Rtt_WebAppPackager.cpp; sourceTree = ""; }; F5FBD1CD20696145009A9D32 /* Rtt_WebAppPackager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Rtt_WebAppPackager.h; path = ../shared/Rtt_WebAppPackager.h; sourceTree = ""; }; @@ -1105,6 +1109,8 @@ C22BAF751C1B8FBD00EF1795 /* OSXPackageApp.lua */, C22BAF761C1B8FBD00EF1795 /* tvosPackageApp.lua */, F5FBD1DE20696201009A9D32 /* webPackageApp.lua */, + F53C328E2404E2C000BC2BED /* CoronaBuilderPluginCollector.lua */, + F5BEA4A723FFE97300206A6A /* CoronaOfflineiOSPackager.lua */, ); name = Lua; sourceTree = ""; @@ -1460,6 +1466,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + F53C329F2404E30900BC2BED /* CoronaBuilderPluginCollector.lua in Sources */, + F5BEA4B823FFE98200206A6A /* CoronaOfflineiOSPackager.lua in Sources */, F5DB5B312113702800EC5CBC /* linuxPackageApp.lua in Sources */, F5FBD1E020696224009A9D32 /* webPackageApp.lua in Sources */, C277D3A01EBA4CF900966BA1 /* _CoronaSetup_android.lua in Sources */, diff --git a/platform/mac/CoronaBuilder.xcodeproj/xcshareddata/xcschemes/CoronaBuilder.xcscheme b/platform/mac/CoronaBuilder.xcodeproj/xcshareddata/xcschemes/CoronaBuilder.xcscheme index 06564d6a9..403f1ae10 100644 --- a/platform/mac/CoronaBuilder.xcodeproj/xcshareddata/xcschemes/CoronaBuilder.xcscheme +++ b/platform/mac/CoronaBuilder.xcodeproj/xcshareddata/xcschemes/CoronaBuilder.xcscheme @@ -27,8 +27,6 @@ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> - - - - + + - - - - - - GetUsername()]; + NSString *username = ticket?[NSString stringWithExternalString:ticket->GetUsername()]:@"anonymous@corona"; const char* identity = [username UTF8String]; bool useStandartResources = (fUseStandartResources.state == NSOnState); diff --git a/platform/mac/OSXAppBuildController.mm b/platform/mac/OSXAppBuildController.mm index 6b5585024..affc774e5 100644 --- a/platform/mac/OSXAppBuildController.mm +++ b/platform/mac/OSXAppBuildController.mm @@ -309,7 +309,7 @@ - (IBAction)build:(id)sender } // We don't login to build macOS apps so we output this here - const char* usr = [appDelegate ticket]->GetUsername(); + const char* usr = [appDelegate ticket]?[appDelegate ticket]->GetUsername():"anonymous@corona"; Rtt_Log("Building %s app for %s with %s", [self.platformTitle UTF8String], usr, Rtt_STRING_BUILD); diff --git a/platform/mac/Rtt_IOSAppPackager.mm b/platform/mac/Rtt_IOSAppPackager.mm index 3ff5f1039..3b84c0f58 100644 --- a/platform/mac/Rtt_IOSAppPackager.mm +++ b/platform/mac/Rtt_IOSAppPackager.mm @@ -29,6 +29,7 @@ #include "Rtt_MPlatformServices.h" #include "Rtt_WebServicesSession.h" #include "Rtt_FileSystem.h" +#include "Rtt_DeviceBuildData.h" #include "XcodeToolHelper.h" @@ -39,6 +40,9 @@ #include Rtt_EXPORT int luaopen_lfs (lua_State *L); +Rtt_EXPORT int luaopen_socket_core (lua_State *L); +Rtt_EXPORT int luaopen_mime_core(lua_State *L); + // ---------------------------------------------------------------------------- @@ -55,6 +59,15 @@ // following function which loads the bytecodes via luaL_loadbuffer. int luaload_iPhonePackageApp(lua_State* L); int luaload_CoronaPListSupport(lua_State* L); + int luaload_CoronaOfflineiOSPackager(lua_State* L); + int luaload_CoronaBuilderPluginCollector(lua_State *L); + extern int luaload_luasocket_socket(lua_State *L); + extern int luaload_luasocket_ftp(lua_State *L); + extern int luaload_luasocket_headers(lua_State *L); + extern int luaload_luasocket_http(lua_State *L); + extern int luaload_luasocket_url(lua_State *L); + extern int luaload_luasocket_mime(lua_State *L); + extern int luaload_luasocket_ltn12(lua_State *L); // ---------------------------------------------------------------------------- @@ -98,6 +111,7 @@ Lua::RegisterModuleLoader( L, "json", Lua::Open< luaload_json > ); Lua::RegisterModuleLoader( L, "lpeg", luaopen_lpeg ); Lua::RegisterModuleLoader( L, "lfs", luaopen_lfs ); + Lua::RegisterModuleLoader( L, "CoronaBuilderPluginCollector", Lua::Open< luaload_CoronaBuilderPluginCollector >); Lua::DoBuffer( fVM, & luaload_iPhonePackageApp, NULL); } @@ -133,13 +147,137 @@ char* outputFile = (char*)malloc( outputFileLen ); sprintf( outputFile, "%s" LUA_DIRSEP "%s", tmpDir, kOutputName ); - if (fSimulatorServices != NULL) - { - fSimulatorServices->SetBuildMessage("Communicating with build server"); - } + if(session.IsOfflineSession()) + { + if (fSimulatorServices != NULL) + { + fSimulatorServices->SetBuildMessage("Collecting plugins locally"); + } - // Send params/input.zip via web api - result = session.BeginBuild( params, inputFile, outputFile ); + lua_State *L = fVM; + Lua::RegisterModuleLoader( L, "socket.core", luaopen_socket_core ); + Lua::RegisterModuleLoader( L, "socket", Lua::Open< luaload_luasocket_socket > ); + Lua::RegisterModuleLoader( L, "socket.ftp", Lua::Open< luaload_luasocket_ftp > ); + Lua::RegisterModuleLoader( L, "socket.headers", Lua::Open< luaload_luasocket_headers > ); + Lua::RegisterModuleLoader( L, "socket.http", Lua::Open< luaload_luasocket_http > ); + Lua::RegisterModuleLoader( L, "socket.url", Lua::Open< luaload_luasocket_url > ); + Lua::RegisterModuleLoader( L, "mime.core", luaopen_mime_core ); + Lua::RegisterModuleLoader( L, "mime", Lua::Open< luaload_luasocket_mime > ); + Lua::RegisterModuleLoader( L, "ltn12", Lua::Open< luaload_luasocket_ltn12 > ); + + Lua::DoBuffer( L, & luaload_CoronaOfflineiOSPackager, NULL); + lua_getglobal(L, "CreateOfflinePackage"); + lua_newtable( L ); + + lua_pushstring(L, tmpDir); + lua_setfield(L, -2, "tmpDir"); + + lua_pushstring(L, outputFile); + lua_setfield(L, -2, "outputFile"); + + lua_pushstring(L, inputFile); + lua_setfield(L, -2, "inputFile"); + + const char *platform, *modernPlatform, *pluginPlatform; + bool isAppleTV = false; + switch (params->GetTargetDevice()) { + case TargetDevice::kIPhone: + case TargetDevice::kIPad: + case TargetDevice::kIOSUniversal: + platform = "iphoneos"; + modernPlatform = "ios"; + pluginPlatform = "iphone"; + break; + case TargetDevice::kAppleTV: + platform = "appletvos"; + modernPlatform = "tvos"; + pluginPlatform = "appletvos"; + isAppleTV = true; + break; + case TargetDevice::kIPhoneXCodeSimulator: + case TargetDevice::kIPadXCodeSimulator: + case TargetDevice::kIOSUniversalXCodeSimulator: + platform = "iphonesimulator"; + modernPlatform = "ios-sim"; + pluginPlatform = "iphone-sim"; + break; + case TargetDevice::kTVOSXCodeSimulator: + platform = "appletvsimulator"; + modernPlatform = "tvos-sim"; + pluginPlatform = "appletvsimulator"; + isAppleTV = true; + break; + default: + Rtt_ASSERT(0); + platform = "iphoneos"; + break; + } + + lua_pushstring(L, platform); + lua_setfield(L, -2, "platform"); + lua_pushstring(L, modernPlatform); + lua_setfield(L, -2, "modernPlatform"); + lua_pushstring(L, pluginPlatform); + lua_setfield(L, -2, "pluginPlatform"); + lua_pushboolean(L, isAppleTV); + lua_setfield(L, -2, "isAppleTV"); + + lua_pushstring(L, Rtt_MACRO_TO_STRING( Rtt_BUILD_REVISION ) ); + lua_setfield(L, -2, "build"); + + char templateZip[255]; + snprintf(templateZip, 255, "%s_%.1f.tar.bz", platform, params->GetTargetVersion()/10000.0f); + lua_pushstring(L, templateZip); + lua_setfield(L, -2, "template"); + + Rtt::String resourceDir; + fServices.Platform().PathForFile(NULL, MPlatform::kSystemResourceDir, 0, resourceDir); + lua_pushstring(L, resourceDir.GetString()); + lua_setfield(L, -2, "resourceDir"); + + lua_pushstring(L, params->GetAppPackage()); + lua_setfield(L, -2, "appPackage"); + + + DeviceBuildData & buildData = params->GetDeviceBuildData(fServices.Platform(), fServices); + Rtt::String json; + buildData.GetJSON(json); + lua_pushstring(L, json.GetString()); + lua_setfield(L, -2, "buildData"); + + String escapedAppName; + PlatformAppPackager::EscapeFileName( params->GetAppName(), escapedAppName ); + lua_pushstring(L, escapedAppName.GetString()); + lua_setfield(L, -2, "appName"); + + lua_pushliteral(L, "vlad@coronalabs.com"); + lua_setfield(L, -2, "user"); + + if ( Rtt_VERIFY( 0 == Lua::DoCall( L, 1, 1 ) ) ) + { + result = WebServicesSession::kNoError; + if ( lua_isstring( L, -1 ) ) + { + Rtt_TRACE_SIM( ( "BUILD ERROR: %s\n", lua_tostring( L, -1 ) ) ); + params->SetBuildMessage( lua_tostring( L, -1 ) ); + result = WebServicesSession::kBuildError; + } + } + else + { + result = WebServicesSession::kCriticalError; + } + lua_pop( L, 1 ); + } + else + { + if (fSimulatorServices != NULL) + { + fSimulatorServices->SetBuildMessage("Communicating with build server"); + } + // Send params/input.zip via web api + result = session.BeginBuild( params, inputFile, outputFile ); + } if ( WebServicesSession::kNoError == result ) { lua_State *L = fVM; diff --git a/platform/mac/Rtt_MacAuthorizationDelegate.mm b/platform/mac/Rtt_MacAuthorizationDelegate.mm index 9c1a57de6..98d7aaf08 100755 --- a/platform/mac/Rtt_MacAuthorizationDelegate.mm +++ b/platform/mac/Rtt_MacAuthorizationDelegate.mm @@ -111,8 +111,11 @@ -(BOOL)shouldStopModal:(DialogController*)sender withCode:(NSInteger)code result = NO; break; case kActionOther2: - // We no longer exit the app on cancellation of login - // [NSApp terminate:self]; + NSDictionary *details = [NSDictionary dictionaryWithObjectsAndKeys:@"Corona will start in offline mode. To change this go to Preferences… / Deauthorize and Quit", NSLocalizedDescriptionKey, nil]; + NSError *error = [NSError errorWithDomain:@"Corona Simulator" code:404 userInfo:details]; + [[NSAlert alertWithError:error] runModal]; + AuthorizationContext* c = (AuthorizationContext*)sender.userdata; + c->authorizer->GetServices().SetPreference(Rtt::Authorization::kOfflineModeConfirmed, "offline"); break; } @@ -1142,7 +1145,7 @@ +(NSSet*)keyPathsForValuesAffectingEnabledDefault // choose register or the help button, the dialog will not disappear. // When a login is attempted, MacAuthorizationDelegate::Login() is called // while the modal dialog is still running. - result = Rtt_VERIFY( kActionDefault == [fLoginController runModalWithMessage:[NSString stringWithExternalString:sender.GetLoginMessage()]] ); + result = ( kActionDefault == [fLoginController runModalWithMessage:[NSString stringWithExternalString:sender.GetLoginMessage()]] ); } return result; diff --git a/platform/mac/Rtt_MacPlatformServices.mm b/platform/mac/Rtt_MacPlatformServices.mm index 7634513a1..b6ab1799f 100644 --- a/platform/mac/Rtt_MacPlatformServices.mm +++ b/platform/mac/Rtt_MacPlatformServices.mm @@ -233,6 +233,7 @@ || ( 0 == strcmp(key, Authorization::kVersionKey) ) || ( 0 == strcmp(key, Authorization::kUsernameKey) ) || ( 0 == strcmp(key, Authorization::kRenewalReminderKey) ) + || ( 0 == strcmp(key, Authorization::kOfflineModeConfirmed) ) || ( 0 == strcmp(key, "LastUpdateCheck") ) // no external constant to refer to ) { diff --git a/platform/mac/Rtt_MacSimulator.mm b/platform/mac/Rtt_MacSimulator.mm index 5f6597ebf..999064e2a 100644 --- a/platform/mac/Rtt_MacSimulator.mm +++ b/platform/mac/Rtt_MacSimulator.mm @@ -174,7 +174,7 @@ AppDelegate *delegate = (AppDelegate*)[[NSApplication sharedApplication] delegate]; const AuthorizationTicket *ticket = [delegate ticket]; - const char *subscription = AuthorizationTicket::StringForSubscription( ticket->GetSubscription() ); + const char *subscription = ticket?AuthorizationTicket::StringForSubscription( ticket->GetSubscription() ):""; [fProperties setValue:[NSString stringWithExternalString:subscription] forKey:@"subscription"]; // Store the simulated device's default font size. diff --git a/platform/mac/Rtt_TVOSAppPackager.mm b/platform/mac/Rtt_TVOSAppPackager.mm index 384ef6f73..1c3691a5b 100644 --- a/platform/mac/Rtt_TVOSAppPackager.mm +++ b/platform/mac/Rtt_TVOSAppPackager.mm @@ -29,6 +29,7 @@ #include "Rtt_MPlatformServices.h" #include "Rtt_WebServicesSession.h" #include "Rtt_FileSystem.h" +#include "Rtt_DeviceBuildData.h" #include "XcodeToolHelper.h" @@ -39,6 +40,9 @@ #include Rtt_EXPORT int luaopen_lfs (lua_State *L); +Rtt_EXPORT int luaopen_socket_core (lua_State *L); +Rtt_EXPORT int luaopen_mime_core(lua_State *L); + // ---------------------------------------------------------------------------- @@ -55,6 +59,16 @@ // following function which loads the bytecodes via luaL_loadbuffer. int luaload_tvosPackageApp(lua_State* L); int luaload_CoronaPListSupport(lua_State* L); + int luaload_CoronaOfflineiOSPackager(lua_State* L); + int luaload_CoronaBuilderPluginCollector(lua_State *L); + extern int luaload_luasocket_socket(lua_State *L); + extern int luaload_luasocket_ftp(lua_State *L); + extern int luaload_luasocket_headers(lua_State *L); + extern int luaload_luasocket_http(lua_State *L); + extern int luaload_luasocket_url(lua_State *L); + extern int luaload_luasocket_mime(lua_State *L); + extern int luaload_luasocket_ltn12(lua_State *L); + // ---------------------------------------------------------------------------- @@ -98,6 +112,7 @@ Lua::RegisterModuleLoader( L, "json", Lua::Open< luaload_json > ); Lua::RegisterModuleLoader( L, "lpeg", luaopen_lpeg ); Lua::RegisterModuleLoader( L, "lfs", luaopen_lfs ); + Lua::RegisterModuleLoader( L, "CoronaBuilderPluginCollector", luaload_CoronaBuilderPluginCollector); Lua::DoBuffer( fVM, & luaload_tvosPackageApp, NULL); } @@ -133,13 +148,136 @@ char* outputFile = (char*)malloc( outputFileLen ); sprintf( outputFile, "%s" LUA_DIRSEP "%s", tmpDir, kOutputName ); - if (fSimulatorServices != NULL) - { - fSimulatorServices->SetBuildMessage("Communicating with build server"); - } + if(session.IsOfflineSession()) + { + if (fSimulatorServices != NULL) + { + fSimulatorServices->SetBuildMessage("Collecting plugins locally"); + } - // Send params/input.zip via web api - result = session.BeginBuild( params, inputFile, outputFile ); + lua_State *L = fVM; + Lua::RegisterModuleLoader( L, "socket.core", luaopen_socket_core ); + Lua::RegisterModuleLoader( L, "socket", Lua::Open< luaload_luasocket_socket > ); + Lua::RegisterModuleLoader( L, "socket.ftp", Lua::Open< luaload_luasocket_ftp > ); + Lua::RegisterModuleLoader( L, "socket.headers", Lua::Open< luaload_luasocket_headers > ); + Lua::RegisterModuleLoader( L, "socket.http", Lua::Open< luaload_luasocket_http > ); + Lua::RegisterModuleLoader( L, "socket.url", Lua::Open< luaload_luasocket_url > ); + Lua::RegisterModuleLoader( L, "mime.core", luaopen_mime_core ); + Lua::RegisterModuleLoader( L, "mime", Lua::Open< luaload_luasocket_mime > ); + Lua::RegisterModuleLoader( L, "ltn12", Lua::Open< luaload_luasocket_ltn12 > ); + + + Lua::DoBuffer( L, & luaload_CoronaOfflineiOSPackager, NULL); + lua_getglobal(L, "CreateOfflinePackage"); + lua_newtable( L ); + + lua_pushstring(L, tmpDir); + lua_setfield(L, -2, "tmpDir"); + + lua_pushstring(L, outputFile); + lua_setfield(L, -2, "outputFile"); + + lua_pushstring(L, inputFile); + lua_setfield(L, -2, "inputFile"); + + const char *platform, *modernPlatform, *pluginPlatform; + bool isAppleTV = false; + switch (params->GetTargetDevice()) { + case TargetDevice::kIPhone: + case TargetDevice::kIPad: + case TargetDevice::kIOSUniversal: + platform = "iphoneos"; + modernPlatform = "ios"; + pluginPlatform = "iphone"; + break; + case TargetDevice::kAppleTV: + platform = "appletvos"; + modernPlatform = "tvos"; + pluginPlatform = "appletvos"; + isAppleTV = true; + break; + case TargetDevice::kIPhoneXCodeSimulator: + case TargetDevice::kIPadXCodeSimulator: + case TargetDevice::kIOSUniversalXCodeSimulator: + platform = "iphonesimulator"; + modernPlatform = "ios-sim"; + pluginPlatform = "iphone-sim"; + break; + case TargetDevice::kTVOSXCodeSimulator: + platform = "appletvsimulator"; + modernPlatform = "tvos-sim"; + pluginPlatform = "appletvsimulator"; + isAppleTV = true; + break; + default: + Rtt_ASSERT(0); + platform = "iphoneos"; + break; + } + + lua_pushstring(L, platform); + lua_setfield(L, -2, "platform"); + lua_pushstring(L, modernPlatform); + lua_setfield(L, -2, "modernPlatform"); + lua_pushstring(L, pluginPlatform); + lua_setfield(L, -2, "pluginPlatform"); + lua_pushboolean(L, isAppleTV); + lua_setfield(L, -2, "isAppleTV"); + + lua_pushstring(L, Rtt_MACRO_TO_STRING( Rtt_BUILD_REVISION ) ); + lua_setfield(L, -2, "build"); + + char templateZip[255]; + snprintf(templateZip, 255, "%s_%.1f.tar.bz", platform, params->GetTargetVersion()/10000.0f); + lua_pushstring(L, templateZip); + lua_setfield(L, -2, "template"); + + Rtt::String resourceDir; + fServices.Platform().PathForFile(NULL, MPlatform::kSystemResourceDir, 0, resourceDir); + lua_pushstring(L, resourceDir.GetString()); + lua_setfield(L, -2, "resourceDir"); + + lua_pushstring(L, params->GetAppPackage()); + lua_setfield(L, -2, "appPackage"); + + + DeviceBuildData & buildData = params->GetDeviceBuildData(fServices.Platform(), fServices); + Rtt::String json; + buildData.GetJSON(json); + lua_pushstring(L, json.GetString()); + lua_setfield(L, -2, "buildData"); + + String escapedAppName; + PlatformAppPackager::EscapeFileName( params->GetAppName(), escapedAppName ); + lua_pushstring(L, escapedAppName.GetString()); + lua_setfield(L, -2, "appName"); + + + if ( Rtt_VERIFY( 0 == Lua::DoCall( L, 1, 1 ) ) ) + { + result = WebServicesSession::kNoError; + if ( lua_isstring( L, -1 ) ) + { + Rtt_TRACE_SIM( ( "BUILD ERROR: %s\n", lua_tostring( L, -1 ) ) ); + params->SetBuildMessage( lua_tostring( L, -1 ) ); + result = WebServicesSession::kBuildError; + } + } + else + { + result = WebServicesSession::kCriticalError; + } + lua_pop( L, 1 ); + } + else + { + if (fSimulatorServices != NULL) + { + fSimulatorServices->SetBuildMessage("Communicating with build server"); + } + // Send params/input.zip via web api + result = session.BeginBuild( params, inputFile, outputFile ); + } if ( WebServicesSession::kNoError == result ) { lua_State *L = fVM; diff --git a/platform/mac/WebAppBuildController.mm b/platform/mac/WebAppBuildController.mm index dd5bb0c25..7f4ca7c91 100644 --- a/platform/mac/WebAppBuildController.mm +++ b/platform/mac/WebAppBuildController.mm @@ -280,7 +280,7 @@ - (IBAction)build:(id)sender } const Rtt::AuthorizationTicket *ticket = [appDelegate ticket]; - NSString *username = [NSString stringWithExternalString:ticket->GetUsername()]; + NSString *username = ticket?[NSString stringWithExternalString:ticket->GetUsername()]:@"anonymous@corona"; const char* identity = [username UTF8String]; bool useStandartResources = (fUseStandartResources.state == NSOnState); diff --git a/platform/mac/ratatouille.xcodeproj/project.pbxproj b/platform/mac/ratatouille.xcodeproj/project.pbxproj index b12b1e507..36237e51b 100644 --- a/platform/mac/ratatouille.xcodeproj/project.pbxproj +++ b/platform/mac/ratatouille.xcodeproj/project.pbxproj @@ -1650,6 +1650,8 @@ F53594BC1B6185EA00C8CAAD /* Rtt_TextureResourceCanvasAdapter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F53594B51B6185EA00C8CAAD /* Rtt_TextureResourceCanvasAdapter.cpp */; }; F53594BD1B6185EA00C8CAAD /* Rtt_TextureResourceCanvasAdapter.h in Headers */ = {isa = PBXBuildFile; fileRef = F53594B61B6185EA00C8CAAD /* Rtt_TextureResourceCanvasAdapter.h */; }; F53594BE1B6185EA00C8CAAD /* Rtt_TextureResourceCanvasAdapter.h in Headers */ = {isa = PBXBuildFile; fileRef = F53594B61B6185EA00C8CAAD /* Rtt_TextureResourceCanvasAdapter.h */; }; + F53C328C2404E2A300BC2BED /* CoronaBuilderPluginCollector.lua in Sources */ = {isa = PBXBuildFile; fileRef = F53C32832404E25600BC2BED /* CoronaBuilderPluginCollector.lua */; }; + F53C328D2404E2AB00BC2BED /* CoronaBuilderPluginCollector.lua in Sources */ = {isa = PBXBuildFile; fileRef = F53C32832404E25600BC2BED /* CoronaBuilderPluginCollector.lua */; }; F545475321178F7400D5EDB7 /* linuxtemplate.tar.gz in Resources */ = {isa = PBXBuildFile; fileRef = F545474A21178F7400D5EDB7 /* linuxtemplate.tar.gz */; }; F54D1E121CA35968000B280E /* Rtt_AppleFont.mm in Sources */ = {isa = PBXBuildFile; fileRef = F54D1E111CA35968000B280E /* Rtt_AppleFont.mm */; }; F54D1E131CA35968000B280E /* Rtt_AppleFont.mm in Sources */ = {isa = PBXBuildFile; fileRef = F54D1E111CA35968000B280E /* Rtt_AppleFont.mm */; }; @@ -1659,6 +1661,7 @@ F55407F61C74ECDE007F53C9 /* Rtt_TextureResourceExternalAdapter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F55407F31C74ECDE007F53C9 /* Rtt_TextureResourceExternalAdapter.cpp */; }; F55407F71C74ECDE007F53C9 /* Rtt_TextureResourceExternalAdapter.h in Headers */ = {isa = PBXBuildFile; fileRef = F55407F41C74ECDE007F53C9 /* Rtt_TextureResourceExternalAdapter.h */; }; F55407F81C74ECDE007F53C9 /* Rtt_TextureResourceExternalAdapter.h in Headers */ = {isa = PBXBuildFile; fileRef = F55407F41C74ECDE007F53C9 /* Rtt_TextureResourceExternalAdapter.h */; }; + F57469D7240291FA00CA5949 /* iostemplate in Resources */ = {isa = PBXBuildFile; fileRef = F57469D6240291FA00CA5949 /* iostemplate */; }; F57AE2051F051610000A4245 /* Rtt_AppleInAppStore.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0077B6571463359A00EEDF75 /* Rtt_AppleInAppStore.mm */; }; F57AE2061F051610000A4245 /* Rtt_AppleInAppStoreApple.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0077B6591463359A00EEDF75 /* Rtt_AppleInAppStoreApple.mm */; }; F57AE2071F051610000A4245 /* Rtt_AppleInAppStoreAppleTransactionObjectWrapper.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0077B65B1463359A00EEDF75 /* Rtt_AppleInAppStoreAppleTransactionObjectWrapper.mm */; }; @@ -1721,6 +1724,8 @@ F5FA0AFB1C73D61700E926A7 /* Rtt_TextureResourceExternal.h in Headers */ = {isa = PBXBuildFile; fileRef = F5FA0AF81C73D61700E926A7 /* Rtt_TextureResourceExternal.h */; }; F5FA0AFC1C73D61700E926A7 /* Rtt_TextureResourceExternal.h in Headers */ = {isa = PBXBuildFile; fileRef = F5FA0AF81C73D61700E926A7 /* Rtt_TextureResourceExternal.h */; }; F5FED7091DF8E1C7001CA415 /* AntLiveManifest.jar in Resources */ = {isa = PBXBuildFile; fileRef = F5FED7061DF8DFE2001CA415 /* AntLiveManifest.jar */; }; + F5FFADBC23FFC7DB004E61A9 /* CoronaOfflineiOSPackager.lua in Sources */ = {isa = PBXBuildFile; fileRef = F5FFADB123FFC798004E61A9 /* CoronaOfflineiOSPackager.lua */; }; + F5FFADBE23FFC80E004E61A9 /* CoronaOfflineiOSPackager.lua in Sources */ = {isa = PBXBuildFile; fileRef = F5FFADB123FFC798004E61A9 /* CoronaOfflineiOSPackager.lua */; }; F818407317E3C221003072C4 /* widget_theme_ios7_sheet.lua in Sources */ = {isa = PBXBuildFile; fileRef = F818407117E3C221003072C4 /* widget_theme_ios7_sheet.lua */; }; F818407417E3C221003072C4 /* widget_theme_ios7.lua in Sources */ = {isa = PBXBuildFile; fileRef = F818407217E3C221003072C4 /* widget_theme_ios7.lua */; }; F818407617E3C28E003072C4 /* widget_theme_ios7.png in Resources */ = {isa = PBXBuildFile; fileRef = F818407517E3C28E003072C4 /* widget_theme_ios7.png */; }; @@ -3382,11 +3387,13 @@ F53594B41B6185EA00C8CAAD /* Rtt_TextureResourceCanvas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Rtt_TextureResourceCanvas.h; path = Display/Rtt_TextureResourceCanvas.h; sourceTree = ""; }; F53594B51B6185EA00C8CAAD /* Rtt_TextureResourceCanvasAdapter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Rtt_TextureResourceCanvasAdapter.cpp; path = Display/Rtt_TextureResourceCanvasAdapter.cpp; sourceTree = ""; }; F53594B61B6185EA00C8CAAD /* Rtt_TextureResourceCanvasAdapter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Rtt_TextureResourceCanvasAdapter.h; path = Display/Rtt_TextureResourceCanvasAdapter.h; sourceTree = ""; }; + F53C32832404E25600BC2BED /* CoronaBuilderPluginCollector.lua */ = {isa = PBXFileReference; lastKnownFileType = text; name = CoronaBuilderPluginCollector.lua; path = ../platform/resources/CoronaBuilderPluginCollector.lua; sourceTree = ""; }; F545474A21178F7400D5EDB7 /* linuxtemplate.tar.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; name = linuxtemplate.tar.gz; path = ../resources/linuxtemplate.tar.gz; sourceTree = ""; }; F54D1E111CA35968000B280E /* Rtt_AppleFont.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = Rtt_AppleFont.mm; path = ../apple/Rtt_AppleFont.mm; sourceTree = ""; }; F54D8E291B4B29120063F98B /* ForceFeedback.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ForceFeedback.framework; path = System/Library/Frameworks/ForceFeedback.framework; sourceTree = SDKROOT; }; F55407F31C74ECDE007F53C9 /* Rtt_TextureResourceExternalAdapter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Rtt_TextureResourceExternalAdapter.cpp; path = Display/Rtt_TextureResourceExternalAdapter.cpp; sourceTree = ""; }; F55407F41C74ECDE007F53C9 /* Rtt_TextureResourceExternalAdapter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Rtt_TextureResourceExternalAdapter.h; path = Display/Rtt_TextureResourceExternalAdapter.h; sourceTree = ""; }; + F57469D6240291FA00CA5949 /* iostemplate */ = {isa = PBXFileReference; lastKnownFileType = folder; name = iostemplate; path = ../resources/iostemplate; sourceTree = ""; }; F57D17651BE25B1C00AABAC1 /* Rtt_AppleInputDeviceManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Rtt_AppleInputDeviceManager.h; path = ../apple/Rtt_AppleInputDeviceManager.h; sourceTree = ""; }; F57D17661BE25B1C00AABAC1 /* Rtt_AppleInputDeviceManager.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = Rtt_AppleInputDeviceManager.mm; path = ../apple/Rtt_AppleInputDeviceManager.mm; sourceTree = ""; }; F57D17691BE25C7C00AABAC1 /* Rtt_AppleInputHIDDeviceListener.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Rtt_AppleInputHIDDeviceListener.h; path = ../apple/Rtt_AppleInputHIDDeviceListener.h; sourceTree = ""; }; @@ -3430,6 +3437,7 @@ F5FA0AF71C73D61700E926A7 /* Rtt_TextureResourceExternal.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Rtt_TextureResourceExternal.cpp; path = Display/Rtt_TextureResourceExternal.cpp; sourceTree = ""; }; F5FA0AF81C73D61700E926A7 /* Rtt_TextureResourceExternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Rtt_TextureResourceExternal.h; path = Display/Rtt_TextureResourceExternal.h; sourceTree = ""; }; F5FED7061DF8DFE2001CA415 /* AntLiveManifest.jar */ = {isa = PBXFileReference; lastKnownFileType = archive.jar; name = AntLiveManifest.jar; path = ../android/AntLiveManifest/AntLiveManifest.jar; sourceTree = SOURCE_ROOT; }; + F5FFADB123FFC798004E61A9 /* CoronaOfflineiOSPackager.lua */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = CoronaOfflineiOSPackager.lua; path = ../platform/resources/CoronaOfflineiOSPackager.lua; sourceTree = ""; }; F818406817E3C1CE003072C4 /* widget_theme_ios7.lua */ = {isa = PBXFileReference; lastKnownFileType = text; name = widget_theme_ios7.lua; path = ../../subrepos/widget/widget_theme_ios7.lua; sourceTree = ""; }; F818407117E3C221003072C4 /* widget_theme_ios7_sheet.lua */ = {isa = PBXFileReference; lastKnownFileType = text; name = widget_theme_ios7_sheet.lua; path = ../../subrepos/widget/widget_theme_ios7_sheet.lua; sourceTree = ""; }; F818407217E3C221003072C4 /* widget_theme_ios7.lua */ = {isa = PBXFileReference; lastKnownFileType = text; name = widget_theme_ios7.lua; path = ../../subrepos/widget/widget_theme_ios7.lua; sourceTree = ""; }; @@ -4165,6 +4173,8 @@ C2AF1E541DA6F0ED00907A65 /* valid_config_lua.lua */, A4A0B56216F1B0C5006D5373 /* config_require.lua */, C200AAE919626D86005828AA /* CoronaPListSupport.lua */, + F53C32832404E25600BC2BED /* CoronaBuilderPluginCollector.lua */, + F5FFADB123FFC798004E61A9 /* CoronaOfflineiOSPackager.lua */, A4E76F181399772500AFB095 /* create_build_properties.lua */, 000CE8CE12B7400900D9B6A4 /* init.lua */, 000CE8CF12B7400900D9B6A4 /* iPhonePackageApp.lua */, @@ -4378,6 +4388,7 @@ 00B73C6D12B720C90057F594 /* Resources */ = { isa = PBXGroup; children = ( + F57469D6240291FA00CA5949 /* iostemplate */, F5C39D7C23EBCC280058DD5E /* CoronaSimulator.entitlements */, F500FB3F1FED5CE20050B28B /* webtemplate.zip */, F545474A21178F7400D5EDB7 /* linuxtemplate.tar.gz */, @@ -6624,6 +6635,7 @@ C25CDF49195B5B7300CE2321 /* widget_theme_ios7.lua in Resources */, 00B73E1A12B727240057F594 /* AndroidKeyStorePassword.xib in Resources */, 00B73E1C12B727240057F594 /* BuildProgress.xib in Resources */, + F57469D7240291FA00CA5949 /* iostemplate in Resources */, 00B73E1F12B727240057F594 /* License.xib in Resources */, 00B73E2112B727240057F594 /* Login.xib in Resources */, 00B73E2412B727240057F594 /* Password.xib in Resources */, @@ -6859,6 +6871,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + F53C328C2404E2A300BC2BED /* CoronaBuilderPluginCollector.lua in Sources */, + F5FFADBC23FFC7DB004E61A9 /* CoronaOfflineiOSPackager.lua in Sources */, C2AF1E571DA6F24500907A65 /* valid_build_settings.lua in Sources */, C2AF1E581DA6F24500907A65 /* valid_config_lua.lua in Sources */, C2AF1E511DA6D8C700907A65 /* ValidateSettings.lua in Sources */, @@ -7469,6 +7483,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + F53C328D2404E2AB00BC2BED /* CoronaBuilderPluginCollector.lua in Sources */, + F5FFADBE23FFC80E004E61A9 /* CoronaOfflineiOSPackager.lua in Sources */, F5DB5B1A21136F9D00EC5CBC /* linuxPackageApp.lua in Sources */, C2AF1E591DA6F39900907A65 /* valid_build_settings.lua in Sources */, C2AF1E5A1DA6F39900907A65 /* valid_config_lua.lua in Sources */, diff --git a/platform/resources/CoronaBuilderPluginCollector.lua b/platform/resources/CoronaBuilderPluginCollector.lua new file mode 100644 index 000000000..28a3127e6 --- /dev/null +++ b/platform/resources/CoronaBuilderPluginCollector.lua @@ -0,0 +1,328 @@ +------------------------------------------------------------------------------ +-- +-- This file is part of the Corona game engine. +-- For overview and more information on licensing please refer to README.md +-- Home page: https://github.com/coronalabs/corona +-- Contact: support@coronalabs.com +-- +------------------------------------------------------------------------------ + +local json = require "json" +local lfs = require "lfs" + +local debugBuildProcess = os.execute("exit $(defaults read com.coronalabs.Corona_Simulator debugBuildProcess 2>/dev/null || echo 0)") +function log(...) + if debugBuildProcess > 1 then + print(...) + end +end + +local function quoteString( str ) + str = str:gsub('\\', '\\\\') + str = str:gsub('"', '\\"') + return "\"" .. str .. "\"" +end + +local function exec(cmd) + log('Running command', cmd) + if debugBuildProcess < 1 then + cmd = cmd .. ' &> /dev/null' + end + assert(0 == os.execute(cmd)) +end + +local pluginLocatorCoronaStore = {} +function pluginLocatorCoronaStore:init(params) + self.user = params.user + if not self.user then + return + end + self.http = require( "socket.http" ) + self.serverBackend = 'https://backendapi.coronalabs.com' + + local authURL = self.serverBackend .. '/v1/plugins/show/' .. self.user + local authorisedPluginsText, msg = self.http.request(authURL) + + if not authorisedPluginsText then + return false + end + + local authPluginsJson = json.decode( authorisedPluginsText ) + if not authPluginsJson then + return false + end + + if authPluginsJson.status ~= 'success' then + return false + end + + if not authPluginsJson.data then + return false + end + + self.authorizedPlugins = {} + + for _, ap in pairs(authPluginsJson.data) do + local pluginName = ap['plugin_name'] + local pluginDeveloper = ap["plugin_developer"] + self.authorizedPlugins[pluginName] = self.authorizedPlugins[pluginName] or {} + self.authorizedPlugins[pluginName][pluginDeveloper] = true + end + return true +end + +function pluginLocatorCoronaStore:collect(destination, plugin, pluginTable, pluginPlatform, params) + if not self.user then + return "Corona Store: no user login" + end + if not self.authorizedPlugins then + return "Corona Store: authorized plugins was not fetched" + end + local developer = pluginTable.publisherId + if not (self.authorizedPlugins[plugin] or {})[developer] then + return "Corona Store: plugin " .. plugin .. " was not authorized by the store. Activate at: https://marketplace.coronalabs.com/plugin/" .. developer .. "/" .. plugin + end + + local build = params.build + local downloadInfoURL = self.serverBackend .. '/v1/plugins/download/' .. developer .. '/' .. plugin .. '/' .. build .. '/' .. pluginPlatform + + local downloadInfoText, msg = self.http.request(downloadInfoURL) + if not downloadInfoText then + return "Corona Store: unable to fetch plugin download location for " .. plugin .. ' ('.. developer.. '). Error message: ' .. msg + end + + local downloadInfoJSON = json.decode(downloadInfoText) + local downloadURL = downloadInfoJSON.url + if not downloadURL then + return "Corona Store: unable to parse plugin download location for " .. plugin .. ' ('.. developer.. ').' + end + + local buildStr = downloadURL:match('/(%d%d%d%d%.%d%d%d%d)/') + local file, err = self.http.request(downloadURL) + if err == 404 then + log("Corona Store: plugin " .. plugin .. " is not supported by the platform!") + return true + elseif err ~= 200 then + return "Corona Store: unable to download " .. plugin .. ' ('.. developer.. '). Code: ' .. err .. 'Error message: \n' .. file + end + + local pluginArchivePath = params.pluginStorage .. '/' .. pluginTable.publisherId .. '/' .. plugin .. '/' .. buildStr .. '/' .. pluginPlatform + exec('/bin/mkdir -p ' .. quoteString(pluginArchivePath)) + pluginArchivePath = pluginArchivePath .. '/data.tgz' + + fi = io.open(pluginArchivePath, "wb") + if (fi == nil) then + return 'Corona Store: unable to create tgz' + end + fi:write(file) + fi:close() + + exec('/bin/mkdir -p ' .. quoteString(destination)) + exec('/bin/cp ' .. quoteString(pluginArchivePath) .. ' ' .. quoteString(destination) ) + return true +end + + +local function pluginLocatorCustomURL(destination, plugin, pluginTable, pluginPlatform, params) + if type(pluginTable.supportedPlatforms) ~= 'table' then + return "Custom URL: skipped because no table supportedPlatforms provided for " .. plugin + end + if type(pluginTable.supportedPlatforms[pluginPlatform]) ~= 'table' then + log("Custom URL: skipped because supportedPlatforms[" .. pluginPlatform .. "] is not a table. Plugin is not supported by the platform") + return true + end + if type(pluginTable.supportedPlatforms[pluginPlatform].url) ~= 'string' then + return "Custom URL: skipped because supportedPlatforms[" .. pluginPlatform .. "].url is not a string" + end + + exec('/bin/mkdir -p ' .. quoteString(destination)) + exec('curl -sS -o ' .. quoteString(destination .. '/data.tgz') .. ' ' .. quoteString(pluginTable.supportedPlatforms[pluginPlatform].url)) + return true +end + + +local function pluginLocatorFileSystemVersionized(destination, plugin, pluginTable, pluginPlatform, params) + if type(pluginTable.publisherId) ~= 'string' then + return "Locally: plugin has no string publisherId" + end + local pluginStorage = params.pluginStorage + local pluginDir = pluginStorage .. '/' .. pluginTable.publisherId .. '/' .. plugin + if lfs.attributes(pluginDir, 'mode') ~= 'directory' then + return "Locally: no directory " .. pluginDir + end + local targetBuild = tonumber(params.build) + local lastFound = -1 + local foundDir + for file in lfs.dir(pluginDir) do + if file ~= "." and file ~= ".." then + local f = pluginDir..'/'..file + if lfs.attributes(f, 'mode') == 'directory' then + local dirBuild = file:match('^%d+%.(%d+)$') + if dirBuild then + dirBuild = tonumber(dirBuild) + if dirBuild > lastFound and dirBuild <= targetBuild then + lastFound = dirBuild + foundDir = f + end + end + end + end + end + if not foundDir then + return "Locally: didn't find suitable version in " .. pluginDir + end + local localPath = foundDir .. '/' .. pluginPlatform .. '/data.tgz' + if lfs.attributes(localPath, 'mode') == 'file' then + exec('/bin/mkdir -p ' .. quoteString(destination)) + exec('/bin/cp ' .. quoteString(localPath) .. ' ' .. quoteString(destination) ) + else + -- if we found suitable version, but no platform directory, it means plugin is not supported + log('Local lookup determined that plugin ' .. plugin .. ' is not supported by the platform in this version ' .. pluginPlatform) + end + return true +end + + +local function pluginLocatorFileSystemAllPlatforms(destination, plugin, pluginTable, pluginPlatform, params) + if type(pluginTable.publisherId) ~= 'string' then + return "Locally: plugin has no string publisherId" + end + local pluginStorage = params.pluginStorage + local localPath = pluginStorage .. '/' .. pluginTable.publisherId .. '/' .. plugin .. '/data.tgz' + if lfs.attributes(localPath, 'mode') == 'file' then + exec('/bin/mkdir -p ' .. quoteString(destination)) + exec('/bin/cp ' .. quoteString(localPath) .. ' ' .. quoteString(destination) ) + return true + else + return "Locally: no file '".. localPath .. "'" + end +end + +local function pluginLocatorFileSystem(destination, plugin, pluginTable, pluginPlatform, params) + if type(pluginTable.publisherId) ~= 'string' then + return "Locally: plugin has no string publisherId" + end + local pluginStorage = params.pluginStorage + local localPath = pluginStorage .. '/' .. pluginTable.publisherId .. '/' .. plugin .. '/' .. pluginPlatform .. '/data.tgz' + if lfs.attributes(localPath, 'mode') == 'file' then + exec('/bin/mkdir -p ' .. quoteString(destination)) + exec('/bin/cp ' .. quoteString(localPath) .. ' ' .. quoteString(destination) ) + return true + elseif lfs.attributes(pluginStorage .. '/' .. pluginTable.publisherId .. '/' .. plugin, 'mode') == 'directory' then + log('Local lookup determined that plugin ' .. plugin .. ' is not supported by the platform ' .. pluginPlatform) + else + return "Locally: no file '".. localPath .. "'" + end +end + + +local initializedLocators = false +local function fetchSinglePlugin(dstDir, plugin, pluginTable, pluginPlatform, params, pluginLocators) + if type(pluginTable.supportedPlatforms) == 'table' and not pluginTable.supportedPlatforms[pluginPlatform] then + return + end + if not initializedLocators then + initializedLocators = true + for i = 1,#pluginLocators do + local locator = pluginLocators[i] + if type(locator) == 'table' and type(locator.init) == 'function' then + locator:init(params) + if type(locator.collect) ~= 'function' then + return "ERROR: Plugin Locator #" .. tostring(i) .. " does not have :collect() method!" + end + elseif type(locator) ~= 'function' then + return "ERROR: Plugin Locator #" .. tostring(i) .. " is not a function!" + end + end + end + local pluginDestination = dstDir .. '/' .. plugin + local err = "Unable to find plugin '" .. plugin .. "' in:" + local ok = false + for i = 1,#pluginLocators do + local locator = pluginLocators[i] + local result + if type(locator) == 'table' then + result = locator:collect(pluginDestination, plugin, pluginTable, pluginPlatform, params) + else + result = locator(pluginDestination, plugin, pluginTable, pluginPlatform, params) + end + if result == true then + ok = true + break + elseif type(result) == 'string' then + err = err .. '\n\t' .. result + end + end + if not ok then + return err + end +end + +function CollectCoronaPlugins(params) + log("Collecting plugins") + + local pluginLocators = { pluginLocatorFileSystemVersionized, pluginLocatorFileSystem, pluginLocatorFileSystemAllPlatforms, pluginLocatorCustomURL, pluginLocatorCoronaStore, } + + local dstDir = params.destinationDirectory + + params.pluginLocators = pluginLocators + if not params.pluginStorage then + params.pluginStorage = os.getenv("HOME") .. '/CoronaPlugins' + end + + local plugins = json.decode(params.buildData).plugins + if type(plugins) ~= 'table' then return end + + local pluginPlatform = params.pluginPlatform + local collectedPlugins = {} + for plugin, pluginTable in pairs(plugins) do + assert(type(plugin) == 'string', "Plugin is not a string") + assert(type(pluginTable) == 'table', 'Invalid plugin table for ' .. plugin) + local result = fetchSinglePlugin(dstDir, plugin, pluginTable, pluginPlatform, params, pluginLocators) + if type(result) == 'string' then + return result + end + collectedPlugins[plugin] = true + end + + log("Collecting plugin dependencies") + local unresolvedDeps = {} + local allFetched + repeat + for plugin, pluginTable in pairs(unresolvedDeps) do + log("Collecting dependency " .. plugin) + local result = fetchSinglePlugin(dstDir, plugin, pluginTable, pluginPlatform, params, pluginLocators) + if type(result) == 'string' then + return result + end + collectedPlugins[plugin] = true + end + unresolvedDeps = {} + allFetched = true + for plugin, _ in pairs(collectedPlugins) do + local pluginDestination = dstDir .. '/' .. plugin + if 0 == os.execute('/usr/bin/tar -xzf ' .. quoteString(pluginDestination .. '/data.tgz') .. ' -C ' .. quoteString(pluginDestination) .. ' metadata.lua') then + local toDownload = {} + local metadataFile = pluginDestination .. "/metadata.lua" + pcall( function() + local metadata = dofile(metadataFile) + toDownload = metadata.coronaManifest.dependencies + end ) + os.remove(metadataFile) + if type(toDownload) == 'table' then + for depPlugin, depDeveloper in pairs(toDownload) do + if not collectedPlugins[depPlugin] then + unresolvedDeps[depPlugin] = {publisherId=depDeveloper} + allFetched = false + end + end + end + end + end + until allFetched +end + + +return { + collect = CollectCoronaPlugins +} \ No newline at end of file diff --git a/platform/resources/CoronaOfflineiOSPackager.lua b/platform/resources/CoronaOfflineiOSPackager.lua new file mode 100644 index 000000000..ace5e2651 --- /dev/null +++ b/platform/resources/CoronaOfflineiOSPackager.lua @@ -0,0 +1,156 @@ +------------------------------------------------------------------------------ +-- +-- This file is part of the Corona game engine. +-- For overview and more information on licensing please refer to README.md +-- Home page: https://github.com/coronalabs/corona +-- Contact: support@coronalabs.com +-- +------------------------------------------------------------------------------ + +local json = require "json" +local lfs = require "lfs" +local pluginCollector = require "CoronaBuilderPluginCollector" + +local debugBuildProcess = os.execute("exit $(defaults read com.coronalabs.Corona_Simulator debugBuildProcess 2>/dev/null || echo 0)") +function log(...) + if debugBuildProcess > 1 then + print(...) + end +end + +local function quoteString( str ) + str = str:gsub('\\', '\\\\') + str = str:gsub('"', '\\"') + return "\"" .. str .. "\"" +end + +local function exec(cmd) + log('Running command', cmd) + if debugBuildProcess < 1 then + cmd = cmd .. ' &> /dev/null' + end + assert(0 == os.execute(cmd)) +end + + +function findTemplate(params) + local template = params.resourceDir .. '/iostemplate/' .. params.template + if lfs.attributes(template, 'mode') == 'file' then + return template + end + template = params.resourceDir .. '/../../../../../../../Corona Simulator.app/Contents/Resources/' .. params.template + if lfs.attributes(template, 'mode') == 'file' then + return template + end + template = params.resourceDir .. '/../../../Corona Simulator.app/Contents/Resources/iostemplate/' .. params.template + if lfs.attributes(template, 'mode') == 'file' then + return template + end +end + +function findBuilder(params) + local builder = params.resourceDir .. '/../MacOS/CoronaBuilder' + if lfs.attributes(builder, 'mode') == 'file' then + return builder + end + builder = params.resourceDir .. '/../../../Native/Corona/mac/bin/CoronaBuilder.app/Contents/MacOS/CoronaBuilder' + if lfs.attributes(builder, 'mode') == 'file' then + return builder + end + builder = params.resourceDir .. '/../../../CoronaBuilder.app/Contents/MacOS/CoronaBuilder' + if lfs.attributes(builder, 'mode') == 'file' then + return builder + end +end + +function findLuac(params) + local luac = params.resourceDir .. '/../../../luac' + if lfs.attributes(luac, 'mode') == 'file' then + return luac + end + luac = params.resourceDir .. '/../../../Native/Corona/mac/bin/luac' + if lfs.attributes(luac, 'mode') == 'file' then + return luac + end +end + +local function getSplashScreen(params) + if params.isAppleTV then + return '_NO_' + end + + local splashScreen = json.decode(params.buildData).splashScreen + if type(splashScreen) ~= 'table' then + return '_YES_' + end + + local enable = true + local image = '_YES_' + if type(splashScreen.enable) == 'boolean' then + enable = splashScreen.enable + end + if type(splashScreen.image) == 'string' then + image = splashScreen.image + end + if type(splashScreen.ios) == 'table' then + if type(splashScreen.ios.enable) == 'boolean' then + enable = splashScreen.ios.enable + end + if type(splashScreen.ios.image) == 'string' then + image = splashScreen.ios.image + end + end + if enable then + return image + else + return '_NO_' + end +end + +function CreateOfflinePackage(params) + log("Building offline package with params", json.prettify(params)) + local template = findTemplate(params) + if not template then + return 'Unable to find template ' .. params.template + end + local builder = findBuilder(params) + if not builder then + return 'Unable to find Corona Builder.' + end + local luac = findLuac(params) + if not luac then + return 'Unable to find luac (lua compiler).' + end + + local splashScreen = getSplashScreen(params) + + local pluginsDir = params.tmpDir .. '/plugins' + params.destinationDirectory = pluginsDir + local result = pluginCollector.collect(params) + if type(result) == 'string' then + return result + end + + local tvOS = params.isAppleTV and 'YES' or 'NO' + + log("Packaging using template", template) + exec('/usr/bin/tar -xvjf ' .. quoteString(template) .. ' -C ' .. quoteString(params.tmpDir) .. '/ --strip-components=2 libtemplate/build_output.sh' ) + exec(quoteString(params.tmpDir .. '/build_output.sh') + .. ' ' .. quoteString(params.tmpDir) -- 1 + .. ' ' .. quoteString(params.inputFile) -- 2 + .. ' ' .. quoteString(template) -- 3 + .. ' ' .. quoteString(params.appName) -- 4 + .. ' ' .. quoteString(params.outputFile) -- 5 + .. ' ' .. quoteString(builder) -- 6 + .. ' ' .. quoteString(luac) -- 7 + .. ' ' .. quoteString(params.appPackage) -- 8 + .. ' ' .. quoteString(params.build) -- 9 + .. ' ' .. quoteString(pluginsDir) -- 10 + .. ' ' .. quoteString(splashScreen) -- 11 + .. ' ' .. quoteString(tvOS) -- 12 + ) + + if lfs.attributes(params.outputFile, 'mode') ~= 'file' then + return 'Build script succeeded but produced no result' + end +end diff --git a/platform/resources/iPhonePackageApp.lua b/platform/resources/iPhonePackageApp.lua index 30f57b478..aa1052fbd 100644 --- a/platform/resources/iPhonePackageApp.lua +++ b/platform/resources/iPhonePackageApp.lua @@ -1257,7 +1257,7 @@ function iPhonePostPackage( params ) -- runScript( "cp "..tmpDir.."/output.zip /tmp/" ) end - setStatus("Unpacking build from server") + setStatus("Unpacking build with plugins") -- The file 'Default-568h@2x.png' is a special case: if there is one in the project, -- don't overwrite it with the one from the template diff --git a/platform/resources/iostemplate/README.md b/platform/resources/iostemplate/README.md new file mode 100644 index 000000000..39fcef132 --- /dev/null +++ b/platform/resources/iostemplate/README.md @@ -0,0 +1,5 @@ + +File named sdk+version, for example `iphoneos13.2.tar.bz` come here. +``` +xcodebuild -showsdks +``` diff --git a/platform/resources/tvosPackageApp.lua b/platform/resources/tvosPackageApp.lua index 012a23041..09fb67d56 100644 --- a/platform/resources/tvosPackageApp.lua +++ b/platform/resources/tvosPackageApp.lua @@ -1499,6 +1499,7 @@ function buildExe( options ) local dstDir = appBundleFileUnquoted local buildDir = dstDir .. '/.build' + runScript('mkdir -p "' .. buildDir .. '"') local pluginsDir = buildDir local dstFrameworksDir = dstDir .. "/Frameworks" @@ -1807,7 +1808,7 @@ function tvosPostPackage( params ) runScript( "unzip -Z -1 "..tmpDir.."/output.zip" ) end - setStatus("Unpacking build from server") + setStatus("Unpacking build with plugins") -- The file 'Default-568h@2x.png' is a special case: if there is one in the project, -- don't overwrite it with the one from the template diff --git a/platform/shared/Rtt_Authorization.cpp b/platform/shared/Rtt_Authorization.cpp index 9ab22a011..1afdc7e2f 100755 --- a/platform/shared/Rtt_Authorization.cpp +++ b/platform/shared/Rtt_Authorization.cpp @@ -53,6 +53,7 @@ const char Authorization::kVersionKey[] = "Version"; const char Authorization::kUsernameKey[] = "Username"; const char Authorization::kRenewalReminderKey[] = "RenewalReminder"; +const char Authorization::kOfflineModeConfirmed[] = "OfflineModeConfirmed"; Authorization::Authorization( const MPlatformServices& services, MAuthorizationDelegate& delegate ) : fServices( services ), @@ -161,7 +162,9 @@ Authorization::Initialize(bool initiateLogin /* = false */) const if (!result) { - if (initiateLogin) + Rtt::String out; + fServices.GetPreference(Rtt::Authorization::kOfflineModeConfirmed, &out); + if (initiateLogin && out.IsEmpty()) { // Display a login window if the user does not have a ticket yet. for (; !result && fDelegate.TicketNotInstalled(*this); result = InitializeTicket()) @@ -682,7 +685,7 @@ Authorization::VerifyTicketOnce() const InitializeTicket(); - if ( Rtt_VERIFY( fTicket ) ) + if ( fTicket ) { result = fTicket->IsAppAllowedToRun(); } diff --git a/platform/shared/Rtt_Authorization.h b/platform/shared/Rtt_Authorization.h index cbe9bc7e9..e5585839f 100644 --- a/platform/shared/Rtt_Authorization.h +++ b/platform/shared/Rtt_Authorization.h @@ -42,6 +42,7 @@ class Authorization static const char kVersionKey[]; static const char kUsernameKey[]; static const char kRenewalReminderKey[]; + static const char kOfflineModeConfirmed[]; public: Authorization( const MPlatformServices& services, MAuthorizationDelegate& delegate ); diff --git a/platform/shared/Rtt_WebServicesSession.cpp b/platform/shared/Rtt_WebServicesSession.cpp index 7e6a69fb9..15d5ae669 100755 --- a/platform/shared/Rtt_WebServicesSession.cpp +++ b/platform/shared/Rtt_WebServicesSession.cpp @@ -306,6 +306,12 @@ GetHexBytes( char* dst, int dstLen, const unsigned char* dataBuffer, size_t len return result; } +bool WebServicesSession::IsOfflineSession() const { + Rtt::String out; + fServices.GetPreference(Rtt::Authorization::kOfflineModeConfirmed, &out); + return !out.IsEmpty(); +} + // This is one version of this interface, see below for another PlatformDictionaryWrapper* diff --git a/platform/shared/Rtt_WebServicesSession.h b/platform/shared/Rtt_WebServicesSession.h index d3a45f56d..78b8c173c 100644 --- a/platform/shared/Rtt_WebServicesSession.h +++ b/platform/shared/Rtt_WebServicesSession.h @@ -92,6 +92,8 @@ class WebServicesSession int BeginBuild( AppPackagerParams * params, const char* inputFile, const char* outputFile ); const char *ErrorMessage() { return fErrorMessage; } + + bool IsOfflineSession() const; private: const MPlatformServices& fServices; diff --git a/platform/tvos/mks3upload b/platform/tvos/mks3upload index c93279a7c..6fa9efd56 100755 --- a/platform/tvos/mks3upload +++ b/platform/tvos/mks3upload @@ -49,13 +49,16 @@ do ARCHIVE="$(pwd)/2100.9999_template_${PLATFORM}_${TVOS_VER}_basic.tar.bz" cp -X ../resources/config_require.lua "$TEMPLATE_DIR/${PLATFORM}/${TVOS_VER}/" + mkdir -p "$TEMPLATE_DIR/${PLATFORM}/${TVOS_VER}/libtemplate/" + cp -X ../../tools/buildsys-ios/libtemplate/build_output.sh "$TEMPLATE_DIR/${PLATFORM}/${TVOS_VER}/libtemplate/" + touch license.ccdata cp -X license.ccdata "$TEMPLATE_DIR/${PLATFORM}/${TVOS_VER}/template.app/" ( cd "$TEMPLATE_DIR/${PLATFORM}/${TVOS_VER}/" || exit # the excludes are common detritus due to a test app in platform/test/assets2 - tar cvjf "${ARCHIVE}" --exclude='CoronaSimLogo-256.png' --exclude='*.jpg' --exclude='Icon*.png' ./template.app ./config_require.lua + tar cvjf "${ARCHIVE}" --exclude='CoronaSimLogo-256.png' --exclude='*.jpg' --exclude='Icon*.png' ./libtemplate ./template.app ./config_require.lua rm config_require.lua @@ -85,3 +88,12 @@ if [ -d $AS2BACKUP ] then mv $AS2BACKUP $ASSETS2 fi + +for TVOS_VER in $TEMPLATE_DIR/appletvos/* +do + TVOS_VER=$(basename "${TVOS_VER}") + + cp "2100.9999_template_appletvos_${TVOS_VER}_basic.tar.bz" "../resources/iostemplate/appletvos_${TVOS_VER}.tar.bz" + cp "2100.9999_template_appletvsimulator_${TVOS_VER}_basic.tar.bz" "../resources/iostemplate/appletvsimulator_${TVOS_VER}.tar.bz" + +done diff --git a/tools/CoronaBuilder/BuilderPluginDownloader.lua b/tools/CoronaBuilder/BuilderPluginDownloader.lua index 439bc333e..7f7345258 100644 --- a/tools/CoronaBuilder/BuilderPluginDownloader.lua +++ b/tools/CoronaBuilder/BuilderPluginDownloader.lua @@ -307,6 +307,7 @@ function fetchDependenciesForDirectories(root, deps, urlSuffix) return 0 end +-- in offline build `user` is nil function DownloadPluginsMain(args, user, buildYear, buildRevision) if args[1] ~= 'download' then print("ERROR: unknows subcommand to 'plugins' command: '" .. tostring(args[1]) .. "'. Only 'download' is currently supported." ) @@ -321,22 +322,22 @@ function DownloadPluginsMain(args, user, buildYear, buildRevision) if args[i] == '--force-load' then table.remove(args, i) forceLoad = true - elseif args[i] == '--android-build' then + elseif args[i] == '--android-build' then -- this switches into android build mode. Emits output instead of downloading anything table.remove(args, i) verbosity = 0 androidBuild = true - elseif args[i] == '--build-data' then + elseif args[i] == '--build-data' then -- build data contains info about additional plugins and metadata table.remove(args, i) buildData = json.decode(io.read('*all')) or {} buildDataPluginEntry = buildData.plugins or {} - elseif args[i] == '--fetch-dependencies' then + elseif args[i] == '--fetch-dependencies' then -- scans directory for dependencies table.remove(args, i) verbosity = 0 fetchDependencies = true - elseif args[i] == '--always-query' then + elseif args[i] == '--always-query' then -- forces always to query for available plugins, for test purposes mostly table.remove(args, i) alwaysQuery = true - elseif args[i] == '--build' then + elseif args[i] == '--build' then --verrides buildYear and buildRevision table.remove(args, i) local build = args[i] table.remove(args, i) @@ -358,7 +359,8 @@ function DownloadPluginsMain(args, user, buildYear, buildRevision) table.remove(args, 1) local root = args[1] table.remove(args, 1) - return fetchDependenciesForDirectories(root, args, ('/%s.%s/%s/'):format(buildYear, buildRevision, platform)) + local urlSuffix = ('/%s.%s/%s/'):format(buildYear, buildRevision, platform) + return fetchDependenciesForDirectories(root, args, urlSuffix) end if type(platform) ~= 'string' then @@ -440,61 +442,50 @@ function DownloadPluginsMain(args, user, buildYear, buildRevision) local addedPluginsToDownload = {} local needsSplashScreenControl = splashScreenImage ~= nil or not splashScreenEnabled if #pluginsToDownload > 0 or alwaysQuery or needsSplashScreenControl then - local authURL = serverBackend .. '/v1/plugins/show/' .. user + local authorisedPlugins = {} - local authorisedPluginsText, msg = builder.fetch(authURL) + if user then + local authURL = serverBackend .. '/v1/plugins/show/' .. user - if not authorisedPluginsText then - print("ERROR: Unable to retrieve authorised plugins list (" .. msg .. ").") - return 1 - end + local authorisedPluginsText, msg = builder.fetch(authURL) - local authPluginsJson = json.decode( authorisedPluginsText ) - if not authPluginsJson then - print("ERROR: Unable to parse authorised plugins list.") - return 1 - end + if not authorisedPluginsText then + print("ERROR: Unable to retrieve authorised plugins list (" .. msg .. ").") + return 1 + end - if authPluginsJson.status ~= 'success' then - print("ERROR: Retrieving authorised plugins was unsuccessful. Info: " .. authorisedPluginsText) - return 1 - end + local authPluginsJson = json.decode( authorisedPluginsText ) + if not authPluginsJson then + print("ERROR: Unable to parse authorised plugins list.") + return 1 + end - if not authPluginsJson.data then - print("ERROR: received empty data for authorised plugins.") - return 1 - end + if authPluginsJson.status ~= 'success' then + print("ERROR: Retrieving authorised plugins was unsuccessful. Info: " .. authorisedPluginsText) + return 1 + end - local authorisedPlugins = {} - for _, ap in pairs(authPluginsJson.data) do -- ap : authorisedPlugin - authorisedPlugins[ tostring(ap['plugin_name']) .. ' ' .. tostring(ap['plugin_developer'])] = ap['status'] + if not authPluginsJson.data then + print("ERROR: received empty data for authorised plugins.") + return 1 + end + + for _, ap in pairs(authPluginsJson.data) do -- ap : authorisedPlugin + authorisedPlugins[ tostring(ap['plugin_name']) .. ' ' .. tostring(ap['plugin_developer'])] = ap['status'] + end end local authErrors = false if needsSplashScreenControl then - local splashStatus = authorisedPlugins["plugin.CoronaSplashControl com.coronalabs"] or 0 - local pluginsDest = "" - if windows then - -- %APPDATA%\Corona Labs\Corona Simulator\NativePlugins\ - pluginsDest = os.getenv('APPDATA') .. '\\Corona Labs' - lfs.mkdir(pluginsDest) - pluginsDest = pluginsDest .. '\\Corona Simulator' - lfs.mkdir(pluginsDest) - pluginsDest = pluginsDest .. '\\NativePlugins\\' - lfs.mkdir(pluginsDest) - else - pluginsDest = os.getenv('HOME') .. '/Library/Application Support/Corona' - lfs.mkdir(pluginsDest) - pluginsDest = pluginsDest .. '/Native Plugins/' - lfs.mkdir(pluginsDest) - end - pluginsDest = pluginsDest .. 'control' - local hasSplashScreenControl = splashStatus > 0 - if needsSplashScreenControl and not hasSplashScreenControl then - print("ERROR: Splash Screen Control plugin could not be validated") - print("ERROR: Activate plugin at: https://marketplace.coronalabs.com/plugin/com.coronalabs/plugin.CoronaSplashControl") - authErrors = true - end + -- alwyas has SPC + -- local splashStatus = authorisedPlugins["plugin.CoronaSplashControl com.coronalabs"] or 0 + -- local hasSplashScreenControl = splashStatus > 0 + -- if needsSplashScreenControl and not hasSplashScreenControl then + -- print("ERROR: Splash Screen Control plugin could not be validated") + -- print("ERROR: Activate plugin at: https://marketplace.coronalabs.com/plugin/com.coronalabs/plugin.CoronaSplashControl") + -- authErrors = true + -- end + local hasSplashScreenControl = true if needsSplashScreenControl and hasSplashScreenControl then print("SPLASH\t" .. tostring(splashScreenImage)) end @@ -516,12 +507,13 @@ function DownloadPluginsMain(args, user, buildYear, buildRevision) print("ERROR: empty custom URL for: " .. plugin .. " (" .. developer .. ")") authErrors = true else - local status = authorisedPlugins['plugin.selfHostedPlugins com.coronalabs'] or 0 - if status == 0 then - print("ERROR: Self-Hosted plugins was not activated.") - print("ERROR: More information at: https://marketplace.coronalabs.com/service/self-hosted-plugins") - return 1 - end + -- always allow Self hosted plugins + -- local status = authorisedPlugins['plugin.selfHostedPlugins com.coronalabs'] or 0 + -- if status == 0 then + -- print("ERROR: Self-Hosted plugins was not activated.") + -- print("ERROR: More information at: https://marketplace.coronalabs.com/service/self-hosted-plugins") + -- return 1 + -- end end else local status = authorisedPlugins[plugin .. ' ' .. developer] or 0 @@ -615,13 +607,39 @@ function DownloadPluginsMain(args, user, buildYear, buildRevision) end if androidBuild then - local downloadInfoText, msg = builder.fetch(serverBackend.. "/v1/buildid/native/" .. user) - if not downloadInfoText then - print("ERROR: unable to fetch build ID: ", msg ) - return 1 + if user then + local downloadInfoText, msg = builder.fetch(serverBackend.. "/v1/buildid/native/" .. user) + if not downloadInfoText then + print("ERROR: unable to fetch build ID: ", msg ) + return 1 + end + print("BUILD\t" .. downloadInfoText) + else + print("BUILD\tOffline") end - print("BUILD\t" .. downloadInfoText) end return 0 end + + +function DownloadAndroidOfflinePlugins(args, user, buildYear, buildRevision) + local buildData = json.decode(io.read('*all')) + assert(buildData) + buildData.build = buildData.build or buildRevision + buildData.user = buildData.user or user + for i=1, #args do + local k,v = args[i]:match('(.+)=(.+)') + if k and v then + buildData[k] = v + end + end + + local pluginCollector = require "CoronaBuilderPluginCollector" + local result = pluginCollector.collect(buildData) + if type(result) == 'string' then + print("ERROR: occured while collecting plugins for Android. ", result) + return 1 + end + return 0 +end \ No newline at end of file diff --git a/tools/CoronaBuilder/Rtt_CoronaBuilder.cpp b/tools/CoronaBuilder/Rtt_CoronaBuilder.cpp index aab9de12b..f58c553b1 100644 --- a/tools/CoronaBuilder/Rtt_CoronaBuilder.cpp +++ b/tools/CoronaBuilder/Rtt_CoronaBuilder.cpp @@ -226,6 +226,9 @@ CoronaBuilder::CoronaBuilder( // fAuthorizerDelegate( NULL ), // fAuthorizer( NULL ) { + Rtt::String offlineModeStr; + services.GetPreference(Rtt::Authorization::kOfflineModeConfirmed, &offlineModeStr); + fOfflineMode = !offlineModeStr.IsEmpty(); lua_pushlightuserdata( fL, this ); Lua::RegisterModuleLoader( fL, "builder", LuaLibBuilder::Open, 1 ); @@ -448,7 +451,7 @@ CoronaBuilder::Main( int argc, const char *argv[] ) bool sign = (strncmp(argv[2], "sign", 4) == 0); - if ( sign ) + if ( sign && !fOfflineMode ) { if(! CanCustomizeSplashScreen(platformName, bundleID)) { @@ -730,6 +733,10 @@ CoronaBuilder::VerifyPermission( AuthorizationTicket::DisplayStringForSubscription( sub ) ); } } + else if(fOfflineMode) + { + return true; + } else { fprintf( stderr, "error: CoronaBuilder: %s\n", errorMsg ); @@ -749,6 +756,14 @@ CoronaBuilder::Authorize( const char *inUsr, const char *inPwd ) const exit( 1 ); } + + if(strcmp(inUsr, "offline") == 0) { + fServices.SetPreference(Authorization::kOfflineModeConfirmed, "offline"); + return kNoError; + } else { + fServices.SetPreference(Authorization::kOfflineModeConfirmed, NULL); + } + if (inPwd == NULL) { fprintf(stderr, "error: CoronaBuilder: missing password\n"); @@ -1254,6 +1269,7 @@ CoronaBuilder::Build( const BuildParams& params ) const if ( VerifyPermission( delegate, kBuildPermission ) ) { AppPackagerFactory factory( fServices ); + WebServicesSession session( fServices ); PlatformAppPackager *packager = params.CreatePackager( factory, targetPlatform ); AppPackagerParams *appParams = params.CreatePackagerParams( factory, targetPlatform ); @@ -1264,16 +1280,22 @@ CoronaBuilder::Build( const BuildParams& params ) const const char *usr = fUsr.GetString(); String encryptedPassword; - fServices.GetPreference( usr, &encryptedPassword ); + if(usr) { + fServices.GetPreference( usr, &encryptedPassword ); + } - if ( fServices.IsInternetAvailable() ) + if ( !usr || fServices.IsInternetAvailable() ) { - WebServicesSession session( fServices ); - int code = session.LoginWithEncryptedPassword( - WebServicesSession::CoronaServerUrl(fServices), - usr, - encryptedPassword.GetString() ); + int code = WebServicesSession::kLoginError; + if(session.IsOfflineSession()) { + code = WebServicesSession::kNoError; + } else if(usr) { + code = session.LoginWithEncryptedPassword( + WebServicesSession::CoronaServerUrl(fServices), + usr, + encryptedPassword.GetString() ); + } if ( WebServicesSession::kNoError == code ) { diff --git a/tools/CoronaBuilder/Rtt_CoronaBuilder.h b/tools/CoronaBuilder/Rtt_CoronaBuilder.h index a606f86a1..cc9a4ac2f 100644 --- a/tools/CoronaBuilder/Rtt_CoronaBuilder.h +++ b/tools/CoronaBuilder/Rtt_CoronaBuilder.h @@ -98,6 +98,7 @@ class CoronaBuilder mutable String fUsr; String fCommandPath; lua_State *fL; + bool fOfflineMode; }; // ---------------------------------------------------------------------------- diff --git a/tools/CoronaBuilder/Rtt_DownloadPluginsMain.cpp b/tools/CoronaBuilder/Rtt_DownloadPluginsMain.cpp index 153b530dc..e7f488f46 100644 --- a/tools/CoronaBuilder/Rtt_DownloadPluginsMain.cpp +++ b/tools/CoronaBuilder/Rtt_DownloadPluginsMain.cpp @@ -15,6 +15,8 @@ // ---------------------------------------------------------------------------- extern "C" { int luaopen_lfs (lua_State *L); +int luaopen_socket_core (lua_State *L); +int luaopen_mime_core(lua_State *L); } namespace Rtt @@ -24,19 +26,46 @@ int luaload_json(lua_State *L); int luaload_dkjson(lua_State *L); int luaload_BuilderPluginDownloader(lua_State *L); int luaload_CoronaPListSupport(lua_State *L); +int luaload_CoronaBuilderPluginCollector(lua_State *L); +extern int luaload_luasocket_socket(lua_State *L); +extern int luaload_luasocket_ftp(lua_State *L); +extern int luaload_luasocket_headers(lua_State *L); +extern int luaload_luasocket_http(lua_State *L); +extern int luaload_luasocket_url(lua_State *L); +extern int luaload_luasocket_mime(lua_State *L); +extern int luaload_luasocket_ltn12(lua_State *L); DownloadPluginsMain::DownloadPluginsMain(lua_State *L) : fL( L ) { + Lua::RegisterModuleLoader( L, "CoronaBuilderPluginCollector", Lua::Open< luaload_CoronaBuilderPluginCollector >); Lua::DoBuffer( L, &luaload_BuilderPluginDownloader, NULL); } int DownloadPluginsMain::Run(int argc, const char* args[], const char* usr) { lua_State *L = fL; - - lua_getglobal(L, "DownloadPluginsMain"); + + if(argc >= 2 && strcmp("--android-offline-plugins", args[1]) == 0) { + lua_getglobal(L, "DownloadAndroidOfflinePlugins"); + args++; + argc--; + + Lua::RegisterModuleLoader( L, "socket.core", luaopen_socket_core ); + Lua::RegisterModuleLoader( L, "socket", Lua::Open< luaload_luasocket_socket > ); + Lua::RegisterModuleLoader( L, "socket.ftp", Lua::Open< luaload_luasocket_ftp > ); + Lua::RegisterModuleLoader( L, "socket.headers", Lua::Open< luaload_luasocket_headers > ); + Lua::RegisterModuleLoader( L, "socket.http", Lua::Open< luaload_luasocket_http > ); + Lua::RegisterModuleLoader( L, "socket.url", Lua::Open< luaload_luasocket_url > ); + Lua::RegisterModuleLoader( L, "mime.core", luaopen_mime_core ); + Lua::RegisterModuleLoader( L, "mime", Lua::Open< luaload_luasocket_mime > ); + Lua::RegisterModuleLoader( L, "ltn12", Lua::Open< luaload_luasocket_ltn12 > ); + } + else { + lua_getglobal(L, "DownloadPluginsMain"); + } + Rtt_ASSERT(lua_type(L, -1) == LUA_TFUNCTION); lua_createtable(L, argc, 0); for(int i = 0; i