xcoder 0.1.13 → 0.1.14
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.
- data/README.md +37 -33
- data/lib/xcode/builder.rb +3 -234
- data/lib/xcode/builder/base_builder.rb +204 -0
- data/lib/xcode/builder/project_target_config_builder.rb +14 -0
- data/lib/xcode/builder/scheme_builder.rb +32 -0
- data/lib/xcode/configuration.rb +1 -1
- data/lib/xcode/keychain.rb +14 -21
- data/lib/xcode/project.rb +109 -109
- data/lib/xcode/registry.rb +1 -1
- data/lib/xcode/scheme.rb +82 -35
- data/lib/xcode/shell.rb +3 -1
- data/lib/xcode/shell/command.rb +42 -0
- data/lib/xcode/version.rb +1 -1
- data/lib/xcode/workspace.rb +2 -1
- data/spec/builder_spec.rb +68 -62
- data/spec/registry_spec.rb +51 -0
- data/spec/scheme_spec.rb +5 -5
- data/spec/xcode_spec.rb +1 -0
- metadata +18 -12
@@ -0,0 +1,204 @@
|
|
1
|
+
module Xcode
|
2
|
+
module Builder
|
3
|
+
#
|
4
|
+
# This class tries to pull various bits of Xcoder together to provide a higher-level API for common
|
5
|
+
# project build tasks.
|
6
|
+
#
|
7
|
+
class BaseBuilder
|
8
|
+
attr_accessor :profile, :identity, :build_path, :keychain, :sdk, :objroot, :symroot
|
9
|
+
attr_reader :config, :target
|
10
|
+
|
11
|
+
def initialize(target, config)
|
12
|
+
@target = target
|
13
|
+
@config = config
|
14
|
+
|
15
|
+
@sdk = @target.project.sdk
|
16
|
+
@build_path = "#{File.dirname(@target.project.path)}/build/"
|
17
|
+
@objroot = @build_path
|
18
|
+
@symroot = @build_path
|
19
|
+
end
|
20
|
+
|
21
|
+
def common_environment
|
22
|
+
env = {}
|
23
|
+
env["OBJROOT"] = "\"#{@objroot}\""
|
24
|
+
env["SYMROOT"] = "\"#{@symroot}\""
|
25
|
+
env
|
26
|
+
end
|
27
|
+
|
28
|
+
def build_environment
|
29
|
+
profile = install_profile
|
30
|
+
env = common_environment
|
31
|
+
env["OTHER_CODE_SIGN_FLAGS"] = "'--keychain #{@keychain.path}'" unless @keychain.nil?
|
32
|
+
env["CODE_SIGN_IDENTITY"] = "\"#{@identity}\"" unless @identity.nil?
|
33
|
+
env["PROVISIONING_PROFILE"] = "#{profile.uuid}" unless profile.nil?
|
34
|
+
env
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
def build(options = {:sdk => @sdk})
|
39
|
+
cmd = xcodebuild
|
40
|
+
cmd << "-sdk #{options[:sdk]}" unless options[:sdk].nil?
|
41
|
+
|
42
|
+
with_keychain do
|
43
|
+
cmd.execute
|
44
|
+
end
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# Invoke the configuration's test target and parse the resulting output
|
50
|
+
#
|
51
|
+
# If a block is provided, the report is yielded for configuration before the test is run
|
52
|
+
#
|
53
|
+
def test(options = {:sdk => 'iphonesimulator'}) #, :parser => :OCUnit })
|
54
|
+
cmd = xcodebuild
|
55
|
+
cmd << "-sdk #{options[:sdk]}" unless options[:sdk].nil?
|
56
|
+
cmd.env["TEST_AFTER_BUILD"]="YES"
|
57
|
+
|
58
|
+
report = Xcode::Test::Report.new
|
59
|
+
if block_given?
|
60
|
+
yield(report)
|
61
|
+
else
|
62
|
+
report.add_formatter :stdout
|
63
|
+
report.add_formatter :junit, 'test-reports'
|
64
|
+
end
|
65
|
+
|
66
|
+
parser = Xcode::Test::Parsers::OCUnitParser.new report
|
67
|
+
|
68
|
+
begin
|
69
|
+
cmd.execute(false) do |line|
|
70
|
+
parser << line
|
71
|
+
end
|
72
|
+
rescue Xcode::Shell::ExecutionError => e
|
73
|
+
puts "Test platform exited: #{e.message}"
|
74
|
+
ensure
|
75
|
+
parser.flush
|
76
|
+
end
|
77
|
+
|
78
|
+
report
|
79
|
+
end
|
80
|
+
|
81
|
+
def testflight(api_token, team_token)
|
82
|
+
raise "Can't find #{ipa_path}, do you need to call builder.package?" unless File.exists? ipa_path
|
83
|
+
raise "Can't fins #{dsym_zip_path}, do you need to call builder.package?" unless File.exists? dsym_zip_path
|
84
|
+
|
85
|
+
testflight = Xcode::Testflight.new(api_token, team_token)
|
86
|
+
yield(testflight) if block_given?
|
87
|
+
testflight.upload(ipa_path, dsym_zip_path)
|
88
|
+
end
|
89
|
+
|
90
|
+
def clean
|
91
|
+
cmd = xcodebuild
|
92
|
+
cmd << "-sdk #{@sdk}" unless @sdk.nil?
|
93
|
+
cmd << "clean"
|
94
|
+
cmd.execute
|
95
|
+
|
96
|
+
@built = false
|
97
|
+
@packaged = false
|
98
|
+
self
|
99
|
+
end
|
100
|
+
|
101
|
+
def sign
|
102
|
+
cmd = Xcode::Shell::Command.new 'codesign'
|
103
|
+
cmd << "--force"
|
104
|
+
cmd << "--sign \"#{@identity}\""
|
105
|
+
cmd << "--resource-rules=\"#{app_path}/ResourceRules.plist\""
|
106
|
+
cmd << "--entitlements \"#{entitlements_path}\""
|
107
|
+
cmd << "\"#{ipa_path}\""
|
108
|
+
cmd.execute
|
109
|
+
|
110
|
+
|
111
|
+
self
|
112
|
+
end
|
113
|
+
|
114
|
+
def package
|
115
|
+
raise "Can't find #{app_path}, do you need to call builder.build?" unless File.exists? app_path
|
116
|
+
|
117
|
+
#package IPA
|
118
|
+
cmd = Xcode::Shell::Command.new 'xcrun'
|
119
|
+
cmd << "-sdk #{@sdk}" unless @sdk.nil?
|
120
|
+
cmd << "PackageApplication"
|
121
|
+
cmd << "-v \"#{app_path}\""
|
122
|
+
cmd << "-o \"#{ipa_path}\""
|
123
|
+
|
124
|
+
unless @profile.nil?
|
125
|
+
cmd << "--embed \"#{@profile}\""
|
126
|
+
end
|
127
|
+
|
128
|
+
with_keychain do
|
129
|
+
cmd.execute
|
130
|
+
end
|
131
|
+
|
132
|
+
# package dSYM
|
133
|
+
cmd = Xcode::Shell::Command.new 'zip'
|
134
|
+
cmd << "-r"
|
135
|
+
cmd << "-T"
|
136
|
+
cmd << "-y \"#{dsym_zip_path}\""
|
137
|
+
cmd << "\"#{dsym_path}\""
|
138
|
+
cmd.execute
|
139
|
+
|
140
|
+
self
|
141
|
+
end
|
142
|
+
|
143
|
+
def configuration_build_path
|
144
|
+
"#{build_path}/#{@config.name}-#{@sdk}"
|
145
|
+
end
|
146
|
+
|
147
|
+
def entitlements_path
|
148
|
+
"#{build_path}/#{@target.name}.build/#{name}-#{@target.project.sdk}/#{@target.name}.build/#{@config.product_name}.xcent"
|
149
|
+
end
|
150
|
+
|
151
|
+
def app_path
|
152
|
+
"#{configuration_build_path}/#{@config.product_name}.app"
|
153
|
+
end
|
154
|
+
|
155
|
+
def product_version_basename
|
156
|
+
version = @config.info_plist.version
|
157
|
+
version = "SNAPSHOT" if version.nil? or version==""
|
158
|
+
"#{configuration_build_path}/#{@config.product_name}-#{@config.name}-#{version}"
|
159
|
+
end
|
160
|
+
|
161
|
+
def ipa_path
|
162
|
+
"#{product_version_basename}.ipa"
|
163
|
+
end
|
164
|
+
|
165
|
+
def dsym_path
|
166
|
+
"#{app_path}.dSYM"
|
167
|
+
end
|
168
|
+
|
169
|
+
def dsym_zip_path
|
170
|
+
"#{product_version_basename}.dSYM.zip"
|
171
|
+
end
|
172
|
+
|
173
|
+
private
|
174
|
+
|
175
|
+
def with_keychain(&block)
|
176
|
+
if @keychain.nil?
|
177
|
+
yield
|
178
|
+
else
|
179
|
+
Xcode::Keychains.with_keychain_in_search_path @keychain, &block
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def install_profile
|
184
|
+
return nil if @profile.nil?
|
185
|
+
# TODO: remove other profiles for the same app?
|
186
|
+
p = ProvisioningProfile.new(@profile)
|
187
|
+
|
188
|
+
ProvisioningProfile.installed_profiles.each do |installed|
|
189
|
+
if installed.identifiers==p.identifiers and installed.uuid==p.uuid
|
190
|
+
installed.uninstall
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
p.install
|
195
|
+
p
|
196
|
+
end
|
197
|
+
|
198
|
+
def xcodebuild #:yield: Xcode::Shell::Command
|
199
|
+
Xcode::Shell::Command.new 'xcodebuild', build_environment
|
200
|
+
end
|
201
|
+
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Xcode
|
2
|
+
module Builder
|
3
|
+
class ProjectTargetConfigBuilder < BaseBuilder
|
4
|
+
|
5
|
+
def xcodebuild
|
6
|
+
cmd = super
|
7
|
+
cmd << "-project \"#{@target.project.path}\""
|
8
|
+
cmd << "-target \"#{@target.name}\""
|
9
|
+
cmd << "-config \"#{@config.name}\""
|
10
|
+
cmd
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Xcode
|
2
|
+
|
3
|
+
class Workspace
|
4
|
+
def to_xcodebuild_option
|
5
|
+
"-workspace \"#{self.path}\""
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class Project
|
10
|
+
def to_xcodebuild_option
|
11
|
+
"-project \"#{self.path}\""
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module Builder
|
16
|
+
class SchemeBuilder < BaseBuilder
|
17
|
+
|
18
|
+
def initialize(scheme)
|
19
|
+
@scheme = scheme
|
20
|
+
super @scheme.build_targets.last, @scheme.build_config
|
21
|
+
end
|
22
|
+
|
23
|
+
def xcodebuild
|
24
|
+
cmd = super
|
25
|
+
cmd << @scheme.parent.to_xcodebuild_option
|
26
|
+
cmd << "-scheme \"#{@scheme.name}\""
|
27
|
+
cmd
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/xcode/configuration.rb
CHANGED
data/lib/xcode/keychain.rb
CHANGED
@@ -37,11 +37,10 @@ module Xcode
|
|
37
37
|
"\"#{kc.path}\""
|
38
38
|
end
|
39
39
|
|
40
|
-
cmd =
|
41
|
-
cmd << "security"
|
40
|
+
cmd = Xcode::Shell::Command.new "security"
|
42
41
|
cmd << "list-keychain"
|
43
42
|
cmd << "-s #{search_list.join(' ')}"
|
44
|
-
|
43
|
+
cmd.execute
|
45
44
|
end
|
46
45
|
end
|
47
46
|
|
@@ -70,13 +69,12 @@ module Xcode
|
|
70
69
|
# @param [String] the password to open the certificate file
|
71
70
|
#
|
72
71
|
def import(cert, password)
|
73
|
-
cmd =
|
74
|
-
cmd << "security"
|
72
|
+
cmd = Xcode::Shell::Command.new "security"
|
75
73
|
cmd << "import '#{cert}'"
|
76
74
|
cmd << "-k \"#{@path}\""
|
77
75
|
cmd << "-P #{password}"
|
78
76
|
cmd << "-T /usr/bin/codesign"
|
79
|
-
|
77
|
+
cmd.execute
|
80
78
|
end
|
81
79
|
|
82
80
|
#
|
@@ -86,12 +84,11 @@ module Xcode
|
|
86
84
|
#
|
87
85
|
def identities
|
88
86
|
names = []
|
89
|
-
cmd =
|
90
|
-
cmd << "security"
|
87
|
+
cmd = Xcode::Shell::Command.new "security"
|
91
88
|
cmd << "find-certificate"
|
92
89
|
cmd << "-a"
|
93
90
|
cmd << "\"#{@path}\""
|
94
|
-
data =
|
91
|
+
data = cmd.execute(false).join("")
|
95
92
|
data.scan /\s+"labl"<blob>="([^"]+)"/ do |m|
|
96
93
|
names << m[0]
|
97
94
|
end
|
@@ -102,11 +99,10 @@ module Xcode
|
|
102
99
|
# Secure the keychain
|
103
100
|
#
|
104
101
|
def lock
|
105
|
-
cmd =
|
106
|
-
cmd << "security"
|
102
|
+
cmd = Xcode::Shell::Command.new "security"
|
107
103
|
cmd << "lock-keychain"
|
108
104
|
cmd << "\"#{@path}\""
|
109
|
-
|
105
|
+
cmd.execute
|
110
106
|
end
|
111
107
|
|
112
108
|
#
|
@@ -115,12 +111,11 @@ module Xcode
|
|
115
111
|
# @param [String] the password to open the keychain
|
116
112
|
#
|
117
113
|
def unlock(password)
|
118
|
-
cmd =
|
119
|
-
cmd << "security"
|
114
|
+
cmd = Xcode::Shell::Command.new "security"
|
120
115
|
cmd << "unlock-keychain"
|
121
116
|
cmd << "-p #{password}"
|
122
117
|
cmd << "\"#{@path}\""
|
123
|
-
|
118
|
+
cmd.execute
|
124
119
|
end
|
125
120
|
|
126
121
|
#
|
@@ -131,12 +126,11 @@ module Xcode
|
|
131
126
|
# @return [Xcode::Keychain] an object representing the new keychain
|
132
127
|
#
|
133
128
|
def self.create(path, password)
|
134
|
-
cmd =
|
135
|
-
cmd << "security"
|
129
|
+
cmd = Xcode::Shell::Command.new "security"
|
136
130
|
cmd << "create-keychain"
|
137
131
|
cmd << "-p #{password}"
|
138
132
|
cmd << "\"#{path}\""
|
139
|
-
|
133
|
+
cmd.execute
|
140
134
|
|
141
135
|
kc = Xcode::Keychain.new(path)
|
142
136
|
yield(kc) if block_given?
|
@@ -149,10 +143,9 @@ module Xcode
|
|
149
143
|
# FIXME: dangerous
|
150
144
|
#
|
151
145
|
def delete
|
152
|
-
cmd =
|
153
|
-
cmd << "security"
|
146
|
+
cmd = Xcode::Shell::Command.new "security"
|
154
147
|
cmd << "delete-keychain \"#{@path}\""
|
155
|
-
|
148
|
+
cmd.execute
|
156
149
|
end
|
157
150
|
|
158
151
|
#
|
data/lib/xcode/project.rb
CHANGED
@@ -3,145 +3,145 @@ require 'xcode/resource'
|
|
3
3
|
require 'xcode/registry'
|
4
4
|
|
5
5
|
module Xcode
|
6
|
-
|
6
|
+
|
7
7
|
#
|
8
8
|
# The project is the representation of an Xcode Project folder, the `*.xcodeproj`,
|
9
9
|
# folder that contains a number of workspace files and project files like
|
10
|
-
# `project.pbxproj`.
|
11
|
-
#
|
12
|
-
#
|
10
|
+
# `project.pbxproj`.
|
11
|
+
#
|
12
|
+
#
|
13
13
|
# The Project represents encapsulates the an actual Resource that is ProjectReference
|
14
14
|
# and other objects.
|
15
|
-
#
|
16
|
-
class Project
|
17
|
-
|
18
|
-
# @return [String] the name of the project; This value is deteremined from the
|
19
|
-
# first part of the Xcode project folder name (e.g. "TestProject.xcodeproj"
|
15
|
+
#
|
16
|
+
class Project
|
17
|
+
|
18
|
+
# @return [String] the name of the project; This value is deteremined from the
|
19
|
+
# first part of the Xcode project folder name (e.g. "TestProject.xcodeproj"
|
20
20
|
# name is "TestProject")
|
21
21
|
attr_reader :name
|
22
|
-
|
22
|
+
|
23
23
|
# @return [String] the sdks for the project. This is specified during the project
|
24
24
|
# initialization. If none are provided this currently defaults to "iphoneos"
|
25
25
|
attr_reader :sdk
|
26
|
-
|
26
|
+
|
27
27
|
# @return [String] the expanded file path for the project. This is expanded from the
|
28
28
|
# file path specified during initialization.
|
29
29
|
attr_reader :path
|
30
|
-
|
30
|
+
|
31
31
|
# @return [Array<Scheme>] the schemes that are found within the project path.
|
32
32
|
attr_reader :schemes
|
33
|
-
|
33
|
+
|
34
34
|
# @return [Registry] the data that is parsed from the project.pbxproj, it is
|
35
35
|
# in most part a Hash with additional methods included to provide core
|
36
36
|
# functionality.
|
37
37
|
attr_reader :registry
|
38
|
-
|
39
|
-
# @return [ProjectReference] the project object that is contained in the
|
38
|
+
|
39
|
+
# @return [ProjectReference] the project object that is contained in the
|
40
40
|
# project file that contains additional information
|
41
41
|
attr_reader :project
|
42
|
-
|
42
|
+
|
43
43
|
#
|
44
44
|
# Initialized with a specific path and sdk.
|
45
|
-
#
|
45
|
+
#
|
46
46
|
# This initialization is not often used. Instead projects are generated
|
47
47
|
# through the Xcode#project method.
|
48
|
-
#
|
48
|
+
#
|
49
49
|
# @see Xcode
|
50
50
|
#
|
51
51
|
# @param [String] path of the project to open.
|
52
|
-
# @param [String] sdk the sdk value of the project. This will default to
|
52
|
+
# @param [String] sdk the sdk value of the project. This will default to
|
53
53
|
# `iphoneos`.
|
54
|
-
#
|
54
|
+
#
|
55
55
|
def initialize(path, sdk=nil)
|
56
56
|
@sdk = sdk || "iphoneos" # FIXME: should support OSX/simulator too
|
57
57
|
@path = File.expand_path path
|
58
58
|
@schemes = nil
|
59
59
|
@groups = []
|
60
60
|
@name = File.basename(@path).gsub(/\.xcodeproj/,'')
|
61
|
-
|
61
|
+
|
62
62
|
# Parse the Xcode project file and create the registry
|
63
|
-
|
63
|
+
|
64
64
|
@registry = parse_pbxproj
|
65
65
|
raise "Unable to parse project at #{@path}" if @registry.nil?
|
66
|
-
@project = Xcode::Resource.new @registry.root, @registry
|
66
|
+
@project = Xcode::Resource.new @registry.root, @registry
|
67
67
|
end
|
68
|
-
|
68
|
+
|
69
69
|
#
|
70
70
|
# @return [Array<Xcode::Scheme>] available schemes for the project
|
71
71
|
#
|
72
72
|
def schemes
|
73
73
|
return @schemes unless @schemes.nil?
|
74
|
-
@schemes = Xcode::Scheme.
|
74
|
+
@schemes = Xcode::Scheme.find_in_project self
|
75
75
|
end
|
76
|
-
|
77
|
-
|
76
|
+
|
77
|
+
|
78
78
|
#
|
79
79
|
# @return [Fixnum] the project's object version
|
80
|
-
#
|
80
|
+
#
|
81
81
|
def object_version
|
82
82
|
@registry.object_version
|
83
83
|
end
|
84
84
|
|
85
85
|
#
|
86
86
|
# @return [Fixnum] the project's archive version
|
87
|
-
#
|
87
|
+
#
|
88
88
|
def archive_version
|
89
89
|
@registry.archive_version
|
90
90
|
end
|
91
|
-
|
91
|
+
|
92
92
|
#
|
93
93
|
# @return [Array<Configuration>] a list of configurations global to the project
|
94
|
-
#
|
94
|
+
#
|
95
95
|
def global_configs
|
96
96
|
@project.configs
|
97
97
|
end
|
98
|
-
|
98
|
+
|
99
99
|
#
|
100
100
|
# @param [String,Symbol] name of the configuration for the project
|
101
|
-
#
|
101
|
+
#
|
102
102
|
# @return [Configuration] the project level configuration with the given name;
|
103
103
|
# raise an exception if no configuration exists with that name.
|
104
104
|
#
|
105
105
|
def global_config(name)
|
106
106
|
@project.config(name)
|
107
107
|
end
|
108
|
-
|
108
|
+
|
109
109
|
#
|
110
110
|
# Returns the main group of the project where all the files reside.
|
111
|
-
#
|
111
|
+
#
|
112
112
|
# @todo this really could use a better name then groups as it is the main group
|
113
113
|
# but it should likely be something like main_group, root or something
|
114
114
|
# else that conveys that this is the project root for files, and such.
|
115
|
-
#
|
115
|
+
#
|
116
116
|
# @return [Group] the main group, the heart of the action of the file
|
117
117
|
# explorer for the Xcode project. From here all other groups and items
|
118
118
|
# may be found.
|
119
|
-
#
|
119
|
+
#
|
120
120
|
def groups
|
121
121
|
@project.main_group
|
122
122
|
end
|
123
|
-
|
123
|
+
|
124
124
|
#
|
125
125
|
# Returns the group specified. If any part of the group does not exist along
|
126
|
-
# the path the group is created. Also paths can be specified to make the
|
126
|
+
# the path the group is created. Also paths can be specified to make the
|
127
127
|
# traversing of the groups easier.
|
128
|
-
#
|
128
|
+
#
|
129
129
|
# @note this will attempt to find the paths specified, if it fails to find them
|
130
130
|
# it will create one and then continue traversing.
|
131
131
|
#
|
132
132
|
# @example Traverse a path through the various sub-groups.
|
133
|
-
#
|
133
|
+
#
|
134
134
|
# project.group('Vendor/MyCode/Support Files')
|
135
135
|
# # is equivalent to ...
|
136
136
|
# project.group('Vendor').first.group('MyCode').first.group('Supporting Files')
|
137
|
-
#
|
137
|
+
#
|
138
138
|
# @note this path functionality current is only exercised from the project level
|
139
139
|
# all groups will treat the path division `/` as simply a character.
|
140
|
-
#
|
140
|
+
#
|
141
141
|
# @param [String] name the group name to find/create
|
142
|
-
#
|
142
|
+
#
|
143
143
|
# @return [Group] the group with the specified name.
|
144
|
-
#
|
144
|
+
#
|
145
145
|
def group(name,options = {},&block)
|
146
146
|
@project.group(name,options,&block)
|
147
147
|
end
|
@@ -157,81 +157,81 @@ module Xcode
|
|
157
157
|
path, name = File.split(name_with_path)
|
158
158
|
group(path).file(name).first
|
159
159
|
end
|
160
|
-
|
160
|
+
|
161
161
|
#
|
162
|
-
# Most Xcode projects have a products group where products are placed. This
|
162
|
+
# Most Xcode projects have a products group where products are placed. This
|
163
163
|
# will generate an exception if there is no products group.
|
164
|
-
#
|
164
|
+
#
|
165
165
|
# @return [Group] the 'Products' group of the project.
|
166
166
|
def products_group
|
167
167
|
current_group = groups.group('Products').first
|
168
168
|
current_group.instance_eval(&block) if block_given? and current_group
|
169
169
|
current_group
|
170
170
|
end
|
171
|
-
|
171
|
+
|
172
172
|
#
|
173
|
-
# Most Xcode projects have a Frameworks gorup where all the imported
|
173
|
+
# Most Xcode projects have a Frameworks gorup where all the imported
|
174
174
|
# frameworks are shown. This will generate an exception if there is no
|
175
175
|
# Frameworks group.
|
176
|
-
#
|
176
|
+
#
|
177
177
|
# @return [Group] the 'Frameworks' group of the projet.
|
178
178
|
def frameworks_group
|
179
179
|
current_group = groups.group('Frameworks').first
|
180
180
|
current_group.instance_eval(&block) if block_given? and current_group
|
181
181
|
current_group
|
182
182
|
end
|
183
|
-
|
183
|
+
|
184
184
|
#
|
185
|
-
# This will convert the current project file into a supported Xcode Plist
|
185
|
+
# This will convert the current project file into a supported Xcode Plist
|
186
186
|
# format. This format is not json or a traditional plist so several core
|
187
187
|
# Ruby objects gained the #to_xcplist method to save it properly.
|
188
|
-
#
|
189
|
-
# Specifically this will add the necessary file header information and the
|
188
|
+
#
|
189
|
+
# Specifically this will add the necessary file header information and the
|
190
190
|
# surrounding mustache braces around the xcode plist format of the registry.
|
191
|
-
#
|
191
|
+
#
|
192
192
|
# @return [String] Xcode Plist format of the project.
|
193
193
|
def to_xcplist
|
194
|
-
|
194
|
+
|
195
195
|
# @note The Hash#to_xcplist, which the Registry will save out as xcode,
|
196
|
-
# saves a semi-colon at the end which needs to be removed to ensure
|
196
|
+
# saves a semi-colon at the end which needs to be removed to ensure
|
197
197
|
# the project file can be opened.
|
198
|
-
|
198
|
+
|
199
199
|
%{// !$*UTF8*$!"\n#{@registry.to_xcplist.gsub(/\};\s*\z/,'}')}}
|
200
200
|
end
|
201
|
-
|
202
|
-
#
|
201
|
+
|
202
|
+
#
|
203
203
|
# Save the current project at the current path that it exists.
|
204
|
-
#
|
204
|
+
#
|
205
205
|
def save!
|
206
206
|
save @path
|
207
207
|
end
|
208
|
-
|
208
|
+
|
209
209
|
#
|
210
210
|
# Saves the current project at the specified path.
|
211
|
-
#
|
212
|
-
# @note currently this does not support saving the workspaces associated
|
211
|
+
#
|
212
|
+
# @note currently this does not support saving the workspaces associated
|
213
213
|
# with the project to their new location.
|
214
|
-
#
|
214
|
+
#
|
215
215
|
# @param [String] path the path to save the project
|
216
216
|
#
|
217
217
|
def save(path)
|
218
|
-
|
218
|
+
|
219
219
|
Dir.mkdir(path) unless File.exists?(path)
|
220
220
|
project_filepath = "#{path}/project.pbxproj"
|
221
|
-
|
221
|
+
|
222
222
|
# @toodo Save the workspace when the project is saved
|
223
223
|
# FileUtils.cp_r "#{path}/project.xcworkspace", "#{path}/project.xcworkspace"
|
224
|
-
|
224
|
+
|
225
225
|
Xcode::PLUTILProjectParser.save "#{path}/project.pbxproj", to_xcplist
|
226
|
-
|
226
|
+
|
227
227
|
end
|
228
|
-
|
228
|
+
|
229
229
|
#
|
230
|
-
# Return the scheme with the specified name. Raises an error if no schemes
|
230
|
+
# Return the scheme with the specified name. Raises an error if no schemes
|
231
231
|
# match the specified name.
|
232
|
-
#
|
232
|
+
#
|
233
233
|
# @note if two schemes match names, the first matching scheme is returned.
|
234
|
-
#
|
234
|
+
#
|
235
235
|
# @param [String] name of the specific scheme
|
236
236
|
# @return [Scheme] the specific scheme that matches the name specified
|
237
237
|
#
|
@@ -241,26 +241,26 @@ module Xcode
|
|
241
241
|
yield scheme if block_given?
|
242
242
|
scheme
|
243
243
|
end
|
244
|
-
|
244
|
+
|
245
245
|
#
|
246
246
|
# All the targets specified within the project.
|
247
|
-
#
|
247
|
+
#
|
248
248
|
# @return [Array<Target>] an array of all the available targets for
|
249
249
|
# the specific project.
|
250
|
-
#
|
250
|
+
#
|
251
251
|
def targets
|
252
252
|
@project.targets.map do |target|
|
253
253
|
target.project = self
|
254
254
|
target
|
255
255
|
end
|
256
256
|
end
|
257
|
-
|
257
|
+
|
258
258
|
#
|
259
259
|
# Return the target with the specified name. Raises an error if no targets
|
260
260
|
# match the specified name.
|
261
|
-
#
|
261
|
+
#
|
262
262
|
# @note if two targets match names, the first matching target is returned.
|
263
|
-
#
|
263
|
+
#
|
264
264
|
# @param [String] name of the specific target
|
265
265
|
# @return [Target] the specific target that matches the name specified
|
266
266
|
#
|
@@ -270,55 +270,55 @@ module Xcode
|
|
270
270
|
yield target if block_given?
|
271
271
|
target
|
272
272
|
end
|
273
|
-
|
273
|
+
|
274
274
|
#
|
275
275
|
# Creates a new target within the Xcode project. This will by default not
|
276
276
|
# generate all the additional build phases, configurations, and files
|
277
277
|
# that create a project.
|
278
|
-
#
|
278
|
+
#
|
279
279
|
# Available targts:
|
280
|
-
#
|
280
|
+
#
|
281
281
|
# * native
|
282
282
|
# * aggregate
|
283
|
-
#
|
283
|
+
#
|
284
284
|
# @param [String] name the name to provide to the target. This will also
|
285
285
|
# be the value that other defaults will be based on.
|
286
286
|
# @param [String,Symbol] type the type of build target to create.
|
287
287
|
#
|
288
288
|
# @return [Target] the target created.
|
289
|
-
#
|
289
|
+
#
|
290
290
|
def create_target(name,type=:native)
|
291
|
-
|
291
|
+
|
292
292
|
target = @registry.add_object Target.send(type)
|
293
293
|
@project.properties['targets'] << target.identifier
|
294
|
-
|
294
|
+
|
295
295
|
target.name = name
|
296
|
-
|
296
|
+
|
297
297
|
build_configuration_list = @registry.add_object(ConfigurationList.configration_list)
|
298
298
|
target.build_configuration_list = build_configuration_list.identifier
|
299
|
-
|
299
|
+
|
300
300
|
target.project = self
|
301
|
-
|
301
|
+
|
302
302
|
yield target if block_given?
|
303
|
-
|
303
|
+
|
304
304
|
target.save!
|
305
305
|
end
|
306
|
-
|
306
|
+
|
307
307
|
#
|
308
308
|
# Remove a target from the Xcode project.
|
309
|
-
#
|
309
|
+
#
|
310
310
|
# @note this will remove the first target that matches the specified name.
|
311
|
-
#
|
311
|
+
#
|
312
312
|
# @note this will remove only the project entry at the moment and not the
|
313
|
-
# the files that may be associated with the target. All build phases,
|
313
|
+
# the files that may be associated with the target. All build phases,
|
314
314
|
# build files, and configurations will automatically be cleaned up when
|
315
315
|
# Xcode is opened.
|
316
|
-
#
|
316
|
+
#
|
317
317
|
# @param [String] name the name of the target to remove from the Xcode
|
318
318
|
# project.
|
319
319
|
#
|
320
320
|
# @return [Target] the target that has been removed.
|
321
|
-
#
|
321
|
+
#
|
322
322
|
def remove_target(name)
|
323
323
|
found_target = targets.find {|target| target.name == name }
|
324
324
|
if found_target
|
@@ -327,9 +327,9 @@ module Xcode
|
|
327
327
|
end
|
328
328
|
found_target
|
329
329
|
end
|
330
|
-
|
331
|
-
#
|
332
|
-
# Prints to STDOUT a description of this project's targets, configuration and schemes.
|
330
|
+
|
331
|
+
#
|
332
|
+
# Prints to STDOUT a description of this project's targets, configuration and schemes.
|
333
333
|
#
|
334
334
|
def describe
|
335
335
|
puts "Project #{name} contains"
|
@@ -345,25 +345,25 @@ module Xcode
|
|
345
345
|
puts " + Test action => target:#{s.test.target.name}, config:#{s.test.name}" unless s.test.nil?
|
346
346
|
end
|
347
347
|
end
|
348
|
-
|
348
|
+
|
349
349
|
private
|
350
|
-
|
350
|
+
|
351
351
|
#
|
352
|
-
# Using the sytem tool plutil, the specified project file is parsed and
|
353
|
-
# converted to JSON, which is then converted to a hash object. This content
|
354
|
-
# contains all the data within the project file and is used to create the
|
352
|
+
# Using the sytem tool plutil, the specified project file is parsed and
|
353
|
+
# converted to JSON, which is then converted to a hash object. This content
|
354
|
+
# contains all the data within the project file and is used to create the
|
355
355
|
# Registry.
|
356
|
-
#
|
356
|
+
#
|
357
357
|
# @return [Registry] the representation of the project, this is a Hash with
|
358
358
|
# additional methods to provide easier functionality.
|
359
|
-
#
|
359
|
+
#
|
360
360
|
def parse_pbxproj
|
361
361
|
registry = Xcode::PLUTILProjectParser.parse "#{@path}/project.pbxproj"
|
362
|
-
|
362
|
+
|
363
363
|
class << registry
|
364
364
|
include Xcode::Registry
|
365
365
|
end
|
366
|
-
|
366
|
+
|
367
367
|
registry
|
368
368
|
end
|
369
369
|
|