测量所有目标(包括从属目标)的xcodebuild持续时间

时间:2017-11-13 18:18:28

标签: xcode profiling xcodebuild

是否可以测量时间,单个xcodebuild命令用于构建每个不同的目标?

假设我有一个目标,这取决于某些cocoapodpod1pod2。 我使用xcodebuild构建目标。我可以衡量整体时间。 我需要衡量在pod1pod2和我的目标

上单独花费的时间

我试图在xcodebuild的输出中找到答案,但未能这样做。

提前致谢!

1 个答案:

答案 0 :(得分:5)

我最终编写了一个自定义ruby脚本,用于修改xcodeprojPods.xcodeproj的每个目标。此脚本添加两个build phase,用于将目标名称和当前时间戳记录到输出文件中。一个build phase首先执行,另一个执行最后一个。稍后我只是在一个单独的脚本中减去另一个时间戳。

以下是脚本的结果:

/Users/tryusipov/Desktop/screenshots/Image 2017-11-17 18-23-49.png

输出文件将如下所示(排序后)

Alamofire end: 1510929112.3409
Alamofire start: 1510929110.2161
AlamofireImage end: 1510929113.6925
AlamofireImage start: 1510929112.5205

输出文件的路径(屏幕截图中的/a/ci_automation/metrics/performance-metrics/a.txt)无论如何都不是硬编码的。相反,您将其作为ruby脚本的参数传递,如下所示:

$ruby prepare-for-target-build-time-profiling.rb ${PWD}/output.txt

请注意,此脚本需要cocoapods 1.3.1(可能是1.3)。

以下是ruby脚本:ruby prepare-for-target-build-time-profiling.rb

#!/usr/bin/env ruby

require 'xcodeproj'
require 'cocoapods'
require 'fileutils'

def inject_build_time_profiling_build_phases(project_path)
    project = Xcodeproj::Project.open(project_path)

    log_time_before_build_phase_name = '[Prefix placeholder] Log time before build'.freeze
    log_time_after_build_phase_name = '[Prefix placeholder] Log time after build'.freeze

    puts "Patching project at path: #{project_path}"
    puts
    project.targets.each do |target|
        puts "Target: #{target.name}"

        first_build_phase = create_leading_build_phase(target, log_time_before_build_phase_name)
        last_build_phase = create_trailing_build_phase(target, log_time_after_build_phase_name)

        puts
    end

    project.save

    puts "Finished patching project at path: #{project_path}"
    puts
end

def create_leading_build_phase(target, build_phase_name)
    remove_existing_build_phase(target, build_phase_name)

    build_phase = create_build_phase(target, build_phase_name)

    shift_build_phase_leftwards(target, build_phase)

    is_build_phase_leading = true

    inject_shell_code_into_build_phase(target, build_phase, is_build_phase_leading)

    return build_phase
end

def create_trailing_build_phase(target, build_phase_name)
    remove_existing_build_phase(target, build_phase_name)

    build_phase = create_build_phase(target, build_phase_name)

    is_build_phase_leading = false

    inject_shell_code_into_build_phase(target, build_phase, is_build_phase_leading)

    return build_phase
end

def remove_existing_build_phase(target, build_phase_name)
    existing_build_phase = target.shell_script_build_phases.find do |build_phase|
        build_phase.name.end_with?(build_phase_name)
        # We use `end_with` instead of `==`, because `cocoapods` adds its `[CP]` prefix to a `build_phase_name`
    end

    if !existing_build_phase.nil?
        puts "deleting build phase #{existing_build_phase.name}"

        target.build_phases.delete(existing_build_phase)
    end
end

def create_build_phase(target, build_phase_name)
    puts "creating build phase: #{build_phase_name}"

    build_phase = Pod::Installer::UserProjectIntegrator::TargetIntegrator
        .create_or_update_build_phase(target, build_phase_name)

    return build_phase
end

def shift_build_phase_leftwards(target, build_phase)
    puts "moving build phase leftwards: #{build_phase.name}"

    target.build_phases.unshift(build_phase).uniq! unless target.build_phases.first == build_phase
end

def inject_shell_code_into_build_phase(target, build_phase, is_build_phase_leading)
    start_or_end = is_build_phase_leading ? "start" : "end"

    build_phase.shell_script = <<-SH.strip_heredoc
        timestamp=`echo "scale=4; $(gdate +%s%N/1000000000)" | bc`
        echo "#{target.name} #{start_or_end}: ${timestamp}" >> #{$build_time_logs_output_file}
    SH
end

def parse_arguments
    $build_time_logs_output_file = ARGV[0]

    if $build_time_logs_output_file.to_s.empty? || ! $build_time_logs_output_file.start_with?("/")
        puts "Error: you should pass a full path to a output file as an script's argument. Example:"
        puts "$ruby prepare-for-target-build-time-profiling.rb /path/to/script/output.txt"
        puts
        exit 1
    end
end

def print_arguments
    puts "Arguments:"
    puts "Output path: #{$build_time_logs_output_file}"
    puts
end

def clean_up_before_script
    if File.exist?($build_time_logs_output_file)
        FileUtils.rm($build_time_logs_output_file)
    end

    build_time_logs_output_folder = File.dirname($build_time_logs_output_file)
    unless File.directory?(build_time_logs_output_folder)
        FileUtils.mkdir_p(build_time_logs_output_folder)
    end
end

def main 
    parse_arguments
    print_arguments
    clean_up_before_script
    inject_build_time_profiling_build_phases("path/to/project.xcodeproj")
    inject_build_time_profiling_build_phases("path/to/pods/project.xcodeproj")
end

# arguments:
$build_time_logs_output_file

main