rxcode 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +17 -0
- data/LICENSE.txt +12 -0
- data/README.md +7 -0
- data/Rakefile +54 -0
- data/VERSION +1 -0
- data/bin/rxcode +9 -0
- data/lib/rxcode.rb +17 -0
- data/lib/rxcode/command.rb +179 -0
- data/lib/rxcode/commands.rb +4 -0
- data/lib/rxcode/commands/env.rb +45 -0
- data/lib/rxcode/commands/init.rb +74 -0
- data/lib/rxcode/commands/unwrap.rb +76 -0
- data/lib/rxcode/environment.rb +80 -0
- data/lib/rxcode/macruby.rb +16 -0
- data/lib/rxcode/models.rb +8 -0
- data/lib/rxcode/models/archive.rb +78 -0
- data/lib/rxcode/models/archived_object.rb +62 -0
- data/lib/rxcode/models/build_configuration.rb +19 -0
- data/lib/rxcode/models/build_configuration_list.rb +15 -0
- data/lib/rxcode/models/file_reference.rb +23 -0
- data/lib/rxcode/models/model.rb +66 -0
- data/lib/rxcode/models/project.rb +71 -0
- data/lib/rxcode/models/target.rb +47 -0
- data/lib/rxcode/preferences.rb +32 -0
- data/lib/rxcode/spec/nserror_helpers.rb +19 -0
- data/lib/rxcode/spec/rake_ext.rb +27 -0
- data/lib/rxcode/spec/rake_task.rb +20 -0
- data/lib/rxcode/spec_helper.rb +19 -0
- data/lib/rxcode/tasks.rb +24 -0
- data/lib/rxcode/tasks/bridge_support.rb +39 -0
- data/lib/rxcode/tasks/ios_framework.rb +266 -0
- data/lib/rxcode/tasks/spec.rb +3 -0
- data/lib/rxcode/templates/Gemfile +4 -0
- data/lib/rxcode/templates/Rakefile +4 -0
- data/lib/rxcode/templates/spec/spec_helper.rb +14 -0
- data/lib/rxcode/templates/spec/support/.gitkeep +0 -0
- data/lib/rxcode/workspace.rb +173 -0
- metadata +184 -0
data/Gemfile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
# Example:
|
4
|
+
# gem "activesupport", ">= 2.3.5"
|
5
|
+
gem 'plist'
|
6
|
+
gem 'trollop'
|
7
|
+
gem 'nokogiri'
|
8
|
+
|
9
|
+
# Add dependencies to develop your gem here.
|
10
|
+
# Include everything needed to run rake, tests, features, etc.
|
11
|
+
group :development do
|
12
|
+
gem "gemcutter", "~> 0.7.0"
|
13
|
+
gem "rspec", "~> 2.6.0"
|
14
|
+
gem "bundler", "~> 1.0.0"
|
15
|
+
gem "jeweler", "~> 1.6.2"
|
16
|
+
gem "rcov", ">= 0"
|
17
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
Copyright (c) 2011, Vulpine Labs LLC
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
5
|
+
|
6
|
+
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
7
|
+
|
8
|
+
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
9
|
+
|
10
|
+
* Neither the name of the Vulpine Labs, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
11
|
+
|
12
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "rxcode"
|
18
|
+
gem.homepage = "http://github.com/vulpinelabs/rxcode"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{A Ruby interface for working with XCode projects.}
|
21
|
+
gem.description = %Q{A Ruby interface for working with XCode projects.}
|
22
|
+
gem.email = "christian@nerdyc.com"
|
23
|
+
gem.authors = ["Christian Niles"]
|
24
|
+
|
25
|
+
gem.files = FileList[ 'lib/**/*.rb',
|
26
|
+
'lib/rxcode/templates/**',
|
27
|
+
'lib/rxcode/templates/spec/support/.gitkeep',
|
28
|
+
'bin/*',
|
29
|
+
'[A-Z]*'
|
30
|
+
|
31
|
+
].to_a - %w[Gemfile.lock]
|
32
|
+
gem.executables << 'rxcode'
|
33
|
+
|
34
|
+
# dependencies defined in Gemfile
|
35
|
+
end
|
36
|
+
Jeweler::RubygemsDotOrgTasks.new
|
37
|
+
|
38
|
+
require 'rspec/core'
|
39
|
+
require 'rspec/core/rake_task'
|
40
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
41
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
42
|
+
end
|
43
|
+
|
44
|
+
task :default => :spec
|
45
|
+
|
46
|
+
require 'rake/rdoctask'
|
47
|
+
Rake::RDocTask.new do |rdoc|
|
48
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
49
|
+
|
50
|
+
rdoc.rdoc_dir = 'rdoc'
|
51
|
+
rdoc.title = "rxcode #{version}"
|
52
|
+
rdoc.rdoc_files.include('README*')
|
53
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
54
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.5
|
data/bin/rxcode
ADDED
data/lib/rxcode.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module RXCode
|
4
|
+
|
5
|
+
VERSION = File.read(File.expand_path('../../VERSION', __FILE__)).strip
|
6
|
+
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'rxcode/environment'
|
10
|
+
require 'rxcode/preferences'
|
11
|
+
|
12
|
+
require 'rxcode/workspace'
|
13
|
+
require 'rxcode/models'
|
14
|
+
|
15
|
+
if defined?(MACRUBY_VERSION)
|
16
|
+
require 'rxcode/macruby'
|
17
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
module RXCode
|
2
|
+
|
3
|
+
class Command
|
4
|
+
|
5
|
+
#
|
6
|
+
# Initializes the command with a set of +options+ and/or +arguments+. The formal way to initialize a command looks
|
7
|
+
# like this, with options followed by arguments:
|
8
|
+
#
|
9
|
+
# Command.new({ :option => 'value' }, [ 'first arg', 'second arg' ])
|
10
|
+
#
|
11
|
+
# This is the way it's done using parsed command-line arguments. However, this isn't as intuitive when creating
|
12
|
+
# commands programmatically, so the more rubyish constructor signature is also supported:
|
13
|
+
#
|
14
|
+
# Command.new('first arg', 'second arg', :option => 'value')
|
15
|
+
# Command.new('first arg', 'second arg')
|
16
|
+
#
|
17
|
+
def initialize(*args)
|
18
|
+
if args.first.is_a?(Hash)
|
19
|
+
@options = args.shift
|
20
|
+
@arguments = args.shift
|
21
|
+
else
|
22
|
+
if args.last.is_a?(Hash)
|
23
|
+
@options = args.pop
|
24
|
+
else
|
25
|
+
@options = {}
|
26
|
+
end
|
27
|
+
|
28
|
+
@arguments = args
|
29
|
+
end
|
30
|
+
|
31
|
+
yield self if block_given?
|
32
|
+
end
|
33
|
+
|
34
|
+
# ===== STREAMS ====================================================================================================
|
35
|
+
|
36
|
+
attr_accessor :output
|
37
|
+
def output
|
38
|
+
@output || $>
|
39
|
+
end
|
40
|
+
|
41
|
+
attr_accessor :err
|
42
|
+
def err
|
43
|
+
@err || $stderr
|
44
|
+
end
|
45
|
+
|
46
|
+
attr_accessor :input
|
47
|
+
def input
|
48
|
+
@input || $<
|
49
|
+
end
|
50
|
+
|
51
|
+
# ===== OPTIONS ====================================================================================================
|
52
|
+
|
53
|
+
attr_reader :options
|
54
|
+
|
55
|
+
# ===== ARGUMENTS ==================================================================================================
|
56
|
+
|
57
|
+
attr_reader :arguments
|
58
|
+
|
59
|
+
# ===== XCODE ENVIRONMENT ==========================================================================================
|
60
|
+
|
61
|
+
def env
|
62
|
+
@env ||= RXCode::Environment.new(Dir.pwd)
|
63
|
+
end
|
64
|
+
|
65
|
+
# ===== COMMAND REGISTRATION =======================================================================================
|
66
|
+
# Commands are automatically registered when they subclass Command.
|
67
|
+
|
68
|
+
def self.inherited(subclass)
|
69
|
+
self.commands[subclass.display_name] = subclass
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.commands
|
73
|
+
@commands ||= {}
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.command_names
|
77
|
+
self.commands.keys
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.command_class_for_name(command_name)
|
81
|
+
self.commands[command_name]
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.display_name
|
85
|
+
self.name.split('::').last.
|
86
|
+
gsub(/([A-Z]+)/) { |uppercase| '_' + uppercase.downcase }.
|
87
|
+
gsub(/^_/, '').
|
88
|
+
gsub(/[^a-z]_/) { |str| str.gsub(/_$/, '') }
|
89
|
+
end
|
90
|
+
|
91
|
+
# ===== COMMAND RUNNING ============================================================================================
|
92
|
+
|
93
|
+
def run!
|
94
|
+
if self.class == Command
|
95
|
+
raise "#{self.class.name} is an abstract class."
|
96
|
+
else
|
97
|
+
raise "#{Command.name}#run! is abstract and should be overridden"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.run!(args = ARGV)
|
102
|
+
require 'trollop'
|
103
|
+
|
104
|
+
global_options = {}
|
105
|
+
command_name = nil
|
106
|
+
command_arguments = []
|
107
|
+
|
108
|
+
parser = self.new_global_option_parser
|
109
|
+
Trollop::with_standard_exception_handling parser do
|
110
|
+
global_options = parser.parse(args)
|
111
|
+
command_arguments = parser.leftovers
|
112
|
+
command_name = command_arguments.shift
|
113
|
+
|
114
|
+
if command_name.nil?
|
115
|
+
|
116
|
+
if (global_options.keys - [:version, :help]).empty?
|
117
|
+
raise Trollop::HelpNeeded
|
118
|
+
else
|
119
|
+
parser.die("No command given", nil)
|
120
|
+
end
|
121
|
+
|
122
|
+
elsif !RXCode::Command.command_names.include?(command_name)
|
123
|
+
|
124
|
+
parser.die("Unknown command (#{command_name.inspect})", nil)
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
run_command(command_name, command_arguments, global_options)
|
131
|
+
end
|
132
|
+
|
133
|
+
def self.run_command(command_name, command_args = [], global_options = {})
|
134
|
+
if command_class = self.command_class_for_name(command_name)
|
135
|
+
parser = command_class.new_command_option_parser
|
136
|
+
|
137
|
+
Trollop::with_standard_exception_handling parser do
|
138
|
+
|
139
|
+
command_options = parser.parse(command_args)
|
140
|
+
command_arguments = parser.leftovers
|
141
|
+
|
142
|
+
command = command_class.new(command_options, command_arguments)
|
143
|
+
command.run!
|
144
|
+
end
|
145
|
+
|
146
|
+
else
|
147
|
+
raise "Invalid Command: #{command_name.inspect}"
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
|
152
|
+
# ----- COMMAND LINE PARSER ----------------------------------------------------------------------------------------
|
153
|
+
|
154
|
+
def self.new_global_option_parser
|
155
|
+
|
156
|
+
Trollop::Parser.new do
|
157
|
+
version "rxcode #{RXCode::VERSION}"
|
158
|
+
banner <<-END
|
159
|
+
A utility for manipulating XCode projects.
|
160
|
+
|
161
|
+
Usage:
|
162
|
+
#{$0} [global options] command [command options]
|
163
|
+
|
164
|
+
Available Commands: #{::RXCode::Command.command_names.sort.join(', ')}
|
165
|
+
|
166
|
+
Global Options:
|
167
|
+
END
|
168
|
+
stop_on_unknown
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
|
173
|
+
def self.new_command_option_parser
|
174
|
+
Trollop::Parser.new
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
|
179
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module RXCode
|
2
|
+
module Commands
|
3
|
+
|
4
|
+
#
|
5
|
+
# Displays information about the current XCode environment
|
6
|
+
#
|
7
|
+
class Env < ::RXCode::Command
|
8
|
+
|
9
|
+
def self.display(env, output=$>)
|
10
|
+
output.puts "[ #{env.root} ]"
|
11
|
+
output.puts
|
12
|
+
workspace_path = env.workspace_path
|
13
|
+
output.puts "Workspace: #{workspace_path || '(none)'}"
|
14
|
+
output.puts "Build Location: #{env.workspace.build_location || '(none)'}"
|
15
|
+
output.puts "Built Products: #{env.workspace.built_products_dir || '(none)'}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def run!
|
19
|
+
if arguments.empty?
|
20
|
+
self.class.display(Dir.pwd)
|
21
|
+
else
|
22
|
+
arguments.each do |root|
|
23
|
+
env = RXCode::Environment.new(File.expand_path(root))
|
24
|
+
self.class.display(env)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.new_command_option_parser
|
30
|
+
Trollop::Parser.new do
|
31
|
+
banner <<-TEXT
|
32
|
+
Displays information about the current XCode environment, culled from the current directory.
|
33
|
+
|
34
|
+
Usage:
|
35
|
+
#{$0} [global options] env [env_root]
|
36
|
+
|
37
|
+
Options:
|
38
|
+
TEXT
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module RXCode
|
2
|
+
module Commands
|
3
|
+
|
4
|
+
#
|
5
|
+
# Displays information about the current XCode environment
|
6
|
+
#
|
7
|
+
class Init < ::RXCode::Command
|
8
|
+
|
9
|
+
def projects
|
10
|
+
if arguments.empty?
|
11
|
+
[ '.' ]
|
12
|
+
else
|
13
|
+
arguments
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
TEMPLATE_FILES = [
|
18
|
+
'Gemfile',
|
19
|
+
'Rakefile',
|
20
|
+
'spec/spec_helper.rb',
|
21
|
+
'spec/support/.gitkeep'
|
22
|
+
]
|
23
|
+
|
24
|
+
def run!
|
25
|
+
|
26
|
+
templates_path = File.expand_path("../../templates", __FILE__)
|
27
|
+
|
28
|
+
projects.each do |project_path|
|
29
|
+
|
30
|
+
if RXCode::Project.is_project_at_path?(project_path) ||
|
31
|
+
RXCode::Workspace.is_workspace_at_path?(project_path)
|
32
|
+
|
33
|
+
project_path = File.dirname(project_path)
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
TEMPLATE_FILES.each do |template_name|
|
38
|
+
template_path = File.join(templates_path, template_name)
|
39
|
+
instance_path = File.join(project_path, template_name)
|
40
|
+
|
41
|
+
if File.exist?(instance_path)
|
42
|
+
|
43
|
+
output.puts "#{project_path.inspect} already exists"
|
44
|
+
|
45
|
+
else
|
46
|
+
# create any intermediate directories...
|
47
|
+
FileUtils.mkdir_p(File.dirname(instance_path))
|
48
|
+
|
49
|
+
# ...and copy the file contents
|
50
|
+
FileUtils.cp(template_path, instance_path)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.new_command_option_parser
|
59
|
+
Trollop::Parser.new do
|
60
|
+
banner <<-TEXT
|
61
|
+
Initializes the current project or workspace with RXCode.
|
62
|
+
|
63
|
+
Usage:
|
64
|
+
#{$0} [global options] init
|
65
|
+
|
66
|
+
Options:
|
67
|
+
TEXT
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module RXCode
|
2
|
+
module Commands
|
3
|
+
|
4
|
+
class Unwrap < ::RXCode::Command
|
5
|
+
|
6
|
+
# ===== COMMAND LINE OPTIONS =======================================================================================
|
7
|
+
|
8
|
+
def self.new_command_option_parser
|
9
|
+
Trollop::Parser.new do
|
10
|
+
banner <<-TEXT
|
11
|
+
Unwraps archive files, in particular the project.pbxproj file, and prints their contents to standard out as a ruby hash.
|
12
|
+
|
13
|
+
Usage:
|
14
|
+
rxcode [global options] unwrap [options] FILENAME...
|
15
|
+
|
16
|
+
Options:
|
17
|
+
TEXT
|
18
|
+
|
19
|
+
opt :raw, "Prints the raw archive structure instead of the object structure"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# ===== OBJECT UNWRAPPING ==========================================================================================
|
24
|
+
|
25
|
+
def self.unwrap_object_with_id(objects, archive, object_id)
|
26
|
+
if objects.has_key?(object_id)
|
27
|
+
|
28
|
+
objects[object_id]
|
29
|
+
|
30
|
+
elsif object_hash = archive.object_hashes[object_id]
|
31
|
+
|
32
|
+
unwrapped_object = {}
|
33
|
+
objects[object_id] = unwrapped_object
|
34
|
+
|
35
|
+
object_hash.each do |object_key, object_value|
|
36
|
+
unwrapped_object[object_key] = unwrap_object_value(objects, archive, object_value)
|
37
|
+
end
|
38
|
+
|
39
|
+
unwrapped_object
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.unwrap_object_value(objects, archive, object_value)
|
44
|
+
if object_value.is_a?(String) && archive.object_hashes.has_key?(object_value)
|
45
|
+
unwrap_object_with_id(objects, archive, object_value)
|
46
|
+
elsif object_value.is_a?(Array) && object_value.all? { |array_member| archive.object_hashes.has_key?(array_member) }
|
47
|
+
object_value.map { |array_member| unwrap_object_with_id(objects, archive, array_member) }
|
48
|
+
else
|
49
|
+
object_value
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def unwrap_objects?
|
54
|
+
!options[:raw]
|
55
|
+
end
|
56
|
+
|
57
|
+
# ===== RUN! =======================================================================================================
|
58
|
+
|
59
|
+
def run!
|
60
|
+
require 'pp'
|
61
|
+
|
62
|
+
arguments.each do |filename|
|
63
|
+
archive = ::RXCode::Archive.new(filename)
|
64
|
+
if unwrap_objects?
|
65
|
+
unwrapped_dictionary = self.class.unwrap_object_with_id({}, archive, archive.root_object_archive_id)
|
66
|
+
PP.pp(unwrapped_dictionary, output)
|
67
|
+
else
|
68
|
+
PP.pp(archive.archive_hash, output)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|