zerg_xcode 0.3.5 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/.document +5 -0
  2. data/.project +17 -0
  3. data/Gemfile +10 -0
  4. data/Gemfile.lock +30 -0
  5. data/Rakefile +50 -23
  6. data/VERSION +1 -0
  7. data/lib/zerg_xcode/file_format/archiver.rb +0 -24
  8. data/lib/zerg_xcode/file_format/id_generator.rb +30 -0
  9. data/lib/zerg_xcode/file_format/lexer.rb +113 -56
  10. data/lib/zerg_xcode/file_format/parser.rb +28 -34
  11. data/lib/zerg_xcode/file_format/scan_buffer.rb +46 -0
  12. data/lib/zerg_xcode/objects/pbx_project.rb +1 -1
  13. data/lib/zerg_xcode/objects/xcode_object.rb +2 -1
  14. data/lib/zerg_xcode/{file_format/paths.rb → paths.rb} +5 -6
  15. data/lib/zerg_xcode/shortcuts.rb +2 -2
  16. data/lib/zerg_xcode.rb +7 -1
  17. data/spec/archiver_spec.rb +83 -0
  18. data/spec/builder/runner_spec.rb +27 -0
  19. data/spec/builder/sdk_spec.rb +11 -0
  20. data/spec/encoder_spec.rb +17 -0
  21. data/{test → spec}/fixtures/ClosedLib/ClosedLib.xcodeproj/project.pbxproj +0 -0
  22. data/{test → spec}/fixtures/ClosedLib/ClosedLib_Prefix.pch +0 -0
  23. data/{test → spec}/fixtures/ClosedLib/ClosedNative.c +0 -0
  24. data/{test → spec}/fixtures/ClosedLib/ClosedNative.h +0 -0
  25. data/{test → spec}/fixtures/FlatTestApp/FlatTestApp.xcodeproj/project.pbxproj +0 -0
  26. data/{test → spec}/fixtures/TestApp/TestApp.xcodeproj/project.pbxproj +0 -0
  27. data/{test → spec}/fixtures/TestApp30.xcodeproj/project.pbxproj +0 -0
  28. data/{test → spec}/fixtures/TestLib30.xcodeproj/project.pbxproj +0 -0
  29. data/{test → spec}/fixtures/ZergSupport.xcodeproj/project.pbxproj +0 -0
  30. data/{test → spec}/fixtures/project.pbxproj +0 -0
  31. data/{test → spec}/fixtures/project.pbxproj.compat +0 -0
  32. data/spec/id_generator_spec.rb +38 -0
  33. data/spec/lexer_spec.rb +111 -0
  34. data/spec/parser_spec.rb +34 -0
  35. data/spec/paths_spec.rb +75 -0
  36. data/spec/shortcuts_spec.rb +24 -0
  37. data/test/objects/pbx_build_file_test.rb +3 -3
  38. data/test/objects/pbx_build_phase_test.rb +1 -1
  39. data/test/objects/pbx_container_item_proxy_test.rb +1 -1
  40. data/test/objects/pbx_group_test.rb +1 -1
  41. data/test/objects/pbx_native_target_test.rb +2 -2
  42. data/test/objects/pbx_project_test.rb +9 -9
  43. data/test/objects/pbx_target_dependency_test.rb +1 -1
  44. data/test/objects/xc_configuration_list_test.rb +1 -1
  45. data/test/plugins/addlibrary_test.rb +2 -2
  46. data/test/plugins/core/core_test.rb +1 -1
  47. data/test/plugins/import_test.rb +71 -71
  48. data/test/plugins/irb_test.rb +3 -3
  49. data/test/plugins/ls_test.rb +3 -3
  50. data/test/plugins/lstargets_test.rb +3 -3
  51. data/test/plugins/retarget_test.rb +2 -2
  52. data/zerg_xcode.gemspec +112 -17
  53. metadata +115 -113
  54. data/test/builder/runner_test.rb +0 -35
  55. data/test/builder/sdk_test.rb +0 -17
  56. data/test/file_format/archiver_test.rb +0 -74
  57. data/test/file_format/encoder_test.rb +0 -15
  58. data/test/file_format/lexer_test.rb +0 -60
  59. data/test/file_format/parser_test.rb +0 -49
  60. data/test/file_format/path_test.rb +0 -47
  61. data/test/shortcuts_test.rb +0 -22
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.project ADDED
@@ -0,0 +1,17 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <projectDescription>
3
+ <name>zerg_xcode</name>
4
+ <comment></comment>
5
+ <projects>
6
+ </projects>
7
+ <buildSpec>
8
+ <buildCommand>
9
+ <name>org.rubypeople.rdt.core.rubybuilder</name>
10
+ <arguments>
11
+ </arguments>
12
+ </buildCommand>
13
+ </buildSpec>
14
+ <natures>
15
+ <nature>org.rubypeople.rdt.core.rubynature</nature>
16
+ </natures>
17
+ </projectDescription>
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source :rubygems
2
+
3
+ group :development do
4
+ gem 'bundler', '~> 1.0.0'
5
+ gem 'jeweler', '~> 1.6.4'
6
+ gem 'rcov', '>= 0'
7
+ gem 'rspec', '~> 2.7.0'
8
+
9
+ gem 'flexmock'
10
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,30 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ diff-lcs (1.1.3)
5
+ flexmock (0.9.0)
6
+ git (1.2.5)
7
+ jeweler (1.6.4)
8
+ bundler (~> 1.0)
9
+ git (>= 1.2.5)
10
+ rake
11
+ rake (0.9.2.2)
12
+ rcov (0.9.11)
13
+ rspec (2.7.0)
14
+ rspec-core (~> 2.7.0)
15
+ rspec-expectations (~> 2.7.0)
16
+ rspec-mocks (~> 2.7.0)
17
+ rspec-core (2.7.1)
18
+ rspec-expectations (2.7.0)
19
+ diff-lcs (~> 1.1.2)
20
+ rspec-mocks (2.7.0)
21
+
22
+ PLATFORMS
23
+ ruby
24
+
25
+ DEPENDENCIES
26
+ bundler (~> 1.0.0)
27
+ flexmock
28
+ jeweler (~> 1.6.4)
29
+ rcov
30
+ rspec (~> 2.7.0)
data/Rakefile CHANGED
@@ -5,29 +5,56 @@
5
5
  # License:: MIT
6
6
 
7
7
  require 'rubygems'
8
- require 'echoe'
9
-
10
- Echoe.new('zerg_xcode') do |p|
11
- p.project = 'zerglings' # rubyforge project
12
-
13
- p.author = 'Victor Costan'
14
- p.email = 'victor@zergling.net'
15
- p.summary = 'Automated modifications for Xcode project files'
16
- p.url = 'http://www.zergling.net/'
17
- p.runtime_dependencies = []
18
- p.dependencies = []
19
- # remove echoe, because it becomes a runtime dependency for rubygems < 1.2
20
- p.development_dependencies = []
21
- p.eval = proc do |p|
22
- p.default_executable = 'bin/zerg-xcode'
23
- end
24
-
25
- p.need_tar_gz = true
26
- p.need_zip = true
27
- p.rdoc_pattern = /^(lib|bin|tasks|ext)|^BUILD|^README|^CHANGELOG|^LICENSE$/
8
+ require 'bundler'
9
+ begin
10
+ Bundler.setup(:default, :development)
11
+ rescue Bundler::BundlerError => e
12
+ $stderr.puts e.message
13
+ $stderr.puts "Run `bundle install` to install missing gems"
14
+ exit e.status_code
15
+ end
16
+ require 'rake'
17
+
18
+ require 'jeweler'
19
+ Jeweler::Tasks.new do |gem|
20
+ gem.name = 'zerg_xcode'
21
+ gem.homepage = "http://github.com/zerglings/zerg_xcode"
22
+ gem.authors = ['Victor Costan']
23
+ gem.email = 'victor@zergling.net'
24
+ gem.summary = 'Automated modifications for Xcode project files'
28
25
  end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rake/testtask'
29
+ Rake::TestTask.new(:test) do |test|
30
+ test.libs << 'lib' << 'test'
31
+ test.pattern = 'test/**/*_test.rb'
32
+ test.verbose = true
33
+ end
34
+
35
+ require 'rspec/core/rake_task'
36
+ RSpec::Core::RakeTask.new(:spec)
29
37
 
30
- if $0 == __FILE__
31
- Rake.application = Rake::Application.new
32
- Rake.application.run
38
+ require 'rcov/rcovtask'
39
+ Rcov::RcovTask.new do |test|
40
+ test.libs << 'test'
41
+ test.pattern = 'test/**/test_*.rb'
42
+ test.verbose = true
43
+ test.rcov_opts << '--exclude "gems/*"'
44
+ end
45
+
46
+ task :default => [:spec, :test]
47
+
48
+ begin
49
+ require 'rdoc/task'
50
+ RDoc::Task.new do |rdoc|
51
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
52
+
53
+ rdoc.rdoc_dir = 'rdoc'
54
+ rdoc.title = "zerg_xcode #{version}"
55
+ rdoc.rdoc_files.include('README*')
56
+ rdoc.rdoc_files.include('lib/**/*.rb')
57
+ end
58
+ rescue LoadError => e
59
+ # Rdoc compatible with 1.8 doesn't have rdoc/task
33
60
  end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.4.0
@@ -71,30 +71,6 @@ module Archiver
71
71
  'objects' => objects }
72
72
  end
73
73
 
74
- # Generates archive IDs for objects.
75
- class IdGenerator
76
- def initialize
77
- @assigned_ids = Set.new
78
- end
79
-
80
- def new_id
81
- loop do
82
- id = (0...24).map { '%02X' % rand(256) }.join
83
- next if @assigned_ids.include? id
84
- @assigned_ids << id
85
- return id
86
- end
87
- end
88
-
89
- def id_for(object)
90
- if object.archive_id && !@assigned_ids.include?(object.archive_id)
91
- @assigned_ids << object.archive_id
92
- return object.archive_id
93
- else
94
- return new_id
95
- end
96
- end
97
- end
98
74
  end # module ZergXcode::Archiver
99
75
 
100
76
  end # namespace ZergXcode
@@ -0,0 +1,30 @@
1
+
2
+ # :nodoc: namespace
3
+ module ZergXcode
4
+
5
+ # Generates archive IDs for objects.
6
+ class IdGenerator
7
+ def initialize
8
+ @assigned_ids = Set.new
9
+ end
10
+
11
+ def new_id
12
+ loop do
13
+ id = (0...24).map { '%02X' % rand(256) }.join
14
+ next if @assigned_ids.include? id
15
+ @assigned_ids << id
16
+ return id
17
+ end
18
+ end
19
+
20
+ def id_for(object)
21
+ if object.archive_id && !@assigned_ids.include?(object.archive_id)
22
+ @assigned_ids << object.archive_id
23
+ return object.archive_id
24
+ else
25
+ return new_id
26
+ end
27
+ end
28
+ end
29
+
30
+ end
@@ -9,65 +9,122 @@ module ZergXcode
9
9
 
10
10
 
11
11
  # Lexer for flattened object graphs stored in .xcodeproj files.
12
- module Lexer
13
- def self.tokenize(string)
14
-
15
- encoding_match = string.match(/^\/\/ \!\$\*(.*?)\*\$\!/)
16
- raise "No encoding - #{string[0, 20]}" unless encoding_match
17
-
18
- i = encoding_match[0].length
19
- tokens = [[:encoding, encoding_match[1]]]
20
- while i < string.length
21
- # skip comments
22
- if string[i, 2] == '/*'
23
- i += 2
24
- i += 1 while string[i, 2] != '*/'
25
- i += 2
26
- next
27
- end
28
-
29
- case string[i, 1]
30
- when /\s/
31
- i += 1
32
- when '(', ')', '{', '}', '=', ';', ','
33
- tokens << {'(' => :begin_array, ')' => :end_array,
34
- '{' => :begin_hash, '}' => :end_hash,
35
- '=' => :assign, ';' => :stop, ',' => :comma}[string[i, 1]]
36
- i += 1
37
- when '"'
38
- # string
39
- i += 1
40
- token = ''
41
- while string[i, 1] != '"'
42
- if string[i, 1] == '\\'
43
- i += 1
44
- case string[i, 1]
45
- when 'n', 'r', 't'
46
- token << { 'n' => "\n", 't' => "\t", 'r' => "\r" }[string[i, 1]]
47
- i += 1
48
- when '"', "'", '\\'
49
- token << string[i]
50
- i += 1
51
- else
52
- raise "Uknown escape sequence \\#{string[i, 20]}"
53
- end
54
- else
55
- token << string[i]
56
- i += 1
57
- end
12
+ class Lexer
13
+
14
+ SIMPLE_TOKENS = [ '(', ')', '{', '}', '=', ';', ',' ]
15
+
16
+ def initialize(string)
17
+ @scan_buffer = ScanBuffer.new(string)
18
+ end
19
+
20
+ def tokenize
21
+ tokens, token = [], nil
22
+ tokens << token until (token = scan_token).nil?
23
+ tokens
24
+ end
25
+
26
+ def scan_token
27
+ skip_encodings_comments_and_whitespace
28
+
29
+ return nil if at_end_of_buffer?
30
+ return scan_simple_token if at_simple_token?
31
+ return scan_quoted_string if at_quoted_string?
32
+ return scan_bare_string
33
+ end
34
+ private :scan_token
35
+
36
+ def skip_encodings_comments_and_whitespace
37
+ nil while skip_encoding_comment_or_whitespace
38
+ end
39
+ private :skip_encodings_comments_and_whitespace
40
+
41
+ def skip_encoding_comment_or_whitespace
42
+ if at_encoding?
43
+ skip_encoding
44
+ true
45
+ elsif @scan_buffer.peek =~ /\s/
46
+ @scan_buffer.advance
47
+ true
48
+ elsif @scan_buffer.at?('/*')
49
+ skip_comment
50
+ true
51
+ else
52
+ false
53
+ end
54
+ end
55
+ private :skip_encoding_comment_or_whitespace
56
+
57
+ def skip_comment
58
+ @scan_buffer.advance(2)
59
+ @scan_buffer.advance until @scan_buffer.at?('*/')
60
+ @scan_buffer.advance(2)
61
+ end
62
+ private :skip_comment
63
+
64
+ def at_end_of_buffer?
65
+ @scan_buffer.at_end?
66
+ end
67
+ private :at_end_of_buffer?
68
+
69
+ def at_encoding?
70
+ @scan_buffer.at?('// !$*')
71
+ end
72
+ private :at_encoding?
73
+
74
+ def skip_encoding
75
+ @scan_buffer.match_and_advance(/^\/\/ \!\$\*(.*?)\*\$\!/)
76
+ end
77
+ private :skip_encoding
78
+
79
+ def at_quoted_string?
80
+ @scan_buffer.at?('"')
81
+ end
82
+ private :at_quoted_string?
83
+
84
+ def scan_quoted_string
85
+ @scan_buffer.advance
86
+ token = ''
87
+ until @scan_buffer.at?('"')
88
+ if @scan_buffer.at?('\\')
89
+ @scan_buffer.advance
90
+ case @scan_buffer.peek
91
+ when 'n', 'r', 't'
92
+ token << { 'n' => "\n", 't' => "\t", 'r' => "\r" }[@scan_buffer.take]
93
+ when '"', "'", '\\'
94
+ token << @scan_buffer.take
95
+ else
96
+ raise "Uknown escape sequence \\#{@scan_buffer.peek 20}"
58
97
  end
59
- tokens << [:string, token]
60
- i += 1
61
98
  else
62
- # something
63
- len = 0
64
- len += 1 while /[^\s\t\r\n\f(){}=;,]/ =~ string[i + len, 1]
65
- tokens << [:symbol, string[i, len]]
66
- i += len
99
+ token << @scan_buffer.take
67
100
  end
68
101
  end
69
- return tokens
102
+ @scan_buffer.advance
103
+ return [token]
104
+ end
105
+ private :scan_quoted_string
106
+
107
+ def at_simple_token?
108
+ SIMPLE_TOKENS.include?(@scan_buffer.peek)
109
+ end
110
+ private :at_simple_token?
111
+
112
+ def scan_simple_token
113
+ @scan_buffer.take
114
+ end
115
+ private :scan_simple_token
116
+
117
+ def scan_bare_string
118
+ text = ""
119
+ text << @scan_buffer.take while @scan_buffer.peek(1) =~ /[^\s\t\r\n\f(){}=;,]/
120
+ return [text]
70
121
  end
71
- end # module ZergXcode::Lexer
122
+ private :scan_bare_string
123
+
124
+ def self.tokenize(string)
125
+ Lexer.new(string).tokenize
126
+ end
127
+
128
+ end
72
129
 
73
- end # namespace ZergXcode
130
+ end
@@ -13,47 +13,41 @@ module Parser
13
13
  def self.parse(project_string)
14
14
  tokens = ZergXcode::Lexer.tokenize project_string
15
15
 
16
- context = [[]]
17
- last_token = nil
16
+ stack = [[]]
18
17
  tokens.each do |token|
19
18
  case token
20
- when :begin_array
21
- context << Array.new
22
- when :begin_hash
23
- context << Hash.new
24
- when :end_array, :end_hash
25
- last_object = context.pop
26
- if context.last.kind_of? Array
27
- context.last << last_object
28
- elsif context.last.kind_of? String
29
- hash_key = context.pop
30
- context.last[hash_key] = last_object
19
+ when '('
20
+ stack << Array.new
21
+ when '{'
22
+ stack << Hash.new
23
+ when ')', '}'
24
+ last_object = stack.pop
25
+ if stack.last.kind_of? Array
26
+ stack.last << last_object
27
+ elsif stack.last.kind_of? String
28
+ hash_key = stack.pop
29
+ stack.last[hash_key] = last_object
31
30
  end
32
- when :assign, :stop, :comma
33
-
34
31
  when Array
35
- case token.first
36
- when :encoding
37
- when :string, :symbol
38
- token_string = token.last
39
- if context.last.kind_of? Hash
40
- context << token_string
41
- elsif context.last.kind_of? Array
42
- context.last << token_string
43
- elsif context.last.kind_of? String
44
- key = context.pop
45
- context.last[key] = token_string
46
- else
47
- p context
48
- raise 'WTFed'
49
- end
50
- end
32
+ token_string = token.first
33
+ if stack.last.kind_of? Hash
34
+ stack << token_string
35
+ elsif stack.last.kind_of? Array
36
+ stack.last << token_string
37
+ elsif stack.last.kind_of? String
38
+ key = stack.pop
39
+ stack.last[key] = token_string
40
+ else
41
+ p stack
42
+ raise 'WTFed'
43
+ end
44
+ when '=', ';', ','
51
45
  else
52
46
  raise "Unknown token #{token}"
53
47
  end
54
48
  end
55
- return context[0][0]
49
+ return stack[0][0]
56
50
  end
57
- end # module ZergXcode::Parser
51
+ end
58
52
 
59
- end # namespace ZergXcode
53
+ end
@@ -0,0 +1,46 @@
1
+ module ZergXcode
2
+
3
+ class ScanBuffer
4
+ def initialize(string)
5
+ @string = string
6
+ @i = 0
7
+ end
8
+
9
+ def at_beginning?
10
+ @i == 0
11
+ end
12
+
13
+ def at_end?
14
+ @i == @string.length
15
+ end
16
+
17
+ def at?(literal)
18
+ peek(literal.length) == literal
19
+ end
20
+
21
+ def unconsumed
22
+ @string[@i..-1]
23
+ end
24
+
25
+ def advance(n=1)
26
+ @i += n
27
+ end
28
+
29
+ def peek(n=1)
30
+ @string[@i, n]
31
+ end
32
+
33
+ def take(n=1)
34
+ result = peek(n)
35
+ advance(n)
36
+ result
37
+ end
38
+
39
+ def match_and_advance(pattern)
40
+ match = @string[@i..-1].match(pattern)
41
+ @i += match[0].length if match
42
+ match
43
+ end
44
+ end
45
+
46
+ end
@@ -27,7 +27,7 @@ class PBXProject < ZergXcode::XcodeObject
27
27
 
28
28
  # The root path of the project.
29
29
  def root_path
30
- ZergXcode::Paths.project_root_at source_filename
30
+ ZergXcode.project_root_at source_filename
31
31
  end
32
32
 
33
33
  # All the files referenced by the project.
@@ -36,7 +36,8 @@ class XcodeObject
36
36
  def self.new(*args)
37
37
  return super unless self == ZergXcode::XcodeObject
38
38
  if hash_isa = args.first['isa']
39
- classes = ZergXcode::Objects.constants
39
+ hash_isa = hash_isa.to_sym
40
+ classes = ZergXcode::Objects.constants.map(&:to_sym)
40
41
  if classes.include? hash_isa
41
42
  return ZergXcode::Objects.const_get(hash_isa).new(*args)
42
43
  end
@@ -7,11 +7,10 @@
7
7
  # :nodoc: namespace
8
8
  module ZergXcode
9
9
 
10
-
11
- # Finds the .pbxproj file inside an Xcode project.
12
10
  module Paths
11
+
13
12
  # The most likely project file name for the given path.
14
- def self.project_file_at(base_path)
13
+ def project_file_at(base_path)
15
14
  return base_path if File.exist?(base_path) and File.file?(base_path)
16
15
  pbxfile = 'project.pbxproj'
17
16
 
@@ -48,10 +47,10 @@ module Paths
48
47
  end
49
48
 
50
49
  # The most likely project root dir for the given path.
51
- def self.project_root_at(base_path)
50
+ def project_root_at(base_path)
52
51
  file = project_file_at base_path
53
52
  File.dirname File.dirname(file)
54
53
  end
55
- end # module ZergXcode::Paths
54
+ end
56
55
 
57
- end # module ZergXcode
56
+ end
@@ -9,7 +9,7 @@
9
9
  module ZergXcode
10
10
  # Reads an Xcode project from the filesystem.
11
11
  def self.load(path)
12
- file = ZergXcode::Paths.project_file_at path
12
+ file = ZergXcode.project_file_at path
13
13
  pbx_contents = File.read file
14
14
  project = Archiver.unarchive pbx_contents
15
15
  project.source_filename = file
@@ -18,7 +18,7 @@ module ZergXcode
18
18
 
19
19
  # Dumps an Xcode project to the filesystem.
20
20
  def self.dump(project, path)
21
- file = ZergXcode::Paths.project_file_at path
21
+ file = ZergXcode.project_file_at path
22
22
  pbx_contents = Archiver.archive project
23
23
  File.open(file, 'w') { |f| f.write pbx_contents }
24
24
  end
data/lib/zerg_xcode.rb CHANGED
@@ -6,9 +6,10 @@
6
6
 
7
7
  require 'zerg_xcode/file_format/archiver.rb'
8
8
  require 'zerg_xcode/file_format/encoder.rb'
9
+ require 'zerg_xcode/file_format/id_generator.rb'
9
10
  require 'zerg_xcode/file_format/lexer.rb'
10
11
  require 'zerg_xcode/file_format/parser.rb'
11
- require 'zerg_xcode/file_format/paths.rb'
12
+ require 'zerg_xcode/file_format/scan_buffer.rb'
12
13
 
13
14
  require 'zerg_xcode/builder/runner.rb'
14
15
  require 'zerg_xcode/builder/sdks.rb'
@@ -24,4 +25,9 @@ require 'zerg_xcode/objects/pbx_target_dependency.rb'
24
25
  require 'zerg_xcode/objects/xc_configuration_list.rb'
25
26
  require 'zerg_xcode/plugins/core/core.rb'
26
27
 
28
+ require 'zerg_xcode/paths.rb'
27
29
  require 'zerg_xcode/shortcuts.rb'
30
+
31
+ module ZergXcode
32
+ extend ZergXcode::Paths
33
+ end