Class: Rucola::Xcode

Inherits:
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' => [].to_ns
}]

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::NSMutableDictionary.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.



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

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.



73
74
75
# File 'lib/rucola/xcode.rb', line 73

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

#add_object_to_build_phase(object_id, build_phase_id) ⇒ Object



84
85
86
87
# File 'lib/rucola/xcode.rb', line 84

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.



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

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.



148
149
150
# File 'lib/rucola/xcode.rb', line 148

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.



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

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.



115
116
117
# File 'lib/rucola/xcode.rb', line 115

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.



103
104
105
# File 'lib/rucola/xcode.rb', line 103

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]



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

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]



50
51
52
# File 'lib/rucola/xcode.rb', line 50

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]



62
63
64
# File 'lib/rucola/xcode.rb', line 62

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]



56
57
58
# File 'lib/rucola/xcode.rb', line 56

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



44
45
46
# File 'lib/rucola/xcode.rb', line 44

def objects
  @project_data['objects'] = @project_data['objects']
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