Class: PBXProject::PBXProject

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

Overview

PBXProject class is main class for whole project file. Initializer takes one named argument, ‘file` which defines pbxproject file to be parsed.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(args = {}) ⇒ PBXProject

Returns a new instance of PBXProject.



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/pbxproject/pbxproject.rb', line 13

def initialize(args = {})
  return unless args[:file]
  raise "Project file cannot be read" unless File.file?(args[:file])

  @filename = args[:file]

  # initialize our class
  # make sure that our sections are on proper order
  @sections = {
    # "PBXBuildFile" => nil,
    # "PBXReferenceProxy" => nil,
    # "PBXContainerItemProxy" => nil,
    # "PBXFileReference" => nil,
    # "PBXFrameworksBuildPhase" => nil,
    # "PBXGroup" => nil,
    # "PBXHeadersBuildPhase" => nil,
    # "PBSLegacyTarget" => nil,
    # "PBXNativeTarget" => nil,
    # "PBXProject" => nil,
    # "PBXResourcesBuildPhase" => nil,
    # "PBXShellScriptBuildPhase" => nil,
    # "PBXSourcesBuildPhase" => nil,
    # "PBXTargetDependency" => nil,
    # "PBXVariantGroup" => nil,
    # "XCBuildConfiguration" => nil,
    # "XCConfigurationList" => nil,
    # "XCVersionGroup" => nil
  }

  # and set that we're ready for parsing
  @state = :ready
  
end

Instance Attribute Details

#archiveVersionObject

Returns the value of attribute archiveVersion.



11
12
13
# File 'lib/pbxproject/pbxproject.rb', line 11

def archiveVersion
  @archiveVersion
end

#filenameObject (readonly)

Filename of pbxproj file



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

def filename
  @filename
end

#objectVersionObject

Returns the value of attribute objectVersion.



11
12
13
# File 'lib/pbxproject/pbxproject.rb', line 11

def objectVersion
  @objectVersion
end

#sectionsObject

Returns the value of attribute sections.



11
12
13
# File 'lib/pbxproject/pbxproject.rb', line 11

def sections
  @sections
end

#stateObject (readonly)

Current state of parser, either nil, :ready, :parsed



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

def state
  @state
end

Instance Method Details

#add_item(item, position = -1) ⇒ Object



257
258
259
260
261
262
263
264
265
# File 'lib/pbxproject/pbxproject.rb', line 257

def add_item item, position = -1
  type = item.class.name.split('::').last || ''

  # Ensure that we have array
  @sections[type] ||= []
  @sections[type].insert(position, item)

  item.guid
end

#find_item(args = {}) ⇒ Object



230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/pbxproject/pbxproject.rb', line 230

def find_item args = {}
  type = args[:type]
  type_name = type.name.split('::').last || ''
  args.delete(:type)
  @sections.each do |t, arr|
    next unless t == type_name
    
    # Return all if we searched only for item type
    return arr if args.count == 0
    
    arr.each do |item|
      args.each do |k,v|
        val = item.instance_variable_get("@#{k}")
        val = (val.respond_to?(:value) ? val.value : val)
        
        next unless val
        
        if (val.match(Regexp.new("\"?#{v}\"?")))
          return item
        end
      end
    end
  end

  nil
end

#parseObject

This is one big parser



48
49
50
51
52
53
54
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
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
142
143
144
145
146
147
148
149
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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/pbxproject/pbxproject.rb', line 48

def parse
  raise "Not ready for parsing" unless @state == :ready
  pbx = File.open(@filename, 'r')

  line_num = 0
  group_name = []
  # group = []
  section_type = nil
  section = nil
  item = nil
  list_name = nil
  list = nil
  grouplist_name = nil
  grouplist = nil

  # Read our file
  pbx.each_line do |line|
    if (line_num == 0 && !line.match(Regexp.new(Regexp.escape('// !$*UTF8*$!'))))
      raise "Unknown file format"
    end
  
    # Main level Attributes
    if (group_name.count == 0 && !section_type && m = line.match(/\s+(.*?) = (.*?)( \/\* (.*) \*\/)?;/))
      # d = { :value => m[2], :comment => m[4] }
      self.instance_variable_set("@#{m[1]}", PBXTypes::BasicValue.new(:value => m[2], :comment => m[4]))
    
      next
    end
  
    # Begin object group
    if (m = line.match(/\s+(.*) = \{/))
      group_name.push m[1]
      # group.push {}
    end
  
    # End our object group
    if (line.match(/\s+\};/))
      group_name.pop
      # group.pop
    
      if (item && group_name.count < 2)
        @sections[section_type].push item
        item = nil
      end
    end
  
    # Begin section
    if (m = line.match(/\/\* Begin (.*) section \*\//))
      section_type = m[1]
      @sections[section_type] = []

      next
    end
  
    # One line section data, simple. huh?
    if (section_type && group_name.count < 3 && m = line.match(/\s+(.*?) (\/\* (.*?) \*\/ )?= \{(.*)\};/))
      begin
        # cls = PBXProject::PBXProject::const_get(section_type)
        # item = cls.new
        item = eval("PBXTypes::#{section_type}").new
    
        item.guid = m[1]
        item.comment = m[3]
        # m[4].scan(/(.*?) = (.*?)( \/\* (.*) \*\/)?; ?/).each do |v|
        # 1: name
        # 2: value
        # 3: /* comment */
        # 4: comment
        m[4].scan(/(\S*?) = (\s*?(.*?)(?:(?={){[^}]*}|(?: \/\* (.*?) \*\/)?(?=;)))/).each do |v|
          if (v[3])
            # d = { :value => v[1], :comment => v[3]}
            item.instance_variable_set("@#{v[0]}", PBXTypes::BasicValue.new(:value => v[2], :comment => v[3]))
          else
            item.instance_variable_set("@#{v[0]}", v[1][/^{/] ? v[1] : v[2])
          end
        end
    
        @sections[section_type].push item
        item = nil
      rescue NameError => e
        puts e.inspect
      end
    
      next
    end
  
    # Multiline with lists
    if (section_type && group_name.count < 3 && m = line.match(/\s+(.*?) (\/\* (.*?) \*\/ )?= \{/))
      begin
        # cls = PBXProject::PBXProject::const_get(section_type)
        # item = cls.new
        item = eval("PBXTypes::#{section_type}").new
      
        item.guid = m[1]
        item.comment = m[3]
      
        # puts item.inspect
      rescue NameError => e
        puts e.inspect
      end
    
      next
    end
  
    # Next line in multiline
    if (item && m = line.match(/^\s*(.*?) = (.*?)(?: \/\* (.*) \*\/)?;$/))
      if (group_name.count < 3)
        # i = { :value => m[2], :comment => m[3] }
        item.instance_variable_set("@#{m[1]}", PBXTypes::BasicValue.new(:value => m[2], :comment => m[3]))
      else
        grp = item.instance_variable_get("@#{group_name.last}")
        if (!grp.kind_of?(Hash))
          grp = {}
        end
        # grp[m[1]] = { :value => m[2], :comment => m[3] }
        grp[m[1]] = PBXTypes::BasicValue.new :value => m[2], :comment => m[3]
        item.instance_variable_set("@#{group_name.last}", grp)
      end
    
      next
    end
  
    # And the multiline list begin
    if (item && m = line.match(/\s+(.*?) = \(/))
      if (group_name.count < 3)
        list_name = m[1]
        list = []
      else
        grouplist_name = m[1]
        grouplist = []
      end
    
      next
    end
  
    # And list items
    if (item && m = line.match(/\s+(.*?)( \/\* (.*?) \*\/)?,/))
      if (group_name.count < 3)
        # i = { :item => m[1], :comment => m[3] }
        list.push PBXTypes::BasicValue.new :value => m[1], :comment => m[3]
      else
        # i = { :item => m[1], :comment => m[3] }
        grouplist.push PBXTypes::BasicValue.new :value => m[1], :comment => m[3]
      end
    
      next
    end
  
    if (item && line.match(/\s+\);/))
      if (group_name.count < 3)
        item.instance_variable_set("@#{list_name}", list)
        list = nil
        list_name = nil
      else
        grp = item.instance_variable_get("@#{group_name.last}")
        if (!grp.kind_of?(Hash))
          grp = {}
        end
        grp[grouplist_name] = grouplist
        item.instance_variable_set("@#{group_name.last}", grp)
        grouplist_name = nil
        grouplist = nil
      end
    
      next
    end
  
    # End section
    if (m = line.match(/\/\* End (.*) section \*\//))
      section_type = nil
      section = nil
    end
  
    # Increse our line counter
    line_num += 1
  end

  @state = :parsed

  true
end

#to_pbx(ind = 0) ⇒ Object



267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# File 'lib/pbxproject/pbxproject.rb', line 267

def to_pbx ind = 0
  ind = 2
  pbx = ''

  pbx += "// !$*UTF8*$!\n{\n"
  pbx += "\tarchiveVersion = %s;\n" % @archiveVersion.to_pbx
  pbx += "\tclasses = {\n\t};\n"
  pbx += "\tobjectVersion = %s;\n" % @objectVersion.to_pbx
  pbx += "\tobjects = {\n"

  @sections.each do |type, val|
    next if val.nil?  # skip section if it's empty
    pbx += "\n/* Begin %s section */\n" % type

    val.each do |item|
      pbx += item.to_pbx ind
    end
  
    pbx += "/* End %s section */\n" % type
  end

  pbx += "\t};\n"
  pbx += "\trootObject = %s;\n" % @rootObject.to_pbx
  pbx += "}\n"

  pbx
end

#write_to(args = {}) ⇒ Object



295
296
297
298
299
300
301
302
303
# File 'lib/pbxproject/pbxproject.rb', line 295

def write_to args = {}
  # Get our serialized format first
  pbx = self.to_pbx
  
  # Open file
  File.open(args[:file], 'w') {|f| f.write(pbx) }
  
  args[:file]
end