├── .gitignore ├── .ruby-version ├── CODE_OF_CONDUCT.md ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── LICENSE.txt ├── README.md ├── Rakefile ├── bin └── itools ├── itools.gemspec └── lib ├── itools.rb └── itools ├── class_unuse_finder.rb ├── count_code_line.rb ├── file_handle.rb ├── find_unuse_img.rb ├── get_size.rb ├── git_sets.rb ├── link_map.rb ├── podfile_tiled.rb ├── string_searcher.rb ├── temple-commit-msg.dat └── version.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | /.config 4 | /coverage/ 5 | /InstalledFiles 6 | /pkg/ 7 | /spec/reports/ 8 | /spec/examples.txt 9 | /test/tmp/ 10 | /test/version_tmp/ 11 | /tmp/ 12 | /pkg/ 13 | # Used by dotenv library to load environment variables. 14 | # .env 15 | 16 | ## Specific to RubyMotion: 17 | .dat* 18 | .repl_history 19 | build/ 20 | *.bridgesupport 21 | build-iPhoneOS/ 22 | build-iPhoneSimulator/ 23 | 24 | ## Specific to RubyMotion (use of CocoaPods): 25 | # 26 | # We recommend against adding the Pods directory to your .gitignore. However 27 | # you should judge for yourself, the pros and cons are mentioned at: 28 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 29 | # 30 | # vendor/Pods/ 31 | 32 | ## Documentation cache and generated files: 33 | /.yardoc/ 34 | /_yardoc/ 35 | /doc/ 36 | /rdoc/ 37 | 38 | ## Environment normalization: 39 | /.bundle/ 40 | /vendor/bundle 41 | /lib/bundler/man/ 42 | 43 | # for a library or gem, you might want to ignore these files since the code is 44 | # intended to run in multiple environments; otherwise, check them in: 45 | # Gemfile.lock 46 | # .ruby-version 47 | # .ruby-gemset 48 | 49 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: 50 | .rvmrc 51 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | ruby-2.4.0 -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at scottzg@126.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } 4 | 5 | # Specify your gem's dependencies in itools.gemspec 6 | gemspec -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | itools (1.2.0) 5 | bundler (>= 2.2.33) 6 | git (~> 1.5) 7 | gli (~> 2.17) 8 | rake (>= 12.3.3) 9 | spreadsheet (~> 1.1, >= 1.1.8) 10 | 11 | GEM 12 | remote: https://rubygems.org/ 13 | specs: 14 | git (1.10.1) 15 | rchardet (~> 1.8) 16 | gli (2.20.1) 17 | rake (13.0.6) 18 | rchardet (1.8.0) 19 | ruby-ole (1.2.12.2) 20 | spreadsheet (1.3.0) 21 | ruby-ole 22 | 23 | PLATFORMS 24 | x86_64-darwin-20 25 | 26 | DEPENDENCIES 27 | itools! 28 | 29 | BUNDLED WITH 30 | 2.3.4 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Zhang Gui 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 张贵 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # itools 2 | 3 | 一个方便iOS开发的工具类。主要是使用Ruby编写,用于进行一些方便的处理,例如字符串查找,LinkMap解析等。 4 | ## 安装 5 | 6 | 7 | ```ruby 8 | gem install itools 9 | ``` 10 | ## 说明 11 | 该命令执行的时间与文件夹的大小以及文件多少有关。所以如果有执行停留问题,请耐心等待。 12 | ## 使用 13 | 安装之后再终端执行itools,结果如下: 14 | ```shell 15 | NAME 16 | itools - a collection of tools for ios developer 17 | 18 | SYNOPSIS 19 | itools [global options] command [command options] [arguments...] 20 | 21 | VERSION 22 | 1.0.0 23 | 24 | GLOBAL OPTIONS 25 | --help - Show this message 26 | --version - Display the program version 27 | 28 | COMMANDS 29 | count_code_line - 统计代码行数 30 | find - 查找无用图片 31 | help - Shows a list of commands or help for one command 32 | parse - 解析linkmap 33 | pre_commit - 通过执行该命令,hook 本地commit,然后进行规范化 34 | search - 在文件夹或文件中查找字符串(或者字符串组) 35 | search_file - 查找文件 36 | search_unuse_class - 查找无用类 37 | size_for - 计算文件或者文件夹占用内存大小 38 | ``` 39 | 40 | ### 功能1:解析LinkMap 41 | 命令: 42 | ```shell 43 | itools parse LinkMapDemo-LinkMap-normal-arm64.txt (分析文件夹) 44 | # or 45 | itools parse /user/Desk/LinkMapDemo-LinkMap-normal-arm64.txt 46 | ``` 47 | 解析结果实例: 48 | ```ruby 49 | AppDelegate.o 8.50KB 50 | ViewController.o 735B 51 | LinkMapDemo.app-Simulated.xcent 386B 52 | main.o 192B 53 | linker synthesized 128B 54 | libobjc.tbd 120B 55 | Foundation.tbd 24B 56 | UIKit.tbd 24B 57 | Total Size:10.07KB 58 | ``` 59 | For more information, see:[https://www.cnblogs.com/zhanggui/p/9991455.html](https://www.cnblogs.com/zhanggui/p/9991455.html) 60 | 也可以根据linkmap按照folder解析,即传入第二个参数为分析的引用库目录,例如想查看AFNetWorking中各个文件夹(内容)所占二进制的大小。 61 | ### 功能2:字符串查找 62 | 命令 63 | ```ruby 64 | itools search folder/file str/strs 65 | ``` 66 | 例如: 67 | 1.单字符查找:查找number.txt中包含7的行 68 | ```ruby 69 | itools search number.txt 7 70 | ``` 71 | 将会把number.txt中包含7的行输出,内容包括:文件名(这里是number.txt)、包含字符串(这里是7)、文件所在目录、查找内容所在行、查找结果。 72 | 2.多字符查找:查找number.txt中包含7,8的行 73 | ```ruby 74 | itools search number.txt 7,8 75 | ``` 76 | 将会把number.txt中包含7、8的行记录到excel表中,表内容和上面的展示一样。 77 | 3.在文件夹所有文件中查找某个字符串: 78 | ```ruby 79 | itools search /Users/zhanggui/zhanggui/my-dev MAYGO 80 | ``` 81 | 将会把/Users/zhanggui/zhanggui/my-dev文件夹中所有的文件进行遍历,然后找到包含MAYGO字符串的类,并生成excel文件。 82 | 4.在文件夹所有文件文件中查找某些字符串: 83 | ```ruby 84 | itools search /Users/zhanggui/zhanggui/Ruby/ 3434,Scott 85 | ``` 86 | 将会在/Users/zhanggui/zhanggui/Ruby/中查找所有包含3434和Scott的文件,并生成excel。 87 | 88 | ### 功能3:查找工程中无用的图片 89 | 命令 90 | ``` 91 | itools find /Users/zhanggui/zhanggui/tdp 92 | ``` 93 | 这里的目录代表项目的根目录,查找原理为: 94 | 先将目录下面所有的图片(仅支持png、jpg、gif)找到,然后遍历所有.m文件。查找出没有使用的图片。 95 | **注:这里查找不太准确,仅供参考,因为有可能有的图片不是通过.m文件使用的。而有的图片仅仅是为了配置(例如1024*1024),所以还是不要依赖该工具的图片查找,找到之后可以自行再次确认一下。** 96 | 97 | ### 功能4:计算文件大小 98 | 命令 99 | ``` 100 | itools size_for /Users/zhanggui/zhanggui/my 1000 101 | or 102 | itools size_for /Users/zhanggui/zhanggui/my 103 | ``` 104 | 计算sizeFor后面跟的参数内容所占内存大小,如果参数为文件路径,则计算文件大小,如果是文件夹,会遍历所有文件,然后计算大小。第二个参数为计算系数(这个系数为1MB = 1024KB中的1024;windows为1024,mac为1000,不传默认为1024)。 105 | 在中途会提示你输入要查找的文件后缀,不输入任何则表示查找文件夹下的所有文件,输入后缀则会计算特定文件类型包含的大小,例如:png,jpg,gif,这样会计算出文件夹中三种类型格式的图片所占有内存的大小。 106 | 107 | ### 功能5:查找文件 108 | 命令 109 | ``` 110 | itools search_file /Users/zhanggui/zhanggui/my-dev/search_vc ViewController.m #第二个参数现在只支持单字符串查找 111 | ``` 112 | 查找/Users/zhanggui/zhanggui/my-dev/search_vc文件夹下所有的文件名包含ViewController.m的文件,并且输出到excel表格 113 | 114 | ### 功能6:查找工程中无用的文件 115 | 命令 116 | ``` 117 | itools search_unuse_class /Users/zhanggui/zhanggui/my-dev/search_vc 118 | ``` 119 | 参数为工程所在的文件夹,例如/Users/zhanggui/zhanggui/my-dev/search_vc。查出的结果可能包含category或者extension,请拿结果作为参考,不作为最终要删除的文件。 120 | 121 | ### 功能7:统计代码行数 122 | 命令 123 | ``` 124 | itools count_code_line 文件路径/文件夹路径 125 | #例如 126 | itools count_code_line /User/zhanggui/mydemoapp #统计mydemoapp项目的代码行数 127 | ``` 128 | 该工具只统计了.m、.mm、.h、.cpp这几个文件,并且不包含单行注释以及空行。 129 | ## Contributing 130 | 131 | Bug reports and pull requests are welcome on GitHub at https://github.com/ScottZg/itools. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the 132 | 133 | ## FBI Warning 134 | 如有误删而导致的线上问题,本人概不负责! 135 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | task :default => :spec 3 | -------------------------------------------------------------------------------- /bin/itools: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'gli' 3 | require 'itools' 4 | 5 | include GLI::App 6 | 7 | program_desc 'a collection of tools for ios developer' 8 | version Itools::VERSION 9 | 10 | desc "解析linkmap" 11 | arg 'xxx.txt' 12 | command :parse do |c| 13 | c.action do |global_options, options, args| 14 | if args.size == 1 15 | Itools::LinkMap.parser(args[0]) 16 | else 17 | Itools::LinkMap.parser_by_folder(args) 18 | end 19 | 20 | end 21 | end 22 | 23 | desc "在文件夹或文件中查找字符串(或者字符串组)" 24 | arg 'xxx.txt' 25 | command :search do |c| 26 | c.action do |global_options, options, args| 27 | Itools::StringSearcher.search_result(args[0],args[1]) 28 | end 29 | end 30 | # 查找工程中没有用到的图片 31 | desc "查找无用图片" 32 | arg 'xxx.txt' 33 | command :find do |c| 34 | c.action do |global_options, options, args| 35 | Itools::ImgFinder.find(args[0]) 36 | end 37 | end 38 | # 查找Xcode工程中没有用到的类 39 | desc "查找无用类" 40 | arg 'xxx.txt' 41 | command :search_unuse_class do |c| 42 | c.action do |global_options, options, args| 43 | Itools::ClassFinder.search_unuse_class(args) 44 | end 45 | end 46 | 47 | # 计算占用内存大小 48 | desc "计算文件或者文件夹占用内存大小" 49 | arg 'xxx.txt' 50 | command :size_for do |c| 51 | c.action do |global_options, options, args| 52 | Itools::Memory.sizeFor(args) 53 | end 54 | end 55 | 56 | # 查找文件 57 | desc "查找文件" 58 | arg 'file name' 59 | command :search_file do |c| 60 | c.action do |global_options, options, args| 61 | Itools::FileSearcher.searchFile(args) 62 | end 63 | end 64 | 65 | # 统计代码行数 66 | desc "统计代码行数" 67 | arg 'file name or folder' 68 | command :count_code_line do |c| 69 | c.action do |global_options, options, args| 70 | Itools::CodeCouner.count_line(args) 71 | end 72 | end 73 | 74 | #本地commit规范化 75 | desc "通过执行该命令,hook 本地commit,然后进行规范化" 76 | arg 'git项目根目录' 77 | command :pre_commit do |c| 78 | c.action do |global_options,options, args| 79 | Itools::GitSets.commit_msg_init(args) 80 | end 81 | end 82 | #本地commit规范化 83 | desc "通过执行该命令,可以将podfile.lock中所有的依赖平铺至Podifle中" 84 | arg 'podfile.lock' 85 | command :podfile_tiled do |c| 86 | c.action do |global_options,options, args| 87 | Itools::PodfileTiled.podfile_tiled(args) 88 | end 89 | end 90 | exit run(ARGV) -------------------------------------------------------------------------------- /itools.gemspec: -------------------------------------------------------------------------------- 1 | 2 | lib = File.expand_path("../lib", __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require "itools/version" 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "itools" 8 | spec.version = Itools::VERSION 9 | spec.authors = ["zhanggui"] 10 | spec.email = ["scottzg@126.com"] 11 | 12 | spec.summary = %q{iOS tools} 13 | spec.description = %q{iOS dev tools } 14 | spec.homepage = "https://github.com/ScottZg/itools" 15 | spec.license = "MIT" 16 | 17 | 18 | if spec.respond_to?(:metadata) 19 | spec.metadata["allowed_push_host"] = "https://rubygems.org" 20 | 21 | spec.metadata["homepage_uri"] = spec.homepage 22 | spec.metadata["source_code_uri"] = "https://github.com/ScottZg/itools" 23 | spec.metadata["changelog_uri"] = "https://github.com/ScottZg/itools/blob/master/README.md" 24 | else 25 | raise "RubyGems 2.0 or newer is required to protect against " \ 26 | "public gem pushes." 27 | end 28 | 29 | # Specify which files should be added to the gem when it is released. 30 | # The `git ls-files -z` loads the files in the RubyGem that have been added into git. 31 | spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do 32 | `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } 33 | end 34 | spec.bindir = "bin" 35 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 36 | spec.require_paths = ["lib"] 37 | 38 | spec.add_runtime_dependency "bundler", ">= 2.2.33" 39 | spec.add_runtime_dependency "rake", ">= 12.3.3" 40 | spec.add_runtime_dependency "gli", "~> 2.17" 41 | spec.add_runtime_dependency "spreadsheet", '~> 1.1', '>= 1.1.8' 42 | spec.add_runtime_dependency 'git', '~> 1.5' 43 | end 44 | -------------------------------------------------------------------------------- /lib/itools.rb: -------------------------------------------------------------------------------- 1 | require "itools/version" 2 | require "itools/link_map" 3 | require "itools/string_searcher" 4 | require "itools/find_unuse_img" 5 | require "itools/get_size" 6 | require "itools/file_handle" 7 | require "itools/class_unuse_finder" 8 | require "itools/count_code_line" 9 | require "itools/git_sets" 10 | require "itools/podfile_tiled" -------------------------------------------------------------------------------- /lib/itools/class_unuse_finder.rb: -------------------------------------------------------------------------------- 1 | require 'find' 2 | module Itools 3 | class ClassFinder 4 | attr_accessor :search_path, :classes, :search_in_files 5 | def initialize(temp_search_path) 6 | @search_path = temp_search_path 7 | @classes = [] 8 | @search_in_files = [] 9 | end 10 | def search 11 | # 找到所有的.h以及所有要查找的文件 12 | Find.find(@search_path) do |path| 13 | if File.file?(path) 14 | if !get_not_contain_file_ext.include?(File.extname(path)) 15 | @search_in_files << path 16 | end 17 | 18 | if File.extname(path).eql?(".h") 19 | ff_result = ClassFindResult.new(File.basename(path,".h"),path) 20 | @classes << ff_result 21 | end 22 | end 23 | end 24 | 25 | # 删除使用的文件 26 | use_idxs = Set.new 27 | @search_in_files.each{|s_file| 28 | s_containet = "" 29 | File.read(s_file).each_line do |line| 30 | s_containet << line 31 | s_containet << "," 32 | end 33 | # 查找所有文件在单个文件中是否被引用 34 | @classes.each_with_index{|f_result,idx| 35 | search_file_no_ext = get_no_ext_path(s_file) 36 | check_file_no_ext = get_no_ext_path(f_result.fr_path) 37 | # 判断是否是同一个文件或者是通文件的.m/.h,不是同一个文件才查找 38 | if !check_file_no_ext.eql?(search_file_no_ext) 39 | inheritance_str = ": #{f_result.fr_name}" 40 | contain_str = '@"' + f_result.fr_name + '"' 41 | reference_str = "#{f_result.fr_name}.h" 42 | 43 | # if s_containet.match(/: #{f_result.fr_name}|@"#{f_result.fr_name}"|#{f_result.fr_name}.h/) != nil 44 | if s_containet.include?(inheritance_str) or s_containet.include?(contain_str) or s_containet.include?(reference_str) 45 | use_idxs << f_result 46 | puts "#{f_result.fr_name}已使用,剩余查找文件数#{@classes.size - use_idxs.size}..." 47 | end 48 | end 49 | } 50 | 51 | } 52 | final_result = [] 53 | 54 | temp_final_result_str = '' 55 | use_idxs.to_a.each {|u_x| 56 | temp_final_result_str << u_x.fr_name 57 | temp_final_result_str << "," 58 | } 59 | @classes.delete_if {|find_result| temp_final_result_str.include?(find_result.fr_name) } 60 | puts "\033[32m查找结束,共同无用文件#{@classes.size}个,如下:\033[0m" 61 | Spreadsheet.client_encoding = 'utf-8' 62 | book = Spreadsheet::Workbook.new 63 | sheet1 = book.create_worksheet 64 | sheet1.row(0)[0] = "序号" 65 | sheet1.row(0)[1] = "文件名" 66 | sheet1.row(0)[2] = "文件路径" 67 | sheet1.row(0)[3] = "文件占用内存大小" 68 | total_size = 0 69 | @classes.each_with_index{|r_item,f_index| 70 | # if !r_item.fr_name.include?("+") 71 | total_size = total_size + File.size(r_item.fr_path) 72 | # end 73 | puts r_item.fr_name 74 | sheet1.row(f_index+1)[0] = f_index + 1 75 | sheet1.row(f_index+1)[1] = r_item.fr_name 76 | sheet1.row(f_index+1)[2] = r_item.fr_path 77 | sheet1.row(f_index+1).height = 20 78 | } 79 | sheet1.column(0).width = 4 80 | sheet1.column(1).width = 45 81 | sheet1.column(2).width = 100 82 | book.write "#{@search_path}/search_unuseclass_result.xls" 83 | puts "\033[32m文件已经保存到#{@search_path}/search_unuseclass_result.xls,无用文件#{@classes.size}个,预计可减少内存占用#{handleSize(total_size)}\033[0m" 84 | end 85 | # 大小格式化 86 | def handleSize(size) 87 | if size > 1024 * 1024 88 | return format("%.2f",(size.to_f/(1024*1024))) + "MB" 89 | elsif size > 1024 90 | return format("%.2f",(size.to_f/1024)) + "KB" 91 | else 92 | return size.to_s + "B" 93 | end 94 | end 95 | # 不包含后缀的路径 96 | def get_no_ext_path(item) 97 | return File.dirname(item) + "/" + File.basename(item,".*") 98 | end 99 | # 不需要查找的类 100 | def get_not_contain_file_ext 101 | nc_ext = [".jpg",".png",".md",".xls",".xcworkspace",".DS_Store",""] 102 | return nc_ext 103 | end 104 | # 对外暴露 105 | def self.search_unuse_class(args) 106 | folder_path = args[0] 107 | if folder_path.nil? 108 | puts "\033[31m传入的参数不能为空\033[0m" 109 | return 110 | end 111 | if !File::directory?(folder_path) 112 | puts "\033[31m参数不是文件夹\033[0m" 113 | return 114 | end 115 | class_finder = ClassFinder.new(folder_path) 116 | class_finder.search 117 | end 118 | end 119 | 120 | # ------------------------查找结果类------------------------ 121 | 122 | class ClassFindResult 123 | attr_accessor :fr_name, :fr_path 124 | def initialize(temp_name,temp_path) 125 | @fr_name = temp_name 126 | @fr_path = temp_path 127 | end 128 | end 129 | end -------------------------------------------------------------------------------- /lib/itools/count_code_line.rb: -------------------------------------------------------------------------------- 1 | require 'find' 2 | module Itools 3 | class CodeCouner 4 | attr_accessor :file_path, :line_number 5 | def initialize(path) 6 | @file_path = path 7 | @line_number = 0 8 | end 9 | # 统计行数 10 | def calculate_line_number 11 | puts "\033[32m正在统计,请稍后...\033[0m" 12 | if File.file?(@file_path) 13 | File.read(@file_path).each_line do |line| 14 | if line.match(/^\/\/|^$/) == nil #去掉单行注释和空行 15 | @line_number = @line_number + 1 16 | end 17 | end 18 | 19 | return 20 | end 21 | if File::directory?(@file_path) 22 | Find.find(@file_path) do |file| 23 | if File.file?(file) #判断是否是文件 24 | if File.extname(file).match(/^.[hm]m?$|.cpp/) #只统计.h/.m/.mm/.cpp几个文件 25 | File.read(file).each_line do |line| 26 | if line.match(/^\/\/|^$/) == nil #去掉单行注释和空行 27 | @line_number = @line_number + 1 28 | end 29 | end 30 | end 31 | end 32 | end 33 | return 34 | end 35 | puts "\033[31m找不到指定路径的文件或者文件夹,请重新输入路径\033[0m" 36 | end 37 | 38 | def self.count_line(args) 39 | file = args[0] 40 | if file.nil? 41 | puts "\033[31m参数异常,请传入一个参数(项目目录/要统计的文件目录/要统计的文件)\033[0m" 42 | return 43 | end 44 | counter = CodeCouner.new(file) 45 | counter.calculate_line_number 46 | puts "\033[32m统计#{counter.file_path}结束,共#{counter.line_number}行\033[0m" 47 | end 48 | end 49 | end -------------------------------------------------------------------------------- /lib/itools/file_handle.rb: -------------------------------------------------------------------------------- 1 | require 'find' 2 | require 'spreadsheet' 3 | module Itools 4 | class FileResult 5 | attr_accessor :keyword, :file_path, :file_name 6 | def initialize(temp_path,temp_name) 7 | @file_path = temp_path 8 | @file_name = temp_name 9 | end 10 | end 11 | class FileSearcher 12 | # path:搜索的路径,files要搜索的文件,支持数组用逗号隔开即可。支持模糊搜索 13 | attr_accessor :path ,:files, :search_result 14 | def initialize(temp_path,temp_files) 15 | @path = temp_path 16 | @files = temp_files 17 | @search_result = [] 18 | end 19 | def search 20 | puts "\033[32m开始查找...\033[0m" 21 | if File::directory?(@path) 22 | Find.find(@path) do |file| 23 | if File.file?(file) 24 | file_name = File.basename(file) 25 | if file_name.include?(@files) 26 | fr = FileResult.new(file,file_name) 27 | @search_result << fr 28 | end 29 | else 30 | # puts "查找#{file}..." 31 | end 32 | end 33 | else 34 | puts "\033[31m文件夹有误,请输入文件夹路径作为第一个参数\033[0m" 35 | end 36 | end 37 | # 对外暴露方法 38 | def self.searchFile(args) 39 | path = args[0] 40 | files = args[1] 41 | if path.nil? || files.nil? 42 | puts "\033[31m参数异常,请传入两个参数,第一个为路径,第二个为要搜索的文件名\033[0m" 43 | return 44 | end 45 | # temp_files = files.split(",") 46 | file_searcher = FileSearcher.new(path,files) 47 | file_searcher.search 48 | if file_searcher.search_result.size == 0 49 | puts "\033[32m没有找到符合条件的文件\033[0m" 50 | return 51 | end 52 | # 输出 53 | # 输出搜索的内容 54 | 55 | Spreadsheet.client_encoding = 'utf-8' 56 | book = Spreadsheet::Workbook.new 57 | sheet1 = book.create_worksheet 58 | sheet1.row(0)[0] = "序号" 59 | sheet1.row(0)[1] = "文件名" 60 | sheet1.row(0)[2] = "文件所在路径" 61 | 62 | 63 | puts "\033[32m找到共#{file_searcher.search_result.size}个文件结果如下;\033[0m" 64 | file_searcher.search_result.each_with_index {|item,i| 65 | puts item.file_name 66 | sheet1.row(i+1)[0] = i + 1 67 | sheet1.row(i+1)[1] = item.file_name 68 | sheet1.row(i+1)[2] = item.file_path 69 | sheet1.row(i+1).height = 20 70 | } 71 | sheet1.column(0).width = 4 72 | sheet1.column(1).width = 45 73 | sheet1.column(2).width = 100 74 | book.write "#{File.dirname(path)}/search_#{files}_result.xls" 75 | puts "\033[32m查找成功,共#{file_searcher.search_result.size}个文件,内容已经保存到#{File.dirname(path)}/search_#{files}_result.xls,请点击查看\033[0m" 76 | end 77 | end 78 | end -------------------------------------------------------------------------------- /lib/itools/find_unuse_img.rb: -------------------------------------------------------------------------------- 1 | require 'find' 2 | require 'spreadsheet' 3 | module Itools 4 | class FindResult 5 | attr_accessor :name , :path 6 | def initialize(name,path) 7 | @name = name 8 | @path = path 9 | end 10 | 11 | end 12 | # -------------------------------------------- 13 | class ImgFinder 14 | # 15 | attr_accessor :image_count, :images, :unuse_images,:find_path 16 | attr_accessor :search_files 17 | def initialize 18 | @image_count = 0 19 | @images = [] 20 | @search_files = [] 21 | end 22 | # 得到所有图片名称字符 23 | def get_img_name_strs 24 | result_arr = [] 25 | @images.each {|item| 26 | item_name = Image.get_image_name(File.basename(item.name, ".*")) 27 | result_arr << item_name 28 | } 29 | return result_arr 30 | end 31 | def get_image_path(image) 32 | @images.each {|item| 33 | if item.name.eql?(image) 34 | return item.path 35 | end 36 | } 37 | end 38 | # 查找 39 | def self.find(temp_find_dir) 40 | imgFinder = ImgFinder.new 41 | imgFinder.find_path = temp_find_dir 42 | # 第一步:找到该文件夹下所有的图片文件 43 | Find.find(temp_find_dir) do |filename| 44 | if File.file?(filename) #如果是文件,则从文件中查找,忽略文件夹 45 | if Image.is_image_format(File.extname(filename)) 46 | # p File.basename(filename) 47 | # exit 48 | imgFinder.image_count = imgFinder.image_count + 1 49 | imageResult = FindResult.new(Image.get_image_name(File.basename(filename,".*")),filename) 50 | imgFinder.images << imageResult 51 | elsif File.extname(filename).eql?(".m") || File.extname(filename).eql?(".swift") 52 | imgFinder.search_files << filename 53 | end 54 | end 55 | end 56 | if imgFinder.images.size == 0 57 | puts "\033[32m查找成功,未发现图片\033[0m" 58 | return 59 | else 60 | puts "\033[32m查找成功,共发现图片#{imgFinder.images.size}张\033[0m" 61 | end 62 | # 第二步:找到图片是否使用 63 | imags = imgFinder.get_img_name_strs.uniq #要查找的图片名称数组 64 | 65 | puts "\033[32m需要查找的图片有#{imags.size}张\033[0m" 66 | # imgFinder.search_files #要查找的文件 67 | imgFinder.search_files.each {|file| 68 | File.read(file).each_line do |line| 69 | haveStr = StringHandle.containsStr(line,imags) 70 | if haveStr != -1 71 | puts "#{imags[haveStr]}在使用...,剩余查找项#{imags.size-1}个" 72 | imags.delete_at(haveStr) 73 | end 74 | end 75 | } 76 | puts "\033[32m无用图片#{imags.size}张,图片名称如下:\033[0m" 77 | unuse_total_size = 0 78 | 79 | Spreadsheet.client_encoding = 'utf-8' 80 | book = Spreadsheet::Workbook.new 81 | sheet1 = book.create_worksheet 82 | sheet1.row(0)[0] = "文件名" 83 | sheet1.row(0)[1] = "文件路径" 84 | sheet1.row(0)[2] = "文件大小(B)" 85 | imags.each_with_index {|item,idx| 86 | sheet1.row(idx+1)[0] = item 87 | path = imgFinder.get_image_path(item) 88 | sheet1.row(idx+1)[1] = path 89 | unuse_total_size = unuse_total_size + File.size(path) 90 | sheet1.row(idx+1)[2] = File.size(path) 91 | puts item 92 | } 93 | book.write "#{imgFinder.find_path}/search_result.xls" 94 | puts "\033[32m文件已经保存到#{imgFinder.find_path}/search_result.xls,无用图片大小:#{unuse_total_size}B\033[0m" 95 | puts "\033[32m内容仅供参考,具体还要自己通过结果查看一下\033[0m" 96 | end 97 | end 98 | # 字符串操作类 99 | class StringHandle 100 | # originStr中是否包含targetStrs中的内容 101 | def self.containsStr(originStr,targetStrs) 102 | targetStrs.each_with_index {|item,idx| 103 | if originStr.include?(item) 104 | return idx 105 | end 106 | } 107 | return -1 108 | end 109 | end 110 | # ---------------------------- 111 | class Image 112 | 113 | # 是否是图片格式,这里只判断了jpg、png和gif 114 | def self.is_image_format(temp_ext_name) 115 | if ['.jpg','.png','.gif'].include?(temp_ext_name) 116 | return true 117 | else 118 | return false 119 | end 120 | end 121 | def self.get_image_name(file) 122 | return file.gsub(/@2x|@3x/,"") 123 | end 124 | end 125 | # class ObjectiveC 126 | # def self.is_h_file(temp_ext_name) 127 | # if ['.h'] 128 | 129 | # end 130 | # end 131 | # end 132 | end 133 | -------------------------------------------------------------------------------- /lib/itools/get_size.rb: -------------------------------------------------------------------------------- 1 | require 'find' 2 | module Itools 3 | class Memory 4 | attr_accessor :pro 5 | # 分发吹 6 | def hand_cal_size(file,prop) 7 | if prop.nil? 8 | @pro = 1024 9 | elsif prop == 0 10 | @pro = 1024 11 | else 12 | @pro = prop 13 | end 14 | handle_method = '' 15 | if File.file?(file) 16 | puts "\033[32m开始计算文件的大小...\033[0m" 17 | handle_method = 'cal_file' 18 | elsif File::directory?(file) 19 | handle_method = 'cal_folder' 20 | puts "\033[32m开始计算文件夹的大小...\033[0m" 21 | else 22 | puts "\033[31m参数异常,请确保传入的第一个参数是文件路径或者文件夹路径\033[0m" 23 | return 24 | end 25 | self.send(handle_method,file) 26 | end 27 | # 计算单个文件 28 | def cal_file(file) 29 | puts "\033[32m文件的大小为:#{get_show_size(File.size(file))}.\033[0m" 30 | end 31 | # 计算整个文件夹 32 | def cal_folder(folder) 33 | print "\033[32m请输入要查找文件后缀\033[0m(例如想文件夹中图片大小则输入:{png,jpg,gif},不输入则默认计算文件夹下所有文件大小之和):" 34 | file_exts_string = STDIN.gets 35 | file_exts_string.chomp! #过滤换行符 36 | if file_exts_string.size == 0 37 | file_exts = [] 38 | else 39 | file_exts = file_exts_string.split(",") 40 | end 41 | sum = 0 42 | file_count = 0 43 | total_count = 0 44 | total_size = 0 45 | file_size = 0 46 | Find.find(folder) do |filename| 47 | if File.file?(filename) 48 | total_count = total_count + 1 49 | total_size = total_size + File.size(filename) 50 | if file_exts.size == 0 #说明计算所有文件 51 | sum = sum + File.size(filename) 52 | file_count = file_count + 1 53 | elsif file_exts.include?(File.extname(filename).delete(".")) #查找指定后缀的文件 54 | sum = sum + File.size(filename) 55 | file_count = file_count + 1 56 | file_size = file_size + File.size(filename) 57 | else 58 | #不做任何处理 59 | end 60 | end 61 | end 62 | if file_exts.size == 0 63 | puts "\033[32m文件夹中共#{total_count}个文件,共#{get_show_size(total_size)}(#{total_size})\033[0m" 64 | else 65 | puts "\033[32m文件夹中共#{total_count}个文件,共#{get_show_size(total_size)}(#{total_size});找到后缀为(#{file_exts_string})的文件#{file_count}个,共#{get_show_size(file_size)}(#{file_size}).\033[0m" 66 | end 67 | 68 | # puts `du -b #{folder} | awk '{print $1}'`.to_i 69 | end 70 | # get_show_size 71 | def get_show_size(size) 72 | if size > @pro * @pro * @pro 73 | return format("%.2f",(size.to_f/(@pro*@pro*@pro))) + "GB" 74 | elsif size > @pro * @pro 75 | return format("%.2f",(size.to_f/(@pro*@pro))) + "MB" 76 | elsif size > @pro 77 | return format("%.2f",(size.to_f/@pro)) + "KB" 78 | else 79 | return size.to_s + "B" 80 | end 81 | end 82 | # 对外暴露方法 83 | def self.sizeFor(proport) 84 | file = proport[0] 85 | pro = proport[1].to_i 86 | if file.nil? 87 | puts "\033[31m参数异常,请传入一个参数\033[0m" 88 | return 89 | end 90 | memory = Memory.new 91 | memory.hand_cal_size(file,pro) 92 | end 93 | end 94 | end -------------------------------------------------------------------------------- /lib/itools/git_sets.rb: -------------------------------------------------------------------------------- 1 | module Itools 2 | class GitSets 3 | def self.commit_msg_init(args) 4 | puts "\033[32m开始配置...\033[0m" 5 | system('touch ./.git/commit_msg') 6 | system('git config commit.template "`pwd`/.git/commit_msg"') 7 | a = <<-EOF 8 | #!/bin/sh 9 | echo "$(git symbolic-ref --short HEAD) subject" > `pwd`/.git/commit_msg 10 | echo "" >> `pwd`/.git/commit_msg 11 | echo "Cause:" >> `pwd`/.git/commit_msg 12 | echo "Solution:" >> `pwd`/.git/commit_msg 13 | echo "Releated Doc Address:" >> `pwd`/.git/commit_msg 14 | echo '''\n#TYPE类型\n#新功能 feature\n#bug修复 bugfix\n#性能优化 perf\n#代码重构 refactor\n#线上修复 hotfix\n#发布版本 release\n#文档变更 docs''' >> `pwd`/.git/commit_msg 15 | EOF 16 | File.open('.git/hooks/pre-commit', 'w') do |f| 17 | f.puts a 18 | end 19 | logo_path = File.join( File.dirname(__FILE__), 'temple-commit-msg.dat' ) 20 | content = File.read( logo_path ) 21 | File.open('.git/hooks/commit-msg', 'w') do |f| 22 | f.puts content 23 | end 24 | system('chmod a+x .git/hooks/pre-commit') 25 | system('chmod a+x .git/hooks/commit-msg') 26 | puts "\033[32m配置成功,后续请直接使用git commit ,不要加 -m\033[0m" 27 | end 28 | end 29 | end -------------------------------------------------------------------------------- /lib/itools/link_map.rb: -------------------------------------------------------------------------------- 1 | require 'fileutils' 2 | require 'pathname' 3 | require 'find' 4 | require 'spreadsheet' 5 | module Itools 6 | # ---------------------------------ObjectFile class--------------------------------- 7 | class ObjectFile 8 | # file_size:单个文件的大小 9 | # o_name:某个文件的名字 10 | # o_size: 某个o文件的大小 11 | attr_accessor :serial_number, :file_path, :file_name, :file_size, :o_name, :o_size 12 | def initialize() 13 | @serial_number = [] 14 | @file_size = 0 15 | @o_size = 0 16 | end 17 | 18 | end 19 | # ---------------------------------Sections class--------------------------------- 20 | class Sections 21 | attr_accessor :address, :size, :segment, :section 22 | 23 | end 24 | # ---------------------------------Symbols class--------------------------------- 25 | class Symbols 26 | attr_accessor :s_address, :s_size, :s_file_serial_number, :s_name 27 | def initialize 28 | @s_file_serial_number = -1 #防止为0 29 | end 30 | end 31 | 32 | # ---------------------------------DSSymbols class--------------------------------- 33 | class DSSymbols 34 | attr_accessor :size, :file, :name 35 | end 36 | 37 | # ---------------------------------LinkMap class--------------------------------- 38 | class LinkMap 39 | # 包含的属性 40 | attr_accessor :l_name, :l_path, :l_arch, :l_obj_files, :l_sections, :l_symbols, :l_dead_stripped_symbols, :l_sym_map 41 | # 初始化方法 42 | def initialize(fName) 43 | @l_name = fName 44 | @l_obj_files = [] 45 | @l_symbols = [] 46 | @l_sections = [] 47 | @l_dead_stripped_symbols = [] 48 | end 49 | # 得到path 50 | def get_path(str) 51 | splitparam = str.split(" ") 52 | @l_path = splitparam.last 53 | end 54 | # 处理object文件 55 | def handle_ojbect_files(str) 56 | tempSplit = str.split("]") 57 | if tempSplit.size > 1 58 | obj_file = ObjectFile.new 59 | obj_file.serial_number = tempSplit[0].delete("[").strip.to_i #设置serial_number 60 | obj_file.file_path = tempSplit[1].strip 61 | obj_file.file_name = tempSplit[1].split("/").last.chomp 62 | obj_file.o_name = get_o_name(tempSplit[1].split("/").last.chomp) 63 | l_obj_files << obj_file 64 | end 65 | end 66 | # 得到o_ame 有待优化,可以使用正则表达式处理TODO 67 | def get_o_name(str) 68 | temp_arr = str.split("(") 69 | if temp_arr.size > 1 70 | temp_arr[1].split(".o)")[0] 71 | else 72 | return temp_arr[0].split(".o")[0] 73 | end 74 | end 75 | # 处理sections 76 | def handle_sections(str) 77 | sectionSplit = str.split(" ") 78 | if sectionSplit.size == 4 79 | section = Sections.new 80 | section.address = sectionSplit[0] 81 | section.size = sectionSplit[1] 82 | section.segment = sectionSplit[2] 83 | section.section = sectionSplit[3] 84 | l_sections << section 85 | end 86 | end 87 | # get arch 88 | def get_arch(str) 89 | splitparam = str.split(" ") 90 | @l_arch = splitparam.last 91 | end 92 | # 处理Symbols 93 | def handle_symbols(str) 94 | # 字符编码产生的异常处理 95 | begin 96 | symbolsSplit = str.split("\t") 97 | rescue => exception 98 | return 99 | end 100 | if symbolsSplit.size > 2 101 | symbol = Symbols.new 102 | symbol.s_address = symbolsSplit[0] 103 | symbol.s_size = symbolsSplit[1] 104 | # 获取编号和名字 105 | serial_name_str = symbolsSplit[2] 106 | file_and_name_split = serial_name_str.split("]") 107 | if file_and_name_split.size > 1 108 | symbol.s_file_serial_number = file_and_name_split[0].delete("[").strip.to_i #设置文件编号 109 | symbol.s_name = file_and_name_split[1] 110 | end 111 | 112 | l_symbols << symbol 113 | else 114 | end 115 | end 116 | # 处理symbols的数组,把symbols转换成hashmap 117 | def handle_l_sym_map 118 | @l_sym_map = @l_symbols.group_by(&:s_file_serial_number) 119 | if @l_sym_map.include?(-1) 120 | puts "移除无用元素" 121 | @l_sym_map.delete(-1) 122 | end 123 | end 124 | # 处理link map file 125 | def handle_map 126 | handle_method_name = "" 127 | File.read(@l_name).each_line do |line| 128 | if line[0] == "#" 129 | if line.include?("Path:") 130 | handle_method_name = "get_path" 131 | puts "处理path..." 132 | elsif line.include?("Arch:") 133 | handle_method_name = "get_arch" 134 | puts "处理Arch..." 135 | elsif line.include?("Object files") 136 | handle_method_name = "handle_ojbect_files" 137 | puts "处理Object files..." 138 | elsif line.include?("Sections") 139 | handle_method_name = "handle_sections" 140 | puts "处理Sections..." 141 | elsif line.include?("Symbols:") #symbols:和Dead Stripped Symbols处理一样 142 | # 这里不处理Dead Stripped Symbols 143 | if line.delete('#').strip.include?("Dead Stripped Symbols") 144 | puts "不处理处理#{line.delete('#').strip}..." 145 | break 146 | end 147 | puts "处理#{line.delete('#').strip}..." 148 | handle_method_name = "handle_symbols" 149 | end 150 | end 151 | self.send(handle_method_name, line) 152 | 153 | end 154 | end 155 | # 对linkmap进行解析,然后输出结果 156 | def self.parser(path_para) 157 | start_time = Time.now.to_i #程序开始执行时间(以毫秒为单位) 158 | # 获取link map file's name 159 | link_map_file_name = path_para 160 | puts "获取的文件路径为:#{link_map_file_name}" 161 | if link_map_file_name.nil? 162 | puts "请按照如下命令执行该脚本:" 163 | puts "\033[31mitools parse **.txt \033[0m" 164 | puts "**指代Link Map File的名字,例如LinkMapApp-LinkMap-normal-x86_64.txt,parse后面为绝对路径" 165 | return 166 | end 167 | if File.exist?(link_map_file_name) 168 | puts "\033[32m获取LinkMap文件: #{link_map_file_name}成功,开始分析数据...\033[0m" 169 | else 170 | puts "\033[31m#{link_map_file_name}文件不存在,请重新输入文件 \033[0m" 171 | return 172 | end 173 | 174 | link_map = LinkMap.new(link_map_file_name) 175 | link_map.handle_map #处理文件为对象,然后继续后续操作 176 | link_map.handle_l_sym_map #处理symbols为hashmap 177 | sizeResultArr = [] 178 | 179 | link_map.l_obj_files.each do |obj| 180 | temp_file_name = obj.file_name.split("(")[0] 181 | 182 | last_file = sizeResultArr.last 183 | 184 | if last_file && temp_file_name.eql?(last_file.file_name) 185 | last_file.file_serial_numers << obj.serial_number 186 | else 187 | sz_obj = SizeResult.new 188 | sz_obj.file_name = temp_file_name 189 | sz_obj.file_serial_numers << obj.serial_number 190 | sizeResultArr << sz_obj 191 | end 192 | end 193 | data_analyze_time = Time.now.to_i 194 | puts "\033[32m数据分析完成,耗时#{data_analyze_time - start_time}秒。开始计算结果\033[0m" 195 | 196 | # 计算赋值size,此处耗时较长 197 | total_size = 0 198 | sizeResultArr.each do |obj| 199 | # 处理方法2 200 | obj.file_serial_numers.each do |s_number| 201 | begin 202 | link_map.l_sym_map[s_number].each do |symb| 203 | obj.size = obj.size + symb.s_size.hex 204 | total_size = total_size +symb.s_size.hex #统计总大小 205 | end 206 | rescue => exception 207 | end 208 | end 209 | # 处理方法1 太过耗时 210 | # link_map.l_symbols.each do |symb| 211 | # if obj.file_serial_numers.include?(symb.s_file_serial_number) 212 | # obj.size = obj.size + symb.s_size.hex 213 | # end 214 | # end 215 | # puts "正在计算#{obj.file_name}的大小..." 216 | end 217 | data_handle_time = Time.now.to_i #处理数据时间 218 | puts "\033[32m数据处理完成,耗时#{data_handle_time - data_analyze_time}秒。开始对结果进行大小排序(从大到小)...\033[0m" 219 | # 按照从大到小排序 220 | sizeResultArr.sort_by!{|obj|[-obj.size]} 221 | sort_handle_time = Time.now.to_i #排序耗时 222 | puts "\033[32m数据排序完成,耗时#{sort_handle_time - data_handle_time}秒。开始输出结果:\033[0m" 223 | puts "\033[32m--------------------------------\033[0m" 224 | 225 | # 判断文件是否存在 226 | save_file_path = SizeResult.getSaveFileName(path_para) 227 | if File.exist?(save_file_path) 228 | File.delete(save_file_path) 229 | end 230 | 231 | # 创建要保存数据的文件 232 | Spreadsheet.client_encoding = 'utf-8' 233 | book = Spreadsheet::Workbook.new 234 | sheet1 = book.create_worksheet 235 | sheet1.row(0)[0] = "文件名" 236 | sheet1.row(0)[1] = "文件大小(B)" 237 | sheet1.row(0)[2] = "文件大小" 238 | sizeResultArr.each_with_index{|item, idx| 239 | sheet1.row(idx+1)[0] = item.file_name 240 | sheet1.row(idx+1)[1] = item.size 241 | sheet1.row(idx+1)[2] = SizeResult.handleSize(item.size) 242 | puts "#{item.file_name} " + SizeResult.handleSize(item.size) 243 | } 244 | book.write "#{save_file_path}" 245 | # save_file = File.new(save_file_path,"w+") 246 | # # 打印结果 247 | # sizeResultArr.each do |obj| 248 | # puts "#{obj.file_name} " + SizeResult.handleSize(obj.size) 249 | # save_file.puts("#{obj.file_name} #{SizeResult.handleSize(obj.size)}(#{obj.size})") 250 | # end 251 | # save_file.puts("总大小为:#{SizeResult.handleSize(total_size)}") 252 | # save_file.close 253 | puts "总大小为(仅供参考):#{SizeResult.handleSize(total_size)}" 254 | puts "\033[32m--------------------------------\033[0m" 255 | end_time = Time.now.to_i #程序执行结束时间 256 | puts "分析结果已经保存为文件,位置为:\n\033[32m#{save_file_path}\033[0m" 257 | puts " " 258 | puts "\033[32m整个程序执行时间为:#{end_time - start_time}秒\033[0m" 259 | end 260 | 261 | # 根据linkmap && folder计算占用 262 | # 第一个参数为linkmap路径,第二个参数为要分析的项目文件夹 263 | def self.parser_by_folder(args) 264 | link_map_file_name = args[0] #linkmap文件路径 265 | project_folder = args[1] #项目文件夹 266 | # 对参数进行校验 267 | if File::directory?(project_folder) 268 | puts "获取的项目目录路径为:#{project_folder}" 269 | else 270 | puts "\033[31m#{project_folder}文件夹不存在,请重新输入 \033[0m" 271 | return 272 | end 273 | if File.exist?(link_map_file_name) 274 | puts "获取的linkmap文件路径为:#{link_map_file_name}" 275 | puts "\033[32m获取LinkMap文件: #{link_map_file_name}成功,开始分析数据...\033[0m" 276 | else 277 | puts "\033[31m#{link_map_file_name}文件不存在,请重新输入 \033[0m" 278 | return 279 | end 280 | # 开始处理数据 281 | link_map = LinkMap.new(link_map_file_name) 282 | link_map.handle_map #处理文件为对象,然后继续后续操作 283 | link_map.handle_l_sym_map #处理symbols为hashmap 284 | 285 | # 所有的文件 286 | link_map.l_obj_files 287 | # 所有的symbols 288 | link_map.l_symbols 289 | 290 | # 处理得到每个obj_file的大小 291 | link_map.l_obj_files.each do |obj| 292 | if link_map.l_sym_map[obj.serial_number] 293 | link_map.l_sym_map[obj.serial_number].each do |symb| 294 | obj.o_size = obj.o_size + symb.s_size.hex 295 | end 296 | end 297 | end 298 | # key为文件名,value为ojbect 299 | sort_by_obj_files_map = link_map.l_obj_files.group_by(&:o_name) 300 | # save_file_path = SizeResult.getSaveFileName(project_folder) 301 | # save_file = File.new(save_file_path,"w+") 302 | # sort_by_obj_files_map.keys.each do |sss| 303 | 304 | # save_file.puts("#{sort_by_obj_files_map[sss][0].o_name} #{sort_by_obj_files_map[sss][0].o_size}") 305 | # end 306 | # save_file.close 307 | # exit 308 | 309 | 310 | 311 | size_results = [] #盛放计算结果 312 | size_files = [] 313 | space_index = 0 314 | puts "计算开始" 315 | traverse_dir(sort_by_obj_files_map,project_folder,size_results,size_files,space_index) 316 | size_results.reverse! 317 | # 存储为文件 318 | save_file_path = SizeResult.getSaveFileName(project_folder) 319 | if File.exist?(save_file_path) 320 | File.delete(save_file_path) 321 | end 322 | save_file = File.new(save_file_path,"w+") 323 | o_index = 2 324 | size_results.each do |o| 325 | result_str = "#{' ' * o.space_count}├── #{o.folder_name.split('/').last} #{SizeResult.handleSize(o.size)}(#{o.size})" 326 | save_file.puts(result_str) 327 | end 328 | save_file.close 329 | puts "分析结果已经保存为文件,位置为:\n\033[32m#{save_file_path}\033[0m" 330 | end 331 | def self.traverse_dir(sort_by_obj_files_map,file_path,results,size_files,space_index) 332 | s_result = SizeResult.new 333 | s_result.folder_name = file_path 334 | space_index = space_index + 2 335 | file_name_arr = [] #盛放计算过的类 336 | Find.find(file_path) do |file| 337 | # 不包含图片 338 | if File.file?(file) && !(File.extname(file) =~ /(png|gif|jpg|bmp|jpeg)/) 339 | file_name = File.basename(file,".*") 340 | if !file_name_arr.include?(file_name) && sort_by_obj_files_map[file_name] #没有已经计算过 341 | s_result.size = s_result.size + sort_by_obj_files_map[file_name][0].o_size 342 | file_name_arr << file_name 343 | end 344 | elsif File::directory?(file) && file != file_path 345 | traverse_dir(sort_by_obj_files_map,file,results,size_files,space_index) 346 | end 347 | end 348 | if s_result.size > 0 && !size_files.include?(s_result.folder_name) 349 | s_result.space_count = space_index 350 | results << s_result 351 | size_files << s_result.folder_name 352 | end 353 | end 354 | end 355 | class SizeResult 356 | attr_accessor :file_name, :file_serial_numers, :size,:folder_name, :space_count 357 | def initialize 358 | @file_serial_numers = [] 359 | @size = 0 360 | end 361 | # size字符化 362 | def self.handleSize(size) 363 | if size > 1024 * 1024 364 | return format("%.2f",(size.to_f/(1024*1024))) + "MB" 365 | elsif size > 1024 366 | return format("%.2f",(size.to_f/1024)) + "KB" 367 | else 368 | return size.to_s + "B" 369 | end 370 | end 371 | # 获取结果文件保存到目录 372 | def self.getSaveFileName(path_para) 373 | path = Pathname.new(path_para) 374 | # 要保存的地址 375 | save_file_path = path.dirname.to_s + "/" + "parse_" + path.basename.to_s + "_result(#{Time.new.strftime("%Y%m%d%H%M%S")}).xls" 376 | return save_file_path 377 | end 378 | end 379 | end 380 | 381 | # --------------------------------- Size utils class --------------------------------- 382 | -------------------------------------------------------------------------------- /lib/itools/podfile_tiled.rb: -------------------------------------------------------------------------------- 1 | module Itools 2 | class PodInfo 3 | attr_accessor :pod_name, :pod_version 4 | def initialize(name, version) 5 | @pod_name = name 6 | @pod_version = version 7 | end 8 | end 9 | class PodfileTiled 10 | def self.podfile_tiled(args) 11 | all_pods = [] #所有依赖的pod 12 | exist_pods = [] #当前podfile已经存在的pod 13 | need_add_pods = [] #当前需要添加到podfile中的pod 14 | pod_tag = 1 15 | 16 | fileInfo = File.open(args[0]) 17 | 18 | total_index = 0 19 | fileInfo.each_line do |line| 20 | line_string = line.delete("\n") 21 | if line_string == 'PODS:' 22 | # 配置成功,后续请直接使用git commit ,不要加 -m\033[0m 23 | puts "\033[32m开始分析依赖\033[0m" 24 | pod_tag = 1 25 | next 26 | elsif line_string == 'DEPENDENCIES:' 27 | puts "\033[32m开始分析当前Podfile中已添加的依赖项\033[0m" 28 | pod_tag = 2 29 | next 30 | elsif line_string == 'SPEC REPOS:' 31 | pod_tag = 0 32 | puts "\033[32mpodfile.lock分析结束\033[0m" 33 | end 34 | 35 | if pod_tag == 1 #分析所有pod 36 | if line_string[0, 3] == ' -' && !line_string.include?('/') 37 | # puts line_string 38 | pod_version = line_string[/\((.*?)\)/, 1] 39 | pod_name = 40 | line_string.gsub(pod_version, '').delete('(').delete(')').delete( 41 | ':' 42 | ).delete('-').strip 43 | temp_pod = PodInfo.new(pod_name, pod_version) 44 | all_pods << temp_pod 45 | puts "查找到pod库:#{pod_name}, 版本号为:'#{pod_version}' #{all_pods.length}" 46 | end 47 | end 48 | 49 | if pod_tag == 2 #分析当前podfile已经有的pod 50 | pod_name 51 | pod_version = line_string[/\((.*?)\)/, 1] 52 | if pod_version 53 | pod_name = 54 | line_string.gsub(pod_version, '').delete('(').delete(')').delete( 55 | ':' 56 | ).delete('-').strip 57 | else 58 | pod_name = line_string.delete('-').lstrip.rstrip 59 | end 60 | # if pod_name.length == 0 || pod_name.include?('/') 61 | # next 62 | # end 63 | temp_pod = PodInfo.new(pod_name, pod_version) 64 | if pod_version 65 | temp_pod.pod_version = pod_version.delete('=').strip 66 | end 67 | 68 | exist_pods << temp_pod 69 | puts "Podfile中已包含 #{pod_name}, 版本号为:'#{pod_version}'" 70 | end 71 | end 72 | temp_exist_pods = [] 73 | all_pods.each do |all_pod| 74 | exist_pods.each do |exist_pod| 75 | exist_pod_name = exist_pod.pod_name 76 | if exist_pod_name.include?('/') 77 | exist_pod_name = exist_pod_name.split('/')[0] 78 | end 79 | if all_pod.pod_name == exist_pod_name 80 | temp_exist_pods << all_pod 81 | end 82 | end 83 | end 84 | 85 | need_add_pods = all_pods - temp_exist_pods 86 | if need_add_pods.length == 0 87 | puts "\033[32m恭喜!!!无需平铺,当前已全部平铺\033[0m" 88 | else 89 | puts "\033[32m以下为要平铺的库,直接复制粘贴至Podfile中即可:\033[0m" 90 | need_add_pods.each do |to_add| 91 | puts "pod '#{to_add.pod_name}', '#{to_add.pod_version}'" 92 | end 93 | end 94 | 95 | end 96 | end 97 | end 98 | -------------------------------------------------------------------------------- /lib/itools/string_searcher.rb: -------------------------------------------------------------------------------- 1 | require 'find' 2 | require 'spreadsheet' 3 | module Itools 4 | # 字符串操作类 5 | class StringHandle 6 | # originStr中是否包含targetStrs中的内容 7 | def self.containsStr(originStr,targetStrs) 8 | targetStrs.each_with_index {|item,idx| 9 | if originStr.include?(item) 10 | return idx 11 | end 12 | } 13 | return -1 14 | end 15 | end 16 | # 搜索结果类 17 | class SearchResult 18 | attr_accessor :file_name, :in_line, :result_str, :key_str 19 | 20 | def initialize(tempName,tempInLine,tempResultStr,tempKeyStr) 21 | @file_name = tempName 22 | @in_line = tempInLine 23 | @result_str = tempResultStr 24 | @key_str = tempKeyStr 25 | end 26 | end 27 | 28 | # Main class 29 | class StringSearcher 30 | attr_accessor :search_strs, :search_in, :result 31 | def initialize(temp_SearchStrs,temp_SearchIn) 32 | @search_strs = temp_SearchStrs 33 | @search_in = temp_SearchIn 34 | @result = [] 35 | end 36 | # 第二步开始搜索 37 | def search 38 | if check_exist 39 | handle_method = '' 40 | if File.file?(@search_in) #如果是文件 41 | handle_method = "search_in_file" 42 | else 43 | handle_method = "search_in_folder" 44 | end 45 | self.send(handle_method,@search_in) 46 | else 47 | puts "\033[31m文件不存在,请检查输入是否正确\033[0m" 48 | return 49 | end 50 | end 51 | # 从文件查找 52 | def search_in_file(temp_file) 53 | line_index = 1 54 | File.read(temp_file).each_line do |line| 55 | haveIndex = StringHandle.containsStr(line,@search_strs) 56 | if haveIndex != -1 57 | search_result = SearchResult.new(temp_file,line_index,line,@search_strs[haveIndex]) 58 | @result << search_result 59 | end 60 | line_index = line_index + 1 61 | end 62 | end 63 | 64 | # 从文件夹查找 65 | def search_in_folder(unuse) 66 | puts @search_in_folder 67 | Find.find(@search_in) do |filename| 68 | if File.file?(filename) #如果是文件,则从文件中查找,忽略文件夹 69 | search_in_file(filename) 70 | end 71 | end 72 | end 73 | # 第一步:检查是否存在 74 | def check_exist 75 | if File.file?(@search_in) 76 | puts "\033[32m从文件中查找\033[0m" 77 | return true 78 | elsif File::directory?(@search_in) 79 | puts "\033[32m从文件夹中查找\033[0m" 80 | return true 81 | else 82 | return false 83 | end 84 | end 85 | # 第一个参数为要搜索的文件或者文件夹名称 86 | # 第二个参数为要搜索的字符串 87 | def self.search_result(temp_search_in,temp_search_strs) 88 | if temp_search_in.nil? 89 | puts "\033[31m传入的参数有误,第一个参数为要搜索的文件或者文件夹名称,第二个参数为要搜索的字符串(如要查找多个str使用英文,分割),两个参数中间用空格区分\033[0m" 90 | return 91 | end 92 | if temp_search_strs.nil? 93 | puts "\033[31m传入的参数有误,第一个参数为要搜索的文件或者文件夹名称,第二个参数为要搜索的字符串(如要查找多个str使用英文,分割),两个参数中间用空格区分\033[0m" 94 | return 95 | end 96 | # 传入的可能是字符串数组 97 | searcher = StringSearcher.new(temp_search_strs.split(","),temp_search_in) 98 | searcher.search 99 | if searcher.result.size == 0 100 | puts "\033[32m没有找到相关字段\033[0m" 101 | return 102 | end 103 | # 输出搜索的内容 104 | Spreadsheet.client_encoding = 'utf-8' 105 | book = Spreadsheet::Workbook.new 106 | sheet1 = book.create_worksheet 107 | sheet1.row(0)[0] = "文件名" 108 | sheet1.row(0)[1] = "包含字符串" 109 | sheet1.row(0)[2] = "文件所在目录" 110 | sheet1.row(0)[3] = "查找内容所在行" 111 | sheet1.row(0)[4] = "查找结果Str" 112 | 113 | searcher.result.each_with_index do |item,i| 114 | sheet1.row(i+1)[0] = File.basename(item.file_name) 115 | sheet1.row(i+1)[1] = item.key_str 116 | sheet1.row(i+1)[2] = File.dirname(item.file_name) 117 | sheet1.row(i+1)[3] = item.in_line 118 | sheet1.row(i+1)[4] = item.result_str 119 | if i < 10 120 | puts "#{item.key_str} is in file:#{File.basename(item.file_name)} and Inline:#{item.in_line}" 121 | end 122 | 123 | end 124 | 125 | puts "\033[32m查找成功,内容已经保存到#{File.dirname(searcher.search_in)},请点击查看\033[0m" 126 | book.write "#{File.dirname(searcher.search_in)}/search_result.xls" 127 | end 128 | end 129 | end 130 | -------------------------------------------------------------------------------- /lib/itools/temple-commit-msg.dat: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'find' 3 | class CommitMsg 4 | attr_accessor :edit_file_path, :branch_name 5 | def initialize(editing_file_path,branch_name) 6 | @edit_file_path = editing_file_path 7 | @branch_name = branch_name 8 | end 9 | 10 | # type 规范判断 11 | def is_white_list_branch_type 12 | if @branch_name.match(/^(feature|bugfix|refactor|pref|hotfix|release|docs)\//) 13 | return true 14 | else 15 | return false 16 | end 17 | end 18 | # subject校验 19 | def verify_subject(str) 20 | subject = '' 21 | contain_subs = str.split(" ") 22 | if contain_subs.size == 2 #理想情况,也是大多数情况,只取subject即可 23 | subject = contain_subs[1] 24 | elsif contain_subs.size == 1 25 | subject = str.gsub(/#{@branch_name}/,"") 26 | else 27 | contain_subs.delete_at(0) 28 | subject = contain_subs.join("") #把剩下的数据组合起来 29 | end 30 | if subject.size > 50 31 | fail_with_msg("subject内容过长,请不要超过50个字") 32 | elsif subject == "subject" 33 | fail_with_msg("请完善subject的内容") 34 | else 35 | puts "subject校验通过..." 36 | end 37 | end 38 | # cause校验 39 | def verify_cause(str) 40 | if str.strip.size > 6 41 | puts "Cause校验通过..." 42 | else 43 | fail_with_msg("请完善Cause内容") 44 | end 45 | end 46 | #solution校验 47 | def verify_solution(str) 48 | if str.strip.size > 9 49 | puts "Solution校验通过..." 50 | else 51 | fail_with_msg("请完善Solution内容") 52 | end 53 | end 54 | # doc address校验 55 | def verify_doc_address(str) 56 | if str.strip.size > 21 57 | puts "Doc Address校验通过..." 58 | else 59 | fail_with_msg("请完善Doc Address内容") 60 | end 61 | end 62 | 63 | # 逐行校验 64 | def handle_line(line) 65 | handle_line_method = '' 66 | if line.match(/^#{@branch_name.strip}/) 67 | handle_line_method = 'verify_subject' 68 | elsif line.match(/^Cause/) 69 | handle_line_method = 'verify_cause' 70 | elsif line.match(/^Solution/) 71 | handle_line_method = 'verify_solution' 72 | elsif line.match(/^Releated Doc Address/) 73 | handle_line_method = 'verify_doc_address' 74 | else 75 | fail_with_msg("系统错误") 76 | end 77 | self.send(handle_line_method,line) 78 | end 79 | # 校验commit message内容是否符合规范 80 | def verify_content 81 | if File.file?(@edit_file_path) 82 | File.read(@edit_file_path).each_line do |line| 83 | match_data = line.match(/^#|^$/) 84 | if match_data == nil #过滤注释内容 85 | handle_line(line) 86 | end 87 | end 88 | end 89 | end 90 | # faile method 91 | def fail_with_msg(msg) 92 | puts "\033[31m#{msg}\033[0m" 93 | puts "\033[31mcommit fail!\033[0m" 94 | exit 1 95 | end 96 | end 97 | 98 | puts "commit message校验中..." 99 | branch_name = `git symbolic-ref --short HEAD` 100 | file_path = ARGV[0] 101 | commit_msg = CommitMsg.new(file_path,branch_name) 102 | # 校验分支名是否符合规范,这里没有对taskid进行校验 103 | # -------------------------------type校验------------------------------- 104 | if commit_msg.is_white_list_branch_type 105 | puts "分支名称校验通过..." 106 | else 107 | commit_msg.fail_with_msg("分支名称不符合规则,请按照约定规则处理") 108 | end 109 | 110 | if commit_msg.verify_content 111 | puts "\033[32mcommit success!\033[0m" 112 | end -------------------------------------------------------------------------------- /lib/itools/version.rb: -------------------------------------------------------------------------------- 1 | module Itools 2 | VERSION = "1.2.0" 3 | 4 | end 5 | --------------------------------------------------------------------------------