module PodBuilder
This class is the model that PodBuilder
uses for every pod spec. The model is instantiated from Pod::Specification
This file contains the logic that generates the .podspec files that are placed along to the prebuild frameworks under PodBuilder/Prebuilt
Constants
- VERSION
Public Class Methods
add_lockfile()
click to toggle source
# File lib/pod_builder/core.rb, line 186 def self.add_lockfile lockfile_path = Configuration.lockfile_path if File.exist?(lockfile_path) if pid = File.read(lockfile_path) begin if Process.getpgid(pid) if Configuration.deterministic_build raise "\n\nAnother PodBuilder pending task is running\n".red else raise "\n\nAnother PodBuilder pending task is running on this project\n".red end end rescue end end end File.write(lockfile_path, Process.pid, mode: "w") end
add_simulator_conditional(path)
click to toggle source
# File lib/pod_builder/rome/post_install.rb, line 199 def self.add_simulator_conditional(path) file_content = File.read(path) content = %{ #if TARGET_OS_SIMULATOR #{file_content} #endif } File.write(path, content) end
basepath(child = "")
click to toggle source
# File lib/pod_builder/core.rb, line 59 def self.basepath(child = "") if child.nil? return nil end return "#{Configuration.base_path}/#{child}".gsub("//", "/").gsub(/\/$/, '') end
build_for_iosish_platform_framework(sandbox, build_dir, target, device, simulator, configuration, deterministic_build)
click to toggle source
# File lib/pod_builder/rome/post_install.rb, line 26 def self.build_for_iosish_platform_framework(sandbox, build_dir, target, device, simulator, configuration, deterministic_build) dsym_device_folder = File.join(build_dir, "dSYM", device) dsym_simulator_folder = File.join(build_dir, "dSYM", simulator) FileUtils.mkdir_p(dsym_device_folder) FileUtils.mkdir_p(dsym_simulator_folder) deployment_target = target.platform_deployment_target target_label = target.cocoapods_target_label xcodebuild(sandbox, target_label, device, deployment_target, configuration, deterministic_build, [], {}) excluded_archs = ["i386"] # Fixes https://github.com/Subito-it/PodBuilder/issues/17 excluded_archs += ["arm64"] # Exclude apple silicon slice xcodebuild(sandbox, target_label, simulator, deployment_target, configuration, deterministic_build, excluded_archs, {}) spec_names = target.specs.map { |spec| [spec.root.name, spec.root.module_name] }.uniq spec_names.each do |root_name, module_name| device_base = "#{build_dir}/#{configuration}-#{device}/#{root_name}" device_lib = "#{device_base}/#{module_name}.framework/#{module_name}" device_dsym = "#{device_base}/#{module_name}.framework.dSYM" device_framework_lib = File.dirname(device_lib) device_swift_header_path = "#{device_framework_lib}/Headers/#{module_name}-Swift.h" simulator_base = "#{build_dir}/#{configuration}-#{simulator}/#{root_name}" simulator_lib = "#{simulator_base}/#{module_name}.framework/#{module_name}" simulator_dsym = "#{simulator_base}/#{module_name}.framework.dSYM" simulator_framework_lib = File.dirname(simulator_lib) simulator_swift_header_path = "#{simulator_framework_lib}/Headers/#{module_name}-Swift.h" next unless File.file?(device_lib) && File.file?(simulator_lib) # Starting with Xcode 12b3 the simulator binary contains an arm64 slice as well which conflict with the one in the device_lib # when creating the fat library. A naive workaround is to remove the arm64 from the simulator_lib however this is wrong because # we might actually need to have 2 separated arm64 slices, one for simulator and one for device each built with different # compile time directives (e.g #if targetEnvironment(simulator)) # # For the time being we remove the arm64 slice bacause otherwise the `xcrun lipo -create -output ...` would fail. if `xcrun lipo -info #{simulator_lib}`.include?("arm64") `xcrun lipo -remove arm64 #{simulator_lib} -o #{simulator_lib}` end raise "Lipo failed on #{device_lib}" unless system("xcrun lipo -create -output #{device_lib} #{device_lib} #{simulator_lib}") merge_header_into(device_swift_header_path, simulator_swift_header_path) # Merge device framework into simulator framework (so that e.g swift Module folder is merged) # letting device framework files overwrite simulator ones FileUtils.cp_r(File.join(device_framework_lib, "."), simulator_framework_lib) source_lib = File.dirname(simulator_framework_lib) FileUtils.mv(device_dsym, dsym_device_folder) if File.exist?(device_dsym) FileUtils.mv(simulator_dsym, dsym_simulator_folder) if File.exist?(simulator_dsym) FileUtils.mv(source_lib, build_dir) # Remove frameworks leaving dSYMs FileUtils.rm_rf(device_framework_lib) FileUtils.rm_rf(simulator_framework_lib) end end
build_for_iosish_platform_lib(sandbox, build_dir, target, device, simulator, configuration, deterministic_build, prebuilt_root_paths)
click to toggle source
# File lib/pod_builder/rome/post_install.rb, line 86 def self.build_for_iosish_platform_lib(sandbox, build_dir, target, device, simulator, configuration, deterministic_build, prebuilt_root_paths) deployment_target = target.platform_deployment_target target_label = target.cocoapods_target_label spec_names = target.specs.map { |spec| [spec.root.name, spec.root.module_name] }.uniq xcodebuild(sandbox, target_label, device, deployment_target, configuration, deterministic_build, [], prebuilt_root_paths) excluded_archs = ["arm64"] # Exclude Apple silicon slice xcodebuild(sandbox, target_label, simulator, deployment_target, configuration, deterministic_build, excluded_archs, prebuilt_root_paths) spec_names.each do |root_name, module_name| simulator_base = "#{build_dir}/#{configuration}-#{simulator}/#{root_name}" simulator_lib = "#{simulator_base}/lib#{root_name}.a" device_base = "#{build_dir}/#{configuration}-#{device}/#{root_name}" device_lib = "#{device_base}/lib#{root_name}.a" unless File.file?(device_lib) && File.file?(simulator_lib) next end # Starting with Xcode 12b3 the simulator binary contains an arm64 slice as well which conflict with the one in the device_lib # when creating the fat library. A naive workaround is to remove the arm64 from the simulator_lib however this is wrong because # we might actually need to have 2 separated arm64 slices, one for simulator and one for device each built with different # compile time directives (e.g #if targetEnvironment(simulator)) # # For the time being we remove the arm64 slice bacause otherwise the `xcrun lipo -create -output ...` would fail. if `xcrun lipo -info #{simulator_lib}`.include?("arm64") `xcrun lipo -remove arm64 #{simulator_lib} -o #{simulator_lib}` end raise "Lipo failed on #{device_lib}" unless system("xcrun lipo -create -output #{device_lib} #{device_lib} #{simulator_lib}") device_headers = Dir.glob("#{device_base}/**/*.h") simulator_headers = Dir.glob("#{simulator_base}/**/*.h") device_headers.each do |device_path| simulator_path = device_path.gsub(device_base, simulator_base) merge_header_into(device_path, simulator_path) end simulator_only_headers = simulator_headers - device_headers.map { |t| t.gsub(device_base, simulator_base) } simulator_only_headers.each do |path| add_simulator_conditional(path) dir_name = File.dirname(path) destination_folder = dir_name.gsub(simulator_base, device_base) FileUtils.mkdir_p(destination_folder) FileUtils.cp(path, destination_folder) end swiftmodule_path = "#{simulator_base}/#{root_name}.swiftmodule" if File.directory?(swiftmodule_path) FileUtils.cp_r("#{swiftmodule_path}/.", "#{device_base}/#{root_name}.swiftmodule") end if File.exist?("#{device_base}/#{root_name}.swiftmodule") # This is a swift pod with a swiftmodule in the root of the prebuilt folder else # Objective-C pods have the swiftmodule generated under Pods/Headers/Public public_headers_path = "#{Configuration.build_path}/Pods/Headers/Public/#{root_name}" module_public_headers_path = "#{Configuration.build_path}/Pods/Headers/Public/#{module_name}" if public_headers_path.downcase != module_public_headers_path.downcase && File.directory?(public_headers_path) && File.directory?(module_public_headers_path) # For pods with module_name != name we have to move the modulemap files to the root_name one module_public_headers_path = "#{Configuration.build_path}/Pods/Headers/Public/#{module_name}" FileUtils.cp_r("#{module_public_headers_path}/.", public_headers_path, :remove_destination => true) end Dir.glob("#{public_headers_path}/**/*.*").each do |path| destination_folder = "#{device_base}/Headers" + path.gsub(public_headers_path, "") destination_folder = File.dirname(destination_folder) FileUtils.mkdir_p(destination_folder) FileUtils.cp(path, destination_folder) end end destination_path = "#{build_dir}/#{root_name}" if Dir.glob("#{device_base}/**/*.{a,framework,h}").count > 0 FileUtils.mv(device_base, destination_path) module_maps = Dir.glob("#{destination_path}/**/*.modulemap") module_map_device_base = device_base.gsub(/^\/private/, "") + "/" module_maps.each do |module_map| content = File.read(module_map) content.gsub!(module_map_device_base, "") File.write(module_map, content) end end end end
buildpath_dsympath(child = "")
click to toggle source
# File lib/pod_builder/core.rb, line 93 def self.buildpath_dsympath(child = "") if child.nil? return nil end path = "#{Configuration.build_path}/dSYM" if child.length > 0 path += "/#{child}" end return path end
buildpath_prebuiltpath(child = "")
click to toggle source
# File lib/pod_builder/core.rb, line 80 def self.buildpath_prebuiltpath(child = "") if child.nil? return nil end path = "#{Configuration.build_path}/Prebuilt" if child.length > 0 path += "/#{child}" end return path end
clean_basepath()
click to toggle source
# File lib/pod_builder/core.rb, line 169 def self.clean_basepath if path = PodBuilder::find_xcodeproj PodBuilder::safe_rm_rf(basepath(File.basename(path))) end if path = PodBuilder::find_xcodeworkspace PodBuilder::safe_rm_rf(basepath(File.basename(path))) end PodBuilder::safe_rm_rf(basepath("Pods")) end
dsympath(child = "")
click to toggle source
# File lib/pod_builder/core.rb, line 106 def self.dsympath(child = "") if child.nil? return nil end path = basepath("dSYM") if child.length > 0 path += "/#{child}" end return path end
enable_debug_information(project_path, configuration)
click to toggle source
# File lib/pod_builder/rome/post_install.rb, line 274 def self.enable_debug_information(project_path, configuration) project = Xcodeproj::Project.open(project_path) project.targets.each do |target| config = target.build_configurations.find { |config| config.name.eql? configuration } config.build_settings["DEBUG_INFORMATION_FORMAT"] = "dwarf-with-dsym" config.build_settings["ONLY_ACTIVE_ARCH"] = "NO" end project.save end
execute_command(executable, command, raise_on_failure = true, environmental_variables = {})
click to toggle source
Copy paste implementation from CocoaPods internals to be able to call poopen3 passing environmental variables
# File lib/pod_builder/rome/post_install.rb, line 233 def self.execute_command(executable, command, raise_on_failure = true, environmental_variables = {}) bin = Pod::Executable.which!(executable) command = command.map(&:to_s) full_command = "#{bin} #{command.join(' ')}" stdout = Pod::Executable::Indenter.new stderr = Pod::Executable::Indenter.new status = popen3(bin, command, stdout, stderr, environmental_variables) stdout = stdout.join stderr = stderr.join output = stdout + stderr unless status.success? if raise_on_failure raise "#{full_command}\n\n#{output}" else UI.message("[!] Failed: #{full_command}".red) end end output end
find_xcodeproj()
click to toggle source
# File lib/pod_builder/core.rb, line 125 def self.find_xcodeproj unless @@xcodeproj_path.nil? return @@xcodeproj_path end project_name = File.basename(find_xcodeworkspace, ".*") xcodeprojects = Dir.glob("#{home}/**/#{project_name}.xcodeproj").select { |x| folder_in_home = x.gsub(home, "") !folder_in_home.include?("/Pods/") && !x.include?(PodBuilder::basepath("Sources")) && !x.include?(PodBuilder::basepath + "/") } raise "\n\nxcodeproj not found!".red if xcodeprojects.count == 0 raise "\n\nFound multiple xcodeproj:\n#{xcodeprojects.join("\n")}".red if xcodeprojects.count > 1 @@xcodeproj_path = xcodeprojects.first return @@xcodeproj_path end
find_xcodeworkspace()
click to toggle source
# File lib/pod_builder/core.rb, line 142 def self.find_xcodeworkspace unless @@xcodeworkspace_path.nil? return @@xcodeworkspace_path end xcworkspaces = Dir.glob("#{home}/**/#{Configuration.project_name}*.xcworkspace").select { |x| folder_in_home = x.gsub(home, "") !folder_in_home.include?("/Pods/") && !x.include?(PodBuilder::basepath("Sources")) && !x.include?(PodBuilder::basepath + "/") && !x.include?(".xcodeproj/") } raise "\n\nxcworkspace not found!".red if xcworkspaces.count == 0 raise "\n\nFound multiple xcworkspaces:\n#{xcworkspaces.join("\n")}".red if xcworkspaces.count > 1 @@xcodeworkspace_path = xcworkspaces.first return @@xcodeworkspace_path end
git_rootpath()
click to toggle source
# File lib/pod_builder/core.rb, line 22 def self.git_rootpath return `git rev-parse --show-toplevel`.strip() end
gitignoredfiles()
click to toggle source
# File lib/pod_builder/core.rb, line 53 def self.gitignoredfiles Dir.chdir(git_rootpath) do return `git status --ignored -s | grep "^\!\!" | cut -c4-`.strip().split("\n") end end
merge_header_into(device_file, simulator_file)
click to toggle source
# File lib/pod_builder/rome/post_install.rb, line 174 def self.merge_header_into(device_file, simulator_file) unless File.exist?(device_file) || File.exist?(simulator_file) return end device_content = File.file?(device_file) ? File.read(device_file) : "" simulator_content = File.file?(simulator_file) ? File.read(simulator_file) : "" merged_content = %{ #if TARGET_OS_SIMULATOR // -> #{simulator_content} // -> #else // -> #{device_content} // -> #endif } File.write(device_file, merged_content) end
popen3(bin, command, stdout, stderr, environmental_variables)
click to toggle source
# File lib/pod_builder/rome/post_install.rb, line 257 def self.popen3(bin, command, stdout, stderr, environmental_variables) require 'open3' Open3.popen3(environmental_variables, bin, *command) do |i, o, e, t| Pod::Executable::reader(o, stdout) Pod::Executable::reader(e, stderr) i.close status = t.value o.flush e.flush sleep(0.01) status end end
prebuiltpath(child = "")
click to toggle source
# File lib/pod_builder/core.rb, line 67 def self.prebuiltpath(child = "") if child.nil? return nil end path = basepath("Prebuilt") if child.length > 0 path += "/#{child}" end return path end
prepare_basepath()
click to toggle source
# File lib/pod_builder/core.rb, line 158 def self.prepare_basepath workspace_path = PodBuilder::find_xcodeworkspace project_path = PodBuilder::find_xcodeproj if workspace_path && project_path FileUtils.mkdir_p(basepath("Pods/Target Support Files")) FileUtils.cp_r(workspace_path, basepath) FileUtils.cp_r(project_path, basepath) FileUtils.rm_f(basepath("Podfile.lock")) end end
project_path(child = "")
click to toggle source
# File lib/pod_builder/core.rb, line 119 def self.project_path(child = "") project = PodBuilder::find_xcodeworkspace return project ? "#{File.dirname(project)}/#{child}".gsub("//", "/").gsub(/\/$/, '') : nil end
remove_lockfile()
click to toggle source
# File lib/pod_builder/core.rb, line 207 def self.remove_lockfile lockfile_path = Configuration.lockfile_path if File.exist?(lockfile_path) FileUtils.rm(lockfile_path) end end
safe_rm_rf(path)
click to toggle source
# File lib/pod_builder/core.rb, line 26 def self.safe_rm_rf(path) unless File.exist?(path) return end unless File.directory?(path) FileUtils.rm(path) return end current_dir = Dir.pwd Dir.chdir(path) rootpath = git_rootpath() raise "\n\nNo git repository found in '#{path}', can't delete files!\n".red if rootpath.empty? && !path.start_with?(Configuration.build_base_path) FileUtils.rm_rf(path) if File.exist?(current_dir) Dir.chdir(current_dir) else Dir.chdir(basepath) end end
system_swift_version()
click to toggle source
# File lib/pod_builder/core.rb, line 180 def self.system_swift_version swift_version = `swiftc --version | grep -o 'swiftlang-.*\s'`.strip() raise "\n\nUnsupported swift compiler version, expecting `swiftlang` keyword in `swiftc --version`".red if swift_version.length == 0 return swift_version end
xcodebuild(sandbox, target, sdk='macosx', deployment_target=nil, configuration, deterministic_build, exclude_archs, prebuilt_root_paths)
click to toggle source
# File lib/pod_builder/rome/post_install.rb, line 209 def self.xcodebuild(sandbox, target, sdk='macosx', deployment_target=nil, configuration, deterministic_build, exclude_archs, prebuilt_root_paths) args = %W(-project #{sandbox.project_path.realdirpath} -scheme #{target} -configuration #{configuration} -sdk #{sdk}) supported_platforms = { 'iphonesimulator' => 'iOS', 'appletvsimulator' => 'tvOS', 'watchsimulator' => 'watchOS' } if platform = supported_platforms[sdk] args += Fourflusher::SimControl.new.destination(:oldest, platform, deployment_target) unless platform.nil? end xcodebuild_version = `xcodebuild -version | head -n1 | awk '{print $2}'`.strip().to_f if exclude_archs.count > 0 && xcodebuild_version >= 12.0 args += ["EXCLUDED_ARCHS=#{exclude_archs.join(" ")}"] end prebuilt_root_paths.each do |k, v| args += ["#{k.upcase.gsub("-", "_")}_PREBUILT_ROOT=#{v.gsub(/ /, '\ ')}"] end environmental_variables = {} if deterministic_build environmental_variables["ZERO_AR_DATE"] = "1" end execute_command 'xcodebuild', args, true, environmental_variables end
Private Class Methods
home()
click to toggle source
# File lib/pod_builder/core.rb, line 217 def self.home rootpath = git_rootpath raise "\n\nNo git repository found in current folder `#{Dir.pwd}`!\n".red if rootpath.empty? return rootpath end