xcoder 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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