mobo 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/mobo +14 -0
- data/lib/mobo/android.rb +247 -0
- data/lib/mobo/system_check.rb +41 -0
- data/lib/mobo/system_setup.rb +32 -0
- data/lib/mobo.rb +105 -0
- data/mobo.rb +80 -0
- metadata +50 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 769b5307fcef24c8fd89ae3812977029429d9aa3
|
4
|
+
data.tar.gz: 03bb960b0e2aa5bec20a8d9605d0dece15504896
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 17af1fe2a87d0929d89cdc1d4b8762939a32300845fb0099279d7d638fd4047f3b9cbf2e8c1e11a11f240a67b02e08dd042f110dda9aa8a524b45f1c14ca4348
|
7
|
+
data.tar.gz: 7e8831e69051b35a96797f5b31817a35ddc3eea5358cca8ac10e3afd7e2cd7deb4c20c2ef0dcf2d95ed785e0efaf05158ba4a532d69ba4859f8e7817dfdd4db2
|
data/bin/mobo
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require_relative '../lib/mobo'
|
3
|
+
|
4
|
+
MOBO_DEVICES_FILE = Dir.pwd + '/devices.yaml'
|
5
|
+
MOBO_DEVICES_CACHE_FILE = Dir.pwd + '/.mobo-devices.yaml'
|
6
|
+
|
7
|
+
case ARGV[0]
|
8
|
+
when "up"
|
9
|
+
Mobo.up(MOBO_DEVICES_FILE)
|
10
|
+
when "status"
|
11
|
+
Mobo.status(MOBO_DEVICES_CACHE_FILE)
|
12
|
+
when "destroy"
|
13
|
+
Mobo.destroy(MOBO_DEVICES_CACHE_FILE)
|
14
|
+
end
|
data/lib/mobo/android.rb
ADDED
@@ -0,0 +1,247 @@
|
|
1
|
+
module Mobo
|
2
|
+
|
3
|
+
module Android
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def exists?
|
7
|
+
add_to_path
|
8
|
+
Mobo.cmd("which android")
|
9
|
+
end
|
10
|
+
|
11
|
+
def install
|
12
|
+
Mobo.cmd("curl -O http://dl.google.com/android/android-sdk_r24.3.4-linux.tgz")
|
13
|
+
Mobo.cmd("sudo tar -xf android-sdk_r24.3.4-linux.tgz -C /usr/local/")
|
14
|
+
Mobo.cmd("sudo chown -R $(whoami) /usr/local/android-sdk-linux")
|
15
|
+
add_to_path
|
16
|
+
end
|
17
|
+
|
18
|
+
# setting env variables in the bash profile and trying to reload them isn't easy,
|
19
|
+
# as the variables are only set in the sub process the bash_profile is executed in
|
20
|
+
# so we can set env variables, which take effect here, and also set them in bash_profile
|
21
|
+
# for the user to use later on
|
22
|
+
def add_to_path
|
23
|
+
android_home = "/usr/local/android-sdk-linux"
|
24
|
+
|
25
|
+
if !ENV['PATH'].match(/#{android_home}/)
|
26
|
+
ENV['ANDROID_HOME'] = android_home
|
27
|
+
ENV['PATH'] += ":#{ENV['ANDROID_HOME']}/tools"
|
28
|
+
Mobo.cmd("echo \"export ANDROID_HOME=#{ENV['ANDROID_HOME']}\" >> ~/.bash_profile")
|
29
|
+
Mobo.cmd("echo \"export PATH=#{ENV['PATH']}\" >> ~/.bash_profile")
|
30
|
+
Mobo.log.info("ANDROID_HOME and PATH env variables have been updated.
|
31
|
+
Start a new terminal session for them to take effect")
|
32
|
+
raise "Setting ANDROID_HOME and PATH failed" unless self.exists?
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def package_exists?(package)
|
37
|
+
Mobo.cmd("android list sdk --extended --no-ui --all | grep '\"#{package}\"'")
|
38
|
+
end
|
39
|
+
|
40
|
+
def install_package(package)
|
41
|
+
Mobo.cmd("echo y | android update sdk --no-ui --all --filter #{package} 2>&1 >> /dev/null")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
module Targets
|
46
|
+
class << self
|
47
|
+
def exists?(target)
|
48
|
+
Mobo.cmd("android list targets --compact | grep #{target}")
|
49
|
+
end
|
50
|
+
|
51
|
+
def target_details(target)
|
52
|
+
output = Mobo.cmd_out("android list targets")
|
53
|
+
records = output.split('----------')
|
54
|
+
records.each do |record|
|
55
|
+
return record if record.match(/#{target}/)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def abi_package(target, abi)
|
60
|
+
prepend = "sys-img"
|
61
|
+
"#{prepend}-#{abi}-#{target}"
|
62
|
+
end
|
63
|
+
|
64
|
+
def has_abi?(target, abi)
|
65
|
+
target_details(target).match(/#{abi}/)
|
66
|
+
end
|
67
|
+
|
68
|
+
def has_skin?(target, skin)
|
69
|
+
target_details(target).match(/#{skin}/)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
module Avd
|
75
|
+
class << self
|
76
|
+
def create(device)
|
77
|
+
SystemCheck.target_exists(device["target"])
|
78
|
+
SystemCheck.abi_exists?(device["target"], device["abi"])
|
79
|
+
SystemCheck.skin_exists?(device["target"], device["skin"])
|
80
|
+
emulator_cmd =
|
81
|
+
"echo no | android create avd \
|
82
|
+
--name #{device["name"]} \
|
83
|
+
--target #{device["target"]} \
|
84
|
+
--abi #{device["abi"]} \
|
85
|
+
--force"
|
86
|
+
emulator_cmd += " --skin #{device["skin"]}" if device["skin"]
|
87
|
+
Mobo.cmd(emulator_cmd)
|
88
|
+
|
89
|
+
raise "Error creating AVD" unless $?.success?
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
class Adb
|
95
|
+
class << self
|
96
|
+
|
97
|
+
def add_to_path
|
98
|
+
if !ENV['PATH'].match(/platform-tools/)
|
99
|
+
ENV['PATH'] += ":#{ENV['ANDROID_HOME']}/platform-tools"
|
100
|
+
Mobo.cmd("echo \"export PATH=#{ENV['PATH']}\" >> ~/.bash_profile")
|
101
|
+
Mobo.log.debug("ENV['PATH'] set to #{ENV['PATH']}")
|
102
|
+
Mobo.log.info("PATH env variables has been updated.
|
103
|
+
Start a new terminal session for it to take effect")
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def exists?
|
108
|
+
add_to_path
|
109
|
+
Mobo.cmd("which adb")
|
110
|
+
end
|
111
|
+
|
112
|
+
def install
|
113
|
+
Mobo.cmd("echo yes | android update sdk --all --no-ui --filter platform-tools 2>&1 >> /dev/null")
|
114
|
+
add_to_path
|
115
|
+
raise "Installing adb failed" unless self.exists?
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
class Emulator
|
121
|
+
PORT_START = 5600
|
122
|
+
BOOT_SLEEP = 5
|
123
|
+
BOOT_WAIT_ATTEMPTS = 30
|
124
|
+
UNLOCK_KEY_EVENT = 82
|
125
|
+
BACK_KEY_EVENT = 4
|
126
|
+
|
127
|
+
class << self
|
128
|
+
attr_accessor :last_port
|
129
|
+
end
|
130
|
+
|
131
|
+
def initialize(device)
|
132
|
+
@device = device
|
133
|
+
end
|
134
|
+
|
135
|
+
def find_open_port
|
136
|
+
port = (Emulator.last_port || PORT_START) + 2
|
137
|
+
until not Mobo.cmd("netstat -an | grep 127.0.0.1.#{port}")
|
138
|
+
port += 2
|
139
|
+
end
|
140
|
+
Emulator.last_port = port
|
141
|
+
@device["port"] = port
|
142
|
+
end
|
143
|
+
|
144
|
+
def create_sdcard
|
145
|
+
@device["sdcard"] = "#{@device["name"]}_sdcard.img"
|
146
|
+
Mobo.cmd("mksdcard -l e #{@device["sdcard_size"]} #{@device["sdcard"]}")
|
147
|
+
end
|
148
|
+
|
149
|
+
def destroy_sdcard
|
150
|
+
Mobo.cmd("rm -f #{@device["sdcard"]}")
|
151
|
+
end
|
152
|
+
|
153
|
+
def create_cache
|
154
|
+
@device["cache"] = @device["name"] + "_cache.img"
|
155
|
+
end
|
156
|
+
|
157
|
+
def destroy_cache
|
158
|
+
Mobo.cmd("rm -f #{@device["cache"]}")
|
159
|
+
end
|
160
|
+
|
161
|
+
def start
|
162
|
+
find_open_port
|
163
|
+
create_sdcard
|
164
|
+
create_cache
|
165
|
+
|
166
|
+
cmd ="emulator @#{@device["name"]} \
|
167
|
+
-port #{@device["port"]} \
|
168
|
+
-sdcard #{@device["sdcard"]} \
|
169
|
+
-cache #{@device["cache"]}"
|
170
|
+
pid = Process.fork {
|
171
|
+
Mobo.cmd(cmd)
|
172
|
+
Mobo.log.info("Emulator #{@device["name"]} has stopped")
|
173
|
+
}
|
174
|
+
|
175
|
+
@device["pid"] = pid
|
176
|
+
@device["id"] = "emulator-#{@device["port"]}"
|
177
|
+
return @device
|
178
|
+
end
|
179
|
+
|
180
|
+
def unlock_when_booted
|
181
|
+
Process.fork do
|
182
|
+
BOOT_WAIT_ATTEMPTS.times do |attempt|
|
183
|
+
if booted?
|
184
|
+
Mobo.log.info("#{@device["id"]} has booted")
|
185
|
+
break
|
186
|
+
elsif !running?
|
187
|
+
Mobo.log.error("Emulator #{@device["name"]} has stopped")
|
188
|
+
break
|
189
|
+
else
|
190
|
+
Mobo.log.debug("waiting for #{@device["id"]} to boot...")
|
191
|
+
sleep(BOOT_SLEEP)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
if running? && booted?
|
196
|
+
unlock
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def running?
|
202
|
+
begin
|
203
|
+
Process.getpgid( @device["pid"] )
|
204
|
+
true
|
205
|
+
rescue Errno::ESRCH
|
206
|
+
false
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def booted?
|
211
|
+
bootanim = Mobo.cmd_out("adb -s #{@device["id"]} shell 'getprop init.svc.bootanim'")
|
212
|
+
if bootanim.match(/stopped/)
|
213
|
+
return true
|
214
|
+
else
|
215
|
+
return false
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
def unlock
|
220
|
+
# unlock the emulator, so it can be used for UI testing
|
221
|
+
# then, pressing back because sometimes a menu appears
|
222
|
+
[UNLOCK_KEY_EVENT, BACK_KEY_EVENT].each do |key|
|
223
|
+
sleep(BOOT_SLEEP)
|
224
|
+
Mobo.cmd("adb -s #{@device["id"]} shell input keyevent #{key}")
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
def status
|
229
|
+
Mobo.log.info("#{@device["name"]} (#{@device["id"]}) is running: #{booted?}")
|
230
|
+
end
|
231
|
+
|
232
|
+
def destroy
|
233
|
+
(0..10).each do
|
234
|
+
if booted?
|
235
|
+
Mobo.cmd("adb -s #{@device["id"]} emu kill")
|
236
|
+
sleep 1
|
237
|
+
else
|
238
|
+
Mobo.log.info("#{@device["name"]} (#{@device["id"]}) is shutdown")
|
239
|
+
destroy_cache
|
240
|
+
destroy_sdcard
|
241
|
+
break
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Mobo
|
2
|
+
module SystemCheck
|
3
|
+
class << self
|
4
|
+
def bash_check(cmd, msg, error)
|
5
|
+
system(cmd)
|
6
|
+
Mobo.log.info("#{msg} : #{$?}")
|
7
|
+
raise error unless $?.success?
|
8
|
+
end
|
9
|
+
|
10
|
+
def android
|
11
|
+
Android.exists?
|
12
|
+
end
|
13
|
+
|
14
|
+
def target_exists(target)
|
15
|
+
unless Android::Targets.exists?(target)
|
16
|
+
SystemSetup.install_target(target)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def abi_exists?(target, abi)
|
21
|
+
unless Android::Targets.has_abi?(target, abi)
|
22
|
+
SystemSetup.install_abi(target, abi)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def skin_exists?(target, abi)
|
27
|
+
Android::Targets.has_skin?(target, abi)
|
28
|
+
end
|
29
|
+
|
30
|
+
def adb
|
31
|
+
unless Android::Adb.exists?
|
32
|
+
SystemSetup.install_adb
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def device_file_exists?(filename)
|
37
|
+
File.exists?(filename)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Mobo
|
2
|
+
module SystemSetup
|
3
|
+
class << self
|
4
|
+
|
5
|
+
def base_libraries
|
6
|
+
#if ubuntu
|
7
|
+
if(Mobo.cmd("which apt-get"))
|
8
|
+
Mobo.cmd("sudo apt-get install -y libc6-i386 lib32stdc++6 lib32gcc1 lib32ncurses5 lib32z1")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def install_android
|
13
|
+
Android.install
|
14
|
+
end
|
15
|
+
|
16
|
+
def install_target(target)
|
17
|
+
Android.install_package(target) if Android.package_exists?(target)
|
18
|
+
raise "Cannot install target: #{target}" unless $?.success?
|
19
|
+
end
|
20
|
+
|
21
|
+
def install_abi(target, abi)
|
22
|
+
package_name = Android::Targets.abi_package(target, abi)
|
23
|
+
Android.install_package(package_name) if Android.package_exists?(package_name)
|
24
|
+
raise "Cannot install abi: #{abi} for #{target}" unless $?.success?
|
25
|
+
end
|
26
|
+
|
27
|
+
def install_adb
|
28
|
+
Android::Adb.install unless Android::Adb.exists?
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/mobo.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'yaml'
|
3
|
+
require 'pp'
|
4
|
+
require 'logger'
|
5
|
+
require_relative 'mobo/android'
|
6
|
+
require_relative 'mobo/system_check'
|
7
|
+
require_relative 'mobo/system_setup'
|
8
|
+
|
9
|
+
module Mobo
|
10
|
+
class << self
|
11
|
+
attr_accessor :log, :data, :devices
|
12
|
+
|
13
|
+
def log
|
14
|
+
@log || @log = Logger.new(STDOUT)
|
15
|
+
end
|
16
|
+
|
17
|
+
def cmd(command)
|
18
|
+
log.debug(command)
|
19
|
+
system(command)
|
20
|
+
end
|
21
|
+
|
22
|
+
def cmd_out(command)
|
23
|
+
log.debug(command)
|
24
|
+
`#{command}`
|
25
|
+
end
|
26
|
+
|
27
|
+
def start_process(cmd)
|
28
|
+
pid = Process.spawn(cmd)
|
29
|
+
Process.detach(pid)
|
30
|
+
pid
|
31
|
+
end
|
32
|
+
|
33
|
+
def ask_user(msg, &block)
|
34
|
+
puts msg
|
35
|
+
ans = STDIN.gets.chomp
|
36
|
+
if ans.match(/yes|y|Y/)
|
37
|
+
yield
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def devices
|
42
|
+
@devices = @devices.nil? ? {} : @devices
|
43
|
+
end
|
44
|
+
|
45
|
+
def device_config(device)
|
46
|
+
defaults = {
|
47
|
+
"name" => "default",
|
48
|
+
"target" => "android-22",
|
49
|
+
"abi" => "x86_64",
|
50
|
+
"height" => "1200",
|
51
|
+
"width" => "720",
|
52
|
+
"sdcard_size" => "20M" }
|
53
|
+
device = defaults.merge(device)
|
54
|
+
end
|
55
|
+
|
56
|
+
def load_data(filename)
|
57
|
+
raise "Cannot file #{filename}" unless SystemCheck.device_file_exists?(filename)
|
58
|
+
Mobo.data = YAML::load_file(filename)
|
59
|
+
end
|
60
|
+
|
61
|
+
def system_checks
|
62
|
+
SystemSetup.base_libraries
|
63
|
+
SystemCheck.android
|
64
|
+
SystemCheck.adb
|
65
|
+
end
|
66
|
+
|
67
|
+
def up(filename)
|
68
|
+
system_checks
|
69
|
+
load_data(filename)
|
70
|
+
# retrive settings for one of each device
|
71
|
+
Mobo.data["devices"].each do |device|
|
72
|
+
device = device_config(device)
|
73
|
+
Mobo.devices[device["name"]] = device
|
74
|
+
end
|
75
|
+
|
76
|
+
# build and boot devices
|
77
|
+
Mobo.devices.each_pair do |name, device|
|
78
|
+
Mobo::Android::Avd.create(device)
|
79
|
+
emulator = Android::Emulator.new(device)
|
80
|
+
emulator.start
|
81
|
+
emulator.unlock_when_booted
|
82
|
+
end
|
83
|
+
File.open(MOBO_DEVICES_CACHE_FILE, "w") do |file|
|
84
|
+
file.write Mobo.devices.to_yaml
|
85
|
+
end
|
86
|
+
Process.waitall
|
87
|
+
end
|
88
|
+
|
89
|
+
def status(filename)
|
90
|
+
load_data(filename)
|
91
|
+
Mobo.data.each_pair do |name, device|
|
92
|
+
Mobo::Android::Emulator.new(device).status
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def destroy(filename)
|
97
|
+
load_data(filename)
|
98
|
+
Mobo.data.each_pair do |name, device|
|
99
|
+
Mobo::Android::Emulator.new(device).destroy
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
data/mobo.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'yaml'
|
3
|
+
require 'pp'
|
4
|
+
require 'logger'
|
5
|
+
require_relative 'lib/mobo/android'
|
6
|
+
require_relative 'lib/mobo/system_check'
|
7
|
+
|
8
|
+
module Mobo
|
9
|
+
class << self
|
10
|
+
attr_accessor :log, :data, :devices
|
11
|
+
|
12
|
+
def log
|
13
|
+
@log || @log = Logger.new(STDOUT)
|
14
|
+
end
|
15
|
+
|
16
|
+
def cmd(command)
|
17
|
+
log.debug(command)
|
18
|
+
system(command)
|
19
|
+
end
|
20
|
+
|
21
|
+
def cmd_out(command)
|
22
|
+
log.debug(command)
|
23
|
+
`#{command}`
|
24
|
+
end
|
25
|
+
|
26
|
+
def start_process(cmd)
|
27
|
+
pid = Process.spawn(cmd)
|
28
|
+
Process.detach(pid)
|
29
|
+
pid
|
30
|
+
end
|
31
|
+
|
32
|
+
def devices
|
33
|
+
@devices = @devices.nil? ? {} : @devices
|
34
|
+
end
|
35
|
+
|
36
|
+
def device_config(device)
|
37
|
+
defaults = {
|
38
|
+
"name" => "default",
|
39
|
+
"target" => "android-22",
|
40
|
+
"abi" => "default/x86_64",
|
41
|
+
"height" => "1200",
|
42
|
+
"width" => "720",
|
43
|
+
"sdcard_size" => "20M" }
|
44
|
+
defaults.merge(device)
|
45
|
+
end
|
46
|
+
|
47
|
+
def up
|
48
|
+
# retrive settings for one of each device
|
49
|
+
Mobo.data["devices"].each do |device|
|
50
|
+
device = device_config(device)
|
51
|
+
Mobo.devices[device["name"]] = device
|
52
|
+
end
|
53
|
+
|
54
|
+
# build and boot devices
|
55
|
+
Mobo.devices.each_pair do |name, device|
|
56
|
+
Mobo::Android::Avd.create(device)
|
57
|
+
emulator = Android::Emulator.new(device)
|
58
|
+
emulator.start
|
59
|
+
emulator.unlock_when_booted
|
60
|
+
end
|
61
|
+
File.open(MOBO_DEVICES_FILE, "w") do |file|
|
62
|
+
file.write Mobo.devices.to_yaml
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def status
|
67
|
+
Mobo.data.each_pair do |name, device|
|
68
|
+
Mobo::Android::Emulator.new(device).status
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def destroy
|
73
|
+
Mobo.data.each_pair do |name, device|
|
74
|
+
Mobo::Android::Emulator.new(device).destroy
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
metadata
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mobo
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mark O'Shea
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-08-05 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Abstract android emaulators with a simple yaml file
|
14
|
+
email: mark@osheatech.com
|
15
|
+
executables:
|
16
|
+
- mobo
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- mobo.rb
|
21
|
+
- lib/mobo/android.rb
|
22
|
+
- lib/mobo/system_check.rb
|
23
|
+
- lib/mobo/system_setup.rb
|
24
|
+
- lib/mobo.rb
|
25
|
+
- bin/mobo
|
26
|
+
homepage: https://github.com/moshea/mobo
|
27
|
+
licenses:
|
28
|
+
- MIT
|
29
|
+
metadata: {}
|
30
|
+
post_install_message:
|
31
|
+
rdoc_options: []
|
32
|
+
require_paths:
|
33
|
+
- lib
|
34
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - '>='
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
requirements: []
|
45
|
+
rubyforge_project:
|
46
|
+
rubygems_version: 2.0.3
|
47
|
+
signing_key:
|
48
|
+
specification_version: 4
|
49
|
+
summary: Mobo - android emulator abstraction
|
50
|
+
test_files: []
|