Class: ZergXcode::Plugins::Import

Inherits:
Object
  • Object
show all
Includes:
Objects
Defined in:
lib/zerg_xcode/plugins/import.rb

Instance Method Summary collapse

Instance Method Details

#bin_mappings(mappings, source) ⇒ Object

Bins merge mappings for a project into mappings to be merged and mappings to be overwritten.


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

def bin_mappings(mappings, source)
  merge_set = Set.new
  overwrite_set = Set.new

  # the project's top-level attributes are always merged
  source.attrs.each do |attr|
    merge_set << source[attr] if mappings[source[attr]]
  end
      
  mappings.each do |source_object, target_object|
    next if source_object == target_object
    next if merge_set.include? source_object
    
    if source_object.kind_of?(PBXGroup) && source_object['path'].nil?
      merge_set << source_object
    elsif source_object.kind_of? XCConfigurationList
      merge_set << source_object
    else
      overwrite_set << source_object
    end
  end
  
  overwrite_set.delete source
  { :merge => merge_set, :overwrite => overwrite_set }
end

#clean_join(root, path) ⇒ Object


116
117
118
# File 'lib/zerg_xcode/plugins/import.rb', line 116

def clean_join(root, path)
  Pathname.new(File.join(root, path)).cleanpath.to_s
end

#compute_copies(source_root, source_paths, target_root) ⇒ Object

Computes the file copy operations in a merge.

Copies all the files from the source project assuming they received the same path in the target project. The assumption is correct if source was imported into target.


109
110
111
112
113
114
# File 'lib/zerg_xcode/plugins/import.rb', line 109

def compute_copies(source_root, source_paths, target_root)
  source_paths.select { |path| path[0, 2] == './' }.map do |path|
    { :op => :copy, :from => clean_join(source_root, path),
      :to => clean_join(target_root, path) }
  end
end

#compute_deletes(root, old_paths, new_paths) ⇒ Object

Computes the file delete operations in a merge.

Deletes all the files that aren't in the target project anymore.


97
98
99
100
101
102
# File 'lib/zerg_xcode/plugins/import.rb', line 97

def compute_deletes(root, old_paths, new_paths)
  new_path_set = Set.new(new_paths)
  old_paths.select { |path| path[0, 2] == './' }.
            reject { |path| new_path_set.include? path }.
            map { |path| { :op => :delete, :path => clean_join(root, path) } }
end

#cross_reference(source, target, strict = false, mappings = {}) ⇒ Object

Cross-references the objects in two object graphs that are to be merged. Returns a Hash associating objects in both the source and the target object graphs with the target objects that represent the same entities. Setting strict to true returns fewer matches, and should be used for entangling projects. Setting strict to false should work better for merges.


225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# File 'lib/zerg_xcode/plugins/import.rb', line 225

def cross_reference(source, target, strict = false, mappings = {})
  if source.class != target.class
    raise "Attempting to cross-reference different kinds of objects - " +
          "#{source.class} != #{target.class}"
  end
  
  case target
  when ZergXcode::XcodeObject
    cross_op = :cross_reference_objects
  when Hash
    cross_op = :cross_reference_hashes
  when Enumerable
    cross_op = :cross_reference_enumerables
  else
    return mappings
  end
  
  self.send cross_op, source, target, strict, mappings
end

#execute_file_ops!(file_ops) ⇒ Object

Executes the given file operations.


28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/zerg_xcode/plugins/import.rb', line 28

def execute_file_ops!(file_ops)
  file_ops.each do |op|
    case op[:op]
    when :delete
      FileUtils.rm_r op[:path] if File.exist? op[:path]
    when :copy
      target_dir = File.dirname op[:to]
      FileUtils.mkdir_p  target_dir unless File.exist? target_dir
      if File.exist? op[:from]
        FileUtils.cp_r op[:from], op[:to]
      else
        print "Source does not have file #{op[:from]}\n"
      end
    end
  end
end

#helpObject


7
8
9
10
11
12
13
14
15
16
# File 'lib/zerg_xcode/plugins/import.rb', line 7

def help
  {:short => 'imports the objects from a project into another project',
   :long => <<"END" }
Usage: import source_path [target_path]

Imports the objects (build targets, files) from a source project into another
target project. Useful when the source project wraps code that can be used as a
library.
END
end

#import_project!(source, target) ⇒ Object

Imports the objects of the source project into the target project.

Attempts to preserve reference integrity in the target project, as follows. If the source objects have counterparts in the target, their contents is merged into the target project's objects.

Returns an array of file operations that need to be performed to migrate the files associated with the two projects.

The target project is modified in place.


55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/zerg_xcode/plugins/import.rb', line 55

def import_project!(source, target)
  old_target_paths = target.all_files.map { |file| file[:path] } 
  
  # duplicate the source, because the duplicate's object graph will be warped
  scrap_source = ZergXcode::XcodeObject.from source
  
  mappings = cross_reference scrap_source, target
  bins = bin_mappings mappings, scrap_source
  
  # special case for merging targets
  map! scrap_source, mappings
  merge! scrap_source['targets'], target['targets']
  
  # merge the object graphs
  bins[:merge].each do |object|
    map! object, mappings
    merge! object, mappings[object]
  end
  bins[:overwrite].each do |object|
    map! object, mappings
    overwrite! object, mappings[object]
  end
  
  # make sure all the mappings point in the right place 
  target.visit_once do |object, parent, key, value|
    if mappings[value]
      next mappings[value]
    else
      next true
    end
  end
  
  new_target_paths = target.all_files.map { |file| file[:path] }
  source_paths = source.all_files.map { |file| file[:path] }
  return compute_deletes(target.root_path, old_target_paths,
                         new_target_paths) +
         compute_copies(source.root_path, source_paths, target.root_path)
end

#map!(object, mappings) ⇒ Object

Modifies an object's attributes according to the given mappings. This explores the object graph, but does not go into sub-objects.


150
151
152
153
154
155
156
157
158
# File 'lib/zerg_xcode/plugins/import.rb', line 150

def map!(object, mappings)
  object.visit_once do |object, parent, key, value|
    if mappings[value]
      parent[key] = mappings[value]
      next false
    end
    next true
  end
end

#merge!(source, target) ⇒ Object

Merges the contents of a source object into the target object.

Warning: the target will share internal objects with the source. This is intended to be used in a bigger-level opration, where the source will be thrown away afterwards.


165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/zerg_xcode/plugins/import.rb', line 165

def merge!(source, target)
  if source.class != target.class
    raise "Attempting to merge-combine different kinds of objects - " +
          "#{source.class} != #{target.class}"
  end
  
  case source
  when ZergXcode::XcodeObject
    merge! source._attr_hash, target._attr_hash
  when Hash
    source.each_key do |key|
      if !target.has_key?(key)
        target[key] = source[key]          
      elsif source[key].kind_of? ZergXcode::XcodeObject
        target[key] = source[key]
      elsif source[key].kind_of?(String)
        target[key] = source[key]
      elsif !source[key].kind_of?(Enumerable)
        target[key] = source[key]
      else
        merge! source[key], target[key]
      end
    end
  when Enumerable
    target_set = Set.new(target.to_a)
    source.each do |value|
      next if target_set.include? value
      target << value
    end
  end   
end

#overwrite!(source, target) ⇒ Object

Overwrites the contents of the target object with the source object.

Warning: the target will share internal objects with the source. This is intended to be used in a bigger-level opration, where the source will be thrown away afterwards.


202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/zerg_xcode/plugins/import.rb', line 202

def overwrite!(source, target)
  if source.class != target.class    
    raise "Attempting to overwrite-combine different kinds of objects - " +
          "#{source.class} != #{target.class}"
  end
  
  case source
  when ZergXcode::XcodeObject
    overwrite! source._attr_hash, target._attr_hash
  when Hash
    target.clear
    target.merge! source
  when Enumerable
    target.clear
    source.each { |value| target << value }      
  end
end

#run(args) ⇒ Object


18
19
20
21
22
23
24
25
# File 'lib/zerg_xcode/plugins/import.rb', line 18

def run(args)
  source = ZergXcode.load args.shift
  target = ZergXcode.load(args.shift || '.')
  
  file_ops = import_project! source, target
  target.save!
  execute_file_ops! file_ops 
end