Class: Rucola::Xcode

Inherits:
Object
  • Object
show all
Defined in:
lib/rucola/xcode.rb

Constant Summary collapse

NEW_COPY_FRAMEWORKS_BUILD_PHASE =
['519A79DB0CC8AE6B00CBE85D', {
  'name' => 'Copy Frameworks',
  'isa' => 'PBXCopyFilesBuildPhase',
  'buildActionMask' => '2147483647',
  'dstPath' => '',
  'dstSubfolderSpec' => 10, # TODO: is 10 the number for the location popup choice: Frameworks
  'runOnlyForDeploymentPostprocessing' => 0,
  'files' => []
}]

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(project_path) ⇒ Xcode

Returns a new instance of Xcode.



12
13
14
15
16
17
# File 'lib/rucola/xcode.rb', line 12

def initialize(project_path)
  @project_path = Pathname.new(project_path)
  @project = @project_path.basename.to_s.sub(/\.xcodeproj$/, '')
  @project_path_data = @project_path + 'project.pbxproj'
  @project_data = OSX::NSDictionary.dictionaryWithContentsOfFile(@project_path_data.to_s)
end

Instance Attribute Details

#projectObject (readonly)

FIXME: We should probably generate random id keys!



8
9
10
# File 'lib/rucola/xcode.rb', line 8

def project
  @project
end

#project_dataObject (readonly)

Returns the value of attribute project_data.



10
11
12
# File 'lib/rucola/xcode.rb', line 10

def project_data
  @project_data
end

#project_pathObject (readonly)

Returns the value of attribute project_path.



9
10
11
# File 'lib/rucola/xcode.rb', line 9

def project_path
  @project_path
end

Instance Method Details

#add_build_phase_to_project_target(object_id) ⇒ Object

Adds a build phase specified by object_id to the build phases of the project target.



74
75
76
77
78
# File 'lib/rucola/xcode.rb', line 74

def add_build_phase_to_project_target(object_id)
  # Add the new build phase to the main project target if it doesn't already exist
  build_target_id, build_target_values = object_for_project_target
  build_target_values['buildPhases'].push(object_id) unless build_target_values['buildPhases'].include?(object_id)
end

#add_object(object_id, object_values) ⇒ Object

Adds an object to the objects.



69
70
71
# File 'lib/rucola/xcode.rb', line 69

def add_object(object_id, object_values)
  @project_data['objects'][object_id] = object_values
end

#add_object_to_build_phase(object_id, build_phase_id) ⇒ Object



80
81
82
83
# File 'lib/rucola/xcode.rb', line 80

def add_object_to_build_phase(object_id, build_phase_id)
  build_phase = object_for_id(build_phase_id).last
  build_phase['files'].push(object_id) unless build_phase['files'].include?(object_id)
end

#bundle_framework(framework_name) ⇒ Object

Bundles the given framework in the application.



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/rucola/xcode.rb', line 116

def bundle_framework(framework_name)
  framework_id, framework_values = object_for_name(framework_name)
  
  # create a new file wrapper for in the copy build phase
  framework_in_build_phase_id = '511E98590CC8C5940003DED9'
  framework_in_build_phase_values = {
    'isa' => 'PBXBuildFile',
    'fileRef' => framework_id
  }
  add_object(framework_in_build_phase_id, framework_in_build_phase_values)
  
  # get or define the Copy Frameworks build phase
  build_phase = object_for_name('Copy Frameworks')
  if build_phase.nil?
    build_phase_id, build_phase_values = new_framework_copy_build_phase
    # add the new build phase to the objects
    add_object(build_phase_id, build_phase_values)
    
    # add the new build phase to the project target
    add_build_phase_to_project_target(build_phase_id)
  else
    build_phase_id, build_phase_values = build_phase
  end
  # add the framework to the build phase
  add_object_to_build_phase(framework_in_build_phase_id, build_phase_id)
end

#bundle_rubycocoa_frameworkObject

Bundles the RubyCocoa framework in the application.



144
145
146
# File 'lib/rucola/xcode.rb', line 144

def bundle_rubycocoa_framework
  bundle_framework 'RubyCocoa.framework'
end

#change_framework_location(framework_name, new_path_to_framework) ⇒ Object

Changes the path of the framework framework_name to the path new_path_to_framework.



104
105
106
107
108
# File 'lib/rucola/xcode.rb', line 104

def change_framework_location(framework_name, new_path_to_framework)
  framework_id, framework_values = object_for_name(framework_name)
  framework_values['path'] = new_path_to_framework
  framework_values['sourceTree'] = '<group>'
end

#change_rubycocoa_framework_location(new_path_to_framework) ⇒ Object

Changes the path of the RubyCocoa framework to new_path_to_framework.



111
112
113
# File 'lib/rucola/xcode.rb', line 111

def change_rubycocoa_framework_location(new_path_to_framework)
  change_framework_location 'RubyCocoa.framework', new_path_to_framework
end

#new_framework_copy_build_phaseObject

Creates a new framework copy build phase. It does not add it to the objects nor the build phases, do this with add_object and add_build_phase_to_project_target.

FIXME: Need to generate the id’s instead of static.



99
100
101
# File 'lib/rucola/xcode.rb', line 99

def new_framework_copy_build_phase
  NEW_COPY_FRAMEWORKS_BUILD_PHASE
end

#object_for_id(object_id) ⇒ Object

Returns the object for a given name. Returns an array: [id, values]



64
65
66
# File 'lib/rucola/xcode.rb', line 64

def object_for_id(object_id)
  @project_data['objects'][object_id].nil? ? nil : [object_id, @project_data['objects'][object_id]]
end

#object_for_name(name) ⇒ Object

Get’s the id & values for a object which name is the one passed to this method. Returns an array: [id, values]



46
47
48
# File 'lib/rucola/xcode.rb', line 46

def object_for_name(name)
  nil_if_empty @project_data['objects'].select { |object| object.last['name'] == name }.flatten
end

#object_for_project_targetObject

Returns the object that represents this projects target. Returns an array: [id, values]



58
59
60
# File 'lib/rucola/xcode.rb', line 58

def object_for_project_target
  nil_if_empty object_for_type_and_name('PBXNativeTarget', @project)
end

#object_for_type_and_name(type, name) ⇒ Object

Get’s the id & values for a object which type and name is the one passed to this method. Returns an array: [id, values]



52
53
54
# File 'lib/rucola/xcode.rb', line 52

def object_for_type_and_name(type, name)
  nil_if_empty @project_data['objects'].select { |object| object.last['isa'] == type and object.last['name'] == name }.flatten
end

#saveObject

Saves the project data atomically. Returns false if it failed.



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/rucola/xcode.rb', line 21

def save
  # FIXME: Because we use non generated id's atm (which is bad!)
  # we should first make a bakup of the project.
  unless $TESTING
    puts "\n========================================================================="
    puts "Backing up project #{@project}.xcodeproj to /tmp/#{@project}.xcodeproj.bak"
    puts "Please retrieve that one if for some reason the project was damaged!\n"
  end
  backup = "/tmp/#{@project}.xcodeproj.bak"
  Kernel.system("rm -rf #{backup}") if File.exists?(backup)
  Kernel.system("cp -R #{project_path} #{backup}")
  
  # this writes the plist as a new xml style plist,
  # but luckily xcode recognizes it as well and
  # writes it back out as an old style plist.
  @project_data.writeToFile_atomically(@project_path_data.to_s, true)
end