Class: Pod::Builder

Inherits:
Object
  • Object
show all
Defined in:
lib/cocoapods-packager/builder.rb

Instance Method Summary collapse

Constructor Details

#initialize(platform, static_installer, source_dir, static_sandbox_root, dynamic_sandbox_root, public_headers_root, spec, embedded, mangle, dynamic, config, bundle_identifier, exclude_deps) ⇒ Builder

Returns a new instance of Builder.



3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# File 'lib/cocoapods-packager/builder.rb', line 3

def initialize(platform, static_installer, source_dir, static_sandbox_root, dynamic_sandbox_root, public_headers_root, spec, embedded, mangle, dynamic, config, bundle_identifier, exclude_deps)
  @platform = platform
  @static_installer = static_installer
  @source_dir = source_dir
  @static_sandbox_root = static_sandbox_root
  @dynamic_sandbox_root = dynamic_sandbox_root
  @public_headers_root = public_headers_root
  @spec = spec
  @embedded = embedded
  @mangle = mangle
  @dynamic = dynamic
  @config = config
  @bundle_identifier = bundle_identifier
  @exclude_deps = exclude_deps

  @file_accessors = @static_installer.pod_targets.select { |t| t.pod_name == @spec.name }.flat_map(&:file_accessors)
end

Instance Method Details

#build(package_type) ⇒ Object



21
22
23
24
25
26
27
28
29
30
# File 'lib/cocoapods-packager/builder.rb', line 21

def build(package_type)
  case package_type
  when :static_library
    build_static_library
  when :static_framework
    build_static_framework
  when :dynamic_framework
    build_dynamic_framework
  end
end

#build_dynamic_frameworkObject



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/cocoapods-packager/builder.rb', line 81

def build_dynamic_framework
  UI.puts("Building dynamic framework #{@spec} with configuration #{@config}")

  defines = compile
  build_sim_libraries(defines)

  if @bundle_identifier
    defines = "#{defines} PRODUCT_BUNDLE_IDENTIFIER='#{@bundle_identifier}'"
  end

  output = "#{@dynamic_sandbox_root}/build/#{@spec.name}.framework/#{@spec.name}"

  clean_directory_for_dynamic_build
  if @platform.name == :ios
    build_dynamic_framework_for_ios(defines, output)
  else
    build_dynamic_framework_for_mac(defines, output)
  end

  copy_resources
end

#build_dynamic_framework_for_ios(defines, output) ⇒ Object



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/cocoapods-packager/builder.rb', line 103

def build_dynamic_framework_for_ios(defines, output)
  # Specify frameworks to link and search paths
  linker_flags = static_linker_flags_in_sandbox
  defines = "#{defines} OTHER_LDFLAGS='$(inherited) #{linker_flags.join(' ')}'"

  # Build Target Dynamic Framework for both device and Simulator
  device_defines = "#{defines} LIBRARY_SEARCH_PATHS=\"#{Dir.pwd}/#{@static_sandbox_root}/build\""
  device_options = ios_build_options << ' -sdk iphoneos'
  xcodebuild(device_defines, device_options, 'build', @spec.name.to_s, @dynamic_sandbox_root.to_s)

  sim_defines = "#{defines} LIBRARY_SEARCH_PATHS=\"#{Dir.pwd}/#{@static_sandbox_root}/build-sim\" ONLY_ACTIVE_ARCH=NO"
  xcodebuild(sim_defines, '-sdk iphonesimulator', 'build-sim', @spec.name.to_s, @dynamic_sandbox_root.to_s)

  # Combine architectures
  `lipo #{@dynamic_sandbox_root}/build/#{@spec.name}.framework/#{@spec.name} #{@dynamic_sandbox_root}/build-sim/#{@spec.name}.framework/#{@spec.name} -create -output #{output}`

  FileUtils.mkdir(@platform.name.to_s)
  `mv #{@dynamic_sandbox_root}/build/#{@spec.name}.framework #{@platform.name}`
  `mv #{@dynamic_sandbox_root}/build/#{@spec.name}.framework.dSYM #{@platform.name}`
end

#build_dynamic_framework_for_mac(defines, _output) ⇒ Object



124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/cocoapods-packager/builder.rb', line 124

def build_dynamic_framework_for_mac(defines, _output)
  # Specify frameworks to link and search paths
  linker_flags = static_linker_flags_in_sandbox
  defines = "#{defines} OTHER_LDFLAGS=\"#{linker_flags.join(' ')}\""

  # Build Target Dynamic Framework for osx
  defines = "#{defines} LIBRARY_SEARCH_PATHS=\"#{Dir.pwd}/#{@static_sandbox_root}/build\""
  xcodebuild(defines, nil, 'build', @spec.name.to_s, @dynamic_sandbox_root.to_s)

  FileUtils.mkdir(@platform.name.to_s)
  `mv #{@dynamic_sandbox_root}/build/#{@spec.name}.framework #{@platform.name}`
  `mv #{@dynamic_sandbox_root}/build/#{@spec.name}.framework.dSYM #{@platform.name}`
end

#build_sim_libraries(defines) ⇒ Object



138
139
140
141
142
# File 'lib/cocoapods-packager/builder.rb', line 138

def build_sim_libraries(defines)
  if @platform.name == :ios
    xcodebuild(defines, '-sdk iphonesimulator', 'build-sim')
  end
end

#build_static_frameworkObject



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/cocoapods-packager/builder.rb', line 50

def build_static_framework
  UI.puts("Building static framework #{@spec} with configuration #{@config}")

  defines = compile
  build_sim_libraries(defines)

  create_framework
  output = @fwk.versions_path + Pathname.new(@spec.name)

  if @platform.name == :ios
    build_static_library_for_ios(output)
  else
    build_static_library_for_mac(output)
  end

  copy_headers
  copy_license
  copy_resources
  @fwk.process_static_framework
end

#build_static_libraryObject



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/cocoapods-packager/builder.rb', line 32

def build_static_library
  UI.puts("Building static library #{@spec} with configuration #{@config}")

  defines = compile
  build_sim_libraries(defines)

  platform_path = Pathname.new(@platform.name.to_s)
  platform_path.mkdir unless platform_path.exist?

  output = platform_path + "lib#{@spec.name}.a"

  if @platform.name == :ios
    build_static_library_for_ios(output)
  else
    build_static_library_for_mac(output)
  end
end

#build_static_library_for_ios(output) ⇒ Object



144
145
146
147
148
149
150
151
152
153
# File 'lib/cocoapods-packager/builder.rb', line 144

def build_static_library_for_ios(output)
  static_libs = static_libs_in_sandbox('build') + static_libs_in_sandbox('build-sim') + vendored_libraries
  libs = ios_architectures.map do |arch|
    library = "#{@static_sandbox_root}/build/package-#{arch}.a"
    `libtool -arch_only #{arch} -static -o #{library} #{static_libs.join(' ')}`
    library
  end

  `lipo -create -output #{output} #{libs.join(' ')}`
end

#build_static_library_for_mac(output) ⇒ Object



155
156
157
158
# File 'lib/cocoapods-packager/builder.rb', line 155

def build_static_library_for_mac(output)
  static_libs = static_libs_in_sandbox + vendored_libraries
  `libtool -static -o #{output} #{static_libs.join(' ')}`
end

#build_with_mangling(options) ⇒ Object



160
161
162
163
164
165
166
167
168
# File 'lib/cocoapods-packager/builder.rb', line 160

def build_with_mangling(options)
  UI.puts 'Mangling symbols'
  defines = Symbols.mangle_for_pod_dependencies(@spec.name, @static_sandbox_root)
  defines << ' ' << @spec.consumer(@platform).compiler_flags.join(' ')

  UI.puts 'Building mangled framework'
  xcodebuild(defines, options)
  defines
end

#clean_directory_for_dynamic_buildObject



170
171
172
173
174
175
176
177
# File 'lib/cocoapods-packager/builder.rb', line 170

def clean_directory_for_dynamic_build
  # Remove static headers to avoid duplicate declaration conflicts
  FileUtils.rm_rf("#{@static_sandbox_root}/Headers/Public/#{@spec.name}")
  FileUtils.rm_rf("#{@static_sandbox_root}/Headers/Private/#{@spec.name}")

  # Equivalent to removing derrived data
  FileUtils.rm_rf('Pods/build')
end

#compileObject



179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/cocoapods-packager/builder.rb', line 179

def compile
  defines = "GCC_PREPROCESSOR_DEFINITIONS='$(inherited) PodsDummy_Pods_#{@spec.name}=PodsDummy_PodPackage_#{@spec.name}'"
  defines << ' ' << @spec.consumer(@platform).compiler_flags.join(' ')

  if @platform.name == :ios
    options = ios_build_options
  end

  xcodebuild(defines, options)

  if @mangle
    return build_with_mangling(options)
  end

  defines
end

#copy_headersObject



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
# File 'lib/cocoapods-packager/builder.rb', line 196

def copy_headers
  headers_source_root = "#{@public_headers_root}/#{@spec.name}"

  Dir.glob("#{headers_source_root}/**/*.h").
    each { |h| `ditto #{h} #{@fwk.headers_path}/#{h.sub(headers_source_root, '')}` }

  # If custom 'module_map' is specified add it to the framework distribution
  # otherwise check if a header exists that is equal to 'spec.name', if so
  # create a default 'module_map' one using it.
  if !@spec.module_map.nil?
    module_map_file = @file_accessors.flat_map(&:module_map).first
    module_map = File.read(module_map_file) if Pathname(module_map_file).exist?
  elsif File.exist?("#{@public_headers_root}/#{@spec.name}/#{@spec.name}.h")
    module_map = <<MAP
framework module #{@spec.name} {
  umbrella header "#{@spec.name}.h"

  export *
  module * { export * }
}
MAP
  end

  unless module_map.nil?
    @fwk.module_map_path.mkpath unless @fwk.module_map_path.exist?
    File.write("#{@fwk.module_map_path}/module.modulemap", module_map)
  end
end

#copy_licenseObject



225
226
227
228
229
# File 'lib/cocoapods-packager/builder.rb', line 225

def copy_license
  license_file = @spec.license[:file] || 'LICENSE'
  license_file = Pathname.new("#{@static_sandbox_root}/#{@spec.name}/#{license_file}")
  FileUtils.cp(license_file, '.') if license_file.exist?
end

#copy_resourcesObject



231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
# File 'lib/cocoapods-packager/builder.rb', line 231

def copy_resources
  bundles = Dir.glob("#{@static_sandbox_root}/build/*.bundle")
  if @dynamic
    resources_path = "ios/#{@spec.name}.framework"
    `cp -rp #{@static_sandbox_root}/build/*.bundle #{resources_path} 2>&1`
  else
    `cp -rp #{@static_sandbox_root}/build/*.bundle #{@fwk.resources_path} 2>&1`
    resources = expand_paths(@spec.consumer(@platform).resources)
    if resources.count == 0 && bundles.count == 0
      @fwk.delete_resources
      return
    end
    if resources.count > 0
      `cp -rp #{resources.join(' ')} #{@fwk.resources_path}`
    end
  end
end

#create_frameworkObject



249
250
251
252
# File 'lib/cocoapods-packager/builder.rb', line 249

def create_framework
  @fwk = Framework::Tree.new(@spec.name, @platform.name.to_s, @embedded)
  @fwk.make
end

#dependency_countObject



254
255
256
257
258
259
260
261
262
# File 'lib/cocoapods-packager/builder.rb', line 254

def dependency_count
  count = @spec.dependencies.count

  @spec.subspecs.each do |subspec|
    count += subspec.dependencies.count
  end

  count
end

#expand_paths(path_specs) ⇒ Object



264
265
266
267
268
# File 'lib/cocoapods-packager/builder.rb', line 264

def expand_paths(path_specs)
  path_specs.map do |path_spec|
    Dir.glob(File.join(@source_dir, path_spec))
  end
end

#ios_architecturesObject



307
308
309
310
311
312
313
# File 'lib/cocoapods-packager/builder.rb', line 307

def ios_architectures
  archs = %w(x86_64 arm64 armv7 armv7s)
  vendored_libraries.each do |library|
    archs = `lipo -info #{library}`.split & archs
  end
  archs
end

#ios_build_optionsObject



303
304
305
# File 'lib/cocoapods-packager/builder.rb', line 303

def ios_build_options
  "ARCHS=\'#{ios_architectures.join(' ')}\' OTHER_CFLAGS=\'-fembed-bitcode -Qunused-arguments\'"
end


71
72
73
74
75
76
77
78
79
# File 'lib/cocoapods-packager/builder.rb', line 71

def link_embedded_resources
  target_path = @fwk.root_path + Pathname.new('Resources')
  target_path.mkdir unless target_path.exist?

  Dir.glob(@fwk.resources_path.to_s + '/*').each do |resource|
    resource = Pathname.new(resource).relative_path_from(target_path)
    `ln -sf #{resource} #{target_path}`
  end
end

#static_libs_in_sandbox(build_dir = 'build') ⇒ Object



270
271
272
273
274
275
276
277
# File 'lib/cocoapods-packager/builder.rb', line 270

def static_libs_in_sandbox(build_dir = 'build')
  if @exclude_deps
    UI.puts 'Excluding dependencies'
    Dir.glob("#{@static_sandbox_root}/#{build_dir}/lib#{@spec.name}.a")
  else
    Dir.glob("#{@static_sandbox_root}/#{build_dir}/lib*.a")
  end
end

#static_linker_flags_in_sandboxObject



294
295
296
297
298
299
300
301
# File 'lib/cocoapods-packager/builder.rb', line 294

def static_linker_flags_in_sandbox
  linker_flags = static_libs_in_sandbox.map do |lib|
    lib.slice!('lib')
    lib_flag = lib.chomp('.a').split('/').last
    "-l#{lib_flag}"
  end
  linker_flags.reject { |e| e == "-l#{@spec.name}" || e == '-lPods-packager' }
end

#vendored_librariesObject



279
280
281
282
283
284
285
286
287
288
289
290
291
292
# File 'lib/cocoapods-packager/builder.rb', line 279

def vendored_libraries
  if @vendored_libraries
    @vendored_libraries
  end
  file_accessors = if @exclude_deps
                     @file_accessors
                   else
                     @static_installer.pod_targets.flat_map(&:file_accessors)
                   end
  libs = file_accessors.flat_map(&:vendored_static_frameworks).map { |f| f + f.basename('.*') } || []
  libs += file_accessors.flat_map(&:vendored_static_libraries)
  @vendored_libraries = libs.compact.map(&:to_s)
  @vendored_libraries
end

#xcodebuild(defines = '', args = '', build_dir = 'build', target = 'Pods-packager', project_root = @static_sandbox_root, config = @config) ⇒ Object



315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
# File 'lib/cocoapods-packager/builder.rb', line 315

def xcodebuild(defines = '', args = '', build_dir = 'build', target = 'Pods-packager', project_root = @static_sandbox_root, config = @config)
  if defined?(Pod::DONT_CODESIGN)
    args = "#{args} CODE_SIGN_IDENTITY=\"\" CODE_SIGNING_REQUIRED=NO"
  end

  command = "xcodebuild #{defines} #{args} CONFIGURATION_BUILD_DIR=#{build_dir} clean build -configuration #{config} -target #{target} -project #{project_root}/Pods.xcodeproj 2>&1"
  output = `#{command}`.lines.to_a

  if $?.exitstatus != 0
    puts UI::BuildFailedReport.report(command, output)

    # Note: We use `Process.exit` here because it fires a `SystemExit`
    # exception, which gives the caller a chance to clean up before the
    # process terminates.
    #
    # See http://ruby-doc.org/core-1.9.3/Process.html#method-c-exit
    Process.exit
  end
end