module Pod class Command class JxedtCommand < Command class Header < JxedtCommand self.summary = '修改头文件引用问题' self.description = <<-DESC 修改头文件引用问题\n 例如修改#import "AFNetworking.h"或#import 这种头文件引用方式为#import \n 使用方式:\n 到Podfile文件所在目录执行以下命令\n 1. 修改某个组件的头文件引用\n pod jxedt headerfix --name=PodA --dir=PodA RealPath --write\n 2. 修改工程的头文件引用\n pod jxedt headerfix --dir=project path --write\n DESC self.command = 'headerfix' self.arguments = [ ] def self.options [ ['--name', '需要修改header引用的pod name。没有该参数则会认为修改的是工程中的文件'], ['--dir', '需要修改的文件夹路径,一般情况下为pod组件的真实目录。如果修改的pod组件是development pods,也可以不传该参数。'], ['--write', '修改命中的头文件引用,不写入则直接输出需要修改的文件和修改内容。默认false'], ['--suffix', '处理的文件后缀,默认处理\'h,m,mm,pch\'后缀的文件'], ['--header-regulation', 'header name的正则,默认为\'[\w+-]*\.h\',如果不满足也可以自定义'], ['--verbose', '在屏幕上输出修改文件的详情'], ['--log-path', '需要修改文件内容导出路径'] ] end def initialize(argv) @pod_name = argv.option('name') @dir = argv.option('dir') @suffix = argv.option('suffix', 'h,m,mm,pch') @force_write = argv.flag?('write', false) @header_regulation = argv.option('header-regulation') @verbose_flag = argv.flag?('verbose', false) @log_path = argv.option('log-path') super end def validate! super help! 'A podspec name or dir is required.' if @pod_name.nil? && @dir.nil? end def run @installer = installer_for_config help! '请检查命令执行路径,需要在Podfile文件所在目录执行' if @installer.nil? @installer.sandbox.prepare # sandbox prepare @installer.resolve_dependencies # 解析依赖 # @installer.integrate # 不需要执行也可以解析依赖关系 process_target_files = process_target_files! dependent_targets = dependent_targets! # 查找 public_header_mapping = {} dependent_targets.each {|target| next unless target.should_build? # 获取所有.h文件 all_header_files = target.all_files.select {|file| file.to_s =~ /#{header_name_regulation}$/ } # 建立header文件和product_module_name的映射 public_header_mapping.merge! all_header_files.reduce({}) { |hash, file| basename = File.basename(file) hash.update(basename.to_s => target.product_module_name) } } # 遍历需要处理的文件 files, failed, record = [], [], [] process_target_files.each {|file_path| changed = false lines = File.readlines(file_path) # 获取命中的行 File.foreach(file_path).with_index {|line, num| matched = line =~ /^\s*#import\s*"#{header_name_regulation}"\s*\n$/ || line =~ /^\s*#import\s*<#{header_name_regulation}>\s*\n$/ next unless matched header_name = line.match(/(?<=")#{header_name_regulation}(?=")/) || line.match(/(?<=<)#{header_name_regulation}(?=>)/) next unless public_header_mapping.include?("#{header_name}") changed = true # 文件需要修改 project_module_name = public_header_mapping["#{header_name}"] replace_line = "#import " << "<#{project_module_name}/#{header_name}>\n" lines[num] = replace_line record << "#{file_path} 第#{num}行,#{line} => #{replace_line}" } files << file_path if changed begin File.open(file_path, 'w') { |f| f.write(lines.join) } if changed && @force_write rescue failed << file_path ensure help! "因权限问题文件修改失败:\n#{file_path}" if failed.size > 0 end } puts "需要修改文件共有:#{files.size} 个,需要修改的头文件的地方共有:#{record.size} 处" puts "已修改完成" if files.size > 0 && @force_write if files.size > 0 logs = <<~LOGS 需要修改的文件: #{JSON.pretty_generate(files)} 文件修改详情: #{JSON.pretty_generate(record)} LOGS logs.gsub!('\\"', '"').gsub!('\\n', '') # 去除转义字符 puts logs if @verbose_flag File.open(log_file_path, 'w') { |file| file.write("#{logs}") } unless log_file_path.nil? end end def installer_for_config config = Pod::Config.instance return if config.podfile.nil? Pod::Installer.new(config.sandbox, config.podfile, config.lockfile) end def header_name_regulation @regulation = @header_regulation || '[\w+-]*\.h' # header name的规则 [a-zA-Z0-9_+-] @regulation end def process_target_files! process_target_files = [] unless @dir.nil? def walk(path, &action) return unless path.exist? path.children.each do |child| result = action.call(child, &action) walk(child, &action) if result and child.directory? end end path = Pathname.new(@dir) walk(path) do |child| if child.directory? and [".framework", ".xcframework", ".bundle", ".dSYM"].include? child.extname next false elsif child.file? suffix = @suffix.split(',').join('|') process_target_files << (path + child) if child.to_s =~ /.*\.(#{suffix})$/ next true else next true end end else process_target_files = @installer.pod_targets.flat_map {|target| suffix = @suffix.split(',').join('|') target.all_files.select {|file| file.to_s =~ /.*\.(#{suffix})$/ } if target.pod_name.to_s == @pod_name }.reject(&:nil?).uniq end process_target_files end def dependent_targets! dependent_targets = @installer.pod_targets unless @pod_name.nil? # 递归查找依赖 def recursion_dependent_targets(target) target.dependent_targets.flat_map {|t| targets = [t] targets += recursion_dependent_targets(t) if target.dependent_targets.size > 0 targets }.reject(&:nil?) end # 获取依赖 dependent_targets = @installer.pod_targets.flat_map {|target| recursion_dependent_targets(target) if target.pod_name.to_s == @pod_name }.reject(&:nil?).uniq end dependent_targets end def log_file_path log_path = @log_path log_path << "/headerfix_output.txt" if log_path && File.directory?(log_path) return if log_path && (!File.exist?(File.dirname(log_path))) log_path end end end end end