Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
An opinionated workflow for Carthage
#!/usr/bin/env ruby
# Copyright (c) 2016 Tommaso Piazza
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software
# and associated documentation files (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of
# the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
# TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE
require 'fileutils'
require 'xcodeproj'
require 'colored'
require 'terminal-notifier'
require 'inifile'
#CONSTANTS
PROJECT_DIRECTORY = Dir.pwd
CARTHAGE_CHECKOUT_DIR = "Carthage/Checkouts"
CARTHAGE_BUILD_DIR = "Carthage/Build"
CARTHAGE_BUILD_DIR_BKP = "Carthage/_Build"
CARTHAGE_BUILD_DIR_IOS = "#{CARTHAGE_BUILD_DIR}/iOS"
$TARGET_NAME = ""
COPY_FRAMEWORK_PHASE_NAME = "Carthage Copy Frameworks"
ROMEFILE = "Romefile"
REPOSITORY_MAP = "RepositoryMap"
$PROJECT_MAP = {}
#FUNCTIONS
def get_framework_checkout_dir(framework_name)
framework_checkout_dir = File.join PROJECT_DIRECTORY, CARTHAGE_CHECKOUT_DIR, framework_name
framework_checkout_dir_bkp = File.join PROJECT_DIRECTORY, CARTHAGE_CHECKOUT_DIR, "_" << framework_name
return framework_checkout_dir, framework_checkout_dir_bkp
end
def get_carthage_build_dir(dir)
framework_build_dir = File.join dir, CARTHAGE_BUILD_DIR
framework_build_dir_bkp = File.join dir, CARTHAGE_BUILD_DIR_BKP
return framework_build_dir, framework_build_dir_bkp
end
def link_target_framework(framework_name)
framework_src_dir = File.join PROJECT_DIRECTORY, "..", framework_name
if Dir.exist? framework_src_dir
framework_checkout_dir, framework_checkout_dir_bkp = get_framework_checkout_dir framework_name
if File.symlink? framework_checkout_dir
puts "Nothing to do #{framework_checkout_dir} seems already set up for development."
else
#backup the current framework checkout
FileUtils.mv framework_checkout_dir, framework_checkout_dir_bkp if Dir.exist? framework_checkout_dir
#link the directory of the framwork to co-develop to Carthage/Checkouts
FileUtils.ln_s framework_src_dir, framework_checkout_dir
#backup the framwork's build folder
framework_build_dir, framework_build_dir_bkp = get_carthage_build_dir framework_checkout_dir
FileUtils.mv framework_build_dir, framework_build_dir_bkp if Dir.exist?(framework_build_dir)
#link the build folders
FileUtils.ln_s (File.join PROJECT_DIRECTORY, CARTHAGE_BUILD_DIR), framework_build_dir
end
else
puts "Error: Missing required dir #{framework_src_dir}."
return
end
end
def unlink_target_framework(framework_name)
framework_checkout_dir, framework_checkout_dir_bkp = get_framework_checkout_dir framework_name
framework_build_dir, framework_build_dir_bkp = get_carthage_build_dir framework_checkout_dir
if File.symlink? framework_checkout_dir
FileUtils.rm framework_build_dir if File.symlink? framework_build_dir
FileUtils.mv framework_build_dir_bkp, framework_build_dir if (Dir.exist? framework_build_dir_bkp) and !(Dir.exist? framework_build_dir)
FileUtils.rm framework_checkout_dir if File.symlink? framework_checkout_dir
FileUtils.mv framework_checkout_dir_bkp, framework_checkout_dir if (Dir.exist? framework_checkout_dir_bkp) and !(Dir.exist? framework_checkout_dir)
end
end
def open_target_xcode_project()
project_path = File.join PROJECT_DIRECTORY, "#{$TARGET_NAME}.xcodeproj"
project = Xcodeproj::Project.open(project_path)
return project
end
def save_target_xcode_project(project)
project.save("#{$TARGET_NAME} Dev.xcodeproj")
end
def add_framework_to_project(project, framework_name)
real_green_target = project.targets.select do |target|
target.name == $TARGET_NAME
end .first
framework_project_name = $PROJECT_MAP[framework_name]
if framework_project_name == nil
return
end
framework_project_path = File.join "..", framework_name, (framework_project_name + ".xcodeproj")
framework_project = Xcodeproj::Project.open(framework_project_path)
framework_target = framework_project.targets.select do |target|
target.name == framework_project_name
end .first
project.main_group.new_reference framework_project_path
real_green_target.add_dependency framework_target
framework_build_phase = real_green_target.frameworks_build_phases
frameworks_group = project.main_group["#{$TARGET_NAME}/Frameworks/"]
old_f = frameworks_group["#{framework_project_name}.framework"]
frameworks_group.children.delete old_f
framework_product_reference = frameworks_group.new_product_ref_for_target(framework_target.name, :framework)
delete_ref = framework_build_phase.files_references.select do |ref|
ref.path.include? "#{CARTHAGE_BUILD_DIR_IOS}/#{framework_project_name}.framework"
end .first
framework_build_phase.remove_file_reference delete_ref
framework_build_phase.add_file_reference framework_product_reference
framework_shell_script_build_phases = real_green_target.shell_script_build_phases
carthage_copy_phase = framework_shell_script_build_phases.select do |phase|
phase.name == COPY_FRAMEWORK_PHASE_NAME
end .first
carthage_copy_phase.input_paths.delete_if do |s|
s.include? (framework_project_name + ".framework")
end
carthage_copy_phase.input_paths << "$(BUILT_PRODUCTS_DIR)/#{framework_project_name}.framework"
end
def start_notifying(interval)
pid = fork do
sleeping = 0
while sleep interval do
sleeping += interval
TerminalNotifier.notify "Developing for #{sleeping/sleep_interval} hours", { :title => "🤖 develop.rb" , :subtitle => "#{ARGV[1]}" }
end
end
return pid
end
def read_project_map
romefile = nil
if File.exists? ROMEFILE
romefile = IniFile.load ROMEFILE
else
puts "Error: Missing Romefile"
exit 1
end
$PROJECT_MAP = romefile["RepositoryMap"]
if $PROJECT_MAP == nil
puts "Error: Missing section #{REPOSITORY_MAP} in #{ROMEFILE}"
exit 1
end
end
def update_target_name
$TARGET_NAME = ENV["TARGET_NAME"]
if $TARGET_NAME == nil
puts "Error: Enviroment variable "<< "TARGET_NAME" << " not found. Set this to the name of your project without .xcodeproj extension"
exit 1
end
end
def print_help()
puts "develop.rb <subcommand> FRAMEWORK [ADDITIONAL_FRAMEWORKS]"
puts ""
puts "Subcommands:"
puts "\t start\t Starts development on FRAMEWORK. ADDITIONAL_FRAMEWORKS is a list of space separated frameworks."
puts "\t stop\t Stop development on FRAMEWORK. ADDITIONAL_FRAMEWORKS is a list of space separated frameworks. You should never need to call this directly"
puts ""
end
#MAIN
if ARGV.length > 1
repos = ARGV.drop(1)
parent_pid = Process.pid
case ARGV[0]
when "start"
read_project_map
update_target_name
Signal.trap("INT") do
if Process.pid == parent_pid
puts
puts "Stopping development and cleaning up...".green
repos.each do | repo_name|
unlink_target_framework repo_name
end
exit
end
exit
end
project = open_target_xcode_project
puts project
repos.each do | repo_name|
link_target_framework repo_name
add_framework_to_project project, repo_name
end
save_target_xcode_project project
pid = start_notifying 3600
puts "Press ^C to stop development".red
Process.wait2 pid
when "stop"
read_project_map
update_target_name
repos.each do | repo_name|
unlink_target_framework repo_name
end
else
print_help
end
else
print_help
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment