-
Notifications
You must be signed in to change notification settings - Fork 1
/
project_helper.rb
267 lines (187 loc) · 8.56 KB
/
project_helper.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
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
require 'find'
require 'xcodeproj'
require 'json'
require 'plist'
require 'English'
# ProjectHelper ...
class ProjectHelper
attr_reader :main_target
def initialize(project_or_workspace_path, scheme_name)
raise "project not exist at: #{project_or_workspace_path}" unless File.exist?("#{project_or_workspace_path}")
extname = File.extname(project_or_workspace_path)
raise "unkown project extension: #{extname}, should be: .xcodeproj or .xcworkspace" unless ['.xcodeproj', '.xcworkspace'].include?(extname)
@project_path = "#{project_or_workspace_path}"
# ensure scheme exist
scheme, scheme_container_project_path = read_scheme_and_container_project(scheme_name)
# read scheme application targets
@main_target, @targets_container_project_path = read_scheme_archivable_target_and_container_project(scheme, scheme_container_project_path)
puts "Scheme target: #{@main_target}, container path: #{@targets_container_project_path}"
puts
raise "failed to find #{scheme_name} scheme's main archivable target" unless @main_target
end
def link_swift_framework_if_objective_c_only_project()
hasSwift = @project.files.find do |file| file.path.end_with?(".swift") end
if hasSwift
puts "Project already has Swift files.."
return
end
puts "Writing swift file to project since the project does not have any files"
# create swift file
swiftPath = "#{File.dirname(@project.path)}/bitrise_empty_swift_file.swift"
# write file to root of xcode project
File.open(swiftPath, "w") do |f|
f.write("// Empty Swift file for Xcode to enable swift inside project")
end
@swiftFile = @project.new_file(swiftPath)
puts "Written swift file to project"
end
def link_static_library()
@project.targets.each do |target_obj|
next if target_obj.name != @main_target.name
puts "Found iOS app target: #{target_obj.name}"
if ([email protected]?)
puts "Writing swift file to target"
buildFiles = target_obj.add_file_references([@swiftFile])
puts "Written swift file to target"
end
target_obj.build_configuration_list.build_configurations.each do |build_configuration|
configuration_found = true
# Add other linker flags
build_settings = build_configuration.build_settings
codesign_settings = {
'OTHER_LDFLAGS' => '$(inherited) -ObjC -force_load libTrace.a',
'LIBRARY_SEARCH_PATH' => '$(inherited) $(PROJECT_DIR)/trace-cocoa-sdk',
}
if ([email protected]?)
codesign_settings['SWIFT_VERSION'] = 5.0
end
build_settings.merge!(codesign_settings)
puts "Added other linker flag for target: #{target_obj.name}, configuration: #{build_configuration.type}"
puts "New build settings(#{build_configuration.type}): #{build_settings}"
end
# Add system libraries
if !target_obj.frameworks_build_phase.file_display_names.include?("libc++.tbd")
puts "Added c++ library"
target_obj.add_system_library_tbd("c++")
else
puts "libc++.tbd library already exist"
end
if !target_obj.frameworks_build_phase.file_display_names.include?("libz.tbd")
puts "Added z library"
target_obj.add_system_library_tbd("z")
else
puts "libz.tbd library already exist"
end
# Add system frameworks
if !target_obj.frameworks_build_phase.file_display_names.include?("SystemConfiguration.framework")
puts "Added SystemConfiguration frameworks"
target_obj.add_system_framework("SystemConfiguration")
else
puts "SystemConfiguration framework already exist"
end
# Add post script
puts "Installing post-script for uploading dSYMS"
shellScript = target_obj.new_shell_script_build_phase("Bitrise Trace SDK - Upload dSYM's")
shellScript.show_env_vars_in_log = '0'
shellScript.shell_script = <<~eos
#!/bin/sh
set +o posix
echo "Bitrise Trace SDK - starting Upload dSYM's"
# See script header for more information - https://github.com/bitrise-io/trace-cocoa-sdk/blob/main/UploadDSYM/main.swift#L4
# Run script
/usr/bin/xcrun --sdk macosx swift <(curl -Ls --retry 3 --connect-timeout 20 https://raw.githubusercontent.com/bitrise-io/trace-cocoa-sdk/main/UploadDSYM/main.swift)
# Script logs can be viewed in Xcode report navigator or Bitrise app build logs
echo "Bitrise Trace SDK - finished Upload dSYM's"
eos
puts "Installed post-script"
end
@project.save
puts "Saving project"
end
def register_resource()
apm_collector_token = ENV['APM_COLLECTOR_TOKEN']
bitrise_configuration_path = "#{@project.path}/../bitrise_configuration.plist"
obj = Xcodeproj::Plist.write_to_path({
"APM_COLLECTOR_TOKEN" => apm_collector_token,
"APM_COLLECTOR_ENVIRONMENT" => "",
"APM_INSTALLATION_SOURCE" => "Trace step"
}, bitrise_configuration_path)
added_fileref = @project.new_file(bitrise_configuration_path)
res_build_phase = @main_target.resources_build_phase
added_buildf = res_build_phase.add_file_reference(added_fileref)
group = @project.main_group
referenced_build_files = added_buildf.file_ref.build_files
@project.save
end
private
def read_scheme_and_container_project(scheme_name)
project_paths = [@project_path]
project_paths += contained_projects if workspace?
project_paths.each do |project_path|
schema_path = File.join(project_path, 'xcshareddata', 'xcschemes', scheme_name + '.xcscheme')
# if shared scheme does not exist, find the first user scheme
unless File.exist?(schema_path)
schema_path = Find.find(project_path).select { |f| f =~ /.*#{scheme_name}\.xcscheme$/ }[0]
end
next unless schema_path
return Xcodeproj::XCScheme.new(schema_path), project_path
end
raise "project (#{@project_path}) does not contain scheme: #{scheme_name}"
end
def archivable_target_and_container_project(buildable_references, scheme_container_project_dir)
buildable_references.each do |reference|
next if reference.target_name.to_s.empty?
next if reference.target_referenced_container.to_s.empty?
container = reference.target_referenced_container.sub(/^container:/, '')
next if container.empty?
target_project_path = File.expand_path(container, scheme_container_project_dir)
next unless File.exist?(target_project_path)
@project = Xcodeproj::Project.open(target_project_path)
target = @project.targets.find { |t| t.name == reference.target_name }
next unless target
next unless runnable_target?(target)
return target, target_project_path
end
end
def read_scheme_archivable_target_and_container_project(scheme, scheme_container_project_path)
build_action = scheme.build_action
return nil unless build_action
entries = build_action.entries || []
return nil if entries.empty?
entries = entries.select(&:build_for_archiving?) || []
return nil if entries.empty?
scheme_container_project_dir = File.dirname(scheme_container_project_path)
entries.each do |entry|
buildable_references = entry.buildable_references || []
next if buildable_references.empty?
target, target_project_path = archivable_target_and_container_project(buildable_references, scheme_container_project_dir)
next if target.nil? || target_project_path.nil?
return target, target_project_path
end
nil
end
def workspace?
extname = File.extname(@project_path)
extname == '.xcworkspace'
end
def contained_projects
return [@project_path] unless workspace?
workspace = Xcodeproj::Workspace.new_from_xcworkspace(@project_path)
workspace_dir = File.dirname(@project_path)
project_paths = []
workspace.file_references.each do |ref|
pth = ref.path
next unless File.extname(pth) == '.xcodeproj'
next if pth.end_with?('Pods/Pods.xcodeproj')
project_path = File.expand_path(pth, workspace_dir)
project_paths << project_path
end
project_paths
end
def runnable_target?(target)
return false unless target.is_a?(Xcodeproj::Project::Object::PBXNativeTarget)
product_reference = target.product_reference
return false unless product_reference
product_reference.path.end_with?('.app', '.appex')
end
end