Class: Rucola::Xcode

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

Overview

:nodoc:

Constant Summary collapse

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

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(project_path) ⇒ Xcode

Returns a new instance of Xcode.



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

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::NSMutableDictionary.dictionaryWithContentsOfFile(@project_path_data.to_s)
end

Instance Attribute Details

#projectObject (readonly)

Returns the value of attribute project.



6
7
8
# File 'lib/rucola/xcode.rb', line 6

def project
  @project
end

#project_dataObject (readonly)

Returns the value of attribute project_data.



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

def project_data
  @project_data
end

#project_pathObject (readonly)

Returns the value of attribute project_path.



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

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.



76
77
78
79
80
# File 'lib/rucola/xcode.rb', line 76

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_framework(name, path) ⇒ Object

Adds a framework to a project. Returns [framework_obj, fileref_obj].



94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/rucola/xcode.rb', line 94

def add_framework(name, path)
  source_tree = path[0, 1] == '/' ? '<absolute>' : '<group>'
  framework_obj = [generate_object_id, { 'isa' => 'PBXFileReference', 'lastKnownFileType' => 'wrapper.framework', 'name' => name, 'path' => path, 'sourceTree' => source_tree }.to_ns]
  add_object(*framework_obj)
  
  fileref_obj = [generate_object_id, { 'fileRef' => framework_obj.first, 'isa' => 'PBXBuildFile'}.to_ns]
  add_object(*fileref_obj)
  
  linked_frameworks_group = object_for_name('Linked Frameworks')
  linked_frameworks_group.last['children'].push(framework_obj.first)
  
  [framework_obj, fileref_obj]
end

#add_object(object_id, object_values) ⇒ Object

Adds an object to the objects.



71
72
73
# File 'lib/rucola/xcode.rb', line 71

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

#add_object_to_build_phase(object_id, build_phase_id) ⇒ Object



82
83
84
85
# File 'lib/rucola/xcode.rb', line 82

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.



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/rucola/xcode.rb', line 150

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 = generate_object_id
  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.



178
179
180
# File 'lib/rucola/xcode.rb', line 178

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.



138
139
140
141
142
# File 'lib/rucola/xcode.rb', line 138

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.



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

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

#frameworksObject

Returns an array of framework objects that are in the project.



88
89
90
# File 'lib/rucola/xcode.rb', line 88

def frameworks
  objects.select {|obj| obj.last['name'].include?('framework') unless obj.last['name'].nil? }
end

#generate_object_idObject

Makes sure that an unique object UUID is returned



113
114
115
116
117
118
119
# File 'lib/rucola/xcode.rb', line 113

def generate_object_id
  uuids = objects.keys
  begin
   uuid = generate_uuid
  end while uuids.include?(uuid)
  uuid
end

#generate_uuidObject



108
109
110
# File 'lib/rucola/xcode.rb', line 108

def generate_uuid
  ("%04x%04x%04x%04x%04x%04x" % [rand(0x0010000),rand(0x0010000),rand(0x0010000),rand(0x0010000),rand(0x0010000),rand(0x0010000)]).upcase
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.



133
134
135
# File 'lib/rucola/xcode.rb', line 133

def new_framework_copy_build_phase
  [generate_object_id, NEW_COPY_FRAMEWORKS_BUILD_PHASE]
end

#object_for_id(object_id) ⇒ Object

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



66
67
68
# File 'lib/rucola/xcode.rb', line 66

def object_for_id(object_id)
  objects[object_id].nil? ? nil : [object_id, 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]



48
49
50
# File 'lib/rucola/xcode.rb', line 48

def object_for_name(name)
  nil_if_empty 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]



60
61
62
# File 'lib/rucola/xcode.rb', line 60

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]



54
55
56
# File 'lib/rucola/xcode.rb', line 54

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

#objectsObject



42
43
44
# File 'lib/rucola/xcode.rb', line 42

def objects
  @project_data['objects'] = @project_data['objects']
end

#saveObject

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



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

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