class CacheManager

Public Instance Methods

add_cache(target, target_info) click to toggle source
# File lib/kcache.rb, line 410
def add_cache(target, target_info)
    target_md5 = target_info[:target_md5]
    product_dir = target_info[CONFIGURATION_BUILD_DIR]
    full_product_name = target_info[FULL_PRODUCT_NAME]

    Dir.glob("#{product_dir}/**/*.modulemap").each do | modulemap |
    modulemap_content = File.read(modulemap)
        if modulemap_content.include? File.dirname(modulemap) + "/"
            modulemap_content = modulemap_content.gsub(File.dirname(modulemap) + "/", "")
            File.write(modulemap, modulemap_content)
        end
    end

    unless full_product_name and full_product_name.size > 0 and File.exist? "#{product_dir}/#{full_product_name}"
        puts "<ERROR> #{target.name} #{product_dir}/#{full_product_name} should exist"
        return false
    end

    zip_start_time = Time.now

    command = "cd \"#{File.dirname(product_dir)}\" && tar -L -c -f #{target.name}.#{FILE_NAME_PRODUCT} #{File.basename(product_dir)}/#{full_product_name}"
    if target.product_type == "com.apple.product-type.library.static"
        command = "cd \"#{File.dirname(product_dir)}\" && tar --exclude=*.bundle --exclude=*.framework -L -c -f #{target.name}.#{FILE_NAME_PRODUCT} #{File.basename(product_dir)}"
    end
    
    unless system command
        puts "<INFO> #{command} should succeed"
        return false
    end

    already_target_cache_dirs = Dir.glob(get_cache_root + "/" + target.name + "-" + target_md5 + "-*")
    already_target_cache_dirs.each do |path|
        if File.exist? path
            raise unless system "rm -rf \"#{path}\""
        end
    end
    
    target_cache_dir = get_cache_root + "/" + target.name + "-" + target_md5 + "-" + (Time.now.to_f * 1000).to_i.to_s
    if File.exist? target_cache_dir
        puts "<INFO> #{target_cache_dir} should not exist"
        raise unless system "rm -rf \"#{target_cache_dir}\""
        return false
    end

    command = "mkdir -p \"#{target_cache_dir}\""
    unless system command
        puts "<INFO> #{command} should succeed"
        return false
    end

    cache_product_path = target_cache_dir + "/#{FILE_NAME_PRODUCT}"
    command = "mv \"#{File.dirname(product_dir)}/#{target.name}.#{FILE_NAME_PRODUCT}\" \"#{cache_product_path}\""
    unless system command
        puts "<INFO> #{command} should succeed"
        return false
    end
    unless File.exist? cache_product_path
         puts "<INFO> #{cache_product_path} should exist after mv"
        return false
    end

    target_info[:product_md5] = get_file_md5(cache_product_path)
    target_info[:build_product_dir] = target_info[CONFIGURATION_BUILD_DIR].gsub(target_info[SYMROOT] + "/", "")
    target_info[:build_intermediate_dir] = target_info[TARGET_TEMP_DIR].gsub(target_info[OBJROOT] + "/", "")
    if target_info[MODULEMAP_FILE]
        target_info[MODULEMAP_FILE] = get_content_without_pwd(target_info[MODULEMAP_FILE])
    end

    target_info = target_info.clone
    target_info.delete(:dependency_files)
    target_info.delete(:target_status)
    target_info.delete(:hit_target_cache_dir)
    target_info.delete(:target_md5_content)
    [SYMROOT, CONFIGURATION_BUILD_DIR, CONFIGURATION_TEMP_DIR, OBJROOT, TARGET_TEMP_DIR, TARGET_BUILD_DIR, PODS_XCFRAMEWORKS_BUILD_DIR, SRCROOT].each do | key |
        target_info.delete(key)
    end

    File.write(target_cache_dir + "/" + FILE_NAME_CONTEXT, target_info.to_yaml)
    
    return true

end
backup_project(project) click to toggle source
# File lib/kcache.rb, line 87
def backup_project(project)
    command = "cp \"#{project.path}/project.pbxproj\" \"#{project.path}/ysTest_backup.pbxproj\""
    raise unless system command
end
can_cache_target(target) click to toggle source
# File lib/kcache.rb, line 108
def can_cache_target(target)
    if target.product_type == "com.apple.product-type.bundle" or
        target.product_type == "com.apple.product-type.library.static" or
        target.product_type == "com.apple.product-type.framework"
        return true
    end
    return false
end
clean_temp_files() click to toggle source
# File lib/kcache.rb, line 92
def clean_temp_files
    # command = "rm -rf Pods/*.xcodeproj/ysTest_backup.pbxproj"
    # raise unless system command

    command = "rm -rf Pods/*.xcodeproj/*.#{FILE_NAME_TARGET_CONTEXT}"
    raise unless system command
end
copy_cache() click to toggle source
# File lib/kcache.rb, line 373
def copy_cache

    puts "<INFO> #{Time.now.to_f.to_s}"
    start_time = Time.now
    target_cache_dir = ARGV[1]
    cache_product_path = target_cache_dir + "/#{FILE_NAME_PRODUCT}"

    [SYMROOT, CONFIGURATION_BUILD_DIR, CONFIGURATION_TEMP_DIR, OBJROOT, TARGET_BUILD_DIR, TARGET_TEMP_DIR, SRCROOT, FULL_PRODUCT_NAME].sort.each do | key |
        unless ENV.has_key? key and ENV[key] and ENV[key].size > 0
            raise "<INFO> #{$0} should have #{key}"
            break
        end
    end

    command = "mkdir -p \"#{ENV[CONFIGURATION_BUILD_DIR]}\" && cd \"#{File.dirname(ENV[CONFIGURATION_BUILD_DIR])}/\" && tar -xf \"#{cache_product_path}\""
    raise unless system command

    if ENV[CONFIGURATION_BUILD_DIR] != ENV[TARGET_BUILD_DIR]

        command = "rm -rf \"#{ENV[TARGET_BUILD_DIR]+"/"+ENV[FULL_PRODUCT_NAME]}\""
        raise unless system command

        command = "mkdir -p \"#{File.dirname(ENV[TARGET_BUILD_DIR]+"/"+ENV[FULL_PRODUCT_NAME])}\""
        raise unless system command

        command = "mv \"#{ENV[CONFIGURATION_BUILD_DIR]+"/"+ENV[FULL_PRODUCT_NAME]}\" \"#{ENV[TARGET_BUILD_DIR]+"/"+ENV[FULL_PRODUCT_NAME]}\""
        raise unless system command

        command = "/bin/ln -sfh \"#{ENV[TARGET_BUILD_DIR]+"/"+ENV[FULL_PRODUCT_NAME]}\" \"#{ENV[CONFIGURATION_BUILD_DIR]+"/"+ENV[FULL_PRODUCT_NAME]}\""
        raise unless system command
    end 

    puts "<INFO> duration = #{((Time.now - start_time)*1000).to_i} ms in copy cache action"
    puts "<INFO> #{Time.now.to_f.to_s}"
end
generate_target_all_infomation(project, target, source_files) click to toggle source
# File lib/kcache.rb, line 184
def generate_target_all_infomation(project, target, source_files)

    if $podfile_spec_checksums == nil and File.exist? "Podfile.lock"
        podfile_lock = YAML.load(File.read("Podfile.lock"))
        $podfile_spec_checksums = podfile_lock["SPEC CHECKSUMS"]
    end

    project_configuration = project.build_configurations.detect { | config | config.name == $target_build_configuration}
    project_configuration_content = project_configuration.pretty_print.to_yaml

    project_xcconfig = ""
    if project_configuration.base_configuration_reference
        config_file_path = project_configuration.base_configuration_reference.real_path
        if File.exist? config_file_path
            project_xcconfig = File.read(config_file_path).lines.reject{|line|line.include? "_SEARCH_PATHS"}.sort.join("")
        end
    end

    target_configuration = target.build_configurations.detect { | config | config.name == $target_build_configuration}
    target_configuration_content = target_configuration.pretty_print.to_yaml

    target_xcconfig = ""
    if target_configuration.base_configuration_reference
        config_file_path = target_configuration.base_configuration_reference.real_path
        if File.exist? config_file_path
            target_xcconfig = File.read(config_file_path).lines.reject{|line|line.include? "_SEARCH_PATHS"}.sort.join("")
        end
    end


    files_configuration = []
    build_phases = []
    build_phases.push target.source_build_phase if target.methods.include? :source_build_phase
    build_phases.push target.resources_build_phase if target.methods.include? :resources_build_phase
    build_phases.each do | build_phase |
        target.source_build_phase.files_references.each do | files_reference |
            files_reference.build_files.each do |build_file|
                if build_file.settings and build_file.settings.class == Hash
                    files_configuration.push File.basename(build_file.file_ref.real_path.to_s) + "\n" + build_file.settings.to_yaml
                end
            end
        end
    end
    files_configuration = files_configuration.sort.uniq.join("\n")


    source_md5_list = []
    has_found_checksum = false
    split_parts = target.name.split("-")
    split_parts.each_with_index do | part, index |
        spec_name = split_parts[0..index].join("-")
        if $podfile_spec_checksums.has_key? spec_name
            source_md5_list.push "SPEC CHECKSUM : #{spec_name} #{$podfile_spec_checksums[spec_name]}"
            has_found_checksum = true
        end
    end

    if has_found_checksum
        if target.name.start_with? "AK" or target.name.start_with? "EUR"

            source_md5_list.push "Project : #{File.basename(project.path)}"
            source_md5_list.push "Project configuration : "
            source_md5_list.push project_configuration_content.strip
            source_md5_list.push "Project xcconfig : "
            source_md5_list.push project_xcconfig.strip

            source_md5_list.push "Target : #{target.name}, #{target.product_type}"
            source_md5_list.push "Target configuration : "
            source_md5_list.push target_configuration_content.strip
            source_md5_list.push "Target xcconfig : "
            source_md5_list.push target_xcconfig.strip
            source_md5_list.push "Files settings : "
            source_md5_list.push files_configuration.strip

            source_md5_list.push "Files MD5 : "
            source_files.uniq.sort.each do | file |
                path =  get_content_without_pwd(file)
                if target.name == "AKULocalizedStrings" or target.name == "EURLocalizedStrings"
                    if file.include? ".swift"
                        source_md5_list.push path + " : " + get_file_md5(path)
                    end
                else
                    source_md5_list.push path + " : " + get_file_md5(path)
                end
            end
        else
            source_md5_list.push "Target configuration: #{$target_build_configuration}"    
        end
        
        source_md5_content = source_md5_list.join("\n")
        return source_md5_content

    end
end
get_cache_root() click to toggle source
# File lib/kcache.rb, line 45
def get_cache_root
    return Dir.home + "/ysCache"
end
get_content_without_pwd(content) click to toggle source
# File lib/kcache.rb, line 49
def get_content_without_pwd(content)
    content = content.gsub("#{Dir.pwd}/", "").gsub(/#{Dir.pwd}(\W|$)/, '\1')
    return content
end
get_custom_config_information() click to toggle source
# File lib/kcache.rb, line 493
def get_custom_config_information
    if File.exist? "project_cache_config.yml"
        custom_config = YAML.load (File.read("project_cache_config.yml"))
        $exclude_target = custom_config[:exclude_targets]
    else
        source = {
            :description => nil,
            :dependency_check => true,
            :exclude_targets => ["target1", "target2"],
        }.to_yaml

        substitution_list = {
            /:description:/ => "# You can assign values to these parameters to achieve some custom functions\n",
            /:dependency_check:/ => "\n\n# Whether to check the dependencies of the target. For exzample: target A depends on target B. Under normal circumstances, target B is recompiled, then target A will be recompiled, but if the dependencies are not checked, target A will not be recompiled at this time\n:dependency_check:",
            /:exclude_targets:/ => "\n\n# the targets that will not participate in cache dependency detection\n:exclude_targets:",
        }

        substitution_list.each do |pattern, replacement|
          source.gsub!(pattern, replacement)
        end
        puts source
        File.write("project_cache_config.yml", source)
    end

end
get_file_md5(file) click to toggle source
# File lib/kcache.rb, line 54
def get_file_md5(file)
    if $file_md5_hash.has_key? file
        return $file_md5_hash[file]
    end
    md5 = Digest::MD5.hexdigest(File.read(file))
    $file_md5_hash[file] = md5
    return md5
end
get_projects() click to toggle source
# File lib/kcache.rb, line 64
def get_projects
    pods_project = Xcodeproj::Project.open("Pods/Pods.xcodeproj")
    super_project_paths = get_super_project(pods_project)
    super_projects = []
    super_project_paths.each do | path |
        next if path.end_with? "Pods/Pods.xcodeproj"
        project = Xcodeproj::Project.open(path)
        super_projects.push project
    end
    return (super_projects + [pods_project])
end
get_super_project(project) click to toggle source
# File lib/kcache.rb, line 76
def get_super_project(project)
    wrapper_projects = project.files.select{|file|file.last_known_file_type=="wrapper.pb-project"}
    wrapper_project_paths = []
    wrapper_projects.each do | wrapper_project_file |
        wrapper_project_file_path = wrapper_project_file.real_path.to_s
        wrapper_project_paths.push wrapper_project_file_path
    end
    return wrapper_project_paths.uniq
end
get_target_cache(target, target_md5) click to toggle source
# File lib/kcache.rb, line 281
def get_target_cache(target, target_md5)

    dependency_start_time = Time.now

    target_cache_dirs = Dir.glob(get_cache_root + "/" + target.name + "-" + target_md5 + "-*")

    hit_results = []

    target_cache_dirs.each do |target_cache_dir|
        unless File.exist? target_cache_dir + "/" + FILE_NAME_PRODUCT
            puts "<ERROR> #{target.name} target cache dir missed files: #{target_cache_dir}"
            next
        end
        unless File.exist? target_cache_dir + "/" + FILE_NAME_CONTEXT
            puts "<ERROR> #{target.name} target cache dir missed files: #{target_cache_dir}"
            next
        end

        target_context = YAML.load(File.read(target_cache_dir + "/" + FILE_NAME_CONTEXT))

        if target_context[:target_md5] != target_md5 or target_context[:product_md5] != get_file_md5(target_cache_dir + "/" + FILE_NAME_PRODUCT)
            command = "rm -rf \"#{target_cache_dir}\""
            raise unless system command
            puts "<ERROR> #{target.name} target md5 does not match:  #{target_cache_dir}"
        end

        dependency_exit = true
        
        if target_context[:dependency_files_md5] 
            target_context[:dependency_files_md5].each do | item |
                dependency_file = item[0]
                dependency_md5 = item[1]
                unless File.exist? dependency_file
                    puts "<WARNING> #{target.name} dependency file miss: #{dependency_file}"
                    dependency_exit = false
                    break
                end
                unless get_file_md5(dependency_file) == dependency_md5
                    puts "<WARNING> #{target.name} dependency file md5 does not match: #{dependency_file}"
                    dependency_exit = false
                    break
                end
            end
        end

        if dependency_exit
            hit_results.push target_cache_dir
        end
    end

    return hit_results

end
get_target_dependency_files(target, intermediate_dir, product_dir, xcframeworks_build_dir, src_path) click to toggle source
# File lib/kcache.rb, line 117
def get_target_dependency_files(target, intermediate_dir, product_dir, xcframeworks_build_dir, src_path)
    dependency_files = []
    Dir.glob("#{intermediate_dir}/**/*.d").each do | dependency_file |
        content = File.read(dependency_file)
        files = content.scan(/(?:\S(?:\\ )*)+/).flatten.uniq
        files = files - ["dependencies:", "\\", ":"]
        files.each do | file |
            file = file.gsub("\\ ", " ")
            if file.start_with? target.name
                file = src_path + "/" + file
            end
            unless File.exist? file
                puts "<ERROR> #{target.name} #{file} should exist in dependency file #{dependency_file}"
                return []
            end

            if file.start_with? intermediate_dir + "/" or 
                file.start_with? product_dir + "/"
                next
            end

            if xcframeworks_build_dir and xcframeworks_build_dir.size > 0 and file.start_with? xcframeworks_build_dir + "/"
                next
            end
            dependency_files.push file
        end
    end
    
    return dependency_files.uniq
end
get_target_source_files(target) click to toggle source
# File lib/kcache.rb, line 149
def get_target_source_files(target)
    files = []
    #获取所有可执行文件(.m .swift .cpp .mm)
    target.source_build_phase.files.each do | file |
        file_path = file.file_ref.real_path.to_s
        files.push file_path
    end

    #获取所有头文件(.h)
    target.headers_build_phase.files.each do | file |
        file_path = file.file_ref.real_path.to_s
        files.push file_path
    end
    #获取所有资源文件(.png,.strings .json .html)
    target.resources_build_phase.files.each do | file |
        file_path = file.file_ref.real_path.to_s
        files.push file_path
    end
    expand_files = []
    files.uniq.each do | file |
        next unless File.exist? file
        if File.file? file
            expand_files.push file
        else
            Find.find(file).each do | file_in_dir |
                if File.file? file_in_dir
                    expand_files.push file_in_dir
                end
            end
        end
    end
    return expand_files.uniq
end
inject_copy_action(project, target, target_context) click to toggle source
# File lib/kcache.rb, line 358
 def inject_copy_action(project, target, target_context)
    target_cache_dir = target_context[:hit_target_cache_dir]

    target.build_phases.delete_if { | build_phase | 
        build_phase.class == Xcodeproj::Project::Object::PBXHeadersBuildPhase or 
        build_phase.class == Xcodeproj::Project::Object::PBXSourcesBuildPhase or 
        build_phase.class == Xcodeproj::Project::Object::PBXResourcesBuildPhase
    }

    command_exec = "\"#{$0}\""        
    inject_phase = target.new_shell_script_build_phase("ys_copy_#{target.name}")
    inject_phase.shell_script = "#{command_exec} #{"copy"} \"#{target_cache_dir}\""
    inject_phase.show_env_vars_in_log = '1'
end
inject_flag_action(project, target) click to toggle source
# File lib/kcache.rb, line 351
def inject_flag_action(project, target)
    command_exec = "\"#{$0}\""        
    inject_phase = target.new_shell_script_build_phase("ys_inject_#{target.name}")
    inject_phase.shell_script = "#{command_exec} #{"inject"} #{target.name} \"#{project.path}\""
    inject_phase.show_env_vars_in_log = '1'
end
project_task_begin(argv) click to toggle source
# File lib/kcache.rb, line 520
def project_task_begin(argv)

    argv.each do |prama|
        if prama.include? "configuration" 
            if prama.include? "Debug"
                $target_build_configuration = "Debug"
            elsif prama.include? "Release"
                $target_build_configuration = "Release"
            end
        end
    end

    projects = get_projects
    total_count = 0
    hit_count = 0
    miss_count = 0
    error_count = 0
    hit_target_md5_cache_set = Set.new
    miss_target_cache_set = Set.new

    get_custom_config_information

    pre_targets_info = {}

    projects.each do |project|
        restore_project(project)
        backup_project(project)
    end

    clean_temp_files 
    
    projects = get_projects

    projects.each do |project|
        project.native_targets.each do |target|
            if can_cache_target(target)
                total_count = total_count + 1
                source_files = get_target_source_files(target)
                target_md5_content = generate_target_all_infomation(project, target, source_files)
                unless target_md5_content
                    puts "<ERROR> target md5 content can not generate: #{target.name}"
                    error_count = error_count + 1
                    next
                end

                target_md5 = Digest::MD5.hexdigest(target_md5_content)
                hit_target_cache_dirs = get_target_cache(target, target_md5)
                target_info = {}
                target_info[:target_md5] = target_md5

                if hit_target_cache_dirs.count == 0
                    puts "<INFO> #{target.name} #{target_md5} does not hit any cache"
                    target_info[:target_status] = CACHE_STATUS_MISS
                    inject_flag_action(project, target)
                    miss_count = miss_count + 1
                    File.write("#{project.path}/#{target.name}.#{FILE_NAME_TARGET_CONTEXT}", target_info.to_yaml)
                else
                    target_info[:hit_target_cache_dir] = hit_target_cache_dirs
                    hit_target_md5_cache_set.add "#{target.name}-#{target_info[:target_md5]}"
                end
                pre_targets_info[target] = target_info
            end
        end
    end
      
    while true
        projects.each do |project|
            project.native_targets.each do |target|
                target_info = pre_targets_info[target]
                next unless target_info and target_info[:target_status] != CACHE_STATUS_MISS
                next unless target_info and target_info[:target_status] != CACHE_STATUS_HIT
                hit_target_cache_dirs = target_info[:hit_target_cache_dir]
                next unless hit_target_cache_dirs and hit_target_cache_dirs.count > 0
                target_md5 = target_info[:target_md5]
                
                target_really_hit_dir = ""
                target_really_miss = false
                hit_target_cache_dirs.each do |hit_target_cache_dir|
                    all_dependency_target_exist = true
                    target_really_miss = false
                    hit_targets_info = YAML.load(File.read(hit_target_cache_dir + "/" + FILE_NAME_CONTEXT))
                    if hit_targets_info[:dependency_targets_md5]
                        hit_targets_info[:dependency_targets_md5].each do | item |
                            dependency_target = item[0]
                            dependency_target_md5 = item[1]
                            if $exclude_target.include? dependency_target 
                                puts "<INFO> #{target.name} #{target_md5} hit cache, dependency target #{dependency_target}-#{dependency_target_md5} skip check"
                                next
                            end
                            
                            if miss_target_cache_set.include? "#{dependency_target}"
                                puts "<INFO> #{target.name} #{target_md5} hit cache, but dependency target #{dependency_target}-#{dependency_target_md5} does not hit"
                                target_really_miss = true
                                all_dependency_target_exist = false
                                break
                            end

                            unless hit_target_md5_cache_set.include? "#{dependency_target}-#{dependency_target_md5}"
                                puts "<INFO> #{target.name} #{target_md5} hit cache, but dependency target #{dependency_target}-#{dependency_target_md5} does not hit"
                                all_dependency_target_exist = false
                                target_really_miss = true
                                break
                            end
                        end
                        
                        if all_dependency_target_exist
                            target_really_hit_dir = hit_target_cache_dir
                            break
                        end

                        if target_really_miss
                            break
                        end
                    end
                end
    
                if target_really_hit_dir.length > 0
                    puts "<INFO> #{target.name} #{target_md5} hit cache"
                    hit_count = hit_count + 1
                    hit_target_info = YAML.load(File.read(target_really_hit_dir + "/" + FILE_NAME_CONTEXT))
                    target_info = target_info.merge!(hit_target_info)
                    target_info[:target_status] = CACHE_STATUS_HIT
                    target_info[:hit_target_cache_dir] = target_really_hit_dir
                    inject_copy_action(project, target, target_info)
                end

                if target_really_miss 
                    miss_count = miss_count + 1
                    miss_target_cache_set.add "#{target.name}"
                    hit_target_md5_cache_set.delete "#{target.name}-#{target_info[:target_md5]}"
                    target_info[:target_status] = CACHE_STATUS_MISS
                    target_info.delete(:hit_target_cache_dir)
                    inject_flag_action(project, target)
                end

                File.write("#{project.path}/#{target.name}.#{FILE_NAME_TARGET_CONTEXT}", target_info.to_yaml)
            end
            project.save
        end
        if total_count == hit_count + miss_count + error_count
            break
        else
            puts "<INFO> continue to find hit cache"
        end
    end
    puts "<INFO> total count: #{total_count}, hit count: #{hit_count}, miss_count: #{miss_count}, error_count: #{error_count}"
end
project_task_end() click to toggle source
# File lib/kcache.rb, line 669
def project_task_end
    
    projects = get_projects
    post_targets_context = {}
    total_add_count = 0

    projects.each do |project|
        project.native_targets.each do | target |
            target_context_file = "#{project.path}/#{target.name}.#{FILE_NAME_TARGET_CONTEXT}"
            next unless File.exist? target_context_file
            if can_cache_target(target)
                target_info = YAML.load(File.read(target_context_file))
                if target_info[:target_status] == CACHE_STATUS_READY
                    # 保证编译前后MD5不变
                    source_files = get_target_source_files(target)
                    target_md5_content = generate_target_all_infomation(project, target, source_files)
                    target_md5 = Digest::MD5.hexdigest(target_md5_content)
                    unless target_info[:target_md5] == target_md5
                        puts "<ERROR> #{target.name} md5 should not be changed after build"
                        next
                    end
    
                    target_info[:target_md5_content] = target_md5_content
    
                    product_dir = target_info[CONFIGURATION_BUILD_DIR]
                    intermediate_dir = target_info[TARGET_TEMP_DIR].to_s
                    xcframeworks_build_dir = target_info[PODS_XCFRAMEWORKS_BUILD_DIR]
                    src_path = target_info[SRCROOT]
    
                    unless intermediate_dir.length > 0
                        puts "<ERROR> #{target.name} should have intermediate_dir"
                        next
                    end
                    dependency_files = get_target_dependency_files(target, intermediate_dir, product_dir, xcframeworks_build_dir, src_path)
                    if source_files.size > 0 and dependency_files.size == 0 and target.product_type != "com.apple.product-type.bundle"
                        puts "<ERROR> #{target.name} should have dependency files"
                        next
                    end
                    target_info[:dependency_files] = dependency_files - source_files
    
                    module_file_path = target_info[MODULEMAP_FILE]
    
                    if src_path and src_path.size > 0 and module_file_path and module_file_path.size > 0
                        if File.exist? Dir.pwd + "/" + get_content_without_pwd("#{src_path}/#{module_file_path}")
                            target_info[MODULEMAP_FILE] = get_content_without_pwd("#{src_path}/#{module_file_path}")
                        else
                            puts "<ERROR> #{target.name} #{module_file_path} should be supported"
                            next
                        end
                    end
                elsif target_info[:target_status] == CACHE_STATUS_HIT
                    module_file_path = target_info[MODULEMAP_FILE]
                    if module_file_path and module_file_path.size > 0
                        if not File.exist? Dir.pwd + "/" + target_info[MODULEMAP_FILE]
                            puts "<ERROR> #{target.name} #{module_file_path} should be supported"
                            next
                        end
                    end
                end
                post_targets_context[target] = target_info
    
            end
        end
    end

    projects.each do |project|
        project.native_targets.each do | target |
            if post_targets_context.has_key? target
                target_info = post_targets_context[target]
                next unless target_info[:target_status] == CACHE_STATUS_READY
              
                dependency_targets_set = Set.new
                implicit_dependencies = []
                
                post_targets_context.each do | other_target, other_target_context |
                    next if target == other_target
                    next if target.product_type == "com.apple.product-type.bundle"
                    next if other_target.product_type == "com.apple.product-type.bundle"
    
                    configuration_build_dir = other_target_context[CONFIGURATION_BUILD_DIR]
                    target_temp_dir = other_target_context[TARGET_TEMP_DIR]
                    build_product_dir = other_target_context[:build_product_dir]
                    build_intermediate_dir = other_target_context[:build_intermediate_dir]
    
                    target_info[:dependency_files].each do | dependency |
                        if configuration_build_dir and configuration_build_dir.size > 0 and dependency.start_with? configuration_build_dir + "/"
                            dependency_targets_set.add other_target
                            implicit_dependencies.push dependency
                        end
    
                        if target_temp_dir and target_temp_dir.size > 0 and dependency.start_with? target_temp_dir + "/"
                            dependency_targets_set.add other_target
                            implicit_dependencies.push dependency
                        end
    
                        if build_product_dir and build_product_dir.size > 0 and dependency.start_with? target_info[SYMROOT] + "/" + build_product_dir + "/"
                            dependency_targets_set.add other_target
                            implicit_dependencies.push dependency
                        end
    
                        if build_intermediate_dir and build_intermediate_dir.size > 0 and dependency.start_with? target_info[OBJROOT] + "/" + build_intermediate_dir + "/"
                            dependency_targets_set.add other_target
                            implicit_dependencies.push dependency
                        end
    
                    end
                    target_info[:dependency_files] = target_info[:dependency_files] - implicit_dependencies
    
                end
                target_info[:dependency_files] = target_info[:dependency_files] - implicit_dependencies
    
                dependency_files_md5 = []
    
                should_cache = true
                if target_info and target_info[:dependency_files].size > 0
                    target_info[:dependency_files].each do | file |
                        if file.start_with? target_info[OBJROOT] + "/" or file.start_with? target_info[SYMROOT] + "/"
                            puts "#{target.name} #{file} dependecy should not include build path"
                            should_cache = false
                            break
                        end
                        dependency_files_md5.push [get_content_without_pwd(file), get_file_md5(file)]
                    end
                end
                next unless should_cache
    
                target_info[:dependency_files_md5] = dependency_files_md5.sort.uniq
                dependency_targets_md5 = dependency_targets_set.to_a.map { | target |  [target.name, post_targets_context[target][:target_md5]]}
                target_info[:dependency_targets_md5] = dependency_targets_md5
    
                if add_cache(target, target_info)
                    puts  "<INFO> #{target} is being added to cache dir"
                    total_add_count = total_add_count + 1
                    target.build_phases.delete_if { |phase|
                        phase.class == Xcodeproj::Project::Object::PBXShellScriptBuildPhase and phase.name.include? "ys_"
                    }
                end
    
            end
        end
        restore_project(project)
    end

    puts "<INFO> total add cache count: #{total_add_count}"
end
restore_project(project) click to toggle source
# File lib/kcache.rb, line 100
def restore_project(project)
    if File.exist? "#{project.path}/ysTest_backup.pbxproj"
        command = "mv \"#{project.path}/ysTest_backup.pbxproj\" \"#{project.path}/project.pbxproj\""
        raise unless system command
    end
end
set_prama_to_yaml() click to toggle source
# File lib/kcache.rb, line 335
def set_prama_to_yaml
    target_name = ARGV[1]
    project_path = ARGV[2]
    if File.exist? "#{project_path}/#{target_name}.#{FILE_NAME_TARGET_CONTEXT}" 
        target_context = YAML.load(File.read("#{project_path}/#{target_name}.#{FILE_NAME_TARGET_CONTEXT}"))

        [SYMROOT, OBJROOT, SRCROOT, CONFIGURATION_BUILD_DIR, CONFIGURATION_TEMP_DIR, TARGET_BUILD_DIR, TARGET_TEMP_DIR, PODS_XCFRAMEWORKS_BUILD_DIR, MODULEMAP_FILE, FULL_PRODUCT_NAME].sort.each do | key |
            if ENV[key]
                target_context[key] = ENV[key] 
            end
        end
        target_context[:target_status] = CACHE_STATUS_READY
        File.write("#{project_path}/#{target_name}.#{FILE_NAME_TARGET_CONTEXT}", target_context.to_yaml)
    end
end