class RunLoop::App
A class for interacting with .app bundles.
Attributes
@!attribute [r] path @return [String] The path to the app bundle .app
Public Class Methods
@!visibility private
Starting in Xcode
10 betas, this can happen if there was an incomplete install or uninstall.
# File lib/run_loop/app.rb, line 93 def self.cached_app_on_simulator?(app_bundle_path) return false if Dir[File.join(app_bundle_path, "**/*")].length != 2 return false if !app_bundle_path[RunLoop::Regex::CORE_SIMULATOR_UDID_REGEX] [File.join(app_bundle_path, "Info.plist"), File.join(app_bundle_path, "Icon.png")].all? do |file| File.exist?(file) end end
Creates a new App
instance.
@note The ‘app_bundle_path` is expanded during initialization.
@param [String] app_bundle_path A path to a .app @return [RunLoop::App] A instance of App
with a path.
# File lib/run_loop/app.rb, line 15 def initialize(app_bundle_path) @path = File.expand_path(app_bundle_path) if !App.valid?(app_bundle_path) if App.cached_app_on_simulator?(app_bundle_path) raise RuntimeError, %Q{ App is "cached" on the simulator. #{app_bundle_path} This can happen if there was an incomplete install or uninstall. Try manually deleting the application data container and relaunching the simulator. $ rm -r #{File.dirname(app_bundle_path)} $ run-loop simctl manage-processes } else raise ArgumentError, %Q{App does not exist at path or is not an app bundle. #{app_bundle_path} Bundle must: 1. be a directory that exists, 2. have a .app extension, 3. contain an Info.plist, 4. and the app binary (CFBundleExecutable) must exist } end end end
@!visibility private
# File lib/run_loop/app.rb, line 78 def self.valid?(app_bundle_path) return false if app_bundle_path.nil? return false if !File.directory?(app_bundle_path) return false if !File.extname(app_bundle_path) == ".app" return false if !self.info_plist_exist?(app_bundle_path) return false if !self.executable_file_exist?(app_bundle_path) true end
Private Class Methods
@!visibility private
# File lib/run_loop/app.rb, line 278 def self.executable_file_exist?(app_bundle_path) return false if !self.info_plist_exist?(app_bundle_path) info_plist = File.join(app_bundle_path, "Info.plist") pbuddy = RunLoop::PlistBuddy.new name = pbuddy.plist_read("CFBundleExecutable", info_plist) if name File.exist?(File.join(app_bundle_path, name)) else false end end
@!visibility private
# File lib/run_loop/app.rb, line 272 def self.info_plist_exist?(app_bundle_path) info_plist = File.join(app_bundle_path, "Info.plist") File.exist?(info_plist) end
Public Instance Methods
Returns the arches for the binary.
# File lib/run_loop/app.rb, line 133 def arches @arches ||= lipo.info end
Returns the CFBundleVersion of the app as Version
instance.
Apple docs:
CFBundleVersion specifies the build version number of the bundle, which identifies an iteration (released or unreleased) of the bundle. The build version number should be a string comprised of three non-negative, period-separated integers with the first integer being greater than zero. The string should only contain numeric (0-9) and period (.) characters. Leading zeros are truncated from each integer and will be ignored (that is, 1.02.3 is equivalent to 1.2.3).
@return [RunLoop::Version, nil] Returns a Version
instance if the
CFBundleVersion string is well formed and nil if not.
# File lib/run_loop/app.rb, line 233 def build_version string = plist_buddy.plist_read("CFBundleVersion", info_plist_path) begin version = RunLoop::Version.new(string) rescue if string && string != "" RunLoop.log_debug("CFBundleVersion: '#{string}' is not a well formed version string") else RunLoop.log_debug("CFBundleVersion is not defined in Info.plist") end version = nil end version end
Inspects the app’s Info.plist for the bundle identifier. @return [String] The value of CFBundleIdentifier. @raise [RuntimeError] If the plist cannot be read or the
CFBundleIdentifier is empty or does not exist.
# File lib/run_loop/app.rb, line 112 def bundle_identifier identifier = plist_buddy.plist_read("CFBundleIdentifier", info_plist_path) unless identifier raise "Expected key 'CFBundleIdentifier' in '#{info_plist_path}'" end identifier end
@!visibility private Return the fingerprint of the linked server
# File lib/run_loop/app.rb, line 161 def calabash_server_id name = plist_buddy.plist_read("CFBundleExecutable", info_plist_path) app_executable = File.join(self.path, name) strings(app_executable).server_id end
Inspects the app’s file for the server version
# File lib/run_loop/app.rb, line 150 def calabash_server_version version = nil executables.each do |executable| version = strings(executable).server_version break if version end version end
@!visibility private
# File lib/run_loop/app.rb, line 168 def codesign_info RunLoop::Codesign.info(path) end
@!visibility private
# File lib/run_loop/app.rb, line 173 def developer_signed? RunLoop::Codesign.developer?(path) end
@!visibility private
# File lib/run_loop/app.rb, line 178 def distribution_signed? RunLoop::Codesign.distribution?(path) end
Inspects the app’s Info.plist for the executable name. @return [String] The value of CFBundleExecutable. @raise [RuntimeError] If the plist cannot be read or the
CFBundleExecutable is empty or does not exist.
# File lib/run_loop/app.rb, line 124 def executable_name name = plist_buddy.plist_read("CFBundleExecutable", info_plist_path) unless name raise "Expected key 'CFBundleExecutable' in '#{info_plist_path}'" end name end
@!visibility private Collects the paths to executables in the bundle.
# File lib/run_loop/app.rb, line 253 def executables executables = [] Dir.glob("#{path}/**/*") do |file| next if skip_executable_check?(file) if otool.executable?(file) executables << file end end executables end
Returns the Info.plist path. @raise [RuntimeError] If there is no Info.plist.
# File lib/run_loop/app.rb, line 104 def info_plist_path @info_plist_path ||= File.join(path, 'Info.plist') end
@!visibility private
# File lib/run_loop/app.rb, line 68 def inspect to_s end
Returns the CFBundleShortVersionString of the app as Version
instance.
Apple docs:
CFBundleShortVersionString specifies the release version number of the bundle, which identifies a released iteration of the app. The release version number is a string comprised of three period-separated integers.
The first integer represents major revisions to the app, such as revisions that implement new features or major changes. The second integer denotes revisions that implement less prominent features. The third integer represents maintenance releases.
The value for this key differs from the value for CFBundleVersion, which identifies an iteration (released or unreleased) of the app. This key can be localized by including it in your InfoPlist.strings files.
@return [RunLoop::Version, nil] Returns a Version
instance if the
CFBundleShortVersion string is well formed and nil if not.
# File lib/run_loop/app.rb, line 201 def marketing_version string = plist_buddy.plist_read("CFBundleShortVersionString", info_plist_path) begin version = RunLoop::Version.new(string) rescue if string && string != "" RunLoop.log_debug("CFBundleShortVersionString: '#{string}' is not a well formed version string") else RunLoop.log_debug("CFBundleShortVersionString is not defined in Info.plist") end version = nil end version end
True if the app has been built for physical devices
# File lib/run_loop/app.rb, line 143 def physical_device? arches.any? do |arch| arch[/arm/, 0] end end
Returns the sha1 of the application.
# File lib/run_loop/app.rb, line 265 def sha1 RunLoop::Directory.directory_digest(path) end
True if the app has been built for the simulator
# File lib/run_loop/app.rb, line 138 def simulator? arches.include?("i386") || arches.include?("x86_64") end
@!visibility private
# File lib/run_loop/app.rb, line 50 def to_s cf_bundle_version = bundle_version cf_bundle_short_version = short_bundle_version if cf_bundle_version && cf_bundle_short_version version = "#{cf_bundle_version.to_s} / #{cf_bundle_short_version}" elsif cf_bundle_version version = cf_bundle_version.to_s elsif cf_bundle_short_version version = cf_bundle_short_version else version = "" end "#<APP #{bundle_identifier} #{version} #{path}>" end
Is this a valid app?
# File lib/run_loop/app.rb, line 73 def valid? App.valid?(path) end
Private Instance Methods
@!visibility private
# File lib/run_loop/app.rb, line 413 def build_artifact?(file) File.extname(file) == ".xcconfig" end
@!visibility private
# File lib/run_loop/app.rb, line 382 def code_signing_asset?(file) name = File.basename(file) extension = File.extname(file) dirname = File.basename(File.dirname(file)) name == "PkgInfo" || name == "embedded" || extension == ".mobileprovision" || extension == ".xcent" || dirname == "_CodeSignature" end
@!visibility private
# File lib/run_loop/app.rb, line 395 def core_data_asset?(file) extension = File.extname(file) dir_extension = File.extname(File.dirname(file)) dir_extension == ".momd" || extension == ".mom" || extension == ".db" || extension == ".omo" end
@!visibility private
# File lib/run_loop/app.rb, line 406 def font?(file) extension = File.extname(file) extension == ".ttf" || extension == ".otf" end
@!visibility private
# File lib/run_loop/app.rb, line 349 def image?(file) extension = File.extname(file) extension == ".jpeg" || extension == ".jpg" || extension == ".gif" || extension == ".png" || extension == ".tiff" || extension == ".svg" || extension == ".pdf" || extension == ".car" || file[/iTunesArtwork/, 0] end
@!visibility private
# File lib/run_loop/app.rb, line 291 def lipo @lipo ||= RunLoop::Lipo.new(path) end
@!visibility private
# File lib/run_loop/app.rb, line 369 def lproj_asset?(file) extension = File.extname(file) dir_extension = File.extname(File.dirname(file)) dir_extension == ".lproj" || dir_extension == ".storyboard" || dir_extension == ".storyboardc" || extension == ".strings" || extension == ".xib" || extension == ".nib" end
@!visibility private
# File lib/run_loop/app.rb, line 301 def otool @otool ||= RunLoop::Otool.new(xcode) end
@!visibility private
# File lib/run_loop/app.rb, line 364 def plist?(file) File.extname(file) == ".plist" end
@!visibility private
# File lib/run_loop/app.rb, line 296 def plist_buddy @plist_buddy ||= RunLoop::PlistBuddy.new end
@!visibility private
# File lib/run_loop/app.rb, line 317 def skip_executable_check?(file) File.directory?(file) || image?(file) || text?(file) || plist?(file) || lproj_asset?(file) || code_signing_asset?(file) || core_data_asset?(file) || font?(file) || build_artifact?(file) end
@!visibility private A strings factory
# File lib/run_loop/app.rb, line 312 def strings(file) RunLoop::Strings.new(file) end
@!visibility private
# File lib/run_loop/app.rb, line 330 def text?(file) extension = File.extname(file) filename = File.basename(file) extension == ".txt" || extension == ".md" || extension == ".html" || extension == ".xml" || extension == ".json" || extension == ".yaml" || extension == ".yml" || extension == ".rtf" || ["NOTICE", "LICENSE", "README", "ABOUT"].any? do |elm| filename[/#{elm}/] end end
@!visibility private
# File lib/run_loop/app.rb, line 306 def xcode @xcode ||= RunLoop::Xcode.new end