mobo 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|