xcoder 0.1.1 → 0.1.2

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,77 @@
1
+ module Xcode
2
+
3
+ #
4
+ # Within the project file the PBXFileReference represents the object
5
+ # representation to the file on the file sytem. This is usually your source
6
+ # files within your project.
7
+ #
8
+ module FileReference
9
+
10
+ # This is the group for which this file is contained within.
11
+ attr_accessor :supergroup
12
+
13
+ def self.file(properties)
14
+ default_properties = { 'isa' => 'PBXFileReference',
15
+ 'path' => nil,
16
+ 'sourceTree' => '<group>' }
17
+
18
+ default_properties.merge(properties)
19
+ end
20
+
21
+ #
22
+ # Generate the properties for a framework. A name and a path option need
23
+ # to be specified in the properties
24
+ #
25
+ # @note a 'name' and 'path' key need to be specified in the framework for
26
+ # the framework to be added correctly.
27
+ #
28
+ # @param [Hash] properties to override for the Framework
29
+ # @return [Hash] properties for a framework
30
+ #
31
+ def self.framework(properties)
32
+ default_properties = { 'isa' => "PBXFileReference",
33
+ 'lastKnownFileType' => "wrapper.framework",
34
+ 'name' => "FRAMEWORK.framework",
35
+ 'path' => "FRAMEWORK.framework",
36
+ 'sourceTree' => "<group>" }
37
+
38
+ default_properties.merge(properties)
39
+ end
40
+
41
+ #
42
+ # Generate the properties for a system framework.
43
+ #
44
+ # @param [String] name of the system framework
45
+ # @param [Hash] properties the parameters to override for the system framework
46
+ #
47
+ def self.system_framework(name,properties = {})
48
+ default_properties = { 'isa' => 'PBXFileReference',
49
+ 'lastKnownFileType' => 'wrapper.framework',
50
+ 'name' => "#{name}.framework",
51
+ 'path' => "System/Library/Frameworks/#{name}.framework",
52
+ "sourceTree" => "SDKROOT" }
53
+
54
+ default_properties.merge(properties)
55
+ end
56
+
57
+ #
58
+ # @example app product properties
59
+ #
60
+ # E21D8AAA14E0F817002E56AA /* newtarget.app */ = {
61
+ # isa = PBXFileReference;
62
+ # explicitFileType = wrapper.application;
63
+ # includeInIndex = 0;
64
+ # path = newtarget.app;
65
+ # sourceTree = BUILT_PRODUCTS_DIR; };
66
+ #
67
+ def self.app_product(name)
68
+ { 'isa' => 'PBXFileReference',
69
+ 'explicitFileType' => 'wrapper.application',
70
+ 'includeInIndex' => 0,
71
+ 'path' => "#{name}.app",
72
+ 'sourceTree' => "BUILT_PRODUCTS_DIR" }
73
+ end
74
+
75
+ end
76
+
77
+ end
@@ -0,0 +1,197 @@
1
+ module Xcode
2
+
3
+ #
4
+ # Within the project file there are logical separation of resources into groups
5
+ # these groups may contain subgroups, files, or other objects. They have
6
+ # children.
7
+ #
8
+ # Group here provides the methods to traverse the groups to find these
9
+ # children resources as well as provide the ability to generate child
10
+ # resources.
11
+ #
12
+ # 7165D451146B4EA100DE2F0E /* Products */ = {
13
+ # isa = PBXGroup;
14
+ # children = (
15
+ # 7165D450146B4EA100DE2F0E /* TestProject.app */,
16
+ # 7165D46B146B4EA100DE2F0E /* TestProjectTests.octest */,
17
+ # E21EB9D614E357CF0058122A /* Specs.app */,
18
+ # );
19
+ # name = Products;
20
+ # sourceTree = "<group>";
21
+ # };
22
+ #
23
+ module Group
24
+
25
+ #
26
+ # Return the hash that maps to the properties for a logical group
27
+ #
28
+ # @param [String] name of the logical group
29
+ #
30
+ def self.logical_group(name)
31
+ { 'isa' => 'PBXGroup',
32
+ 'name' => name,
33
+ 'sourceTree' => '<group>',
34
+ 'children' => [] }
35
+ end
36
+
37
+
38
+ # This is the group for which this file is contained within.
39
+ # @note this value is only set if the group has been discovered
40
+ # by traversing groups to this group.
41
+ attr_accessor :supergroup
42
+
43
+ #
44
+ # @example Return all the sub-groups of the main group
45
+ #
46
+ # main_group = Xcode.project('MyProject.xcodeproj').groups
47
+ # main_group.groups
48
+ #
49
+ # @return [Array] the sub-groups contained within this group.
50
+ #
51
+ def groups
52
+ children.find_all {|child| child.is_a?(Group) }.map do |group|
53
+ group.supergroup = self
54
+ group
55
+ end
56
+ end
57
+
58
+ #
59
+ # Find all the child groups that have a name that matches the specified name.
60
+ #
61
+ # @param [String] name of the group that you are looking to return.
62
+ # @return [Array<Group>] the groups with the same matching name. This
63
+ # could be no groups, one group, or multiple groups.
64
+ #
65
+ def group(name)
66
+ groups.find_all {|group| group.name == name or group.path == name }
67
+ end
68
+
69
+ #
70
+ # Find all the non-group objects within the group and return them
71
+ # @return [Array] the children of the group, excluding the groups
72
+ #
73
+ def files
74
+ children.reject {|child| child.is_a?(Group) }
75
+ end
76
+
77
+ #
78
+ # Find all the files that have have a name that matches the specified name.
79
+ #
80
+ # @param [String] name of the file that you are looking to return.
81
+ # @return [Array<FileReference>] the files with the same mathching
82
+ # name. This could be no files, one file, or multiple files.
83
+ #
84
+ def file(name)
85
+ files.find_all {|file| file.name == name or file.path == name }
86
+ end
87
+
88
+ #
89
+ # Check whether a file or group is contained within this group.
90
+ #
91
+ def exists?(name)
92
+ children.find_all {|child| child.name == name }
93
+ end
94
+ #
95
+ # Adds a group as a child to current group with the given name.
96
+ #
97
+ # @note A group may be added that has the same name as another group as they
98
+ # are distinguished by a unique identifier and not by name.
99
+ #
100
+ # @param [String] name of the group that you want to add as a child group of
101
+ # the specified group.
102
+ #
103
+ def create_group(name)
104
+ new_group = create_child_object Group.logical_group(name)
105
+ new_group.supergroup = self
106
+ new_group
107
+ end
108
+
109
+ #
110
+ # Add a file to the specified group. Currently the file creation requires
111
+ # the path to the physical file.
112
+ #
113
+ # @param [String,Hash] path to the file that is being added or a hash that
114
+ # contains the values would be merged with the default values.
115
+ #
116
+ def create_file(file_properties)
117
+ # This allows both support for the string value or the hash as the parameter
118
+ file_properties = { 'path' => file_properties } if file_properties.is_a? String
119
+
120
+ # IF the file already exists then we will not create the file with the
121
+ # parameters that are being supplied, instead we will return what we
122
+ # found.
123
+
124
+ find_file_by = file_properties['name'] || file_properties['path']
125
+ found_or_created_file = exists?(find_file_by).first
126
+
127
+ unless found_or_created_file
128
+ found_or_created_file = create_child_object FileReference.file(file_properties)
129
+ end
130
+
131
+ found_or_created_file
132
+ end
133
+
134
+ #
135
+ # Create a framework within this group.
136
+ #
137
+ # @param [Hash] framework_properties the properties to merge with the default
138
+ # properties.
139
+ #
140
+ def create_framework(framework_properties)
141
+ create_child_object FileReference.framework(framework_properties)
142
+ end
143
+
144
+ #
145
+ # Create a system framework reference within this group
146
+ #
147
+ # @param [String] name the name of the System framework to add to this group.
148
+ #
149
+ def create_system_framework(name)
150
+ create_child_object FileReference.system_framework(name)
151
+ end
152
+
153
+ #
154
+ # Create an infoplist within this group.
155
+ #
156
+ # @param [Hash] infoplist_properties the properties to merge with the default
157
+ # properties.
158
+ #
159
+ # @see VariantGroup#info_plist
160
+ #
161
+ def create_infoplist(infoplist_properties)
162
+ create_child_object VariantGroup.info_plist(infoplist_properties)
163
+ end
164
+
165
+ #
166
+ # Create a product reference witin this group.
167
+ #
168
+ # @note this is usually performed through the target as it is necessary within
169
+ # the target to specify what is the product reference.
170
+ #
171
+ # @see Target#create_product_reference
172
+ #
173
+ # @param [String] name the name of the product to generate
174
+ #
175
+ def create_product_reference(name)
176
+ create_child_object FileReference.app_product(name)
177
+ end
178
+
179
+ private
180
+
181
+ #
182
+ # This method is used internally to add objects to the registry and add the
183
+ # object as a child of this group.
184
+ #
185
+ # @param [Hash] child_as_properties the hash of resource to add as a child
186
+ # object of this group.
187
+ #
188
+ # @return [Resource] returns the resource that was added a child
189
+ def create_child_object(child_properties)
190
+ child_object = @registry.add_object child_properties
191
+ @properties['children'] << child_object.identifier
192
+ child_object
193
+ end
194
+
195
+ end
196
+
197
+ end
@@ -0,0 +1,41 @@
1
+ require 'plist'
2
+ require 'pp'
3
+
4
+ module Xcode
5
+
6
+ #
7
+ # @see https://developer.apple.com/library/ios/#documentation/general/Reference/InfoPlistKeyReference/Articles/AboutInformationPropertyListFiles.html
8
+ #
9
+ class InfoPlist
10
+ def initialize(config, plist_location)
11
+ @config = config
12
+
13
+ @plist_location = File.expand_path("#{File.dirname(@config.target.project.path)}/#{plist_location}")
14
+ unless File.exists?(@plist_location)
15
+ puts 'Plist not found ' + @plist_location
16
+ exit 1
17
+ end
18
+ @plist = Plist::parse_xml(@plist_location)
19
+ end
20
+
21
+ def marketing_version
22
+ @plist['CFBundleShortVersionString']
23
+ end
24
+
25
+ def marketing_version=(version)
26
+ @plist['CFBundleShortVersionString'] = version
27
+ end
28
+
29
+ def version
30
+ @plist['CFBundleVersion']
31
+ end
32
+
33
+ def version=(version)
34
+ @plist['CFBundleVersion'] = version.to_s
35
+ end
36
+
37
+ def save
38
+ File.open(@plist_location, 'w') {|f| f << @plist.to_plist}
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,122 @@
1
+ module Xcode
2
+ class Keychain
3
+ attr_accessor :name, :path
4
+
5
+ TEMP_PASSWORD = "build_keychain_password"
6
+
7
+ #
8
+ # Open the keychain with the specified name. It is assumed that keychains reside in the
9
+ # ~/Library/Keychains directory
10
+ #
11
+ # @param [String] the name of the keychain
12
+ #
13
+ def initialize(name)
14
+ @name = name
15
+ @path = File.expand_path "~/Library/Keychains/#{name}"
16
+ end
17
+
18
+ #
19
+ # Import the .p12 certificate file into the keychain using the provided password
20
+ #
21
+ # @param [String] the path to the .p12 certificate file
22
+ # @param [String] the password to open the certificate file
23
+ #
24
+ def import(cert, password)
25
+ cmd = []
26
+ cmd << "security"
27
+ cmd << "import '#{cert}'"
28
+ cmd << "-k '#{@path}'"
29
+ cmd << "-P #{password}"
30
+ cmd << "-T /usr/bin/codesign"
31
+ Xcode::Shell.execute(cmd)
32
+ end
33
+
34
+ #
35
+ # Returns a list of identities in the keychain.
36
+ #
37
+ # @return [Array<String>] a list of identity names
38
+ #
39
+ def identities
40
+ names = []
41
+ cmd = []
42
+ cmd << "security"
43
+ cmd << "find-certificate"
44
+ cmd << "-a"
45
+ cmd << "#{@name}"
46
+ data = Xcode::Shell.execute(cmd, false).join("")
47
+ data.scan /\s+"labl"<blob>="([^"]+)"/ do |m|
48
+ names << m[0]
49
+ end
50
+ names
51
+ end
52
+
53
+ #
54
+ # Unlock the keychain using the provided password
55
+ #
56
+ # @param [String] the password to open the keychain
57
+ #
58
+ def unlock(password)
59
+ cmd = []
60
+ cmd << "security"
61
+ cmd << "unlock-keychain"
62
+ cmd << "-p #{password}"
63
+ cmd << "#{@name}"
64
+ Xcode::Shell.execute(cmd)
65
+ end
66
+
67
+ #
68
+ # Create a new keychain with the given name and password
69
+ #
70
+ # @param [String] the name for the new keychain
71
+ # @param [String] the password for the new keychain
72
+ # @return [Xcode::Keychain] an object representing the new keychain
73
+ #
74
+ def self.create(name, password)
75
+ cmd = []
76
+ cmd << "security"
77
+ cmd << "create-keychain"
78
+ cmd << "-p #{password}"
79
+ cmd << "#{name}"
80
+ Xcode::Shell.execute(cmd)
81
+
82
+ Xcode::Keychain.new(name)
83
+ end
84
+
85
+ #
86
+ # Remove the keychain from the filesystem
87
+ #
88
+ # FIXME: dangerous
89
+ #
90
+ def delete
91
+ cmd = []
92
+ cmd << "security"
93
+ cmd << "delete-keychain #{name}"
94
+ Xcode::Shell.execute(cmd)
95
+ end
96
+
97
+ #
98
+ # Creates a keychain with the given name that lasts for the duration of the provided block.
99
+ # The keychain is deleted even if the block throws an exception.
100
+ #
101
+ # @param [String] the name of the temporary keychain to create
102
+ #
103
+ def self.temp_keychain(name, &block)
104
+ kc = Xcode::Keychain.create(name, TEMP_PASSWORD)
105
+ begin
106
+ kc.unlock(TEMP_PASSWORD)
107
+ block.call(kc)
108
+ ensure
109
+ kc.delete
110
+ end
111
+ end
112
+
113
+ #
114
+ # Opens the default login.keychain for current user
115
+ #
116
+ # @return [Xcode::Keychain] the current user's login keychain
117
+ #
118
+ def self.login_keychain
119
+ Xcode::Keychain.new("login.keychain")
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,20 @@
1
+ require 'json'
2
+ require 'plist'
3
+
4
+ module Xcode
5
+
6
+ module PLUTILProjectParser
7
+ extend self
8
+
9
+ #
10
+ # Using the sytem tool plutil, the specified project file is parsed and
11
+ # converted to XML, and then converted into a ruby hash object.
12
+ #
13
+ def parse path
14
+ xml = `plutil -convert xml1 -o - "#{path}"`
15
+ Plist::parse_xml(xml)
16
+ end
17
+
18
+ end
19
+
20
+ end