def dup_scheme(project_name, pbx_dir) userdata_dirs = Dir.foreach("#{pbx_dir}/xcuserdata").find_all { |x| /\.xcuserdatad$/.match(x) } userdata_dirs.each do |userdata_dir| scheme_to_find = Regexp.new(Regexp.escape("#{project_name}.xcscheme")) cal_scheme_to_find = Regexp.new(Regexp.escape("#{project_name}-cal.xcscheme")) schemes = Dir.foreach("#{pbx_dir}/xcuserdata/#{userdata_dir}/xcschemes") scheme = schemes.find do |scheme| scheme_to_find.match(scheme) end cal_scheme = schemes.find do |scheme| cal_scheme_to_find.match(scheme) end if scheme.nil? puts "-"*10 + "Warning" + "-"*10 puts "Unable to find scheme: #{project_name}.xcscheme." puts "You must manually create a scheme." puts "Make sure your scheme uses the Calabash build configuration." puts "-"*10 + "-------" + "-"*10 else if not cal_scheme.nil? msg("Warning") do puts "Scheme: #{project_name}-cal.xcscheme already exists." puts "Will not try to duplicate #{project_name}.xcscheme." end else msg("Action") do puts "Duplicating scheme #{project_name}.xcscheme as #{project_name}-cal.xcscheme" doc = REXML::Document.new(File.new("#{pbx_dir}/xcuserdata/#{userdata_dir}/xcschemes/#{scheme}")) doc.elements.each("Scheme/LaunchAction") do |la| la.attributes["buildConfiguration"] = "Calabash" end doc.write(File.open("#{pbx_dir}/xcuserdata/#{userdata_dir}/xcschemes/#{project_name}-cal.xcscheme", "w")) end end end end end def calabash_setup(args) puts "Checking if Xcode is running..." res = `ps x -o pid,command | grep -v grep | grep Xcode.app/Contents/MacOS/Xcode` if res=="" puts "Xcode not running." project_name, project_path, xpath = find_project_files(args) setup_project(project_name, project_path, xpath) dup_scheme(project_name, xpath) msg("Setup done") do puts "Please validate by running the #{project_name}-cal scheme" puts "from Xcode." puts "When starting the iOS Simulator using the" puts "new scheme: #{project_name}-cal, you should see:\n" puts ' "Started LPHTTP server on port 37265"' puts "\nin the application log in Xcode." puts "\n\n" puts "After validating, you can generate a features folder:" puts "Go to your project (the dir containing the .xcodeproj file)." puts "Then run calabash-ios gen" puts "(if you don't already have a features folder)." end else puts "Xcode is running. We'll be changing the project file so we'd better stop it." puts "Shall I stop Xcode? Please answer yes (y) or no (n)" answer = STDIN.gets.chomp if (answer == 'yes' or answer == 'y') res.split("\n").each do |line| pid = line.split(" ")[0] if system("kill #{pid}") puts "Stopped XCode. Retrying... " calabash_setup(args) else puts "Killing Xcode seemed to fail :( Aborting..." end end else puts "Please stop Xcode and try again." exit(0) end end end def find_project_files(args) dir_to_search, project_files = ensure_correct_path(args) xc_project_file = project_files[0] project_name = xc_project_file.split(".xcodeproj")[0] puts "Found Project: #{project_name}" pbx_dir = "#{dir_to_search}/#{xc_project_file}" pbx_files = Dir.foreach(pbx_dir).find_all { |x| /\.pbxproj$/.match(x) } if pbx_files.empty? puts "Found no *.pbxproj files in dir #{xc_project_file}." puts "Please setup calabash manually." exit 1 elsif pbx_files.count > 1 puts "Found several *.pbxproj files in dir #{xc_project_file}." puts "Found: #{pbx_files.join("\n")}" puts "We don't yet support this. Please setup calabash manually." exit 1 end return project_name, dir_to_search, File.expand_path("#{dir_to_search}/#{xc_project_file}") end def setup_project(project_name, project_path, path) ##Ensure exists and parse proj_file = "#{path}/project.pbxproj" if not File.exists?(proj_file) msg("Error") do puts "Directory #{path} doesn't contain #{proj_file}" end exit 1 end pbx = PBXProject::PBXProject.new(:file => proj_file) pbx.parse pwd = FileUtils.pwd FileUtils.cd project_path ##Backup msg("Info") do puts "Making backup of project file: #{proj_file}.bak" if File.exists? "#{proj_file}.bak" msg("Error") do puts "Backup file already exists. #{proj_file}.bak" puts "For safety, I won't overwrite this file." puts "You must manually move this file, if you want to" puts "Run calabash-ios setup again." end exit 1 end FileUtils.cp(proj_file, "#{proj_file}.bak") end file = 'calabash.framework' ##Download calabash.framework if not Dir.exists?(File.join(project_path, file)) msg("Info") do zip_file = 'calabash.framework-0.9.15.zip' puts "Did not find calabash.framework. I'll download the latest version...'" puts "https://raw.github.com/calabash/calabash-ios/master/frameworks/#{zip_file}" require 'uri' require 'net/https' uri = URI.parse "https://raw.github.com/calabash/calabash-ios/master/frameworks/#{zip_file}" Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') do |http| request = Net::HTTP::Get.new uri.request_uri http.request request do |response| open zip_file, 'wb' do |io| response.read_body do |chunk| print "." io.write chunk end end end end puts "\nDownload done: #{file}. Unzipping..." if not system("unzip -C -K -o -q -d #{project_path} #{zip_file}") msg("Error") do puts "Unable to unzip file: #{zip_file}" puts "You must install manually." end exit 1 end FileUtils.rm(zip_file) end else puts "Found calabash.framework. Will not download." end file_ref = pbx.sections['PBXFileReference'].find do |fr| /calabash\.framework/.match(fr.path) end if file_ref msg("Error") do puts "Your project already contains a file reference to calabash.framework." puts "I was not expecting this. Aborting." end exit 1 end msg("Info") do puts "Setting up project file for calabash-ios." end ## Augment f = PBXProject::PBXTypes::PBXFileReference.new(:path => file, :lastKnownFileType => "wrapper.framework", :sourceTree => '""') f.comment = "calabash.framework" pbx.add_item f bf = PBXProject::PBXTypes::PBXBuildFile.new(:comment => "calabash.framework in Frameworks", :fileRef => f.guid) bf.comment = "calabash.framework in Frameworks" pbx.add_item bf group = pbx.find_item :name => "Frameworks", :type => PBXProject::PBXTypes::PBXGroup group.add_children f build_phase_entry = PBXProject::PBXTypes::BasicValue.new(:value => bf.guid, :comment => bf.comment) pbx.sections['PBXFrameworksBuildPhase'][0].files << build_phase_entry #TODO first check if already linking with CFNetwork cfnet = pbx.find_item :name => "CFNetwork.framework", :type => PBXProject::PBXTypes::PBXFileReference unless cfnet f = PBXProject::PBXTypes::PBXFileReference.new(:path => "System/Library/Frameworks/CFNetwork.framework", :lastKnownFileType => "wrapper.framework", :sourceTree => 'SDKROOT') f.comment = "CFNetwork.framework" f.name = f.comment pbx.add_item f bf = PBXProject::PBXTypes::PBXBuildFile.new(:comment => "CFNetwork.framework in Frameworks", :fileRef => f.guid) bf.comment = "CFNetwork.framework in Frameworks" pbx.add_item bf group.add_children f build_phase_entry = PBXProject::PBXTypes::BasicValue.new(:value => bf.guid, :comment => bf.comment) pbx.sections['PBXFrameworksBuildPhase'][0].files << build_phase_entry end targets = pbx.sections['PBXNativeTarget'] target = nil if targets.count == 0 msg("Error") do puts "Unable to find targets in project." puts "Aborting..." end exit 1 elsif (targets.count == 1) target = targets[0] else preferred_target = targets.find { |t| t.name.value == project_name } msg("Question") do puts "You have several targets..." puts (targets.map { |t| t.name.value }).join("\n") found = nil until found do puts "Please specify which is your production app target." puts "Please enter target name." puts "Hit Enter for default choice: #{preferred_target.name.value}" unless preferred_target.nil? answer = STDIN.gets.chomp if (preferred_target and answer == '') target = preferred_target found = true else target = found = targets.find { |t| t.name.value == answer } end end end end bc_list_id = target.buildConfigurationList.value bc_list = pbx.find_item :guid => bc_list_id, :type => PBXProject::PBXTypes::XCConfigurationList bc_ref = bc_list.buildConfigurations.find { |bc| bc.comment =="Debug" } bc_id = bc_ref.value bc = pbx.find_item :guid => bc_id, :type => PBXProject::PBXTypes::XCBuildConfiguration cal_build_settings = bc.buildSettings.clone bc.buildSettings.each do |k, v| cal_build_settings[k] = v.clone end project_bc_id = pbx.sections['PBXProject'][0].buildConfigurationList.value project_bc_list = pbx.find_item :guid => project_bc_id, :type => PBXProject::PBXTypes::XCConfigurationList project_bc_ref = project_bc_list.buildConfigurations.find { |bc| bc.comment =="Debug" } project_bc_id = project_bc_ref.value project_bc = pbx.find_item :guid => project_bc_id, :type => PBXProject::PBXTypes::XCBuildConfiguration project_cal_build_settings = project_bc.buildSettings.clone project_bc.buildSettings.each do |k, v| project_cal_build_settings[k] = v.clone end ld_flags = cal_build_settings['OTHER_LDFLAGS'] || [] ld_flags << PBXProject::PBXTypes::BasicValue.new(:value => '"-force_load"') ld_flags << PBXProject::PBXTypes::BasicValue.new(:value => '"$(SRCROOT)/calabash.framework/calabash"') ld_flags << PBXProject::PBXTypes::BasicValue.new(:value => '"-lstdc++"') cal_build_settings['OTHER_LDFLAGS'] = ld_flags cal_bc = PBXProject::PBXTypes::XCBuildConfiguration.new(:name => "Calabash") cal_bc.buildSettings = cal_build_settings cal_bc.comment = "Calabash" project_cal_bc = PBXProject::PBXTypes::XCBuildConfiguration.new(:name => "Calabash") project_cal_bc.buildSettings = project_cal_build_settings project_cal_bc.comment = "Calabash" bc_list.buildConfigurations << PBXProject::PBXTypes::BasicValue.new(:value => cal_bc.guid, :comment => "Calabash") project_bc_list.buildConfigurations << PBXProject::PBXTypes::BasicValue.new(:value => project_cal_bc.guid, :comment => "Calabash") FileUtils.cd pwd pbx.sections['XCBuildConfiguration']< '"$(inherited)"') unless inherit sp << PBXProject::PBXTypes::BasicValue.new(:value => "\"$(SRCROOT)\"") unless srcroot bc.buildSettings["FRAMEWORK_SEARCH_PATHS"] = sp end pbx.write_to :file => proj_file end