初投稿です。Sumallyのエンジニアです。主にSumally for iOSとSumally Pocket for iOSの開発をしてます。
さて、Sumally Pocket for iOSはSwift 1.2で開発しましたが、iOS7対応をしていたため、ライブラリはgit submoduleとCocoaPodsで入れていました。
ということで今更になってしまったのですが、Swift 2.0移行及びiOS7対応を切る作業に取りかかりはじめたので、ビルドが遅くなってきていることもあり、これを機にCarthageを導入することにしました。現段階ではすべてを移行することはできないのでCarthageとCocoaPodsとの両方を使っています。
導入してみて、ビルドのスピードは大改善され大満足なのですが、ライブラリを追加する手間がCarthageは結構かかるなと思いました。
ということで、carthage update
からプロジェクトへの追加やその他の設定までをするスクリプトを書きました。
スクリプトの流れ
- (スクリプト内で指定可能 / 後々コマンドからのオプションで指定できるようにするかもです)
carthage update --platform iOS
の実行。 - (1が実行された場合のみ)ビルドされたframeworkをプロジェクトに追加。
- Build Phasesの
carthage copy-frameworks
のスクリプトのInput Filesの更新(まだ追加だけの対応です)。 - (Podfileがある場合・スクリプト内で指定可能 / 後々コマンドからのオプションで指定できるようにするかもです)pod installの実行。
- (スクリプト内で指定があれば)CocoaPodsのPods-Acknowledgements.plistをベースにCocoaPodsとCarthageのAcknowledgementsファイルの生成。
プロジェクトファイルの下準備
実装を端折ったので、下準備をお願いします。
- プロジェクトからこれまでのCarthage/Build以下へのReferenceをすべて消す。
- Xcodeプロジェクトの直下にFile > New > Groupを選択し
Carthage
という名前をつける。(今後Carthage/Build/*.frameworkはこの直下に入っていきます) - 作成した
Carthage
グループのLocation(Xcode画面右側から指定)をRelative To Groupにする(デフォルトでなってます)。また、その右下あたりのフォルダアイコンをクリックしパネルからプロジェクト直下にある(なければ作ってください)Carthageフォルダを選択する。 - XcodeプロジェクトのターゲットのBuild Phasesのタブを選択する。
- すでにCarthageの
copy-frameworks
のスクリプトのRun ScriptのPhaseがある方は、そのPhase名をRun Carthage Script
に変更する。ない人は新しく追加する。手順は次の通り。- 左上の+ボタンから
New Run Script Phase
を選択する。 - スクリプトに
/usr/local/bin/carthage copy-frameworks
を指定する。 - Phase名をRun Scriptから
Run Carthage Script
に変更する。
- 左上の+ボタンから
手順3と4はそれぞれのターゲット(例えばステージング版と本番版など)で必要です。
実行方法
こちらからダウンロードして好きなところに配置してください。僕はプロジェクトルートの直下にあるtools
というフォルダの中に入れてます。
実行にはCocoaPods
、colorize
が必要なのでgem
で入れましょう。
sudo gem install cocoapods
sudo gem install colorize
あとはうえでダウンロード&配置したスクリプトを実行するだけです。
./{スクリプト名}.rb
配置した場所によってはエラーが出るかもなので、エラーが出た人は次の「スクリプトの説明」を読んでください。
スクリプトの説明
コメント書いてます。カスタマイズしようと思ったらこの辺をいじっていただけたらと思います。
#!/usr/bin/env ruby
require 'xcodeproj'
require 'colorize'
Dir.chdir "#{__dir__}/../" # プロジェクトのルートへのパスを指定してください(__dir__はこのRubyファイルのあるディレクトリの絶対パスです)
class LibraryUpdateManager
attr_accessor :update_carthage, :update_cocoapods, :acknowledgements_path
def initialize
@update_carthage = true
@update_cocoapods = File.exists?('Podfile')
end
def perform_update
begin
if @update_carthage
puts 'update carthage🏃'.bold
_update_carthage
puts 'finish updating carthage🍺'.green
end
if @update_cocoapods
puts 'update cocoapods🏃'.bold
_update_cocoapods
puts 'finish updating cocoapods🍺'.green
end
if !@acknowledgements_path.nil?
puts 'update acknowledgements🏃'.bold
_update_acknowledgements
puts 'finish updating acknowledgements🍺'.green
end
puts 'complete update libraries🎉'.green
rescue => error
puts "#{error.message}😱".red
end
end
private
def _update_carthage
raise 'carthage update failure' if !system('carthage update --platform iOS')
_update_projects
end
def _update_projects
project = Xcodeproj::Project.open(*Dir.glob('*.xcodeproj'))
carthage_group = project.groups.find do |group|
group.path == 'Carthage'
end
if !carthage_group
raise 'carthage group not found'
end
existing_file_names = carthage_group.files.map do |file|
file.name
end
new_framework_paths = Dir.glob('Carthage/Build/iOS/*.framework').select do |framework|
!existing_file_names.include?(File.basename(framework))
end
if new_framework_paths.empty?
return
end
framework_references = new_framework_paths.map do |framework_path|
path = File.basename(framework_path)
carthage_group.new_file("Build/iOS/#{path}")
end
project.targets.each do |target|
next if target.product_type == 'com.apple.product-type.bundle.unit-test'
run_carthage_script_phase = target.build_phases.find do |phase|
phase.display_name == 'Run Carthage Script'
end
next if run_carthage_script_phase.nil?
framework_references.each do |reference|
target.frameworks_build_phase.add_file_reference(reference)
end
new_input_paths = new_framework_paths.map do |path|
"$(SRCROOT)/#{path}"
end
input_paths_set = run_carthage_script_phase.input_paths.to_set
run_carthage_script_phase.input_paths = input_paths_set.merge(new_input_paths).to_a
end
project.save
end
def _update_cocoapods
raise 'pod install failure' if !system('pod install')
end
def _update_acknowledgements
cocoapods_acknowledgement = Xcodeproj::PlistHelper.read('Pods/Target Support Files/Pods/Pods-Acknowledgements.plist')
carthage_preference_specifiers = Dir.glob('Carthage/Checkouts/**/LICENSE').map do |license|
{
:FooterText => IO.read(license, :encoding => 'utf-8'),
:Title => File.basename(File.dirname(license)),
:Type => 'PSGroupSpecifier',
}
end
key = 'PreferenceSpecifiers'
cocoapods_acknowledgement[key].insert(cocoapods_acknowledgement[key].length - 1, *carthage_preference_specifiers)
Xcodeproj::PlistHelper.write(cocoapods_acknowledgement, @acknowledgements_path)
end
end
if __FILE__ == $PROGRAM_NAME
manager = LibraryUpdateManager.new
manager.update_carthage = true # carthage updateを実行するかどうか(default: true)
manager.update_cocoapods = true # pod installを実行するかどうか(default: Podfileがあればtrue, なければfalse)
manager.acknowledgements_path = 'Sumally/Resources/Settings.bundle/Acknowledgements.plist' # default: nil / Acknowledgements.plistへのパス / nilの時は更新しない
manager.perform_update
end