├── .gitignore ├── BinArchive.json ├── Gemfile ├── Gemfile.lock ├── LICENSE.txt ├── README.md ├── Rakefile ├── cocoapods-meitu-bin.gemspec ├── lib ├── cocoapods-meitu-bin.rb ├── cocoapods-meitu-bin │ ├── command.rb │ ├── command │ │ ├── bin.rb │ │ └── bin │ │ │ ├── archive.rb │ │ │ ├── auto.rb │ │ │ ├── build_all.rb │ │ │ ├── get_checksum.rb │ │ │ ├── header_files_specifications.rb │ │ │ ├── init.rb │ │ │ ├── install.rb │ │ │ ├── lib │ │ │ └── lint.rb │ │ │ ├── lock.rb │ │ │ ├── lock │ │ │ ├── dependency.rb │ │ │ ├── spec_repo.rb │ │ │ └── version.rb │ │ │ ├── output_source.rb │ │ │ ├── repo.rb │ │ │ ├── repo │ │ │ ├── push.rb │ │ │ └── update.rb │ │ │ ├── source.rb │ │ │ ├── source │ │ │ ├── add.rb │ │ │ ├── delete.rb │ │ │ └── list.rb │ │ │ ├── spec.rb │ │ │ ├── spec │ │ │ ├── create.rb │ │ │ └── lint.rb │ │ │ ├── update.rb │ │ │ └── upload.rb │ ├── config │ │ ├── config.rb │ │ ├── config_asker.rb │ │ └── config_builder.rb │ ├── gem_version.rb │ ├── helpers.rb │ ├── helpers │ │ ├── Info.plist │ │ ├── buildAll │ │ │ ├── bin_helper.rb │ │ │ ├── builder.rb │ │ │ ├── podspec_util.rb │ │ │ └── zip_file_helper.rb │ │ ├── build_helper.rb │ │ ├── build_utils.rb │ │ ├── framework.rb │ │ ├── framework_builder.rb │ │ ├── library.rb │ │ ├── library_builder.rb │ │ ├── pod_size_helper.rb │ │ ├── sources_helper.rb │ │ ├── spec_creator.rb │ │ ├── spec_files_helper.rb │ │ ├── spec_source_creator.rb │ │ └── upload_helper.rb │ ├── native.rb │ ├── native │ │ ├── acknowledgements.rb │ │ ├── analyzer.rb │ │ ├── file_accessor.rb │ │ ├── gen.rb │ │ ├── installation_options.rb │ │ ├── installer.rb │ │ ├── linter.rb │ │ ├── lockfile.rb │ │ ├── path_source.rb │ │ ├── pod_source_installer.rb │ │ ├── pod_target_installer.rb │ │ ├── podfile.rb │ │ ├── podfile_env.rb │ │ ├── podfile_generator.rb │ │ ├── podspec_finder.rb │ │ ├── resolver.rb │ │ ├── sandbox_analyzer.rb │ │ ├── source.rb │ │ ├── sources_manager.rb │ │ ├── specification.rb │ │ ├── target_validator.rb │ │ └── validator.rb │ └── source_provider_hook.rb └── cocoapods_plugin.rb └── spec ├── command └── bin_spec.rb └── spec_helper.rb /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | pkg 3 | .idea/ 4 | examples/ 5 | *.gem 6 | -------------------------------------------------------------------------------- /BinArchive.json: -------------------------------------------------------------------------------- 1 | { 2 | "archive-white-pod-list" : [ 3 | "YYTargetDemo", 4 | "YYModel" 5 | ], 6 | "ignore-git-list": [ 7 | "git@gitlab.xxx.com:Github-iOS" 8 | ], 9 | "ignore-http-list": [ 10 | "https://gitlab.xxx.com/Github-iOS" 11 | ], 12 | "//": "xcode_build_path 设置编译缓存完整路径", 13 | "xcode_build_path" : "xcode-build/Build/Intermediates.noindex/ArchiveIntermediates/#{target_name}/IntermediateBuildFilesPath/UninstalledProducts/iphoneos/", 14 | } 15 | 16 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in cocoapods-meitu-bin.gemspec 4 | gemspec 5 | 6 | group :development do 7 | gem 'cocoapods', '1.10.2' 8 | 9 | gem 'ruby-debug-ide' 10 | gem 'debase' 11 | 12 | gem 'mocha' 13 | gem 'bacon' 14 | gem 'mocha-on-bacon' 15 | gem 'prettybacon' 16 | end 17 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | cocoapods-meitu-bin (1.3.0) 5 | cocoapods (>= 1.10.2, <= 1.11.2) 6 | cocoapods-generate (~> 2.0.1) 7 | parallel (~> 1.22.0) 8 | 9 | GEM 10 | remote: https://rubygems.org/ 11 | specs: 12 | CFPropertyList (3.0.5) 13 | rexml 14 | activesupport (5.2.8.1) 15 | concurrent-ruby (~> 1.0, >= 1.0.2) 16 | i18n (>= 0.7, < 2) 17 | minitest (~> 5.1) 18 | tzinfo (~> 1.1) 19 | addressable (2.8.1) 20 | public_suffix (>= 2.0.2, < 6.0) 21 | algoliasearch (1.27.5) 22 | httpclient (~> 2.8, >= 2.8.3) 23 | json (>= 1.5.1) 24 | atomos (0.1.3) 25 | bacon (1.2.0) 26 | claide (1.1.0) 27 | cocoapods (1.10.2) 28 | addressable (~> 2.6) 29 | claide (>= 1.0.2, < 2.0) 30 | cocoapods-core (= 1.10.2) 31 | cocoapods-deintegrate (>= 1.0.3, < 2.0) 32 | cocoapods-downloader (>= 1.4.0, < 2.0) 33 | cocoapods-plugins (>= 1.0.0, < 2.0) 34 | cocoapods-search (>= 1.0.0, < 2.0) 35 | cocoapods-trunk (>= 1.4.0, < 2.0) 36 | cocoapods-try (>= 1.1.0, < 2.0) 37 | colored2 (~> 3.1) 38 | escape (~> 0.0.4) 39 | fourflusher (>= 2.3.0, < 3.0) 40 | gh_inspector (~> 1.0) 41 | molinillo (~> 0.6.6) 42 | nap (~> 1.0) 43 | ruby-macho (~> 1.4) 44 | xcodeproj (>= 1.19.0, < 2.0) 45 | cocoapods-core (1.10.2) 46 | activesupport (> 5.0, < 6) 47 | addressable (~> 2.6) 48 | algoliasearch (~> 1.0) 49 | concurrent-ruby (~> 1.1) 50 | fuzzy_match (~> 2.0.4) 51 | nap (~> 1.0) 52 | netrc (~> 0.11) 53 | public_suffix 54 | typhoeus (~> 1.0) 55 | cocoapods-deintegrate (1.0.5) 56 | cocoapods-disable-podfile-validations (0.1.1) 57 | cocoapods-downloader (1.6.3) 58 | cocoapods-generate (2.0.1) 59 | cocoapods-disable-podfile-validations (~> 0.1.1) 60 | cocoapods-plugins (1.0.0) 61 | nap 62 | cocoapods-search (1.0.1) 63 | cocoapods-trunk (1.6.0) 64 | nap (>= 0.8, < 2.0) 65 | netrc (~> 0.11) 66 | cocoapods-try (1.2.0) 67 | colored2 (3.1.2) 68 | concurrent-ruby (1.1.10) 69 | debase (0.2.4.1) 70 | debase-ruby_core_source (>= 0.10.2) 71 | debase-ruby_core_source (0.10.16) 72 | escape (0.0.4) 73 | ethon (0.15.0) 74 | ffi (>= 1.15.0) 75 | ffi (1.15.5) 76 | fourflusher (2.3.1) 77 | fuzzy_match (2.0.4) 78 | gh_inspector (1.1.3) 79 | httpclient (2.8.3) 80 | i18n (1.12.0) 81 | concurrent-ruby (~> 1.0) 82 | json (2.6.2) 83 | minitest (5.16.3) 84 | mocha (1.13.0) 85 | mocha-on-bacon (0.2.3) 86 | mocha (>= 0.13.0) 87 | molinillo (0.6.6) 88 | nanaimo (0.3.0) 89 | nap (1.1.0) 90 | netrc (0.11.0) 91 | parallel (1.22.1) 92 | prettybacon (0.0.2) 93 | bacon (~> 1.2) 94 | public_suffix (5.0.0) 95 | rake (13.0.6) 96 | rexml (3.2.5) 97 | ruby-debug-ide (0.7.3) 98 | rake (>= 0.8.1) 99 | ruby-macho (1.4.0) 100 | thread_safe (0.3.6) 101 | typhoeus (1.4.0) 102 | ethon (>= 0.9.0) 103 | tzinfo (1.2.10) 104 | thread_safe (~> 0.1) 105 | xcodeproj (1.22.0) 106 | CFPropertyList (>= 2.3.3, < 4.0) 107 | atomos (~> 0.1.3) 108 | claide (>= 1.0.2, < 2.0) 109 | colored2 (~> 3.1) 110 | nanaimo (~> 0.3.0) 111 | rexml (~> 3.2.4) 112 | 113 | PLATFORMS 114 | ruby 115 | 116 | DEPENDENCIES 117 | bacon 118 | bundler 119 | cocoapods (= 1.10.2) 120 | cocoapods-meitu-bin! 121 | debase 122 | mocha 123 | mocha-on-bacon 124 | prettybacon 125 | rake 126 | ruby-debug-ide 127 | 128 | BUNDLED WITH 129 | 2.4.16 130 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008-present, Meitu, Inc. 2 | All rights reserved. 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cocoapods-meitu-bin 2 | 3 | ## 简介 4 | 5 | `cocoapods-meitu-bin`是`CocoaPods`的二进制插件,提供了二进制相关的功能,如基于壳工程的二进制制作、二进制 / 源码切换等 6 | 7 | [美图秀秀 iOS 客户端二进制之路](https://juejin.cn/post/7175023366783385659) 8 | 9 | ## 安装 10 | 11 | `cocoapods-meitu-bin`有2种安装方式: 12 | 13 | * 安装到本机目录 14 | * 使用`Gemfile` 15 | 16 | ### 安装到本机目录 17 | 18 | ```shell 19 | $ sudo gem install cocoapods-meitu-bin 20 | ``` 21 | 22 | ### 使用Gemfile 23 | 24 | 在`Gemfile`中添加如下代码,然后执行`bundle install` 25 | 26 | ```ruby 27 | gem 'cocoapods-meitu-bin' 28 | ``` 29 | 30 | ## 使用 31 | 32 | ### 制作二进制 33 | 34 | 进入`Podfile`所在目录,执行`pod bin build-all`即可,根据需要添加相应的`option`选项,支持的`option`选项如下: 35 | 36 | | 选项 | 含义 | 37 | |---|---| 38 | | `--clean` | 全部二进制包制作完成后删除编译临时目录 | 39 | | `--clean-single` | 每制作完一个二进制包就删除该编译临时目录 | 40 | | `--repo-update` | 更新`Podfile`中指定的`repo`仓库 | 41 | | `--full-build` | 是否全量编译 | 42 | | `--skip-simulator` | 是否跳过模拟器编译 | 43 | | `--configuration=configName` | 在构建每个目标时使用`configName`指定构建配置,如:`Debug`、`Release`等 | 44 | 45 | > 如果想查看详细信息,可以使用`pod bin build-all --help`来查看帮助文档 46 | 47 | ### 使用二进制 48 | 49 | 在`Podfile`中添加如下代码,然后执行`pod install`即可 50 | 51 | ```ruby 52 | # 加载插件 53 | plugin 'cocoapods-meitu-bin' 54 | # 开启二进制 55 | use_binaries! 56 | # 设置源码白名单 57 | set_use_source_pods ['AFNetworking'] 58 | ``` 59 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler/gem_tasks' 2 | 3 | def specs(dir) 4 | FileList["spec/#{dir}/*_spec.rb"].shuffle.join(' ') 5 | end 6 | 7 | desc 'Runs all the specs' 8 | task :specs do 9 | sh "bundle exec bacon #{specs('**')}" 10 | end 11 | 12 | task :default => :specs 13 | 14 | -------------------------------------------------------------------------------- /cocoapods-meitu-bin.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'cocoapods-meitu-bin/gem_version.rb' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = 'cocoapods-meitu-bin' 8 | spec.version = CBin::VERSION 9 | spec.authors = ['Jensen'] 10 | spec.email = ['zys2@meitu.com'] 11 | spec.description = %q{cocoapods-meitu-bin is a plugin which helps develpers switching pods between source code and binary.} 12 | spec.summary = %q{cocoapods-meitu-bin is a plugin which helps develpers switching pods between source code and binary.} 13 | spec.homepage = 'https://github.com/meitu/cocoapods-meitu-bin.git' 14 | spec.license = 'MIT' 15 | 16 | spec.files = Dir["lib/**/*.rb","spec/**/*.rb","lib/**/*.plist"] + %w{README.md LICENSE.txt } 17 | # spec.files = `git ls-files`.split($/) 18 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 19 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 20 | spec.require_paths = ['lib'] 21 | 22 | spec.add_dependency 'parallel', '~> 1.22.0' 23 | spec.add_dependency 'cocoapods' 24 | # spec.add_dependency 'cocoapods', '1.10.2' 25 | spec.add_dependency "cocoapods-generate",'~> 2.0.1' 26 | 27 | spec.add_development_dependency 'bundler' 28 | spec.add_development_dependency 'rake' 29 | end 30 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods-meitu-bin/gem_version' 2 | require 'cocoapods-meitu-bin/native/sources_manager' 3 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/command.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods-meitu-bin/command/bin' 2 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/command/bin.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods-meitu-bin/command/bin/init' 2 | # require 'cocoapods-meitu-bin/command/bin/archive' 3 | # require 'cocoapods-meitu-bin/command/bin/auto' 4 | # require 'cocoapods-meitu-bin/command/bin/code' 5 | # require 'cocoapods-meitu-bin/command/bin/update' 6 | # require 'cocoapods-meitu-bin/command/bin/install' 7 | require 'cocoapods-meitu-bin/command/bin/repo' 8 | require 'cocoapods-meitu-bin/command/bin/spec' 9 | require 'cocoapods-meitu-bin/command/bin/build_all' 10 | require 'cocoapods-meitu-bin/command/bin/output_source' 11 | require 'cocoapods-meitu-bin/command/bin/get_checksum.rb' 12 | require 'cocoapods-meitu-bin/command/bin/header_files_specifications' 13 | require 'cocoapods-meitu-bin/command/bin/upload' 14 | require 'cocoapods-meitu-bin/command/bin/lock' 15 | require 'cocoapods-meitu-bin/command/bin/source' 16 | require 'cocoapods-meitu-bin/helpers' 17 | require 'cocoapods-meitu-bin/helpers/framework_builder' 18 | 19 | module Pod 20 | class Command 21 | # This is an example of a cocoapods plugin adding a top-level subcommand 22 | # to the 'pod' command. 23 | # 24 | # You can also create subcommands of existing or new commands. Say you 25 | # wanted to add a subcommand to `list` to show newly deprecated pods, 26 | # (e.g. `pod list deprecated`), there are a few things that would need 27 | # to change. 28 | # 29 | # - move this file to `lib/pod/command/list/deprecated.rb` and update 30 | # the class to exist in the the Pod::Command::List namespace 31 | # - change this class to extend from `List` instead of `Command`. This 32 | # tells the plugin system that it is a subcommand of `list`. 33 | # - edit `lib/cocoapods_plugins.rb` to require this file 34 | # 35 | # @todo Create a PR to add your plugin to CocoaPods/cocoapods.org 36 | # in the `plugins.json` file, once your plugin is released. 37 | # 38 | class Bin < Command 39 | include CBin::SourcesHelper 40 | include CBin::SpecFilesHelper 41 | 42 | self.abstract_command = true 43 | 44 | # self.default_subcommand = 'open' 45 | self.summary = '组件二进制化插件' 46 | # self.description = <<-DESC.strip_heredoc 47 | # 组件二进制化插件 48 | # 49 | # 利用源码私有源与二进制私有源实现对组件依赖类型的切换 50 | # DESC 51 | 52 | def initialize(argv) 53 | # !!! 下面这个require必须放在这里,不能放到文件顶部,切记 !!! 54 | require 'cocoapods-meitu-bin/native' 55 | 56 | # @help = argv.flag?('help') 57 | super 58 | # @env = argv.option('env') || 'dev' 59 | # CBin.config.set_configuration_env(@env) 60 | # msg = "cocoapods-meitu-bin #{CBin::VERSION} 版本 #{@env} 环境" 61 | # UI.info "\033[44;30m#{msg}\033[0m\n" 62 | end 63 | 64 | # def validate! 65 | # super 66 | # banner! if @help 67 | # end 68 | end 69 | end 70 | end -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/command/bin/archive.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods-meitu-bin/native/podfile' 2 | require 'cocoapods/command/gen' 3 | require 'cocoapods/generate' 4 | require 'cocoapods-meitu-bin/helpers/framework_builder' 5 | require 'cocoapods-meitu-bin/helpers/library_builder' 6 | require 'cocoapods-meitu-bin/helpers/build_helper' 7 | require 'cocoapods-meitu-bin/helpers/spec_source_creator' 8 | require 'cocoapods-meitu-bin/config/config_builder' 9 | require 'cocoapods-meitu-bin/command/bin/lib/lint' 10 | 11 | module Pod 12 | class Command 13 | class Bin < Command 14 | class Archive < Bin 15 | 16 | @@missing_binary_specs = [] 17 | 18 | self.summary = '将组件归档为 .a 或 .framework(目前仅支持静态 framework )' 19 | self.description = <<-DESC 20 | 将组件归档为 .a 或 .framework ,仅支持iOS平台 21 | 22 | 此静态 framework 不包含依赖组件的 symbol 23 | 24 | 目前仅支持 .framework,.a 尚未验证是否可以 25 | DESC 26 | 27 | def self.options 28 | [ 29 | ['--all-make', '对该组件的依赖库,全部制作为二进制组件'], 30 | ['--code-dependencies', '使用源码依赖'], 31 | ['--no-clean', '保留构建中间产物'], 32 | ['--sources', '私有源地址,多个用分号区分'], 33 | ['--framework-output', '输出framework文件'], 34 | ['--no-zip', '不压缩静态库 为 zip'], 35 | ['--configuration', 'Build the specified configuration (e.g. Debug). Defaults to Release'], 36 | ['--env', "该组件上传的环境 %w[dev debug_iphoneos release_iphoneos]"] 37 | ].concat(Pod::Command::Gen.options).concat(super).uniq 38 | end 39 | 40 | self.arguments = [ 41 | CLAide::Argument.new('NAME.podspec', false) 42 | ] 43 | 44 | def initialize(argv) 45 | @podspec = argv.shift_argument 46 | 47 | @code_dependencies = argv.flag?('code-dependencies') 48 | @framework_output = argv.flag?('framework-output', false ) 49 | @clean = argv.flag?('no-clean', false) 50 | @zip = argv.flag?('zip', true) 51 | @all_make = argv.flag?('all-make', false ) 52 | @sources = argv.option('sources') || [] 53 | @platform = Platform.new(:ios) 54 | 55 | @config = argv.option('configuration', 'Release') 56 | 57 | @framework_path 58 | super 59 | 60 | @additional_args = argv.remainder! 61 | @build_finshed = false 62 | end 63 | 64 | def run 65 | # 清除之前的缓存 66 | zip_dir = CBin::Config::Builder.instance.zip_dir 67 | FileUtils.rm_rf(zip_dir) if File.exist?(zip_dir) 68 | # 加载podspec 69 | @spec = Specification.from_file(spec_file) 70 | # 如果有 default_subspecs 报错提示 71 | raise Informative, "#{@spec.root.name} (#{@spec.root.version}) 有default_subspecs:#{@spec.default_subspecs},请注释掉重新执行命令!" unless @spec.default_subspecs.empty? 72 | # 生成xcode工程 73 | generate_project 74 | # 构建当前库 75 | build_root_spec 76 | 77 | sources_sepc = Array.new 78 | sources_sepc << @spec 79 | # 如果有 --all-make 选项,则打包依赖组件 80 | 81 | sources_sepc.concat(build_dependencies) if @all_make 82 | 83 | # 返回所有打包二进制组件的podspec 84 | sources_sepc 85 | end 86 | 87 | # 构建当前库 88 | def build_root_spec 89 | builder = CBin::Build::Helper.new(@spec, 90 | @platform, 91 | @framework_output, 92 | @zip, 93 | @spec, 94 | CBin::Config::Builder.instance.white_pod_list.include?(@spec.name), 95 | @config, 96 | @installers.size > 0 ? @installers[0] : nil) 97 | builder.build 98 | builder.clean_workspace if @clean && !@all_make 99 | end 100 | 101 | # 构建依赖库 102 | def build_dependencies 103 | @build_finshed = true 104 | #如果没要求,就清空依赖库数据 105 | sources_sepc = [] 106 | @@missing_binary_specs.uniq.each do |spec| 107 | next if spec.name.include?('/') # 过滤subspec 108 | next if spec.name == @spec.name # 过滤当前库 109 | #过滤白名单 110 | next if CBin::Config::Builder.instance.white_pod_list.include?(spec.name) 111 | #过滤 git 112 | if spec.source[:git] && spec.source[:git] 113 | spec_git = spec.source[:git] 114 | spec_git_res = false 115 | CBin::Config::Builder.instance.ignore_git_list.each do |ignore_git| 116 | spec_git_res = spec_git.include?(ignore_git) 117 | break if spec_git_res 118 | end 119 | next if spec_git_res 120 | end 121 | UI.warn "#{spec.name}.podspec 带有 vendored_frameworks 字段,请检查是否有效!!!" if spec.attributes_hash['vendored_frameworks'] 122 | UI.warn "#{spec.name}.podspec 带有 vendored_libraries 字段,请检查是否有效!!!" if spec.attributes_hash['vendored_libraries'] 123 | next if (spec.attributes_hash['vendored_frameworks'] || spec.attributes_hash['vendored_libraries']) && @spec.name != spec.name 124 | next if (spec.attributes_hash['ios.vendored_frameworks'] || spec.attributes_hash['ios.vendored_libraries']) && @spec.name != spec.name 125 | #获取没有制作二进制版本的spec集合 126 | sources_sepc << spec 127 | end 128 | 129 | fail_build_specs = [] 130 | sources_sepc.uniq.each do |spec| 131 | begin 132 | builder = CBin::Build::Helper.new(spec, 133 | @platform, 134 | @framework_output, 135 | @zip, 136 | @spec, 137 | false , 138 | @config, 139 | nil ) 140 | builder.build 141 | rescue Object => exception 142 | UI.puts exception 143 | fail_build_specs << spec 144 | end 145 | end 146 | 147 | if fail_build_specs.any? 148 | fail_build_specs.uniq.each do |spec| 149 | UI.warn "【#{spec.name} | #{spec.version}】组件二进制版本编译失败 ." 150 | end 151 | end 152 | sources_sepc - fail_build_specs 153 | end 154 | 155 | # 解析器传过来的 156 | def Archive.missing_binary_specs(missing_binary_specs) 157 | @@missing_binary_specs = missing_binary_specs unless @build_finshed 158 | end 159 | 160 | private 161 | 162 | # 生成xcode工程 163 | def generate_project 164 | Podfile.execute_with_bin_plugin do 165 | Podfile.execute_with_use_binaries(!@code_dependencies) do 166 | argvs = [ 167 | "--sources=#{sources_option(@code_dependencies, @sources)},https:\/\/cdn.cocoapods.org", 168 | "--gen-directory=#{CBin::Config::Builder.instance.gen_dir}", 169 | '--clean', 170 | *@additional_args 171 | ] 172 | 173 | podfile= File.join(Pathname.pwd, "Podfile") 174 | if File.exist?(podfile) 175 | argvs += ['--use-podfile'] 176 | argvs += ["--podfile-path=#{podfile}"] 177 | end 178 | 179 | argvs << spec_file if spec_file 180 | 181 | gen = Pod::Command::Gen.new(CLAide::ARGV.new(argvs)) 182 | gen.validate! 183 | @installers = gen.run 184 | end 185 | end 186 | end 187 | 188 | # 查找podspec 189 | def spec_file 190 | @spec_file ||= begin 191 | if @podspec 192 | find_spec_file(@podspec) 193 | else 194 | if code_spec_files.empty? 195 | raise Informative, '当前目录下没有找到可用源码 podspec.' 196 | end 197 | 198 | spec_file = code_spec_files.first 199 | spec_file 200 | end 201 | end 202 | end 203 | end 204 | end 205 | end 206 | end 207 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/command/bin/auto.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods-meitu-bin/command/bin/auto' 2 | require 'cocoapods-meitu-bin/helpers/upload_helper' 3 | 4 | module Pod 5 | class Command 6 | class Bin < Command 7 | class Auto < Bin 8 | self.summary = '一键打包二进制并上传' 9 | self.description = <<-DESC 10 | 生成二进制文件并上传至文件服务器,生成二进制 podspec 并上传至二进制私有源 11 | DESC 12 | 13 | self.arguments = [ 14 | CLAide::Argument.new('NAME.podspec', false) 15 | ] 16 | def self.options 17 | [ 18 | ['--push-source-podspec', '上传源码 podspec '], 19 | ['--code-dependencies', '使用源码依赖'], 20 | ['--allow-prerelease', '允许使用 prerelease 的版本'], 21 | ['--no-clean', '保留构建中间产物'], 22 | ['--framework-output', '输出framework文件'], 23 | ['--no-zip', '不压缩静态 framework 为 zip'], 24 | ['--all-make', '对该组件的依赖库,全部制作为二进制组件'], 25 | ['--configuration', 'Build the specified configuration (e.g. Release ). Defaults to Debug'], 26 | ['--env', "该组件上传的环境 %w[dev debug_iphoneos release_iphoneos]"] 27 | ].concat(Pod::Command::Gen.options).concat(super).uniq 28 | end 29 | 30 | def initialize(argv) 31 | 32 | @env = argv.option('env') || 'dev' 33 | CBin.config.set_configuration_env(@env) 34 | 35 | # @podspec = argv.shift_argument || find_podspec 36 | @podspec = argv.shift_argument 37 | 38 | @push_source_podspec = argv.flag?('push-source-podspec') 39 | @code_dependencies = argv.flag?('code-dependencies') 40 | @allow_prerelease = argv.flag?('allow-prerelease') 41 | @framework_output = argv.flag?('framework-output', false) 42 | @clean = argv.flag?('clean', true) 43 | @zip = argv.flag?('zip', true) 44 | @all_make = argv.flag?('all-make', false) 45 | @verbose = argv.flag?('verbose', false) 46 | @sources = argv.option('sources', 'https://cdn.cocoapods.org') 47 | @config = argv.option('configuration', 'Release') 48 | 49 | super 50 | 51 | # !!! 这一行加载在 super 的后面,否则会出现问题,切记 !!! 52 | @additional_args = argv.remainder! 53 | end 54 | 55 | def validate! 56 | super 57 | raise Informative, '当前目录下没有 podspec 文件' if @podspec.nil? && code_spec_files.size == 0 58 | raise Informative, '当前目录有多个 podspec 文件,请指定具体的 podspec 文件' if @podspec.nil? && code_spec_files.size > 1 59 | end 60 | 61 | def run 62 | @podspec = find_podspec unless @podspec 63 | @specification = Specification.from_file(@podspec) 64 | 65 | # 归档.a或.framework 66 | sources_sepc = run_archive 67 | 68 | fail_push_specs = [] 69 | sources_sepc.uniq.each do |spec| 70 | begin 71 | # 上传所有打包好的二进制文件及podspec 72 | fail_push_specs << spec unless CBin::Upload::Helper.new(spec,@code_dependencies,@sources).upload 73 | rescue Object => exception 74 | UI.puts exception 75 | fail_push_specs << spec 76 | end 77 | end 78 | 79 | if fail_push_specs.any? 80 | fail_push_specs.uniq.each do |spec| 81 | UI.warn "【#{spec.name} | #{spec.version}】组件spec push失败 ." 82 | end 83 | end 84 | 85 | success_specs = sources_sepc - fail_push_specs 86 | if success_specs.any? 87 | auto_success = "" 88 | success_specs.uniq.each do |spec| 89 | auto_success += "#{spec.name} | #{spec.version}\n" 90 | UI.message "【 #{spec.name} | #{spec.version} 】二进制组件制作完成".green 91 | end 92 | UI.message auto_success 93 | ENV['auto_success'] = auto_success 94 | end 95 | #pod repo update 96 | UI.title("Updating Spec Repositories\n".yellow) do 97 | Pod::Command::Bin::Repo::Update.new(CLAide::ARGV.new([])).run 98 | end 99 | 100 | # 上传源码podspec 101 | UI.title("Pushing source podspec for #{@specification.name}") do 102 | Pod::Command::Bin::Repo::Push.new(CLAide::ARGV.new([@podspec, '--loose-options'])).run 103 | end if @push_source_podspec 104 | 105 | end 106 | 107 | #制作二进制包 108 | def run_archive 109 | argvs = [ 110 | "--sources=#{sources_option(@code_dependencies, @sources)},https:\/\/cdn.cocoapods.org" 111 | ] 112 | 113 | argvs += @additional_args unless @additional_args.nil? 114 | 115 | argvs << @podspec if @podspec 116 | argvs.delete(Array.new) 117 | 118 | unless @clean 119 | argvs += ['--no-clean'] 120 | end 121 | if @code_dependencies 122 | argvs += ['--code-dependencies'] 123 | end 124 | if @verbose 125 | argvs += ['--verbose'] 126 | end 127 | if @allow_prerelease 128 | argvs += ['--allow-prerelease'] 129 | end 130 | if @framework_output 131 | argvs += ['--framework-output'] 132 | end 133 | if @all_make 134 | argvs += ['--all-make'] 135 | end 136 | # if @env 137 | # argvs += ["--env=#{@env}"] 138 | # end 139 | argvs += ["--configuration=#{@config}"] 140 | 141 | archive = Pod::Command::Bin::Archive.new(CLAide::ARGV.new(argvs)) 142 | archive.validate! 143 | sources_sepc = archive.run 144 | sources_sepc 145 | end 146 | 147 | def code_podsepc_extname 148 | '.podsepc' 149 | end 150 | 151 | def binary_podsepc_json 152 | "#{@specification.name}.binary.podspec.json" 153 | end 154 | 155 | def binary_template_podsepc 156 | "#{@specification.name}.binary-template.podspec" 157 | end 158 | 159 | def template_spec_file 160 | @template_spec_file ||= begin 161 | if @template_podspec 162 | find_spec_file(@template_podspec) 163 | else 164 | binary_template_spec_file 165 | end 166 | end 167 | end 168 | 169 | def spec_file 170 | @spec_file ||= begin 171 | if @podspec 172 | find_spec_file(@podspec) || @podspec 173 | else 174 | if code_spec_files.empty? 175 | raise Informative, '当前目录下没有找到可用源码 podspec.' 176 | end 177 | 178 | spec_file = if @binary 179 | code_spec = Pod::Specification.from_file(code_spec_files.first) 180 | if template_spec_file 181 | template_spec = Pod::Specification.from_file(template_spec_file) 182 | end 183 | create_binary_spec_file(code_spec, template_spec) 184 | else 185 | code_spec_files.first 186 | end 187 | spec_file 188 | end 189 | end 190 | end 191 | 192 | #Dir.glob 可替代 193 | def find_podspec 194 | name = nil 195 | Pathname.pwd.children.each do |child| 196 | # puts child 197 | if File.file?(child) 198 | if child.extname == '.podspec' 199 | name = File.basename(child) 200 | unless name.include?("binary-template") 201 | return name 202 | end 203 | end 204 | end 205 | end 206 | return name 207 | end 208 | 209 | end 210 | end 211 | end 212 | end 213 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/command/bin/get_checksum.rb: -------------------------------------------------------------------------------- 1 | require 'digest' 2 | module Pod 3 | class Command 4 | class Bin < Command 5 | class GetChecksum < Bin 6 | self.summary = '根据输入的podfile路径返回该podfile对应checksum(类似文件MD5值)' 7 | self.description = <<-DESC 8 | #{summary} 9 | DESC 10 | 11 | def self.options 12 | [ 13 | %w[--path=podfile路径] 14 | ].concat(super).uniq 15 | end 16 | 17 | def initialize(argv) 18 | @path = argv.option('path', "") 19 | super 20 | end 21 | 22 | def run 23 | puts calculate_checksum(@path) 24 | end 25 | # 计算checksum值 26 | def calculate_checksum(file_path) 27 | return "" unless File.exist?(file_path) 28 | content = "" 29 | lines = [] 30 | #过滤出实际使用pod 31 | File.open(file_path, 'r') do |file| 32 | file.each_line do |line| 33 | new_line = line.strip 34 | if new_line.start_with?("pod") 35 | lines << new_line 36 | end 37 | end 38 | end 39 | #给获取的pod list 排序,排除因组件顺序调整导致获取SHA1值不一样 40 | lines = lines.sort 41 | lines.each do |line| 42 | content << line 43 | end 44 | checksum = Digest::SHA1.hexdigest(content) 45 | checksum = checksum.encode('UTF-8') if checksum.respond_to?(:encode) 46 | return checksum 47 | end 48 | 49 | end 50 | end 51 | end 52 | end -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/command/bin/header_files_specifications.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods-meitu-bin/helpers/buildAll/builder' 2 | require 'cocoapods-meitu-bin/helpers/buildAll/podspec_util' 3 | require 'cocoapods-meitu-bin/helpers/buildAll/zip_file_helper' 4 | require 'cocoapods-meitu-bin/helpers/buildAll/bin_helper' 5 | require 'cocoapods-meitu-bin/config/config' 6 | require 'xcodeproj' 7 | require 'yaml' 8 | require 'digest' 9 | 10 | module Pod 11 | class Command 12 | class Bin < Command 13 | class HeaderFilesSpecifications < Bin 14 | self.summary = '规范组件在壳工程使用方式,非<>引入头文件会提示修改,同时会检查壳工程不在参与编译的文件并提示删除' 15 | self.description = <<-DESC 16 | #{summary} 17 | DESC 18 | 19 | def self.options 20 | [ 21 | %w[--xcodeproj-path xcodeproj路径,默认会查找podfile同级目录下的xcodeproj_path], 22 | %w[--classes-path 壳工程默认的代码文件路径,默认会查找podfile同级目录下的Classes目录], 23 | %w[--error-del 提示不规范的组件头文件引入,并删除壳工程不参与编译的文件], 24 | %w[--ignore-header header白名单设置,也可以在BinConfig.yml里配置,比如壳工程中的协议.h,或者纯.h文件,多个文件用;分割,例如CanvasProtocol.h;PainterProtocol.h] 25 | ].concat(super).uniq 26 | end 27 | 28 | def initialize(argv) 29 | @xcodeproj_path = argv.option('xcodeproj_path', "") 30 | @classes_path = argv.option('classes_path', "") 31 | @error_del = argv.flag?('error-del', false) 32 | @ignore_header = argv.option('ignore-header', '') 33 | super 34 | end 35 | 36 | def run 37 | # 开始时间 38 | @start_time = Time.now.to_i 39 | # 读取配置文件 40 | read_config 41 | #xcodeproj_path为空字符串,默认获取当前目录下的xxx.xcodeproj 42 | # 获取xcodeproj_path 43 | if @xcodeproj_path.empty? 44 | files = `ls` 45 | file_list = files.split("\n") 46 | xcodeproj = file_list.find_all { |n| n.include? ".xcodeproj" }[0] 47 | scheme = xcodeproj.split(".")[0] 48 | @xcodeproj_path = File.join(Pod::Config.instance.project_root, xcodeproj) 49 | end 50 | 51 | if !@xcodeproj_path.include? ".xcodeproj" 52 | UI.info ".xcodeproj 路径不存在".red 53 | return 54 | end 55 | # 获取xcodeproj 中的 Compile Sources 实际参与编译的文件 56 | project = Xcodeproj::Project.open(@xcodeproj_path) 57 | target = project.targets.select { |a_target| a_target.name.eql?(scheme) }[0] 58 | files = target.source_build_phase.files 59 | #获取 Compile Sources 中的所有文件名称 60 | names = Array.new 61 | files.each do |reference| 62 | if reference.respond_to? 'file_ref' and reference.file_ref.respond_to? 'path' 63 | names << reference.file_ref.path 64 | else 65 | puts reference 66 | end 67 | end 68 | # puts names 69 | #过滤出 .m 和 .mm 文件名 70 | find_names = names.find_all { |n| (n.respond_to? 'include?' and (n.include? ".m" or n.include? ".mm")) } 71 | find_swift_names = names.find_all { |n| (n.respond_to? 'include?' and n.include? ".swift") } 72 | if @classes_path.empty? 73 | @classes_path = File.join(Pod::Config.instance.project_root, "Classes") 74 | end 75 | 76 | if File.exist?(@classes_path) 77 | files_h = `find #{@classes_path} -name "*.h"` 78 | files_m = `find #{@classes_path} -name "*.m"` 79 | files_swift = `find #{@classes_path} -name "*.swift"` 80 | file_h_list = files_h.split("\n") 81 | file_m_list = files_m.split("\n") 82 | file_s_list = files_swift.split("\n") 83 | del_h_path_list = Array.new 84 | save_h_path_list = Array.new 85 | del_m_path_list = Array.new 86 | save_m_path_list = Array.new 87 | del_s_path_list = Array.new 88 | #获取在Compile Sources 的.h 和 本地路径下不在Compile Sources 的.h(需要删除的) 89 | file_h_list.each do |file_h_path| 90 | is_save = false 91 | find_names.each { |name| 92 | name_to = '' 93 | if name.include? '.mm' 94 | name_to = name.sub(".mm", ".h") 95 | elsif name.include? '.m' 96 | name_to = name.sub(".m", ".h") 97 | else 98 | puts "error:------#{name}" 99 | end 100 | 101 | if file_h_path.include? name_to 102 | is_save = true 103 | break 104 | end 105 | } 106 | if is_save 107 | save_h_path_list << file_h_path 108 | else 109 | del_h_path_list << file_h_path 110 | end 111 | end 112 | #获取在Compile Sources 的.m .mm 和 本地路径下不在Compile Sources 的.m .mm(需要删除的) 113 | file_m_list.each do |file_m_path| 114 | is_save = false 115 | find_names.each { |name| 116 | if file_m_path.include? name 117 | is_save = true 118 | break 119 | end 120 | } 121 | if is_save 122 | save_m_path_list << file_m_path 123 | else 124 | del_m_path_list << file_m_path 125 | end 126 | end 127 | #获取在Compile Sources 的.swift 和 本地路径下不在Compile Sources 的.swift(需要删除的) 128 | file_s_list.each do |file_s_path| 129 | is_save = false 130 | find_swift_names.each { |name| 131 | if file_s_path.include? name 132 | is_save = true 133 | break 134 | end 135 | } 136 | if !is_save 137 | del_s_path_list << file_s_path 138 | end 139 | end 140 | 141 | # puts "del_h_path_list" 142 | # puts del_h_path_list 143 | # puts "save_h_path_list" 144 | # puts save_h_path_list 145 | # puts "del_m_path_list" 146 | UI.title "提示: 壳工程不参与编译的文件,由于之前下层组件,删除引用并未删除代码文件或者其他分支又合入的不在使用或者已经下沉到组件的代码文件".green 147 | del_m_path_list.each { |name| UI.info "- #{name}".red } 148 | del_s_path_list.each { |name| UI.info "- #{name}".red } 149 | # puts "save_m_path_list" 150 | # puts save_m_path_list 151 | if @error_del 152 | del_m_path_list.each { |path| 153 | h_path = path.gsub(".m", ".h") 154 | if File.exist?(h_path) 155 | `rm -rf #{h_path}` 156 | end 157 | `rm -rf #{path}` 158 | } 159 | 160 | del_s_path_list.each { |path| 161 | `rm -rf #{path}` 162 | } 163 | end 164 | 165 | header_path = save_h_path_list.join(sep = "#",) 166 | all_header_list = Array.new 167 | all_header_annotation_list = Array.new 168 | save_h_path_list.each { |h_path| 169 | list = `cat #{h_path} | grep '#import "' | awk -F ' ' '{print $2}'` 170 | list_annotation = `cat #{h_path} | grep '//#import' | awk -F ' ' '{print $2}'` 171 | #注释的头文件 172 | list_annotation.split("\n").each { |name| 173 | if !name.include? "-Swift.h\"" and name.include? ".h\"" 174 | all_header_annotation_list << name.gsub("\"", "") 175 | end 176 | } 177 | list.split("\n").each { |name| 178 | if !name.include? "-Swift.h\"" and name.include? ".h\"" 179 | all_header_list << name.gsub("\"", "") 180 | end 181 | } 182 | } 183 | save_m_path_list.each { |m_path| 184 | list = `cat #{m_path} | grep '#import "' | awk -F ' ' '{print $2}'` 185 | list_annotation = `cat #{m_path} | grep '//#import' | awk -F ' ' '{print $2}'` 186 | list_annotation.split("\n").each { |name| 187 | if !name.include? "-Swift.h\"" and name.include? ".h\"" 188 | all_header_annotation_list << name.gsub("\"", "") 189 | end 190 | } 191 | list.split("\n").each { |name| 192 | if !name.include? "-Swift.h\"" and name.include? ".h\"" 193 | all_header_list << name.gsub("\"", "") 194 | end 195 | } 196 | } 197 | 198 | modified_header_file_list = Array.new 199 | all_header_annotation_list_to = all_header_annotation_list.uniq 200 | all_header_list_to = all_header_list.uniq 201 | 202 | all_header_del_list = Array.new 203 | 204 | all_header_list_to.each { |name| 205 | is_unsave = true 206 | all_header_annotation_list_to.each { |name_to| 207 | if name == name_to 208 | is_unsave = false 209 | break 210 | end 211 | } 212 | if is_unsave 213 | all_header_del_list << name 214 | end 215 | } 216 | 217 | UI.title "提示: 使用方式需要调整为<>方式的头文件,请在壳工程代码搜索并修改".green 218 | if !@ignore_header.empty? 219 | @ignore_header.split(";").each { |name| 220 | @ignore_header_list << name 221 | } 222 | end 223 | @ignore_header_list.uniq 224 | all_header_del_list.each { |name| 225 | if !header_path.include? name and !@ignore_header_list.include? name 226 | modified_header_file_list << name 227 | UI.info "- #{name} 头文件不能在壳工程使用 #import \"#{name}\" 需要修改成#import ".red 228 | end 229 | } 230 | 231 | end 232 | 233 | # 计算耗时 234 | show_cost_time 235 | end 236 | 237 | private 238 | 239 | # 读取配置文件 240 | def read_config 241 | UI.title 'Read config from file `BinConfig.yaml`'.green do 242 | config_file = File.join(Pod::Config.instance.project_root, 'BinConfig.yaml') 243 | return unless File.exist?(config_file) 244 | config = YAML.safe_load(File.open(config_file)) 245 | return if config.nil? 246 | build_config = config['build_config'] 247 | return if build_config.nil? 248 | @ignore_header_list = build_config['ignore_header_list'] 249 | end 250 | end 251 | 252 | # 打印耗时 253 | def show_cost_time 254 | return if @start_time.nil? 255 | UI.info "总耗时:#{Time.now.to_i - @start_time}s".green 256 | end 257 | 258 | end 259 | end 260 | end 261 | end 262 | 263 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/command/bin/init.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods-meitu-bin/config/config_asker' 2 | 3 | module Pod 4 | class Command 5 | class Bin < Command 6 | class Init < Bin 7 | self.summary = '配置插件' 8 | self.description = <<-DESC 9 | 创建yml配置文件,保存插件需要的配置信息,如二进制podspec仓库、二进制下载地址等 10 | DESC 11 | 12 | def self.options 13 | [ 14 | %w[--bin-url=URL 配置文件地址,直接从此地址下载配置文件] 15 | ].concat(super) 16 | end 17 | 18 | def initialize(argv) 19 | @bin_url = argv.option('bin-url') 20 | super 21 | end 22 | 23 | def run 24 | raise Informative, "当前目录下没有`Podfile`文件" unless File.exist?(File.join(Dir.pwd, "Podfile")) 25 | raise Informative, "当前目录下已经存在配置文件" if File.exist?(CBin.config.config_file) 26 | if @bin_url.nil? 27 | config_with_asker 28 | else 29 | config_with_url(@bin_url) 30 | end 31 | end 32 | 33 | private 34 | 35 | # 从远端下载配置文件 36 | def config_with_url(url) 37 | require 'open-uri' 38 | 39 | UI.puts "开始下载配置文件..." 40 | file = open(url) 41 | contents = YAML.safe_load(file.read) 42 | 43 | UI.puts "开始同步配置文件..." 44 | CBin.config.sync_config(contents.to_hash) 45 | UI.puts "设置完成.".green 46 | rescue Errno::ENOENT => e 47 | raise Informative, "配置文件路径 #{url} 无效,请确认后重试." 48 | end 49 | 50 | # 询问用户相关的配置 51 | def config_with_asker 52 | asker = CBin::Config::Asker.new 53 | asker.welcome_message 54 | 55 | config = {} 56 | template_hash = CBin.config.template_hash 57 | template_hash.each do |k, v| 58 | default = begin 59 | CBin.config.send(k) 60 | rescue StandardError 61 | nil 62 | end 63 | config[k] = asker.ask_with_answer(v[:description], default, v[:selection]) 64 | end 65 | 66 | CBin.config.sync_config(config) 67 | asker.done_message 68 | end 69 | end 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/command/bin/install.rb: -------------------------------------------------------------------------------- 1 | 2 | require 'cocoapods-meitu-bin/command/bin/update' 3 | module Pod 4 | class Command 5 | class Bin < Command 6 | class Install < Bin 7 | include Pod 8 | 9 | self.summary = 'pod install 拦截器' 10 | 11 | self.description = <<-DESC 12 | pod install 拦截器,会加载本地Podfile_local文件 13 | 会通过DSL加载到原始Podfile文件中 14 | 支持 pod 'xxx' 各种写法 15 | 支持 post_install/pre_install钩子,采用覆盖做法 16 | DESC 17 | def self.options 18 | [ 19 | ['--repo-update', 'Force running `pod repo update` before install'], 20 | ['--deployment', 'Disallow any changes to the Podfile or the Podfile.lock during installation'], 21 | ['--clean-install', 'Ignore the contents of the project cache and force a full pod installation. This only ' \ 22 | 'applies to projects that have enabled incremental installation'] 23 | ].concat(super).reject { |(name, _)| name == '--no-repo-update' } 24 | end 25 | 26 | def initialize(argv) 27 | @update = argv.flag?('update') 28 | super 29 | @additional_args = argv.remainder! 30 | end 31 | 32 | def run 33 | Update.load_local_podfile 34 | argvs = [ 35 | *@additional_args 36 | ] 37 | gen = Pod::Command::Install.new(CLAide::ARGV.new(argvs)) 38 | gen.validate! 39 | gen.run 40 | end 41 | end 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/command/bin/lib/lint.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods-meitu-bin/config/config' 2 | require 'cocoapods-meitu-bin/native/podfile' 3 | 4 | module Pod 5 | class Command 6 | class Bin < Command 7 | class Lib < Bin 8 | class Lint < Lib 9 | self.summary = 'lint 组件.' 10 | self.description = <<-DESC 11 | lint 二进制组件 / 源码组件 12 | DESC 13 | 14 | self.arguments = [ 15 | CLAide::Argument.new('NAME.podspec', false) 16 | ] 17 | 18 | # lib lint 不会下载 source,所以不能进行二进制 lint 19 | # 要 lint 二进制版本,需要进行 spec lint,此 lint 会去下载 source 20 | def self.options 21 | [ 22 | ['--code-dependencies', '使用源码依赖进行 lint'], 23 | ['--loose-options', '添加宽松的 options, 包括 --use-libraries (可能会造成 entry point (start) undefined)'], 24 | ['--allow-prerelease', '允许使用 prerelease 的版本 lint'] 25 | ].concat(Pod::Command::Lib::Lint.options).concat(super).uniq 26 | end 27 | 28 | def initialize(argv) 29 | @loose_options = argv.flag?('loose-options') 30 | @code_dependencies = argv.flag?('code-dependencies') 31 | @sources = argv.option('sources') || [] 32 | @allow_prerelease = argv.flag?('allow-prerelease') 33 | @podspec = argv.shift_argument 34 | super 35 | 36 | @additional_args = argv.remainder! 37 | end 38 | 39 | def run 40 | Podfile.execute_with_bin_plugin do 41 | Podfile.execute_with_allow_prerelease(@allow_prerelease) do 42 | Podfile.execute_with_use_binaries(!@code_dependencies) do 43 | argvs = [ 44 | @podspec || code_spec_files.first, 45 | "--sources=#{sources_option(@code_dependencies, @sources)}", 46 | *@additional_args 47 | ] 48 | 49 | if @loose_options 50 | argvs << '--allow-warnings' 51 | if code_spec&.all_dependencies&.any? 52 | argvs << '--use-libraries' 53 | end 54 | end 55 | 56 | lint = Pod::Command::Lib::Lint.new(CLAide::ARGV.new(argvs)) 57 | lint.validate! 58 | lint.run 59 | end 60 | end 61 | end 62 | end 63 | end 64 | end 65 | end 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/command/bin/lock.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods-meitu-bin/command/bin/lock/spec_repo' 2 | require 'cocoapods-meitu-bin/command/bin/lock/version' 3 | require 'cocoapods-meitu-bin/command/bin/lock/dependency' 4 | 5 | module Pod 6 | class Command 7 | class Bin < Command 8 | class Lock < Bin 9 | include Pod 10 | include Config::Mixin 11 | 12 | self.abstract_command = true 13 | self.summary = '分析 Pod 依赖关系' 14 | 15 | def initialize(argv) 16 | super 17 | end 18 | 19 | def run 20 | # 校验Podfile是否存在 21 | verify_podfile_exists! 22 | # 依赖分析 23 | @analyze_result = analyze 24 | end 25 | 26 | # 依赖分析 27 | def analyze 28 | UI.title 'Analyze dependencies'.green do 29 | analyzer = Pod::Installer::Analyzer.new(config.sandbox, config.podfile, config.lockfile) 30 | analyzer.analyze(true ) 31 | end 32 | end 33 | end 34 | end 35 | end 36 | end -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/command/bin/lock/dependency.rb: -------------------------------------------------------------------------------- 1 | 2 | module Pod 3 | class Command 4 | class Bin < Command 5 | class Lock < Bin 6 | class Dependency < Lock 7 | include Pod 8 | 9 | self.summary = '分析`POD_NAME`依赖的库' 10 | self.description = <<-DESC 11 | #{self.summary},如果加上`--reverse`,则分析依赖`POD_NAME`的库 12 | DESC 13 | 14 | self.arguments = [ 15 | CLAide::Argument.new('POD_NAME', true) 16 | ] 17 | 18 | def self.options 19 | [ 20 | %w[--reverse 分析依赖`POD_NAME`的库] 21 | ].concat(super).uniq 22 | end 23 | 24 | def initialize(argv) 25 | super 26 | @pod_name = argv.shift_argument 27 | @reverse = argv.flag?('reverse', false) 28 | end 29 | 30 | def run 31 | super 32 | raise Informative, "请输入Pod库名称,如:AFNetworking" if @pod_name.nil? 33 | if @reverse 34 | reverse_dependencies 35 | else 36 | dependencies 37 | end 38 | end 39 | 40 | def dependencies 41 | deps = [] 42 | @analyze_result.specifications.map do |spec| 43 | if spec.root.name == @pod_name 44 | deps.concat(spec.dependencies) 45 | end 46 | end 47 | UI.puts "\n" 48 | if deps.empty? 49 | UI.puts "`#{@pod_name}`无依赖的库".red 50 | else 51 | deps.reject! { |dep| dep.root_name == @pod_name } 52 | unless deps.nil? 53 | deps.uniq! 54 | end 55 | if deps.nil? or deps.empty? 56 | UI.puts "`#{@pod_name}`无依赖的库".red 57 | else 58 | UI.puts "`#{@pod_name}`依赖的库如下:".yellow 59 | deps.map { |dep| UI.puts "- #{dep}" } 60 | UI.puts "total #{deps.size} deps".green 61 | end 62 | end 63 | end 64 | 65 | def reverse_dependencies 66 | pods = [] 67 | @analyze_result.specifications.map do |spec| 68 | spec.dependencies.map do |dep| 69 | if dep.root_name == @pod_name and !spec.root.name.include?(@pod_name) 70 | pods << spec.root.name 71 | break 72 | end 73 | end 74 | end 75 | UI.puts "\n" 76 | if pods.empty? 77 | UI.puts "没有依赖`#{@pod_name}`的库".red 78 | else 79 | pods.uniq! 80 | UI.puts "依赖`#{@pod_name}`的库如下:".yellow 81 | pods.map { |pod| UI.puts "- #{pod}" } 82 | UI.puts "total #{pods.size} pods".green 83 | end 84 | end 85 | end 86 | end 87 | end 88 | end 89 | end -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/command/bin/lock/spec_repo.rb: -------------------------------------------------------------------------------- 1 | 2 | module Pod 3 | class Command 4 | class Bin < Command 5 | class Lock < Bin 6 | class SpecRepo < Lock 7 | include Pod 8 | 9 | self.summary = '分析`podspec`源' 10 | self.description = <<-DESC 11 | #{self.summary},如果加`POD_NAME`,则分析该`POD_NAME`所属的repo仓库;如果不加,则打印项目中用到的repo仓库及每个仓库下Pod个数 12 | DESC 13 | 14 | self.arguments = [ 15 | CLAide::Argument.new('POD_NAME', false) 16 | ] 17 | 18 | def initialize(argv) 19 | super 20 | @pod_name = argv.shift_argument 21 | end 22 | 23 | def run 24 | super 25 | if @pod_name.nil? 26 | spec_repo_summary 27 | else 28 | pod_source 29 | end 30 | end 31 | 32 | private 33 | 34 | def spec_repos 35 | raise Informative, "依赖分析失败" if @analyze_result.nil? 36 | result = Hash.new 37 | @analyze_result.specs_by_source.map do |source, specs| 38 | next unless source 39 | next if specs.empty? 40 | key = source.url || source.name 41 | 42 | # save `trunk` as 'trunk' so that the URL itself can be changed without lockfile churn 43 | key = Pod::TrunkSource::TRUNK_REPO_NAME if source.name == Pod::TrunkSource::TRUNK_REPO_NAME 44 | 45 | value = specs.map { |s| s.root.name }.uniq 46 | # 合并重复的source源,而不是替换 47 | if result[key].nil? 48 | result[key] = YAMLHelper.sorted_array(value) 49 | else 50 | result[key] = YAMLHelper.sorted_array(result[key].concat(value)) 51 | end 52 | end 53 | result.compact 54 | end 55 | 56 | def external_sources 57 | deps = config.podfile.dependencies.select(&:external?) 58 | deps = deps.sort { |d, other| d.name <=> other.name } 59 | sources = {} 60 | deps.each { |d| sources[d.root_name] = d.external_source } 61 | sources 62 | end 63 | 64 | # 打印所有source及其pods个数 65 | def spec_repo_summary 66 | pod_count = 0 67 | UI.puts "\n" 68 | spec_repos.map do |source, specs| 69 | pod_count += specs.size 70 | UI.puts "#{source}".yellow 71 | UI.puts "- #{specs.size} pods" 72 | end 73 | pod_count += external_sources.keys.size 74 | UI.puts "External".yellow 75 | UI.puts "- #{external_sources.keys.size} pods" 76 | 77 | UI.puts "\n" 78 | UI.puts "total #{spec_repos.size + 1} sources, #{pod_count} pods".green 79 | end 80 | 81 | # 打印pod所属的source 82 | def pod_source 83 | sources = [] 84 | external = false 85 | spec_repos.map do |source, specs| 86 | if specs.include?(@pod_name) 87 | sources << source 88 | end 89 | end 90 | external_sources.map do |pod, ext_source| 91 | if pod == @pod_name 92 | external = true 93 | sources << ext_source 94 | end 95 | end 96 | UI.puts "\n" 97 | raise Informative, "未找到`#{@pod_name}`所属的source,请检查`#{@pod_name}`是否拼写错误" if sources.empty? 98 | UI.puts "#{@pod_name}#{external ? ' (External Source)' : ''}".yellow 99 | sources.map { |source| UI.puts "- #{source}" } 100 | end 101 | end 102 | end 103 | end 104 | end 105 | end 106 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/command/bin/lock/version.rb: -------------------------------------------------------------------------------- 1 | 2 | module Pod 3 | class Command 4 | class Bin < Command 5 | class Lock < Bin 6 | class Version < Lock 7 | include Pod 8 | 9 | self.summary = '分析项目中使用的`Pod`库版本' 10 | 11 | self.arguments = [ 12 | CLAide::Argument.new('POD_NAME', true) 13 | ] 14 | 15 | def self.options 16 | [ 17 | %w[--source 查看二进制对应源码版本] 18 | ].concat(super).uniq 19 | end 20 | 21 | def initialize(argv) 22 | super 23 | @pod_name = argv.shift_argument 24 | @source = argv.flag?('source', false) 25 | end 26 | 27 | def run 28 | super 29 | raise Informative, "请输入Pod库名称,如:AFNetworking" if @pod_name.nil? 30 | versions = [] 31 | @analyze_result.specifications.map do |spec| 32 | if spec.name == @pod_name 33 | versions << spec.version 34 | end 35 | end 36 | versions = versions.uniq 37 | UI.puts "\n" 38 | raise Informative, "未查找到`#{@pod_name}`的版本号,请检查`#{@pod_name}`是否拼写错误" if versions.empty? 39 | UI.puts "`#{@pod_name}`版本号如下:".yellow 40 | versions.map do |v| 41 | if @source 42 | UI.puts "- #{get_source_version(v.to_s)}" 43 | else 44 | UI.puts "- #{v}" 45 | end 46 | end 47 | 48 | UI.puts "[!] `#{@pod_name}`有`#{versions.size}`个版本,可能会导致意想不到的事情,请确保每个Pod库只有一个依赖版本".yellow if versions.size > 1 49 | end 50 | 51 | private 52 | 53 | # 获取源码版本号 54 | def get_source_version(version) 55 | source_version = version 56 | version_arr = version.split('.') 57 | if version_arr.last.include?('bin') 58 | version_arr.delete_at(version_arr.size - 1) 59 | source_version = version_arr.join('.') 60 | end 61 | source_version 62 | end 63 | 64 | end 65 | end 66 | end 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/command/bin/output_source.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods-meitu-bin/helpers/buildAll/builder' 2 | require 'cocoapods-meitu-bin/helpers/buildAll/podspec_util' 3 | require 'cocoapods-meitu-bin/helpers/buildAll/zip_file_helper' 4 | require 'cocoapods-meitu-bin/helpers/buildAll/bin_helper' 5 | require 'cocoapods-meitu-bin/config/config' 6 | require 'yaml' 7 | require 'digest' 8 | 9 | module Pod 10 | class Command 11 | class Bin < Command 12 | class OutputSource < Bin 13 | self.summary = '输出各个组件的source源,默认输出全部组件的source' 14 | self.description = <<-DESC 15 | #{summary} 16 | DESC 17 | 18 | def self.options 19 | [ 20 | %w[--error-source 过滤异常的source,比如http的,CI打包只支持SSH认证], 21 | %w[--export-file 导出当前所有tag版本的podspec] 22 | ].concat(super).uniq 23 | end 24 | 25 | def initialize(argv) 26 | @error_source = argv.flag?('error-source', false) 27 | @export_file = argv.flag?('export-file', false) 28 | super 29 | end 30 | 31 | def run 32 | # 开始时间 33 | @start_time = Time.now.to_i 34 | # 更新repo仓库 35 | repo_update 36 | # 分析依赖 37 | @analyze_result = analyse 38 | 39 | if @error_source 40 | # 打印source 41 | show_cost_source 42 | end 43 | 44 | if @export_file 45 | pod_targets = @analyze_result.pod_targets.uniq 46 | pod_targets.map { |pod_target| 47 | current_path = Dir.pwd 48 | spec_path = "#{current_path}/podfile_shell/#{pod_target.root_spec.name}/#{pod_target.root_spec.version.version}/" 49 | `mkdir -p #{spec_path}` 50 | if system("cp #{pod_target.root_spec.defined_in_file.to_s} #{spec_path} > /dev/null 2>&1") 51 | puts "#{pod_target.root_spec.name} 的 #{pod_target.root_spec.version.version} 已经导出" 52 | else 53 | `rm -rf #{current_path}/podfile_shell/#{pod_target.root_spec.name} > /dev/null 2>&1` 54 | end 55 | # puts pod_target.root_spec.name pod_target.root_spec.version.version pod_target.root_spec.defined_in_file 56 | } 57 | end 58 | # 计算耗时 59 | show_cost_time 60 | end 61 | 62 | private 63 | 64 | # 打印source 65 | def show_cost_source 66 | all_source_list = [] 67 | error_source_list = [] 68 | @analyze_result.specifications.each do |specification| 69 | all_source_list << { specification.root.name => specification.root.source } 70 | if @error_source && invalid_git_address?(specification) 71 | error_source_list << { specification.root.name => specification.root.source } 72 | end 73 | end 74 | if @error_source 75 | if error_source_list.uniq.empty? 76 | UI.info "没有有问题的组件".green 77 | else 78 | UI.info '问题组件,source 为http CI打包不支持http认证,应修改为ssh'.red 79 | error_source_list.uniq.map do |source| 80 | UI.info "- #{source}".red 81 | end 82 | end 83 | else 84 | UI.info '输出所有pod组件source'.yellow 85 | all_source_list.uniq.map do |source| 86 | UI.info "- #{source}" 87 | end 88 | end 89 | end 90 | 91 | # git clone 地址 是否非法 92 | def invalid_git_address?(specification) 93 | return false if specification.root.source[:git].nil? 94 | git = specification.root.source[:git] 95 | git.include?('http://') || git.include?('https://') 96 | end 97 | 98 | # 打印耗时 99 | def show_cost_time 100 | return if @start_time.nil? 101 | UI.info "总耗时:#{Time.now.to_i - @start_time}s".green 102 | end 103 | 104 | # 更新repo仓库 105 | def repo_update 106 | if @repo_update 107 | UI.title 'Repo update'.green do 108 | return if podfile.nil? 109 | sources_manager = Pod::Config.instance.sources_manager 110 | podfile.sources.uniq.map do |src| 111 | UI.message "Update repo: #{src}" 112 | source = sources_manager.source_with_name_or_url(src) 113 | source.update(false) 114 | end 115 | end 116 | end 117 | end 118 | 119 | # 获取 podfile 120 | def podfile 121 | @podfile ||= begin 122 | podfile_path = File.join(Pathname.pwd, 'Podfile') 123 | raise 'Podfile不存在' unless File.exist?(podfile_path) 124 | sources_manager = Pod::Config.instance.sources_manager 125 | podfile = Podfile.from_file(Pathname.new(podfile_path)) 126 | podfile_hash = podfile.to_hash 127 | podfile_hash['sources'] = (podfile_hash['sources'] || []).concat(sources_manager.code_source_list.map(&:url)) 128 | podfile_hash['sources'] << sources_manager.binary_source.url 129 | podfile_hash['sources'].uniq! 130 | Podfile.from_hash(podfile_hash) 131 | end 132 | end 133 | 134 | # 获取 podfile.lock 135 | def lockfile 136 | @lockfile ||= begin 137 | lock_path = File.join(Pathname.pwd, 'Podfile.lock') 138 | raise 'Podfile.lock不存在,请执行pod install' unless File.exist?(lock_path) 139 | Lockfile.from_file(Pathname.new(lock_path)) 140 | end 141 | end 142 | 143 | # 获取 sandbox 144 | def sandbox 145 | @sandbox ||= begin 146 | sandbox_path = File.join(Pathname.pwd, 'Pods') 147 | raise 'Pods文件夹不存在,请执行pod install' unless File.exist?(sandbox_path) 148 | Pod::Sandbox.new(sandbox_path) 149 | end 150 | end 151 | 152 | # 根据podfile和podfile.lock分析依赖 153 | def analyse 154 | UI.title 'Analyze dependencies'.green do 155 | analyzer = Pod::Installer::Analyzer.new( 156 | sandbox, 157 | podfile, 158 | lockfile 159 | ) 160 | analyzer.analyze(true) 161 | end 162 | end 163 | end 164 | end 165 | end 166 | end 167 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/command/bin/repo.rb: -------------------------------------------------------------------------------- 1 | 2 | require 'cocoapods-meitu-bin/command/bin/repo/push' 3 | require 'cocoapods-meitu-bin/command/bin/repo/update' 4 | 5 | module Pod 6 | class Command 7 | class Bin < Command 8 | class Repo < Bin 9 | self.abstract_command = true 10 | self.summary = '管理 spec 仓库' 11 | end 12 | end 13 | end 14 | end -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/command/bin/repo/push.rb: -------------------------------------------------------------------------------- 1 | 2 | require 'cocoapods-meitu-bin/config/config' 3 | require 'cocoapods-meitu-bin/native/podfile' 4 | 5 | module Pod 6 | class Command 7 | class Bin < Command 8 | class Repo < Bin 9 | class Push < Repo 10 | self.summary = '发布组件' 11 | self.description = <<-DESC 12 | #{self.summary} 13 | 跳过lint过程 14 | DESC 15 | 16 | self.arguments = [ 17 | CLAide::Argument.new('REPO', true ), 18 | CLAide::Argument.new('NAME.podspec', false ) 19 | ] 20 | 21 | def self.options 22 | [].concat(Pod::Command::Repo::Push.options).concat(super).uniq 23 | end 24 | 25 | def initialize(argv) 26 | @repo = argv.shift_argument 27 | @podspec = argv.shift_argument 28 | super 29 | 30 | @additional_args = argv.remainder! 31 | end 32 | 33 | def run 34 | argvs = [ 35 | @repo, 36 | *@additional_args 37 | ] 38 | 39 | push = Pod::Command::Repo::Push.new(CLAide::ARGV.new(argvs)) 40 | push.validate! 41 | push.run 42 | ensure 43 | clear_binary_spec_file_if_needed unless @reserve_created_spec 44 | end 45 | 46 | private 47 | 48 | # def template_spec_file 49 | # @template_spec_file ||= begin 50 | # if @template_podspec 51 | # find_spec_file(@template_podspec) 52 | # else 53 | # binary_template_spec_file 54 | # end 55 | # end 56 | # end 57 | # 58 | # def spec_file 59 | # @spec_file ||= begin 60 | # if @podspec 61 | # find_spec_file(@podspec) 62 | # else 63 | # if code_spec_files.empty? 64 | # raise Informative, '当前目录下没有找到可用源码 podspec.' 65 | # end 66 | # 67 | # spec_file = if @binary 68 | # code_spec = Pod::Specification.from_file(code_spec_files.first) 69 | # if template_spec_file 70 | # template_spec = Pod::Specification.from_file(template_spec_file) 71 | # end 72 | # create_binary_spec_file(code_spec, template_spec) 73 | # else 74 | # code_spec_files.first 75 | # end 76 | # spec_file 77 | # end 78 | # end 79 | # end 80 | # 81 | # def repo 82 | # @binary ? binary_source.name : code_source_list.first.name 83 | # end 84 | end 85 | end 86 | end 87 | end 88 | end -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/command/bin/repo/update.rb: -------------------------------------------------------------------------------- 1 | require 'parallel' 2 | 3 | module Pod 4 | class Command 5 | class Bin < Command 6 | class Repo < Bin 7 | class Update < Repo 8 | self.summary = '更新私有源' 9 | 10 | self.arguments = [ 11 | CLAide::Argument.new('NAME', false) 12 | ] 13 | 14 | def self.options 15 | [ 16 | ['--all', '更新所有私有源,默认只更新二进制相关私有源'] 17 | ].concat(super) 18 | end 19 | 20 | def initialize(argv) 21 | @all = argv.flag?('all') 22 | @name = argv.shift_argument 23 | super 24 | end 25 | 26 | def run 27 | if @all 28 | config.sources_manager.update() 29 | else 30 | valid_sources.map { |source| 31 | UI.puts "更新私有源仓库 #{source.to_s}" 32 | source.update(false) 33 | } 34 | end 35 | end 36 | 37 | 38 | end 39 | end 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/command/bin/source.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods-meitu-bin/command/bin/source/add' 2 | require 'cocoapods-meitu-bin/command/bin/source/list' 3 | require 'cocoapods-meitu-bin/command/bin/source/delete' 4 | 5 | module Pod 6 | class Command 7 | class Bin < Command 8 | class Source < Bin 9 | self.abstract_command = true 10 | self.summary = '管理二进制对应的源码' 11 | self.default_subcommand = 'list' 12 | 13 | # 目标路径 14 | def target_path(source_spec) 15 | "#{source_dir}/#{source_spec.name}/#{source_spec.version}" 16 | end 17 | 18 | # 存放源码的根目录 19 | def source_dir 20 | @source_dir ||= begin 21 | dir = "#{Dir.home}/LLDB_Sources" 22 | FileUtils.mkdir_p(dir) unless File.exist?(dir) 23 | dir 24 | end 25 | end 26 | end 27 | end 28 | end 29 | end 30 | 31 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/command/bin/source/add.rb: -------------------------------------------------------------------------------- 1 | 2 | module Pod 3 | class Command 4 | class Bin < Command 5 | class Source < Bin 6 | class Add < Source 7 | 8 | self.summary = '添加二进制对应源码' 9 | self.description = <<-DESC 10 | #{self.summary} 11 | DESC 12 | 13 | self.arguments = [ 14 | CLAide::Argument.new('NAMES', true ) 15 | ] 16 | 17 | def initialize(argv) 18 | @names = argv.shift_argument 19 | super 20 | end 21 | 22 | def run 23 | raise Informative, "请输入要添加的Pod库名,多个库中间用逗号隔开" if @names.nil? 24 | # 依赖分析 25 | @results = analyse 26 | # 遍历下载 27 | @names.split(',').each do |name| 28 | # 查找二进制spec 29 | bin_spec = find_specification(name) 30 | if bin_spec.nil? 31 | UI.puts "未查找到`#{name}`的二进制spec".red 32 | next 33 | end 34 | # 只有版本号最后一位带`bin`的才有源码 35 | bin_version = bin_spec.version.to_s 36 | unless has_source?(bin_version) 37 | UI.puts "`#{name} (#{bin_version})`已经是源码或无法查看源码".red 38 | next 39 | end 40 | # 查找源码spec 41 | source_spec = find_source_specification(name, bin_version) 42 | if source_spec.nil? 43 | UI.puts "未查找到`#{name}`的源码spec".red 44 | next 45 | end 46 | # 下载源码 47 | download_source(source_spec) 48 | end 49 | end 50 | 51 | private 52 | 53 | # 检查版本号最后一位是否是以`bin`开头的 54 | def has_source?(version) 55 | version.split('.').last.include?('bin') 56 | end 57 | 58 | # 查找二进制spec 59 | def find_specification(name) 60 | find_spec = nil 61 | @results.specifications.each do |spec| 62 | if spec.root.name.downcase == name.downcase 63 | find_spec = spec 64 | break 65 | end 66 | end 67 | find_spec.nil? ? nil : find_spec.root 68 | end 69 | 70 | # 查找源码spec 71 | def find_source_specification(name, version) 72 | source_version = get_source_version(version) 73 | # 根据 pod_name + version 查找spec 74 | podfile_sources = config.podfile.sources.uniq.map { |source| config.sources_manager.source_with_name_or_url(source) } 75 | sources = podfile_sources.select { |s| s.search(name) } 76 | source_spec = nil 77 | sources.each do |source| 78 | begin 79 | source_spec = source.specification(name, source_version) 80 | break 81 | rescue Pod::StandardError => e 82 | next 83 | end 84 | end 85 | source_spec.nil? ? nil : source_spec.root 86 | end 87 | 88 | # 下载源码 89 | def download_source(source_spec) 90 | UI.title "下载源码:#{source_spec.name} (#{source_spec.version})".green do 91 | target = target_path(source_spec) 92 | if exist?(source_spec) 93 | UI.puts "#{source_spec.name} (#{source_spec.version})源码已经存在".yellow 94 | return 95 | end 96 | download_request = Downloader::Request.new( 97 | :spec => source_spec, 98 | :released => true 99 | ) 100 | FileUtils.mkdir_p(target) unless File.exist?(target) 101 | Downloader.download(download_request, target, :can_cache => true) 102 | UI.puts "#{source_spec.name} (#{source_spec.version})源码下载完成!".green 103 | end 104 | end 105 | 106 | # 二进制文件路径 107 | def binary_file(name) 108 | "#{config.sandbox_root}/#{name}/#{name}.framework/#{name}" 109 | end 110 | 111 | # 依赖分析 112 | def analyse 113 | UI.title 'Analyze dependencies'.green do 114 | analyzer = Pod::Installer::Analyzer.new( 115 | config.sandbox, 116 | config.podfile, 117 | config.lockfile 118 | ) 119 | analyzer.analyze(true) 120 | end 121 | end 122 | 123 | # 获取源码版本号 124 | def get_source_version(version) 125 | source_version = version 126 | version_arr = version.split('.') 127 | if version_arr.last.include?('bin') 128 | version_arr.delete_at(version_arr.size - 1) 129 | source_version = version_arr.join('.') 130 | end 131 | source_version 132 | end 133 | 134 | # 是否存在对应源码 135 | def exist?(source_spec) 136 | target = target_path(source_spec) 137 | return false unless File.exist?(target) 138 | entries = Dir.entries(target).reject { |dir| dir == '.' || dir == '..' } 139 | return false if entries.empty? 140 | true 141 | end 142 | 143 | end 144 | end 145 | end 146 | end 147 | end 148 | 149 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/command/bin/source/delete.rb: -------------------------------------------------------------------------------- 1 | 2 | module Pod 3 | class Command 4 | class Bin < Command 5 | class Source < Bin 6 | class Delete < Source 7 | 8 | self.summary = '删除二进制对应的源码' 9 | self.description = <<-DESC 10 | #{self.summary} 11 | DESC 12 | 13 | self.arguments = [ 14 | CLAide::Argument.new('NAMES', true ) 15 | ] 16 | 17 | def self.options 18 | [ 19 | %w[--all 删除所有二进制对应的源码] 20 | ].concat(super).uniq 21 | end 22 | 23 | def initialize(argv) 24 | @names = argv.shift_argument 25 | @all = argv.flag?('all', false) 26 | super 27 | end 28 | 29 | def run 30 | if @names.nil? && !@all 31 | raise Informative, "请输入要删除的Pod库名(多个库中间用逗号分开)或者添加`--all`删除全部的源码" 32 | end 33 | if @all 34 | UI.puts "删除全部源码".yellow 35 | FileUtils.rm_rf(source_dir) 36 | UI.puts "删除完成".green 37 | return 38 | end 39 | unless @names.nil? 40 | name_arr = @names.split(',') 41 | name_arr.each do |name| 42 | UI.puts "删除`#{name}`".yellow 43 | dir = "#{source_dir}/#{name}" 44 | unless File.exist?(dir) 45 | UI.puts "`#{name}`不存在".red 46 | next 47 | end 48 | FileUtils.rm_rf(dir) 49 | UI.puts "删除`#{name}`成功".green 50 | end 51 | end 52 | end 53 | 54 | end 55 | end 56 | end 57 | end 58 | end 59 | 60 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/command/bin/source/list.rb: -------------------------------------------------------------------------------- 1 | 2 | module Pod 3 | class Command 4 | class Bin < Command 5 | class Source < Bin 6 | class List < Source 7 | 8 | SPECIAL_DIRS = %w[. .. .DS_Store].freeze 9 | 10 | self.summary = '打印二进制对应的源码' 11 | self.description = <<-DESC 12 | #{self.summary} 13 | DESC 14 | 15 | self.arguments = [ 16 | CLAide::Argument.new('NAMES', true ) 17 | ] 18 | 19 | def initialize(argv) 20 | @names = argv.shift_argument 21 | super 22 | end 23 | 24 | def run 25 | entries = Dir.entries(source_dir).reject { |dir| SPECIAL_DIRS.include?(dir) } 26 | unless @names.nil? 27 | name_arr = @names.split(',').map(&:downcase) 28 | entries.select! { |entry| name_arr.include?(entry.downcase) } 29 | end 30 | if entries.empty? 31 | UI.puts "无对应的源码".red 32 | return 33 | end 34 | entries.map do |entry| 35 | UI.puts "#{entry}".green 36 | sub_dir = "#{source_dir}/#{entry}" 37 | sub_entries = Dir.entries(sub_dir).reject { |dir| SPECIAL_DIRS.include?(dir) } 38 | sub_entries.map { |sub_entry| UI.puts " - #{sub_entry}".yellow } 39 | end 40 | end 41 | 42 | end 43 | end 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/command/bin/spec.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods-meitu-bin/command/bin/spec/create' 2 | require 'cocoapods-meitu-bin/command/bin/spec/lint' 3 | 4 | module Pod 5 | class Command 6 | class Bin < Command 7 | class Spec < Bin 8 | self.abstract_command = true 9 | self.summary = '管理二进制 spec' 10 | end 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/command/bin/spec/create.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods-meitu-bin/helpers' 2 | 3 | module Pod 4 | class Command 5 | class Bin < Command 6 | class Spec < Bin 7 | class Create < Spec 8 | self.summary = '创建二进制 spec' 9 | self.description = <<-DESC 10 | 根据源码 podspec 文件,创建对应的二进制 podspec 文件. 11 | DESC 12 | 13 | def self.options 14 | [ 15 | ['--platforms=ios', '生成二进制 spec 支持的平台'], 16 | ['--template-podspec=A.binary-template.podspec', '生成拥有 subspec 的二进制 spec 需要的模版 podspec, 插件会更改 version 和 source'], 17 | ['--no-overwrite', '不允许覆盖'] 18 | ].concat(super) 19 | end 20 | 21 | def initialize(argv) 22 | @platforms = argv.option('platforms', 'ios') 23 | @allow_overwrite = argv.flag?('overwrite', true) 24 | @template_podspec = argv.option('template-podspec') 25 | @podspec = argv.shift_argument 26 | super 27 | end 28 | 29 | def run 30 | UI.puts "开始读取 podspec 文件...\n" 31 | 32 | code_spec = Pod::Specification.from_file(spec_file) 33 | if template_spec_file 34 | template_spec = Pod::Specification.from_file(template_spec_file) 35 | end 36 | 37 | if binary_spec && !@allow_overwrite 38 | UI.warn "二进制 podspec 文件 #{binary_spec_files.first} 已存在.\n" 39 | else 40 | UI.puts "开始生成二进制 podspec 文件...\n" 41 | spec_file = create_binary_spec_file(code_spec, template_spec) 42 | UI.puts "创建二进制 podspec 文件 #{spec_file} 成功.\n".green 43 | end 44 | end 45 | 46 | def template_spec_file 47 | @template_spec_file ||= begin 48 | if @template_podspec 49 | find_spec_file(@template_podspec) 50 | else 51 | binary_template_spec_file 52 | end 53 | end 54 | end 55 | 56 | def spec_file 57 | @spec_file ||= begin 58 | if @podspec 59 | find_spec_file(@podspec) 60 | else 61 | if code_spec_files.empty? 62 | raise Informative, '当前目录下没有找到可用源码 podspec.' 63 | end 64 | 65 | code_spec_files.first 66 | end 67 | end 68 | end 69 | end 70 | end 71 | end 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/command/bin/spec/lint.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods-meitu-bin/config/config' 2 | require 'cocoapods-meitu-bin/native/podfile' 3 | 4 | module Pod 5 | class Command 6 | class Bin < Command 7 | class Spec < Bin 8 | class Lint < Spec 9 | self.summary = 'lint spec' 10 | self.description = <<-DESC 11 | spec lint 二进制组件 / 源码组件 12 | DESC 13 | 14 | self.arguments = [ 15 | CLAide::Argument.new(%w[NAME.podspec DIRECTORY http://PATH/NAME.podspec], false, true) 16 | ] 17 | 18 | def self.options 19 | [ 20 | ['--binary', 'lint 组件的二进制版本'], 21 | ['--template-podspec=A.binary-template.podspec', '生成拥有 subspec 的二进制 spec 需要的模版 podspec, 插件会更改 version 和 source'], 22 | ['--reserve-created-spec', '保留生成的二进制 spec 文件'], 23 | ['--code-dependencies', '使用源码依赖进行 lint'], 24 | ['--loose-options', '添加宽松的 options, 包括 --use-libraries (可能会造成 entry point (start) undefined)'], 25 | ['--allow-prerelease', '允许使用 prerelease 的版本 lint'] 26 | ].concat(Pod::Command::Spec::Lint.options).concat(super).uniq 27 | end 28 | 29 | def initialize(argv) 30 | @podspec = argv.shift_argument 31 | @loose_options = argv.flag?('loose-options') 32 | @code_dependencies = argv.flag?('code-dependencies') 33 | @sources = argv.option('sources') || [] 34 | @binary = argv.flag?('binary') 35 | @reserve_created_spec = argv.flag?('reserve-created-spec') 36 | @template_podspec = argv.option('template-podspec') 37 | @allow_prerelease = argv.flag?('allow-prerelease') 38 | super 39 | 40 | @additional_args = argv.remainder! 41 | end 42 | 43 | def run 44 | Podfile.execute_with_bin_plugin do 45 | Podfile.execute_with_allow_prerelease(@allow_prerelease) do 46 | Podfile.execute_with_use_binaries(!@code_dependencies) do 47 | argvs = [ 48 | "--sources=#{sources_option(@code_dependencies, @sources)}", 49 | *@additional_args 50 | ] 51 | 52 | argvs << spec_file if spec_file 53 | 54 | if @loose_options 55 | argvs += ['--allow-warnings'] 56 | if code_spec&.all_dependencies&.any? 57 | argvs << '--use-libraries' 58 | end 59 | end 60 | 61 | lint = Pod::Command::Spec::Lint.new(CLAide::ARGV.new(argvs)) 62 | lint.validate! 63 | lint.run 64 | end 65 | end 66 | end 67 | ensure 68 | clear_binary_spec_file_if_needed unless @reserve_created_spec 69 | end 70 | 71 | private 72 | 73 | def template_spec_file 74 | @template_spec_file ||= begin 75 | if @template_podspec 76 | find_spec_file(@template_podspec) 77 | else 78 | binary_template_spec_file 79 | end 80 | end 81 | end 82 | 83 | def spec_file 84 | @spec_file ||= begin 85 | if @podspec 86 | find_spec_file(@podspec) || @podspec 87 | else 88 | if code_spec_files.empty? 89 | raise Informative, '当前目录下没有找到可用源码 podspec.' 90 | end 91 | 92 | spec_file = if @binary 93 | code_spec = Pod::Specification.from_file(code_spec_files.first) 94 | if template_spec_file 95 | template_spec = Pod::Specification.from_file(template_spec_file) 96 | end 97 | create_binary_spec_file(code_spec, template_spec) 98 | else 99 | code_spec_files.first 100 | end 101 | spec_file 102 | end 103 | end 104 | end 105 | end 106 | end 107 | end 108 | end 109 | end 110 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/command/bin/update.rb: -------------------------------------------------------------------------------- 1 | 2 | require 'cocoapods' 3 | require 'cocoapods-meitu-bin/native/podfile_env' 4 | require 'cocoapods-meitu-bin/native/podfile' 5 | 6 | module Pod 7 | class Command 8 | class Bin < Command 9 | class Update < Bin 10 | include Pod 11 | include Pod::Podfile::DSL 12 | 13 | self.summary = 'pod update 拦截器' 14 | 15 | self.description = <<-DESC 16 | pod update 拦截器,会加载本地Podfile_local文件 17 | 会通过DSL加载到原始Podfile文件中 18 | 支持 pod 'xxx' 各种写法 19 | 支持 post_install/pre_install钩子,采用覆盖做法 20 | DESC 21 | def self.options 22 | [ 23 | ["--sources=#{Pod::TrunkSource::TRUNK_REPO_URL}", 'The sources from which to update dependent pods. ' \ 24 | 'Multiple sources must be comma-delimited'], 25 | ['--exclude-pods=podName', 'Pods to exclude during update. Multiple pods must be comma-delimited'], 26 | ['--clean-install', 'Ignore the contents of the project cache and force a full pod installation. This only ' \ 27 | 'applies to projects that have enabled incremental installation'], 28 | ['--project-directory=/project/dir/', 'The path to the root of the project directory'], 29 | ['--no-repo-update', 'Skip running `pod repo update` before install'] 30 | ].concat(super) 31 | end 32 | 33 | def initialize(argv) 34 | @update = argv.flag?('update') 35 | super 36 | @additional_args = argv.remainder! 37 | end 38 | 39 | def run 40 | Update.load_local_podfile 41 | 42 | argvs = [ 43 | *@additional_args 44 | ] 45 | 46 | gen = Pod::Command::Update.new(CLAide::ARGV.new(argvs)) 47 | gen.validate! 48 | gen.run 49 | end 50 | 51 | def self.load_local_podfile 52 | # 同步 Podfile_local 文件 53 | project_root = Pod::Config.instance.project_root 54 | path = File.join(project_root.to_s, 'Podfile_local') 55 | unless File.exist?(path) 56 | path = File.join(project_root.to_s, 'Podfile_local') 57 | end 58 | 59 | if File.exist?(path) 60 | contents = File.open(path, 'r:utf-8', &:read) 61 | 62 | podfile = Pod::Config.instance.podfile 63 | local_podfile = Podfile.from_file(path) 64 | 65 | if local_podfile 66 | local_pre_install_callback = nil 67 | local_post_install_callback = nil 68 | local_podfile.instance_eval do 69 | local_pre_install_callback = @pre_install_callback 70 | local_post_install_callback = @post_install_callback 71 | end 72 | end 73 | 74 | podfile.instance_eval do 75 | begin 76 | 77 | # podfile HASH_KEYS才有plugins字段,否则会被限制 78 | if local_podfile.plugins.any? 79 | hash_plugins = podfile.plugins || {} 80 | hash_plugins = hash_plugins.merge(local_podfile.plugins) 81 | set_hash_value(%w[plugins].first, hash_plugins) 82 | 83 | # 加入源码白名单,避免本地库被二进制了 84 | podfile.set_use_source_pods(local_podfile.use_source_pods) if local_podfile.use_source_pods 85 | podfile.use_binaries!(local_podfile.use_binaries?) 86 | end 87 | 88 | # 在target把local-target中到dependencies值删除了,再设置 89 | # 把本地和原始到dependencies 合并,设置dependencies 90 | local_podfile&.target_definition_list&.each do |local_target| 91 | next if local_target.name == 'Pods' 92 | 93 | target_definition_list.each do |target| 94 | 95 | unless target.name == local_target.name && 96 | (local_target.to_hash['dependencies'] &&local_target.to_hash['dependencies'].any?) 97 | next 98 | end 99 | 100 | 101 | 102 | target.instance_exec do 103 | # 在target把local-target中到dependencies值删除了,再设置 104 | 105 | local_dependencies = local_target.to_hash['dependencies'] 106 | target_dependencies = target.to_hash['dependencies'] 107 | 108 | local_dependencies.each do |local_dependency| 109 | unless local_dependency.is_a?(Hash) && local_dependency.keys.first 110 | next 111 | end 112 | 113 | target_dependencies.each do |target_dependency| 114 | next unless target_dependency.is_a?(Hash) && 115 | target_dependency.keys.first && 116 | target_dependency.keys.first == local_dependency.keys.first 117 | 118 | target_dependencies.delete target_dependency 119 | break 120 | end 121 | end 122 | # 把本地和原始到dependencies 合并,设置dependencies 123 | local_dependencies.each do |d| 124 | UI.message "Development Pod #{d.to_yaml}" 125 | if podfile.plugins.keys.include?('cocoapods-meitu-bin') 126 | podfile.set_use_source_pods(d.keys.first) if (d.is_a?(Hash) && d.keys.first) 127 | end 128 | end 129 | new_dependencies = target_dependencies + local_dependencies 130 | set_hash_value(%w[dependencies].first, new_dependencies) 131 | 132 | end 133 | end 134 | 135 | end 136 | 137 | if local_pre_install_callback 138 | @pre_install_callback = local_pre_install_callback 139 | end 140 | if local_post_install_callback 141 | @post_install_callback = local_post_install_callback 142 | end 143 | rescue Exception => e 144 | message = "Invalid `#{path}` file: #{e.message}" 145 | raise Pod::DSLError.new(message, path, e, contents) 146 | end 147 | end 148 | 149 | end 150 | end 151 | end 152 | end 153 | end 154 | end 155 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/command/bin/upload.rb: -------------------------------------------------------------------------------- 1 | require 'json' 2 | require 'cocoapods-meitu-bin/helpers/buildAll/bin_helper' 3 | 4 | module Pod 5 | class Command 6 | class Bin < Command 7 | class Upload < Bin 8 | include Pod 9 | 10 | self.summary = "上传二进制文件及podspec" 11 | self.description = <<-DESC 12 | #{self.summary} 13 | 14 | `NAME`: 库名【必填】\n 15 | `VERSION`: 版本号【必填】\n 16 | `FILE`: 需要压缩的二进制文件或目录【必填】\n 17 | `REPO`: 上传podspec的仓库,可以通过`pod repo list`查看【必填】\n 18 | 19 | e.g.:\n 20 | pod bin upload Pod1 1.0.0 Pod1.framework mtxxspecs --spec=Pod1.podspec 21 | DESC 22 | 23 | self.arguments = [ 24 | CLAide::Argument.new('NAME', true ), 25 | CLAide::Argument.new('VERSION', true), 26 | CLAide::Argument.new('FILE', true), 27 | CLAide::Argument.new('REPO', true) 28 | ] 29 | 30 | def self.options 31 | [ 32 | %w[--spec=SPEC 指定podspec文件路径,如果不指定,将在当前目录下查找] 33 | ].concat(super) 34 | end 35 | 36 | def initialize(argv) 37 | @name = argv.shift_argument 38 | @version = argv.shift_argument 39 | @file = argv.shift_argument 40 | @repo = argv.shift_argument 41 | @spec = argv.option('spec', nil) 42 | @xcode_version = CBin::BuildAll::BinHelper.xcode_version 43 | super 44 | end 45 | 46 | def run 47 | # 参数检查 48 | argvsCheck 49 | # 压缩文件 50 | zip_file 51 | # 上传文件 52 | upload_zip_file 53 | # 修改podspec 54 | modify_spec 55 | # 上传podspec 56 | upload_spec 57 | end 58 | 59 | private 60 | 61 | # 参数检查 62 | def argvsCheck 63 | raise Informative, "name不能为空" if @name.nil? 64 | raise Informative, "version不能为空" if @version.nil? 65 | raise Informative, "repo不能为空" if @repo.nil? 66 | raise Informative, "未找到需要压缩的二进制文件" if @file.nil? 67 | raise Informative, "未找到podspec文件" unless File.exist?(podspec) 68 | end 69 | 70 | # 压缩文件 71 | def zip_file 72 | UI.title "压缩二进制文件`#{zip_file_name}`".yellow do 73 | output_zip_file = File.join(Dir.pwd, zip_file_name) 74 | FileUtils.rm_f(output_zip_file) if File.exist?(output_zip_file) 75 | command = "zip --symlinks -r #{zip_file_name} #{@file}" 76 | UI.info "#{command}" 77 | `#{command}` 78 | raise Informative, "压缩二进制文件失败" unless File.exist?(output_zip_file) 79 | end 80 | end 81 | 82 | # 压缩后的zip包名 83 | def zip_file_name 84 | @zip_file_name ||= begin 85 | "#{@name}_#{@version}.zip" 86 | end 87 | end 88 | 89 | # 上传文件 90 | def upload_zip_file 91 | UI.title "上传二进制文件`#{zip_file_name}`".yellow do 92 | zip_file = File.join(Dir.pwd, zip_file_name) 93 | raise Informative, "`#{@zip_file_name}`不存在" unless File.exist?(zip_file) 94 | upload_url = CBin.config.binary_upload_url_str 95 | command = "curl -F \"name=#{@name}\" -F \"version=#{@version}\" -F \"file=@#{zip_file}\" #{upload_url}" 96 | UI.info "#{command}" 97 | json = `#{command}` 98 | UI.info json 99 | begin 100 | error_code = JSON.parse(json)["error_code"] 101 | raise Informative, "`#{zip_file_name}`上传失败" unless error_code == 0 102 | UI.info "`#{zip_file_name}`上传成功".green 103 | rescue JSON::ParserError => e 104 | raise Informative, "`#{zip_file_name}`上传失败\n#{e.to_s}" 105 | end 106 | end 107 | end 108 | 109 | # 修改podspec 110 | def modify_spec 111 | UI.title "修改podspec:`#{podspec}`".yellow do 112 | spec = Specification.from_file(podspec) 113 | spec_hash = spec.to_hash 114 | spec_hash['version'] = @version 115 | spec_hash['source'] = source 116 | spec = Specification.from_hash(spec_hash) 117 | write_podspec_json(spec) 118 | end 119 | end 120 | 121 | # 写入podspec 122 | def write_podspec_json(spec) 123 | FileUtils.rm_f(podspec_json_file) if File.exist?(podspec_json_file) 124 | File.open(podspec_json_file, "w+") do |f| 125 | f.write(spec.to_pretty_json) 126 | end 127 | end 128 | 129 | def source 130 | url = "#{CBin.config.binary_download_url_str}/#{@xcode_version}/#{@name}/#{@version}/#{zip_file_name}" 131 | { http: url, type: CBin.config.download_file_type } 132 | end 133 | 134 | # 上传podspec 135 | def upload_spec 136 | UI.title "推送podspec:`#{podspec_json_file_name}`".yellow do 137 | raise Informative, "`#{podspec_json_file_name}`不存在" unless File.exist?(podspec_json_file) 138 | argvs = %W[#{@repo} #{podspec_json_file} --skip-import-validation --use-libraries --allow-warnings --verbose] 139 | 140 | begin 141 | push = Pod::Command::Repo::Push.new(CLAide::ARGV.new(argvs)) 142 | push.validate! 143 | push.run 144 | rescue Pod::StandardError => e 145 | raise Informative, "推送podspec:`#{podspec_json_file_name}失败\n#{e.to_s}" 146 | end 147 | # 上传完成后,清理工作目录 148 | FileUtils.rm_f(podspec_json_file) if File.exist?(podspec_json_file) 149 | FileUtils.rm_r('binary') if File.exist?('binary') 150 | FileUtils.rm_f(zip_file_name) if File.exist?(zip_file_name) 151 | end 152 | end 153 | 154 | def binary_dir 155 | File.join(Dir.pwd, 'binary') 156 | end 157 | 158 | def podspec_json_file_name 159 | "#{@name}.podspec.json" 160 | end 161 | 162 | def podspec_json_file 163 | File.join(Dir.pwd, podspec_json_file_name) 164 | end 165 | 166 | def podspec 167 | @spec ||= begin 168 | "#{@name}.podspec" 169 | end 170 | end 171 | 172 | end 173 | end 174 | end 175 | end 176 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/config/config.rb: -------------------------------------------------------------------------------- 1 | require 'yaml' 2 | require 'cocoapods-meitu-bin/native/podfile' 3 | require 'cocoapods-meitu-bin/native/podfile_env' 4 | require 'cocoapods/generate' 5 | require 'cocoapods' 6 | module CBin 7 | class Config 8 | def config_file 9 | config_file_with_configuration_env(configuration_env) 10 | end 11 | 12 | def template_hash 13 | { 14 | 'configuration_env' => { description: '编译环境', default: 'dev', selection: %w[dev debug_iphoneos release_iphoneos] }, 15 | 'minimum_deployment_target' => { description: '最低部署目标,参与二进制版本生成', default: 'iOS12.0'}, 16 | 'bundle_identifier' => { description: '捆绑标识符,参与二进制版本生成', default: 'com.xxx.www' }, 17 | 'random_value' => { description: '随机值,参与二进制版本生成', default: 'a1b2c3d4f5' }, 18 | 'binary_repo_url' => { description: '二进制podspec私有源地址', default: 'git@github.com:Zhangyanshen/example-private-spec-bin.git' }, 19 | 'binary_upload_url' => { description: '二进制文件上传地址', default: 'http://localhost:8080/frameworks' }, 20 | 'binary_download_url' => { description: '二进制文件下载地址,后面会依次传入Xcode版本、configuration、组件名称与组件版本', default: 'http://localhost:8080/frameworks' }, 21 | 'download_file_type' => { description: '二进制文件类型', default: 'zip', selection: %w[zip tgz tar tbz txz dmg] } 22 | } 23 | end 24 | 25 | def config_file_with_configuration_env(configuration_env) 26 | file = config_dev_file 27 | if configuration_env == "release_iphoneos" 28 | file = config_release_iphoneos_file 29 | elsif configuration_env == "debug_iphoneos" 30 | file = config_debug_iphoneos_file 31 | elsif configuration_env == "dev" 32 | file = config_dev_file 33 | else 34 | raise "===== #{configuration_env} %w[dev debug_iphoneos release_iphoneos]====" 35 | end 36 | 37 | File.expand_path("#{Pod::Config.instance.project_root}/#{file}") 38 | end 39 | 40 | def configuration_env 41 | #如果是dev 再去 podfile的配置文件中获取,确保是正确的, pod update时会用到 42 | if @configuration_env == "dev" || @configuration_env == nil 43 | if Pod::Config.instance.podfile 44 | configuration_env ||= Pod::Config.instance.podfile.configuration_env 45 | end 46 | configuration_env ||= "dev" 47 | @configuration_env = configuration_env 48 | end 49 | @configuration_env 50 | end 51 | 52 | # 上传二进制的url 53 | def binary_upload_url_str 54 | CBin.config.binary_upload_url 55 | end 56 | 57 | # 下载二进制的url 58 | def binary_download_url_str 59 | CBin.config.binary_download_url 60 | end 61 | 62 | 63 | 64 | def set_configuration_env(env) 65 | @configuration_env = env 66 | end 67 | 68 | #包含arm64 armv7架构,xcodebuild 是Debug模式 69 | def config_debug_iphoneos_file 70 | ".bin_debug_iphoneos.yml" 71 | end 72 | #包含arm64 armv7架构,xcodebuild 是Release模式 73 | def config_release_iphoneos_file 74 | ".bin_release_iphoneos.yml" 75 | end 76 | #包含x86 arm64 armv7架构,xcodebuild 是Release模式 77 | def config_dev_file 78 | ".bin_dev.yml" 79 | end 80 | 81 | # 配置信息写入文件 82 | def sync_config(config) 83 | File.open(config_file_with_configuration_env(config['configuration_env']), 'w+') do |f| 84 | f.write(config.to_yaml) 85 | end 86 | end 87 | 88 | # def sync_config_code_repo_url_list(config) 89 | # File.open(config_file_with_configuration_env_list(config['code_repo_url_list']), 'w+') do |f| 90 | # f.write(config.to_yaml) 91 | # end 92 | # end 93 | 94 | def default_config 95 | @default_config ||= Hash[template_hash.map { |k, v| [k, v[:default]] }] 96 | end 97 | 98 | private 99 | 100 | def load_config 101 | if File.exist?(config_file) 102 | YAML.load_file(config_file) 103 | else 104 | default_config 105 | end 106 | end 107 | 108 | def config 109 | @config ||= begin 110 | @config = OpenStruct.new load_config 111 | validate! 112 | @config 113 | end 114 | end 115 | 116 | def validate! 117 | template_hash.each do |k, v| 118 | selection = v[:selection] 119 | next if !selection || selection.empty? 120 | 121 | config_value = @config.send(k) 122 | next unless config_value 123 | unless selection.include?(config_value) 124 | raise Pod::Informative, "#{k} 字段的值必须限定在可选值 [ #{selection.join(' / ')} ] 内".red 125 | end 126 | end 127 | end 128 | 129 | def respond_to_missing?(method, include_private = false) 130 | config.respond_to?(method) || super 131 | end 132 | 133 | def method_missing(method, *args, &block) 134 | if config.respond_to?(method) 135 | config.send(method, *args) 136 | elsif template_hash.keys.include?(method.to_s) 137 | raise Pod::Informative, "#{method} 字段必须在配置文件 #{config_file} 中设置, 请执行 init 命令配置或手动修改配置文件".red 138 | else 139 | super 140 | end 141 | end 142 | 143 | public 144 | 145 | # 判断配置文件是否存在 146 | def config_file_exist? 147 | raise Pod::Informative, "当前目录下没有配置文件,请先执行`pod bin init`" unless File.exist?(config_file) 148 | end 149 | end 150 | 151 | def self.config 152 | @config ||= Config.new 153 | end 154 | end 155 | 156 | class PodUpdateConfig 157 | @@pods = [] 158 | @@lockfile = nil 159 | @@repo_update = true 160 | @@prepare_time = 0 161 | @@checksum = nil 162 | @@large_pod_hash = {} 163 | @@is_mtxx = false 164 | @@is_clear = false 165 | @@shell_project = false 166 | 167 | 168 | def self.set_shell_project 169 | @@shell_project = true 170 | end 171 | def self.shell_project 172 | @@shell_project 173 | end 174 | def self.add_value(value) 175 | @@pods << value 176 | end 177 | def self.set_is_mtxx(value) 178 | @@is_mtxx = value 179 | end 180 | def self.is_mtxx() 181 | @@is_mtxx 182 | end 183 | def self.set_lockfile(path) 184 | @@lockfile = Pod::Lockfile.from_file(path) if path 185 | end 186 | def self.lockfile() 187 | @@lockfile 188 | end 189 | def self.set_checksum(checksum) 190 | @@checksum = checksum 191 | end 192 | def self.checksum 193 | @@checksum 194 | end 195 | def self.add_pod_hash(name,size) 196 | @@large_pod_hash[name]=size 197 | end 198 | def self.large_pod_hash 199 | @@large_pod_hash 200 | end 201 | 202 | # 一个类方法,用于显示数组中的值 203 | def self.repo_update 204 | @@repo_update 205 | end 206 | def self.set_repo_update 207 | @@repo_update = false 208 | end 209 | def self.set_prepare_time(time) 210 | @@prepare_time = time 211 | end 212 | def self.prepare_time 213 | @@prepare_time 214 | end 215 | 216 | def self.pods 217 | @@pods 218 | end 219 | def self.clear 220 | @@pods = [] 221 | @@lockfile = nil 222 | @@is_clear = true 223 | end 224 | def self.is_clear 225 | @@is_clear 226 | end 227 | end -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/config/config_asker.rb: -------------------------------------------------------------------------------- 1 | require 'yaml' 2 | require 'cocoapods-meitu-bin/config/config' 3 | 4 | module CBin 5 | class Config 6 | class Asker 7 | def show_prompt 8 | print ' > '.green 9 | end 10 | 11 | def ask_with_answer(question, pre_answer, selection) 12 | print "\n#{question}\n" 13 | 14 | print_selection_info = lambda { 15 | print "可选值:[ #{selection.join(' / ')} ]\n" if selection 16 | } 17 | print_selection_info.call 18 | print "旧值:#{pre_answer}\n" unless pre_answer.nil? 19 | 20 | answer = '' 21 | loop do 22 | show_prompt 23 | answer = STDIN.gets.chomp.strip 24 | 25 | if answer == '' && !pre_answer.nil? 26 | answer = pre_answer 27 | print answer.yellow 28 | print "\n" 29 | end 30 | 31 | next if answer.empty? 32 | break if !selection || selection.include?(answer) 33 | 34 | print_selection_info.call 35 | end 36 | 37 | answer 38 | end 39 | 40 | def welcome_message 41 | print <<~EOF 42 | 43 | 设置插件配置信息. 44 | 所有的信息都会保存在 #{CBin.config.config_file} 文件中. 45 | 你可以在对应目录下手动添加编辑该文件. 46 | 文件包含的配置信息样式如下: 47 | 48 | #{CBin.config.default_config.to_yaml} 49 | EOF 50 | end 51 | 52 | def done_message 53 | print "\n设置完成.\n".green 54 | end 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/config/config_builder.rb: -------------------------------------------------------------------------------- 1 | require 'yaml' 2 | 3 | module CBin 4 | class Config 5 | class Builder 6 | 7 | include Pod 8 | 9 | def self.instance 10 | @instance ||= new 11 | end 12 | 13 | def initialize 14 | load_build_config 15 | # clean 16 | end 17 | 18 | # 加载配置项 19 | def load_build_config 20 | @white_pod_list = [] 21 | @ignore_git_list = [] 22 | project_root = Pod::Config.instance.project_root 23 | path = File.join(project_root.to_s, 'BinArchive.json') 24 | 25 | if File.exist?(path) 26 | config = JSON.parse(File.read(path)) 27 | @white_pod_list = config['archive-white-pod-list'] 28 | UI.warn "====== archive-white-pod-list = #{@white_pod_list}" if @white_pod_list 29 | @ignore_git_list = config['ignore-git-list'] 30 | UI.warn "====== ignore_git_list = #{@ignore_git_list}" if @ignore_git_list 31 | @ignore_http_list = config['ignore-http-list'] 32 | 33 | @xcode_build_name = config['xcode_build_path'] 34 | @root_dir = config['root_dir'] unless config['root_dir'].nil? 35 | end 36 | 37 | end 38 | 39 | def clean 40 | #清除之前的缓存 41 | FileUtils.rm_rf(Dir.glob("#{zip_dir}/*")) if File.exist?(zip_dir) 42 | FileUtils.rm_rf(Dir.glob("#{binary_json_dir}/*")) if File.exist?(binary_json_dir) 43 | FileUtils.rm_rf(Dir.glob("#{local_psec_dir}/*")) if File.exist?(local_psec_dir) 44 | end 45 | 46 | # 制作二进制打包 工程目录 47 | def gen_name 48 | 'bin-archive' 49 | end 50 | 51 | # 制作二进制打包 工程目录 52 | def gen_dir 53 | @gen_dir ||= begin 54 | dir = File.join(root_dir,gen_name) 55 | Dir.mkdir(dir) unless File.exist?dir 56 | Pathname.new(dir) 57 | end 58 | end 59 | 60 | 61 | def framework_name(spec) 62 | "#{spec.module_name}.framework" 63 | end 64 | 65 | def framework_name_version(spec) 66 | "#{spec.module_name}.framework_#{spec.version}" 67 | end 68 | 69 | def framework_zip_file(spec) 70 | File.join(zip_dir_name, framework_name_version(spec)) 71 | end 72 | 73 | def framework_file(spec) 74 | File.join(zip_dir_name, framework_name(spec)) 75 | end 76 | 77 | def library_name(spec) 78 | library_name_version(spec.name, spec.version) 79 | end 80 | 81 | def library_name_version(name,version) 82 | "bin_#{name}_#{version}" 83 | end 84 | def library_file(spec) 85 | File.join(zip_dir_name, library_name(spec)) 86 | end 87 | 88 | def zip_dir_name 89 | "bin-zip" 90 | end 91 | 92 | def zip_dir 93 | @zip_dir ||= begin 94 | dir = File.join(root_dir,zip_dir_name) 95 | Dir.mkdir(dir) unless File.exist?dir 96 | Pathname.new(dir) 97 | end 98 | end 99 | 100 | #本地 101 | def local_spec_dir_name 102 | "bin-spec" 103 | end 104 | 105 | def local_psec_dir 106 | @local_psec_dir ||= begin 107 | dir = File.join(root_dir,local_spec_dir_name) 108 | Dir.mkdir(dir) unless File.exist?dir 109 | Pathname.new(dir) 110 | end 111 | end 112 | 113 | def binary_json_dir_name 114 | "bin-json" 115 | end 116 | 117 | def binary_json_dir 118 | @binary_json_dir ||= begin 119 | dir = File.join(root_dir,binary_json_dir_name) 120 | Dir.mkdir(dir) unless File.exist?dir 121 | Pathname.new(dir) 122 | end 123 | end 124 | 125 | 126 | 127 | #编译target名,如 seeyou 128 | def target_name 129 | @target_name ||= begin 130 | target_name_str = Pod::Config.instance.podfile.root_target_definitions.first.children.first.to_s 131 | target_name_str[5,target_name_str.length] 132 | end 133 | end 134 | 135 | #编译缓存文件目录,如Xcodebuild的编译缓存目录 136 | # 如果有配置, 配置完整路径,会使用 137 | def xcode_build_name 138 | @xcode_build_name ||= begin 139 | project_root = Pod::Config.instance.project_root 140 | path = File.join(project_root.to_s, 'BinArchive.json') 141 | 142 | if File.exist?(path) 143 | config = JSON.parse(File.read(path)) 144 | @xcode_build_name = config['xcode_build_path'] 145 | end 146 | #默认值,在美柚上使用默认 147 | if @xcode_build_name.nil? || Dir.exist?(@xcode_build_name) 148 | @xcode_build_name = "xcode-build/Build/Intermediates.noindex/ArchiveIntermediates/#{target_name}/IntermediateBuildFilesPath/UninstalledProducts/iphoneos/" 149 | end 150 | puts @xcode_build_name 151 | @xcode_build_name 152 | end 153 | end 154 | 155 | 156 | #完整的xcodebuild 输出路径 157 | def xcode_build_dir 158 | @xcode_build_dir ||= begin 159 | temp_xcode_build_name = xcode_build_name 160 | if File.exist?(temp_xcode_build_name) 161 | Pathname.new(temp_xcode_build_name) 162 | else 163 | dir = File.join(root_dir,xcode_build_name) 164 | Pathname.new(dir) 165 | end 166 | end 167 | end 168 | #完整的xcodebuild BuildProductsPath输出路径, 169 | def xcode_BuildProductsPath_dir 170 | @xcode_BuildProductsPath_dir ||= begin 171 | temp_xcode_BuildProductsPath_dir = "xcode-build/Build/Intermediates.noindex/ArchiveIntermediates/#{target_name}/BuildProductsPath/" 172 | full_path = File.join(root_dir, temp_xcode_BuildProductsPath_dir) 173 | 174 | if (File.exist?(full_path)) 175 | Dir.chdir(full_path) do 176 | iphoneos = Dir.glob('*-iphoneos') 177 | if iphoneos.length > 0 178 | full_path = File.join(full_path,iphoneos.first) 179 | else 180 | UI.warn "====== 找不到 *-iphoneos @xcode_BuildProductsPath_dir = #{@xcode_BuildProductsPath_dir}" 181 | end 182 | end 183 | end 184 | Pathname.new(full_path) 185 | end 186 | end 187 | 188 | 189 | #处理编译产物后存储根目录,会存放spec、 json、zip的父目录,默认是工程的同级目录下,"#{basename}-build-temp" 190 | def root_dir 191 | @root_dir ||= begin 192 | basename = File.basename(Pod::Config.instance.installation_root) 193 | parent_dir = File.dirname(Pod::Config.instance.installation_root) 194 | root_name = File.join(parent_dir,"#{basename}-build-temp") 195 | Dir.mkdir(root_name) unless File.exist?root_name 196 | Pathname.new(root_name) 197 | end 198 | 199 | end 200 | 201 | #制作二进制 白名单 202 | def white_pod_list 203 | @white_pod_list 204 | end 205 | #忽略制作二进制组件的 git 206 | def ignore_git_list 207 | @ignore_git_list 208 | end 209 | 210 | def ignore_http_list 211 | @ignore_http_list 212 | end 213 | 214 | end 215 | end 216 | end 217 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/gem_version.rb: -------------------------------------------------------------------------------- 1 | module CBin 2 | VERSION = "1.3.2" 3 | end 4 | 5 | module Pod 6 | def self.match_version?(*version) 7 | Gem::Dependency.new('', *version).match?('', Pod::VERSION) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/helpers.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods-meitu-bin/helpers/sources_helper' 2 | require 'cocoapods-meitu-bin/helpers/spec_creator' 3 | require 'cocoapods-meitu-bin/helpers/spec_files_helper' 4 | require 'cocoapods-meitu-bin/helpers/spec_source_creator' 5 | require 'cocoapods-meitu-bin/helpers/build_utils' 6 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/helpers/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meitu/cocoapods-meitu-bin/1cd24ac955938751530820027047e43f8f960fcb/lib/cocoapods-meitu-bin/helpers/Info.plist -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/helpers/buildAll/bin_helper.rb: -------------------------------------------------------------------------------- 1 | require 'digest' 2 | require 'cocoapods-meitu-bin/config/config' 3 | module CBin 4 | module BuildAll 5 | class BinHelper 6 | include Pod 7 | 8 | def initialize 9 | super 10 | @specs_str_md5_hash = Hash.new 11 | end 12 | 13 | # 二进制版本号(x.y.z.bin[md5前6位]) 14 | def version(pod_name, original_version, specifications, configuration = 'Debug', include_dependencies = false) 15 | # 有缓存从缓存中取,没有则新建 16 | if @specs_str_md5_hash[pod_name].nil? 17 | specs = specifications.map(&:name).select { |spec| 18 | spec.include?(pod_name) && !spec.include?('/Binary') 19 | }.sort! 20 | # puts "#{pod_name}:#{include_dependencies}" 21 | if include_dependencies 22 | specs << dependencies_str(pod_name, specifications) 23 | end 24 | specs << minimum_deployment_target_str 25 | specs << bundle_identifier_str 26 | specs << random_value_str 27 | specs << xcode_version 28 | specs << (configuration.nil? ? 'Debug' : configuration) 29 | specs_str = specs.join('') 30 | if ENV['p_bin_v'] == '1' 31 | UI.puts "`#{pod_name}`:#{specs_str}".red 32 | end 33 | specs_str_md5 = Digest::MD5.hexdigest(specs_str)[0,6] 34 | @specs_str_md5_hash[pod_name] = specs_str_md5 35 | else 36 | specs_str_md5 = @specs_str_md5_hash[pod_name] 37 | end 38 | "#{original_version}.bin#{specs_str_md5}" 39 | end 40 | 41 | #最低部署目标,参与二进制版本生成 42 | def minimum_deployment_target_str 43 | CBin.config.minimum_deployment_target 44 | end 45 | 46 | # 捆绑标识符,参与二进制版本生成 47 | def bundle_identifier_str 48 | CBin.config.bundle_identifier 49 | end 50 | # 随机值,参与二进制版本生成 51 | def random_value_str 52 | CBin.config.random_value 53 | end 54 | 55 | def xcode_version 56 | @xcode_version ||= begin 57 | `xcodebuild -version`.split(' ').join('') 58 | end 59 | end 60 | 61 | def self.xcode_version 62 | xcode_version = `xcodebuild -version`.split(' ').join('') 63 | xcode_version 64 | end 65 | 66 | # 将当前 Pod 库的依赖库拼接成字符串(格式:pod1_name(pod1_version),pod2_name(pod2_version),...) 67 | def dependencies_str(pod_name, specifications) 68 | deps = [] 69 | specifications.map do |spec| 70 | if spec.root.name == pod_name 71 | deps.concat(spec.dependencies) 72 | end 73 | end 74 | if deps.empty? 75 | UI.puts "`#{pod_name}`无依赖库".red if ENV['p_bin_d'] == '1' 76 | return '' 77 | end 78 | result = [] 79 | deps.uniq.map do |dep| 80 | if dep.root_name == pod_name 81 | next 82 | end 83 | version = dep_version(dep, specifications) 84 | result << "#{dep.name}(#{version})" 85 | end 86 | if ENV['p_bin_d'] == '1' 87 | if result.empty? 88 | UI.puts "`#{pod_name}`无依赖库".red 89 | else 90 | UI.puts "`#{pod_name}`依赖的库如下:".yellow 91 | result.map { |pod| puts "- #{pod}" } 92 | end 93 | end 94 | result.join(',') 95 | end 96 | 97 | # 获取依赖库版本号 98 | def dep_version(dep, specifications) 99 | version = '' 100 | specifications.map do |spec| 101 | if spec.root.name == dep.root_name 102 | version = spec.root.version.to_s 103 | # 如果是二进制版本,去掉二进制版本号后缀 104 | version_arr = version.split('.') 105 | if version_arr.last.include?('bin') 106 | version_arr.delete_at(version_arr.size - 1) 107 | version = version_arr.join('.') 108 | end 109 | break 110 | end 111 | end 112 | version 113 | end 114 | 115 | end 116 | end 117 | end 118 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/helpers/buildAll/podspec_util.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods-meitu-bin/config/config' 2 | module CBin 3 | module BuildAll 4 | class PodspecUtil 5 | include Pod 6 | 7 | def initialize(pod_target, version, build_as_framework = false, configuration = 'Debug') 8 | @pod_target = pod_target 9 | @version = version 10 | @build_as_framework = build_as_framework 11 | @configuration = configuration 12 | end 13 | 14 | # 创建二进制podspec 15 | def create_binary_podspec 16 | UI.info "创建二进制podspec:`#{@pod_target}`".yellow 17 | spec = @pod_target.root_spec.to_hash 18 | root_dir = @pod_target.framework_name 19 | # 处理版本号 20 | spec['version'] = version 21 | # 处理source 22 | spec['source'] = source 23 | # 处理头文件 24 | spec['source_files'] = "#{root_dir}/Headers/*.h" 25 | spec['public_header_files'] = "#{root_dir}/Headers/*.h" 26 | spec['private_header_files'] = "#{root_dir}/PrivateHeaders/*.h" 27 | # 处理vendored_libraries和vendored_frameworks 28 | spec['vendored_libraries'] = "#{root_dir}/libs/*.a" 29 | #兼容.xcframework 30 | spec['vendored_frameworks'] = %W[#{root_dir} #{root_dir}/fwks/*.framework] 31 | # 处理资源 32 | resources = %W[#{root_dir}/*.{#{special_resource_exts.join(',')}} #{root_dir}/resources/*] 33 | spec['resources'] = resources 34 | # 删除无用的字段 35 | delete_unused(spec) 36 | # 处理subspecs 37 | handle_subspecs(spec) 38 | # 生成二进制podspec 39 | bin_spec = Pod::Specification.from_hash(spec) 40 | bin_spec.description = <<-EOF 41 | 「converted automatically by plugin cocoapods-meitu-bin @美图 - zys」 42 | #{bin_spec.description} 43 | EOF 44 | bin_spec 45 | # puts bin_spec.to_json 46 | end 47 | 48 | # podspec写入文件 49 | def write_binary_podspec(spec) 50 | UI.info "写入podspec:`#{@pod_target}`".yellow 51 | podspec_dir = PodUpdateConfig.shell_project ? "#{Pathname.pwd}/all_build/Build/Products/podspec" : "#{Pathname.pwd}/build_pods/#{@pod_target}/Products/podspec" 52 | FileUtils.mkdir(podspec_dir) unless File.exist?(podspec_dir) 53 | file = "#{podspec_dir}/#{@pod_target.pod_name}.podspec.json" 54 | FileUtils.rm_rf(file) if File.exist?(file) 55 | File.open(file, "w+") do |f| 56 | f.write(spec.to_pretty_json) 57 | end 58 | file 59 | end 60 | 61 | # 上传二进制podspec 62 | def push_binary_podspec(binary_podsepc_json) 63 | UI.info "推送podspec:`#{@pod_target}`".yellow 64 | return unless File.exist?(binary_podsepc_json) 65 | repo_name = Pod::Config.instance.sources_manager.binary_source.name 66 | # repo_name = 'example-private-spec-bin' 67 | argvs = %W[#{repo_name} #{binary_podsepc_json} --skip-import-validation --use-libraries --allow-warnings --verbose] 68 | begin 69 | push = Pod::Command::Repo::Push.new(CLAide::ARGV.new(argvs)) 70 | push.validate! 71 | push.run 72 | return true 73 | rescue Pod::StandardError => e 74 | UI.info "推送podspec:`#{@pod_target}`失败,#{e.to_s}".red 75 | return false 76 | end 77 | end 78 | 79 | private 80 | 81 | # 删除无用的字段 82 | def delete_unused(spec) 83 | spec.delete('project_header_files') 84 | spec.delete('resource_bundles') 85 | spec.delete('exclude_files') 86 | spec.delete('preserve_paths') 87 | spec.delete('prepare_command') 88 | end 89 | 90 | # 处理subspecs 91 | def handle_subspecs(spec) 92 | spec['subspecs'].map do |subspec| 93 | # 处理单个subspec 94 | handle_single_subspec(subspec, spec) 95 | # 递归处理subspec 96 | recursive_handle_subspecs(subspec['subspecs'], spec) 97 | end if spec && spec['subspecs'] 98 | end 99 | 100 | # 递归处理subspecs 101 | def recursive_handle_subspecs(subspecs, spec) 102 | subspecs.map do |s| 103 | # 处理单个subspec 104 | handle_single_subspec(s, spec) 105 | # 递归处理 106 | recursive_handle_subspecs(s['subspecs'], spec) 107 | end if subspecs 108 | end 109 | 110 | # 处理单个subspec 111 | def handle_single_subspec(subspec, spec) 112 | subspec['source_files'] = spec['source_files'] 113 | subspec['public_header_files'] = spec['public_header_files'] 114 | subspec['private_header_files'] = spec['private_header_files'] 115 | subspec['vendored_frameworks'] = spec['vendored_frameworks'] 116 | subspec['vendored_libraries'] = spec['vendored_libraries'] 117 | subspec['resources'] = spec['resources'] 118 | # 删除无用字段 119 | delete_unused(subspec) 120 | end 121 | 122 | def source 123 | # url = "http://localhost:8080/frameworks/#{BinHelper.xcode_version}/#{@pod_target.root_spec.module_name}/#{version}/zip" 124 | url = "#{CBin.config.binary_download_url_str}/#{BinHelper.xcode_version}/#{@configuration}/#{@pod_target.root_spec.module_name}/#{version}/#{@pod_target.framework_name}_#{version}.zip" 125 | { http: url, type: CBin.config.download_file_type } 126 | end 127 | 128 | def version 129 | @version || @pod_target.root_spec.version 130 | end 131 | 132 | # 特殊的资源后缀 133 | def special_resource_exts 134 | %w[momd mom cdm nib storyboardc] 135 | end 136 | 137 | end 138 | end 139 | end 140 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/helpers/buildAll/zip_file_helper.rb: -------------------------------------------------------------------------------- 1 | require 'json' 2 | require 'cocoapods-meitu-bin/helpers/buildAll/bin_helper' 3 | 4 | module CBin 5 | module BuildAll 6 | class ZipFileHelper 7 | include Pod 8 | 9 | def initialize(pod_target, version, product_dir, build_as_framework = false, configuration = 'Debug') 10 | @pod_target = pod_target 11 | @version = version 12 | @product_dir = product_dir 13 | @build_as_framework = build_as_framework 14 | @configuration = configuration 15 | end 16 | 17 | # 上传静态库 18 | def upload_zip_lib 19 | Dir.chdir(@product_dir) do 20 | zip_file = File.join(Dir.pwd, "#{zip_file_name}") 21 | unless File.exist?(zip_file) 22 | UI.info "`#{Dir.pwd}`目录下无`#{zip_file_name}`文件".red 23 | return false 24 | end 25 | UI.info "Uploading binary zip file `#{@pod_target.root_spec.name} (#{version})`".yellow do 26 | upload_url = CBin.config.binary_upload_url_str 27 | xcode_version = BinHelper.xcode_version 28 | command = "curl -F \"name=#{@pod_target.product_module_name}\" -F \"version=#{version}\" -F \"xcode_version=#{xcode_version}\" -F \"configuration=#{@configuration}\" -F \"file=@#{zip_file}\" #{upload_url}" 29 | UI.info "#{command}" 30 | json = `#{command}` 31 | UI.info json 32 | begin 33 | success = JSON.parse(json)["success"] 34 | if success 35 | Pod::UI.info "#{@pod_target.root_spec.name} (#{version}) 上传成功".green 36 | return true 37 | else 38 | Pod::UI.info "#{@pod_target.root_spec.name} (#{version}) 上传失败".red 39 | return false 40 | end 41 | rescue JSON::ParserError => e 42 | Pod::UI.info "#{@pod_target.root_spec.name} (#{version}) 上传失败".red 43 | Pod::UI.info "#{e.to_s}".red 44 | return false 45 | end 46 | end 47 | end 48 | end 49 | 50 | # 压缩静态库 51 | def zip_lib 52 | Dir.chdir(@product_dir) do 53 | input_library = "#{@pod_target.framework_name}" 54 | output_library = File.join(Dir.pwd, zip_file_name) 55 | FileUtils.rm_f(output_library) if File.exist?(output_library) 56 | unless File.exist?(input_library) 57 | UI.info "没有需要压缩的二进制文件:`#{input_library}`".red 58 | return false 59 | end 60 | 61 | UI.info "Compressing `#{input_library}` into `#{zip_file_name}`".yellow do 62 | command = "zip --symlinks -r #{output_library} #{input_library}" 63 | UI.info "#{command}" 64 | `#{command}` 65 | unless File.exist?(output_library) 66 | UI.info "压缩`#{output_library}`失败".red 67 | return false 68 | end 69 | return true 70 | end 71 | end 72 | end 73 | 74 | # zip文件名字 75 | def zip_file_name 76 | @zip_file_name ||= begin 77 | "#{@pod_target.framework_name}_#{@version || @pod_target.root_spec.version}.zip" 78 | end 79 | end 80 | 81 | def version 82 | @version || @pod_target.root_spec.version 83 | end 84 | 85 | end 86 | end 87 | end 88 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/helpers/build_helper.rb: -------------------------------------------------------------------------------- 1 | # copy from https://github.com/CocoaPods/cocoapods-packager 2 | 3 | require 'cocoapods-meitu-bin/native/podfile' 4 | require 'cocoapods/command/gen' 5 | require 'cocoapods/generate' 6 | require 'cocoapods-meitu-bin/helpers/framework_builder' 7 | require 'cocoapods-meitu-bin/helpers/library_builder' 8 | require 'cocoapods-meitu-bin/config/config_builder' 9 | 10 | module CBin 11 | class Build 12 | class Helper 13 | include Pod 14 | #class var 15 | @@build_defines = "" 16 | #Debug下还待完成 17 | def initialize(spec, 18 | platform, 19 | framework_output, 20 | zip, 21 | rootSpec, 22 | skip_archive = false, 23 | build_model="Release", 24 | installer = nil) 25 | @spec = spec 26 | @platform = platform 27 | @build_model = build_model 28 | @rootSpec = rootSpec 29 | @isRootSpec = rootSpec.name == spec.name 30 | @skip_archive = skip_archive 31 | @framework_output = framework_output 32 | @zip = zip 33 | @installer = installer 34 | 35 | @framework_path 36 | end 37 | 38 | # 构建.a或.framework 39 | def build 40 | unless @skip_archive 41 | unless CBin::Build::Utils.is_framework(@spec) 42 | build_static_library 43 | zip_static_library 44 | else 45 | build_static_framework 46 | zip_static_framework 47 | end 48 | end 49 | end 50 | 51 | # 构建静态framework 52 | def build_static_framework 53 | UI.title("Building static framework #{@spec}") do 54 | source_dir = Dir.pwd 55 | # file_accessor = Sandbox::FileAccessor.new(Pathname.new('.').expand_path, @spec.consumer(@platform)) 56 | Dir.chdir(workspace_directory) do 57 | builder = CBin::Framework::Builder.new(@spec, @installer, @platform, source_dir, @isRootSpec, @build_model ) 58 | # 编译当前库 59 | @@build_defines = builder.build if @isRootSpec 60 | begin 61 | @framework_path = builder.lipo_create(@@build_defines) unless @skip_archive 62 | rescue 63 | @skip_archive = true 64 | end 65 | end 66 | end 67 | end 68 | 69 | # 构建library 70 | def build_static_library 71 | source_dir = zip_dir 72 | file_accessor = Sandbox::FileAccessor.new(Pathname.new('.').expand_path, @spec.consumer(@platform)) 73 | Dir.chdir(workspace_directory) do 74 | builder = CBin::Library::Builder.new(@spec, file_accessor, @platform, source_dir,@framework_path) 75 | builder.build 76 | end 77 | end 78 | 79 | # 压缩静态framework 80 | def zip_static_framework 81 | Dir.chdir(File.join(workspace_directory,@framework_path.root_path)) do 82 | output_name = File.join(zip_dir, framework_name_zip) 83 | unless File.exist?(framework_name) 84 | UI.info "没有需要压缩的 framework 文件:#{framework_name}" 85 | return 86 | end 87 | 88 | UI.title "Compressing #{framework_name} into #{output_name}" do 89 | Dir.mkdir(zip_dir) unless File.exist?(zip_dir) 90 | command = "zip --symlinks -r #{output_name} #{framework_name}" 91 | UI.info "#{command}" 92 | `#{command}` 93 | end 94 | end 95 | end 96 | 97 | # 压缩library 98 | def zip_static_library 99 | Dir.chdir(zip_dir) do 100 | output_library = "#{library_name}.zip" 101 | unless File.exist?(library_name) 102 | raise Informative, "没有需要压缩的 library 文件:#{library_name}" 103 | end 104 | 105 | UI.puts "Compressing #{library_name} into #{output_library}" 106 | `zip --symlinks -r #{output_library} #{library_name}` 107 | end 108 | 109 | end 110 | 111 | # 清理缓存 112 | def clean_workspace 113 | UI.puts 'Cleaning workspace' 114 | 115 | FileUtils.rm_rf(gen_name) 116 | Dir.chdir(zip_dir) do 117 | FileUtils.rm_rf(framework_name) if @zip 118 | FileUtils.rm_rf(library_name) 119 | FileUtils.rm_rf(framework_name) unless @framework_output 120 | FileUtils.rm_rf("#{framework_name}.zip") unless @framework_output 121 | end 122 | end 123 | 124 | def framework_name 125 | CBin::Config::Builder.instance.framework_name(@spec) 126 | end 127 | 128 | def framework_name_zip 129 | CBin::Config::Builder.instance.framework_name_version(@spec) + ".zip" 130 | end 131 | 132 | def library_name 133 | CBin::Config::Builder.instance.library_name(@spec) 134 | end 135 | 136 | # "PodA-build-temp/bin-archive/PodA" 137 | def workspace_directory 138 | File.expand_path("#{gen_name}/#{@rootSpec.name}") 139 | end 140 | 141 | def zip_dir 142 | CBin::Config::Builder.instance.zip_dir 143 | end 144 | 145 | def gen_name 146 | CBin::Config::Builder.instance.gen_dir 147 | end 148 | 149 | 150 | def spec_file 151 | @spec_file ||= begin 152 | if @podspec 153 | find_spec_file(@podspec) 154 | else 155 | if code_spec_files.empty? 156 | raise Informative, '当前目录下没有找到可用源码 podspec.' 157 | end 158 | 159 | spec_file = code_spec_files.first 160 | spec_file 161 | end 162 | end 163 | end 164 | 165 | end 166 | end 167 | end 168 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/helpers/build_utils.rb: -------------------------------------------------------------------------------- 1 | require 'yaml' 2 | require 'cocoapods-meitu-bin/config/config' 3 | 4 | module CBin 5 | class Build 6 | 7 | class Utils 8 | 9 | def Utils.is_framework(spec) 10 | if Utils.uses_frameworks? 11 | return true 12 | end 13 | 14 | return Utils.is_swift_module(spec) 15 | end 16 | 17 | def Utils.is_swift_module(spec) 18 | 19 | is_framework = false 20 | dir = File.join(CBin::Config::Builder.instance.gen_dir, CBin::Config::Builder.instance.target_name) 21 | #auto 走这里 22 | if File.exist?(dir) 23 | Dir.chdir(dir) do 24 | public_headers = Array.new 25 | spec_header_dir = "./Headers/Public/#{spec.name}" 26 | 27 | unless File.exist?(spec_header_dir) 28 | spec_header_dir = "./Pods/Headers/Public/#{spec.name}" 29 | end 30 | return false unless File.exist?(spec_header_dir) 31 | 32 | is_framework = File.exist?(File.join(spec_header_dir, "#{spec.name}-umbrella.h")) 33 | end 34 | end 35 | 36 | if $ARGV[1] == "local" 37 | is_framework = File.exist?(File.join(CBin::Config::Builder.instance.xcode_build_dir, "#{spec.name}.framework")) 38 | unless is_framework 39 | is_framework = File.exist?(File.join(CBin::Config::Builder.instance.xcode_BuildProductsPath_dir, "#{spec.name}","Swift Compatibility Header")) 40 | end 41 | end 42 | 43 | is_framework 44 | end 45 | 46 | def Utils.uses_frameworks? 47 | return true 48 | uses_frameworks = false 49 | Pod::Config.instance.podfile.target_definitions.each do |key,value| 50 | if key != "Pods" 51 | uses_frameworks = value.uses_frameworks? 52 | if uses_frameworks 53 | break ; 54 | end 55 | end 56 | end 57 | 58 | return uses_frameworks 59 | end 60 | 61 | end 62 | 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/helpers/framework.rb: -------------------------------------------------------------------------------- 1 | # copy from https://github.com/CocoaPods/cocoapods-packager 2 | 3 | module CBin 4 | class Framework 5 | attr_reader :headers_path 6 | attr_reader :module_map_path 7 | attr_reader :resources_path 8 | attr_reader :root_path 9 | attr_reader :versions_path 10 | attr_reader :swift_module_path 11 | attr_reader :fwk_path 12 | 13 | def initialize(name, platform) 14 | @name = name 15 | @platform = platform 16 | end 17 | 18 | def make 19 | make_root 20 | make_framework 21 | # make_headers 22 | # make_resources 23 | # make_current_version 24 | end 25 | 26 | def delete_resources 27 | Pathname.new(@resources_path).rmtree if File.exist? (@resources_path) 28 | (Pathname.new(@fwk_path) + Pathname.new('Resources')).delete if File.exist?(Pathname.new(@fwk_path) + Pathname.new('Resources')) 29 | end 30 | 31 | def remove_current_version 32 | FileUtils.rm_f(File.join(@fwk_path,@name)) 33 | FileUtils.rm_f(File.join(@fwk_path,"Headers")) 34 | FileUtils.rm_f(File.join(@fwk_path,"Resources")) 35 | 36 | FileUtils.cp_r("#{@versions_path}/.", @fwk_path) 37 | # FileUtils.remove_dir(@versions_path) 38 | FileUtils.remove_dir("#{@fwk_path}/Versions") 39 | 40 | # current_version_path = @versions_path + Pathname.new('../Current') 41 | # `ln -sf A #{current_version_path}` 42 | # `ln -sf Versions/Current/Headers #{@fwk_path}/` 43 | # `ln -sf Versions/Current/Resources #{@fwk_path}/` 44 | # `ln -sf Versions/Current/#{@name} #{@fwk_path}/` 45 | end 46 | 47 | private 48 | 49 | def make_current_version 50 | current_version_path = @versions_path + Pathname.new('../Current') 51 | `ln -sf A #{current_version_path}` 52 | `ln -sf Versions/Current/Headers #{@fwk_path}/` 53 | `ln -sf Versions/Current/Resources #{@fwk_path}/` 54 | `ln -sf Versions/Current/#{@name} #{@fwk_path}/` 55 | end 56 | 57 | 58 | 59 | def make_framework 60 | @fwk_path = @root_path + Pathname.new(@name + '.framework') 61 | @fwk_path.mkdir unless @fwk_path.exist? 62 | 63 | @module_map_path = @fwk_path + Pathname.new('Modules') 64 | @swift_module_path = @module_map_path + Pathname.new(@name + '.swiftmodule') 65 | 66 | 67 | # @versions_path = @fwk_path + Pathname.new('Versions/A') 68 | end 69 | 70 | def make_headers 71 | @headers_path = @versions_path + Pathname.new('Headers') 72 | @headers_path.mkpath unless @headers_path.exist? 73 | end 74 | 75 | def make_resources 76 | @resources_path = @versions_path + Pathname.new('Resources') 77 | @resources_path.mkpath unless @resources_path.exist? 78 | end 79 | 80 | # bin-archive/PodA/ios 81 | def make_root 82 | @root_path = Pathname.new(@platform) 83 | @root_path.mkpath unless @root_path.exist? 84 | end 85 | end 86 | end 87 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/helpers/library.rb: -------------------------------------------------------------------------------- 1 | # copy from https://github.com/CocoaPods/cocoapods-packager 2 | 3 | module CBin 4 | class Library 5 | attr_reader :headers_path 6 | attr_reader :resources_path 7 | attr_reader :root_path 8 | attr_reader :versions_path 9 | attr_reader :name_path 10 | 11 | def initialize(name, platform, version) 12 | @name = name 13 | @platform = platform 14 | @version = version 15 | end 16 | 17 | def make 18 | make_root 19 | make_library 20 | make_headers 21 | make_resources 22 | end 23 | 24 | def delete_resources 25 | Pathname.new(@resources_path).rmtree 26 | (Pathname.new(@fwk_path) + Pathname.new('Resources')).delete 27 | end 28 | 29 | private 30 | 31 | def make_library 32 | @name_path = CBin::Config::Builder.instance.library_name_version(@name,@version) 33 | @fwk_path = @root_path + Pathname.new(@name_path) 34 | @fwk_path.mkdir unless @fwk_path.exist? 35 | 36 | @versions_path = @fwk_path 37 | end 38 | 39 | def make_headers 40 | @headers_path = @versions_path + Pathname.new('Headers') 41 | @headers_path.mkpath unless @headers_path.exist? 42 | end 43 | 44 | def make_resources 45 | @resources_path = @versions_path + Pathname.new('Resources') 46 | @resources_path.mkpath unless @resources_path.exist? 47 | end 48 | 49 | def make_root 50 | @root_path = Pathname.new(@platform) 51 | @root_path.mkpath unless @root_path.exist? 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/helpers/library_builder.rb: -------------------------------------------------------------------------------- 1 | 2 | # copy from https://github.com/CocoaPods/cocoapods-packager 3 | 4 | require 'cocoapods-meitu-bin/helpers/framework.rb' 5 | require 'cocoapods-meitu-bin/helpers/library.rb' 6 | 7 | require 'English' 8 | 9 | module CBin 10 | class Library 11 | class Builder 12 | include Pod 13 | 14 | def initialize(spec, file_accessor, platform, source_dir,framework_path) 15 | @spec = spec 16 | @source_dir = source_dir 17 | @file_accessor = file_accessor 18 | @platform = platform 19 | @framework = framework_path 20 | @source_files = "#{@source_dir}/#{library.name_path}" 21 | @source_zip_file = "#{@source_files}.zip" 22 | end 23 | 24 | def build 25 | UI.section("Building static library #{@spec}") do 26 | 27 | clean_source_dir 28 | 29 | copy_headers 30 | copy_library 31 | copy_resources 32 | 33 | cp_to_source_dir 34 | end 35 | end 36 | 37 | private 38 | 39 | def clean_source_dir 40 | FileUtils.rm_rf(@source_files) if File.exist?(@source_files) 41 | FileUtils.rm_rf(@source_zip_file) if File.exist?(@source_zip_file) 42 | end 43 | 44 | def cp_to_source_dir 45 | target_dir = library.versions_path 46 | dest_file = "#{@source_dir}/#{library.name_path}" 47 | FileUtils.rm_rf(dest_file) if File.exist?(dest_file) 48 | 49 | `cp -fa #{target_dir} #{dest_file}/` 50 | end 51 | 52 | def copy_headers 53 | FileUtils.cp_r(framework.headers_path,library.versions_path) if File.exist?(framework.headers_path) 54 | end 55 | 56 | def copy_library 57 | src_file = "#{framework.versions_path}/#{@spec.name}" 58 | unless File.exist?(src_file) 59 | raise Informative, "framework没有文件:#{src_file}" 60 | end 61 | 62 | dest_file = "#{library.versions_path}/#{@spec.name}" 63 | rename_dest_file = "#{library.versions_path}/lib#{@spec.name}.a" 64 | FileUtils.cp_r(src_file,dest_file) 65 | File.rename(dest_file, rename_dest_file ) if File.exist?(dest_file) 66 | end 67 | 68 | def copy_resources 69 | FileUtils.cp_r(framework.resources_path,library.versions_path) if File.exist?(framework.resources_path) 70 | end 71 | 72 | 73 | def framework 74 | @framework ||= begin 75 | framework = Framework.new(@spec.name, @platform.name.to_s) 76 | framework.make 77 | framework 78 | end 79 | end 80 | 81 | def library 82 | @library ||= begin 83 | library = Library.new(@spec.name, @platform.name.to_s,@spec.version) 84 | library.make 85 | library 86 | end 87 | end 88 | end 89 | end 90 | end 91 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/helpers/pod_size_helper.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods-meitu-bin/config/config' 2 | module CBin 3 | class PodSize 4 | include Pod 5 | 6 | # 多线程锁 7 | @@lock = Mutex.new 8 | # 阈值,单位MB 9 | @@size_threshold = 500 10 | # 存放过大Pod信息的临时文件 11 | @@tmp_file_path = File.join(Dir.pwd, '.mtxx_big_pods.log') 12 | 13 | # 添加超过阈值的pod 14 | def self.add_pod(pod) 15 | if pod[:size].to_i < @@size_threshold * 1024 16 | return 17 | end 18 | @@lock.synchronize do 19 | size = pod[:size].to_i 20 | size = ('%.0f' % (size / 1024)).to_f 21 | PodUpdateConfig.add_pod_hash(pod[:name],size) 22 | File.open(@@tmp_file_path, "a") do |f| 23 | f.write(format_pod_size(pod)) 24 | end 25 | end 26 | end 27 | 28 | # 格式化pod 29 | def self.format_pod_size(pod) 30 | unit = 'KB' 31 | size = pod[:size].to_i 32 | if size >= 1024 * 1024 33 | unit = 'GB' 34 | size = ('%.1f' % (size / 1024.0 / 1024.0)).to_f 35 | elsif size >= 1024 36 | unit = 'MB' 37 | size = ('%.1f' % (size / 1024.0)).to_f 38 | end 39 | "#{pod[:name]}:#{size}#{unit}\n" 40 | end 41 | 42 | # 打印超过阈值的Pod库 43 | def self.print_pods 44 | unless File.exist?(@@tmp_file_path) 45 | return 46 | end 47 | UI.puts "\n" 48 | UI.puts "以下Pod库下载大小大于阈值`#{@@size_threshold}MB`:".green 49 | File.open(@@tmp_file_path, "r") do |f| 50 | f.readlines.map do |line| 51 | UI.puts " - #{line.strip}".green 52 | end 53 | end 54 | # 打印完成后,删除临时文件 55 | FileUtils.rm_f(@@tmp_file_path) if File.exist?(@@tmp_file_path) 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/helpers/sources_helper.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'cocoapods-meitu-bin/native/sources_manager.rb' 4 | 5 | module CBin 6 | module SourcesHelper 7 | def sources_manager 8 | Pod::Config.instance.sources_manager 9 | end 10 | 11 | def binary_source 12 | sources_manager.binary_source 13 | end 14 | 15 | def code_source_list 16 | sources_manager.code_source_list 17 | end 18 | # 优先采用对应依赖的 source 19 | # cocoapods 内部会先匹配前面符合的 specification 20 | # 只允许二进制的 specification subspec 比源码的 specification subspec 多 21 | # 22 | def valid_sources(code_dependencies = false) 23 | sources = code_source_list 24 | unless code_dependencies 25 | sources << binary_source 26 | sources.reverse! 27 | end 28 | sources 29 | end 30 | 31 | def sources_option(code_dependencies, additional_sources) 32 | (valid_sources(code_dependencies).map(&:url) + Array(additional_sources)).join(',') 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/helpers/spec_creator.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'cocoapods' 4 | require 'cocoapods-meitu-bin/config/config' 5 | 6 | module CBin 7 | class Specification 8 | class Creator 9 | attr_reader :code_spec 10 | attr_reader :template_spec 11 | attr_reader :spec 12 | 13 | def initialize(code_spec, template_spec, platforms = 'ios') 14 | @code_spec = code_spec 15 | @template_spec = template_spec 16 | @platforms = Array(platforms) 17 | validate! 18 | end 19 | 20 | def validate! 21 | raise Pod::Informative, '源码 podspec 不能为空 .' unless code_spec 22 | if code_spec.subspecs.any? && template_spec.nil? 23 | raise Pod::Informative, "不支持自动生成存在 subspec 的二进制 podspec , 需要提供模版文件 #{code_spec.name}.binary.podspec.template ." 24 | end 25 | end 26 | 27 | def create 28 | spec = template_spec ? create_from_code_spec_and_template_spec : create_from_code_spec 29 | 30 | Pod::UI.message '生成二进制 podspec 内容: ' 31 | spec.to_pretty_json.split("\n").each do |text| 32 | Pod::UI.message text 33 | end 34 | 35 | spec 36 | end 37 | 38 | def write_spec_file(file = filename) 39 | create unless spec 40 | 41 | File.open(file, 'w+') do |f| 42 | f.write(spec.to_pretty_json) 43 | end 44 | 45 | @filename = file 46 | end 47 | 48 | def clear_spec_file 49 | File.delete(filename) if File.exist?(filename) 50 | end 51 | 52 | def filename 53 | @filename ||= "#{spec.name}.binary.podspec.json" 54 | end 55 | 56 | private 57 | 58 | def create_from_code_spec 59 | @spec = code_spec.dup 60 | # vendored_frameworks | resources | source | source_files | public_header_files 61 | # license | resource_bundles | vendored_libraries 62 | 63 | # Project Linkin 64 | @spec.vendored_frameworks = "#{code_spec.root.name}.framework" 65 | 66 | # Resources 67 | extnames = [] 68 | extnames << '*.bundle' if code_spec_consumer.resource_bundles.any? 69 | if code_spec_consumer.resources.any? 70 | extnames += code_spec_consumer.resources.map { |r| File.basename(r) } 71 | end 72 | if extnames.any? 73 | @spec.resources = framework_contents('Resources').flat_map { |r| extnames.map { |e| "#{r}/#{e}" } } 74 | end 75 | 76 | # Source Location 77 | @spec.source = binary_source 78 | 79 | # Source Code 80 | @spec.source_files = framework_contents('Headers/*') 81 | @spec.public_header_files = framework_contents('Headers/*') 82 | 83 | # Unused for binary 84 | spec_hash = @spec.to_hash 85 | # spec_hash.delete('license') 86 | spec_hash.delete('resource_bundles') 87 | spec_hash.delete('exclude_files') 88 | spec_hash.delete('preserve_paths') 89 | # 这里不确定 vendored_libraries 指定的时动态/静态库 90 | # 如果是静态库的话,需要移除,否则就不移除 91 | # 最好是静态库都独立成 Pod ,cocoapods-package 打静态库去 collect 目标文件时好做过滤 92 | # 这里统一只对命名后缀 .a 文件做处理 93 | # spec_hash.delete('vendored_libraries') 94 | # libraries 只能假设为动态库不做处理了,如果有例外,需要开发者自行处理 95 | spec_hash.delete('vendored_libraries') 96 | spec_hash['vendored_libraries'] = binary_vendored_libraries 97 | 98 | # vendored_libraries = Array(vendored_libraries).reject { |l| l.end_with?('.a') } 99 | # if vendored_libraries.any? 100 | # spec_hash['vendored_libraries'] = vendored_libraries 101 | # end 102 | 103 | # Filter platforms 104 | platforms = spec_hash['platforms'] 105 | selected_platforms = platforms.select { |k, _v| @platforms.include?(k) } 106 | spec_hash['platforms'] = selected_platforms.empty? ? platforms : selected_platforms 107 | 108 | @spec = Pod::Specification.from_hash(spec_hash) 109 | @spec 110 | end 111 | 112 | def create_from_code_spec_and_template_spec 113 | @spec = template_spec.dup 114 | 115 | @spec.version = code_spec.version 116 | @spec.source = binary_source 117 | 118 | @spec.source_files = binary_source_files 119 | @spec.public_header_files = binary_public_header_files 120 | @spec.vendored_libraries = binary_vendored_libraries 121 | 122 | @spec.resources = binary_resources if @spec.attributes_hash.keys.include?("resources") 123 | 124 | 125 | 126 | @spec 127 | end 128 | 129 | def binary_source 130 | { http: format(CBin.config.binary_download_url), type: CBin.config.download_file_type } 131 | end 132 | 133 | def code_spec_consumer(_platform = :ios) 134 | code_spec.consumer(:ios) 135 | end 136 | 137 | def framework_contents(name) 138 | ["#{code_spec.root.name}.framework"].map { |path| "#{path}/#{name}" } 139 | end 140 | 141 | def binary_source_files 142 | { http: format(CBin.config.binary_download_url), type: CBin.config.download_file_type } 143 | end 144 | 145 | def binary_source_files 146 | "bin_#{code_spec.name}_#{code_spec.version}/Headers/*" 147 | end 148 | 149 | def binary_public_header_files 150 | "bin_#{code_spec.name}_#{code_spec.version}/Headers/*.h" 151 | end 152 | 153 | def binary_vendored_libraries 154 | "bin_#{code_spec.name}_#{code_spec.version}/*.a" 155 | end 156 | 157 | def binary_resources 158 | "bin_#{code_spec.name}_#{code_spec.version}/Resources/*" 159 | end 160 | 161 | end 162 | end 163 | end 164 | #模板框架begin 165 | # s.source_files = "bin_#{s.name}_#{s.version}/Headers/*" 166 | # s.public_header_files = "bin_#{s.name}_#{s.version}/Headers/*.h" 167 | # s.vendored_libraries = "bin_#{s.name}_#{s.version}/*.a" 168 | #有图片资源的,要带上 169 | #s.resources = 'bin_#{s.name}_#{s.version}/Resources/*.{json,png,jpg,gif,js,xib,eot,svg,ttf,woff,db,sqlite,mp3,bundle}' 170 | #模板框架end -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/helpers/spec_files_helper.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'cocoapods-meitu-bin/native/sources_manager.rb' 4 | require 'cocoapods-meitu-bin/helpers/spec_creator' 5 | 6 | module CBin 7 | module SpecFilesHelper 8 | def spec_files 9 | @spec_files ||= Pathname.glob('*.podspec{,.json}') 10 | end 11 | 12 | def binary_spec_files 13 | @binary_spec_files ||= Pathname.glob('*.binary.podspec{,.json}') 14 | end 15 | 16 | def binary_template_spec_files 17 | @binary_spec_template_files ||= Pathname.glob('*.binary-template.podspec{,.json}') 18 | end 19 | 20 | def binary_template_spec_file 21 | @binary_spec_template_file ||= binary_template_spec_files.first 22 | end 23 | 24 | def code_spec_files 25 | @code_spec_files ||= spec_files - binary_spec_files - binary_template_spec_files 26 | end 27 | 28 | def code_spec 29 | if code_spec_files.first 30 | Pod::Specification.from_file(code_spec_files.first) 31 | end 32 | end 33 | 34 | def binary_spec 35 | if binary_spec_files.first 36 | Pod::Specification.from_file(binary_spec_files.first) 37 | end 38 | end 39 | 40 | def binary_template_spec 41 | if binary_template_spec_file 42 | Pod::Specification.from_file(binary_template_spec_file) 43 | end 44 | end 45 | 46 | def find_spec_file(podspec) 47 | path = Pathname(podspec) 48 | raise Pod::Informative, "无法找到 #{podspec}" unless path.exist? 49 | 50 | path 51 | end 52 | 53 | def create_binary_spec_file(code_spec, template_spec) 54 | # 1. code spec 是否有 subpsec 55 | # 1.1 有,查找 template spec,并生成 56 | # 1.2 没有,是否有 template spec 57 | # 1.2.1 有,根据 template spec 生成 58 | # 1.2.2 没有,根据 code spec 生成 59 | 60 | unless code_spec 61 | raise Pod::Informative, '没有二进制 podspec 的情况下,必须要提供源码 podspec.' 62 | end 63 | # if code_spec.subspecs.any? && template_spec.nil? 64 | # raise Pod::Informative, '拥有 subspec 的组件,在生成二进制 podspec 时,必须要提供模版 podspec.' 65 | # end 66 | 67 | Dir.chdir(CBin::Config::Builder.instance.root_dir) do 68 | @spec_creator = CBin::SpecificationSource::Creator.new(code_spec) 69 | # @spec_creator = CBin::Specification::Creator.new(code_spec, template_spec) 70 | @spec_creator.create 71 | @spec_creator.write_spec_file 72 | @spec_creator.filename 73 | end 74 | end 75 | 76 | def clear_binary_spec_file_if_needed 77 | @spec_creator&.clear_spec_file 78 | end 79 | end 80 | end 81 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/helpers/upload_helper.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | # copy from https://github.com/CocoaPods/cocoapods-packager 4 | 5 | require 'cocoapods-meitu-bin/native/podfile' 6 | require 'cocoapods/command/gen' 7 | require 'cocoapods/generate' 8 | require 'cocoapods-meitu-bin/helpers/framework_builder' 9 | require 'cocoapods-meitu-bin/helpers/library_builder' 10 | require 'cocoapods-meitu-bin/helpers/sources_helper' 11 | require 'cocoapods-meitu-bin/command/bin/repo/push' 12 | require 'json' 13 | 14 | module CBin 15 | class Upload 16 | class Helper 17 | include CBin::SourcesHelper 18 | 19 | def initialize(spec,code_dependencies,sources) 20 | @spec = spec 21 | @code_dependencies = code_dependencies 22 | @sources = sources 23 | end 24 | 25 | # 创建binary-template.podsepc 26 | # 上传二进制文件 27 | # 上传二进制 podspec 28 | def upload 29 | Dir.chdir(CBin::Config::Builder.instance.root_dir) do 30 | # 上传zip包 31 | res_zip = curl_zip 32 | if res_zip 33 | # 创建二进制podspec 34 | filename = spec_creator 35 | # 上传二进制 podspec 36 | push_binary_repo(filename) 37 | end 38 | res_zip 39 | end 40 | end 41 | 42 | # 创建二进制podspec 43 | def spec_creator 44 | spec_creator = CBin::SpecificationSource::Creator.new(@spec) 45 | # 创建二进制podspec 46 | spec_creator.create 47 | # 将二进制podspec写入文件 48 | spec_creator.write_spec_file 49 | # 返回二进制podspec文件路径 50 | spec_creator.filename 51 | end 52 | 53 | # 推送二进制 54 | # curl http://ci.xxx:9192/frameworks -F "name=IMYFoundation" -F "version=7.7.4.2" -F "annotate=IMYFoundation_7.7.4.2_log" -F "file=@bin_zip/bin_IMYFoundation_7.7.4.2.zip" 55 | def curl_zip 56 | # output_name = File.join(CBin::Config::Builder.instance.zip_dir, CBin::Config::Builder.instance.framework_name_zip) 57 | zip_file = "#{CBin::Config::Builder.instance.library_file(@spec)}.zip" 58 | res = File.exist?(zip_file) 59 | unless res 60 | zip_file = CBin::Config::Builder.instance.framework_zip_file(@spec) + ".zip" 61 | res = File.exist?(zip_file) 62 | end 63 | if res 64 | Pod::UI.title "Uploading binary zip file #{@spec.name} (#{@spec.version})" do 65 | command = "curl -F \"name=#{@spec.module_name}\" -F \"version=#{@spec.version}\" -F \"file=@#{zip_file}\" #{CBin.config.binary_upload_url_str}" 66 | Pod::UI.info "#{command}" 67 | json = `#{command}` 68 | Pod::UI.info json 69 | error_code = JSON.parse(json)["error_code"] 70 | if error_code == 0 71 | Pod::UI.info "#{@spec.name} (#{@spec.version}) 上传成功".green 72 | else 73 | Pod::UI.info "#{@spec.name} (#{@spec.version}) 上传失败".red 74 | raise Informative, "#{@spec.name} (#{@spec.version}) 上传失败" 75 | end 76 | end 77 | end 78 | res 79 | end 80 | 81 | # 上传二进制 podspec 82 | def push_binary_repo(binary_podsepc_json) 83 | argvs = [ 84 | "#{binary_podsepc_json}", 85 | "--binary", 86 | "--sources=#{sources_option(@code_dependencies, @sources)},https:\/\/cdn.cocoapods.org", 87 | "--skip-import-validation", 88 | "--use-libraries", 89 | "--allow-warnings", 90 | "--verbose", 91 | "--code-dependencies" 92 | ] 93 | if @verbose 94 | argvs += ['--verbose'] 95 | end 96 | 97 | push = Pod::Command::Bin::Repo::Push.new(CLAide::ARGV.new(argvs)) 98 | push.validate! 99 | push.run 100 | end 101 | 102 | end 103 | end 104 | end 105 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/native.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods' 2 | require 'cocoapods-meitu-bin/gem_version' 3 | 4 | if Pod.match_version?('~> 1.4') 5 | require 'cocoapods-meitu-bin/native/podfile' 6 | require 'cocoapods-meitu-bin/native/installation_options' 7 | require 'cocoapods-meitu-bin/native/specification' 8 | require 'cocoapods-meitu-bin/native/path_source' 9 | require 'cocoapods-meitu-bin/native/analyzer' 10 | require 'cocoapods-meitu-bin/native/installer' 11 | require 'cocoapods-meitu-bin/native/podfile_generator' 12 | require 'cocoapods-meitu-bin/native/pod_source_installer' 13 | require 'cocoapods-meitu-bin/native/linter' 14 | require 'cocoapods-meitu-bin/native/resolver' 15 | require 'cocoapods-meitu-bin/native/source' 16 | require 'cocoapods-meitu-bin/native/validator' 17 | require 'cocoapods-meitu-bin/native/acknowledgements' 18 | require 'cocoapods-meitu-bin/native/sandbox_analyzer' 19 | require 'cocoapods-meitu-bin/native/podspec_finder' 20 | require 'cocoapods-meitu-bin/native/file_accessor' 21 | require 'cocoapods-meitu-bin/native/pod_target_installer' 22 | require 'cocoapods-meitu-bin/native/target_validator' 23 | require 'cocoapods-meitu-bin/native/gen' 24 | require 'cocoapods-meitu-bin/native/lockfile' 25 | 26 | end 27 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/native/acknowledgements.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Pod 4 | module Generator 5 | class Acknowledgements 6 | def license_text(spec) 7 | return nil unless spec.license 8 | 9 | text = spec.license[:text] 10 | unless text 11 | if license_file = spec.license[:file] 12 | license_path = file_accessor(spec).root + license_file 13 | if File.exist?(license_path) 14 | text = IO.read(license_path) 15 | else 16 | # UI.warn "Unable to read the license file `#{license_file}` " \ 17 | # "for the spec `#{spec}`" 18 | end 19 | elsif license_file = file_accessor(spec).license 20 | text = IO.read(license_file) 21 | end 22 | end 23 | text 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/native/analyzer.rb: -------------------------------------------------------------------------------- 1 | 2 | require 'parallel' 3 | require 'cocoapods' 4 | 5 | module Pod 6 | class Installer 7 | class Analyzer 8 | alias old_fetch_external_sources fetch_external_sources 9 | def fetch_external_sources(podfile_state) 10 | verify_no_pods_with_different_sources! 11 | deps = dependencies_to_fetch(podfile_state) 12 | pods = pods_to_fetch(podfile_state) 13 | return if deps.empty? 14 | UI.section 'Fetching external sources' do 15 | if installation_options.install_with_multi_threads 16 | thread_count = installation_options.multi_threads_count 17 | Parallel.each(deps.sort, in_threads: thread_count) do |dependency| 18 | fetch_external_source(dependency, !pods.include?(dependency.root_name)) 19 | end 20 | else 21 | deps.sort.each do |dependency| 22 | fetch_external_source(dependency, !pods.include?(dependency.root_name)) 23 | end 24 | end 25 | end 26 | end 27 | 28 | # > 1.6.0 29 | # all_specs[dep.name] 为 nil 会崩溃 30 | # 主要原因是 all_specs 分析错误 31 | # 查看 source 是否正确 32 | # 33 | # def dependencies_for_specs(specs, platform, all_specs) 34 | # return [] if specs.empty? || all_specs.empty? 35 | 36 | # dependent_specs = Set.new 37 | 38 | # specs.each do |s| 39 | # s.dependencies(platform).each do |dep| 40 | # all_specs[dep.name].each do |spec| 41 | # dependent_specs << spec 42 | # end 43 | # end 44 | # end 45 | 46 | # dependent_specs - specs 47 | # end 48 | 49 | # > 1.5.3 版本 50 | # rewrite update_repositories 51 | # 52 | alias old_update_repositories update_repositories 53 | def update_repositories 54 | if installation_options.update_source_with_multi_threads 55 | # 并发更新私有源 56 | # 这里多线程会导致 pod update 额外输出 --verbose 的内容 57 | # 不知道为什么? 58 | thread_count = installation_options.multi_threads_count 59 | Parallel.each(sources.uniq(&:url), in_threads: thread_count) do |source| 60 | if source.updateable? 61 | sources_manager.update(source.name, true) 62 | else 63 | UI.message "Skipping `#{source.name}` update because the repository is not an updateable repository." 64 | end 65 | end 66 | @specs_updated = true 67 | else 68 | old_update_repositories 69 | end 70 | end 71 | 72 | # 解决 dep.name = xxx/binary 时,all_specs[dep.name] 返回nil,导致调用 each 方法报错 73 | alias old_dependencies_for_specs dependencies_for_specs 74 | def dependencies_for_specs(specs, platform, all_specs) 75 | dependent_specs = { 76 | :debug => Set.new, 77 | :release => Set.new, 78 | } 79 | 80 | podfile = Pod::Config.instance.podfile 81 | 82 | if !specs.empty? && !all_specs.empty? 83 | specs.each do |s| 84 | s.dependencies(platform).each do |dep| 85 | # use_binary = podfile.use_binaries? && !podfile.use_source_pods.include?(dep.root_name) 86 | # key = use_binary ? dep.root_name : dep.name 87 | all_specs[dep.name].each do |spec| 88 | if spec.non_library_specification? 89 | if s.test_specification? && spec.name == s.consumer(platform).app_host_name && spec.app_specification? 90 | # This needs to be handled separately, since we _don't_ want to treat this as a "normal" dependency 91 | next 92 | end 93 | raise Informative, "`#{s}` depends upon `#{spec}`, which is a `#{spec.spec_type}` spec." 94 | end 95 | 96 | dependent_specs.each do |config, set| 97 | next unless s.dependency_whitelisted_for_configuration?(dep, config) 98 | set << spec 99 | end 100 | end unless all_specs[dep.name].nil? # 解决 dep.name = xxx/binary 时,all_specs[dep.name]返回的是nil,导致调用 each 方法报错 101 | end 102 | end 103 | end 104 | 105 | Hash[dependent_specs.map { |k, v| [k, (v - specs).group_by(&:root)] }].freeze 106 | end 107 | end 108 | end 109 | end 110 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/native/file_accessor.rb: -------------------------------------------------------------------------------- 1 | require 'macho' 2 | require 'cocoapods' 3 | 4 | module Pod 5 | class Sandbox 6 | class FileAccessor 7 | 8 | # swift动态库 需要设置为true 9 | def dynamic_binary?(binary) 10 | @cached_dynamic_binary_results ||= {} 11 | return @cached_dynamic_binary_results[binary] unless @cached_dynamic_binary_results[binary].nil? 12 | return false unless binary.file? 13 | 14 | @cached_dynamic_binary_results[binary] = MachO.open(binary).dylib? 15 | rescue MachO::MachOError 16 | @cached_dynamic_binary_results[binary] = true 17 | 18 | end 19 | 20 | # def expanded_paths(patterns, options = {}) 21 | # return [] if patterns.empty? 22 | # path_list.glob(patterns, options).flatten.compact.uniq 23 | # end 24 | 25 | #-----------------------------------------------------------------------# 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/native/gen.rb: -------------------------------------------------------------------------------- 1 | 2 | # hook cocoapods-generate的Gen类和Installer类 3 | 4 | module Pod 5 | class Command 6 | class Gen < Command 7 | 8 | alias old_run run 9 | 10 | def run 11 | UI.puts "[pod gen] Running with #{configuration.to_s.gsub("\n", " \n")}" if configuration.pod_config.verbose? 12 | 13 | # this is done here rather than in the installer so we only update sources once, 14 | # even if there are multiple podspecs 15 | update_sources if configuration.repo_update? 16 | 17 | installers = [] 18 | Generate::PodfileGenerator.new(configuration).podfiles_by_spec.each do |spec, podfile| 19 | installer = Generate::Installer.new(configuration, spec, podfile).install! 20 | installers << installer 21 | end 22 | 23 | remove_warnings(UI.warnings) 24 | 25 | installers 26 | end 27 | end 28 | end 29 | end 30 | 31 | module Pod 32 | module Generate 33 | class Installer 34 | 35 | alias old_install! install! 36 | 37 | def install! 38 | UI.title "Generating #{spec.name} in #{UI.path install_directory}" do 39 | clean! if configuration.clean? 40 | install_directory.mkpath 41 | 42 | UI.message 'Creating stub application' do 43 | create_app_project 44 | end 45 | 46 | UI.message 'Writing Podfile' do 47 | podfile.defined_in_file.open('w') { |f| f << podfile.to_yaml } 48 | end 49 | 50 | installer = nil 51 | UI.section 'Installing...' do 52 | configuration.pod_config.with_changes(installation_root: install_directory, podfile: podfile, 53 | lockfile: configuration.lockfile, sandbox: nil, 54 | sandbox_root: install_directory + 'Pods', 55 | podfile_path: podfile.defined_in_file, 56 | silent: !configuration.pod_config.verbose?, verbose: false, 57 | lockfile_path: nil) do 58 | installer = ::Pod::Installer.new(configuration.pod_config.sandbox, podfile, configuration.lockfile) 59 | installer.use_default_plugins = configuration.use_default_plugins 60 | installer.install! 61 | end 62 | end 63 | 64 | UI.section 'Performing post-installation steps' do 65 | should_perform_post_install = if installer.respond_to?(:generated_aggregate_targets) # CocoaPods 1.7.0 66 | !installer.generated_aggregate_targets.empty? 67 | else 68 | true 69 | end 70 | perform_post_install_steps(open_app_project, installer) if should_perform_post_install 71 | end 72 | 73 | print_post_install_message 74 | installer 75 | end 76 | end 77 | end 78 | end 79 | end -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/native/installation_options.rb: -------------------------------------------------------------------------------- 1 | 2 | require 'cocoapods' 3 | 4 | module Pod 5 | class Installer 6 | class InstallationOptions 7 | def self.env_option(key, default = true) 8 | option key, ENV[key.to_s].nil? ? default : ENV[key.to_s] == 'true' 9 | end 10 | 11 | # 不同 source 存在相同 spec 名时,默认不警告 12 | defaults.delete('warn_for_multiple_pod_sources') 13 | env_option :warn_for_multiple_pod_sources, false 14 | 15 | # 是否警告不安全 source (如 http ) 16 | env_option :warn_for_unsecure_source, false 17 | 18 | # 是否多线程执行 install_pod_sources 19 | env_option :install_with_multi_threads, false 20 | 21 | # 是否多进程执行 update_repositories 22 | env_option :update_source_with_multi_threads, false 23 | 24 | # 并发执行个数 25 | option :multi_threads_count, 4 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/native/linter.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'cocoapods-meitu-bin/native/specification' 4 | 5 | module Pod 6 | class Specification 7 | class Linter 8 | # !@group Lint steps 9 | 10 | # Checks that the spec's root name matches the filename. 11 | # 12 | # @return [void] 13 | # 14 | def validate_root_name 15 | if spec.root.name && file 16 | acceptable_names = Specification::VALID_EXTNAME.map { |extname| "#{spec.root.name}#{extname}" } 17 | names_match = acceptable_names.include?(file.basename.to_s) 18 | unless names_match 19 | results.add_error('name', 'The name of the spec should match the ' \ 20 | 'name of the file.') 21 | end 22 | end 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/native/lockfile.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Lockfile 3 | def detect_changes_with_podfile(podfile) 4 | result = {} 5 | [:added, :changed, :removed, :unchanged].each { |k| result[k] = [] } 6 | 7 | installed_deps = {} 8 | dependencies.each do |dep| 9 | name = dep.root_name 10 | installed_deps[name] ||= dependencies_to_lock_pod_named(name) 11 | end 12 | 13 | installed_deps = installed_deps.values.flatten(1).group_by(&:name) 14 | 15 | podfile_dependencies = podfile.dependencies 16 | podfile_dependencies_by_name = podfile_dependencies.group_by(&:name) 17 | 18 | all_dep_names = (dependencies + podfile_dependencies).map(&:name).uniq 19 | all_dep_names.each do |name| 20 | installed_dep = installed_deps[name] 21 | installed_dep &&= installed_dep.first 22 | 23 | # 需要将二进制版本的 specific_version 最后一位去掉,否则二进制下依赖解析很慢 24 | unless installed_dep.nil? 25 | installed_dep_version = installed_dep.specific_version.to_s 26 | if installed_dep_version.include?('bin') 27 | req_arr = installed_dep_version.split('.').delete_if { |r| r.include?('bin') } 28 | installed_dep_version = req_arr.join('.') 29 | installed_dep.specific_version = Pod::Version.create(installed_dep_version) 30 | end 31 | end 32 | 33 | podfile_dep = podfile_dependencies_by_name[name] 34 | podfile_dep &&= podfile_dep.first 35 | 36 | if installed_dep.nil? then key = :added 37 | elsif podfile_dep.nil? then key = :removed 38 | elsif podfile_dep.compatible?(installed_dep) then key = :unchanged 39 | else key = :changed 40 | end 41 | result[key] << name 42 | end 43 | result 44 | end 45 | 46 | class << self 47 | def generate(podfile, specs, checkout_options, spec_repos = {}) 48 | hash = { 49 | 'PODS' => generate_pods_data(specs), 50 | 'DEPENDENCIES' => generate_dependencies_data(podfile), 51 | 'SPEC REPOS' => generate_spec_repos(spec_repos), 52 | 'EXTERNAL SOURCES' => generate_external_sources_data(podfile), 53 | 'CHECKOUT OPTIONS' => checkout_options, 54 | 'SPEC CHECKSUMS' => generate_checksums(specs), 55 | 'PODFILE CHECKSUM' => podfile.checksum, 56 | 'USE BINARY' => "#{podfile.use_binaries?}", 57 | 'CONFIGURATION' => ENV['configuration'] || podfile.configuration, 58 | 'COCOAPODS' => CORE_VERSION, 59 | } 60 | Lockfile.new(hash) 61 | end 62 | 63 | def generate_spec_repos(spec_repos) 64 | result = Hash.new 65 | spec_repos.map do |source, specs| 66 | next unless source 67 | next if specs.empty? 68 | key = source.url || source.name 69 | 70 | # save `trunk` as 'trunk' so that the URL itself can be changed without lockfile churn 71 | key = Pod::TrunkSource::TRUNK_REPO_NAME if source.name == Pod::TrunkSource::TRUNK_REPO_NAME 72 | 73 | value = specs.map { |s| s.root.name }.uniq 74 | # 合并重复的source源,而不是替换 75 | if result[key].nil? 76 | result[key] = YAMLHelper.sorted_array(value) 77 | else 78 | result[key] = YAMLHelper.sorted_array(result[key].concat(value)) 79 | end 80 | end 81 | result.compact 82 | end 83 | end 84 | end 85 | end 86 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/native/path_source.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'cocoapods-meitu-bin/native/specification' 4 | 5 | module Pod 6 | module ExternalSources 7 | # Provides support for fetching a specification file from a path local to 8 | # the machine running the installation. 9 | # 10 | class PathSource < AbstractExternalSource 11 | def normalized_podspec_path(declared_path) 12 | extension = File.extname(declared_path) 13 | 14 | if extension == '.podspec' || extension == '.json' 15 | path_with_ext = declared_path 16 | else 17 | # 默认先从 binary podspec 找起,因为 binary podspec 的 subspec 可能比 code podspec 多 18 | # 这里可能出现 code subspec 和 binary subspec 对应不上的情况,导致 lint 失败 19 | # 所以不要在 code podspec 同一目录下保留 binary podspec 20 | path_with_ext = Specification::VALID_EXTNAME 21 | .map { |extname| "#{declared_path}/#{name}#{extname}" } 22 | .find { |file| File.exist?(file) } || "#{declared_path}/#{name}.podspec" 23 | end 24 | 25 | UI.message "获取的 podspec 路径为 `#{path_with_ext}`" 26 | 27 | podfile_dir = File.dirname(podfile_path || '') 28 | 29 | File.expand_path(path_with_ext, podfile_dir) 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/native/pod_source_installer.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'cocoapods-meitu-bin/native/installation_options' 4 | 5 | module Pod 6 | class Installer 7 | class PodSourceInstaller 8 | attr_accessor :installation_options 9 | 10 | alias old_verify_source_is_secure verify_source_is_secure 11 | def verify_source_is_secure(root_spec) 12 | # http source 默认不警告 13 | if installation_options.warn_for_unsecure_source? 14 | old_verify_source_is_secure(root_spec) 15 | end 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/native/pod_target_installer.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Installer 3 | class Xcode 4 | class PodsProjectGenerator 5 | # Creates the target for the Pods libraries in the Pods project and the 6 | # relative support files. 7 | # 8 | class PodTargetInstaller < TargetInstaller 9 | require 'cocoapods/installer/xcode/pods_project_generator/app_host_installer' 10 | 11 | # Adds a shell script phase, intended only for library targets that contain swift, 12 | # to copy the ObjC compatibility header (the -Swift.h file that the swift compiler generates) 13 | # to the built products directory. Additionally, the script phase copies the module map, appending a `.Swift` 14 | # submodule that references the (moved) compatibility header. Since the module map has been moved, the umbrella header 15 | # is _also_ copied, so that it is sitting next to the module map. This is necessary for a successful archive build. 16 | # 17 | # @param [PBXNativeTarget] native_target 18 | # the native target to add the Swift static library script phase into. 19 | # 20 | # @return [Void] 21 | # 22 | alias old_add_swift_library_compatibility_header_phase add_swift_library_compatibility_header_phase 23 | 24 | def add_swift_library_compatibility_header_phase(native_target) 25 | if $ARGV[1] == "auto" 26 | if custom_module_map 27 | raise Informative, 'Using Swift static libraries with custom module maps is currently not supported. ' \ 28 | "Please build `#{target.label}` as a framework or remove the custom module map." 29 | end 30 | 31 | build_phase = native_target.new_shell_script_build_phase('Copy generated compatibility header') 32 | 33 | relative_module_map_path = target.module_map_path.relative_path_from(target.sandbox.root) 34 | relative_umbrella_header_path = target.umbrella_header_path.relative_path_from(target.sandbox.root) 35 | 36 | build_phase.shell_script = <<-SH.strip_heredoc 37 | COMPATIBILITY_HEADER_PATH="${BUILT_PRODUCTS_DIR}/Swift Compatibility Header/${PRODUCT_MODULE_NAME}-Swift.h" 38 | MODULE_MAP_PATH="${BUILT_PRODUCTS_DIR}/${PRODUCT_MODULE_NAME}.modulemap" 39 | 40 | ditto "${DERIVED_SOURCES_DIR}/${PRODUCT_MODULE_NAME}-Swift.h" "${COMPATIBILITY_HEADER_PATH}" 41 | ditto "${PODS_ROOT}/#{relative_module_map_path}" "${MODULE_MAP_PATH}" 42 | ditto "${PODS_ROOT}/#{relative_umbrella_header_path}" "${BUILT_PRODUCTS_DIR}" 43 | 44 | COPY_PATH="${PODS_CONFIGURATION_BUILD_DIR}/${PRODUCT_MODULE_NAME}" 45 | UMBRELLA_PATH="${BUILT_PRODUCTS_DIR}/${PRODUCT_MODULE_NAME}-umbrella.h" 46 | SWIFTMODULE_PATH="${BUILT_PRODUCTS_DIR}/${PRODUCT_MODULE_NAME}.swiftmodule" 47 | 48 | ditto "${MODULE_MAP_PATH}" "${PODS_CONFIGURATION_BUILD_DIR}/${PRODUCT_MODULE_NAME}/${PRODUCT_MODULE_NAME}.modulemap" 49 | ditto "${COMPATIBILITY_HEADER_PATH}" "${COPY_PATH}/Swift Compatibility Header/${PRODUCT_MODULE_NAME}-Swift.h" 50 | ditto "${COMPATIBILITY_HEADER_PATH}" "${COPY_PATH}" 51 | ditto "${UMBRELLA_PATH}" "${COPY_PATH}" 52 | ditto "${SWIFTMODULE_PATH}" "${COPY_PATH}/${PRODUCT_MODULE_NAME}.swiftmodule" 53 | ditto "${SWIFTMODULE_PATH}" "${COPY_PATH}/${PRODUCT_MODULE_NAME}.swiftmodule" 54 | 55 | if [ ${PRODUCT_MODULE_NAME} != ${PRODUCT_NAME} ] ; then 56 | ditto "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}-umbrella.h" "${COPY_PATH}" 57 | ditto "${COPY_PATH}" "${PODS_CONFIGURATION_BUILD_DIR}/${PRODUCT_NAME}" 58 | fi 59 | 60 | MODULE_MAP_SEARCH_PATH = "${PODS_CONFIGURATION_BUILD_DIR}/${PRODUCT_MODULE_NAME}/${PRODUCT_MODULE_NAME}.modulemap" 61 | 62 | if [${MODULE_MAP_PATH} != ${MODULE_MAP_SEARCH_PATH}] ; then 63 | printf "\\n\\nmodule ${PRODUCT_MODULE_NAME}.Swift {\\n header \\"${COPY_PATH}/Swift Compatibility Header/${PRODUCT_MODULE_NAME}-Swift.h\\"\\n requires objc\\n}\\n" >> "${MODULE_MAP_SEARCH_PATH}" 64 | fi 65 | 66 | printf "\\n\\nmodule ${PRODUCT_MODULE_NAME}.Swift {\\n header \\"${COMPATIBILITY_HEADER_PATH}\\"\\n requires objc\\n}\\n" >> "${MODULE_MAP_PATH}" 67 | 68 | SH 69 | build_phase.input_paths = %W( 70 | ${DERIVED_SOURCES_DIR}/${PRODUCT_MODULE_NAME}-Swift.h 71 | ${PODS_ROOT}/#{relative_module_map_path} 72 | ${PODS_ROOT}/#{relative_umbrella_header_path} 73 | ) 74 | build_phase.output_paths = %W( 75 | ${BUILT_PRODUCTS_DIR}/${PRODUCT_MODULE_NAME}.modulemap 76 | ${BUILT_PRODUCTS_DIR}/#{relative_umbrella_header_path.basename} 77 | ${BUILT_PRODUCTS_DIR}/Swift\ Compatibility\ Header/${PRODUCT_MODULE_NAME}-Swift.h 78 | ) 79 | else 80 | old_add_swift_library_compatibility_header_phase(native_target) 81 | end 82 | end 83 | end 84 | end 85 | end 86 | end 87 | end 88 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/native/podfile.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'cocoapods' 4 | require 'cocoapods-meitu-bin/native/podfile_env' 5 | 6 | module Pod 7 | class Podfile 8 | # TREAT_DEVELOPMENTS_AS_NORMAL = 'treat_developments_as_normal'.freeze 9 | 10 | module DSL 11 | def allow_prerelease! 12 | set_internal_hash_value(ALLOW_PRERELEASE, true) 13 | end 14 | 15 | def use_binaries!(flag = true) 16 | set_internal_hash_value(USE_BINARIES, flag) 17 | end 18 | 19 | def include_dependencies!(flag = false) 20 | set_internal_hash_value(INCLUDE_DEPENDENCIES, flag) 21 | end 22 | 23 | def use_binaries_with_spec_selector!(&block) 24 | raise Informative, '必须提供选择需要二进制组件的 block !' unless block_given? 25 | 26 | set_internal_hash_value(USE_BINARIES_SELECTOR, block) 27 | end 28 | 29 | def set_use_source_pods(pods) 30 | hash_pods_use_source = get_internal_hash_value(USE_SOURCE_PODS) || [] 31 | hash_pods_use_source += Array(pods) 32 | set_internal_hash_value(USE_SOURCE_PODS, hash_pods_use_source) 33 | end 34 | 35 | def set_configuration(config = 'Debug') 36 | set_internal_hash_value(CONFIGURATION, config) 37 | end 38 | 39 | # 0 dev 40 | # 1 debug_iphoneos 41 | # 2 release_iphoneos 42 | # 需要在podfile_env 先定义 CONFIGURATION_ENV 43 | def set_configuration_env(env = "dev") 44 | set_internal_hash_value(CONFIGURATION_ENV, env) 45 | end 46 | end 47 | 48 | alias old_plugins plugins 49 | def plugins 50 | if ENV[USE_PLUGINS] 51 | env_plugins = ENV[USE_PLUGINS].split(',').each_with_object({}) do |name, result| 52 | result[name] = {} 53 | end 54 | env_plugins.merge!(old_plugins) 55 | else 56 | old_plugins 57 | end 58 | end 59 | 60 | def use_binaries_selector 61 | get_internal_hash_value(USE_BINARIES_SELECTOR, nil) 62 | end 63 | 64 | def allow_prerelease? 65 | get_internal_hash_value(ALLOW_PRERELEASE, false) || ENV[ALLOW_PRERELEASE] == 'true' 66 | end 67 | 68 | def use_binaries? 69 | get_internal_hash_value(USE_BINARIES, false) || ENV[USE_BINARIES] == 'true' 70 | end 71 | 72 | def include_dependencies? 73 | get_internal_hash_value(INCLUDE_DEPENDENCIES, false) || ENV[INCLUDE_DEPENDENCIES] == 'true' 74 | end 75 | 76 | def use_source_pods 77 | get_internal_hash_value(USE_SOURCE_PODS, []) + String(ENV[USE_SOURCE_PODS]).split('|').uniq 78 | end 79 | 80 | def configuration_env 81 | get_internal_hash_value(CONFIGURATION_ENV, "dev") || ENV[CONFIGURATION_ENV] == "dev" 82 | end 83 | 84 | def configuration 85 | get_internal_hash_value(CONFIGURATION, "Debug") 86 | end 87 | 88 | private 89 | 90 | def valid_bin_plugin 91 | unless plugins.keys.include?('cocoapods-meitu-bin') 92 | raise Pod::Informative, 'You should add `plugin \'cocoapods-meitu-bin\'` before using its DSL' 93 | end 94 | end 95 | 96 | # set_hash_value 有 key 限制 97 | def set_internal_hash_value(key, value) 98 | valid_bin_plugin 99 | 100 | internal_hash[key] = value 101 | end 102 | 103 | def get_internal_hash_value(key, default = nil) 104 | internal_hash.fetch(key, default) 105 | end 106 | end 107 | end 108 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/native/podfile_env.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Pod 4 | class Podfile 5 | USE_BINARIES = 'use_binaries' 6 | USE_SOURCE_PODS = 'use_source_pods' 7 | USE_BINARIES_SELECTOR = 'use_binaries_selector' 8 | ALLOW_PRERELEASE = 'allow_prerelease' 9 | USE_PLUGINS = 'use_plugins' 10 | CONFIGURATION_ENV = 'configuration_env' 11 | CONFIGURATION = 'configuration' 12 | INCLUDE_DEPENDENCIES = 'include_dependencies' 13 | 14 | module ENVExecutor 15 | def execute_with_bin_plugin(&block) 16 | execute_with_key(USE_PLUGINS, -> { 'cocoapods-meitu-bin' }, &block) 17 | end 18 | 19 | def execute_with_allow_prerelease(allow_prerelease, &block) 20 | execute_with_key(ALLOW_PRERELEASE, -> { allow_prerelease ? 'true' : 'false' }, &block) 21 | end 22 | 23 | def execute_with_use_binaries(use_binaries, &block) 24 | execute_with_key(USE_BINARIES, -> { use_binaries ? 'true' : 'false' }, &block) 25 | end 26 | 27 | def execute_with_key(key, value_returner) 28 | origin_value = ENV[key] 29 | ENV[key] = value_returner.call 30 | 31 | yield if block_given? 32 | 33 | ENV[key] = origin_value 34 | end 35 | end 36 | 37 | extend ENVExecutor 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/native/podfile_generator.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'parallel' 4 | require 'cocoapods' 5 | require 'cocoapods-meitu-bin/native/pod_source_installer' 6 | 7 | 8 | require 'parallel' 9 | require 'cocoapods' 10 | 11 | module Pod 12 | module Generate 13 | # Generates podfiles for pod specifications given a configuration. 14 | # 15 | class PodfileGenerator 16 | # @return [Podfile] a podfile suitable for installing the given spec 17 | # 18 | # @param [Specification] spec 19 | # 20 | alias old_podfile_for_spec podfile_for_spec 21 | 22 | def podfile_for_spec(spec) 23 | generator = self 24 | dir = configuration.gen_dir_for_pod(spec.name) 25 | 26 | Pod::Podfile.new do 27 | project "#{spec.name}.xcodeproj" 28 | workspace "#{spec.name}.xcworkspace" 29 | 30 | plugin 'cocoapods-generate' 31 | 32 | install! 'cocoapods', generator.installation_options 33 | 34 | generator.podfile_plugins.each do |name, options| 35 | plugin(*[name, options].compact) 36 | end 37 | 38 | # use_frameworks!(generator.use_frameworks_value) 39 | # 强制生成 static framework 40 | use_frameworks!({:linkage=>:static, :packaging=>:framework}) 41 | 42 | if (supported_swift_versions = generator.supported_swift_versions) 43 | supports_swift_versions(supported_swift_versions) 44 | end 45 | 46 | # Explicitly set sources 47 | generator.configuration.sources.each do |source_url| 48 | source(source_url) 49 | end 50 | 51 | self.defined_in_file = dir.join('CocoaPods.podfile.yaml') 52 | 53 | test_specs = spec.recursive_subspecs.select(&:test_specification?) 54 | app_specs = if spec.respond_to?(:app_specification?) 55 | spec.recursive_subspecs.select(&:app_specification?) 56 | else 57 | [] 58 | end 59 | 60 | # Stick all of the transitive dependencies in an abstract target. 61 | # This allows us to force CocoaPods to use the versions / sources / external sources 62 | # that we want. 63 | # By using an abstract target, 64 | 65 | # 会导致多个dependencies出现, 注释by slj 66 | # abstract_target 'Transitive Dependencies' do 67 | # pods_for_transitive_dependencies = [spec.name] 68 | # .concat(test_specs.map(&:name)) 69 | # .concat(test_specs.flat_map { |ts| ts.dependencies.flat_map(&:name) }) 70 | # .concat(app_specs.map(&:name)) 71 | # .concat(app_specs.flat_map { |as| as.dependencies.flat_map(&:name) }) 72 | # 73 | # dependencies = generator 74 | # .transitive_dependencies_by_pod 75 | # .values_at(*pods_for_transitive_dependencies) 76 | # .compact 77 | # .flatten(1) 78 | # .uniq 79 | # .sort_by(&:name) 80 | # .reject { |d| d.root_name == spec.root.name } 81 | # 82 | # dependencies.each do |dependency| 83 | # pod_args = generator.pod_args_for_dependency(self, dependency) 84 | # pod(*pod_args) 85 | # end 86 | # end 87 | 88 | # Add platform-specific concrete targets that inherit the 89 | # `pod` declaration for the local pod. 90 | spec_platform_names = spec.available_platforms.map(&:string_name).flatten.each.reject do |platform_name| 91 | !generator.configuration.platforms.nil? && !generator.configuration.platforms.include?(platform_name.downcase) 92 | end 93 | 94 | spec_platform_names.sort.each do |platform_name| 95 | target "App-#{platform_name}" do 96 | current_target_definition.swift_version = generator.swift_version if generator.swift_version 97 | end 98 | end 99 | 100 | # this block has to come _before_ inhibit_all_warnings! / use_modular_headers!, 101 | # and the local `pod` declaration 102 | # 会导致多个dependencies出现, 注释by slj 103 | 104 | 105 | inhibit_all_warnings! if generator.inhibit_all_warnings? 106 | use_modular_headers! if generator.use_modular_headers? 107 | 108 | # This is the pod declaration for the local pod, 109 | # it will be inherited by the concrete target definitions below 110 | 111 | pod_options = generator.dependency_compilation_kwargs(spec.name) 112 | pod_options[:path] = spec.defined_in_file.relative_path_from(dir).to_s 113 | # generator.configuration.podfile.dependencies[0].external_source 114 | 115 | 116 | { testspecs: test_specs, appspecs: app_specs }.each do |key, specs| 117 | pod_options[key] = specs.map { |s| s.name.sub(%r{^#{Regexp.escape spec.root.name}/}, '') }.sort unless specs.empty? 118 | end 119 | 120 | pod spec.name, **pod_options 121 | 122 | if Pod::Config.instance.podfile 123 | target_definitions['Pods'].instance_exec do 124 | target_definition = nil 125 | Pod::Config.instance.podfile.target_definition_list.each do |target| 126 | if target.label == "Pods-#{spec.name}" 127 | target_definition = target 128 | break 129 | end 130 | end 131 | # setting modular_headers_for 132 | if(target_definition && target_definition.use_modular_headers_hash.values.any?) 133 | target_definition.use_modular_headers_hash.values.each do |f| 134 | f.each { | pod_name| self.set_use_modular_headers_for_pod(pod_name, true) } 135 | end 136 | end 137 | 138 | 139 | if target_definition 140 | value = target_definition.to_hash['dependencies'] 141 | next if value.blank? 142 | #删除 本地库中的 spec.name,因为本地的./spec.name地址是错的 143 | value.each do |f| 144 | if f.is_a?(Hash) && f.keys.first == spec.name 145 | value.delete f 146 | break 147 | end 148 | end 149 | old_value = self.to_hash['dependencies'].first 150 | value << old_value unless (old_value == nil || value.include?(old_value)) 151 | 152 | set_hash_value(%w(dependencies).first, value) 153 | 154 | value = target_definition.to_hash['configuration_pod_whitelist'] 155 | next if value.blank? 156 | set_hash_value(%w(configuration_pod_whitelist).first, value) 157 | 158 | 159 | end 160 | 161 | 162 | end 163 | 164 | end 165 | 166 | # if generator.configuration && generator.configuration.podfile 167 | # #变量本地podfile下的dependencies 写入新的验证文件中,指定依赖源 168 | # generator.configuration.podfile.dependencies.each { |dependencies| 169 | # #如果不存在dependencies.external_source,就不变量 170 | # next unless dependencies.external_source 171 | # 172 | # dependencies.external_source.each { |key_d, value| 173 | # pod_options = generator.dependency_compilation_kwargs(dependencies.name) 174 | # pod_options[key_d] = value.to_s 175 | # { testspecs: test_specs, appspecs: app_specs }.each do |key, specs| 176 | # pod_options[key] = specs.map { |s| s.name.sub(%r{^#{Regexp.escape spec.root.name}/}, '') }.sort unless specs.empty? 177 | # end 178 | # # 过滤 dependencies.name == spec.name 179 | # pod(dependencies.name, **pod_options) unless dependencies.name == spec.name 180 | # } 181 | # } 182 | # end 183 | 184 | 185 | # Implement local-sources option to set up dependencies to podspecs in the local filesystem. 186 | next if generator.configuration.local_sources.empty? 187 | generator.transitive_local_dependencies(spec, generator.configuration.local_sources).each do |dependency, podspec_file| 188 | pod_options = generator.dependency_compilation_kwargs(dependency.name) 189 | pod_options[:path] = if podspec_file[0] == '/' # absolute path 190 | podspec_file 191 | else 192 | '../../' + podspec_file 193 | end 194 | pod dependency.name, **pod_options 195 | end 196 | end 197 | end 198 | end 199 | end 200 | end 201 | 202 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/native/podspec_finder.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'cocoapods-meitu-bin/native/specification' 4 | 5 | module Pod 6 | class Sandbox 7 | class PodspecFinder 8 | def podspecs 9 | return @specs_by_name if @specs_by_name 10 | 11 | @specs_by_name = {} 12 | spec_files = Pathname.glob(root + '{,*}.podspec{,.json}') 13 | # pod 指向分支时,如果目标组件有 subspec ,并且有 template spec ,request 之后使用的 spec 不应该为 template spec 14 | # 这里做下过滤 15 | spec_files = spec_files.reject { |p| Specification::DEFAULT_TEMPLATE_EXTNAME.find { |e| p.to_s.end_with?(e) } } 16 | spec_files.sort_by { |p| -p.to_path.split(File::SEPARATOR).size }.each do |file| 17 | spec = Specification.from_file(file) 18 | spec.validate_cocoapods_version 19 | @specs_by_name[spec.name] = spec 20 | end 21 | @specs_by_name 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/native/sandbox_analyzer.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Pod 4 | class Installer 5 | class Analyzer 6 | class SandboxAnalyzer 7 | # def pod_changed?(pod) 8 | # spec = root_spec(pod) 9 | # 有 subspec 的情况下,root spec 对应的 used_files 可能始终为空 10 | # 要添加为空 && 没有 subspec 的情况 11 | # file_accessors = spec.available_platforms.map { |platform| Sandbox::FileAccessor.new(sandbox.pod_dir(pod), spec.consumer(platform)) } 12 | # files = [ 13 | # file_accessors.map(&:vendored_frameworks), 14 | # file_accessors.map(&:vendored_libraries), 15 | # file_accessors.map(&:resource_bundle_files), 16 | # file_accessors.map(&:prefix_header), 17 | # file_accessors.map(&:resources), 18 | # file_accessors.map(&:source_files), 19 | # file_accessors.map(&:module_map), 20 | # ] 21 | # used_files = files.flatten.compact.map(&:to_s).uniq 22 | # p pod if used_files.empty? 23 | 24 | # return true if spec.version != sandbox_version(pod) 25 | # return true if spec.checksum != sandbox_checksum(pod) 26 | # return true if resolved_spec_names(pod) != sandbox_spec_names(pod) 27 | # return true if sandbox.predownloaded?(pod) 28 | # return true if folder_empty?(pod) 29 | # false 30 | # end 31 | end 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/native/source.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'cocoapods' 4 | 5 | module Pod 6 | class Source 7 | # Returns the path of the specification with the given name and version. 8 | # 9 | # @param [String] name 10 | # the name of the Pod. 11 | # 12 | # @param [Version,String] version 13 | # the version for the specification. 14 | # 15 | # @return [Pathname] The path of the specification. 16 | # 17 | def specification_path(name, version) 18 | raise ArgumentError, 'No name' unless name 19 | raise ArgumentError, 'No version' unless version 20 | 21 | path = pod_path(name) + version.to_s 22 | 23 | specification_path = Specification::VALID_EXTNAME 24 | .map { |extname| "#{name}#{extname}" } 25 | .map { |file| path + file } 26 | .find(&:exist?) 27 | 28 | unless specification_path 29 | raise StandardError, "Unable to find the specification #{name} " \ 30 | "(#{version}) in the #{self.name} source." 31 | end 32 | specification_path 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/native/sources_manager.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'cocoapods' 4 | require 'cocoapods-meitu-bin/config/config' 5 | 6 | module Pod 7 | class Source 8 | class Manager 9 | # 源码 source list 10 | def code_source_list 11 | [] 12 | # CBin.config.code_repo_url_list.split(";").map { |source| source_with_name_or_url(source)} 13 | end 14 | # 二进制 source 15 | def binary_source 16 | source_with_name_or_url(CBin.config.binary_repo_url) 17 | # source_with_name_or_url('git@github.com:Zhangyanshen/example-private-spec-bin.git') 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/native/specification.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'cocoapods-meitu-bin/native/sources_manager' 4 | 5 | module Pod 6 | class Specification 7 | VALID_EXTNAME = %w[.binary.podspec.json .binary.podspec .podspec.json .podspec].freeze 8 | DEFAULT_TEMPLATE_EXTNAME = %w[.binary-template.podspec .binary-template.podspec.json].freeze 9 | 10 | # TODO 11 | # 可以在这里根据组件依赖二进制或源码调整 sources 的先后顺序 12 | # 如果是源码,则调整 code_source 到最前面 13 | # 如果是二进制,则调整 binary_source 到最前面 14 | # 这样 CocoaPods 分析时,就会优先获取到对应源的 podspec 15 | # 16 | # 先要把 Podfile 里面的配置数据保存到单例中,再在这里判断,就不需要 resolver 了 17 | # 但是现在这个插件依旧可用,重构需要时间 = = ,没什么动力去重构了呀。。。 18 | # 19 | # class Set 20 | # old_initialize = instance_method(:initialize) 21 | # define_method(:initialize) do |name, sources| 22 | # if name == 'TDFAdaptationKit' 23 | # sources_manager = Pod::Config.instance.sources_manager 24 | # # sources = [sources_manager.binary_source, *sources].uniq 25 | # sources = [sources_manager.code_source, *sources].uniq 26 | # end 27 | # old_initialize.bind(self).call(name, sources) 28 | # end 29 | # end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/native/target_validator.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Installer 3 | class Xcode 4 | # The {Xcode::TargetValidator} ensures that the pod and aggregate target 5 | # configuration is valid for installation. 6 | # 7 | class TargetValidator 8 | 9 | 10 | 11 | def verify_swift_pods_have_module_dependencies 12 | error_messages = [] 13 | pod_targets.each do |pod_target| 14 | next unless pod_target.uses_swift? 15 | 16 | non_module_dependencies = [] 17 | pod_target.dependent_targets.each do |dependent_target| 18 | next if !dependent_target.should_build? || dependent_target.defines_module? 19 | non_module_dependencies << dependent_target.name 20 | end 21 | 22 | next if non_module_dependencies.empty? 23 | 24 | error_messages << "The Swift pod `#{pod_target.name}` depends upon #{non_module_dependencies.map { |d| "`#{d}`" }.to_sentence}, " \ 25 | "which #{non_module_dependencies.count == 1 ? 'does' : 'do'} not define modules. " \ 26 | 'To opt into those targets generating module maps '\ 27 | '(which is necessary to import them from Swift when building as static libraries), ' \ 28 | 'you may set `use_modular_headers!` globally in your Podfile, '\ 29 | 'or specify `:modular_headers => true` for particular dependencies.' 30 | end 31 | return false 32 | 33 | # raise Informative, 'The following Swift pods cannot yet be integrated '\ 34 | # "as static libraries:\n\n#{error_messages.join("\n\n")}" 35 | end 36 | 37 | 38 | end 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/native/validator.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Pod 4 | class Validator 5 | # def validate_source_url(spec) 6 | # return if spec.source.nil? || spec.source[:http].nil? 7 | # url = URI(spec.source[:http]) 8 | # return if url.scheme == 'https' || url.scheme == 'file' 9 | # warning('http', "The URL (`#{url}`) doesn't use the encrypted HTTPs protocol. " \ 10 | # 'It is crucial for Pods to be transferred over a secure protocol to protect your users from man-in-the-middle attacks. '\ 11 | # 'This will be an error in future releases. Please update the URL to use https.') 12 | # end 13 | # 14 | # Perform analysis for a given spec (or subspec) 15 | # 16 | def perform_extensive_analysis(spec) 17 | return true 18 | end 19 | 20 | #覆盖 21 | def check_file_patterns 22 | FILE_PATTERNS.each do |attr_name| 23 | next if %i(source_files resources).include? attr_name 24 | if respond_to?("_validate_#{attr_name}", true) 25 | send("_validate_#{attr_name}") 26 | else 27 | validate_nonempty_patterns(attr_name, :error) 28 | end 29 | end 30 | 31 | _validate_header_mappings_dir 32 | if consumer.spec.root? 33 | _validate_license 34 | _validate_module_map 35 | end 36 | end 37 | 38 | def validate_source_url(spec); end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /lib/cocoapods-meitu-bin/source_provider_hook.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods-meitu-bin/native/sources_manager' 2 | require 'cocoapods-meitu-bin/command/bin/repo/update' 3 | require 'cocoapods-meitu-bin/config/config' 4 | require 'cocoapods/user_interface' 5 | require 'digest' 6 | require 'yaml' 7 | require 'cocoapods' 8 | require 'json' 9 | require 'net/http' 10 | #获取服务端podfile.lock文件 11 | def get_podfile_lock 12 | begin 13 | # 默认是获取要获取服务端podfile.lock文件 14 | is_load_podfile_lock = true 15 | #获取 PODFILE CHECKSUM 用来判断服务端是否存在该podfile.lock 16 | checksum = get_checksum(Pod::Config.instance.podfile_path) 17 | PodUpdateConfig.set_checksum(checksum) 18 | #目前只支持MTXX target "MTXX" 项目 想要支持其他项目可以添加对应 target "xxx" 19 | content = File.read(Pod::Config.instance.podfile_path) 20 | if content 21 | if content.include?("target \"MTXX\"") 22 | is_load_podfile_lock = true 23 | PodUpdateConfig.set_is_mtxx(true) 24 | else 25 | is_load_podfile_lock = false 26 | PodUpdateConfig.set_is_mtxx(false) 27 | end 28 | end 29 | # MEITU_LOAD_CACHE_PODFILE_LOCK 为false时不获取服务端podfile.lock文件 30 | if ENV['MEITU_LOAD_CACHE_PODFILE_LOCK'] && ENV['MEITU_LOAD_CACHE_PODFILE_LOCK'] == 'false' 31 | is_load_podfile_lock = false 32 | end 33 | # 判断是否有update参数 时不获取服务端podfile.lock文件 34 | ARGV.each do |arg| 35 | if arg == 'update' || arg == '--no-cloud' 36 | is_load_podfile_lock = false 37 | end 38 | end 39 | # podfile.lock文件下载和使用逻辑 40 | if is_load_podfile_lock 41 | Pod::UI.puts "pod_time_profiler: 当前podfile文件的checksum:#{checksum}".green 42 | # zip下载地址 43 | curl = "https://xiuxiu-dl-meitu-com.obs.cn-north-4.myhuaweicloud.com/ios/binary/MTXX/#{checksum}/podfile.lock.zip" 44 | # 判断服务端是否存在该podfile.lock 45 | is_load_podfile_lock = false 46 | if system("curl -o /dev/null -s -w %{http_code} #{curl} | grep 200 > /dev/null 2>&1") 47 | Pod::UI.puts "pod_time_profiler: 匹配到精准podfile.lock文件,使用当前podfile文件的checksum:#{checksum}获取对应的podfile.lock文件".green 48 | is_load_podfile_lock = true 49 | end 50 | 51 | if !is_load_podfile_lock 52 | branch_value = get_branch_name 53 | curl = "https://xiuxiu-dl-meitu-com.obs.cn-north-4.myhuaweicloud.com/ios/binary/MTXX/#{branch_value}/podfile.lock.zip" 54 | if system("curl -o /dev/null -s -w %{http_code} #{curl} | grep 200 > /dev/null 2>&1") 55 | Pod::UI.puts "pod_time_profiler: 无法匹配到精准podfile.lock文件,使用当前分支:#{branch_value} 对应的podfile.lock文件".green 56 | is_load_podfile_lock = true 57 | end 58 | #兜底使用develop的podfile.lock 59 | if !is_load_podfile_lock 60 | Pod::UI.puts "pod_time_profiler: 服务端不存在该podfile.lock文件,使用develop分支的podfile.lock文件兜底".green 61 | curl = "https://xiuxiu-dl-meitu-com.obs.cn-north-4.myhuaweicloud.com/ios/binary/MTXX/develop/podfile.lock.zip" 62 | is_load_podfile_lock = true 63 | end 64 | end 65 | # 判断是否需要下载podfile.lock文件 66 | if is_load_podfile_lock 67 | Pod::UI.puts "pod_time_profiler: 获取服务端存储的podfile.lcok文件".green 68 | #下载并解压的podfile.zip文件 69 | if system("curl -O #{curl} > /dev/null 2>&1") && system("unzip -o podfile.lock.zip > /dev/null 2>&1") 70 | Pod::UI.puts "pod_time_profiler: 下载并解压podfile.lcok文件成功".green 71 | `rm -rf podfile.lock.zip` 72 | # 设置获取到的podfile.lock对象 73 | PodUpdateConfig.set_lockfile(Pod::Config.instance.installation_root + 'Podfile.lock') 74 | #获取analyzer 75 | Pod::UI.puts "pod_time_profiler: 提前根据checksum命中podfile.lcok进行依赖分析".green 76 | analyzer = Pod::Installer::Analyzer.new( 77 | Pod::Config.instance.sandbox, 78 | Pod::Config.instance.podfile, 79 | PodUpdateConfig.lockfile 80 | ) 81 | analyzer.update_repositories 82 | PodUpdateConfig.set_repo_update 83 | analyzer.analyze(true) 84 | #获取analyzer中所有git 且branch 指向的pod 85 | Pod::Config.instance.podfile.dependencies.map do |dependency| 86 | if dependency.external_source && dependency.external_source[:git] && (dependency.external_source[:branch] || (dependency.external_source.size == 1)) 87 | #brash 指定的组件添加到全局PodUpdateConfig配置中,执行pod install 需要更新的分支最新提交 88 | PodUpdateConfig.add_value(dependency.name) 89 | end 90 | end 91 | else 92 | puts "pod_time_profiler: 获取podfile.lcok文件失败" 93 | `rm -rf podfile.lock.zip` 94 | end 95 | end 96 | end 97 | rescue => error 98 | puts "pod_time_profiler: podfile.lcok相关处理发生异常,报错原因:#{error}" 99 | PodUpdateConfig.clear 100 | `rm -rf podfile.lock.zip` 101 | `rm -rf podfile.lock` 102 | end 103 | end 104 | 105 | 106 | # 上传podfile.lock文件到服务端 107 | def upload_podfile_lock(checksum,upload = false) 108 | begin 109 | curl = "https://xiuxiu-dl-meitu-com.obs.cn-north-4.myhuaweicloud.com/ios/binary/MTXX/#{checksum}/podfile.lock.zip" 110 | # 服务端不存在该podfiel.lock文件才上传,避免频繁上报同一个文件 111 | if upload || !system("curl -o /dev/null -s -w %{http_code} #{curl} | grep 200 > /dev/null 2>&1") 112 | Pod::UI.puts "pod_time_profiler: 根据checksum:#{checksum}上报podfile.lcok文件到服务端".green 113 | if upload 114 | puts "pod_time_profiler: mbox工作目录/mtxx/MTXX/podfile 对应的checksum = #{checksum}" 115 | end 116 | if system("zip podfile.lock.zip Podfile.lock > /dev/null 2>&1") && system("curl -F \"name=MTXX\" -F \"version=#{checksum}\" -F \"file=@#{Pathname.pwd}/podfile.lock.zip\" http://nezha.community.cloud.meitu.com/file/upload.json > /dev/null 2>&1") 117 | Pod::UI.puts "pod_time_profiler: 上报podfile.lcok文件到服务端成功".green 118 | `rm -rf podfile.lock.zip` 119 | else 120 | Pod::UI.puts "pod_time_profiler: 上报podfile.lcok文件到服务端失败".red 121 | `rm -rf podfile.lock.zip` 122 | end 123 | end 124 | rescue => error 125 | puts "pod_time_profiler: 上传podfile.lcok文件失败,失败原因:#{error}" 126 | `rm -rf podfile.zip` 127 | end 128 | end 129 | def upload_mbox_podfile_lock 130 | begin 131 | podfile_path = Pod::Config.instance.installation_root + 'mtxx/MTXX' + 'Podfile' 132 | checksum = get_checksum(podfile_path) 133 | if checksum && checksum.is_a?(String) && checksum.length > 0 134 | upload_podfile_lock(checksum,true ) 135 | end 136 | rescue => error 137 | puts "pod_time_profiler: mbox podfile.lcok文件兼容处理失败,失败原因:#{error}" 138 | end 139 | end 140 | def upload_branch_podfile_lock 141 | begin 142 | branch_value = get_branch_name 143 | if branch_value && branch_value.is_a?(String) && branch_value.length > 0 144 | if system("zip podfile.lock.zip Podfile.lock > /dev/null 2>&1") && system("curl -F \"name=MTXX\" -F \"version=#{branch_value}\" -F \"file=@#{Pathname.pwd}/podfile.lock.zip\" http://nezha.community.cloud.meitu.com/file/upload.json > /dev/null 2>&1") 145 | Pod::UI.puts "pod_time_profiler: 根据开发分支名:#{branch_value}上报podfile.lcok文件到服务端成功".green 146 | `rm -rf podfile.lock.zip` 147 | else 148 | Pod::UI.puts "pod_time_profiler: 根据开发分支名:#{branch_value}上报podfile.lcok文件到服务端失败".red 149 | `rm -rf podfile.lock.zip` 150 | end 151 | end 152 | rescue => error 153 | 154 | end 155 | end 156 | def get_branch_name 157 | branch_value = ENV['branch'] 158 | if !branch_value 159 | mtxx_path = Pod::Config.instance.installation_root + 'mtxx/MTXX' 160 | #判读podfile文件是否存在 161 | if File.exist?(mtxx_path) 162 | Dir.chdir(mtxx_path) do 163 | branch_value = `git symbolic-ref --short -q HEAD` 164 | if branch_value == 'develop' 165 | branch_value = "" 166 | end 167 | end 168 | else 169 | branch_value = `git symbolic-ref --short -q HEAD` 170 | end 171 | end 172 | branch_value = branch_value.gsub("\n", "") 173 | branch_value 174 | end 175 | #过滤出来podfile中实际有效每行内容,拼接成字符串在SHA1 后UTF-8编码下 用来当做依赖缓存文件的key 176 | def get_checksum(file_path) 177 | return nil unless File.exist?(file_path) 178 | content = "" 179 | lines = [] 180 | #过滤出实际使用pod 181 | File.open(file_path, 'r') do |file| 182 | file.each_line do |line| 183 | new_line = line.strip 184 | if new_line.start_with?("pod") 185 | lines << new_line 186 | end 187 | end 188 | end 189 | #给获取的pod list 排序,排除因组件顺序调整导致获取SHA1值不一样 190 | lines = lines.sort 191 | lines.each do |line| 192 | content << line 193 | end 194 | checksum = Digest::SHA1.hexdigest(content) 195 | checksum = checksum.encode('UTF-8') if checksum.respond_to?(:encode) 196 | return checksum 197 | end 198 | 199 | Pod::HooksManager.register('cocoapods-meitu-bin', :pre_install) do |_context| 200 | start_time = Time.now 201 | require 'cocoapods-meitu-bin/native' 202 | require 'cocoapods-meitu-bin/helpers/buildAll/bin_helper' 203 | Pod::UI.puts "当前configuration: `#{ENV['configuration'] || Pod::Config.instance.podfile.configuration}`".green 204 | # pod bin repo update 更新二进制私有源 205 | Pod::Command::Bin::Repo::Update.new(CLAide::ARGV.new($ARGV)).run 206 | content = File.read(Pod::Config.instance.podfile_path) 207 | if content 208 | if content.include?("target \"MTXX\"") 209 | PodUpdateConfig.set_is_mtxx(true) 210 | else 211 | PodUpdateConfig.set_is_mtxx(false) 212 | end 213 | end 214 | # get_podfile_lock 215 | 216 | # 有插件/本地库 且是dev环境下,默认进入源码白名单 过滤 archive命令 217 | if _context.podfile.plugins.keys.include?('cocoapods-meitu-bin') && _context.podfile.configuration_env == 'dev' 218 | dependencies = _context.podfile.dependencies 219 | dependencies.each do |d| 220 | next unless d.respond_to?(:external_source) && 221 | d.external_source.is_a?(Hash) && 222 | !d.external_source[:path].nil? && 223 | $ARGV[1] != 'archive' 224 | _context.podfile.set_use_source_pods d.name 225 | end 226 | 227 | end 228 | PodUpdateConfig.set_prepare_time(Time.now - start_time) 229 | # 同步 BinPodfile 文件 230 | project_root = Pod::Config.instance.project_root 231 | path = File.join(project_root.to_s, 'BinPodfile') 232 | 233 | next unless File.exist?(path) 234 | 235 | contents = File.open(path, 'r:utf-8', &:read) 236 | podfile = Pod::Config.instance.podfile 237 | podfile.instance_eval do 238 | begin 239 | eval(contents, nil, path) 240 | rescue Exception => e 241 | message = "Invalid `#{path}` file: #{e.message}" 242 | raise Pod::DSLError.new(message, path, e, contents) 243 | end 244 | end 245 | PodUpdateConfig.set_prepare_time(Time.now - start_time) 246 | end 247 | 248 | # # 注册 pod install 钩子 249 | # Pod::HooksManager.register('cocoapods-meitu-bin', :post_install) do |context| 250 | # #基于podfile的checksum上报云端podfile.lock文件 251 | # if PodUpdateConfig.is_mtxx 252 | # if PodUpdateConfig.checksum 253 | # upload_podfile_lock(PodUpdateConfig.checksum) 254 | # end 255 | # upload_branch_podfile_lock 256 | # end 257 | # 258 | # end 259 | 260 | Pod::HooksManager.register('cocoapods-meitu-bin', :source_provider) do |context, _| 261 | sources_manager = Pod::Config.instance.sources_manager 262 | podfile = Pod::Config.instance.podfile 263 | if podfile 264 | # 读取配置文件 265 | config_file = File.join(Pod::Config.instance.project_root, 'BinConfig.yaml') 266 | if File.exist?(config_file) 267 | config = YAML.load(File.open(config_file)) 268 | unless config.nil? 269 | build_config = config['install_config'] || {} 270 | use_binary = build_config['use_binary'] || false 271 | podfile.use_binaries!(use_binary) if use_binary 272 | end 273 | end 274 | # 添加源码私有源 && 二进制私有源 275 | added_sources = sources_manager.code_source_list 276 | # if podfile.use_binaries? || podfile.use_binaries_selector 277 | # added_sources << sources_manager.binary_source 278 | # added_sources.reverse! 279 | # end 280 | added_sources.each { |source| context.add_source(source)} 281 | end 282 | end 283 | -------------------------------------------------------------------------------- /lib/cocoapods_plugin.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods-meitu-bin/command' 2 | require 'cocoapods-meitu-bin/source_provider_hook' 3 | # require 'cocoapods-meitu-bin/gem_version' 4 | # 5 | # msg = <<-MSG 6 | # ———————————————————————————————— 7 | # |#{'cocoapods-meitu-bin'.center(30)}| 8 | # |#{CBin::VERSION.center(30)}| 9 | # ———————————————————————————————— 10 | # MSG 11 | # Pod::UI.puts msg.red 12 | -------------------------------------------------------------------------------- /spec/command/bin_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../../spec_helper', __FILE__) 2 | 3 | module Pod 4 | describe Command::Bin do 5 | describe 'CLAide' do 6 | it 'registers it self' do 7 | Command.parse(%w{ bin }).should.be.instance_of Command::Bin 8 | end 9 | end 10 | end 11 | end 12 | 13 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'pathname' 2 | ROOT = Pathname.new(File.expand_path('../../', __FILE__)) 3 | $:.unshift((ROOT + 'lib').to_s) 4 | $:.unshift((ROOT + 'spec').to_s) 5 | 6 | require 'bundler/setup' 7 | require 'bacon' 8 | require 'mocha-on-bacon' 9 | require 'pretty_bacon' 10 | require 'pathname' 11 | require 'cocoapods' 12 | 13 | Mocha::Configuration.prevent(:stubbing_non_existent_method) 14 | 15 | require 'cocoapods_plugin' 16 | 17 | #-----------------------------------------------------------------------------# 18 | 19 | module Pod 20 | 21 | # Disable the wrapping so the output is deterministic in the tests. 22 | # 23 | UI.disable_wrap = true 24 | 25 | # Redirects the messages to an internal store. 26 | # 27 | module UI 28 | @output = '' 29 | @warnings = '' 30 | 31 | class << self 32 | attr_accessor :output 33 | attr_accessor :warnings 34 | 35 | def puts(message = '') 36 | @output << "#{message}\n" 37 | end 38 | 39 | def warn(message = '', actions = []) 40 | @warnings << "#{message}\n" 41 | end 42 | 43 | def print(message) 44 | @output << message 45 | end 46 | end 47 | end 48 | end 49 | 50 | #-----------------------------------------------------------------------------# 51 | --------------------------------------------------------------------------------