zerg_xcode 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/CHANGELOG +1 -0
  2. data/LICENSE +21 -0
  3. data/Manifest +44 -0
  4. data/README.textile +124 -0
  5. data/RUBYFORGE +40 -0
  6. data/Rakefile +27 -0
  7. data/bin/zerg-xcode +7 -0
  8. data/lib/zerg_xcode/file_format/archiver.rb +87 -0
  9. data/lib/zerg_xcode/file_format/encoder.rb +40 -0
  10. data/lib/zerg_xcode/file_format/lexer.rb +60 -0
  11. data/lib/zerg_xcode/file_format/parser.rb +46 -0
  12. data/lib/zerg_xcode/file_format/paths.rb +44 -0
  13. data/lib/zerg_xcode/objects/pbx_build_file.rb +27 -0
  14. data/lib/zerg_xcode/objects/pbx_native_target.rb +19 -0
  15. data/lib/zerg_xcode/objects/pbx_project.rb +60 -0
  16. data/lib/zerg_xcode/objects/xcode_object.rb +144 -0
  17. data/lib/zerg_xcode/plugins/core/core.rb +36 -0
  18. data/lib/zerg_xcode/plugins/help.rb +30 -0
  19. data/lib/zerg_xcode/plugins/import.rb +19 -0
  20. data/lib/zerg_xcode/plugins/irb.rb +22 -0
  21. data/lib/zerg_xcode/plugins/ls.rb +27 -0
  22. data/lib/zerg_xcode/plugins/retarget.rb +90 -0
  23. data/lib/zerg_xcode/shortcuts.rb +22 -0
  24. data/lib/zerg_xcode.rb +17 -0
  25. data/test/file_format/archiver_test.rb +70 -0
  26. data/test/file_format/encoder_test.rb +10 -0
  27. data/test/file_format/lexer_test.rb +56 -0
  28. data/test/file_format/parser_test.rb +44 -0
  29. data/test/file_format/path_test.rb +40 -0
  30. data/test/objects/pbx_build_file_test.rb +38 -0
  31. data/test/objects/pbx_native_target_test.rb +31 -0
  32. data/test/objects/pbx_project_test.rb +62 -0
  33. data/test/objects/xcode_object_test.rb +117 -0
  34. data/test/plugins/core/core_test.rb +32 -0
  35. data/test/plugins/import_test.rb +22 -0
  36. data/test/plugins/irb_test.rb +30 -0
  37. data/test/plugins/ls_test.rb +43 -0
  38. data/test/plugins/retarget_test.rb +65 -0
  39. data/test/plugins/test_helper.rb +21 -0
  40. data/test/shortcuts_test.rb +18 -0
  41. data/testdata/TestApp/TestApp.xcodeproj/project.pbxproj +328 -0
  42. data/testdata/ZergSupport.xcodeproj/project.pbxproj +930 -0
  43. data/testdata/project.pbxproj +250 -0
  44. data/testdata/project.pbxproj.compat +320 -0
  45. data/zerg_xcode.gemspec +34 -0
  46. metadata +137 -0
@@ -0,0 +1,144 @@
1
+ # Xcode objects
2
+ class ZergXcode::XcodeObject
3
+ attr_accessor :version
4
+ attr_accessor :archive_id
5
+
6
+ # Hash-like behavior
7
+
8
+ def [](key)
9
+ @attrs[key]
10
+ end
11
+
12
+ def []=(key, value)
13
+ @attrs[key] = value
14
+ end
15
+
16
+ # Inheritance based on isa
17
+
18
+ def initialize(hash)
19
+ @attrs = hash
20
+ if self.class != ZergXcode::XcodeObject
21
+ class_name = self.class.name
22
+ @attrs['isa'] ||= class_name[(class_name.rindex(':') + 1)..-1]
23
+ end
24
+ end
25
+
26
+ def self.new(*args)
27
+ return super unless self == ZergXcode::XcodeObject
28
+ if hash_isa = args.first['isa']
29
+ classes = ZergXcode::Objects.constants
30
+ if classes.include? hash_isa
31
+ return ZergXcode::Objects.const_get(hash_isa).new(*args)
32
+ end
33
+ end
34
+ super
35
+ end
36
+
37
+ # Inspecting the object
38
+
39
+ # The names of the object's attributes.
40
+ def attrs
41
+ @attrs.keys
42
+ end
43
+
44
+ # The (internal) hash holding the file's attributes.
45
+ def _attr_hash
46
+ @attrs
47
+ end
48
+
49
+ # Object type helpers
50
+ def isa
51
+ return @attrs['isa'].to_sym
52
+ end
53
+
54
+ # Visitor pattern
55
+
56
+ # Visits an object's internal structure.
57
+ #
58
+ # The given block is called like this:
59
+ # yield object, parent, key, value
60
+ # Where
61
+ # object: the object currently visited (can be a sub-object)
62
+ # parent: the collection currently visited (an object, hash, or array)
63
+ # key:
64
+ # value:
65
+ # The block can return
66
+ # false: no recursive visiting for the given value
67
+ # true: normal recursive visiting for the given value
68
+ # something else: replace the given value with the return, the recursive
69
+ # visiting is done on the new value
70
+ def visit(&accept)
71
+ visit_hash(@attrs, &accept)
72
+ self
73
+ end
74
+
75
+ def visit_hash(hash, &accept)
76
+ hash.each_key do |key|
77
+ visit_value(hash, key, hash[key], &accept)
78
+ end
79
+ hash
80
+ end
81
+
82
+ def visit_array(array, &accept)
83
+ array.each_with_index do |value, index|
84
+ visit_value(array, index, value, &accept)
85
+ end
86
+ array
87
+ end
88
+
89
+ def visit_value(parent, key, value, &accept)
90
+ visit_parent = (parent == @attrs) ? self : parent
91
+ recurse = yield self, visit_parent, key, value
92
+ return if recurse == false
93
+
94
+ if recurse != true
95
+ value = recurse
96
+ parent[key] = recurse
97
+ end
98
+
99
+ case value
100
+ when ZergXcode::XcodeObject
101
+ value.visit(&accept)
102
+ when Hash
103
+ visit_hash(value, &accept)
104
+ when Array
105
+ visit_array(value, &accept)
106
+ end
107
+ value
108
+ end
109
+
110
+ # Deep copy
111
+ def self.from(object_or_hash)
112
+ new_object = case object_or_hash
113
+ when ZergXcode::XcodeObject
114
+ object_or_hash.shallow_copy
115
+ else
116
+ self.new object_or_hash.dup
117
+ end
118
+ object_map = { object_or_hash => new_object }
119
+
120
+ new_object.visit do |object, parent, key, value|
121
+ case value
122
+ when Hash, Array
123
+ next value.dup
124
+ when ZergXcode::XcodeObject
125
+ if object_map[value]
126
+ parent[key] = object_map[value]
127
+ next false
128
+ else
129
+ object_map[value] = value.shallow_copy
130
+ next object_map[value]
131
+ end
132
+ else
133
+ next true
134
+ end
135
+ end
136
+ end
137
+
138
+ def shallow_copy
139
+ new_object = self.class.new @attrs.dup
140
+ new_object.version, new_object.archive_id = version, archive_id
141
+ return new_object
142
+ end
143
+ end
144
+
@@ -0,0 +1,36 @@
1
+ require 'set'
2
+
3
+ module ZergXcode::Plugins
4
+ def self.all
5
+ plugin_dir = File.join(File.dirname(__FILE__), '..')
6
+ plugins = Dir.entries(plugin_dir).select { |entry|
7
+ /^[^_].*\.rb$/ =~ entry
8
+ }.map { |entry| entry[0..-4] }
9
+ return Set.new(plugins)
10
+ end
11
+
12
+ def self.require_all
13
+ all.each { |plugin| self.require plugin }
14
+ end
15
+
16
+ def self.require(plugin_name)
17
+ Kernel.require "zerg_xcode/plugins/#{plugin_name}.rb"
18
+ end
19
+
20
+ def self.get(plugin_name)
21
+ self.require plugin_name
22
+ ZergXcode::Plugins.const_get(plugin_name.capitalize).new
23
+ end
24
+
25
+ def self.run(plugin_name, args)
26
+ self.get(plugin_name).run args
27
+ end
28
+
29
+ def self.help(plugin_name)
30
+ self.get(plugin_name).help
31
+ end
32
+ end
33
+
34
+ # :nodoc:
35
+ module ZergXcode
36
+ end
@@ -0,0 +1,30 @@
1
+ class ZergXcode::Plugins::Help
2
+ def help
3
+ {:short => 'command-line usage instructions',
4
+ :long => <<"END" }
5
+ Usage: help [command]
6
+
7
+ Shows the usage for the given command. If no command is given, shows a list of
8
+ commands with a short description for each command.
9
+ END
10
+ end
11
+
12
+ def run(args)
13
+ helpstr = "Xcode Project Modifier brought to you by Zergling.Net.\n"
14
+
15
+ plugin = args.shift
16
+ if ZergXcode::Plugins.all.include? plugin
17
+ help = ZergXcode::Plugins.get(plugin).help
18
+ helpstr << "#{plugin} - #{help[:short]}\n#{help[:long]}"
19
+ else
20
+ helpstr << "Available commands:\n"
21
+ ZergXcode::Plugins.all.each do |plugin|
22
+ short_help = ZergXcode::Plugins.get(plugin).help[:short]
23
+ helpstr << " #{plugin}: #{short_help}\n"
24
+ end
25
+ end
26
+
27
+ print helpstr
28
+ helpstr
29
+ end
30
+ end
@@ -0,0 +1,19 @@
1
+ class ZergXcode::Plugins::Import
2
+ def help
3
+ {:short => 'imports the objects from a project into another project',
4
+ :long => <<"END" }
5
+ Usage: import source_path [target_path]
6
+
7
+ Imports the objects (build targets, files) from a source project into another
8
+ target project. Useful when the source project wraps code that can be used as a
9
+ library.
10
+ END
11
+ end
12
+
13
+ def run(args)
14
+ source = ZergXcode.load args.shift
15
+ target = ZergXcode.load(args.shift || '.')
16
+
17
+ print "Import is not yet implemented."
18
+ end
19
+ end
@@ -0,0 +1,22 @@
1
+ require 'irb'
2
+
3
+ class ZergXcode::Plugins::Irb
4
+ def help
5
+ {:short => 'opens up a project in an interactive ruby shell',
6
+ :long => <<"END" }
7
+ Usage: irb [path]
8
+
9
+ Loads the object graph in the Xcode project at the given path. If no path is
10
+ given, looks for a project in the current directory. The project object is
11
+ available as the global variable $p.
12
+ END
13
+ end
14
+
15
+ def run(args)
16
+ $p = ZergXcode.load(args.shift || '.')
17
+
18
+ print "This is an IRB shell. Your project is available as $p.\n" +
19
+ "Use the 'quit' command if you're here by mistake.\n"
20
+ IRB.start __FILE__
21
+ end
22
+ end
@@ -0,0 +1,27 @@
1
+ class ZergXcode::Plugins::Ls
2
+ def help
3
+ {:short => 'shows the files in a project',
4
+ :long => <<"END" }
5
+ Usage: ls [path]
6
+
7
+ Lists all the files in the project at the given path. If no path is given, looks
8
+ for a project in the current directory.
9
+ END
10
+ end
11
+
12
+ def run(args)
13
+ list = list_for(args.shift || '.')
14
+ output = ""
15
+ list.each do |entry|
16
+ output << "%-20s %s\n" % [entry.last, entry.first]
17
+ end
18
+ print output
19
+ output
20
+ end
21
+
22
+ def list_for(project_name)
23
+ ZergXcode.load(project_name).all_files.map do |file|
24
+ [file[:path], file[:object]['lastKnownFileType']]
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,90 @@
1
+ class ZergXcode::Plugins::Retarget
2
+ include ZergXcode::Objects
3
+ XcodeObject = ZergXcode::XcodeObject
4
+
5
+ def help
6
+ {:short => 'reassign files to a target or set of targets',
7
+ :long => <<"END" }
8
+ Usage: retarget project_path pattern [target target..]
9
+
10
+ Reassigns all the files matching a pattern to a target or set of targets. The
11
+ files matching the pattern will be removed from all other targets. If no target
12
+ is specified, the files are removed from all targets.
13
+ END
14
+ end
15
+
16
+ def run(args)
17
+ path = args.shift
18
+ pattern = args.shift
19
+ regexp = Regexp.compile pattern
20
+ project = ZergXcode.load path
21
+ retarget! project, regexp, args
22
+ project.save!
23
+ end
24
+
25
+ def retarget!(project, regexp, targets)
26
+ # maps each file type to the type of the phase it's in
27
+ file_type_phases = {}
28
+ # maps each file to the phase it's in
29
+ file_phases = {}
30
+ # maps each PBXFileRef to the PBXBuildFile pointing to it
31
+ build_files = {}
32
+
33
+ # populate the maps
34
+ project['targets'].each do |target|
35
+ target.all_files.each do |file|
36
+ build_file = file[:build_object]
37
+ phase_type = file[:phase]['isa']
38
+ build_files[file[:object]] = build_file
39
+ file_type_phases[build_file.file_type] = phase_type
40
+ file_phases[file[:object]] = phase_type
41
+ end
42
+ end
43
+
44
+ # compute target sets
45
+ in_targets = project['targets'].select do |target|
46
+ targets.include? target['name']
47
+ end
48
+ out_targets = project['targets'] - in_targets
49
+
50
+ # clean up targets outside the args
51
+ out_targets.each do |target|
52
+ target['buildPhases'].each do |phase|
53
+ phase['files'].reject! { |build_file| regexp =~ build_file.filename }
54
+ end
55
+ end
56
+
57
+ # build a list of the files in the project matching the pattern
58
+ new_files = project.all_files.map { |file| file[:object] }.select do |file|
59
+ regexp =~ file['path']
60
+ end
61
+ # build PBXBuildFile wrappers around files that don't have them
62
+ new_files.each do |file|
63
+ next if build_files[file]
64
+
65
+ build_file = PBXBuildFile.new 'fileRef' => file
66
+ build_files[file] = build_file
67
+ file_phases[file] = file_type_phases[build_file.file_type] ||
68
+ build_file.guessed_build_phase_type
69
+ end
70
+
71
+ # add files to targets matching the args
72
+ in_targets.each do |target|
73
+ already_in = Set.new(target.all_files.map { |file| file[:object] })
74
+ new_files.each do |file|
75
+ file_ref = file[:object]
76
+ next if already_in.include? file
77
+ phase_type = file_phases[file]
78
+ phase = target['buildPhases'].find { |p| p['isa'] == phase_type }
79
+ unless phase
80
+ phase = XcodeObject.new 'isa' => phase_type, 'dependencies' => [],
81
+ 'files' => [],
82
+ 'buildActionMask' => '2147483647',
83
+ 'runOnlyForDeploymentPostprocessing' => '0'
84
+ target['buildPhases'] << phase
85
+ end
86
+ phase['files'] << build_files[file]
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,22 @@
1
+ module ZergXcode
2
+ # Reads an Xcode project from the filesystem.
3
+ def self.load(path)
4
+ file = ZergXcode::Paths.project_file_at path
5
+ pbx_contents = File.read file
6
+ project = Archiver.unarchive pbx_contents
7
+ project.source_filename = file
8
+ return project
9
+ end
10
+
11
+ # Dumps an Xcode project to the filesystem.
12
+ def self.dump(project, path)
13
+ file = ZergXcode::Paths.project_file_at path
14
+ pbx_contents = Archiver.archive project
15
+ File.open(file, 'w') { |f| f.write pbx_contents }
16
+ end
17
+
18
+ # Instantiate a plug-in.
19
+ def self.plugin(plugin_name)
20
+ ZergXcode::Plugins.get(plugin_name)
21
+ end
22
+ end
data/lib/zerg_xcode.rb ADDED
@@ -0,0 +1,17 @@
1
+ module ZergXcode; end
2
+ module ZergXcode::Objects; end
3
+
4
+
5
+ require 'zerg_xcode/file_format/archiver.rb'
6
+ require 'zerg_xcode/file_format/encoder.rb'
7
+ require 'zerg_xcode/file_format/lexer.rb'
8
+ require 'zerg_xcode/file_format/parser.rb'
9
+ require 'zerg_xcode/file_format/paths.rb'
10
+
11
+ require 'zerg_xcode/objects/xcode_object.rb'
12
+ require 'zerg_xcode/objects/pbx_build_file.rb'
13
+ require 'zerg_xcode/objects/pbx_native_target.rb'
14
+ require 'zerg_xcode/objects/pbx_project.rb'
15
+ require 'zerg_xcode/plugins/core/core.rb'
16
+
17
+ require 'zerg_xcode/shortcuts.rb'
@@ -0,0 +1,70 @@
1
+ require 'test/unit'
2
+ require 'zerg_xcode'
3
+
4
+ class ArchiverTest < Test::Unit::TestCase
5
+ Parser = ZergXcode::Parser
6
+ Archiver = ZergXcode::Archiver
7
+ XcodeObject = ZergXcode::XcodeObject
8
+
9
+ def setup
10
+ @sub1 = XcodeObject.new :array => ['a', 'b', 'c'], :string => 's'
11
+ @sub1.archive_id = '39'
12
+ @sub2 = XcodeObject.new :hash => { :k => 'v', :k2 => 'v2' }, :sub1 => @sub1
13
+ @sub2.archive_id = '42'
14
+ @root = XcodeObject.new :sub1 => @sub1, :sub2 => @sub2
15
+ @root.archive_id = '49'
16
+ @root.version = 45
17
+
18
+ @archived = {
19
+ "archiveVersion" => '1',
20
+ "rootObject" => '49',
21
+ "classes" => {},
22
+ "objectVersion" => '45',
23
+ "objects" => {
24
+ "49" => { :sub1 => "39", :sub2 => "42" },
25
+ "39" => { :array => ["a", "b", "c"], :string => "s" },
26
+ "42" => { :hash => { :k => "v", :k2 => "v2" }, :sub1 => "39" }
27
+ }
28
+ }
29
+
30
+ @pbxdata = File.read 'testdata/project.pbxproj'
31
+ end
32
+
33
+ def test_archive_to_hash
34
+ hash = Archiver.archive_to_hash @root
35
+ assert_equal @archived, hash
36
+ end
37
+
38
+ def test_unarchive_hash
39
+ root = Archiver.unarchive_hash @archived
40
+ assert_equal @sub1[:s], root[:sub1][:s]
41
+ assert_equal @sub1[:array], root[:sub1][:array]
42
+ assert_equal @sub2[:hash], root[:sub2][:hash]
43
+ assert_equal root[:sub1], root[:sub2][:sub1], 'Object graph split'
44
+ end
45
+
46
+ def test_generator
47
+ generator = Archiver::IdGenerator.new
48
+ assert_equal '49', generator.id_for(@root)
49
+ assert_equal '42', generator.id_for(@sub2)
50
+ new_id = generator.id_for @root
51
+ assert '49' != new_id, 'Initial ID generated twice'
52
+ newer_id = generator.id_for @root
53
+ assert !['49', new_id].include?(newer_id), 'New ID generated twice'
54
+ end
55
+
56
+ def test_archive_passthrough
57
+ golden_hash = Parser.parse @pbxdata
58
+ project = Archiver.unarchive @pbxdata
59
+ dupdata = Archiver.archive project
60
+
61
+ assert_equal golden_hash, Parser.parse(dupdata)
62
+ end
63
+
64
+ def test_unarchive
65
+ project = Archiver.unarchive @pbxdata
66
+
67
+ assert_equal 'TestApp.app',
68
+ project['targets'][0]['productReference']['path']
69
+ end
70
+ end
@@ -0,0 +1,10 @@
1
+ require 'test/unit'
2
+
3
+ class EncoderTest < Test::Unit::TestCase
4
+ def test_encoder
5
+ pbxdata = File.read 'testdata/project.pbxproj'
6
+ golden_encoded_proj = File.read 'testdata/project.pbxproj.compat'
7
+ proj = ZergXcode::Parser.parse pbxdata
8
+ assert_equal golden_encoded_proj, ZergXcode::Encoder.encode(proj)
9
+ end
10
+ end
@@ -0,0 +1,56 @@
1
+ require 'test/unit'
2
+ require 'zerg_xcode'
3
+
4
+ class LexerTest < Test::Unit::TestCase
5
+ def test_lexer
6
+ pbxdata = File.read 'testdata/project.pbxproj'
7
+ golden_starts = [[:encoding, "UTF8"],
8
+ :begin_hash,
9
+ [:symbol, "archiveVersion"], :assign, [:symbol, "1"],
10
+ :stop,
11
+ [:symbol, "classes"], :assign, :begin_hash, :end_hash,
12
+ :stop,
13
+ [:symbol, "objectVersion"], :assign, [:symbol, "45"],
14
+ :stop,
15
+ [:symbol, "objects"], :assign, :begin_hash,
16
+ [:symbol, "1D3623260D0F684500981E51"], :assign,
17
+ :begin_hash,
18
+ [:symbol, "isa"], :assign, [:symbol, "PBXBuildFile"],
19
+ :stop,
20
+ [:symbol, "fileRef"], :assign,
21
+ [:symbol, "1D3623250D0F684500981E51"],
22
+ :stop,
23
+ :end_hash,
24
+ :stop,
25
+ [:symbol, "1D60589B0D05DD56006BFB54"], :assign,
26
+ :begin_hash,
27
+ [:symbol, "isa"], :assign, [:symbol, "PBXBuildFile"],
28
+ :stop,
29
+ [:symbol, "fileRef"], :assign,
30
+ [:symbol, "29B97316FDCFA39411CA2CEA"],
31
+ :stop,
32
+ :end_hash,
33
+ :stop,
34
+ [:symbol, "1D60589F0D05DD5A006BFB54"], :assign,
35
+ :begin_hash,
36
+ [:symbol, "isa"], :assign, [:symbol, "PBXBuildFile"],
37
+ :stop,
38
+ [:symbol, "fileRef"], :assign,
39
+ [:symbol, "1D30AB110D05D00D00671497"],
40
+ :stop,
41
+ :end_hash,
42
+ :stop]
43
+
44
+ tokens = ZergXcode::Lexer.tokenize pbxdata
45
+ assert_equal golden_starts, tokens[0, golden_starts.length]
46
+ #p tokens.length
47
+ #p tokens[197, 50]
48
+ end
49
+
50
+ def test_escaped_string
51
+ pbxdata = File.read 'testdata/ZergSupport.xcodeproj/project.pbxproj'
52
+ tokens = ZergXcode::Lexer.tokenize pbxdata
53
+ assert tokens.include?([:string,
54
+ "\"$(SRCROOT)/build/Debug-iphonesimulator\""])
55
+ end
56
+ end
@@ -0,0 +1,44 @@
1
+ require 'test/unit'
2
+
3
+ class ParserTest < Test::Unit::TestCase
4
+ def test_parser
5
+ pbxdata = File.read 'testdata/project.pbxproj'
6
+ proj = ZergXcode::Parser.parse pbxdata
7
+
8
+ assert proj.kind_of?(Hash), 'Project structure should be a hash'
9
+ assert_equal '1', proj['archiveVersion'], 'Archive version'
10
+ assert_equal '45', proj['objectVersion'], 'Object version'
11
+ assert_equal '29B97313FDCFA39411CA2CEA', proj['rootObject'], 'Root object'
12
+
13
+ golden_file_ref = {
14
+ 'isa' => 'PBXBuildFile',
15
+ 'fileRef' => '28AD733E0D9D9553002E5188'
16
+ }
17
+ assert_equal golden_file_ref, proj['objects']['28AD733F0D9D9553002E5188']
18
+
19
+ golden_file = {
20
+ 'isa' => 'PBXFileReference',
21
+ 'fileEncoding' => '4',
22
+ 'lastKnownFileType' => 'sourcecode.c.h',
23
+ 'path' => 'TestAppViewController.h',
24
+ 'sourceTree' => "<group>"
25
+ }
26
+ assert_equal golden_file, proj['objects']['28D7ACF60DDB3853001CB0EB']
27
+
28
+ golden_config = {
29
+ 'isa' => 'XCBuildConfiguration',
30
+ 'buildSettings' => {
31
+ 'ARCHS' => "$(ARCHS_STANDARD_32_BIT)",
32
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" => "iPhone Developer",
33
+ 'GCC_C_LANGUAGE_STANDARD' => 'c99',
34
+ 'GCC_WARN_ABOUT_RETURN_TYPE' => 'YES',
35
+ 'GCC_WARN_UNUSED_VARIABLE' => 'YES',
36
+ 'ONLY_ACTIVE_ARCH' => 'YES',
37
+ 'PREBINDING' => 'NO',
38
+ 'SDKROOT' => 'iphoneos2.2.1'
39
+ },
40
+ 'name' => 'Debug'
41
+ }
42
+ assert_equal golden_config, proj['objects']['C01FCF4F08A954540054247B']
43
+ end
44
+ end
@@ -0,0 +1,40 @@
1
+ require 'test/unit'
2
+
3
+ class PathTest < Test::Unit::TestCase
4
+ Paths = ZergXcode::Paths
5
+
6
+ def test_project_file_at
7
+ assert_equal 'testdata/ZergSupport.xcodeproj/project.pbxproj',
8
+ Paths.project_file_at('testdata/ZergSupport'), 'short'
9
+ assert_equal 'testdata/ZergSupport.xcodeproj/project.pbxproj',
10
+ Paths.project_file_at('testdata/ZergSupport.xcodeproj'),
11
+ 'full project name'
12
+ assert_equal 'testdata/project.pbxproj',
13
+ Paths.project_file_at('testdata'),
14
+ 'enclosing dir with weird name'
15
+ assert_equal './testdata/project.pbxproj',
16
+ Paths.project_file_at('.'),
17
+ 'subdir with weird name'
18
+ assert_equal 'testdata/TestApp/TestApp.xcodeproj/project.pbxproj',
19
+ Paths.project_file_at('testdata/TestApp'), 'project in subdir'
20
+ assert_equal 'testdata/TestApp/TestApp.xcodeproj/project.pbxproj',
21
+ Paths.project_file_at('testdata/TestApp/TestApp.xcodeproj'),
22
+ 'full project name in subdir'
23
+ end
24
+
25
+ def test_project_root_at
26
+ assert_equal 'testdata', Paths.project_root_at('testdata/ZergSupport'),
27
+ 'short project name'
28
+ assert_equal 'testdata',
29
+ Paths.project_root_at('testdata/ZergSupport.xcodeproj'),
30
+ 'full project name'
31
+ assert_equal '.', Paths.project_root_at('testdata'),
32
+ 'enclosing dir with weird name'
33
+ assert_equal '.', Paths.project_root_at('.'), 'subdir with weird name'
34
+ assert_equal 'testdata/TestApp',
35
+ Paths.project_root_at('testdata/TestApp'), 'project dir'
36
+ assert_equal 'testdata/TestApp',
37
+ Paths.project_root_at('testdata/TestApp/TestApp.xcodeproj'),
38
+ 'full project name in subdir'
39
+ end
40
+ end
@@ -0,0 +1,38 @@
1
+ require 'test/unit'
2
+
3
+ require 'zerg_xcode'
4
+
5
+ class PBXBuildFileTest < Test::Unit::TestCase
6
+ PBXBuildFile = ZergXcode::Objects::PBXBuildFile
7
+
8
+ def setup
9
+ @target = ZergXcode.load('testdata/project.pbxproj')['targets'].first
10
+ end
11
+
12
+ def test_attributes
13
+ sources_phase = @target['buildPhases'][1]
14
+ build_file = sources_phase['files'].first
15
+ assert_equal PBXBuildFile, build_file.class
16
+ assert_equal 'main.m', build_file.filename
17
+ assert_equal 'sourcecode.c.objc', build_file.file_type
18
+
19
+ big_project = ZergXcode.load('testdata/ZergSupport')
20
+ big_project['targets'].map { |t| t.all_files }.flatten.each do |file|
21
+ assert_not_nil file[:build_object].file_type,
22
+ "No file type for #{file[:build_object].inspect}"
23
+ end
24
+ end
25
+
26
+ def test_guessed_build_phase_type
27
+ big_project = ZergXcode.load('testdata/ZergSupport')
28
+
29
+ phases = @target['buildPhases'] +
30
+ big_project['targets'].map { |t| t['buildPhases'] }.flatten
31
+ phases.each do |phase|
32
+ phase['files'].each do |file|
33
+ assert_equal phase['isa'], file.guessed_build_phase_type,
34
+ "Wrong phase for #{file.inspect}"
35
+ end
36
+ end
37
+ end
38
+ end