├── .gitignore ├── .idea ├── .name ├── ant.xml ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── highlighting.xml ├── misc.xml ├── modules.xml ├── scala_compiler.xml ├── scopes │ └── scope_settings.xml └── vcs.xml ├── README.md ├── install-genymotion.scala ├── install.scala ├── scala-libs-for-android-emulator.iml └── scala ├── 2.10.1 ├── lib │ ├── akka-actors.jar │ ├── scala-actors-migration.jar │ ├── scala-actors.jar │ ├── scala-collection.jar │ ├── scala-immutable.jar │ ├── scala-library.jar │ ├── scala-mutable.jar │ └── scala-reflect.jar └── permissions │ ├── scala.2-10-1.actors-migration.xml │ ├── scala.2-10-1.actors.xml │ ├── scala.2-10-1.akka-actors.xml │ ├── scala.2-10-1.collection.xml │ ├── scala.2-10-1.immutable.xml │ ├── scala.2-10-1.library.xml │ ├── scala.2-10-1.mutable.xml │ └── scala.2-10-1.reflect.xml └── 2.9.2 ├── lib ├── scala-actors.jar ├── scala-collection.jar ├── scala-immutable.jar ├── scala-library.jar └── scala-mutable.jar └── permissions ├── scala.2-9-2.actors.xml ├── scala.2-9-2.collection.xml ├── scala.2-9-2.immutable.xml ├── scala.2-9-2.library.xml └── scala.2-9-2.mutable.xml /.gitignore: -------------------------------------------------------------------------------- 1 | # IntelliJ specific 2 | .idea/workspace.xml 3 | atlassian-ide-plugin.xml 4 | dist/* 5 | out 6 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | scala-libs-for-android -------------------------------------------------------------------------------- /.idea/ant.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/highlighting.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | http://www.w3.org/1999/xhtml 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/scala_compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/scopes/scope_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | scala-libs-for-android-emulator provides you an easy way to preinstall the scala libraries on your android emulator (from the SDK or genymotion), so 4 | to speed up your development as you do not need to include the scala libs into your debug builds then. I've tested this 5 | also on current emulator images (ICS and Jelly Bean, either ARM or x86 with hardware accelerated emulation). 6 | 7 | **Disclaimer:** I've only tested the script on Windows, but it should also run on Mac OS X and other Unix-platforms. 8 | 9 | ## The Problem 10 | 11 | If you are -- like me -- tired of writing verbose Java code, but want to develop for the Android platform, then Scala is 12 | a great choice. Unfortunately using the normal build process in conjunction with Scala is no good choice, as in each 13 | build the full scala libraries need to be processed, what takes quite a few minutes: You either have to feed the whole 14 | scala library to the `dx` tool (which converts java byte code to dalvik vm code) or the other choice is to use pro guard 15 | to remove all unreferenced artifacts, but this analysis requires some time. 16 | 17 | ## The Solution 18 | 19 | But there is a solution to bring down the turn around times to the same level as if you were using java: Preinstall 20 | the scala libraries on your emulator, so that you can exclude them from your debug build (you will still need to include 21 | them into your release!). 22 | 23 | I've found a few tutorials of how to modify the emulator's ramdisk and to include the scala libs into the `BOOTCLASSPATH`. 24 | This worked fine for emulators up to API level 10 (android 2.3.3), but when I tried it on API level 15 (ICS) or 25 | 16 (Jelly Bean) I've got a boot loop and the emulator never came up. 26 | 27 | While searching around in the web for a solution I've stumbled upon 28 | [jbrechtel's Android-Scala-Installer](https://github.com/jbrechtel/Android-Scala-Installer) which is an android app that 29 | preinstalls the scala libs on a rooted android device and uses another idea: Instead of modifying the `BOOTCLASSPATH` 30 | it installs the scala libs as system libraries which can then be used by your app using `` statements 31 | in the `AndroidManifest.xml`. I've tried this on the emulator and voila: It was working. 32 | 33 | To easily patch an emulator I then simply created this scala script which does everything for us -- from starting up 34 | the emulator to installing the libraries, adjusting the system image and putting it into the right AVD directory. 35 | All you need to do is to start the script and afterwards restart the emulator (cannot be automated on Windows). 36 | 37 | If you are intersted in the details, of how this works, simply take a look into the scala script... 38 | 39 | # Usage 40 | 41 | Using the script is easy: 42 | * Download the whole directory tree of this project to your system (either by cloning the repository or downloading it a ZIP) 43 | * Ensure that your `PATH` contains the scala compiler and the android tools 44 | * call the script without any parameters to get usage help and a list of available scala versions and AVDs: 45 | ``` 46 | scala install.scala 47 | ``` 48 | 49 | On my system the output looks like this: 50 | ```batch 51 | USAGE: scala install.scala 52 | 53 | Installs the scala libraries in the specified emulator to reduce turnaround 54 | times when developing with scala for android. 55 | 56 | the name of the android virtual device to install the libs on 57 | possible values: 58 | 4.0.3_HVGA, 4.1_HVGA, 4.2_Galaxy-Nexus 59 | the scala version to install. possible values: 60 | 2.10.1, 2.9.2 61 | ``` 62 | 63 | * Call the script with the adequate parameters and wait until it's done (what may take a few minutes) 64 | * Add the `` statements outputed at the end of the script run to your `AndroidManifest.xml` 65 | * Enjoy developing android with scala! 66 | 67 | ## Support for Genymotion emulator 68 | 69 | Genymotion is a great alternative to the standard emulator, as it is very fast and the images include Google play and all it's services. There is a dedicated script for installing scala into the currently running genymotion device: ```scala install-genymotion.scala```. The script requires that there is exactly one genymotion emulator currently running and that you configured genymotion for ADB usage. Call it without further paramters to retrieve usage help -- this script only requires you to specify the requested scala version. 70 | 71 | # Supported Scala Versions 72 | 73 | Currently the script supports the following scala versions: 74 | * 2.9.2 75 | * 2.10.1 76 | 77 | It is quite easy to support additional versions: 78 | * Copy your `scala-library.jar` and split it up into several smaller jars based on a package level, so that they can 79 | be processed using the `dx` tool. 80 | * Use the `dx` tool to convert each of the resulting JVM-JARs into Dalvik-JARs as followed: 81 | ```batch 82 | dx -JXmx1024M -JXms1024M -JXss4M --no-optimize --debug --dex 83 | --output=\tmp\scala\android\scala-library.jar \tmp\scala\jvm\scala-library.jar 84 | ``` 85 | * Clone an existing version directory inside the `scala` folder 86 | * Replace the JARs with your newly generated ones 87 | * Adjust the permission files for the version accordingly 88 | 89 | That's it. **And please don't forget to provide me the new version, so that I can include it into the distribution here 90 | at GitHub** -------------------------------------------------------------------------------- /install-genymotion.scala: -------------------------------------------------------------------------------- 1 | import java.io.{FileReader, File} 2 | import java.nio.file.Files 3 | import scala.language.implicitConversions 4 | import scala.io.Source 5 | import sys.process.Process 6 | 7 | /** 8 | * Create an instance and call `install` to install the specified scala version on the specified emulator device. 9 | * @param scalaVersion the version number of the scala version to be installed 10 | */ 11 | private class Installer(private val scalaVersion: String) { 12 | 13 | /** 14 | * Indicates a failed command line execution (e.g. the process returned an exit code != 0). 15 | * @param command the command that failed 16 | * @param exitCode the exit code returned by the command 17 | */ 18 | private class ExecutionFailedException(val command: String, val exitCode: Int) 19 | extends Exception("Command '" + command + "' failed with exit code " + exitCode) 20 | 21 | implicit def file2Path(file: File) = file.toPath 22 | 23 | /** 24 | * Performs the installation. 25 | */ 26 | def install() { 27 | println("Installing Scala " + scalaVersion + " for currently runny genymotion device") 28 | 29 | makeSystemPartitionWritable() 30 | pushScalaLibrary() 31 | pushPermissions() 32 | makeSystemPartitionReadable() 33 | done() 34 | } 35 | 36 | // 37 | // install 38 | // 39 | 40 | private def makeSystemPartitionWritable() { 41 | sudo("mount -o remount,rw /system", "making system partition writable") 42 | } 43 | 44 | private def makeSystemPartitionReadable() { 45 | sudo("mount -o remount,ro /system", "making system partition readable") 46 | } 47 | 48 | private def pushScalaLibrary() { 49 | val targetDir = "/system/framework/scala/" + scalaVersion + "/" 50 | sudo("rm -r " + targetDir, "removing existing Scala library") 51 | sudo("mkdir -p " + targetDir, "creating Scala library directory") 52 | 53 | printProgressHint("pushing Scala " + scalaVersion + " library") 54 | new File("scala/" + scalaVersion + "/lib").listFiles().filter(!_.isDirectory).foreach(pushFile(_, targetDir)) 55 | } 56 | 57 | private def permissionFiles = new File("scala/" + scalaVersion + "/permissions").listFiles() 58 | 59 | private def pushPermissions() { 60 | val targetDir = "/system/etc/permissions/" 61 | printProgressHint("creating permission files") 62 | sudo("chmod 777 /system/etc/permissions", "Allowing write access to /system/etc/permissions") 63 | permissionFiles.foreach(pushFile(_, targetDir)) 64 | sudo("chmod 755 /system/etc/permissions", "Permitting write access to /system/etc/permissions") 65 | } 66 | 67 | private def done() { 68 | println( 69 | """ 70 | |You are done now! 71 | | 72 | |Restart your emulator and the Scala libraries will be available. 73 | | 74 | |Add the following library imports to your AndroidManifest.xml: 75 | """.stripMargin 76 | ) 77 | permissionFiles.map(file => file.getName.substring(0, file.getName.lastIndexOf('.'))).foreach{fileName => 78 | println("""""") 79 | } 80 | } 81 | 82 | // 83 | // helper methods 84 | // 85 | 86 | /** 87 | * Executes the specified command line and optionally prints a hint to the console. 88 | * @param command the command line to be executed 89 | * @param output the hint to be presented to the user or an empty string if no hint should be shown 90 | * @throws ExecutionFailedException if the command's return code is != 0 91 | */ 92 | private def execute(command: String, output: String = "") { 93 | if (!output.isEmpty) 94 | printProgressHint(output) 95 | printCommand(command) 96 | 97 | val exitCode = Process(command).! 98 | if (exitCode != 0) 99 | throw new ExecutionFailedException(command, exitCode) 100 | } 101 | 102 | /** 103 | * Executes the specified adb command and optionally prints a hint to the console. 104 | * @param command the adb command to be executed 105 | * @param output the hint to be presented to the user or an empty string if no hint should be shown 106 | * @throws ExecutionFailedException if the command's return code is != 0 107 | */ 108 | private def adb(command: String, output: String = "") { 109 | execute("adb " + command, output) 110 | } 111 | 112 | /** 113 | * Executes the specified command line in the emulator's shell and optionally prints a hint to the console. 114 | * @param command the command to be executed in the emulator's shell 115 | * @param output the hint to be presented to the user or an empty string if no hint should be shown 116 | * @throws ExecutionFailedException if the command's return code is != 0 117 | */ 118 | private def adbShell(command: String, output: String = "") { 119 | adb("shell " + command, output) 120 | } 121 | 122 | /** 123 | * Executes the specified command line in the emulator's shell as a sudo command and optionally prints a hint to the console. 124 | * @param command the command to be executed in the emulator's shell 125 | * @param output the hint to be presented to the user or an empty string if no hint should be shown 126 | * @throws ExecutionFailedException if the command's return code is != 0 127 | */ 128 | private def sudo(command: String, output: String = "") { 129 | adbShell("su -c \"" + command + "\"", output) 130 | } 131 | 132 | /** 133 | * Pushes the specified file into the emulator. 134 | * @param srcFile the local file to be pushed 135 | * @param target the target path of the file in the emulator's file system 136 | * @throws ExecutionFailedException if adb's return code is != 0 137 | */ 138 | private def pushFile(srcFile: File, target: String) { 139 | adb("push \"" + srcFile.getAbsolutePath + "\" " + target) 140 | } 141 | 142 | /** 143 | * Prints a hint for the user into the console. 144 | * @param hint the hint to be presented to the user. 145 | */ 146 | private def printProgressHint(hint: String) { 147 | println() 148 | println("==> " + hint) 149 | } 150 | 151 | /** 152 | * Prints the currently executed command into the console. 153 | * @param command the command to be shown to the user. 154 | */ 155 | private def printCommand(command: String) { 156 | println("# " + command) 157 | } 158 | } 159 | 160 | /** 161 | * Processes and validates the command line arguments and -- if successful -- starts the installation. 162 | */ 163 | private object Installer { 164 | /** 165 | * Indicates an invalid command line argument. A user readable error message is provided via `getMessage`. 166 | * @param name the name of the invalid argument as known by the user 167 | * @param value the invalid value 168 | * @param possibleValues the list of allowed values 169 | */ 170 | private class InvalidArgumentException( 171 | private val name: String, 172 | private val value: String, 173 | private val possibleValues: Set[_] 174 | ) 175 | extends Exception(name + ": invalid value '" + value + "'. Possible values are: " + makePossibleValuesString(possibleValues)) 176 | 177 | private lazy val availableScalaVersions = new File("scala").list().toSet 178 | 179 | /** 180 | * Processes and validates the command line arguments and -- if successful -- starts the installation. 181 | * @param args the command line arguments 182 | */ 183 | def main(args: Array[String]) { 184 | try { 185 | args match { 186 | case Array(scalaVersion) => 187 | new Installer(validateScalaVersion(scalaVersion)).install() 188 | case _ => printUsageHelp() 189 | } 190 | } catch { 191 | case ex: InvalidArgumentException => 192 | println() 193 | println("ERROR: " + ex.getMessage) 194 | printUsageHelp() 195 | case ex: Exception => 196 | println(ex) 197 | printUsageHelp() 198 | } 199 | } 200 | 201 | private def validateArgument(name: String, value: String, possibleValues: Set[String]) = 202 | if (possibleValues.contains(value)) value else throw new InvalidArgumentException(name, value, possibleValues) 203 | 204 | private def validateScalaVersion(scalaVersion: String) = 205 | validateArgument("scala-version", scalaVersion, availableScalaVersions) 206 | 207 | /** 208 | * Formats a list of possible values for a command line argument, so that it can be presented to the user. 209 | * @param possibleValues the list of possible values. 210 | * @return a string representing the list of possible values. 211 | */ 212 | private def makePossibleValuesString(possibleValues: Set[_]) = possibleValues.mkString(", ") 213 | 214 | /** 215 | * Prints out usage help. 216 | */ 217 | private def printUsageHelp() { 218 | println(( 219 | """ 220 | |USAGE: scala install-genymotion.scala 221 | | 222 | |Installs the scala libraries in the currently running genymotion device to 223 | |reduce turnaround times when developing with scala for android. 224 | | 225 | | the scala version to install. possible values: 226 | | """ + makePossibleValuesString(availableScalaVersions) + """ 227 | """).stripMargin) 228 | } 229 | 230 | } 231 | 232 | Installer.main(args) -------------------------------------------------------------------------------- /install.scala: -------------------------------------------------------------------------------- 1 | import java.io.{FileReader, File} 2 | import java.nio.file.Files 3 | import scala.language.implicitConversions 4 | import scala.io.Source 5 | import sys.process.Process 6 | 7 | /** 8 | * Possible target platform 9 | */ 10 | private object TargetPlatform extends Enumeration { 11 | type Platform = Value 12 | val arm, x86 = Value 13 | } 14 | 15 | /** 16 | * Represents a device specification 17 | * @param name the name of the device as used by android's `emulator` command 18 | * @param platform the platform of the device 19 | */ 20 | private sealed case class Device(name: String, platform: TargetPlatform.Platform) { 21 | lazy val dir = System.getProperty("user.home") + """/.android/avd/""" + name + """.avd""" 22 | } 23 | 24 | /** 25 | * Supports easy creation of `Device`s based on the specification printed out by `android list avd`. 26 | */ 27 | private object Device { 28 | private val SpecPattern = """(?s).*Name: (\S+).*ABI: (\S+).*""".r 29 | private val Arm = """arm.*""".r 30 | private val X86 = ".*x86".r 31 | 32 | /** 33 | * Creates a `Device`. 34 | * @param spec specification of a device as printed out by `android list avd` 35 | * @return the created device 36 | * @throws IllegalArgumentException if the platform of the device is unknown 37 | */ 38 | def apply(spec: String) = { 39 | spec match { 40 | case SpecPattern(name, platform) => new Device(name, platform match { 41 | case Arm() => TargetPlatform.arm 42 | case X86() => TargetPlatform.x86 43 | case abi => throw new IllegalArgumentException("Unknown target platform '" + abi + "' for device '" + name + "'") 44 | }) 45 | } 46 | } 47 | } 48 | 49 | /** 50 | * Create an instance and call `install` to install the specified scala version on the specified emulator device. 51 | * @param device the name of the `Device` to install scala on 52 | * @param scalaVersion the version number of the scala version to be installed 53 | */ 54 | private class Installer( 55 | private val device: Device, 56 | private val scalaVersion: String) { 57 | private val systemImage = new File(device.dir, "system.img") 58 | 59 | /** 60 | * Indicates a failed command line execution (e.g. the process returned an exit code != 0). 61 | * @param command the command that failed 62 | * @param exitCode the exit code returned by the command 63 | */ 64 | private class ExecutionFailedException(val command: String, val exitCode: Int) 65 | extends Exception("Command '" + command + "' failed with exit code " + exitCode) 66 | 67 | implicit def file2Path(file: File) = file.toPath 68 | 69 | /** 70 | * Performs the installation. 71 | */ 72 | def install() { 73 | println("Installing Scala " + scalaVersion + " for " + device.name + " (" + device.platform + ")") 74 | 75 | prepareSystemImage() 76 | val emulatorProcess = startEmulator(systemImage) 77 | waitForEmulator() 78 | makeSystemPartitionWritable() 79 | pushScalaLibrary() 80 | pushPermissions() 81 | done() 82 | emulatorProcess.destroy() 83 | } 84 | 85 | // 86 | // install 87 | // 88 | 89 | private def getSystemImageSource: File = { 90 | val avdConfigFile = new File(device.dir, "hardware-qemu.ini") 91 | val avdConfig = Source.fromFile(avdConfigFile) 92 | val kernelPath = avdConfig.getLines() find { _.startsWith("kernel.path") } match { 93 | case None => throw new IllegalArgumentException("Haven't found kernel.path in " + avdConfigFile.getPath) 94 | case Some(pathDef) => new File(pathDef.split('=')(1).trim) 95 | } 96 | new File(kernelPath.getParentFile, "system.img") 97 | } 98 | 99 | private def prepareSystemImage() { 100 | val systemImage = new File(device.dir, "system.img") 101 | if (!systemImage.exists) { 102 | printProgressHint("copying system image from " + getSystemImageSource.getPath + " to " + systemImage.getPath) 103 | Files.copy(getSystemImageSource, systemImage) 104 | } else { 105 | printProgressHint("using existing image file at " + systemImage.getPath) 106 | } 107 | } 108 | 109 | private def startEmulator(systemImage: File) = { 110 | val targetImageSize = systemImage.length + 50 * 1024 * 1024 // assume about 50MB for the scala stuff 111 | val command = "emulator -avd " + device.name + " -partition-size 1024 -no-boot-anim -no-snapshot " + 112 | "-qemu -nand system,size=0x" + targetImageSize.toHexString + ",file=" + systemImage.getAbsolutePath 113 | printProgressHint("starting emulator ...") 114 | printCommand(command) 115 | Process(command).run() 116 | } 117 | 118 | private def waitForEmulator() { 119 | adb("wait-for-device", "waiting for emulator ...") 120 | } 121 | 122 | private def makeSystemPartitionWritable() { 123 | adbShell("mount -o remount,rw /system", "making system partition writable") 124 | } 125 | 126 | private def pushScalaLibrary() { 127 | val targetDir = "/system/framework/scala/" + scalaVersion + "/" 128 | adbShell("rm -r " + targetDir, "removing existing Scala library") 129 | adbShell("mkdir -p " + targetDir, "creating Scala library directory") 130 | 131 | printProgressHint("pushing Scala " + scalaVersion + " library") 132 | new File("scala/" + scalaVersion + "/lib").listFiles().filter(!_.isDirectory).foreach(pushFile(_, targetDir)) 133 | } 134 | 135 | private def permissionFiles = new File("scala/" + scalaVersion + "/permissions").listFiles() 136 | 137 | private def pushPermissions() { 138 | val targetDir = "/system/etc/permissions/" 139 | printProgressHint("creating permission files") 140 | permissionFiles.foreach(pushFile(_, targetDir)) 141 | } 142 | 143 | private def done() { 144 | println( 145 | """ 146 | |You are done now! 147 | | 148 | |Restart your emulator and the Scala libraries will be available. 149 | | 150 | |Add the following library imports to your AndroidManifest.xml: 151 | """.stripMargin 152 | ) 153 | permissionFiles.map(file => file.getName.substring(0, file.getName.lastIndexOf('.'))).foreach{fileName => 154 | println("""""") 155 | } 156 | } 157 | 158 | // 159 | // helper methods 160 | // 161 | 162 | /** 163 | * Executes the specified command line and optionally prints a hint to the console. 164 | * @param command the command line to be executed 165 | * @param output the hint to be presented to the user or an empty string if no hint should be shown 166 | * @throws ExecutionFailedException if the command's return code is != 0 167 | */ 168 | private def execute(command: String, output: String = "") { 169 | if (!output.isEmpty) 170 | printProgressHint(output) 171 | printCommand(command) 172 | 173 | val exitCode = Process(command).! 174 | if (exitCode != 0) 175 | throw new ExecutionFailedException(command, exitCode) 176 | } 177 | 178 | /** 179 | * Executes the specified adb command and optionally prints a hint to the console. 180 | * @param command the adb command to be executed 181 | * @param output the hint to be presented to the user or an empty string if no hint should be shown 182 | * @throws ExecutionFailedException if the command's return code is != 0 183 | */ 184 | private def adb(command: String, output: String = "") { 185 | execute("adb " + command, output) 186 | } 187 | 188 | /** 189 | * Executes the specified command line in the emulator's shell and optionally prints a hint to the console. 190 | * @param command the command to be executed in the emulator's shell 191 | * @param output the hint to be presented to the user or an empty string if no hint should be shown 192 | * @throws ExecutionFailedException if the command's return code is != 0 193 | */ 194 | private def adbShell(command: String, output: String = "") { 195 | adb("shell " + command, output) 196 | } 197 | 198 | /** 199 | * Pushes the specified file into the emulator. 200 | * @param srcFile the local file to be pushed 201 | * @param target the target path of the file in the emulator's file system 202 | * @throws ExecutionFailedException if adb's return code is != 0 203 | */ 204 | private def pushFile(srcFile: File, target: String) { 205 | val systemImageSize = systemImage.length 206 | adb("push \"" + srcFile.getAbsolutePath + "\" " + target) 207 | 208 | // wait until the change is written back to the system image file 209 | while (systemImage.length() == systemImageSize) { 210 | Thread.sleep(1000) 211 | } 212 | } 213 | 214 | /** 215 | * Prints a hint for the user into the console. 216 | * @param hint the hint to be presented to the user. 217 | */ 218 | private def printProgressHint(hint: String) { 219 | println() 220 | println("==> " + hint) 221 | } 222 | 223 | /** 224 | * Prints the currently executed command into the console. 225 | * @param command the command to be shown to the user. 226 | */ 227 | private def printCommand(command: String) { 228 | println("# " + command) 229 | } 230 | } 231 | 232 | /** 233 | * Processes and validates the command line arguments and -- if successful -- starts the installation. 234 | */ 235 | private object Installer { 236 | /** 237 | * Indicates an invalid command line argument. A user readable error message is provided via `getMessage`. 238 | * @param name the name of the invalid argument as known by the user 239 | * @param value the invalid value 240 | * @param possibleValues the list of allowed values 241 | */ 242 | private class InvalidArgumentException( 243 | private val name: String, 244 | private val value: String, 245 | private val possibleValues: Set[_] 246 | ) 247 | extends Exception(name + ": invalid value '" + value + "'. Possible values are: " + makePossibleValuesString(possibleValues)) 248 | 249 | private lazy val isWindows = System.getProperties.get("os.name").toString.toLowerCase.contains("windows") 250 | private lazy val availableScalaVersions = new File("scala").list().toSet 251 | private lazy val availableDevicesByName = getAvailableDevicesByName 252 | private lazy val availableDeviceNames = availableDevicesByName.keySet 253 | 254 | /** 255 | * Processes and validates the command line arguments and -- if successful -- starts the installation. 256 | * @param args the command line arguments 257 | */ 258 | def main(args: Array[String]) { 259 | try { 260 | args match { 261 | case Array(deviceName, scalaVersion) => 262 | new Installer(validateDevice(deviceName), validateScalaVersion(scalaVersion)).install() 263 | case _ => printUsageHelp() 264 | } 265 | } catch { 266 | case ex: InvalidArgumentException => 267 | println() 268 | println("ERROR: " + ex.getMessage) 269 | printUsageHelp() 270 | case ex: Exception => 271 | println(ex) 272 | printUsageHelp() 273 | } 274 | } 275 | 276 | private def validateArgument(name: String, value: String, possibleValues: Set[String]) = 277 | if (possibleValues.contains(value)) value else throw new InvalidArgumentException(name, value, possibleValues) 278 | private def validateScalaVersion(scalaVersion: String) = 279 | validateArgument("scala-version", scalaVersion, availableScalaVersions) 280 | private def validateDevice(deviceName: String) = availableDevicesByName.get(deviceName) match { 281 | case Some(device) => device 282 | case None => throw new InvalidArgumentException("avd", deviceName, availableDeviceNames) 283 | } 284 | 285 | /** 286 | * The available `Device`s mapped by their names as provided by the `android list avd` command. 287 | * @return available `Device`s mapped by their names 288 | */ 289 | private def getAvailableDevicesByName = { 290 | val lines = shellCommand("android list avd").lines.seq.mkString("\n") 291 | val specs = lines.split("---------") 292 | specs.map(Device(_)).map{device => (device.name, device)}.toMap 293 | } 294 | 295 | private def shellCommand(cmd: String) = { 296 | val fullCommand = if (isWindows) s"cmd /c $cmd" else cmd 297 | Process(fullCommand) 298 | } 299 | 300 | /** 301 | * Formats a list of possible values for a command line argument, so that it can be presented to the user. 302 | * @param possibleValues the list of possible values. 303 | * @return a string representing the list of possible values. 304 | */ 305 | private def makePossibleValuesString(possibleValues: Set[_]) = possibleValues.mkString(", ") 306 | 307 | /** 308 | * Prints out usage help. 309 | */ 310 | private def printUsageHelp() { 311 | println(( 312 | """ 313 | |USAGE: scala install.scala 314 | | 315 | |Installs the scala libraries in the specified emulator to reduce turnaround 316 | |times when developing with scala for android. 317 | | 318 | | the name of the android virtual device to install the libs on 319 | | possible values: 320 | | """ + makePossibleValuesString(availableDeviceNames) + """ 321 | | the scala version to install. possible values: 322 | | """ + makePossibleValuesString(availableScalaVersions) + """ 323 | """).stripMargin) 324 | } 325 | 326 | } 327 | 328 | Installer.main(args) -------------------------------------------------------------------------------- /scala-libs-for-android-emulator.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /scala/2.10.1/lib/akka-actors.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svenwiegand/scala-libs-for-android-emulator/2fc837ecbd2f747cc71c71cee5905c5857db4694/scala/2.10.1/lib/akka-actors.jar -------------------------------------------------------------------------------- /scala/2.10.1/lib/scala-actors-migration.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svenwiegand/scala-libs-for-android-emulator/2fc837ecbd2f747cc71c71cee5905c5857db4694/scala/2.10.1/lib/scala-actors-migration.jar -------------------------------------------------------------------------------- /scala/2.10.1/lib/scala-actors.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svenwiegand/scala-libs-for-android-emulator/2fc837ecbd2f747cc71c71cee5905c5857db4694/scala/2.10.1/lib/scala-actors.jar -------------------------------------------------------------------------------- /scala/2.10.1/lib/scala-collection.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svenwiegand/scala-libs-for-android-emulator/2fc837ecbd2f747cc71c71cee5905c5857db4694/scala/2.10.1/lib/scala-collection.jar -------------------------------------------------------------------------------- /scala/2.10.1/lib/scala-immutable.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svenwiegand/scala-libs-for-android-emulator/2fc837ecbd2f747cc71c71cee5905c5857db4694/scala/2.10.1/lib/scala-immutable.jar -------------------------------------------------------------------------------- /scala/2.10.1/lib/scala-library.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svenwiegand/scala-libs-for-android-emulator/2fc837ecbd2f747cc71c71cee5905c5857db4694/scala/2.10.1/lib/scala-library.jar -------------------------------------------------------------------------------- /scala/2.10.1/lib/scala-mutable.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svenwiegand/scala-libs-for-android-emulator/2fc837ecbd2f747cc71c71cee5905c5857db4694/scala/2.10.1/lib/scala-mutable.jar -------------------------------------------------------------------------------- /scala/2.10.1/lib/scala-reflect.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svenwiegand/scala-libs-for-android-emulator/2fc837ecbd2f747cc71c71cee5905c5857db4694/scala/2.10.1/lib/scala-reflect.jar -------------------------------------------------------------------------------- /scala/2.10.1/permissions/scala.2-10-1.actors-migration.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /scala/2.10.1/permissions/scala.2-10-1.actors.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /scala/2.10.1/permissions/scala.2-10-1.akka-actors.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /scala/2.10.1/permissions/scala.2-10-1.collection.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /scala/2.10.1/permissions/scala.2-10-1.immutable.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /scala/2.10.1/permissions/scala.2-10-1.library.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /scala/2.10.1/permissions/scala.2-10-1.mutable.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /scala/2.10.1/permissions/scala.2-10-1.reflect.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /scala/2.9.2/lib/scala-actors.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svenwiegand/scala-libs-for-android-emulator/2fc837ecbd2f747cc71c71cee5905c5857db4694/scala/2.9.2/lib/scala-actors.jar -------------------------------------------------------------------------------- /scala/2.9.2/lib/scala-collection.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svenwiegand/scala-libs-for-android-emulator/2fc837ecbd2f747cc71c71cee5905c5857db4694/scala/2.9.2/lib/scala-collection.jar -------------------------------------------------------------------------------- /scala/2.9.2/lib/scala-immutable.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svenwiegand/scala-libs-for-android-emulator/2fc837ecbd2f747cc71c71cee5905c5857db4694/scala/2.9.2/lib/scala-immutable.jar -------------------------------------------------------------------------------- /scala/2.9.2/lib/scala-library.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svenwiegand/scala-libs-for-android-emulator/2fc837ecbd2f747cc71c71cee5905c5857db4694/scala/2.9.2/lib/scala-library.jar -------------------------------------------------------------------------------- /scala/2.9.2/lib/scala-mutable.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svenwiegand/scala-libs-for-android-emulator/2fc837ecbd2f747cc71c71cee5905c5857db4694/scala/2.9.2/lib/scala-mutable.jar -------------------------------------------------------------------------------- /scala/2.9.2/permissions/scala.2-9-2.actors.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /scala/2.9.2/permissions/scala.2-9-2.collection.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /scala/2.9.2/permissions/scala.2-9-2.immutable.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /scala/2.9.2/permissions/scala.2-9-2.library.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /scala/2.9.2/permissions/scala.2-9-2.mutable.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | --------------------------------------------------------------------------------