Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
d96f3853
Commit
d96f3853
authored
Aug 29, 2015
by
Ian Fischer
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #944 from iansf/no_android
Don't crash when no Android device is attached
parents
af9212c7
576f51d4
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
180 additions
and
147 deletions
+180
-147
sky_tool
packages/flutter/lib/sky_tool
+180
-147
No files found.
packages/flutter/lib/sky_tool
View file @
d96f3853
...
@@ -157,26 +157,12 @@ class StartSky(object):
...
@@ -157,26 +157,12 @@ class StartSky(object):
default
=
'.'
)
default
=
'.'
)
start_parser
.
set_defaults
(
func
=
self
.
run
)
start_parser
.
set_defaults
(
func
=
self
.
run
)
def
_is_package_installed
(
self
,
package_name
):
pm_path_cmd
=
[
ADB_PATH
,
'shell'
,
'pm'
,
'path'
,
package_name
]
logging
.
info
(
' '
.
join
(
pm_path_cmd
))
return
subprocess
.
check_output
(
pm_path_cmd
)
.
strip
()
!=
''
def
_is_valid_script_path
(
self
):
script_path
=
os
.
path
.
dirname
(
os
.
path
.
abspath
(
__file__
))
script_dirs
=
script_path
.
split
(
'/'
)
return
len
(
script_dirs
)
>
1
and
script_dirs
[
-
2
]
==
'packages'
def
_get_device_apk_sha1
(
self
,
apk_path
):
# We might need to install a new APK, so check SHA1
cmd
=
[
ADB_PATH
,
'shell'
,
'cat'
,
SHA1_PATH
]
logging
.
info
(
' '
.
join
(
cmd
))
return
subprocess
.
check_output
(
cmd
)
def
run
(
self
,
args
,
pids
):
def
run
(
self
,
args
,
pids
):
if
not
args
.
poke
:
if
not
args
.
poke
:
StopSky
()
.
run
(
args
,
pids
)
StopSky
()
.
run
(
args
,
pids
)
android
=
AndroidDevice
()
project_or_path
=
os
.
path
.
abspath
(
args
.
project_or_path
)
project_or_path
=
os
.
path
.
abspath
(
args
.
project_or_path
)
if
args
.
android_build_available
and
args
.
use_release
:
if
args
.
android_build_available
and
args
.
use_release
:
...
@@ -185,7 +171,6 @@ class StartSky(object):
...
@@ -185,7 +171,6 @@ class StartSky(object):
apk_path
=
os
.
path
.
join
(
os
.
path
.
normpath
(
args
.
sky_src_path
),
args
.
android_debug_build_path
,
'apks'
,
APK_NAME
)
apk_path
=
os
.
path
.
join
(
os
.
path
.
normpath
(
args
.
sky_src_path
),
args
.
android_debug_build_path
,
'apks'
,
APK_NAME
)
else
:
else
:
apk_path
=
os
.
path
.
join
(
APK_DIR
,
APK_NAME
)
apk_path
=
os
.
path
.
join
(
APK_DIR
,
APK_NAME
)
source_sha1
=
hashlib
.
sha1
(
open
(
apk_path
,
'rb'
)
.
read
())
.
hexdigest
()
if
os
.
path
.
isdir
(
project_or_path
):
if
os
.
path
.
isdir
(
project_or_path
):
sky_server_root
=
project_or_path
sky_server_root
=
project_or_path
...
@@ -207,33 +192,21 @@ class StartSky(object):
...
@@ -207,33 +192,21 @@ class StartSky(object):
logging
.
error
(
'
%
s is not a valid packages path.'
%
package_root
)
logging
.
error
(
'
%
s is not a valid packages path.'
%
package_root
)
return
2
return
2
if
not
self
.
_
is_package_installed
(
ANDROID_PACKAGE
):
if
not
android
.
is_package_installed
(
ANDROID_PACKAGE
):
logging
.
info
(
'
%
s is not on the device. Installing now...'
%
APK_NAME
)
logging
.
info
(
'
%
s is not on the device. Installing now...'
%
APK_NAME
)
args
.
install
=
True
args
.
install
=
True
elif
self
.
_get_device_apk_sha1
(
apk_path
)
!=
source_sha1
:
elif
android
.
get_device_apk_sha1
(
apk_path
)
!=
android
.
get_source_sha1
(
apk_path
)
:
logging
.
info
(
'
%
s on the device is out of date. Installing now...'
%
APK_NAME
)
logging
.
info
(
'
%
s on the device is out of date. Installing now...'
%
APK_NAME
)
args
.
install
=
True
args
.
install
=
True
if
args
.
install
:
if
args
.
install
:
if
not
self
.
_is_valid_script_path
():
# Install on connected Android device
logging
.
error
(
'"
%
s" must be located in packages/sky. '
if
android
.
is_connected
()
and
args
.
android_build_available
:
'The directory packages/sky_engine must also '
if
args
.
use_release
:
'exist to locate
%
s.'
%
(
os
.
path
.
basename
(
__file__
),
APK_NAME
))
apk_path
=
os
.
path
.
join
(
args
.
sky_src_path
,
args
.
android_release_build_path
,
'apks'
,
APK_NAME
)
return
2
else
:
if
not
os
.
path
.
exists
(
apk_path
):
apk_path
=
os
.
path
.
join
(
args
.
sky_src_path
,
args
.
android_debug_build_path
,
'apks'
,
APK_NAME
)
logging
.
error
(
'"
%
s" does not exist.'
%
apk_path
)
android
.
install_apk
(
apk_path
)
return
2
cmd
=
[
ADB_PATH
,
'install'
,
'-r'
,
apk_path
]
logging
.
info
(
' '
.
join
(
cmd
))
subprocess
.
check_call
(
cmd
)
# record the SHA1 of the APK we just pushed
with
tempfile
.
NamedTemporaryFile
()
as
fp
:
fp
.
write
(
source_sha1
)
fp
.
seek
(
0
)
cmd
=
[
ADB_PATH
,
'push'
,
fp
.
name
,
SHA1_PATH
]
logging
.
info
(
' '
.
join
(
cmd
))
subprocess
.
check_call
(
cmd
)
# Install on connected iOS device
# Install on connected iOS device
if
IOSDevice
.
is_connected
()
and
args
.
ios_build_available
:
if
IOSDevice
.
is_connected
()
and
args
.
ios_build_available
:
...
@@ -251,10 +224,171 @@ class StartSky(object):
...
@@ -251,10 +224,171 @@ class StartSky(object):
app_path
=
os
.
path
.
join
(
args
.
sky_src_path
,
args
.
ios_sim_debug_build_path
,
IOS_APP_NAME
)
app_path
=
os
.
path
.
join
(
args
.
sky_src_path
,
args
.
ios_sim_debug_build_path
,
IOS_APP_NAME
)
IOSSimulator
.
fork_install_app
(
app_path
)
IOSSimulator
.
fork_install_app
(
app_path
)
# TODO(iansf): fix this so that we don't have to pass sky_server_root, main_dart, pid, and args.
android
.
setup_servers
(
sky_server_root
,
main_dart
,
pids
,
args
)
class
StopSky
(
object
):
def
add_subparser
(
self
,
subparsers
):
stop_parser
=
subparsers
.
add_parser
(
'stop'
,
help
=
(
'kill all running SkyShell.apk processes'
))
stop_parser
.
set_defaults
(
func
=
self
.
run
)
def
_run
(
self
,
args
):
with
open
(
'/dev/null'
,
'w'
)
as
dev_null
:
logging
.
info
(
' '
.
join
(
args
))
subprocess
.
call
(
args
,
stdout
=
dev_null
,
stderr
=
dev_null
)
def
run
(
self
,
args
,
pids
):
self
.
_run
([
'fuser'
,
'-k'
,
'
%
s/tcp'
%
SKY_SERVER_PORT
])
if
'remote_sky_server_port'
in
pids
:
port_string
=
'tcp:
%
s'
%
pids
[
'remote_sky_server_port'
]
self
.
_run
([
AndroidDevice
()
.
adb_path
,
'reverse'
,
'--remove'
,
port_string
])
self
.
_run
([
AndroidDevice
()
.
adb_path
,
'shell'
,
'am'
,
'force-stop'
,
ANDROID_PACKAGE
])
pids
.
clear
()
class
AndroidDevice
(
object
):
# _state used in this manner gives a simple way to treat AndroidDevice
# as a singleton while easily allowing subclassing for mocks. All
# AndroidDevices created in a given session will share the same state.
_state
=
{}
def
__init__
(
self
):
self
.
__dict__
=
AndroidDevice
.
_state
self
.
_update_paths
()
# Checking for lollipop only needs to be done if we are starting an
# app, but it has an important side effect, which is to discard any
# progress messages if the adb server is restarted.
self
.
_check_for_adb
()
self
.
_check_for_lollipop_or_later
()
def
_update_paths
(
self
):
if
'adb_path'
in
self
.
__dict__
:
return
if
'ANDROID_HOME'
in
os
.
environ
:
android_home_dir
=
os
.
environ
[
'ANDROID_HOME'
]
self
.
adb_path
=
os
.
path
.
join
(
android_home_dir
,
'sdk'
,
'platform-tools'
,
'adb'
)
else
:
self
.
adb_path
=
ADB_PATH
def
_is_valid_adb_version
(
self
,
adb_version
):
# Sample output: 'Android Debug Bridge version 1.0.31'
version_fields
=
re
.
search
(
'(
\
d+)
\
.(
\
d+)
\
.(
\
d+)'
,
adb_version
)
if
version_fields
:
major_version
=
int
(
version_fields
.
group
(
1
))
minor_version
=
int
(
version_fields
.
group
(
2
))
patch_version
=
int
(
version_fields
.
group
(
3
))
if
major_version
>
1
:
return
True
if
major_version
==
1
and
minor_version
>
0
:
return
True
if
major_version
==
1
and
minor_version
==
0
and
patch_version
>=
32
:
return
True
return
False
else
:
logging
.
warn
(
'Unrecognized adb version string. Skipping version check.'
)
return
True
def
_check_for_adb
(
self
):
if
'has_valid_adb'
in
self
.
__dict__
:
return
try
:
cmd
=
[
self
.
adb_path
,
'version'
]
logging
.
info
(
' '
.
join
(
cmd
))
adb_version
=
subprocess
.
check_output
(
cmd
)
if
self
.
_is_valid_adb_version
(
adb_version
):
self
.
has_valid_adb
=
True
return
cmd
=
[
'which'
,
ADB_PATH
]
logging
.
info
(
' '
.
join
(
cmd
))
adb_path
=
subprocess
.
check_output
(
cmd
)
.
rstrip
()
logging
.
error
(
'"
%
s" is too old. Need 1.0.32 or later. '
'Try setting ANDROID_HOME to use Android builds. Android builds are unavailable.'
%
adb_path
)
self
.
has_valid_adb
=
False
except
OSError
:
logging
.
warning
(
'"adb" (from the Android SDK) not in $PATH, Android builds are unavailable.'
)
self
.
has_valid_adb
=
False
def
_check_for_lollipop_or_later
(
self
):
if
'has_valid_android'
in
self
.
__dict__
:
return
try
:
# If the server is automatically restarted, then we get irrelevant
# output lines like this, which we want to ignore:
# adb server is out of date. killing..
# * daemon started successfully *
cmd
=
[
self
.
adb_path
,
'start-server'
]
logging
.
info
(
' '
.
join
(
cmd
))
subprocess
.
call
(
cmd
)
cmd
=
[
self
.
adb_path
,
'shell'
,
'getprop'
,
'ro.build.version.sdk'
]
logging
.
info
(
' '
.
join
(
cmd
))
sdk_version
=
subprocess
.
check_output
(
cmd
)
.
rstrip
()
# Sample output: '22'
if
not
sdk_version
.
isdigit
():
logging
.
error
(
'Unexpected response from getprop: "
%
s".'
%
sdk_version
)
self
.
has_valid_android
=
False
return
if
int
(
sdk_version
)
<
22
:
logging
.
error
(
'Version "
%
s" of the Android SDK is too old. '
'Need Lollipop (22) or later. '
%
sdk_version
)
self
.
has_valid_android
=
False
return
except
subprocess
.
CalledProcessError
as
e
:
# adb printed the error, so we print nothing.
self
.
has_valid_android
=
False
return
self
.
has_valid_android
=
True
def
is_package_installed
(
self
,
package_name
):
if
not
self
.
is_connected
():
return
False
pm_path_cmd
=
[
self
.
adb_path
,
'shell'
,
'pm'
,
'path'
,
package_name
]
logging
.
info
(
' '
.
join
(
pm_path_cmd
))
return
subprocess
.
check_output
(
pm_path_cmd
)
.
strip
()
!=
''
def
get_device_apk_sha1
(
self
,
apk_path
):
# We might need to install a new APK, so check SHA1
cmd
=
[
self
.
adb_path
,
'shell'
,
'cat'
,
SHA1_PATH
]
logging
.
info
(
' '
.
join
(
cmd
))
return
subprocess
.
check_output
(
cmd
)
def
get_source_sha1
(
self
,
apk_path
):
return
hashlib
.
sha1
(
open
(
apk_path
,
'rb'
)
.
read
())
.
hexdigest
()
def
is_connected
(
self
):
return
self
.
has_valid_android
def
install_apk
(
self
,
apk_path
):
if
not
os
.
path
.
exists
(
apk_path
):
logging
.
error
(
'"
%
s" does not exist.'
%
apk_path
)
return
cmd
=
[
self
.
adb_path
,
'install'
,
'-r'
,
apk_path
]
logging
.
info
(
' '
.
join
(
cmd
))
subprocess
.
check_call
(
cmd
)
# record the SHA1 of the APK we just pushed
with
tempfile
.
NamedTemporaryFile
()
as
fp
:
fp
.
write
(
self
.
get_source_sha1
(
apk_path
))
fp
.
seek
(
0
)
cmd
=
[
self
.
adb_path
,
'push'
,
fp
.
name
,
SHA1_PATH
]
logging
.
info
(
' '
.
join
(
cmd
))
subprocess
.
check_call
(
cmd
)
# TODO(iansf): refactor setup_servers
def
setup_servers
(
self
,
sky_server_root
,
main_dart
,
pids
,
args
):
if
not
self
.
is_connected
():
return
# Set up port forwarding for observatory
# Set up port forwarding for observatory
observatory_port_string
=
'tcp:
%
s'
%
OBSERVATORY_PORT
observatory_port_string
=
'tcp:
%
s'
%
OBSERVATORY_PORT
cmd
=
[
cmd
=
[
ADB_PATH
,
self
.
adb_path
,
'forward'
,
'forward'
,
observatory_port_string
,
observatory_port_string
,
observatory_port_string
observatory_port_string
...
@@ -274,7 +408,7 @@ class StartSky(object):
...
@@ -274,7 +408,7 @@ class StartSky(object):
port_string
=
'tcp:
%
s'
%
sky_server_port
port_string
=
'tcp:
%
s'
%
sky_server_port
cmd
=
[
cmd
=
[
ADB_PATH
,
self
.
adb_path
,
'reverse'
,
'reverse'
,
port_string
,
port_string
,
port_string
port_string
...
@@ -290,42 +424,20 @@ class StartSky(object):
...
@@ -290,42 +424,20 @@ class StartSky(object):
url
+=
'?rand=
%
s'
%
random
.
random
()
url
+=
'?rand=
%
s'
%
random
.
random
()
cmd
=
[
cmd
=
[
ADB_PATH
,
'shell'
,
self
.
adb_path
,
'shell'
,
'am'
,
'start'
,
'am'
,
'start'
,
'-a'
,
'android.intent.action.VIEW'
,
'-a'
,
'android.intent.action.VIEW'
,
'-d'
,
url
,
'-d'
,
url
,
]
]
if
args
.
checked
:
if
args
.
checked
:
cmd
+=
[
'--ez'
,
'enable-checked-mode'
,
'true'
]
cmd
+=
[
'--ez'
,
'enable-checked-mode'
,
'true'
]
cmd
+=
[
ANDROID_COMPONENT
]
cmd
+=
[
ANDROID_COMPONENT
]
logging
.
info
(
' '
.
join
(
cmd
))
logging
.
info
(
' '
.
join
(
cmd
))
subprocess
.
check_output
(
cmd
)
subprocess
.
check_output
(
cmd
)
class
StopSky
(
object
):
def
add_subparser
(
self
,
subparsers
):
stop_parser
=
subparsers
.
add_parser
(
'stop'
,
help
=
(
'kill all running SkyShell.apk processes'
))
stop_parser
.
set_defaults
(
func
=
self
.
run
)
def
_run
(
self
,
args
):
with
open
(
'/dev/null'
,
'w'
)
as
dev_null
:
logging
.
info
(
' '
.
join
(
args
))
subprocess
.
call
(
args
,
stdout
=
dev_null
,
stderr
=
dev_null
)
def
run
(
self
,
args
,
pids
):
self
.
_run
([
'fuser'
,
'-k'
,
'
%
s/tcp'
%
SKY_SERVER_PORT
])
if
'remote_sky_server_port'
in
pids
:
port_string
=
'tcp:
%
s'
%
pids
[
'remote_sky_server_port'
]
self
.
_run
([
ADB_PATH
,
'reverse'
,
'--remove'
,
port_string
])
self
.
_run
([
ADB_PATH
,
'shell'
,
'am'
,
'force-stop'
,
ANDROID_PACKAGE
])
pids
.
clear
()
class
IOSDevice
(
object
):
class
IOSDevice
(
object
):
_has_ios_deploy
=
None
_has_ios_deploy
=
None
...
@@ -810,78 +922,6 @@ class StopTracing(object):
...
@@ -810,78 +922,6 @@ class StopTracing(object):
class
SkyShellRunner
(
object
):
class
SkyShellRunner
(
object
):
def
_update_paths
(
self
):
global
ADB_PATH
if
'ANDROID_HOME'
in
os
.
environ
:
android_home_dir
=
os
.
environ
[
'ANDROID_HOME'
]
ADB_PATH
=
os
.
path
.
join
(
android_home_dir
,
'sdk'
,
'platform-tools'
,
'adb'
)
def
_is_valid_adb_version
(
self
,
adb_version
):
# Sample output: 'Android Debug Bridge version 1.0.31'
version_fields
=
re
.
search
(
'(
\
d+)
\
.(
\
d+)
\
.(
\
d+)'
,
adb_version
)
if
version_fields
:
major_version
=
int
(
version_fields
.
group
(
1
))
minor_version
=
int
(
version_fields
.
group
(
2
))
patch_version
=
int
(
version_fields
.
group
(
3
))
if
major_version
>
1
:
return
True
if
major_version
==
1
and
minor_version
>
0
:
return
True
if
major_version
==
1
and
minor_version
==
0
and
patch_version
>=
32
:
return
True
return
False
else
:
logging
.
warn
(
'Unrecognized adb version string. Skipping version check.'
)
return
True
def
_check_for_adb
(
self
):
try
:
cmd
=
[
ADB_PATH
,
'version'
]
logging
.
info
(
' '
.
join
(
cmd
))
adb_version
=
subprocess
.
check_output
(
cmd
)
if
self
.
_is_valid_adb_version
(
adb_version
):
return
True
cmd
=
[
'which'
,
ADB_PATH
]
logging
.
info
(
' '
.
join
(
cmd
))
adb_path
=
subprocess
.
check_output
(
cmd
)
.
rstrip
()
logging
.
error
(
'"
%
s" is too old. Need 1.0.32 or later. '
'Try setting ANDROID_HOME.'
%
adb_path
)
return
False
except
OSError
:
logging
.
error
(
'"adb" (from the Android SDK) not in $PATH, cannot continue.'
)
return
False
return
True
def
_check_for_lollipop_or_later
(
self
):
try
:
# If the server is automatically restarted, then we get irrelevant
# output lines like this, which we want to ignore:
# adb server is out of date. killing..
# * daemon started successfully *
cmd
=
[
ADB_PATH
,
'start-server'
]
logging
.
info
(
' '
.
join
(
cmd
))
subprocess
.
call
(
cmd
)
cmd
=
[
ADB_PATH
,
'shell'
,
'getprop'
,
'ro.build.version.sdk'
]
logging
.
info
(
' '
.
join
(
cmd
))
sdk_version
=
subprocess
.
check_output
(
cmd
)
.
rstrip
()
# Sample output: '22'
if
not
sdk_version
.
isdigit
():
logging
.
error
(
'Unexpected response from getprop: "
%
s".'
%
sdk_version
)
return
False
if
int
(
sdk_version
)
<
22
:
logging
.
error
(
'Version "
%
s" of the Android SDK is too old. '
'Need Lollipop (22) or later. '
%
sdk_version
)
return
False
except
subprocess
.
CalledProcessError
as
e
:
# adb printed the error, so we print nothing.
return
False
return
True
def
_check_for_dart
(
self
):
def
_check_for_dart
(
self
):
try
:
try
:
cmd
=
[
DART_PATH
,
'--version'
]
cmd
=
[
DART_PATH
,
'--version'
]
...
@@ -895,16 +935,6 @@ class SkyShellRunner(object):
...
@@ -895,16 +935,6 @@ class SkyShellRunner(object):
def
main
(
self
):
def
main
(
self
):
logging
.
basicConfig
(
format
=
'
%(levelname)
s:
%(message)
s'
,
level
=
logging
.
WARNING
)
logging
.
basicConfig
(
format
=
'
%(levelname)
s:
%(message)
s'
,
level
=
logging
.
WARNING
)
self
.
_update_paths
()
# Checking for lollipop only needs to be done if we are starting an
# app, but it has an important side effect, which is to discard any
# progress messages if the adb server is restarted.
if
not
self
.
_check_for_adb
()
or
not
self
.
_check_for_lollipop_or_later
():
sys
.
exit
(
2
)
if
not
self
.
_check_for_dart
():
sys
.
exit
(
2
)
parser
=
argparse
.
ArgumentParser
(
description
=
'Sky App Runner'
)
parser
=
argparse
.
ArgumentParser
(
description
=
'Sky App Runner'
)
parser
.
add_argument
(
'--verbose'
,
dest
=
'verbose'
,
action
=
'store_true'
,
parser
.
add_argument
(
'--verbose'
,
dest
=
'verbose'
,
action
=
'store_true'
,
help
=
'Noisy logging, including all shell commands executed'
)
help
=
'Noisy logging, including all shell commands executed'
)
...
@@ -1008,6 +1038,9 @@ class SkyShellRunner(object):
...
@@ -1008,6 +1038,9 @@ class SkyShellRunner(object):
if
os
.
path
.
isdir
(
os
.
path
.
join
(
args
.
sky_src_path
,
args
.
ios_sim_debug_build_path
)):
if
os
.
path
.
isdir
(
os
.
path
.
join
(
args
.
sky_src_path
,
args
.
ios_sim_debug_build_path
)):
args
.
ios_sim_build_available
=
True
args
.
ios_sim_build_available
=
True
if
not
self
.
_check_for_dart
():
sys
.
exit
(
2
)
pids
=
Pids
.
read_from
(
PID_FILE_PATH
,
PID_FILE_KEYS
)
pids
=
Pids
.
read_from
(
PID_FILE_PATH
,
PID_FILE_KEYS
)
atexit
.
register
(
pids
.
write_to
,
PID_FILE_PATH
)
atexit
.
register
(
pids
.
write_to
,
PID_FILE_PATH
)
exit_code
=
0
exit_code
=
0
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment