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
@@ -0,0 +1,80 @@
|
|
1
|
+
module RXCode
|
2
|
+
|
3
|
+
#
|
4
|
+
# Represents an XCode environment rooted at a particular path in a filesystem -- typically a project/workspace root
|
5
|
+
# directory.
|
6
|
+
#
|
7
|
+
class Environment
|
8
|
+
|
9
|
+
def initialize(root)
|
10
|
+
@root = root
|
11
|
+
@preferences = Preferences.new
|
12
|
+
end
|
13
|
+
|
14
|
+
# ===== ROOT =======================================================================================================
|
15
|
+
|
16
|
+
attr_reader :root
|
17
|
+
|
18
|
+
def name
|
19
|
+
File.basename(root)
|
20
|
+
end
|
21
|
+
|
22
|
+
# ===== PREFERENCES ================================================================================================
|
23
|
+
|
24
|
+
attr_reader :preferences
|
25
|
+
|
26
|
+
# ===== DERIVED DATA ===============================================================================================
|
27
|
+
|
28
|
+
def global_derived_data_location
|
29
|
+
self.preferences.derived_data_location unless preferences.derived_data_location_is_relative_to_workspace?
|
30
|
+
end
|
31
|
+
|
32
|
+
# ===== WORKSPACE ==================================================================================================
|
33
|
+
|
34
|
+
def workspace
|
35
|
+
@workspace ||=
|
36
|
+
if ws_path = self.workspace_path
|
37
|
+
Workspace.new(ws_path)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def workspace_path
|
42
|
+
workspace_paths = Dir[File.join(self.root, '*.xcworkspace')]
|
43
|
+
preferred_workspace_path = File.join(root, "#{name}.xcworkspace")
|
44
|
+
|
45
|
+
if workspace_paths.include?(preferred_workspace_path)
|
46
|
+
|
47
|
+
preferred_workspace_path
|
48
|
+
|
49
|
+
elsif workspace_paths.length == 1
|
50
|
+
|
51
|
+
workspace_paths.first
|
52
|
+
|
53
|
+
else
|
54
|
+
project_paths = Dir[File.join(self.root, '*.xcodeproj')]
|
55
|
+
preferred_project_path = File.join(root, "#{name}.xcodeproj")
|
56
|
+
|
57
|
+
project_path =
|
58
|
+
if project_paths.include?(preferred_project_path)
|
59
|
+
preferred_project_path
|
60
|
+
elsif project_paths.length == 1
|
61
|
+
project_paths.first
|
62
|
+
end
|
63
|
+
|
64
|
+
if project_path
|
65
|
+
project_workspace_path = File.join(project_path, 'project.xcworkspace')
|
66
|
+
if RXCode::Workspace.is_workspace_at_path?(project_workspace_path)
|
67
|
+
project_workspace_path
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.env
|
77
|
+
@env ||= Environment.new(Dir.pwd)
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module RXCode
|
2
|
+
|
3
|
+
def self.framework(framework_name)
|
4
|
+
if workspace = RXCode.env.workspace
|
5
|
+
|
6
|
+
framework_path = File.join(workspace.built_products_dir, 'Debug', "#{framework_name}.framework")
|
7
|
+
::Kernel.framework(framework_path)
|
8
|
+
|
9
|
+
else
|
10
|
+
|
11
|
+
raise "Unable to determine current XCode workspace."
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'rxcode/models/archive'
|
2
|
+
require 'rxcode/models/archived_object'
|
3
|
+
require 'rxcode/models/model'
|
4
|
+
require 'rxcode/models/project'
|
5
|
+
require 'rxcode/models/target'
|
6
|
+
require 'rxcode/models/file_reference'
|
7
|
+
require 'rxcode/models/build_configuration'
|
8
|
+
require 'rxcode/models/build_configuration_list'
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module RXCode
|
2
|
+
|
3
|
+
#
|
4
|
+
# Processes an NSKeyedArchive file into a set of Ruby objects
|
5
|
+
#
|
6
|
+
class Archive
|
7
|
+
|
8
|
+
def initialize(archive_path_or_hash, &object_mapper)
|
9
|
+
if archive_path_or_hash.is_a?(String)
|
10
|
+
|
11
|
+
if File.exist?(archive_path_or_hash)
|
12
|
+
@archive_path = archive_path_or_hash
|
13
|
+
@archive_hash = Plist::parse_xml(`plutil -convert xml1 -o - '#{archive_path_or_hash}'`)
|
14
|
+
else
|
15
|
+
@archive_hash = Plist::parse_xml(archive_path_or_hash)
|
16
|
+
end
|
17
|
+
|
18
|
+
elsif archive_path_or_hash.is_a?(Hash)
|
19
|
+
|
20
|
+
@archive_hash = archive_path_or_hash
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
@objects = {}
|
25
|
+
@object_mapper = object_mapper
|
26
|
+
end
|
27
|
+
|
28
|
+
# ===== ARCHIVE PATH ===============================================================================================
|
29
|
+
|
30
|
+
attr_reader :archive_path
|
31
|
+
|
32
|
+
attr_reader :archive_hash
|
33
|
+
|
34
|
+
attr_accessor :object_mapper
|
35
|
+
|
36
|
+
# ===== ROOT OBJECT ================================================================================================
|
37
|
+
|
38
|
+
def root_object_archive_id
|
39
|
+
@archive_hash['rootObject']
|
40
|
+
end
|
41
|
+
|
42
|
+
def root_object
|
43
|
+
object_with_id(root_object_archive_id)
|
44
|
+
end
|
45
|
+
|
46
|
+
# ===== OBJECTS ====================================================================================================
|
47
|
+
|
48
|
+
def object_with_id(object_id)
|
49
|
+
@objects[object_id] ||=
|
50
|
+
if object_hashes[object_id]
|
51
|
+
ArchivedObject.new(self, object_id)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def model_object_with_id(object_id)
|
56
|
+
if o = object_with_id(object_id)
|
57
|
+
o.model_object
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def object_hashes
|
62
|
+
archive_hash['objects']
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.archive_at_path(archive_path)
|
66
|
+
self.new(archive_path)
|
67
|
+
end
|
68
|
+
|
69
|
+
#
|
70
|
+
# Returns the object archived in the file identified by +archive_path+.
|
71
|
+
#
|
72
|
+
def self.object_at_path(archive_path)
|
73
|
+
archive_at_path(archive_path).root_object
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module RXCode
|
2
|
+
|
3
|
+
class ArchivedObject
|
4
|
+
|
5
|
+
def initialize(archive, archived_object_id)
|
6
|
+
@archive = archive
|
7
|
+
@archived_object_id = archived_object_id
|
8
|
+
end
|
9
|
+
|
10
|
+
# ===== ARCHIVE ====================================================================================================
|
11
|
+
|
12
|
+
attr_reader :archive
|
13
|
+
|
14
|
+
# ===== ARCHIVE OBJECT ID ==========================================================================================
|
15
|
+
|
16
|
+
attr_reader :archived_object_id
|
17
|
+
|
18
|
+
# ===== DATA =======================================================================================================
|
19
|
+
|
20
|
+
def data
|
21
|
+
@data ||= archive.object_hashes[self.archived_object_id]
|
22
|
+
end
|
23
|
+
|
24
|
+
def [](key)
|
25
|
+
data[key]
|
26
|
+
end
|
27
|
+
|
28
|
+
def has_key?(key)
|
29
|
+
data.has_key?(key)
|
30
|
+
end
|
31
|
+
|
32
|
+
def object_for_key(key)
|
33
|
+
object_id = self[key]
|
34
|
+
archive.object_with_id(object_id)
|
35
|
+
end
|
36
|
+
|
37
|
+
def array_of_objects_for_key(key)
|
38
|
+
self[key].map { |object_id| archive.object_with_id(object_id) }
|
39
|
+
end
|
40
|
+
|
41
|
+
def model_object_for_key(key)
|
42
|
+
if o = object_for_key(key)
|
43
|
+
o.model_object
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def array_of_model_objects_for_key(key)
|
48
|
+
array_of_objects_for_key(key).map { |o| o.model_object }
|
49
|
+
end
|
50
|
+
|
51
|
+
attr_accessor :model_object
|
52
|
+
|
53
|
+
def model_object
|
54
|
+
@model_object ||=
|
55
|
+
if archive.object_mapper
|
56
|
+
archive.object_mapper.call(self)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module RXCode
|
2
|
+
|
3
|
+
class BuildConfiguration < Model
|
4
|
+
|
5
|
+
# ===== NAME =======================================================================================================
|
6
|
+
|
7
|
+
def name
|
8
|
+
archive_object['name']
|
9
|
+
end
|
10
|
+
|
11
|
+
# ===== BUILD SETTINGS =============================================================================================
|
12
|
+
|
13
|
+
def build_settings
|
14
|
+
archive_object['buildSettings']
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module RXCode
|
2
|
+
|
3
|
+
class BuildConfigurationList < Model
|
4
|
+
|
5
|
+
def default_configuration_name
|
6
|
+
archive_object['defaultConfigurationName']
|
7
|
+
end
|
8
|
+
|
9
|
+
def build_configurations
|
10
|
+
archive_object.array_of_model_objects_for_key('buildConfigurations')
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module RXCode
|
2
|
+
|
3
|
+
class FileReference < Model
|
4
|
+
|
5
|
+
def name
|
6
|
+
archive_object['name']
|
7
|
+
end
|
8
|
+
|
9
|
+
def path
|
10
|
+
archive_object['path']
|
11
|
+
end
|
12
|
+
|
13
|
+
def source_tree
|
14
|
+
archive_object['sourceTree']
|
15
|
+
end
|
16
|
+
|
17
|
+
def last_known_file_type
|
18
|
+
archive_object['lastKnownFileType']
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module RXCode
|
2
|
+
|
3
|
+
class Model
|
4
|
+
|
5
|
+
def initialize(source)
|
6
|
+
case source
|
7
|
+
when ArchivedObject, NilClass
|
8
|
+
@archive_object = source
|
9
|
+
when Archive
|
10
|
+
@archive_object = source.root_object
|
11
|
+
when String, Hash
|
12
|
+
archive = Archive.new(source)
|
13
|
+
@archive_object = archive.rootObject
|
14
|
+
else
|
15
|
+
raise "Unable to initialize #{self.class.name} from #{source.class.name}: #{source.inspect}"
|
16
|
+
end
|
17
|
+
|
18
|
+
if @archive_object
|
19
|
+
@archive_object.model_object = self
|
20
|
+
@archive_object.archive.object_mapper ||=
|
21
|
+
Proc.new { |archived_object| self.class.map_archived_object(archived_object) }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# ===== ARCHIVE ====================================================================================================
|
26
|
+
|
27
|
+
attr_reader :archive_object
|
28
|
+
|
29
|
+
def archive
|
30
|
+
archive_object.archive
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.class_for_archived_object_type(type)
|
34
|
+
case type
|
35
|
+
when "PBXProject"
|
36
|
+
Project
|
37
|
+
when "PBXFileReference"
|
38
|
+
FileReference
|
39
|
+
when "PBXNativeTarget"
|
40
|
+
Target
|
41
|
+
when "XCConfigurationList"
|
42
|
+
BuildConfigurationList
|
43
|
+
when "XCBuildConfiguration"
|
44
|
+
BuildConfiguration
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.map_archived_object(archived_object)
|
49
|
+
if klass = class_for_archived_object_type(archived_object['isa'])
|
50
|
+
klass.new(archived_object)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.object_at_path(archive_path)
|
55
|
+
Archive.new(archive_path).rootObject.model_object
|
56
|
+
end
|
57
|
+
|
58
|
+
# ===== ROOT =======================================================================================================
|
59
|
+
|
60
|
+
def root
|
61
|
+
self.archive.root_object.model_object
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module RXCode
|
2
|
+
|
3
|
+
class Project < Model
|
4
|
+
|
5
|
+
def initialize(path, options = nil)
|
6
|
+
raise "#{path.inspect} is not a valid XCode project path" unless self.class.is_project_at_path?(path)
|
7
|
+
|
8
|
+
super nil
|
9
|
+
|
10
|
+
@path = path
|
11
|
+
|
12
|
+
if options && options[:workspace]
|
13
|
+
@workspace = options[:workspace]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# ===== WORKSPACE ==================================================================================================
|
18
|
+
|
19
|
+
attr_reader :workspace
|
20
|
+
|
21
|
+
# ===== PROJECT DISCOVERY & LOOKUP =================================================================================
|
22
|
+
|
23
|
+
XCODE_PROJECT_EXTENSION = '.xcodeproj'.freeze
|
24
|
+
|
25
|
+
#
|
26
|
+
# Determines if the provided file path is an XCode project
|
27
|
+
#
|
28
|
+
def self.is_project_at_path?(project_path)
|
29
|
+
File.directory?(project_path) && File.extname(project_path) == XCODE_PROJECT_EXTENSION
|
30
|
+
end
|
31
|
+
|
32
|
+
# ===== PROJECT PATH ===============================================================================================
|
33
|
+
|
34
|
+
attr_reader :path
|
35
|
+
|
36
|
+
def project_archive_path
|
37
|
+
File.join(self.path, 'project.pbxproj')
|
38
|
+
end
|
39
|
+
|
40
|
+
def archive
|
41
|
+
@archive ||=
|
42
|
+
begin
|
43
|
+
a = Archive.new(project_archive_path) { |archived_object| Model.map_archived_object(archived_object) }
|
44
|
+
a.root_object.model_object = self
|
45
|
+
a
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def archive_object
|
50
|
+
archive.root_object
|
51
|
+
end
|
52
|
+
|
53
|
+
# ===== TARGETS ====================================================================================================
|
54
|
+
|
55
|
+
def targets
|
56
|
+
archive_object.array_of_model_objects_for_key('targets')
|
57
|
+
end
|
58
|
+
|
59
|
+
def target_names
|
60
|
+
targets.map(&:name)
|
61
|
+
end
|
62
|
+
|
63
|
+
# ===== BUILD CONFIGURATIONS =======================================================================================
|
64
|
+
|
65
|
+
def build_configuration_list
|
66
|
+
archive_object.model_object_for_key('buildConfigurationList')
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|