zerg_xcode 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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