xcodeproject 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/.gitignore +32 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE +22 -0
  4. data/README.md +14 -0
  5. data/lib/xcodeproject/build_phase_node.rb +87 -0
  6. data/lib/xcodeproject/data.rb +87 -0
  7. data/lib/xcodeproject/exceptions.rb +5 -0
  8. data/lib/xcodeproject/extend/array.rb +8 -0
  9. data/lib/xcodeproject/extend/hash.rb +11 -0
  10. data/lib/xcodeproject/extend/string.rb +7 -0
  11. data/lib/xcodeproject/file_node.rb +63 -0
  12. data/lib/xcodeproject/formatter.rb +23 -0
  13. data/lib/xcodeproject/node.rb +15 -0
  14. data/lib/xcodeproject/pbx_build_file.rb +37 -0
  15. data/lib/xcodeproject/pbx_file_reference.rb +53 -0
  16. data/lib/xcodeproject/pbx_group.rb +169 -0
  17. data/lib/xcodeproject/pbx_native_target.rb +70 -0
  18. data/lib/xcodeproject/pbx_project.rb +33 -0
  19. data/lib/xcodeproject/project.rb +70 -0
  20. data/lib/xcodeproject/resources/example/dir1a/dir2a/dir3a/dir4a/file5a-a.h +1 -0
  21. data/lib/xcodeproject/resources/example/dir1a/dir2a/dir3a/dir4a/file5a-a.m +1 -0
  22. data/lib/xcodeproject/resources/example/dir1a/dir2a/dir3a/dir4a/file5a-r.h +1 -0
  23. data/lib/xcodeproject/resources/example/dir1a/dir2a/dir3a/dir4a/file5a-r.m +1 -0
  24. data/lib/xcodeproject/resources/example/dir1b/dir2b/file3b.m +0 -0
  25. data/lib/xcodeproject/resources/example/dir1b/file2b.m +0 -0
  26. data/lib/xcodeproject/resources/example/dir1c/file2c.h +0 -0
  27. data/lib/xcodeproject/resources/example/dir1c/file2c.m +0 -0
  28. data/lib/xcodeproject/resources/example/example/AppDelegate.h +15 -0
  29. data/lib/xcodeproject/resources/example/example/AppDelegate.m +57 -0
  30. data/lib/xcodeproject/resources/example/example/en.lproj/InfoPlist.strings +2 -0
  31. data/lib/xcodeproject/resources/example/example/example-Info.plist +45 -0
  32. data/lib/xcodeproject/resources/example/example/example-Prefix.pch +14 -0
  33. data/lib/xcodeproject/resources/example/example/main.m +18 -0
  34. data/lib/xcodeproject/resources/example/example.xcodeproj/project.pbxproj +324 -0
  35. data/lib/xcodeproject/root_node.rb +117 -0
  36. data/lib/xcodeproject/spec/build_phase_node_spec.rb +95 -0
  37. data/lib/xcodeproject/spec/file_node_spec.rb +83 -0
  38. data/lib/xcodeproject/spec/pbx_build_file_spec.rb +25 -0
  39. data/lib/xcodeproject/spec/pbx_file_reference_spec.rb +34 -0
  40. data/lib/xcodeproject/spec/pbx_group_spec.rb +274 -0
  41. data/lib/xcodeproject/spec/pbx_native_target_spec.rb +58 -0
  42. data/lib/xcodeproject/spec/pbx_project_spec.rb +34 -0
  43. data/lib/xcodeproject/spec/project_spec.rb +79 -0
  44. data/lib/xcodeproject/spec/spec_helper.rb +45 -0
  45. data/lib/xcodeproject/spec/xc_configuration_list_spec.rb +20 -0
  46. data/lib/xcodeproject/uuid_generator.rb +13 -0
  47. data/lib/xcodeproject/version.rb +3 -0
  48. data/lib/xcodeproject/xc_build_configuration.rb +15 -0
  49. data/lib/xcodeproject/xc_configuration_list.rb +23 -0
  50. data/lib/xcodeproject.rb +3 -0
  51. data/rakefile +3 -0
  52. data/xcodeproject.gemspec +26 -0
  53. metadata +166 -0
data/.gitignore ADDED
@@ -0,0 +1,32 @@
1
+ # OSX
2
+ .DS_Store
3
+ ._*
4
+ .Spotlight-V100
5
+ .Trashes
6
+
7
+ # .gitignore
8
+ .gitignore
9
+
10
+ # vim
11
+ .*.sw[a-z]
12
+ *.un~
13
+ Session.vim
14
+
15
+ # ruby
16
+ *.gem
17
+ *.rbc
18
+ .bundle
19
+ .config
20
+ .yardoc
21
+ Gemfile.lock
22
+ InstalledFiles
23
+ _yardoc
24
+ coverage
25
+ doc/
26
+ lib/bundler/man
27
+ pkg
28
+ rdoc
29
+ spec/reports
30
+ test/tmp
31
+ test/version_tmp
32
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in xcodeproject.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Andrey Nesterov
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,14 @@
1
+ XCodeProject
2
+ ===
3
+ Ruby API for working with Xcode project files
4
+
5
+ Installation
6
+ ---
7
+ `gem install xcodeproject`
8
+
9
+ License
10
+ ---
11
+ XCodeProject is provided under the terms of the [the MIT license][license]
12
+
13
+ [license]:http://www.opensource.org/licenses/MIT
14
+
@@ -0,0 +1,87 @@
1
+ require 'xcodeproject/node'
2
+ require 'xcodeproject/pbx_build_file'
3
+
4
+ module XCodeProject
5
+ class BuildPhaseNode < Node
6
+ def initialize (root, uuid, data)
7
+ super(root, uuid, data)
8
+ @files = data['files']
9
+ end
10
+
11
+ def files
12
+ build_files.map {|obj| obj.file_ref }
13
+ end
14
+
15
+ def add_file (file_obj)
16
+ case file_obj
17
+ when PBXFileReference
18
+ add_build_file(file_obj.uuid)
19
+ when PBXBuildFile
20
+ add_build_file_uuid(file_obj.uuid)
21
+ else
22
+ raise ArgumentError.new("Wrong argument type, expected the PBXFileReference or PBXBuildFile.")
23
+ end
24
+ end
25
+
26
+ def remove_file (file_obj)
27
+ case file_obj
28
+ when PBXFileReference
29
+ remove_build_file(file_obj.uuid)
30
+ when PBXBuildFile
31
+ remove_build_file_uuid(file_obj.uuid)
32
+ else
33
+ raise ArgumentError.new("Wrong argument type, expected the PBXFileReference or PBXBuildFile.")
34
+ end
35
+ end
36
+
37
+ def doctor
38
+ @files.each do |uuid|
39
+ obj = root.object(uuid)
40
+ if obj.nil?
41
+ remove_build_file_uuid(uuid)
42
+ puts "removed #{uuid}"
43
+ end
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ def build_files
50
+ @files.map {|uuid| root.object!(uuid) }
51
+ end
52
+
53
+ def build_file (file_ref_uuid)
54
+ build_files.select {|obj| obj.file_ref.uuid == file_ref_uuid }.first
55
+ end
56
+
57
+ def add_build_file (file_ref_uuid)
58
+ obj = build_file(file_ref_uuid)
59
+ if obj.nil?
60
+ obj = PBXBuildFile.add(root, file_ref_uuid)
61
+ add_build_file_uuid(obj.uuid)
62
+ end
63
+ obj
64
+ end
65
+
66
+ def remove_build_file (file_ref_uuid)
67
+ obj = build_file(file_ref_uuid)
68
+ remove_build_file_uuid(obj.uuid) unless obj.nil?
69
+ end
70
+
71
+ def add_build_file_uuid (build_file_uuid)
72
+ @files << build_file_uuid unless @files.include?(build_file_uuid)
73
+ end
74
+
75
+ def remove_build_file_uuid (build_file_uuid)
76
+ @files.delete(build_file_uuid)
77
+ end
78
+ end
79
+
80
+ class PBXSourcesBuildPhase < BuildPhaseNode; end
81
+ class PBXHeadersBuildPhase < BuildPhaseNode; end
82
+ class PBXResourcesBuildPhase < BuildPhaseNode; end
83
+ class PBXFrameworksBuildPhase < BuildPhaseNode; end
84
+ class PBXAppleScriptBuildPhase < BuildPhaseNode; end
85
+ class PBXShellScriptBuildPhase < BuildPhaseNode; end
86
+ class PBXCopyFilesBuildPhase < BuildPhaseNode; end
87
+ end
@@ -0,0 +1,87 @@
1
+ #--
2
+ # Copyright 2012 by Andrey Nesterov (ae.nesterov@gmail.com)
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to
6
+ # deal in the Software without restriction, including without limitation the
7
+ # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8
+ # sell copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20
+ # IN THE SOFTWARE.
21
+ #++
22
+
23
+ require 'xcodeproject/root_node'
24
+
25
+ module XCodeProject
26
+ class Data
27
+ def initialize (data, wd)
28
+ @root = RootNode.new(data, wd)
29
+ end
30
+
31
+ def project
32
+ @root.project
33
+ end
34
+
35
+ def targets
36
+ project.targets
37
+ end
38
+
39
+ def target (name)
40
+ project.target(name)
41
+ end
42
+
43
+ def main_group
44
+ project.main_group
45
+ end
46
+
47
+ def group (gpath)
48
+ main_group.group(gpath)
49
+ end
50
+
51
+ def add_group (gpath)
52
+ main_group.add_group(gpath)
53
+ end
54
+
55
+ def add_dir(parent_gpath, path)
56
+ main_group.add_group(parent_gpath).add_dir(path)
57
+ end
58
+
59
+ def remove_group (gpath)
60
+ main_group.remove_group(gpath)
61
+ end
62
+
63
+ def file (gpath)
64
+ main_group.file(gpath)
65
+ end
66
+
67
+ def add_file (parent_gpath, path)
68
+ main_group.add_group(parent_gpath).add_file(path)
69
+ end
70
+
71
+ def remove_file (gpath)
72
+ main_group.remove_file(gpath)
73
+ end
74
+
75
+ def doctor
76
+ targets.each {|target| target.doctor }
77
+ end
78
+
79
+ def to_plist (fmtr = Formatter.new)
80
+ @root.to_plist
81
+ end
82
+
83
+ private
84
+ attr_accessor :root
85
+
86
+ end
87
+ end
@@ -0,0 +1,5 @@
1
+ module XCodeProject
2
+ class ParseError < StandardError; end
3
+ class FilePathError < StandardError; end
4
+ class GroupPathError < StandardError; end
5
+ end
@@ -0,0 +1,8 @@
1
+ require 'xcodeproject/formatter'
2
+
3
+ class Array
4
+ def to_plist (fmtr = XCodeProject::Formatter.new)
5
+ items = map { |item| "#{item.to_plist(fmtr)}" }
6
+ %{(#{fmtr.t2}#{items.join(",#{fmtr.t2}")}#{fmtr.t1})}
7
+ end
8
+ end
@@ -0,0 +1,11 @@
1
+ require 'xcodeproject/formatter'
2
+
3
+ class Hash
4
+ def to_plist (fmtr = XCodeProject::Formatter.new)
5
+ fmtr.inc
6
+ items = map { |key, value| "#{key.to_plist(fmtr)} = #{value.to_plist(fmtr)};" }
7
+ fmtr.dec
8
+
9
+ %{{#{fmtr.t2}#{items.join("#{fmtr.t2}")}#{fmtr.t1}}}
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ require 'json'
2
+
3
+ class String
4
+ def to_plist (fmtr = nil)
5
+ to_json
6
+ end
7
+ end
@@ -0,0 +1,63 @@
1
+ require 'xcodeproject/node'
2
+ require 'pathname'
3
+
4
+ module XCodeProject
5
+ class FileNode < Node
6
+ attr_reader :name
7
+ attr_reader :path
8
+ attr_reader :source_tree
9
+
10
+ def initialize (root, uuid, data)
11
+ super(root, uuid, data)
12
+
13
+ @source_tree = data['sourceTree']
14
+ @name ||= data['name']
15
+ @path ||= data['path']
16
+
17
+ @name ||= File.basename(@path) unless @path.nil?
18
+ end
19
+
20
+ def source_tree
21
+ SourceTreeMap[@source_tree]
22
+ end
23
+
24
+ def parent
25
+ root.select_objects do |uuid, data|
26
+ (data['children'].include?(self.uuid) if data['isa'] == 'PBXGroup') ? true : false
27
+ end.first
28
+ end
29
+
30
+ def group_path
31
+ obj = self
32
+ res = ''
33
+ begin
34
+ pn = obj.name ? obj.name : ''
35
+ res = Pathname.new(pn).join(res)
36
+ end while obj = obj.parent;
37
+ res.cleanpath
38
+ end
39
+
40
+ def total_path
41
+ res = ''
42
+ case source_tree
43
+ when :source_root
44
+ res = path
45
+ when :group
46
+ pn = path.nil? ? '' : path
47
+ res = parent.total_path.join(pn) unless parent.nil?
48
+ else
49
+ raise ParseError.new("No such '#{source_tree}' source tree type.")
50
+ end
51
+ root.absolute_path(res)
52
+ end
53
+
54
+ private
55
+
56
+ SourceTreeMap = {
57
+ 'SOURCE_ROOT' => :source_root,
58
+ '<group>' => :group
59
+ }
60
+
61
+ end
62
+ end
63
+
@@ -0,0 +1,23 @@
1
+ module XCodeProject
2
+ class Formatter
3
+ def initialize
4
+ @counter = 0
5
+ end
6
+
7
+ def inc
8
+ @counter += 1
9
+ end
10
+
11
+ def dec
12
+ @counter -= 1
13
+ end
14
+
15
+ def t1
16
+ "\n" + "\t" * @counter
17
+ end
18
+
19
+ def t2
20
+ "\n" + "\t" * (@counter + 1)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,15 @@
1
+ module XCodeProject
2
+ class Node
3
+ attr_reader :uuid
4
+ attr_reader :isa
5
+
6
+ def initialize (root, uuid, data)
7
+ @root, @uuid, @data = root, uuid, data
8
+ @isa = data['isa']
9
+ end
10
+
11
+ protected
12
+ attr_accessor :root
13
+ attr_accessor :data
14
+ end
15
+ end
@@ -0,0 +1,37 @@
1
+ require 'xcodeproject/node'
2
+
3
+ module XCodeProject
4
+ class PBXBuildFile < Node
5
+ attr_reader :file_ref
6
+
7
+ def initialize (root, uuid, data)
8
+ super(root, uuid, data)
9
+
10
+ @file_ref = data['fileRef']
11
+ end
12
+
13
+ def file_ref
14
+ root.object!(@file_ref)
15
+ end
16
+
17
+ def remove!
18
+ root.project.targets.each {|target| target.remove_source(self) }
19
+ root.remove_object(uuid)
20
+ end
21
+
22
+ def self.add(root, file_ref_uuid)
23
+ uuid, data = root.add_object(self.create_object_hash(file_ref_uuid))
24
+ self.new(root, uuid, data)
25
+ end
26
+
27
+ private
28
+
29
+ def self.create_object_hash (file_ref_uuid)
30
+ data = []
31
+ data << ['isa', 'PBXBuildFile']
32
+ data << ['fileRef', file_ref_uuid]
33
+
34
+ Hash[ data ]
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,53 @@
1
+ require 'xcodeproject/file_node'
2
+ require 'xcodeproject/exceptions'
3
+
4
+ module XCodeProject
5
+ class PBXFileReference < FileNode
6
+ def initialize (root, uuid, data)
7
+ super(root, uuid, data)
8
+ end
9
+
10
+ def remove!
11
+ root.build_files(uuid).each do |build_file_obj|
12
+ build_file_obj.remove!
13
+ end
14
+
15
+ parent.remove_child_uuid(uuid)
16
+ root.remove_object(uuid)
17
+ end
18
+
19
+ def self.add(root, path)
20
+ uuid, data = root.add_object(self.create_object_hash(path))
21
+ self.new(root, uuid, data)
22
+ end
23
+
24
+ private
25
+
26
+ def self.create_object_hash (path)
27
+ path = path.to_s
28
+ name = File.basename(path)
29
+ ext = File.extname(path)
30
+ raise ParseError.new("No such file type '#{name}'.") if !FileTypeMap.include?(ext)
31
+
32
+ data = []
33
+ data << ['isa', 'PBXFileReference']
34
+ data << ['sourceTree', '<group>']
35
+ data << ['fileEncoding', '4'] # utf-8
36
+ data << ['lastKnownFileType', FileTypeMap[ext]]
37
+ data << ['path', path]
38
+ data << ['name', name] if name != path
39
+
40
+ Hash[ data ]
41
+ end
42
+
43
+ FileTypeMap = {
44
+ '.h' => 'sourcecode.c.h',
45
+ '.c' => 'sourcecode.c.c',
46
+ '.m' => 'sourcecode.c.objc',
47
+ '.mm' => 'sourcecode.cpp.objcpp',
48
+ '.hpp' => 'sourcecode.cpp.h',
49
+ '.cpp' => 'sourcecode.cpp.cpp',
50
+ '.cc' => 'sourcecode.cpp.cpp'
51
+ }
52
+ end
53
+ end
@@ -0,0 +1,169 @@
1
+ require 'xcodeproject/pbx_file_reference'
2
+ require 'xcodeproject/exceptions'
3
+ require 'pathname'
4
+
5
+ module XCodeProject
6
+ class PBXGroup < FileNode
7
+ def initialize (root, uuid, data)
8
+ super(root, uuid, data)
9
+ @children = data['children']
10
+ end
11
+
12
+ def children
13
+ @children.map {|uuid| root.object!(uuid)}
14
+ end
15
+
16
+ def child (gpath)
17
+ gpath = Pathname.new(gpath).cleanpath
18
+
19
+ if gpath == gpath.basename
20
+ name = gpath.to_s
21
+ case name
22
+ when '.'
23
+ self
24
+ when '..'
25
+ parent
26
+ else
27
+ chs = children.select {|obj| obj.name == name }
28
+ raise GroupPathError.new("The group contains two children with the same name.") if chs.size > 1
29
+ chs.first
30
+ end
31
+ else
32
+ pn, name = gpath.split
33
+ group = child(pn)
34
+ group.child(name) if group.is_a?(PBXGroup)
35
+ end
36
+ end
37
+
38
+ def file_ref (gpath)
39
+ obj = child(gpath)
40
+ obj.is_a?(PBXFileReference) ? obj : nil
41
+ end
42
+
43
+ def group?
44
+ data['path'].nil?
45
+ end
46
+
47
+ def dir?
48
+ not group?
49
+ end
50
+
51
+ def group (gpath)
52
+ obj = child(gpath)
53
+ obj.is_a?(PBXGroup) ? obj : nil
54
+ end
55
+
56
+ def add_group (gpath)
57
+ create_group(gpath)
58
+ end
59
+
60
+ def add_dir (path, gpath = nil)
61
+ path = absolute_path(path)
62
+ raise FilePathError.new("No such directory '#{path}'.") unless path.exist?
63
+
64
+ gpath ||= path.basename
65
+ parent = create_group(gpath, path)
66
+
67
+ chs = path.entries.select {|obj| obj.to_s =~ /\A\./ ? false : true }
68
+ chs.each do |pn|
69
+ parent.absolute_path(pn).directory? ? parent.add_dir(pn) : parent.add_file(pn)
70
+ end
71
+ parent
72
+ end
73
+
74
+ def create_group (gpath, path = nil)
75
+ gpath = Pathname.new(gpath).cleanpath
76
+
77
+ if gpath == gpath.basename
78
+ name = gpath.to_s
79
+ case name
80
+ when '.'
81
+ self
82
+ when '..'
83
+ parent
84
+ else
85
+ obj = group(name)
86
+ if obj.nil?
87
+ path = relative_path(path) unless path.nil?
88
+ obj = PBXGroup.add(root, name, path)
89
+ add_child_uuid(obj.uuid)
90
+ end
91
+ obj
92
+ end
93
+ else
94
+ pn, name = gpath.split
95
+ create_group(pn).create_group(name)
96
+ end
97
+ end
98
+
99
+ def add_child_uuid (uuid)
100
+ @children << uuid
101
+ end
102
+
103
+ def remove_child_uuid (uuid)
104
+ @children.delete(uuid)
105
+ end
106
+
107
+ def absolute_path (path)
108
+ path = Pathname.new(path)
109
+ path.absolute? ? path : root.absolute_path(total_path.join(path))
110
+ end
111
+
112
+ def relative_path (path)
113
+ path = Pathname.new(path)
114
+ path.relative? ? path : path.relative_path_from(total_path)
115
+ end
116
+
117
+ def remove_group (gpath)
118
+ obj = group(gpath)
119
+ obj.remove! unless obj.nil?
120
+ end
121
+
122
+ def remove!
123
+ return if parent.nil?
124
+
125
+ children.each do |obj|
126
+ obj.remove!
127
+ end
128
+
129
+ parent.remove_child_uuid(uuid)
130
+ root.remove_object(uuid)
131
+ end
132
+
133
+ def add_file_ref (path)
134
+ raise FilePathError.new("No such file '#{absolute_path(path)}'.") unless absolute_path(path).exist?
135
+
136
+ name = File.basename(path)
137
+ obj = file_ref(name)
138
+ if obj.nil?
139
+ obj = PBXFileReference.add(root, relative_path(path))
140
+ add_child_uuid(obj.uuid)
141
+ end
142
+ obj
143
+ end
144
+
145
+ def self.add(root, name, path = nil)
146
+ uuid, data = root.add_object(self.create_object_hash(name, path))
147
+ PBXGroup.new(root, uuid, data)
148
+ end
149
+
150
+ alias :file :file_ref
151
+ alias :add_file :add_file_ref
152
+
153
+ private
154
+
155
+ def self.create_object_hash (name, path = nil)
156
+ path = path.to_s
157
+
158
+ data = []
159
+ data << ['isa', 'PBXGroup']
160
+ data << ['children', []]
161
+ data << ['name', name]
162
+ data << ['path', path] unless path.empty?
163
+ data << ['sourceTree', '<group>']
164
+
165
+ Hash[ data ]
166
+ end
167
+ end
168
+ end
169
+