forked from fastlane/fastlane
-
Notifications
You must be signed in to change notification settings - Fork 0
/
package_command_generator_xcode7.rb
228 lines (190 loc) 路 9.04 KB
/
package_command_generator_xcode7.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# encoding: utf-8
# from https://stackoverflow.com/a/9857493/445598
# because of
# `incompatible encoding regexp match (UTF-8 regexp with ASCII-8BIT string) (Encoding::CompatibilityError)`
require 'tempfile'
require 'xcodeproj'
require 'fastlane_core/core_ext/cfpropertylist'
require_relative '../module'
require_relative '../error_handler'
require_relative 'build_command_generator'
module Gym
# Responsible for building the fully working xcodebuild command
class PackageCommandGeneratorXcode7
class << self
DEFAULT_EXPORT_METHOD = "app-store"
def generate
parts = ["/usr/bin/xcrun #{wrap_xcodebuild.shellescape} -exportArchive"]
parts += options
parts += pipe
File.write(config_path, config_content) # overwrite everytime. Could be optimized
parts
end
def options
config = Gym.config
options = []
options << "-exportOptionsPlist '#{config_path}'"
options << "-archivePath #{BuildCommandGenerator.archive_path.shellescape}"
options << "-exportPath '#{temporary_output_path}'"
options << "-toolchain '#{config[:toolchain]}'" if config[:toolchain]
options << config[:export_xcargs] if config[:export_xcargs]
options << config[:xcargs] if config[:xcargs]
options
end
def pipe
[""]
end
# We export the ipa into this directory, as we can't specify the ipa file directly
def temporary_output_path
Gym.cache[:temporary_output_path] ||= Dir.mktmpdir('gym_output')
end
# Wrap xcodebuild to work-around ipatool dependency to system ruby
def wrap_xcodebuild
require 'fileutils'
@wrapped_xcodebuild_path ||= File.join(Gym::ROOT, "lib/assets/wrap_xcodebuild/xcbuild-safe.sh")
end
def ipa_path
path = Gym.cache[:ipa_path]
return path if path
path = Dir[File.join(temporary_output_path, "*.ipa")].last
# We need to process generic IPA
if path
# Try to find IPA file in the output directory, used when app thinning was not set
Gym.cache[:ipa_path] = File.join(temporary_output_path, "#{Gym.config[:output_name]}.ipa")
FileUtils.mv(path, Gym.cache[:ipa_path]) unless File.expand_path(path).casecmp(File.expand_path(Gym.cache[:ipa_path]).downcase).zero?
elsif Dir.exist?(apps_path)
# Try to find "generic" IPA file inside "Apps" folder, used when app thinning was set
files = Dir[File.join(apps_path, "*.ipa")]
# Generic IPA file doesn't have suffix so its name is the shortest
path = files.min_by(&:length)
Gym.cache[:ipa_path] = File.join(temporary_output_path, "#{Gym.config[:output_name]}.ipa")
FileUtils.cp(path, Gym.cache[:ipa_path]) unless File.expand_path(path).casecmp(File.expand_path(Gym.cache[:ipa_path]).downcase).zero?
else
ErrorHandler.handle_empty_archive unless path
end
Gym.cache[:ipa_path]
end
def pkg_path
path = Gym.cache[:pkg_path]
return path if path
path = Dir[File.join(temporary_output_path, "*.pkg")].last
# We need to process generic PKG
if path
# Try to find PKG file in the output directory, used when app thinning was not set
Gym.cache[:pkg_path] = File.join(temporary_output_path, "#{Gym.config[:output_name]}.pkg")
FileUtils.mv(path, Gym.cache[:pkg_path]) unless File.expand_path(path).casecmp(File.expand_path(Gym.cache[:pkg_path]).downcase).zero?
elsif Dir.exist?(apps_path)
# Try to find "generic" PKG file inside "Apps" folder, used when app thinning was set
files = Dir[File.join(apps_path, "*.pkg")]
# Generic PKG file doesn't have suffix so its name is the shortest
path = files.min_by(&:length)
Gym.cache[:pkg_path] = File.join(temporary_output_path, "#{Gym.config[:output_name]}.pkg")
FileUtils.cp(path, Gym.cache[:pkg_path]) unless File.expand_path(path).casecmp(File.expand_path(Gym.cache[:pkg_path]).downcase).zero?
else
ErrorHandler.handle_empty_archive unless path
end
Gym.cache[:pkg_path]
end
# The path the the dsym file for this app. Might be nil
def dsym_path
Dir[BuildCommandGenerator.archive_path + "/**/*.app.dSYM"].last
end
# The path the config file we use to sign our app
def config_path
Gym.cache[:config_path] ||= "#{Tempfile.new('gym_config').path}.plist"
return Gym.cache[:config_path]
end
# The path to the manifest plist file
def manifest_path
Gym.cache[:manifest_path] ||= File.join(temporary_output_path, "manifest.plist")
end
# The path to the app-thinning plist file
def app_thinning_path
Gym.cache[:app_thinning] ||= File.join(temporary_output_path, "app-thinning.plist")
end
# The path to the App Thinning Size Report file
def app_thinning_size_report_path
Gym.cache[:app_thinning_size_report] ||= File.join(temporary_output_path, "App Thinning Size Report.txt")
end
# The path to the Apps folder
def apps_path
Gym.cache[:apps_path] ||= File.join(temporary_output_path, "Apps")
end
# The path to the Apps folder
def asset_packs_path
Gym.cache[:asset_packs_path] ||= File.join(temporary_output_path, "OnDemandResources")
end
private
def normalize_export_options(hash)
# Normalize some values
hash[:onDemandResourcesAssetPacksBaseURL] = URI.escape(hash[:onDemandResourcesAssetPacksBaseURL]) if hash[:onDemandResourcesAssetPacksBaseURL]
if hash[:manifest]
hash[:manifest][:appURL] = URI.escape(hash[:manifest][:appURL]) if hash[:manifest][:appURL]
hash[:manifest][:displayImageURL] = URI.escape(hash[:manifest][:displayImageURL]) if hash[:manifest][:displayImageURL]
hash[:manifest][:fullSizeImageURL] = URI.escape(hash[:manifest][:fullSizeImageURL]) if hash[:manifest][:fullSizeImageURL]
hash[:manifest][:assetPackManifestURL] = URI.escape(hash[:manifest][:assetPackManifestURL]) if hash[:manifest][:assetPackManifestURL]
end
hash
end
def read_export_options
# Reads export options
if Gym.config[:export_options]
hash = normalize_export_options(Gym.config[:export_options])
# Saves configuration for later use
Gym.config[:export_method] ||= hash[:method] || DEFAULT_EXPORT_METHOD
Gym.config[:include_symbols] = hash[:uploadSymbols] if Gym.config[:include_symbols].nil?
Gym.config[:include_bitcode] = hash[:uploadBitcode] if Gym.config[:include_bitcode].nil?
Gym.config[:export_team_id] ||= hash[:teamID]
else
hash = {}
# Sets default values
Gym.config[:export_method] ||= DEFAULT_EXPORT_METHOD
Gym.config[:include_symbols] = true if Gym.config[:include_symbols].nil?
Gym.config[:include_bitcode] = false if Gym.config[:include_bitcode].nil?
end
hash
end
def config_content
hash = read_export_options
# Overrides export options if needed
hash[:method] = Gym.config[:export_method]
if Gym.config[:export_method] == 'app-store'
hash[:uploadSymbols] = (Gym.config[:include_symbols] ? true : false) unless Gym.config[:include_symbols].nil?
hash[:uploadBitcode] = (Gym.config[:include_bitcode] ? true : false) unless Gym.config[:include_bitcode].nil?
end
# xcodebuild will not use provisioning profiles
# if we don't specify signingStyle as manual
if Helper.xcode_at_least?("9.0") && hash[:provisioningProfiles]
hash[:signingStyle] = 'manual'
end
if Gym.config[:installer_cert_name] && (Gym.project.mac? || Gym.building_mac_catalyst_for_mac?)
hash[:installerSigningCertificate] = Gym.config[:installer_cert_name]
end
hash[:teamID] = Gym.config[:export_team_id] if Gym.config[:export_team_id]
UI.important("Generated plist file with the following values:")
UI.command_output("-----------------------------------------")
UI.command_output(JSON.pretty_generate(hash))
UI.command_output("-----------------------------------------")
if FastlaneCore::Globals.verbose?
UI.message("This results in the following plist file:")
UI.command_output("-----------------------------------------")
UI.command_output(hash.to_plist)
UI.command_output("-----------------------------------------")
end
hash.to_plist
end
def signing_style
projects = Gym.project.project_paths
project = projects.first
xcodeproj = Xcodeproj::Project.open(project)
xcodeproj.root_object.attributes["TargetAttributes"].each do |target, sett|
return sett["ProvisioningStyle"].to_s.downcase
end
rescue => e
UI.verbose(e.to_s)
UI.error("Unable to read provisioning style from .pbxproj file.")
return "automatic"
end
end
end
end