module Calabash::Android::Dependencies

Public Class Methods

aapt_path() click to toggle source
# File lib/calabash-android/dependencies.rb, line 63
def self.aapt_path
    android_dependencies(:aapt_path)
end
adb_path() click to toggle source
# File lib/calabash-android/dependencies.rb, line 59
def self.adb_path
    android_dependencies(:adb_path)
end
android_jar_path() click to toggle source
# File lib/calabash-android/dependencies.rb, line 71
def self.android_jar_path
    android_dependencies(:android_jar_path)
end
ant_path() click to toggle source
# File lib/calabash-android/dependencies.rb, line 87
def self.ant_path
    java_dependencies(:ant_path)
end
jarsigner_path() click to toggle source
# File lib/calabash-android/dependencies.rb, line 83
def self.jarsigner_path
    java_dependencies(:jarsigner_path)
end
java_path() click to toggle source
# File lib/calabash-android/dependencies.rb, line 75
def self.java_path
    java_dependencies(:java_path)
end
keytool_path() click to toggle source
# File lib/calabash-android/dependencies.rb, line 79
def self.keytool_path
    java_dependencies(:keytool_path)
end
setup() click to toggle source
# File lib/calabash-android/dependencies.rb, line 91
def self.setup
    if ENV['CI_NO_ANDROID_RUNTIME'] == '1'
        @@android_dependencies = {}
        @@java_dependencies = {}
        return
    end

    @@halt_scanning = false
    @@halt_scanning_thread = nil

    if ENV['ANDROID_HOME']
        android_sdk_location = ENV['ANDROID_HOME']
        Logging.log_debug("Setting Android SDK location to $ANDROID_HOME")
    else
        android_sdk_location = detect_android_sdk_location
    end

    if android_sdk_location.nil?
        Logging.log_error 'Could not find an Android SDK please make sure it is installed.'
        Logging.log_error 'You can read about how Calabash is searching for an Android SDK and how you can help here:'
        Logging.log_error 'https://github.com/calabash/calabash-android/blob/master/documentation/installation.md#prerequisites'

        raise 'Could not find an Android SDK'
    end

    Logging.log_debug("Android SDK location set to '#{android_sdk_location}'")

    @@halt_scanning_thread = Thread.new do
        sleep 60
        @@halt_scanning = true
    end

    begin
        set_android_dependencies(locate_android_dependencies(android_sdk_location))
    rescue ScanningTimedOutError => e
        Logging.log_error 'Timed out locating Android dependency'
        Logging.log_error 'You can read about how Calabash is searching for an Android SDK and how you can help here:'
        Logging.log_error 'https://github.com/calabash/calabash-android/blob/master/documentation/installation.md#prerequisites'

        raise e.message
    rescue Environment::InvalidEnvironmentError => e
        Logging.log_error 'Could not locate Android dependency'
        Logging.log_error 'You can read about how Calabash is searching for an Android SDK and how you can help here:'
        Logging.log_error 'https://github.com/calabash/calabash-android/blob/master/documentation/installation.md#prerequisites'

        raise e
    end

    if ENV['JAVA_HOME']
        java_sdk_home = ENV['JAVA_HOME']
        Logging.log_debug("Setting Java SDK location to $JAVA_HOME")
    else
        java_sdk_home = detect_java_sdk_location
    end

    Logging.log_debug("Java SDK location set to '#{java_sdk_home}'")

    Thread.kill(@@halt_scanning_thread) if @@halt_scanning_thread
    @@halt_scanning = false

    @@halt_scanning_thread = Thread.new do
        sleep 60
        @@halt_scanning = true
    end

    begin
        set_java_dependencies(locate_java_dependencies(java_sdk_home))
    rescue ScanningTimedOutError => e
        Logging.log_error 'Timed out locating Java dependency'
        Logging.log_error "You can read about how Calabash is searching for a JDK and how you can help here:"
        Logging.log_error "https://github.com/calabash/calabash-android/blob/master/documentation/installation.md#prerequisites"

        raise e.message
    rescue Environment::InvalidJavaSDKHome => e
        Logging.log_error "Could not find Java Development Kit please make sure it is installed."
        Logging.log_error "You can read about how Calabash is searching for a JDK and how you can help here:"
        Logging.log_error "https://github.com/calabash/calabash-android/blob/master/documentation/installation.md#prerequisites"

        raise e
    rescue Environment::InvalidEnvironmentError => e
        Logging.log_error "Could not find Java dependency"
        Logging.log_error "You can read about how Calabash is searching for a JDK and how you can help here:"
        Logging.log_error "https://github.com/calabash/calabash-android/blob/master/documentation/installation.md#prerequisites"

        raise e
    end

    Thread.kill(@@halt_scanning_thread) if @@halt_scanning_thread
end
zipalign_path() click to toggle source
# File lib/calabash-android/dependencies.rb, line 67
def self.zipalign_path
    android_dependencies(:zipalign_path)
end

Private Class Methods

aapt_executable() click to toggle source
# File lib/calabash-android/dependencies.rb, line 505
def self.aapt_executable
    is_windows? ? 'aapt.exe' : 'aapt'
end
adb_executable() click to toggle source
# File lib/calabash-android/dependencies.rb, line 501
def self.adb_executable
    is_windows? ? 'adb.exe' : 'adb'
end
android_dependencies(key) click to toggle source
# File lib/calabash-android/dependencies.rb, line 23
def self.android_dependencies(key)
    setup unless defined?(@@android_dependencies)

    if @@android_dependencies.has_key?(key)
        file = @@android_dependencies[key]

        unless File.exists?(file)
            raise "No such file '#{file}'"
        end

        file
    else
        raise "No such dependency '#{key}'"
    end
end
ant_executable() click to toggle source
# File lib/calabash-android/dependencies.rb, line 509
def self.ant_executable
    is_windows? ? 'ant.exe' : 'ant'
end
detect_android_sdk_location() click to toggle source
# File lib/calabash-android/dependencies.rb, line 382
def self.detect_android_sdk_location
    if File.exist?(monodroid_config_file)
        sdk_location = read_attribute_from_monodroid_config('android-sdk', 'path')

        if sdk_location
            Logging.log_debug("Setting Android SDK location from '#{monodroid_config_file}'")

            return sdk_location
        end
    end

    if File.exist?(File.expand_path('~/Library/Developer/Xamarin/android-sdk-mac_x86/'))
        return File.expand_path('~/Library/Developer/Xamarin/android-sdk-mac_x86/')
    end
    
    if File.exist?(File.expand_path('~/Library/Developer/Xamarin/android-sdk-macosx/'))
        return File.expand_path('~/Library/Developer/Xamarin/android-sdk-macosx/')
    end
    
    # Default location when installing with Android Studio
    if File.exist?(File.expand_path('~/Library/Android/sdk/'))
        return File.expand_path('~/Library/Android/sdk/')
    end
    
    if File.exist?('C:\\Android\\android-sdk')
        return 'C:\\Android\\android-sdk'
    end

    if is_windows?
        from_registry = read_registry(::Win32::Registry::HKEY_CURRENT_USER, "Software\\Novell\\Mono for Android", 'AndroidSdkDirectory')

        if from_registry && File.exist?(from_registry)
            Logging.log_debug("Setting Android SDK location from HKEY_CURRENT_USER Software\\Novell\\Mono for Android")
            return from_registry
        end

        from_registry = read_registry(::Win32::Registry::HKEY_LOCAL_MACHINE, 'Software\\Android SDK Tools', 'Path')

        if from_registry && File.exist?(from_registry)
            Logging.log_debug("Setting Android SDK location from HKEY_LOCAL_MACHINE Software\\Android SDK Tools")
            return from_registry
        end
    end

    nil
end
detect_java_sdk_location() click to toggle source
# File lib/calabash-android/dependencies.rb, line 429
def self.detect_java_sdk_location
    if File.exist?(monodroid_config_file)
        sdk_location = read_attribute_from_monodroid_config('java-sdk', 'path')

        if sdk_location
            Logging.log_debug("Setting Java SDK location from '#{monodroid_config_file}'")

            return sdk_location
        end
    end

    java_versions = ['1.9', '1.8', '1.7', '1.6']

    if is_windows?
        java_versions.each do |java_version|
            key = "SOFTWARE\\JavaSoft\\Java Development Kit\\#{java_version}"
            from_registry = read_registry(::Win32::Registry::HKEY_LOCAL_MACHINE, key, 'JavaHome')

            if from_registry && File.exist?(from_registry)
                Logging.log_debug("Setting Java SDK location from HKEY_LOCAL_MACHINE #{key}")
                return from_registry
            end
        end
    end

    nil
end
find_executable_on_path(executable) click to toggle source
# File lib/calabash-android/dependencies.rb, line 471
def self.find_executable_on_path(executable)
    path_elements.each do |x|
        f = File.join(x, executable)
        return f if File.exists?(f)
    end

    nil
end
halt_scanning?() click to toggle source
# File lib/calabash-android/dependencies.rb, line 340
def self.halt_scanning?
    @@halt_scanning
end
is_windows?() click to toggle source
# File lib/calabash-android/dependencies.rb, line 513
def self.is_windows?
    (RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/)
end
jarsigner_executable() click to toggle source
# File lib/calabash-android/dependencies.rb, line 489
def self.jarsigner_executable
    is_windows? ? 'jarsigner.exe' : 'jarsigner'
end
java_dependencies(key) click to toggle source
# File lib/calabash-android/dependencies.rb, line 39
def self.java_dependencies(key)
    setup unless defined?(@@java_dependencies)

    if key == :ant_path
        ant_executable
    elsif @@java_dependencies.has_key?(key)
        file = @@java_dependencies[key]

        unless File.exists?(file)
            raise "No such file '#{file}'"
        end

        file
    else
        raise "No such dependency '#{key}'"
    end
end
java_executable() click to toggle source
# File lib/calabash-android/dependencies.rb, line 493
def self.java_executable
    is_windows? ? 'java.exe' : 'java'
end
keytool_executable() click to toggle source
# File lib/calabash-android/dependencies.rb, line 497
def self.keytool_executable
    is_windows? ? 'keytool.exe' : 'keytool'
end
list_files(path) click to toggle source
# File lib/calabash-android/dependencies.rb, line 525
def self.list_files(path)
    # Dir.glob does not accept backslashes, even on windows. We have to
    # substitute all backwards slashes to forward.
    # C:\foo becomes C:/foo

    if is_windows?
        Dir.glob(File.join(path, '*').gsub('\\', '/'))
    else
        Dir.glob(File.join(path, '*'))
    end
end
locate_android_dependencies(android_sdk_location) click to toggle source
# File lib/calabash-android/dependencies.rb, line 227
def self.locate_android_dependencies(android_sdk_location)
    adb_path = scan_for_path(android_sdk_location, adb_executable, ['platform-tools'])
    aapt_path = scan_for_path(android_sdk_location, aapt_executable, tools_directories(android_sdk_location))
    zipalign_path = scan_for_path(android_sdk_location, zipalign_executable, tools_directories(android_sdk_location))

    if adb_path.nil?
        raise Environment::InvalidEnvironmentError,
              "Could not find '#{adb_executable}' in '#{android_sdk_location}'"
    end

    if aapt_path.nil?
        raise Environment::InvalidEnvironmentError,
              "Could not find '#{aapt_executable}' in '#{android_sdk_location}'"
    end

    if zipalign_path.nil?
        raise Environment::InvalidEnvironmentError,
              "Could not find '#{zipalign_executable}' in '#{android_sdk_location}'"
    end

    Logging.log_debug("Set aapt path to '#{aapt_path}'")
    Logging.log_debug("Set zipalign path to '#{zipalign_path}'")
    Logging.log_debug("Set adb path to '#{adb_path}'")

    android_jar_path = scan_for_path(File.join(android_sdk_location, 'platforms'), 'android.jar', [File.basename(platform_directory(android_sdk_location))])

    if android_jar_path.nil?
        raise Environment::InvalidEnvironmentError,
              "Could not find 'android.jar' in '#{File.join(android_sdk_location, 'platforms')}'"
    end

    Logging.log_debug("Set android jar path to '#{android_jar_path}'")

    {
        aapt_path: aapt_path,
        zipalign_path: zipalign_path,
        adb_path: adb_path,
        android_jar_path: android_jar_path
    }
end
locate_java_dependencies(java_sdk_location) click to toggle source
# File lib/calabash-android/dependencies.rb, line 268
def self.locate_java_dependencies(java_sdk_location)
    # For the Java dependencies, we will use the PATH elements of they exist
    on_path = find_executable_on_path(java_executable)

    if on_path
        Logging.log_debug('Found java on PATH')
        java_path = on_path
    else
        if java_sdk_location.nil? || java_sdk_location.empty?
            raise Environment::InvalidJavaSDKHome,
                  "Could not locate '#{java_executable}' on path, and Java SDK Home is invalid."
        end

        java_path = scan_for_path(java_sdk_location, java_executable, ['bin'])
    end

    Logging.log_debug("Set java path to '#{java_path}'")

    on_path = find_executable_on_path(keytool_executable)

    if on_path
        Logging.log_debug('Found keytool on PATH')
        keytool_path = on_path
    else
        if java_sdk_location.nil? || java_sdk_location.empty?
            raise Environment::InvalidJavaSDKHome,
                  "Could not locate '#{keytool_executable}' on path, and Java SDK Home is invalid."
        end

        keytool_path = scan_for_path(java_sdk_location, keytool_executable, ['bin'])
    end

    Logging.log_debug("Set keytool path to '#{keytool_path}'")

    on_path = find_executable_on_path(jarsigner_executable)

    if on_path
        Logging.log_debug('Found jarsigner on PATH')
        jarsigner_path = on_path
    else
        if java_sdk_location.nil? || java_sdk_location.empty?
            raise Environment::InvalidJavaSDKHome,
                  "Could not locate '#{jarsigner_executable}' on path, and Java SDK Home is invalid."
        end

        jarsigner_path = scan_for_path(java_sdk_location, jarsigner_executable, ['bin'])
    end

    Logging.log_debug("Set jarsigner path to '#{jarsigner_path}'")

    if java_path.nil?
        raise Environment::InvalidEnvironmentError,
              "Could not find '#{java_executable}' on PATH or in '#{java_sdk_location}'"
    end

    if keytool_path.nil?
        raise Environment::InvalidEnvironmentError,
              "Could not find '#{keytool_executable}' on PATH or in '#{java_sdk_location}'"
    end

    if jarsigner_path.nil?
        raise Environment::InvalidEnvironmentError,
              "Could not find '#{jarsigner_executable}' on PATH or in '#{java_sdk_location}'"
    end

    {
        java_path: java_path,
        keytool_path: keytool_path,
        jarsigner_path: jarsigner_path
    }
end
monodroid_config_file() click to toggle source
# File lib/calabash-android/dependencies.rb, line 457
def self.monodroid_config_file
    File.expand_path('~/.config/xbuild/monodroid-config.xml')
end
path_elements() click to toggle source
# File lib/calabash-android/dependencies.rb, line 480
def self.path_elements
    return [] unless ENV['PATH']
    ENV['PATH'].split (/[:;]/)
end
platform_directory(android_sdk_location) click to toggle source
# File lib/calabash-android/dependencies.rb, line 213
def self.platform_directory(android_sdk_location)
    files = list_files(File.join(android_sdk_location, 'platforms'))
                .select {|file| File.directory?(file)}

    sorted_files = files.sort_by {|item| '%08s' % item.split('-').last}.reverse

    if sorted_files.first.nil?
        raise Environment::InvalidEnvironmentError,
              "Could not find any platform directory in '#{File.join(android_sdk_location, 'platforms')}'"
    end

    File.join('platforms', File.basename(sorted_files.first))
end
read_attribute_from_monodroid_config(element, attribute) click to toggle source
# File lib/calabash-android/dependencies.rb, line 461
def self.read_attribute_from_monodroid_config(element, attribute)
    element = REXML::Document.new(IO.read(monodroid_config_file)).elements["//#{element}"]

    if element
        element.attributes[attribute]
    else
        nil
    end
end
read_registry(root_key, key, value) click to toggle source
# File lib/calabash-android/dependencies.rb, line 517
def self.read_registry(root_key, key, value)
    begin
        root_key.open(key)[value]
    rescue
        nil
    end
end
scan_for_path(path, file_name, expected_sub_folders = nil) click to toggle source
# File lib/calabash-android/dependencies.rb, line 344
def self.scan_for_path(path, file_name, expected_sub_folders = nil)
    if self.halt_scanning?
        Logging.log_error("Timed out looking for '#{file_name}', currently looking in '#{path}'")
        raise ScanningTimedOutError, "Timed out looking for '#{file_name}'"
    end

    # Optimization for expected folders
    if expected_sub_folders && !expected_sub_folders.empty?
        expected_sub_folders.each do |expected_sub_folder|
            result = scan_for_path(File.join(path, expected_sub_folder), file_name)

            return result if result
        end

        Logging.log_warn("Did not find '#{file_name}' in any standard directory of '#{path}'. Calabash will therefore take longer to load")
        Logging.log_debug(" - Expected to find '#{file_name}' in any of:")

        expected_sub_folders.each do |expected_sub_folder|
            Logging.log_debug(" - #{File.join(path, expected_sub_folder)}")
        end
    end

    files = list_files(path).sort.reverse

    if files.reject{|file| File.directory?(file)}.
        map{|file| File.basename(file)}.include?(file_name)
        return File.join(path, file_name)
    else
        files.select{|file| File.directory?(file)}.each do |dir|
            result = scan_for_path(dir, file_name)

            return result if result
        end
    end

    nil
end
set_android_dependencies(android_dependencies) click to toggle source
# File lib/calabash-android/dependencies.rb, line 15
def self.set_android_dependencies(android_dependencies)
    @@android_dependencies = android_dependencies
end
set_java_dependencies(java_dependencies) click to toggle source
# File lib/calabash-android/dependencies.rb, line 19
def self.set_java_dependencies(java_dependencies)
    @@java_dependencies = java_dependencies
end
tools_directories(android_sdk_location) click to toggle source
# File lib/calabash-android/dependencies.rb, line 189
def self.tools_directories(android_sdk_location)
    build_tools_files = list_files(File.join(android_sdk_location, 'build-tools')).select {|file| File.directory?(file)}

    build_tools_directories =
        build_tools_files.select do |dir|
            begin
                Calabash::Android::Version.new(File.basename(dir))
                true
            rescue ArgumentError
                false
            end
        end.sort do |a, b|
            Calabash::Android::Version.compare(Calabash::Android::Version.new(File.basename(a)), Calabash::Android::Version.new(File.basename(b)))
        end.reverse.map{|dir| File.join('build-tools', File.basename(dir))}

    if build_tools_directories.empty?
        unless build_tools_files.reverse.first.nil?
            build_tools_directories = [File.join('build-tools', File.basename(build_tools_files.reverse.first))]
        end
    end

    build_tools_directories + ['platform-tools', 'tools']
end
tools_directory() click to toggle source
# File lib/calabash-android/dependencies.rb, line 183
def self.tools_directory
    tools_directories = tools_directories(ENV['ANDROID_HOME'])

    File.join(ENV['ANDROID_HOME'], tools_directories.first)
end
zipalign_executable() click to toggle source
# File lib/calabash-android/dependencies.rb, line 485
def self.zipalign_executable
    is_windows? ? 'zipalign.exe' : 'zipalign'
end