rxcode 0.0.5

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.
@@ -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