├── .gitignore ├── README.md ├── cocoapods-imy-bin ├── .gitignore ├── .rakeTasks ├── BinArchive.json ├── Gemfile ├── Gemfile.lock ├── LICENSE.txt ├── README.md ├── Rakefile ├── cocoapods-imy-bin-0.3.1.2.gem ├── cocoapods-imy-bin.gemspec ├── lib │ ├── cocoapods-imy-bin.rb │ ├── cocoapods-imy-bin │ │ ├── command.rb │ │ ├── command │ │ │ ├── bin.rb │ │ │ └── bin │ │ │ │ ├── archive.rb │ │ │ │ ├── auto.rb │ │ │ │ ├── code.rb │ │ │ │ ├── imy.rb │ │ │ │ ├── init.rb │ │ │ │ ├── initHotKey.rb │ │ │ │ ├── install.rb │ │ │ │ ├── lib │ │ │ │ └── lint.rb │ │ │ │ ├── repo │ │ │ │ └── update.rb │ │ │ │ ├── spec │ │ │ │ ├── create.rb │ │ │ │ └── push.rb │ │ │ │ └── update.rb │ │ ├── config │ │ │ ├── config.rb │ │ │ ├── config_asker.rb │ │ │ ├── config_builder.rb │ │ │ ├── config_hot_key.rb │ │ │ └── config_hot_key_asker.rb │ │ ├── gem_version.rb │ │ ├── helpers.rb │ │ ├── helpers │ │ │ ├── Info.plist │ │ │ ├── build_helper.rb │ │ │ ├── build_utils.rb │ │ │ ├── framework.rb │ │ │ ├── framework_builder.rb │ │ │ ├── library.rb │ │ │ ├── library_builder.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 │ │ │ ├── installation_options.rb │ │ │ ├── installer.rb │ │ │ ├── linter.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 ├── img ├── Podfile.png ├── Podfile_local.png └── time.png ├── 使用教程.md └── 演示视频 ├── 二进制源码调试演示.mp4 ├── 使用二进制.mp4 └── 制作二进制组件.mp4 /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea/ 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 只要两行代码,即可提升10倍以上编译速度 4 | 5 | time 6 | 7 | Podfile 8 | 9 |
10 | 11 | ### 特色: 12 | 13 | 1. **无入侵、无感知、不影响现有业务,不影响现有代码框架、完全绿色产品,~** 14 | 2. **轻量级,只要项目能编译通过就能使用,无视组件化、无视耦合** 15 | 3. **完全自动化,不需要维护Pods库,一键使用、无需手动操作,** 16 | 4. **一步步教你使用,新手也能欢乐玩转** 17 | 5. **提供几个特色服务** 18 | 6. **支持 使用与不使用 use_frameworks!** 19 | 7. **少数支持swift项目二进制化编译的开源项目之一** 20 | 21 | 22 | 23 | 关于 插件具体的架构部署实践和更详细的资源,可以参考 24 | 25 | > [iOS编译速度如何稳定提高10倍以上](https://juejin.cn/post/6903407900006449160) 26 | > 27 | > [iOS美团同款"ZSource"二进制调试实现](https://juejin.im/post/5f066cfa5188252e893a136e) 28 | > 29 | > [iOS教你如何像RN一样实时编译](https://juejin.im/post/6850037272415813645) 30 | > 31 | > [Swift编译慢?请看这里,全套开源](https://juejin.im/post/6890419459639476237) 32 | > 33 | > [OC-Demo](https://github.com/su350380433/cocoapods-imy-bin-demo) 34 | > 35 | > [Swift-OC-Demo](https://github.com/su350380433/Swift-OC-Demo) 36 | 37 | 38 |
39 | 40 | 41 | ### 更新 42 | 43 | - 2021-01-12 44 | 45 | v0.3.1.3 fix some bugs 46 | 47 | - 2020-11-02 48 | 49 | v0.3.0.11 支持纯Swift、纯Object-C、Swift-OC混编 50 | 51 | 52 | ## 一、使用教程 53 | 54 | [使用教程](./使用教程.md) 55 | 56 |
57 | 58 | 59 | ## 二、感谢 60 | 61 | 62 | 63 | **[cocoapods-bin](https://github.com/tripleCC/cocoapods-bin)** 64 | 65 | [美团 iOS 工程 zsource 命令背后的那些事儿](https://tech.meituan.com/2019/08/08/the-things-behind-the-ios-project-zsource-command.html) 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | pkg 3 | .idea/ 4 | examples/ 5 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/.rakeTasks: -------------------------------------------------------------------------------- 1 | 2 | 8 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/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 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/Gemfile: -------------------------------------------------------------------------------- 1 | SKIP_UNRELEASED_VERSIONS = false 2 | 3 | # Specify your gem's dependencies in cocoapods-imy-bin.gemspec 4 | 5 | 6 | def cp_gem(name, repo_name, branch = 'master', path: false) 7 | return gem name if SKIP_UNRELEASED_VERSIONS 8 | opts = if path 9 | { :path => "../#{repo_name}" } 10 | else 11 | url = "https://github.com/CocoaPods/#{repo_name}.git" 12 | { :git => url, :branch => branch } 13 | end 14 | gem name, opts 15 | end 16 | 17 | source 'https://rubygems.org' 18 | 19 | 20 | group :development do 21 | 22 | cp_gem 'cocoapods', 'cocoapods',path: 'CocoaPods' 23 | cp_gem 'xcodeproj', 'xcodeproj',path: 'Xcodeproj' 24 | cp_gem 'cocoapods-imy-bin', 'cocoapods-imy-bin',path: 'cocoapods-imy-bin' 25 | 26 | gem 'cocoapods-generate', '1.6.0' 27 | gem 'mocha' 28 | gem 'bacon' 29 | gem 'mocha-on-bacon' 30 | gem 'prettybacon' 31 | 32 | end 33 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: ../cocoapods 3 | specs: 4 | cocoapods (1.8.4) 5 | activesupport (>= 4.0.2, < 5) 6 | claide (>= 1.0.2, < 2.0) 7 | cocoapods-core (= 1.8.4) 8 | cocoapods-deintegrate (>= 1.0.3, < 2.0) 9 | cocoapods-downloader (>= 1.2.2, < 2.0) 10 | cocoapods-plugins (>= 1.0.0, < 2.0) 11 | cocoapods-search (>= 1.0.0, < 2.0) 12 | cocoapods-stats (>= 1.0.0, < 2.0) 13 | cocoapods-trunk (>= 1.4.0, < 2.0) 14 | cocoapods-try (>= 1.1.0, < 2.0) 15 | colored2 (~> 3.1) 16 | escape (~> 0.0.4) 17 | fourflusher (>= 2.3.0, < 3.0) 18 | gh_inspector (~> 1.0) 19 | molinillo (~> 0.6.6) 20 | nap (~> 1.0) 21 | ruby-macho (~> 1.4) 22 | xcodeproj (>= 1.11.1, < 2.0) 23 | 24 | PATH 25 | remote: ../xcodeproj 26 | specs: 27 | xcodeproj (1.15.0) 28 | CFPropertyList (>= 2.3.3, < 4.0) 29 | atomos (~> 0.1.3) 30 | claide (>= 1.0.2, < 2.0) 31 | colored2 (~> 3.1) 32 | nanaimo (~> 0.2.6) 33 | 34 | PATH 35 | remote: . 36 | specs: 37 | cocoapods-imy-bin (0.2.0) 38 | cocoapods 39 | cocoapods-generate (= 1.6.0) 40 | parallel 41 | 42 | GEM 43 | remote: https://rubygems.org/ 44 | specs: 45 | CFPropertyList (3.0.2) 46 | activesupport (4.2.11.3) 47 | i18n (~> 0.7) 48 | minitest (~> 5.1) 49 | thread_safe (~> 0.3, >= 0.3.4) 50 | tzinfo (~> 1.1) 51 | algoliasearch (1.27.3) 52 | httpclient (~> 2.8, >= 2.8.3) 53 | json (>= 1.5.1) 54 | atomos (0.1.3) 55 | bacon (1.2.0) 56 | claide (1.0.3) 57 | cocoapods-core (1.8.4) 58 | activesupport (>= 4.0.2, < 6) 59 | algoliasearch (~> 1.0) 60 | concurrent-ruby (~> 1.1) 61 | fuzzy_match (~> 2.0.4) 62 | nap (~> 1.0) 63 | cocoapods-deintegrate (1.0.4) 64 | cocoapods-disable-podfile-validations (0.1.1) 65 | cocoapods-downloader (1.3.0) 66 | cocoapods-generate (1.6.0) 67 | cocoapods-disable-podfile-validations (~> 0.1.1) 68 | cocoapods-plugins (1.0.0) 69 | nap 70 | cocoapods-search (1.0.0) 71 | cocoapods-stats (1.1.0) 72 | cocoapods-trunk (1.5.0) 73 | nap (>= 0.8, < 2.0) 74 | netrc (~> 0.11) 75 | cocoapods-try (1.2.0) 76 | colored2 (3.1.2) 77 | concurrent-ruby (1.1.6) 78 | escape (0.0.4) 79 | fourflusher (2.3.1) 80 | fuzzy_match (2.0.4) 81 | gh_inspector (1.1.3) 82 | httpclient (2.8.3) 83 | i18n (0.9.5) 84 | concurrent-ruby (~> 1.0) 85 | json (2.3.0) 86 | minitest (5.14.1) 87 | mocha (1.11.2) 88 | mocha-on-bacon (0.2.3) 89 | mocha (>= 0.13.0) 90 | molinillo (0.6.6) 91 | nanaimo (0.2.6) 92 | nap (1.1.0) 93 | netrc (0.11.0) 94 | parallel (1.19.1) 95 | prettybacon (0.0.2) 96 | bacon (~> 1.2) 97 | ruby-macho (1.4.0) 98 | thread_safe (0.3.6) 99 | tzinfo (1.2.7) 100 | thread_safe (~> 0.1) 101 | 102 | PLATFORMS 103 | ruby 104 | 105 | DEPENDENCIES 106 | bacon 107 | cocoapods! 108 | cocoapods-generate (= 1.6.0) 109 | cocoapods-imy-bin! 110 | mocha 111 | mocha-on-bacon 112 | prettybacon 113 | xcodeproj! 114 | 115 | BUNDLED WITH 116 | 2.1.4 117 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 tripleCC 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/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-imy-bin/cocoapods-imy-bin-0.3.1.2.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MeetYouDevs/cocoapods-imy-bin/36b201004ea3610e3278429c48d3cf49b4b650d2/cocoapods-imy-bin/cocoapods-imy-bin-0.3.1.2.gem -------------------------------------------------------------------------------- /cocoapods-imy-bin/cocoapods-imy-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-imy-bin/gem_version.rb' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = 'cocoapods-imy-bin' 8 | spec.version = CBin::VERSION 9 | spec.authors = ['苏良锦'] 10 | spec.email = ['suliangjin@xiaoyouzi.com'] 11 | spec.description = %q{cocoapods-imy-bin is a plugin which helps develpers switching pods between source code and binary.} 12 | spec.summary = %q{cocoapods-imy-bin is a plugin which helps develpers switching pods between source code and binary.} 13 | spec.homepage = 'https://github.com/su350380433/cocaopods-imy-bin' 14 | spec.license = 'MIT' 15 | 16 | spec.files = Dir["lib/**/*.rb","spec/**/*.rb","lib/**/*.plist"] + %w{README.md LICENSE.txt } 17 | 18 | #spec.files = `git ls-files`.split($/) 19 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 20 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 21 | spec.require_paths = ['lib'] 22 | 23 | spec.add_dependency 'parallel' 24 | spec.add_dependency 'cocoapods' 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 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods-imy-bin/gem_version' 2 | require 'cocoapods-imy-bin/native/sources_manager' 3 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/command.rb: -------------------------------------------------------------------------------- 1 | 2 | require 'cocoapods-imy-bin/command/bin' 3 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/command/bin.rb: -------------------------------------------------------------------------------- 1 | 2 | require 'cocoapods-imy-bin/command/bin/initHotKey' 3 | require 'cocoapods-imy-bin/command/bin/init' 4 | require 'cocoapods-imy-bin/command/bin/archive' 5 | require 'cocoapods-imy-bin/command/bin/auto' 6 | require 'cocoapods-imy-bin/command/bin/code' 7 | require 'cocoapods-imy-bin/command/bin/update' 8 | require 'cocoapods-imy-bin/command/bin/install' 9 | require 'cocoapods-imy-bin/command/bin/imy' 10 | 11 | require 'cocoapods-imy-bin/helpers' 12 | 13 | module Pod 14 | class Command 15 | # This is an example of a cocoapods plugin adding a top-level subcommand 16 | # to the 'pod' command. 17 | # 18 | # You can also create subcommands of existing or new commands. Say you 19 | # wanted to add a subcommand to `list` to show newly deprecated pods, 20 | # (e.g. `pod list deprecated`), there are a few things that would need 21 | # to change. 22 | # 23 | # - move this file to `lib/pod/command/list/deprecated.rb` and update 24 | # the class to exist in the the Pod::Command::List namespace 25 | # - change this class to extend from `List` instead of `Command`. This 26 | # tells the plugin system that it is a subcommand of `list`. 27 | # - edit `lib/cocoapods_plugins.rb` to require this file 28 | # 29 | # @todo Create a PR to add your plugin to CocoaPods/cocoapods.org 30 | # in the `plugins.json` file, once your plugin is released. 31 | # 32 | class Bin < Command 33 | include CBin::SourcesHelper 34 | include CBin::SpecFilesHelper 35 | 36 | self.abstract_command = true 37 | self.summary = '组件二进制化插件.' 38 | self.description = <<-DESC 39 | 组件二进制化插件。利用源码私有源与二进制私有源实现对组件依赖类型的切换。 40 | DESC 41 | 42 | def initialize(argv) 43 | require 'cocoapods-imy-bin/native' 44 | 45 | @help = argv.flag?('help') 46 | super 47 | end 48 | 49 | def validate! 50 | super 51 | # 这里由于 --help 是在 validate! 方法中提取的,会导致 --help 失效 52 | # pod lib create 也有这个问题 53 | banner! if @help 54 | end 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/command/bin/archive.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods-imy-bin/native/podfile' 2 | require 'cocoapods/command/gen' 3 | require 'cocoapods/generate' 4 | require 'cocoapods-imy-bin/helpers/framework_builder' 5 | require 'cocoapods-imy-bin/helpers/library_builder' 6 | require 'cocoapods-imy-bin/helpers/build_helper' 7 | require 'cocoapods-imy-bin/helpers/spec_source_creator' 8 | require 'cocoapods-imy-bin/config/config_builder' 9 | require 'cocoapods-imy-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.' 19 | self.description = <<-DESC 20 | 将组件归档为静态库 framework,仅支持 iOS 平台 21 | 此静态 framework 不包含依赖组件的 symbol 22 | DESC 23 | 24 | def self.options 25 | [ 26 | ['--all-make', '对该组件的依赖库,全部制作为二进制组件'], 27 | ['--code-dependencies', '使用源码依赖'], 28 | ['--no-clean', '保留构建中间产物'], 29 | ['--sources', '私有源地址,多个用分号区分'], 30 | ['--framework-output', '输出framework文件'], 31 | ['--no-zip', '不压缩静态库 为 zip'], 32 | ['--configuration', 'Build the specified configuration (e.g. Debug). Defaults to Release'], 33 | ['--env', "该组件上传的环境 %w[dev debug_iphoneos release_iphoneos]"] 34 | ].concat(Pod::Command::Gen.options).concat(super).uniq 35 | end 36 | 37 | self.arguments = [ 38 | CLAide::Argument.new('NAME.podspec', false) 39 | ] 40 | 41 | def initialize(argv) 42 | @env = argv.option('env') || 'dev' 43 | CBin.config.set_configuration_env(@env) 44 | UI.warn "====== cocoapods-imy-bin #{CBin::VERSION} 版本 ======== \n " 45 | UI.warn "====== #{@env} 环境 ======== \n " 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 | 69 | @spec = Specification.from_file(spec_file) 70 | generate_project 71 | 72 | source_specs = Array.new 73 | source_specs.concat(build_root_spec) 74 | source_specs.concat(build_dependencies) if @all_make 75 | 76 | source_specs 77 | end 78 | 79 | def build_root_spec 80 | source_specs = [] 81 | builder = CBin::Build::Helper.new(@spec, 82 | @platform, 83 | @framework_output, 84 | @zip, 85 | @spec, 86 | CBin::Config::Builder.instance.white_pod_list.include?(@spec.name), 87 | @config) 88 | builder.build 89 | builder.clean_workspace if @clean && !@all_make 90 | source_specs << @spec unless CBin::Config::Builder.instance.white_pod_list.include?(@spec.name) 91 | 92 | source_specs 93 | end 94 | 95 | def build_dependencies 96 | @build_finshed = true 97 | #如果没要求,就清空依赖库数据 98 | source_specs = [] 99 | @@missing_binary_specs.uniq.each do |spec| 100 | next if spec.name.include?('/') 101 | next if spec.name == @spec.name 102 | #过滤白名单 103 | next if CBin::Config::Builder.instance.white_pod_list.include?(spec.name) 104 | #过滤 git 105 | if spec.source[:git] && spec.source[:git] 106 | spec_git = spec.source[:git] 107 | spec_git_res = false 108 | CBin::Config::Builder.instance.ignore_git_list.each do |ignore_git| 109 | spec_git_res = spec_git.include?(ignore_git) 110 | break if spec_git_res 111 | end 112 | next if spec_git_res 113 | end 114 | UI.warn "#{spec.name}.podspec 带有 vendored_frameworks 字段,请检查是否有效!!!" if spec.attributes_hash['vendored_frameworks'] 115 | next if spec.attributes_hash['vendored_frameworks'] && @spec.name != spec.name #过滤带有vendored_frameworks的 116 | next if spec.attributes_hash['ios.vendored_frameworks'] && @spec.name != spec.name #过滤带有vendored_frameworks的 117 | #获取没有制作二进制版本的spec集合 118 | source_specs << spec 119 | end 120 | 121 | fail_build_specs = [] 122 | source_specs.uniq.each do |spec| 123 | begin 124 | builder = CBin::Build::Helper.new(spec, 125 | @platform, 126 | @framework_output, 127 | @zip, 128 | @spec, 129 | false , 130 | @config) 131 | builder.build 132 | rescue Object => exception 133 | UI.puts exception 134 | fail_build_specs << spec 135 | end 136 | end 137 | 138 | if fail_build_specs.any? 139 | fail_build_specs.uniq.each do |spec| 140 | UI.warn "【#{spec.name} | #{spec.version}】组件二进制版本编译失败 ." 141 | end 142 | end 143 | source_specs - fail_build_specs 144 | end 145 | 146 | # 解析器传过来的 147 | def Archive.missing_binary_specs(missing_binary_specs) 148 | @@missing_binary_specs = missing_binary_specs unless @build_finshed 149 | end 150 | 151 | private 152 | 153 | def generate_project 154 | Podfile.execute_with_bin_plugin do 155 | Podfile.execute_with_use_binaries(!@code_dependencies) do 156 | argvs = [ 157 | "--sources=#{sources_option(@code_dependencies, @sources)}", 158 | "--gen-directory=#{CBin::Config::Builder.instance.gen_dir}", 159 | '--clean', 160 | *@additional_args 161 | ] 162 | 163 | podfile= File.join(Pathname.pwd, "Podfile") 164 | if File.exist?(podfile) 165 | argvs += ['--use-podfile'] 166 | end 167 | 168 | argvs << spec_file if spec_file 169 | 170 | gen = Pod::Command::Gen.new(CLAide::ARGV.new(argvs)) 171 | gen.validate! 172 | gen.run 173 | end 174 | end 175 | end 176 | 177 | 178 | def spec_file 179 | @spec_file ||= begin 180 | if @podspec 181 | find_spec_file(@podspec) 182 | else 183 | if code_spec_files.empty? 184 | raise Informative, '当前目录下没有找到可用源码 podspec.' 185 | end 186 | 187 | spec_file = code_spec_files.first 188 | spec_file 189 | end 190 | end 191 | end 192 | 193 | 194 | 195 | end 196 | end 197 | end 198 | end 199 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/command/bin/auto.rb: -------------------------------------------------------------------------------- 1 | 2 | require 'cocoapods-imy-bin/command/bin/auto' 3 | require 'cocoapods-imy-bin/helpers/upload_helper' 4 | 5 | module Pod 6 | class Command 7 | class Bin < Command 8 | class Auto < Bin 9 | self.summary = '打开 workspace 工程.' 10 | 11 | self.arguments = [ 12 | CLAide::Argument.new('NAME.podspec', false) 13 | ] 14 | def self.options 15 | [ 16 | ['--code-dependencies', '使用源码依赖'], 17 | ['--allow-prerelease', '允许使用 prerelease 的版本'], 18 | ['--no-clean', '保留构建中间产物'], 19 | ['--framework-output', '输出framework文件'], 20 | ['--no-zip', '不压缩静态 framework 为 zip'], 21 | ['--all-make', '对该组件的依赖库,全部制作为二进制组件'], 22 | ['--configuration', 'Build the specified configuration (e.g. Release ). Defaults to Debug'], 23 | ['--env', "该组件上传的环境 %w[dev debug_iphoneos release_iphoneos]"] 24 | ].concat(Pod::Command::Gen.options).concat(super).uniq 25 | end 26 | 27 | def initialize(argv) 28 | 29 | @env = argv.option('env') || 'dev' 30 | CBin.config.set_configuration_env(@env) 31 | 32 | @podspec = argv.shift_argument || find_podspec 33 | @specification = Specification.from_file(@podspec) 34 | 35 | @code_dependencies = argv.flag?('code-dependencies') 36 | @allow_prerelease = argv.flag?('allow-prerelease') 37 | @framework_output = argv.flag?('framework-output', false ) 38 | @clean = argv.flag?('clean', true) 39 | @zip = argv.flag?('zip', true) 40 | @all_make = argv.flag?('all-make', false ) 41 | @verbose = argv.flag?('verbose',true) 42 | 43 | @config = argv.option('configuration', 'Debug') 44 | @additional_args = argv.remainder! 45 | 46 | super 47 | end 48 | 49 | def validate! 50 | help! "未找到 podspec文件" unless @podspec 51 | super 52 | end 53 | 54 | def run 55 | @specification = Specification.from_file(@podspec) 56 | 57 | source_specs = run_archive 58 | 59 | fail_push_specs = [] 60 | source_specs.uniq.each do |spec| 61 | begin 62 | fail_push_specs << spec unless CBin::Upload::Helper.new(spec,@code_dependencies,@sources).upload 63 | rescue Object => exception 64 | UI.puts exception 65 | fail_push_specs << spec 66 | end 67 | end 68 | 69 | if fail_push_specs.any? 70 | fail_push_specs.uniq.each do |spec| 71 | UI.warn "【#{spec.name} | #{spec.version}】组件spec push失败 ." 72 | end 73 | end 74 | 75 | success_specs = source_specs - fail_push_specs 76 | if success_specs.any? 77 | auto_success = "" 78 | success_specs.uniq.each do |spec| 79 | auto_success += "#{spec.name} | #{spec.version}\n" 80 | UI.warn "===【 #{spec.name} | #{spec.version} 】二进制组件制作完成 !!! " 81 | end 82 | puts "============== auto_success" 83 | puts auto_success 84 | ENV['auto_success'] = auto_success 85 | end 86 | #pod repo update 87 | UI.section("\nUpdating Spec Repositories\n".yellow) do 88 | Pod::Command::Bin::Repo::Update.new(CLAide::ARGV.new([])).run 89 | end 90 | 91 | end 92 | 93 | #制作二进制包 94 | 95 | def run_archive 96 | argvs = [ 97 | "--sources=#{sources_option(@code_dependencies, @sources)},https:\/\/cdn.cocoapods.org", 98 | @additional_args 99 | ] 100 | 101 | argvs << spec_file if spec_file 102 | argvs.delete(Array.new) 103 | 104 | unless @clean 105 | argvs += ['--no-clean'] 106 | end 107 | if @code_dependencies 108 | argvs += ['--code-dependencies'] 109 | end 110 | if @verbose 111 | argvs += ['--verbose'] 112 | end 113 | if @allow_prerelease 114 | argvs += ['--allow-prerelease'] 115 | end 116 | if @framework_output 117 | argvs += ['--framework-output'] 118 | end 119 | if @all_make 120 | argvs += ['--all-make'] 121 | end 122 | if @env 123 | argvs += ["--env=#{@env}"] 124 | end 125 | argvs += ["--configuration=#{@config}"] 126 | 127 | archive = Pod::Command::Bin::Archive.new(CLAide::ARGV.new(argvs)) 128 | archive.validate! 129 | source_specs = archive.run 130 | source_specs 131 | end 132 | 133 | 134 | def code_podsepc_extname 135 | '.podsepc' 136 | end 137 | 138 | def binary_podsepc_json 139 | "#{@specification.name}.binary.podspec.json" 140 | end 141 | 142 | def binary_template_podsepc 143 | "#{@specification.name}.binary-template.podspec" 144 | end 145 | 146 | def template_spec_file 147 | @template_spec_file ||= begin 148 | if @template_podspec 149 | find_spec_file(@template_podspec) 150 | else 151 | binary_template_spec_file 152 | end 153 | end 154 | end 155 | 156 | def spec_file 157 | @spec_file ||= begin 158 | if @podspec 159 | find_spec_file(@podspec) || @podspec 160 | else 161 | if code_spec_files.empty? 162 | raise Informative, '当前目录下没有找到可用源码 podspec.' 163 | end 164 | 165 | spec_file = if @binary 166 | code_spec = Pod::Specification.from_file(code_spec_files.first) 167 | if template_spec_file 168 | template_spec = Pod::Specification.from_file(template_spec_file) 169 | end 170 | create_binary_spec_file(code_spec, template_spec) 171 | else 172 | code_spec_files.first 173 | end 174 | spec_file 175 | end 176 | end 177 | end 178 | 179 | #Dir.glob 可替代 180 | def find_podspec 181 | name = nil 182 | Pathname.pwd.children.each do |child| 183 | puts child 184 | if File.file?(child) 185 | if child.extname == '.podspec' 186 | name = File.basename(child) 187 | unless name.include?("binary-template") 188 | return name 189 | end 190 | end 191 | end 192 | end 193 | return name 194 | end 195 | 196 | end 197 | end 198 | end 199 | end 200 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/command/bin/code.rb: -------------------------------------------------------------------------------- 1 | 2 | module Pod 3 | class Command 4 | class Bin < Command 5 | class Code < Bin 6 | self.summary = '通过将二进制对应源码放置在临时目录中,让二进制出现断点时可以跳到对应的源码,方便调试。' 7 | 8 | self.description = <<-DESC 9 | 通过将二进制对应源码放置在临时目录中,让二进制出现断点时可以跳到对应的源码,方便调试。 10 | 在不删除二进制的情况下为某个组件添加源码调试能力,多个组件名称用空格分隔 11 | DESC 12 | 13 | self.arguments = [ 14 | CLAide::Argument.new('NAME', false) 15 | ] 16 | def self.options 17 | [ 18 | ['--all-clean', '删除所有已经下载的源码'], 19 | ['--clean', '删除所有指定下载的源码'], 20 | ['--list', '展示所有一级下载的源码以及其大小'], 21 | ['--source', '源码路径,本地路径,会去自动链接本地源码'] 22 | ] 23 | end 24 | 25 | def initialize(argv) 26 | @codeSource = argv.option('source') || nil 27 | @names = argv.arguments! unless argv.arguments.empty? 28 | @list = argv.flag?('list', false ) 29 | @all_clean = argv.flag?('all-clean', false ) 30 | @clean = argv.flag?('clean', false ) 31 | 32 | @config = Pod::Config.instance 33 | 34 | super 35 | end 36 | 37 | 38 | def run 39 | 40 | podfile_lock = File.join(Pathname.pwd,"Podfile.lock") 41 | raise "podfile.lock,不存在,请先pod install/update" unless File.exist?(podfile_lock) 42 | @lockfile ||= Lockfile.from_file(Pathname.new(podfile_lock) ) 43 | 44 | if @list 45 | list 46 | elsif @clean 47 | clean 48 | elsif @all_clean 49 | all_clean 50 | elsif @names 51 | add 52 | end 53 | 54 | if @list && @clean && @names 55 | raise "请选择您要执行的命令。" 56 | end 57 | end 58 | 59 | #==========================begin add ============== 60 | 61 | def add 62 | if @names == nil 63 | raise "请输入要调试组件名,多个组件名称用空格分隔" 64 | end 65 | 66 | @names.each do |name| 67 | lib_file = get_lib_path(name) 68 | unless File.exist?(lib_file) 69 | raise "找不到 #{lib_file}" 70 | end 71 | UI.puts "#{lib_file}" 72 | 73 | target_path = @codeSource || download_source(name) 74 | 75 | link(lib_file,target_path,name) 76 | end 77 | end 78 | 79 | #下载源码到本地 80 | def download_source(name) 81 | target_path = File.join(source_root, name) 82 | UI.puts target_path 83 | FileUtils.rm_rf(target_path) 84 | 85 | find_dependency = find_dependency(name) 86 | 87 | spec = fetch_external_source(find_dependency, @config.podfile,@config.lockfile, @config.sandbox,true ) 88 | 89 | download_request = Pod::Downloader::Request.new(:name => name, :spec => spec) 90 | Downloader.download(download_request, Pathname.new(target_path), :can_cache => true) 91 | 92 | target_path 93 | end 94 | 95 | #找出依赖 96 | def find_dependency (name) 97 | find_dependency = nil 98 | @config.podfile.dependencies.each do |dependency| 99 | if dependency.root_name.downcase == name.downcase 100 | find_dependency = dependency 101 | break 102 | end 103 | end 104 | find_dependency 105 | end 106 | 107 | # 获取external_source 下的仓库 108 | # @return spec 109 | def fetch_external_source(dependency ,podfile , lockfile, sandbox,use_lockfile_options) 110 | source = ExternalSources.from_dependency(dependency, podfile.defined_in_file, true) 111 | source.fetch(sandbox) 112 | end 113 | 114 | 115 | #==========================link begin ============== 116 | 117 | #链接,.a文件位置, 源码目录,工程名=IMYFoundation 118 | def link(lib_file,target_path,basename) 119 | dir = (`dwarfdump "#{lib_file}" | grep "AT_comp_dir" | head -1 | cut -d \\" -f2 `) 120 | sub_path = "#{basename}/bin-archive/#{basename}" 121 | dir = dir.gsub(sub_path, "").chomp 122 | # UI.puts "dir = #{dir}" 123 | 124 | unless File.exist?(dir) 125 | # UI.puts "不存在 = #{dir}" 126 | begin 127 | FileUtils.mkdir_p(dir) 128 | rescue SystemCallError 129 | #判断用户目录是否存在 130 | array = dir.split('/') 131 | if array.length > 3 132 | root_path = '/' + array[1] + '/' + array[2] 133 | unless File.exist?(root_path) 134 | raise "由于权限不足,请手动创建#{root_path} 后重试" 135 | end 136 | end 137 | end 138 | end 139 | 140 | if Pathname.new(lib_file).extname == ".a" 141 | FileUtils.rm_rf(File.join(dir,basename)) 142 | `ln -s #{target_path} #{dir}` 143 | else 144 | FileUtils.rm_rf(File.join(dir,basename)) 145 | `ln -s #{target_path} #{dir}/#{basename}` 146 | end 147 | 148 | check(lib_file,dir,basename) 149 | end 150 | 151 | def check(lib_file,dir,basename) 152 | file = `dwarfdump "#{lib_file}" | grep -E "DW_AT_decl_file.*#{basename}.*\\.m|\\.c" | head -1 | cut -d \\" -f2` 153 | if File.exist?(file) 154 | raise "#{file} 不存在 请检测代码源是否正确~" 155 | end 156 | UI.puts "link successfully!" 157 | UI.puts "view linked source at path: #{dir}" 158 | end 159 | 160 | def get_lib_path(name) 161 | dir = Pathname.new(File.join(Pathname.pwd,"Pods",name)) 162 | lib_name = "lib#{name}.a" 163 | lib_path = File.join(dir,lib_name) 164 | 165 | unless File.exist?(lib_path) 166 | lib_path = File.join(dir.children.first,lib_name) 167 | end 168 | 169 | lib_path 170 | end 171 | 172 | #源码地址 173 | # def get_gitlib_iOS_path(name) 174 | # "git@gitlab.xxx.com:iOS/#{name}.git" 175 | # end 176 | #要转换的地址,Github-iOS默认都是静态库 177 | # def git_gitlib_iOS_path 178 | # 'git@gitlab.xxx.com:Github-iOS/' 179 | # end 180 | 181 | 182 | #要转换的地址,Github-iOS默认都是静态库 183 | # def http_gitlib_GitHub_iOS_path 184 | # 'https://gitlab.xxx.com/Github-iOS/' 185 | # end 186 | 187 | #要转换的地址,iOS默认都是静态库 188 | # def http_gitlib_iOS_path 189 | # 'https://gitlab.xxx.com/iOS/' 190 | # end 191 | 192 | #==========================list begin ============== 193 | 194 | def list 195 | Dir.entries(source_root).each do |sub| 196 | UI.puts "- #{sub}" unless sub.include?('.') 197 | end 198 | UI.puts "加载完成" 199 | end 200 | 201 | 202 | #==========================clean begin ============== 203 | def all_clean 204 | FileUtils.rm_rf(source_root) if File.directory?(source_root) 205 | UI.puts "清理完成 #{source_root}" 206 | end 207 | 208 | def clean 209 | raise "请输入要删除的组件库" if @names.nil? 210 | @names.each do |name| 211 | full_path = File.join(source_root,name) 212 | if File.directory?(full_path) 213 | FileUtils.rm_rf(full_path) 214 | else 215 | UI.puts "找不到 #{full_path}".yellow 216 | end 217 | end 218 | UI.puts "清理完成 #{@names.to_s}" 219 | end 220 | 221 | private 222 | 223 | def source_root 224 | dir = File.join(@config.cache_root,"Source") 225 | FileUtils.mkdir_p(dir) unless File.exist? dir 226 | dir 227 | end 228 | 229 | end 230 | end 231 | end 232 | end 233 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/command/bin/imy.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods-imy-bin/config/config_hot_key_asker' 2 | 3 | module Pod 4 | class Command 5 | class Bin < Command 6 | class Imy < Bin 7 | self.summary = '快捷键' 8 | self.description = <<-DESC 9 | 创建 文件,在其中保存插件需要的配置信息, 10 | 如二进制私有源地址、源码私有源地址等。 11 | DESC 12 | 13 | self.arguments = [ 14 | CLAide::Argument.new('1', false) 15 | ] 16 | 17 | def self.options 18 | [ 19 | ].concat(super) 20 | end 21 | 22 | def initialize(argv) 23 | @hot_key = argv.shift_argument || '1' 24 | super 25 | end 26 | 27 | def run 28 | CBin.config_hot_key.set_hot_key_index(@hot_key) 29 | UI.puts "cd #{CBin.config_hot_key.hot_key_dir}".yellow 30 | 31 | if Dir.exist?(CBin.config_hot_key.hot_key_dir) 32 | Dir.chdir(CBin.config_hot_key.hot_key_dir) do 33 | UI.puts " #{CBin.config_hot_key.hot_key_cmd}".yellow 34 | system CBin.config_hot_key.hot_key_cmd 35 | end 36 | else 37 | raise "配置项中文件目录不存在 #{CBin.config_hot_key.hot_key_dir}" 38 | end 39 | 40 | 41 | end 42 | 43 | end 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/command/bin/init.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods-imy-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 | 创建 文件,在其中保存插件需要的配置信息, 10 | 如快捷键1 快捷键2 所对应执行的命令 11 | DESC 12 | 13 | def self.options 14 | [ 15 | ['--bin-url=URL', '配置文件地址,直接从此地址下载配置文件'] 16 | ].concat(super) 17 | end 18 | 19 | def initialize(argv) 20 | @bin_url = argv.option('bin-url') 21 | super 22 | end 23 | 24 | def run 25 | if @bin_url.nil? 26 | config_with_asker 27 | else 28 | config_with_url(@bin_url) 29 | end 30 | end 31 | 32 | private 33 | 34 | def config_with_url(url) 35 | require 'open-uri' 36 | 37 | UI.puts "开始下载配置文件...\n" 38 | file = open(url) 39 | contents = YAML.safe_load(file.read) 40 | 41 | UI.puts "开始同步配置文件...\n" 42 | CBin.config.sync_config(contents.to_hash) 43 | UI.puts "设置完成.\n".green 44 | rescue Errno::ENOENT => e 45 | raise Informative, "配置文件路径 #{url} 无效,请确认后重试." 46 | end 47 | 48 | def config_with_asker 49 | asker = CBin::Config::Asker.new 50 | asker.wellcome_message 51 | 52 | config = {} 53 | template_hash = CBin.config.template_hash 54 | template_hash.each do |k, v| 55 | default = begin 56 | CBin.config.send(k) 57 | rescue StandardError 58 | nil 59 | end 60 | config[k] = asker.ask_with_answer(v[:description], default, v[:selection]) 61 | end 62 | 63 | CBin.config.sync_config(config) 64 | asker.done_message 65 | end 66 | end 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/command/bin/initHotKey.rb: -------------------------------------------------------------------------------- 1 | 2 | require 'cocoapods-imy-bin/config/config_hot_key_asker' 3 | 4 | module Pod 5 | class Command 6 | class Bin < Command 7 | class Inithk < Bin 8 | self.summary = '初始化快捷键配置.' 9 | self.description = <<-DESC 10 | 创建 文件,在其中保存插件需要的配置信息, 11 | 如二进制私有源地址、源码私有源地址等。 12 | DESC 13 | 14 | def self.options 15 | [ 16 | ['--bin-url=URL', '配置文件地址,直接从此地址下载配置文件'] 17 | ].concat(super) 18 | end 19 | 20 | def initialize(argv) 21 | @bin_url = argv.option('bin-url') 22 | super 23 | end 24 | 25 | def run 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 | def config_with_url(url) 36 | require 'open-uri' 37 | 38 | UI.puts "开始下载配置文件...\n" 39 | file = open(url) 40 | contents = YAML.safe_load(file.read) 41 | 42 | UI.puts "开始同步配置文件...\n" 43 | CBin.config_hot_key.sync_config(contents.to_hash) 44 | UI.puts "设置完成.\n".green 45 | rescue Errno::ENOENT => e 46 | raise Informative, "配置文件路径 #{url} 无效,请确认后重试." 47 | end 48 | 49 | def config_with_asker 50 | asker = CBin::Config_Hot_Key::Asker.new 51 | asker.wellcome_message 52 | 53 | config = {} 54 | template_hash = CBin.config_hot_key.template_hash 55 | template_hash.each do |k, v| 56 | default = begin 57 | CBin.config_hot_key.send(k) 58 | rescue StandardError 59 | nil 60 | end 61 | config[k] = asker.ask_with_answer(v[:description], default, v[:selection]) 62 | end 63 | 64 | CBin.config_hot_key.sync_config(config) 65 | asker.done_message 66 | end 67 | end 68 | end 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/command/bin/install.rb: -------------------------------------------------------------------------------- 1 | 2 | require 'cocoapods-imy-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 拦截器,会加载本地Podfile_local文件,DSL加载到原始Podfile文件中。' 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 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/command/bin/lib/lint.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods-imy-bin/config/config' 2 | require 'cocoapods-imy-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 | 64 | 65 | end 66 | end 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-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 | show_output = !config.silent? 28 | if @name || @all 29 | config.sources_manager.update(@name, show_output) 30 | else 31 | Parallel.each(valid_sources, in_threads: 4) do |source| 32 | UI.puts "更新私有源仓库 #{source.to_s}".yellow 33 | source.update(show_output) 34 | end 35 | end 36 | end 37 | 38 | 39 | end 40 | end 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/command/bin/spec/create.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods-imy-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 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/command/bin/spec/push.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods-imy-bin/config/config' 2 | require 'cocoapods-imy-bin/native/podfile' 3 | 4 | module Pod 5 | class Command 6 | class Bin < Command 7 | class Repo < Bin 8 | class Push < Repo 9 | self.summary = '发布组件.' 10 | self.description = <<-DESC 11 | 发布二进制组件 / 源码组件 12 | DESC 13 | 14 | self.arguments = [ 15 | CLAide::Argument.new('NAME.podspec', false) 16 | ] 17 | 18 | def self.options 19 | [ 20 | ['--binary', '发布组件的二进制版本'], 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::Repo::Push.options).concat(super).uniq 27 | end 28 | 29 | def initialize(argv) 30 | @podspec = argv.shift_argument 31 | @binary = argv.flag?('binary') 32 | @loose_options = argv.flag?('loose-options') 33 | @code_dependencies = argv.flag?('code-dependencies') 34 | @sources = argv.option('sources') || [] 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 | repo, 49 | "--sources=#{sources_option(@code_dependencies, @sources)}", 50 | *@additional_args 51 | ] 52 | 53 | argvs << spec_file if spec_file 54 | 55 | if @loose_options 56 | argvs += ['--allow-warnings', '--use-json'] 57 | if code_spec&.all_dependencies&.any? 58 | argvs << '--use-libraries' 59 | end 60 | end 61 | 62 | push = Pod::Command::Repo::Push.new(CLAide::ARGV.new(argvs)) 63 | push.validate! 64 | push.run 65 | end 66 | end 67 | end 68 | ensure 69 | clear_binary_spec_file_if_needed unless @reserve_created_spec 70 | end 71 | 72 | private 73 | 74 | def template_spec_file 75 | @template_spec_file ||= begin 76 | if @template_podspec 77 | find_spec_file(@template_podspec) 78 | else 79 | binary_template_spec_file 80 | end 81 | end 82 | end 83 | 84 | def spec_file 85 | @spec_file ||= begin 86 | if @podspec 87 | find_spec_file(@podspec) 88 | else 89 | if code_spec_files.empty? 90 | raise Informative, '当前目录下没有找到可用源码 podspec.' 91 | end 92 | 93 | spec_file = if @binary 94 | code_spec = Pod::Specification.from_file(code_spec_files.first) 95 | if template_spec_file 96 | template_spec = Pod::Specification.from_file(template_spec_file) 97 | end 98 | create_binary_spec_file(code_spec, template_spec) 99 | else 100 | code_spec_files.first 101 | end 102 | spec_file 103 | end 104 | end 105 | end 106 | 107 | def repo 108 | @binary ? binary_source.name : code_source.name 109 | end 110 | end 111 | end 112 | end 113 | end 114 | end 115 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/command/bin/update.rb: -------------------------------------------------------------------------------- 1 | 2 | require 'cocoapods' 3 | require 'cocoapods-imy-bin/native/podfile_env' 4 | require 'cocoapods-imy-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 拦截器,会加载本地Podfile_local文件,DSL加载到原始Podfile文件中。' 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 | dp_hash_equal = target_dependency.is_a?(Hash) && 115 | target_dependency.keys.first && 116 | target_dependency.keys.first == local_dependency.keys.first 117 | dp_str_equal = target_dependency.is_a?(String) && 118 | target_dependency == local_dependency.keys.first 119 | next unless dp_hash_equal || dp_str_equal 120 | 121 | target_dependencies.delete target_dependency 122 | break 123 | end 124 | end 125 | # 把本地和原始到dependencies 合并,设置dependencies 126 | local_dependencies.each do |d| 127 | UI.message "Development Pod #{d.to_yaml}" 128 | if podfile.plugins.keys.include?('cocoapods-imy-bin') 129 | podfile.set_use_source_pods(d.keys.first) if (d.is_a?(Hash) && d.keys.first) 130 | end 131 | end 132 | new_dependencies = target_dependencies + local_dependencies 133 | set_hash_value(%w[dependencies].first, new_dependencies) 134 | 135 | end 136 | end 137 | 138 | end 139 | 140 | if local_pre_install_callback 141 | @pre_install_callback = local_pre_install_callback 142 | end 143 | if local_post_install_callback 144 | @post_install_callback = local_post_install_callback 145 | end 146 | rescue Exception => e 147 | message = "Invalid `#{path}` file: #{e.message}" 148 | raise Pod::DSLError.new(message, path, e, contents) 149 | end 150 | end 151 | 152 | end 153 | end 154 | end 155 | end 156 | end 157 | end 158 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/config/config.rb: -------------------------------------------------------------------------------- 1 | require 'yaml' 2 | require 'cocoapods-imy-bin/native/podfile' 3 | require 'cocoapods-imy-bin/native/podfile_env' 4 | require 'cocoapods/generate' 5 | 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 | 'code_repo_url' => { description: '源码私有源 Git 地址', default: 'git@github.com:su350380433/example_spec_source.git' }, 16 | 'binary_repo_url' => { description: '二进制私有源 Git 地址', default: 'git@github.com:su350380433/example_spec_bin_dev.git' }, 17 | 'binary_download_url' => { description: '二进制下载地址,内部会依次传入组件名称与版本,替换字符串中的 %s ', default: 'http://localhost:8080/frameworks/%s/%s/zip' }, 18 | # 'binary_type' => { description: '二进制打包类型', default: 'framework', selection: %w[framework library] }, 19 | 'download_file_type' => { description: '下载二进制文件类型', default: 'zip', selection: %w[zip tgz tar tbz txz dmg] } 20 | } 21 | end 22 | 23 | def config_file_with_configuration_env(configuration_env) 24 | file = config_dev_file 25 | if configuration_env == "release_iphoneos" 26 | file = config_release_iphoneos_file 27 | puts "\n====== #{configuration_env} 环境 ========" 28 | elsif configuration_env == "debug_iphoneos" 29 | file = config_debug_iphoneos_file 30 | puts "\n====== #{configuration_env} 环境 ========" 31 | elsif configuration_env == "dev" 32 | puts "\n====== #{configuration_env} 环境 ========" 33 | else 34 | raise "\n===== #{configuration_env} 参数有误,请检查%w[dev debug_iphoneos release_iphoneos]====" 35 | end 36 | 37 | File.expand_path("#{Pod::Config.instance.home_dir}/#{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 54 | cut_string = "/%s/%s/zip" 55 | binary_download_url[0,binary_download_url.length - cut_string.length] 56 | end 57 | 58 | def set_configuration_env(env) 59 | @configuration_env = env 60 | end 61 | 62 | #包含arm64 armv7架构,xcodebuild 是Debug模式 63 | def config_debug_iphoneos_file 64 | "bin_debug_iphoneos.yml" 65 | end 66 | #包含arm64 armv7架构,xcodebuild 是Release模式 67 | def config_release_iphoneos_file 68 | "bin_release_iphoneos.yml" 69 | end 70 | #包含x86 arm64 armv7架构,xcodebuild 是Release模式 71 | def config_dev_file 72 | "bin_dev.yml" 73 | end 74 | 75 | def sync_config(config) 76 | File.open(config_file_with_configuration_env(config['configuration_env']), 'w+') do |f| 77 | f.write(config.to_yaml) 78 | end 79 | end 80 | 81 | def default_config 82 | @default_config ||= Hash[template_hash.map { |k, v| [k, v[:default]] }] 83 | end 84 | 85 | private 86 | 87 | def load_config 88 | if File.exist?(config_file) 89 | YAML.load_file(config_file) 90 | else 91 | default_config 92 | end 93 | end 94 | 95 | def config 96 | @config ||= begin 97 | puts "====== cocoapods-imy-bin #{CBin::VERSION} 版本 ======== \n" 98 | @config = OpenStruct.new load_config 99 | validate! 100 | @config 101 | end 102 | end 103 | 104 | def validate! 105 | template_hash.each do |k, v| 106 | selection = v[:selection] 107 | next if !selection || selection.empty? 108 | 109 | config_value = @config.send(k) 110 | next unless config_value 111 | unless selection.include?(config_value) 112 | raise Pod::Informative, "#{k} 字段的值必须限定在可选值 [ #{selection.join(' / ')} ] 内".red 113 | end 114 | end 115 | end 116 | 117 | def respond_to_missing?(method, include_private = false) 118 | config.respond_to?(method) || super 119 | end 120 | 121 | def method_missing(method, *args, &block) 122 | if config.respond_to?(method) 123 | config.send(method, *args) 124 | elsif template_hash.keys.include?(method.to_s) 125 | raise Pod::Informative, "#{method} 字段必须在配置文件 #{config_file} 中设置, 请执行 init 命令配置或手动修改配置文件".red 126 | else 127 | super 128 | end 129 | end 130 | end 131 | 132 | def self.config 133 | @config ||= Config.new 134 | end 135 | 136 | 137 | end 138 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/config/config_asker.rb: -------------------------------------------------------------------------------- 1 | require 'yaml' 2 | require 'cocoapods-imy-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 wellcome_message 41 | print <<~EOF 42 | 43 | 开始设置二进制化初始信息. 44 | 所有的信息都会保存在 #{CBin.config.config_file} 文件中. 45 | %w[bin_dev.yml bin_debug_iphoneos.yml bin_release_iphoneos.yml] 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 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-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.name}.framework" 63 | end 64 | 65 | def framework_name_version(spec) 66 | "#{spec.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 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/config/config_hot_key.rb: -------------------------------------------------------------------------------- 1 | require 'yaml' 2 | require 'cocoapods-imy-bin/native/podfile' 3 | require 'cocoapods-imy-bin/native/podfile_env' 4 | require 'cocoapods/generate' 5 | 6 | module CBin 7 | class Config_Hot_Key 8 | 9 | def config_file 10 | config_file_with_hot_key_index(hot_key_index) 11 | end 12 | 13 | def template_hash 14 | { 15 | 'hot_key_index' => { description: '快捷键', default: '1', selection: %w[1 2 3...] }, 16 | 'hot_key_dir' => { description: '快捷键执行目录', default: '' }, 17 | 'hot_key_cmd' => { description: '快捷键执行命令', default: 'pod bin update --no-repo-update' } 18 | } 19 | end 20 | 21 | def config_file_with_hot_key_index(hot_key_index) 22 | file = config_file_whith_hot_key_index(hot_key_index) 23 | raise "\n===== #{hot_key_index} 参数有误,请检查%w[1 2 3...]===" unless (hot_key_index.to_i).is_a?(Integer) 24 | File.expand_path("#{Pod::Config.instance.home_dir}/#{file}") 25 | end 26 | 27 | def hot_key_index 28 | @hot_key_index = 1 if @hot_key_index.is_a?(NilClass) 29 | @hot_key_index 30 | end 31 | 32 | def set_hot_key_index(hot_key_index) 33 | @hot_key_index = hot_key_index 34 | end 35 | 36 | def config_file_whith_hot_key_index(hot_key_index) 37 | "hot_key_#{hot_key_index}.yml" 38 | end 39 | 40 | def sync_config(config) 41 | File.open(config_file_with_hot_key_index(config['hot_key_index']), 'w+') do |f| 42 | f.write(config.to_yaml) 43 | end 44 | end 45 | 46 | def default_config 47 | @default_config ||= Hash[template_hash.map { |k, v| [k, v[:default]] }] 48 | end 49 | 50 | private 51 | 52 | def load_config 53 | file = config_file 54 | if (!file.nil?) && File.exist?(config_file) 55 | YAML.load_file(config_file) 56 | else 57 | default_config 58 | end 59 | end 60 | 61 | def config 62 | @config ||= begin 63 | puts "====== cocoapods-imy-bin #{CBin::VERSION} 版本 ======== \n" 64 | @config = OpenStruct.new load_config 65 | validate! 66 | @config 67 | end 68 | end 69 | 70 | def validate! 71 | template_hash.each do |k, v| 72 | selection = v[:selection] 73 | next if !selection || selection.empty? 74 | 75 | config_value = @config.send(k) 76 | next unless config_value 77 | unless selection.include?(config_value) 78 | raise Pod::Informative, "#{k} 字段的值必须限定在可选值 [ #{selection.join(' / ')} ] 内".red 79 | end 80 | end 81 | end 82 | 83 | def respond_to_missing?(method, include_private = false) 84 | config.respond_to?(method) || super 85 | end 86 | 87 | def method_missing(method, *args, &block) 88 | if config.respond_to?(method) 89 | config.send(method, *args) 90 | elsif template_hash.keys.include?(method.to_s) 91 | raise Pod::Informative, "#{method} 字段必须在配置文件 #{config_file} 中设置, 请执行 init 命令配置或手动修改配置文件".red 92 | else 93 | super 94 | end 95 | end 96 | end 97 | 98 | def self.config_hot_key 99 | @config_hot_key ||= Config_Hot_Key.new 100 | end 101 | 102 | end 103 | 104 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/config/config_hot_key_asker.rb: -------------------------------------------------------------------------------- 1 | require 'yaml' 2 | require 'cocoapods-imy-bin/config/config_hot_key' 3 | 4 | module CBin 5 | class Config_Hot_Key 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 wellcome_message 41 | print <<~EOF 42 | 43 | 开始设置快捷键 pod bin imy. 44 | 所有的信息都会保存在 #{CBin.config_hot_key.config_file} 文件中. 45 | %w[hot_key.yaml] 46 | 你可以在对应目录下手动添加编辑该文件. 文件包含的配置信息样式如下: 47 | 48 | #{CBin.config_hot_key.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 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/gem_version.rb: -------------------------------------------------------------------------------- 1 | 2 | module CBin 3 | VERSION = '0.3.1.2' 4 | end 5 | 6 | module Pod 7 | def self.match_version?(*version) 8 | Gem::Dependency.new('', *version).match?('', Pod::VERSION) 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/helpers.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods-imy-bin/helpers/sources_helper' 2 | require 'cocoapods-imy-bin/helpers/spec_creator' 3 | require 'cocoapods-imy-bin/helpers/spec_files_helper' 4 | require 'cocoapods-imy-bin/helpers/spec_source_creator' 5 | require 'cocoapods-imy-bin/helpers/build_utils' 6 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/helpers/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MeetYouDevs/cocoapods-imy-bin/36b201004ea3610e3278429c48d3cf49b4b650d2/cocoapods-imy-bin/lib/cocoapods-imy-bin/helpers/Info.plist -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/helpers/build_helper.rb: -------------------------------------------------------------------------------- 1 | # copy from https://github.com/CocoaPods/cocoapods-packager 2 | 3 | require 'cocoapods-imy-bin/native/podfile' 4 | require 'cocoapods/command/gen' 5 | require 'cocoapods/generate' 6 | require 'cocoapods-imy-bin/helpers/framework_builder' 7 | require 'cocoapods-imy-bin/helpers/library_builder' 8 | require 'cocoapods-imy-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 | @spec = spec 25 | @platform = platform 26 | @build_model = build_model 27 | @rootSpec = rootSpec 28 | @isRootSpec = rootSpec.name == spec.name 29 | @skip_archive = skip_archive 30 | @framework_output = framework_output 31 | @zip = zip 32 | 33 | @framework_path 34 | end 35 | 36 | def build 37 | UI.section("Building static framework #{@spec}") do 38 | 39 | build_static_framework 40 | unless @skip_archive 41 | unless CBin::Build::Utils.is_framework(@spec) 42 | build_static_library 43 | zip_static_library 44 | else 45 | zip_static_framework 46 | end 47 | end 48 | 49 | end 50 | 51 | end 52 | 53 | def build_static_framework 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, file_accessor, @platform, source_dir, @isRootSpec, @build_model ) 58 | @@build_defines = builder.build if @isRootSpec 59 | begin 60 | @framework_path = builder.lipo_build(@@build_defines) unless @skip_archive 61 | rescue 62 | @skip_archive = true 63 | end 64 | end 65 | end 66 | 67 | def build_static_library 68 | source_dir = zip_dir 69 | file_accessor = Sandbox::FileAccessor.new(Pathname.new('.').expand_path, @spec.consumer(@platform)) 70 | Dir.chdir(workspace_directory) do 71 | builder = CBin::Library::Builder.new(@spec, file_accessor, @platform, source_dir,@framework_path) 72 | builder.build 73 | end 74 | end 75 | 76 | def zip_static_framework 77 | Dir.chdir(File.join(workspace_directory,@framework_path.root_path)) do 78 | output_name = File.join(zip_dir, framework_name_zip) 79 | unless File.exist?(framework_name) 80 | UI.puts "没有需要压缩的 framework 文件:#{framework_name}" 81 | return 82 | end 83 | 84 | UI.puts "Compressing #{framework_name} into #{output_name}" 85 | `zip --symlinks -r #{output_name} #{framework_name}` 86 | end 87 | end 88 | 89 | def zip_static_library 90 | Dir.chdir(zip_dir) do 91 | output_library = "#{library_name}.zip" 92 | unless File.exist?(library_name) 93 | raise Informative, "没有需要压缩的 library 文件:#{library_name}" 94 | end 95 | 96 | UI.puts "Compressing #{library_name} into #{output_library}" 97 | 98 | `zip --symlinks -r #{output_library} #{library_name}` 99 | end 100 | 101 | end 102 | 103 | 104 | def clean_workspace 105 | UI.puts 'Cleaning workspace' 106 | 107 | FileUtils.rm_rf(gen_name) 108 | Dir.chdir(zip_dir) do 109 | FileUtils.rm_rf(framework_name) if @zip 110 | FileUtils.rm_rf(library_name) 111 | FileUtils.rm_rf(framework_name) unless @framework_output 112 | FileUtils.rm_rf("#{framework_name}.zip") unless @framework_output 113 | end 114 | end 115 | 116 | def framework_name 117 | CBin::Config::Builder.instance.framework_name(@spec) 118 | end 119 | 120 | def framework_name_zip 121 | CBin::Config::Builder.instance.framework_name_version(@spec) + ".zip" 122 | end 123 | 124 | def library_name 125 | CBin::Config::Builder.instance.library_name(@spec) 126 | end 127 | 128 | def workspace_directory 129 | File.expand_path("#{gen_name}/#{@rootSpec.name}") 130 | end 131 | 132 | def zip_dir 133 | CBin::Config::Builder.instance.zip_dir 134 | end 135 | 136 | def gen_name 137 | CBin::Config::Builder.instance.gen_dir 138 | end 139 | 140 | 141 | def spec_file 142 | @spec_file ||= begin 143 | if @podspec 144 | find_spec_file(@podspec) 145 | else 146 | if code_spec_files.empty? 147 | raise Informative, '当前目录下没有找到可用源码 podspec.' 148 | end 149 | 150 | spec_file = code_spec_files.first 151 | spec_file 152 | end 153 | end 154 | end 155 | 156 | end 157 | end 158 | end 159 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/helpers/build_utils.rb: -------------------------------------------------------------------------------- 1 | require 'yaml' 2 | require 'cocoapods-imy-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.spec_header_dir(spec) 18 | 19 | header_dir = "./Headers/Public/#{spec.name}" 20 | header_dir = "./Pods/Headers/Public/#{spec.name}" unless File.exist?(header_dir) 21 | 22 | unless File.exist?(header_dir) 23 | # 一些库名称中使用了中划线如AAA-BBB,public header中库名称会默认处理成下划线AAA_BBB 24 | module_name = spec.name.gsub("-", "_") 25 | header_dir = "./Pods/Headers/Public/#{module_name}" 26 | end 27 | 28 | # 暂时只支持:ios 29 | consumer = Pod::Specification::Consumer.new(spec, :ios) 30 | unless consumer.header_dir.nil? 31 | header_dir = File.join(header_dir, consumer.header_dir) 32 | end 33 | 34 | header_dir 35 | end 36 | 37 | def Utils.spec_module_dir(spec) 38 | if spec.module_name.nil? 39 | module_dir = "./Headers/Public/#{spec.name}" 40 | module_dir = "./Pods/Headers/Public/#{spec.name}" unless File.exist?(module_dir) 41 | unless File.exist?(module_dir) 42 | # 一些库名称中使用了中划线如AAA-BBB,public header中库名称会默认处理成下划线AAA_BBB 43 | module_name = spec.name.gsub("-", "_") 44 | module_dir = "./Pods/Headers/Public/#{module_name}" 45 | end 46 | else 47 | module_dir = "./Headers/Public/#{spec.module_name}" 48 | module_dir = "./Pods/Headers/Public/#{spec.module_name}" unless File.exist?(module_dir) 49 | end 50 | 51 | module_dir 52 | end 53 | 54 | def Utils.is_swift_module(spec) 55 | 56 | is_framework = false 57 | dir = File.join(CBin::Config::Builder.instance.gen_dir, CBin::Config::Builder.instance.target_name) 58 | #auto 走这里 59 | if File.exist?(dir) 60 | Dir.chdir(dir) do 61 | spec_module_dir = Utils.spec_module_dir(spec) 62 | return false unless File.exist?(spec_module_dir) 63 | is_framework = File.exist?(File.join(spec_module_dir, "#{spec.name}-umbrella.h")) 64 | end 65 | end 66 | 67 | if $ARGV[1] == "local" 68 | is_framework = File.exist?(File.join(CBin::Config::Builder.instance.xcode_build_dir, "#{spec.name}.framework")) 69 | unless is_framework 70 | is_framework = File.exist?(File.join(CBin::Config::Builder.instance.xcode_BuildProductsPath_dir, "#{spec.name}","Swift Compatibility Header")) 71 | end 72 | end 73 | 74 | is_framework 75 | end 76 | 77 | def Utils.uses_frameworks? 78 | uses_frameworks = false 79 | Pod::Config.instance.podfile.target_definitions.each do |key,value| 80 | if key != "Pods" 81 | uses_frameworks = value.uses_frameworks? 82 | if uses_frameworks 83 | break ; 84 | end 85 | end 86 | end 87 | 88 | return uses_frameworks 89 | end 90 | 91 | end 92 | 93 | end 94 | end 95 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-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 | def make_root 81 | @root_path = Pathname.new(@platform) 82 | @root_path.mkpath unless @root_path.exist? 83 | end 84 | end 85 | end 86 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/helpers/framework_builder.rb: -------------------------------------------------------------------------------- 1 | # copy from https://github.com/CocoaPods/cocoapods-packager 2 | 3 | require 'cocoapods-imy-bin/helpers/framework.rb' 4 | require 'English' 5 | require 'cocoapods-imy-bin/config/config_builder' 6 | require 'shellwords' 7 | 8 | module CBin 9 | class Framework 10 | class Builder 11 | include Pod 12 | #Debug下还待完成 13 | def initialize(spec, file_accessor, platform, source_dir, isRootSpec = true, build_model="Debug") 14 | @spec = spec 15 | @source_dir = source_dir 16 | @file_accessor = file_accessor 17 | @platform = platform 18 | @build_model = build_model 19 | @isRootSpec = isRootSpec 20 | #vendored_static_frameworks 只有 xx.framework 需要拼接为 xx.framework/xx by slj 21 | vendored_static_frameworks = file_accessor.vendored_static_frameworks.map do |framework| 22 | path = framework 23 | extn = File.extname path 24 | if extn.downcase == '.framework' 25 | path = File.join(path,File.basename(path, extn)) 26 | end 27 | path 28 | end 29 | 30 | @vendored_libraries = (vendored_static_frameworks + file_accessor.vendored_static_libraries).map(&:to_s) 31 | end 32 | 33 | def build 34 | defines = compile 35 | build_sim_libraries(defines) 36 | 37 | defines 38 | end 39 | 40 | def lipo_build(defines) 41 | UI.section("Building static Library #{@spec}") do 42 | # defines = compile 43 | 44 | # build_sim_libraries(defines) 45 | output = framework.versions_path + Pathname.new(@spec.name) 46 | 47 | build_static_library_for_ios(output) 48 | 49 | copy_headers 50 | copy_license 51 | copy_resources 52 | 53 | cp_to_source_dir 54 | end 55 | framework 56 | end 57 | 58 | private 59 | 60 | def cp_to_source_dir 61 | framework_name = "#{@spec.name}.framework" 62 | target_dir = File.join(CBin::Config::Builder.instance.zip_dir,framework_name) 63 | FileUtils.rm_rf(target_dir) if File.exist?(target_dir) 64 | 65 | zip_dir = CBin::Config::Builder.instance.zip_dir 66 | FileUtils.mkdir_p(zip_dir) unless File.exist?(zip_dir) 67 | 68 | `cp -fa #{@platform}/#{framework_name} #{target_dir}` 69 | end 70 | 71 | #模拟器,目前只支持 debug x86-64 72 | def build_sim_libraries(defines) 73 | UI.message 'Building simulator libraries' 74 | 75 | # archs = %w[i386 x86_64] 76 | archs = ios_architectures_sim 77 | archs.map do |arch| 78 | xcodebuild(defines, "-sdk iphonesimulator ARCHS=\'#{arch}\' ", "build-#{arch}",@build_model) 79 | end 80 | 81 | end 82 | 83 | 84 | def static_libs_in_sandbox(build_dir = 'build') 85 | file = Dir.glob("#{build_dir}/lib#{target_name}.a") 86 | unless file 87 | UI.warn "file no find = #{build_dir}/lib#{target_name}.a" 88 | end 89 | file 90 | end 91 | 92 | def build_static_library_for_ios(output) 93 | UI.message "Building ios libraries with archs #{ios_architectures}" 94 | static_libs = static_libs_in_sandbox('build') + static_libs_in_sandbox('build-simulator') + @vendored_libraries 95 | # if is_debug_model 96 | ios_architectures.map do |arch| 97 | static_libs += static_libs_in_sandbox("build-#{arch}") + @vendored_libraries 98 | end 99 | ios_architectures_sim do |arch| 100 | static_libs += static_libs_in_sandbox("build-#{arch}") + @vendored_libraries 101 | end 102 | # end 103 | 104 | build_path = Pathname("build") 105 | build_path.mkpath unless build_path.exist? 106 | 107 | # if is_debug_model 108 | libs = (ios_architectures + ios_architectures_sim) .map do |arch| 109 | library = "build-#{arch}/lib#{@spec.name}.a" 110 | library 111 | end 112 | # else 113 | # libs = ios_architectures.map do |arch| 114 | # library = "build/package-#{@spec.name}-#{arch}.a" 115 | # # libtool -arch_only arm64 -static -o build/package-armv64.a build/libIMYFoundation.a build-simulator/libIMYFoundation.a 116 | # # 从liBFoundation.a 文件中,提取出 arm64 架构的文件,命名为build/package-armv64.a 117 | # UI.message "libtool -arch_only #{arch} -static -o #{library} #{static_libs.join(' ')}" 118 | # `libtool -arch_only #{arch} -static -o #{library} #{static_libs.join(' ')}` 119 | # library 120 | # end 121 | # end 122 | 123 | UI.message "lipo -create -output #{output} #{libs.join(' ')}" 124 | `lipo -create -output #{output} #{libs.join(' ')}` 125 | end 126 | 127 | def ios_build_options 128 | "ARCHS=\'#{ios_architectures.join(' ')}\' OTHER_CFLAGS=\'-fembed-bitcode -Qunused-arguments\'" 129 | end 130 | 131 | def ios_architectures 132 | # >armv7 133 | # iPhone4 134 | # iPhone4S 135 | # >armv7s 去掉 136 | # iPhone5 137 | # iPhone5C 138 | # >arm64 139 | # iPhone5S(以上) 140 | # >i386 141 | # iphone5,iphone5s以下的模拟器 142 | # >x86_64 143 | # iphone6以上的模拟器 144 | archs = %w[arm64 armv7] 145 | # archs = %w[x86_64 arm64 armv7s i386] 146 | # @vendored_libraries.each do |library| 147 | # archs = `lipo -info #{library}`.split & archs 148 | # end 149 | archs 150 | end 151 | 152 | def ios_architectures_sim 153 | 154 | archs = %w[x86_64] 155 | # TODO 处理是否需要 i386 156 | archs 157 | end 158 | 159 | def compile 160 | defines = "GCC_PREPROCESSOR_DEFINITIONS='$(inherited)'" 161 | defines += ' ' 162 | defines += @spec.consumer(@platform).compiler_flags.join(' ') 163 | 164 | options = ios_build_options 165 | # if is_debug_model 166 | archs = ios_architectures 167 | # archs = %w[arm64 armv7 armv7s] 168 | archs.map do |arch| 169 | xcodebuild(defines, "ARCHS=\'#{arch}\' OTHER_CFLAGS=\'-fembed-bitcode -Qunused-arguments\'","build-#{arch}",@build_model) 170 | end 171 | # else 172 | # xcodebuild(defines,options) 173 | # end 174 | 175 | defines 176 | end 177 | 178 | def is_debug_model 179 | @build_model == "Debug" 180 | end 181 | 182 | def target_name 183 | #区分多平台,如配置了多平台,会带上平台的名字 184 | # 如libwebp-iOS 185 | if @spec.available_platforms.count > 1 186 | "#{@spec.name}-#{Platform.string_name(@spec.consumer(@platform).platform_name)}" 187 | else 188 | @spec.name 189 | end 190 | end 191 | 192 | def xcodebuild(defines = '', args = '', build_dir = 'build', build_model = 'Debug') 193 | 194 | unless File.exist?("Pods.xcodeproj") #cocoapods-generate v2.0.0 195 | command = "xcodebuild #{defines} #{args} CONFIGURATION_BUILD_DIR=#{File.join(File.expand_path("..", build_dir), File.basename(build_dir))} clean build -configuration #{build_model} -target #{target_name} -project ./Pods/Pods.xcodeproj 2>&1" 196 | else 197 | command = "xcodebuild #{defines} #{args} CONFIGURATION_BUILD_DIR=#{build_dir} clean build -configuration #{build_model} -target #{target_name} -project ./Pods.xcodeproj 2>&1" 198 | end 199 | 200 | UI.message "command = #{command}" 201 | output = `#{command}`.lines.to_a 202 | 203 | if $CHILD_STATUS.exitstatus != 0 204 | raise <<~EOF 205 | Build command failed: #{command} 206 | Output: 207 | #{output.map { |line| " #{line}" }.join} 208 | EOF 209 | 210 | Process.exit 211 | end 212 | end 213 | 214 | def copy_headers 215 | #走 podsepc中的public_headers 216 | public_headers = Array.new 217 | 218 | #by slj 如果没有头文件,去 "Headers/Public"拿 219 | # if public_headers.empty? 220 | spec_header_dir = CBin::Build::Utils.spec_header_dir(@spec) 221 | raise "copy_headers #{spec_header_dir} no exist " unless File.exist?(spec_header_dir) 222 | 223 | Dir.chdir(spec_header_dir) do 224 | headers = Dir.glob('**/*.h') 225 | headers.each do |h| 226 | public_headers << Pathname.new(File.join(Dir.pwd,h)) 227 | end 228 | end 229 | # end 230 | 231 | # UI.message "Copying public headers #{public_headers.map(&:basename).map(&:to_s)}" 232 | 233 | public_headers.each do |h| 234 | `ditto #{h} #{framework.headers_path}/#{h.basename}` 235 | end 236 | 237 | # If custom 'module_map' is specified add it to the framework distribution 238 | # otherwise check if a header exists that is equal to 'spec.name', if so 239 | # create a default 'module_map' one using it. 240 | if !@spec.module_map.nil? 241 | module_map_file = @file_accessor.module_map 242 | if Pathname(module_map_file).exist? 243 | module_map = File.read(module_map_file) 244 | end 245 | elsif public_headers.map(&:basename).map(&:to_s).include?("#{@spec.name}.h") 246 | module_map = <<-MAP 247 | framework module #{@spec.name} { 248 | umbrella header "#{@spec.name}.h" 249 | 250 | export * 251 | module * { export * } 252 | } 253 | MAP 254 | else 255 | # by ChildhoodAndy 256 | # try to read modulemap file from module dir 257 | module_map_file = File.join(CBin::Build::Utils.spec_module_dir(@spec), "#{@spec.name}.modulemap") 258 | if Pathname(module_map_file).exist? 259 | module_map = File.read(module_map_file) 260 | end 261 | end 262 | 263 | unless module_map.nil? 264 | UI.message "Writing module map #{module_map}" 265 | unless framework.module_map_path.exist? 266 | framework.module_map_path.mkpath 267 | end 268 | File.write("#{framework.module_map_path}/module.modulemap", module_map) 269 | end 270 | end 271 | 272 | def copy_license 273 | UI.message 'Copying license' 274 | license_file = @spec.license[:file] || 'LICENSE' 275 | `cp "#{license_file}" .` if Pathname(license_file).exist? 276 | end 277 | 278 | def copy_resources 279 | resource_dir = './build/*.bundle' 280 | resource_dir = './build-armv7/*.bundle' if File.exist?('./build-armv7') 281 | resource_dir = './build-arm64/*.bundle' if File.exist?('./build-arm64') 282 | 283 | bundles = Dir.glob(resource_dir) 284 | 285 | bundle_names = [@spec, *@spec.recursive_subspecs].flat_map do |spec| 286 | consumer = spec.consumer(@platform) 287 | consumer.resource_bundles.keys + 288 | consumer.resources.map do |r| 289 | File.basename(r, '.bundle') if File.extname(r) == 'bundle' 290 | end 291 | end.compact.uniq 292 | 293 | bundles.select! do |bundle| 294 | bundle_name = File.basename(bundle, '.bundle') 295 | bundle_names.include?(bundle_name) 296 | end 297 | 298 | if bundles.count > 0 299 | UI.message "Copying bundle files #{bundles}" 300 | bundle_files = bundles.join(' ') 301 | `cp -rp #{bundle_files} #{framework.resources_path} 2>&1` 302 | end 303 | 304 | real_source_dir = @source_dir 305 | unless @isRootSpec 306 | spec_source_dir = File.join(Dir.pwd,"#{@spec.name}") 307 | unless File.exist?(spec_source_dir) 308 | spec_source_dir = File.join(Dir.pwd,"Pods/#{@spec.name}") 309 | end 310 | raise "copy_resources #{spec_source_dir} no exist " unless File.exist?(spec_source_dir) 311 | 312 | real_source_dir = spec_source_dir 313 | end 314 | resources = [@spec, *@spec.recursive_subspecs].flat_map do |spec| 315 | expand_paths(real_source_dir, spec.consumer(@platform).resources) 316 | end.compact.uniq 317 | 318 | if resources.count == 0 && bundles.count == 0 319 | framework.delete_resources 320 | return 321 | end 322 | 323 | if resources.count > 0 324 | #把 路径转义。 避免空格情况下拷贝失败 325 | escape_resource = [] 326 | resources.each do |source| 327 | escape_resource << Shellwords.join(source) 328 | end 329 | UI.message "Copying resources #{escape_resource}" 330 | `cp -rp #{escape_resource.join(' ')} #{framework.resources_path}` 331 | end 332 | end 333 | 334 | def expand_paths(source_dir, path_specs) 335 | path_specs.map do |path_spec| 336 | Dir.glob(File.join(source_dir, path_spec)) 337 | end 338 | end 339 | 340 | def framework 341 | @framework ||= begin 342 | framework = Framework.new(@spec.name, @platform.name.to_s) 343 | framework.make 344 | framework 345 | end 346 | end 347 | 348 | 349 | end 350 | end 351 | end 352 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-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 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/helpers/library_builder.rb: -------------------------------------------------------------------------------- 1 | 2 | # copy from https://github.com/CocoaPods/cocoapods-packager 3 | 4 | require 'cocoapods-imy-bin/helpers/framework.rb' 5 | require 'cocoapods-imy-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 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/helpers/sources_helper.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'cocoapods-imy-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 16 | sources_manager.code_source 17 | end 18 | 19 | # 优先采用对应依赖的 source 20 | # cocoapods 内部会先匹配前面符合的 specification 21 | # 只允许二进制的 specification subspec 比源码的 specification subspec 多 22 | # 23 | def valid_sources(code_dependencies = false) 24 | sources = [code_source] 25 | unless code_dependencies 26 | sources << binary_source 27 | sources.reverse! 28 | end 29 | sources 30 | end 31 | 32 | def sources_option(code_dependencies, additional_sources) 33 | (valid_sources(code_dependencies).map(&:url) + Array(additional_sources)).join(',') 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/helpers/spec_creator.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'cocoapods' 4 | require 'cocoapods-imy-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, code_spec.root.name, code_spec.version), 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", "#{code_spec.root.name}.framework/Versions/A"].map { |path| "#{path}/#{name}" } 139 | end 140 | 141 | def binary_source_files 142 | { http: format(CBin.config.binary_download_url, code_spec.root.name, code_spec.version), 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 -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/helpers/spec_files_helper.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'cocoapods-imy-bin/native/sources_manager.rb' 4 | require 'cocoapods-imy-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 | @spec_creator = CBin::Specification::Creator.new(code_spec, template_spec) 68 | @spec_creator.create 69 | @spec_creator.write_spec_file 70 | @spec_creator.filename 71 | end 72 | 73 | def clear_binary_spec_file_if_needed 74 | @spec_creator&.clear_spec_file 75 | end 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/helpers/spec_source_creator.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'cocoapods' 4 | require 'cocoapods-imy-bin/config/config' 5 | 6 | module CBin 7 | class SpecificationSource 8 | class Creator 9 | attr_reader :code_spec 10 | attr_reader :spec 11 | 12 | def initialize(code_spec, platforms = 'ios') 13 | @code_spec = code_spec 14 | @platforms = Array(platforms) 15 | validate! 16 | end 17 | 18 | def validate! 19 | raise Pod::Informative, '源码 podspec 不能为空 .' unless code_spec 20 | end 21 | 22 | def create 23 | # spec = nil 24 | if CBin::Build::Utils.is_framework(@code_spec) 25 | spec = create_framework_from_code_spec 26 | else 27 | spec = create_from_code_spec 28 | end 29 | 30 | spec 31 | end 32 | 33 | def write_spec_file(file = filename) 34 | create unless spec 35 | 36 | FileUtils.mkdir_p(CBin::Config::Builder.instance.binary_json_dir) unless File.exist?(CBin::Config::Builder.instance.binary_json_dir) 37 | FileUtils.rm_rf(file) if File.exist?(file) 38 | 39 | File.open(file, 'w+') do |f| 40 | # f.write("# MARK: converted automatically by plugin cocoapods-imy-bin @slj \r\n") 41 | f.write(spec.to_pretty_json) 42 | end 43 | 44 | @filename = file 45 | end 46 | 47 | def clear_spec_file 48 | File.delete(filename) if File.exist?(filename) 49 | end 50 | 51 | def filename 52 | @filename ||= "#{CBin::Config::Builder.instance.binary_json_dir_name}/#{spec.name}.binary.podspec.json" 53 | end 54 | 55 | private 56 | 57 | def create_from_code_spec 58 | @spec = code_spec.dup 59 | # vendored_frameworks | resources | source | source_files | public_header_files 60 | # license | resource_bundles | vendored_libraries 61 | 62 | # Project Linkin 63 | # @spec.vendored_frameworks = "#{code_spec.root.name}.framework" 64 | 65 | # Resources 66 | extnames = [] 67 | extnames << '*.bundle' if code_spec_consumer.resource_bundles.any? 68 | if code_spec_consumer.resources.any? 69 | extnames += code_spec_consumer.resources.map { |r| File.basename(r) } 70 | end 71 | if extnames.any? 72 | @spec.resources = framework_contents('Resources').flat_map { |r| extnames.map { |e| "#{r}/#{e}" } } 73 | end 74 | 75 | # Source Location 76 | @spec.source = binary_source 77 | 78 | # Source Code 79 | # @spec.source_files = framework_contents('Headers/*') 80 | # @spec.public_header_files = framework_contents('Headers/*') 81 | 82 | # Unused for binary 83 | spec_hash = @spec.to_hash 84 | # spec_hash.delete('license') 85 | spec_hash.delete('resource_bundles') 86 | spec_hash.delete('exclude_files') 87 | spec_hash.delete('preserve_paths') 88 | spec_hash.delete('vendored_frameworks') 89 | spec_hash.delete('vendored_framework') 90 | 91 | # 这里不确定 vendored_libraries 指定的时动态/静态库 92 | # 如果是静态库的话,需要移除,否则就不移除 93 | # 最好是静态库都独立成 Pod ,cocoapods-package 打静态库去 collect 目标文件时好做过滤 94 | # 这里统一只对命名后缀 .a 文件做处理 95 | # spec_hash.delete('vendored_libraries') 96 | # libraries 只能假设为动态库不做处理了,如果有例外,需要开发者自行处理 97 | spec_hash.delete('vendored_libraries') 98 | 99 | # vendored_libraries = Array(vendored_libraries).reject { |l| l.end_with?('.a') } 100 | # if vendored_libraries.any? 101 | # spec_hash['vendored_libraries'] = vendored_libraries 102 | # end 103 | 104 | # Filter platforms 105 | platforms = spec_hash['platforms'] 106 | selected_platforms = platforms.select { |k, _v| @platforms.include?(k) } 107 | spec_hash['platforms'] = selected_platforms.empty? ? platforms : selected_platforms 108 | 109 | @spec = Pod::Specification.from_hash(spec_hash) 110 | 111 | #把命令 prepare_command 移除掉,如ReactiveCocoa会执行修改重命令的脚步 112 | @spec.prepare_command = "" if @spec.prepare_command 113 | @spec.version = code_spec.version 114 | @spec.source = binary_source 115 | @spec.source_files = binary_source_files 116 | @spec.public_header_files = binary_public_header_files 117 | @spec.vendored_libraries = binary_vendored_libraries 118 | @spec.resources = binary_resources if @spec.attributes_hash.keys.include?("resources") 119 | @spec.description = <<-EOF 120 | 「 converted automatically by plugin cocoapods-imy-bin @厦门美柚 - slj 」 121 | #{@spec.description} 122 | EOF 123 | 124 | # 处理一下 subspecs ,使用相同的配置 125 | if @spec.subspecs 126 | @spec.subspecs.each do |subspec| 127 | subspec.source_files = binary_source_files 128 | subspec.public_header_files = binary_public_header_files 129 | subspec.vendored_libraries = binary_vendored_libraries 130 | subspec.resources = binary_resources if @spec.attributes_hash.keys.include?("resources") 131 | end 132 | end 133 | @spec 134 | end 135 | 136 | def create_framework_from_code_spec 137 | @spec = code_spec.dup 138 | # vendored_frameworks | resources | source | source_files | public_header_files 139 | # license | resource_bundles | vendored_libraries 140 | 141 | # Project Linkin 142 | @spec.vendored_frameworks = "#{code_spec.root.name}.framework" 143 | 144 | # Resources 145 | extnames = [] 146 | extnames << '*.bundle' if code_spec_consumer.resource_bundles.any? 147 | if code_spec_consumer.resources.any? 148 | extnames += code_spec_consumer.resources.map { |r| File.basename(r) } 149 | end 150 | if extnames.any? 151 | @spec.resources = framework_contents('Resources').flat_map { |r| extnames.map { |e| "#{r}/#{e}" } } 152 | end 153 | 154 | # Source Location 155 | @spec.source = binary_source 156 | 157 | # Source Code 158 | # @spec.source_files = framework_contents('Headers/*') 159 | # @spec.public_header_files = framework_contents('Headers/*') 160 | 161 | # Unused for binary 162 | spec_hash = @spec.to_hash 163 | # spec_hash.delete('license') 164 | spec_hash.delete('resource_bundles') 165 | spec_hash.delete('exclude_files') 166 | spec_hash.delete('preserve_paths') 167 | # 这里不确定 vendored_libraries 指定的时动态/静态库 168 | # 如果是静态库的话,需要移除,否则就不移除 169 | # 最好是静态库都独立成 Pod ,cocoapods-package 打静态库去 collect 目标文件时好做过滤 170 | # 这里统一只对命名后缀 .a 文件做处理 171 | # spec_hash.delete('vendored_libraries') 172 | # libraries 只能假设为动态库不做处理了,如果有例外,需要开发者自行处理 173 | vendored_libraries = spec_hash.delete('vendored_libraries') 174 | vendored_libraries = Array(vendored_libraries).reject { |l| l.end_with?('.a') } 175 | if vendored_libraries.any? 176 | spec_hash['vendored_libraries'] = vendored_libraries 177 | end 178 | 179 | # Filter platforms 180 | platforms = spec_hash['platforms'] 181 | selected_platforms = platforms.select { |k, _v| @platforms.include?(k) } 182 | spec_hash['platforms'] = selected_platforms.empty? ? platforms : selected_platforms 183 | 184 | @spec = Pod::Specification.from_hash(spec_hash) 185 | @spec.description = <<-EOF 186 | 「 converted automatically by plugin cocoapods-imy-bin @厦门美柚 - slj 」 187 | #{@spec.description} 188 | EOF 189 | @spec 190 | end 191 | 192 | 193 | def binary_source 194 | { http: format(CBin.config.binary_download_url, code_spec.root.name, code_spec.version), type: CBin.config.download_file_type } 195 | end 196 | 197 | def code_spec_consumer(_platform = :ios) 198 | code_spec.consumer(:ios) 199 | end 200 | 201 | def framework_contents(name) 202 | ["#{code_spec.root.name}.framework", "#{code_spec.root.name}.framework/Versions/A"].map { |path| "#{path}/#{name}" } 203 | end 204 | 205 | def binary_source_files 206 | { http: format(CBin.config.binary_download_url, code_spec.root.name, code_spec.version), type: CBin.config.download_file_type } 207 | end 208 | 209 | def binary_source_files 210 | "bin_#{code_spec.name}_#{code_spec.version}/Headers/*" 211 | end 212 | 213 | def binary_public_header_files 214 | "bin_#{code_spec.name}_#{code_spec.version}/Headers/*.h" 215 | end 216 | 217 | def binary_vendored_libraries 218 | "bin_#{code_spec.name}_#{code_spec.version}/*.a" 219 | end 220 | 221 | def binary_resources 222 | "bin_#{code_spec.name}_#{code_spec.version}/Resources/*" 223 | end 224 | 225 | end 226 | end 227 | end 228 | #模板框架begin 229 | # s.source_files = "bin_#{s.name}_#{s.version}/Headers/*" 230 | # s.public_header_files = "bin_#{s.name}_#{s.version}/Headers/*.h" 231 | # s.vendored_libraries = "bin_#{s.name}_#{s.version}/*.a" 232 | #有图片资源的,要带上 233 | #s.resources = 'bin_#{s.name}_#{s.version}/Resources/*.{json,png,jpg,gif,js,xib,eot,svg,ttf,woff,db,sqlite,mp3,bundle}' 234 | #模板框架end -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/helpers/upload_helper.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | # copy from https://github.com/CocoaPods/cocoapods-packager 4 | 5 | require 'cocoapods-imy-bin/native/podfile' 6 | require 'cocoapods/command/gen' 7 | require 'cocoapods/generate' 8 | require 'cocoapods-imy-bin/helpers/framework_builder' 9 | require 'cocoapods-imy-bin/helpers/library_builder' 10 | require 'cocoapods-imy-bin/helpers/sources_helper' 11 | require 'cocoapods-imy-bin/command/bin/spec/push' 12 | 13 | module CBin 14 | class Upload 15 | class Helper 16 | include CBin::SourcesHelper 17 | 18 | def initialize(spec,code_dependencies,sources) 19 | @spec = spec 20 | @code_dependencies = code_dependencies 21 | @sources = sources 22 | end 23 | 24 | def upload 25 | Dir.chdir(CBin::Config::Builder.instance.root_dir) do 26 | # 创建binary-template.podsepc 27 | # 上传二进制文件 28 | # 上传二进制 podspec 29 | res_zip = curl_zip 30 | if res_zip 31 | filename = spec_creator 32 | push_binary_repo(filename) 33 | end 34 | res_zip 35 | end 36 | end 37 | 38 | def spec_creator 39 | spec_creator = CBin::SpecificationSource::Creator.new(@spec) 40 | spec_creator.create 41 | spec_creator.write_spec_file 42 | spec_creator.filename 43 | end 44 | 45 | #推送二进制 46 | # 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" 47 | def curl_zip 48 | zip_file = "#{CBin::Config::Builder.instance.library_file(@spec)}.zip" 49 | res = File.exist?(zip_file) 50 | unless res 51 | zip_file = CBin::Config::Builder.instance.framework_zip_file(@spec) + ".zip" 52 | res = File.exist?(zip_file) 53 | end 54 | if res 55 | command = "curl #{CBin.config.binary_upload_url} -F \"name=#{@spec.name}\" -F \"version=#{@spec.version}\" -F \"annotate=#{@spec.name}_#{@spec.version}_log\" -F \"file=@#{zip_file}\"" 56 | print < 1.4') 4 | require 'cocoapods-imy-bin/native/podfile' 5 | require 'cocoapods-imy-bin/native/installation_options' 6 | require 'cocoapods-imy-bin/native/specification' 7 | require 'cocoapods-imy-bin/native/path_source' 8 | require 'cocoapods-imy-bin/native/analyzer' 9 | require 'cocoapods-imy-bin/native/installer' 10 | require 'cocoapods-imy-bin/native/podfile_generator' 11 | require 'cocoapods-imy-bin/native/pod_source_installer' 12 | require 'cocoapods-imy-bin/native/linter' 13 | require 'cocoapods-imy-bin/native/resolver' 14 | require 'cocoapods-imy-bin/native/source' 15 | require 'cocoapods-imy-bin/native/validator' 16 | require 'cocoapods-imy-bin/native/acknowledgements' 17 | require 'cocoapods-imy-bin/native/sandbox_analyzer' 18 | require 'cocoapods-imy-bin/native/podspec_finder' 19 | require 'cocoapods-imy-bin/native/file_accessor' 20 | require 'cocoapods-imy-bin/native/pod_target_installer' 21 | require 'cocoapods-imy-bin/native/target_validator' 22 | 23 | end 24 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-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 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/native/analyzer.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'parallel' 4 | require 'cocoapods' 5 | 6 | module Pod 7 | class Installer 8 | class Analyzer 9 | # > 1.6.0 10 | # all_specs[dep.name] 为 nil 会崩溃 11 | # 主要原因是 all_specs 分析错误 12 | # 查看 source 是否正确 13 | # 14 | # def dependencies_for_specs(specs, platform, all_specs) 15 | # return [] if specs.empty? || all_specs.empty? 16 | 17 | # dependent_specs = Set.new 18 | 19 | # specs.each do |s| 20 | # s.dependencies(platform).each do |dep| 21 | # all_specs[dep.name].each do |spec| 22 | # dependent_specs << spec 23 | # end 24 | # end 25 | # end 26 | 27 | # dependent_specs - specs 28 | # end 29 | 30 | # > 1.5.3 版本 31 | # rewrite update_repositories 32 | # 33 | alias old_update_repositories update_repositories 34 | def update_repositories 35 | if installation_options.update_source_with_multi_processes 36 | # 并发更新私有源 37 | # 这里多线程会导致 pod update 额外输出 --verbose 的内容 38 | # 不知道为什么? 39 | Parallel.each(sources.uniq(&:url), in_processes: 4) do |source| 40 | if source.git? 41 | config.sources_manager.update(source.name, true) 42 | else 43 | UI.message "Skipping `#{source.name}` update because the repository is not a git source repository." 44 | end 45 | end 46 | @specs_updated = true 47 | else 48 | old_update_repositories 49 | end 50 | end 51 | 52 | 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-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 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-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, true 20 | 21 | # 是否多进程执行 update_repositories 22 | env_option :update_source_with_multi_processes, true 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/native/installer.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods/installer/project_cache/target_metadata.rb' 2 | require 'parallel' 3 | require 'cocoapods' 4 | require 'xcodeproj' 5 | require 'cocoapods-imy-bin/native/pod_source_installer' 6 | 7 | module Pod 8 | class Installer 9 | alias old_create_pod_installer create_pod_installer 10 | def create_pod_installer(pod_name) 11 | installer = old_create_pod_installer(pod_name) 12 | installer.installation_options = installation_options 13 | installer 14 | end 15 | 16 | alias old_install_pod_sources install_pod_sources 17 | def install_pod_sources 18 | if installation_options.install_with_multi_threads 19 | if Pod.match_version?('~> 1.4.0') 20 | install_pod_sources_for_version_in_1_4_0 21 | elsif Pod.match_version?('~> 1.5') 22 | install_pod_sources_for_version_above_1_5_0 23 | else 24 | old_install_pod_sources 25 | end 26 | else 27 | old_install_pod_sources 28 | end 29 | end 30 | 31 | # rewrite install_pod_sources 32 | def install_pod_sources_for_version_in_1_4_0 33 | @installed_specs = [] 34 | pods_to_install = sandbox_state.added | sandbox_state.changed 35 | title_options = { verbose_prefix: '-> '.green } 36 | Parallel.each(root_specs.sort_by(&:name), in_threads: 4) do |spec| 37 | if pods_to_install.include?(spec.name) 38 | if sandbox_state.changed.include?(spec.name) && sandbox.manifest 39 | previous = sandbox.manifest.version(spec.name) 40 | title = "Installing #{spec.name} #{spec.version} (was #{previous})" 41 | else 42 | title = "Installing #{spec}" 43 | end 44 | UI.titled_section(title.green, title_options) do 45 | install_source_of_pod(spec.name) 46 | end 47 | else 48 | UI.titled_section("Using #{spec}", title_options) do 49 | create_pod_installer(spec.name) 50 | end 51 | end 52 | end 53 | end 54 | 55 | def install_pod_sources_for_version_above_1_5_0 56 | @installed_specs = [] 57 | pods_to_install = sandbox_state.added | sandbox_state.changed 58 | title_options = { verbose_prefix: '-> '.green } 59 | # 多进程下载,多线程时 log 会显著交叉,多进程好点,但是多进程需要利用文件锁对 cache 进行保护 60 | # in_processes: 10 61 | Parallel.each(root_specs.sort_by(&:name), in_threads: 4) do |spec| 62 | if pods_to_install.include?(spec.name) 63 | if sandbox_state.changed.include?(spec.name) && sandbox.manifest 64 | current_version = spec.version 65 | previous_version = sandbox.manifest.version(spec.name) 66 | has_changed_version = current_version != previous_version 67 | current_repo = analysis_result.specs_by_source.detect do |key, values| 68 | break key if values.map(&:name).include?(spec.name) 69 | end 70 | current_repo &&= current_repo.url || current_repo.name 71 | previous_spec_repo = sandbox.manifest.spec_repo(spec.name) 72 | has_changed_repo = !previous_spec_repo.nil? && current_repo && (current_repo != previous_spec_repo) 73 | title = "Installing #{spec.name} #{spec.version}" 74 | if has_changed_version && has_changed_repo 75 | title += " (was #{previous_version} and source changed to `#{current_repo}` from `#{previous_spec_repo}`)" 76 | end 77 | if has_changed_version && !has_changed_repo 78 | title += " (was #{previous_version})" 79 | end 80 | if !has_changed_version && has_changed_repo 81 | title += " (source changed to `#{current_repo}` from `#{previous_spec_repo}`)" 82 | end 83 | else 84 | title = "Installing #{spec}" 85 | end 86 | UI.titled_section(title.green, title_options) do 87 | install_source_of_pod(spec.name) 88 | end 89 | else 90 | UI.titled_section("Using #{spec}", title_options) do 91 | create_pod_installer(spec.name) 92 | end 93 | end 94 | end 95 | end 96 | 97 | alias old_write_lockfiles write_lockfiles 98 | def write_lockfiles 99 | old_write_lockfiles 100 | if File.exist?('Podfile_local') 101 | 102 | project = Xcodeproj::Project.open(config.sandbox.project_path) 103 | #获取主group 104 | group = project.main_group 105 | group.set_source_tree('SOURCE_ROOT') 106 | #向group中添加 文件引用 107 | file_ref = group.new_reference(config.sandbox.root + '../Podfile_local') 108 | #podfile_local排序 109 | podfile_local_group = group.children.last 110 | group.children.pop 111 | group.children.unshift(podfile_local_group) 112 | #保存 113 | project.save 114 | end 115 | end 116 | end 117 | 118 | module Downloader 119 | class Cache 120 | # 多线程锁 121 | @@lock = Mutex.new 122 | 123 | # 后面如果要切到进程的话,可以在 cache root 里面新建一个文件 124 | # 利用这个文件 lock 125 | # https://stackoverflow.com/questions/23748648/using-fileflock-as-ruby-global-lock-mutex-for-processes 126 | 127 | # rmtree 在多进程情况下可能 Directory not empty @ dir_s_rmdir 错误 128 | # old_ensure_matching_version 会移除不是同一个 CocoaPods 版本的组件缓存 129 | alias old_ensure_matching_version ensure_matching_version 130 | def ensure_matching_version 131 | @@lock.synchronize { old_ensure_matching_version } 132 | end 133 | end 134 | end 135 | end 136 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/native/linter.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'cocoapods-imy-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 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/native/path_source.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'cocoapods-imy-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 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/native/pod_source_installer.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'cocoapods-imy-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 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-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 | UI.warn("========= swift add_swift_library_compatibility_header_phase") 26 | if $ARGV[1] == "auto" 27 | UI.warn("========= auto swift add_swift_library_compatibility_header_phase") 28 | 29 | if custom_module_map 30 | raise Informative, 'Using Swift static libraries with custom module maps is currently not supported. ' \ 31 | "Please build `#{target.label}` as a framework or remove the custom module map." 32 | end 33 | 34 | build_phase = native_target.new_shell_script_build_phase('Copy generated compatibility header') 35 | 36 | relative_module_map_path = target.module_map_path.relative_path_from(target.sandbox.root) 37 | relative_umbrella_header_path = target.umbrella_header_path.relative_path_from(target.sandbox.root) 38 | 39 | build_phase.shell_script = <<-SH.strip_heredoc 40 | COMPATIBILITY_HEADER_PATH="${BUILT_PRODUCTS_DIR}/Swift Compatibility Header/${PRODUCT_MODULE_NAME}-Swift.h" 41 | MODULE_MAP_PATH="${BUILT_PRODUCTS_DIR}/${PRODUCT_MODULE_NAME}.modulemap" 42 | 43 | ditto "${DERIVED_SOURCES_DIR}/${PRODUCT_MODULE_NAME}-Swift.h" "${COMPATIBILITY_HEADER_PATH}" 44 | ditto "${PODS_ROOT}/#{relative_module_map_path}" "${MODULE_MAP_PATH}" 45 | ditto "${PODS_ROOT}/#{relative_umbrella_header_path}" "${BUILT_PRODUCTS_DIR}" 46 | 47 | COPY_PATH="${PODS_CONFIGURATION_BUILD_DIR}/${PRODUCT_MODULE_NAME}" 48 | UMBRELLA_PATH="${BUILT_PRODUCTS_DIR}/${PRODUCT_MODULE_NAME}-umbrella.h" 49 | SWIFTMODULE_PATH="${BUILT_PRODUCTS_DIR}/${PRODUCT_MODULE_NAME}.swiftmodule" 50 | 51 | ditto "${MODULE_MAP_PATH}" "${PODS_CONFIGURATION_BUILD_DIR}/${PRODUCT_MODULE_NAME}/${PRODUCT_MODULE_NAME}.modulemap" 52 | ditto "${COMPATIBILITY_HEADER_PATH}" "${COPY_PATH}/Swift Compatibility Header/${PRODUCT_MODULE_NAME}-Swift.h" 53 | ditto "${COMPATIBILITY_HEADER_PATH}" "${COPY_PATH}" 54 | ditto "${UMBRELLA_PATH}" "${COPY_PATH}" 55 | ditto "${SWIFTMODULE_PATH}" "${COPY_PATH}/${PRODUCT_MODULE_NAME}.swiftmodule" 56 | ditto "${SWIFTMODULE_PATH}" "${COPY_PATH}/${PRODUCT_MODULE_NAME}.swiftmodule" 57 | 58 | if [ ${PRODUCT_MODULE_NAME} != ${PRODUCT_NAME} ] ; then 59 | ditto "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}-umbrella.h" "${COPY_PATH}" 60 | ditto "${COPY_PATH}" "${PODS_CONFIGURATION_BUILD_DIR}/${PRODUCT_NAME}" 61 | fi 62 | 63 | MODULE_MAP_SEARCH_PATH = "${PODS_CONFIGURATION_BUILD_DIR}/${PRODUCT_MODULE_NAME}/${PRODUCT_MODULE_NAME}.modulemap" 64 | 65 | if [${MODULE_MAP_PATH} != ${MODULE_MAP_SEARCH_PATH}] ; then 66 | 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}" 67 | fi 68 | 69 | printf "\\n\\nmodule ${PRODUCT_MODULE_NAME}.Swift {\\n header \\"${COMPATIBILITY_HEADER_PATH}\\"\\n requires objc\\n}\\n" >> "${MODULE_MAP_PATH}" 70 | 71 | SH 72 | build_phase.input_paths = %W( 73 | ${DERIVED_SOURCES_DIR}/${PRODUCT_MODULE_NAME}-Swift.h 74 | ${PODS_ROOT}/#{relative_module_map_path} 75 | ${PODS_ROOT}/#{relative_umbrella_header_path} 76 | ) 77 | build_phase.output_paths = %W( 78 | ${BUILT_PRODUCTS_DIR}/${PRODUCT_MODULE_NAME}.modulemap 79 | ${BUILT_PRODUCTS_DIR}/#{relative_umbrella_header_path.basename} 80 | ${BUILT_PRODUCTS_DIR}/Swift\ Compatibility\ Header/${PRODUCT_MODULE_NAME}-Swift.h 81 | ) 82 | else 83 | UI.warn("========= null swift add_swift_library_compatibility_header_phase") 84 | old_add_swift_library_compatibility_header_phase(native_target) 85 | end 86 | 87 | end 88 | 89 | #-----------------------------------------------------------------------# 90 | end 91 | end 92 | end 93 | end 94 | end 95 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/native/podfile.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'cocoapods' 4 | require 'cocoapods-imy-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 use_binaries_with_spec_selector!(&block) 20 | raise Informative, '必须提供选择需要二进制组件的 block !' unless block_given? 21 | 22 | set_internal_hash_value(USE_BINARIES_SELECTOR, block) 23 | end 24 | 25 | def set_use_source_pods(pods) 26 | hash_pods_use_source = get_internal_hash_value(USE_SOURCE_PODS) || [] 27 | hash_pods_use_source += Array(pods) 28 | set_internal_hash_value(USE_SOURCE_PODS, hash_pods_use_source) 29 | end 30 | 31 | # 0 dev 32 | # 1 debug_iphoneos 33 | # 2 release_iphoneos 34 | # 需要在podfile_env 先定义 CONFIGURATION_ENV 35 | def set_configuration_env(env = "dev") 36 | set_internal_hash_value(CONFIGURATION_ENV, env) 37 | end 38 | end 39 | 40 | alias old_plugins plugins 41 | def plugins 42 | if ENV[USE_PLUGINS] 43 | env_plugins = ENV[USE_PLUGINS].split(',').each_with_object({}) do |name, result| 44 | result[name] = {} 45 | end 46 | env_plugins.merge!(old_plugins) 47 | else 48 | old_plugins 49 | end 50 | end 51 | 52 | def use_binaries_selector 53 | get_internal_hash_value(USE_BINARIES_SELECTOR, nil) 54 | end 55 | 56 | def allow_prerelease? 57 | get_internal_hash_value(ALLOW_PRERELEASE, false) || ENV[ALLOW_PRERELEASE] == 'true' 58 | end 59 | 60 | def use_binaries? 61 | get_internal_hash_value(USE_BINARIES, false) || ENV[USE_BINARIES] == 'true' 62 | end 63 | 64 | def use_source_pods 65 | get_internal_hash_value(USE_SOURCE_PODS, []) + String(ENV[USE_SOURCE_PODS]).split('|').uniq 66 | end 67 | 68 | def configuration_env 69 | get_internal_hash_value(CONFIGURATION_ENV, "dev") || ENV[CONFIGURATION_ENV] == "dev" 70 | end 71 | 72 | private 73 | 74 | def valid_bin_plugin 75 | unless plugins.keys.include?('cocoapods-imy-bin') 76 | raise Pod::Informative, 'You should add `plugin \'cocoapods-imy-bin\'` before using its DSL' 77 | end 78 | end 79 | 80 | # set_hash_value 有 key 限制 81 | def set_internal_hash_value(key, value) 82 | valid_bin_plugin 83 | 84 | internal_hash[key] = value 85 | end 86 | 87 | def get_internal_hash_value(key, default = nil) 88 | internal_hash.fetch(key, default) 89 | end 90 | end 91 | end 92 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-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 | 12 | module ENVExecutor 13 | def execute_with_bin_plugin(&block) 14 | execute_with_key(USE_PLUGINS, -> { 'cocoapods-imy-bin' }, &block) 15 | end 16 | 17 | def execute_with_allow_prerelease(allow_prerelease, &block) 18 | execute_with_key(ALLOW_PRERELEASE, -> { allow_prerelease ? 'true' : 'false' }, &block) 19 | end 20 | 21 | def execute_with_use_binaries(use_binaries, &block) 22 | execute_with_key(USE_BINARIES, -> { use_binaries ? 'true' : 'false' }, &block) 23 | end 24 | 25 | def execute_with_key(key, value_returner) 26 | origin_value = ENV[key] 27 | ENV[key] = value_returner.call 28 | 29 | yield if block_given? 30 | 31 | ENV[key] = origin_value 32 | end 33 | end 34 | 35 | extend ENVExecutor 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/native/podfile_generator.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'parallel' 4 | require 'cocoapods' 5 | require 'cocoapods-imy-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 | 40 | if (supported_swift_versions = generator.supported_swift_versions) 41 | supports_swift_versions(supported_swift_versions) 42 | end 43 | 44 | # Explicitly set sources 45 | generator.configuration.sources.each do |source_url| 46 | source(source_url) 47 | end 48 | 49 | self.defined_in_file = dir.join('CocoaPods.podfile.yaml') 50 | 51 | test_specs = spec.recursive_subspecs.select(&:test_specification?) 52 | app_specs = if spec.respond_to?(:app_specification?) 53 | spec.recursive_subspecs.select(&:app_specification?) 54 | else 55 | [] 56 | end 57 | 58 | # Stick all of the transitive dependencies in an abstract target. 59 | # This allows us to force CocoaPods to use the versions / sources / external sources 60 | # that we want. 61 | # By using an abstract target, 62 | 63 | # 会导致多个dependencies出现, 注释by slj 64 | # abstract_target 'Transitive Dependencies' do 65 | # pods_for_transitive_dependencies = [spec.name] 66 | # .concat(test_specs.map(&:name)) 67 | # .concat(test_specs.flat_map { |ts| ts.dependencies.flat_map(&:name) }) 68 | # .concat(app_specs.map(&:name)) 69 | # .concat(app_specs.flat_map { |as| as.dependencies.flat_map(&:name) }) 70 | # 71 | # dependencies = generator 72 | # .transitive_dependencies_by_pod 73 | # .values_at(*pods_for_transitive_dependencies) 74 | # .compact 75 | # .flatten(1) 76 | # .uniq 77 | # .sort_by(&:name) 78 | # .reject { |d| d.root_name == spec.root.name } 79 | # 80 | # dependencies.each do |dependency| 81 | # pod_args = generator.pod_args_for_dependency(self, dependency) 82 | # pod(*pod_args) 83 | # end 84 | # end 85 | 86 | # Add platform-specific concrete targets that inherit the 87 | # `pod` declaration for the local pod. 88 | spec_platform_names = spec.available_platforms.map(&:string_name).flatten.each.reject do |platform_name| 89 | !generator.configuration.platforms.nil? && !generator.configuration.platforms.include?(platform_name.downcase) 90 | end 91 | 92 | spec_platform_names.sort.each do |platform_name| 93 | target "App-#{platform_name}" do 94 | current_target_definition.swift_version = generator.swift_version if generator.swift_version 95 | end 96 | end 97 | 98 | # this block has to come _before_ inhibit_all_warnings! / use_modular_headers!, 99 | # and the local `pod` declaration 100 | # 会导致多个dependencies出现, 注释by slj 101 | 102 | 103 | inhibit_all_warnings! if generator.inhibit_all_warnings? 104 | use_modular_headers! if generator.use_modular_headers? 105 | 106 | # This is the pod declaration for the local pod, 107 | # it will be inherited by the concrete target definitions below 108 | 109 | pod_options = generator.dependency_compilation_kwargs(spec.name) 110 | pod_options[:path] = spec.defined_in_file.relative_path_from(dir).to_s 111 | # generator.configuration.podfile.dependencies[0].external_source 112 | 113 | 114 | { testspecs: test_specs, appspecs: app_specs }.each do |key, specs| 115 | pod_options[key] = specs.map { |s| s.name.sub(%r{^#{Regexp.escape spec.root.name}/}, '') }.sort unless specs.empty? 116 | end 117 | 118 | pod spec.name, **pod_options 119 | 120 | if Pod::Config.instance.podfile 121 | target_definitions['Pods'].instance_exec do 122 | target_definition = nil 123 | Pod::Config.instance.podfile.target_definition_list.each do |target| 124 | if target.label == "Pods-#{spec.name}" 125 | target_definition = target 126 | break 127 | end 128 | end 129 | # setting modular_headers_for 130 | if(target_definition && target_definition.use_modular_headers_hash.values.any?) 131 | target_definition.use_modular_headers_hash.values.each do |f| 132 | f.each { | pod_name| self.set_use_modular_headers_for_pod(pod_name, true) } 133 | end 134 | end 135 | 136 | 137 | if target_definition 138 | value = target_definition.to_hash['dependencies'] 139 | next if value.blank? 140 | #删除 本地库中的 spec.name,因为本地的./spec.name地址是错的 141 | value.each do |f| 142 | if f.is_a?(Hash) && f.keys.first == spec.name 143 | value.delete f 144 | break 145 | end 146 | end 147 | old_value = self.to_hash['dependencies'].first 148 | value << old_value unless (old_value == nil || value.include?(old_value)) 149 | 150 | set_hash_value(%w(dependencies).first, value) 151 | 152 | value = target_definition.to_hash['configuration_pod_whitelist'] 153 | next if value.blank? 154 | set_hash_value(%w(configuration_pod_whitelist).first, value) 155 | 156 | 157 | end 158 | 159 | 160 | end 161 | 162 | end 163 | 164 | # if generator.configuration && generator.configuration.podfile 165 | # #变量本地podfile下的dependencies 写入新的验证文件中,指定依赖源 166 | # generator.configuration.podfile.dependencies.each { |dependencies| 167 | # #如果不存在dependencies.external_source,就不变量 168 | # next unless dependencies.external_source 169 | # 170 | # dependencies.external_source.each { |key_d, value| 171 | # pod_options = generator.dependency_compilation_kwargs(dependencies.name) 172 | # pod_options[key_d] = value.to_s 173 | # { testspecs: test_specs, appspecs: app_specs }.each do |key, specs| 174 | # pod_options[key] = specs.map { |s| s.name.sub(%r{^#{Regexp.escape spec.root.name}/}, '') }.sort unless specs.empty? 175 | # end 176 | # # 过滤 dependencies.name == spec.name 177 | # pod(dependencies.name, **pod_options) unless dependencies.name == spec.name 178 | # } 179 | # } 180 | # end 181 | 182 | 183 | # Implement local-sources option to set up dependencies to podspecs in the local filesystem. 184 | next if generator.configuration.local_sources.empty? 185 | generator.transitive_local_dependencies(spec, generator.configuration.local_sources).each do |dependency, podspec_file| 186 | pod_options = generator.dependency_compilation_kwargs(dependency.name) 187 | pod_options[:path] = if podspec_file[0] == '/' # absolute path 188 | podspec_file 189 | else 190 | '../../' + podspec_file 191 | end 192 | pod dependency.name, **pod_options 193 | end 194 | end 195 | end 196 | end 197 | end 198 | end 199 | 200 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/native/podspec_finder.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'cocoapods-imy-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 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/native/resolver.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'parallel' 4 | require 'cocoapods' 5 | require 'cocoapods-imy-bin/native/podfile' 6 | require 'cocoapods-imy-bin/native/sources_manager' 7 | require 'cocoapods-imy-bin/native/installation_options' 8 | require 'cocoapods-imy-bin/gem_version' 9 | require 'cocoapods-imy-bin/command/bin/archive' 10 | 11 | module Pod 12 | class Resolver 13 | if Pod.match_version?('~> 1.6') 14 | # 其实不用到 resolver_specs_by_target 再改 spec 15 | # 在这个方法里面,通过修改 dependency 的 source 应该也可以 16 | # 就是有一点,如果改了之后,对应的 source 没有符合 dependency 的版本 17 | # 分析依赖阶段就会报错了,没法像 resolver_specs_by_target 一样 18 | # 没有对应的二进制版本时还可以转到源码源码 19 | # 20 | def aggregate_for_dependency(dependency) 21 | sources_manager = Config.instance.sources_manager 22 | if dependency&.podspec_repo 23 | sources_manager.aggregate_for_dependency(dependency) 24 | # 采用 lock 中的 source ,会导致插件对 source 的先后调整失效 25 | # elsif (locked_vertex = @locked_dependencies.vertex_named(dependency.name)) && (locked_dependency = locked_vertex.payload) && locked_dependency.podspec_repo 26 | # sources_manager.aggregate_for_dependency(locked_dependency) 27 | else 28 | @aggregate ||= Source::Aggregate.new(sources) 29 | end 30 | end 31 | end 32 | 33 | if Pod.match_version?('~> 1.4') 34 | def specifications_for_dependency(dependency, additional_requirements_frozen = []) 35 | additional_requirements = additional_requirements_frozen.dup.compact 36 | requirement = Requirement.new(dependency.requirement.as_list + additional_requirements.flat_map(&:as_list)) 37 | if podfile.allow_prerelease? && !requirement.prerelease? 38 | requirement = Requirement.new(dependency.requirement.as_list.map { |r| r + '.a' } + additional_requirements.flat_map(&:as_list)) 39 | end 40 | 41 | options = if Pod.match_version?('~> 1.7') 42 | podfile.installation_options 43 | else 44 | installation_options 45 | end 46 | 47 | if Pod.match_version?('~> 1.8') 48 | specifications = find_cached_set(dependency) 49 | .all_specifications(options.warn_for_multiple_pod_sources, requirement) 50 | else 51 | specifications = find_cached_set(dependency) 52 | .all_specifications(options.warn_for_multiple_pod_sources) 53 | .select { |s| requirement.satisfied_by? s.version } 54 | end 55 | 56 | specifications 57 | .map { |s| s.subspec_by_name(dependency.name, false, true) } 58 | .compact 59 | end 60 | end 61 | 62 | if Pod.match_version?('~> 1.6') 63 | alias old_valid_possibility_version_for_root_name? valid_possibility_version_for_root_name? 64 | 65 | def valid_possibility_version_for_root_name?(requirement, activated, spec) 66 | return true if podfile.allow_prerelease? 67 | 68 | old_valid_possibility_version_for_root_name?(requirement, activated, spec) 69 | end 70 | elsif Pod.match_version?('~> 1.4') 71 | def requirement_satisfied_by?(requirement, activated, spec) 72 | version = spec.version 73 | return false unless requirement.requirement.satisfied_by?(version) 74 | 75 | shared_possibility_versions, prerelease_requirement = possibility_versions_for_root_name(requirement, activated) 76 | if !shared_possibility_versions.empty? && !shared_possibility_versions.include?(version) 77 | return false 78 | end 79 | if !podfile.allow_prerelease? && version.prerelease? && !prerelease_requirement 80 | return false 81 | end 82 | unless spec_is_platform_compatible?(activated, requirement, spec) 83 | return false 84 | end 85 | 86 | true 87 | end 88 | end 89 | 90 | # >= 1.4.0 才有 resolver_specs_by_target 以及 ResolverSpecification 91 | # >= 1.5.0 ResolverSpecification 才有 source,供 install 或者其他操作时,输入 source 变更 92 | # 93 | if Pod.match_version?('~> 1.4') 94 | old_resolver_specs_by_target = instance_method(:resolver_specs_by_target) 95 | define_method(:resolver_specs_by_target) do 96 | specs_by_target = old_resolver_specs_by_target.bind(self).call 97 | 98 | sources_manager = Config.instance.sources_manager 99 | use_source_pods = podfile.use_source_pods 100 | 101 | missing_binary_specs = [] 102 | specs_by_target.each do |target, rspecs| 103 | # use_binaries 并且 use_source_pods 不包含 本地可过滤 104 | use_binary_rspecs = if podfile.use_binaries? || podfile.use_binaries_selector 105 | rspecs.select do |rspec| 106 | ([rspec.name, rspec.root.name] & use_source_pods).empty? && 107 | (podfile.use_binaries_selector.nil? || podfile.use_binaries_selector.call(rspec.spec)) 108 | end 109 | else 110 | [] 111 | end 112 | 113 | # Parallel.map(rspecs, in_threads: 8) do |rspec| 114 | specs_by_target[target] = rspecs.map do |rspec| 115 | # 含有 subspecs 的组件暂不处理 116 | # next rspec if rspec.spec.subspec? || rspec.spec.subspecs.any? 117 | 118 | # developments 组件采用默认输入的 spec (development pods 的 source 为 nil) 119 | # 可以使 :podspec => "htts://IMYFoundation.podspec"可以走下去,by slj 120 | # unless rspec.spec.respond_to?(:spec_source) && rspec.spec.spec_source 121 | # next rspec 122 | # end 123 | 124 | # 采用二进制依赖并且不为开发组件 125 | use_binary = use_binary_rspecs.include?(rspec) 126 | source = use_binary ? sources_manager.binary_source : sources_manager.code_source 127 | 128 | spec_version = rspec.spec.version 129 | UI.message 'cocoapods-imy-bin 插件' 130 | UI.message "- 开始处理 #{rspec.spec.name} #{spec_version} 组件." 131 | 132 | begin 133 | # 从新 source 中获取 spec,在bin archive中会异常,因为找不到 134 | specification = source.specification(rspec.root.name, spec_version) 135 | UI.message "#{rspec.root.name} #{spec_version} \r\n specification =#{specification} " 136 | # 组件是 subspec 137 | if rspec.spec.subspec? 138 | specification = specification.subspec_by_name(rspec.name, false, true) 139 | end 140 | # 这里可能出现分析依赖的 source 和切换后的 source 对应 specification 的 subspec 对应不上 141 | # 造成 subspec_by_name 返回 nil,这个是正常现象 142 | next unless specification 143 | 144 | used_by_only = if Pod.match_version?('~> 1.7') 145 | rspec.used_by_non_library_targets_only 146 | else 147 | rspec.used_by_tests_only 148 | end 149 | # used_by_only = rspec.respond_to?(:used_by_tests_only) ? rspec.used_by_tests_only : rspec.used_by_non_library_targets_only 150 | # 组装新的 rspec ,替换原 rspec 151 | if use_binary 152 | rspec = if Pod.match_version?('~> 1.4.0') 153 | ResolverSpecification.new(specification, used_by_only) 154 | else 155 | ResolverSpecification.new(specification, used_by_only, source) 156 | end 157 | UI.message "组装新的 rspec ,替换原 rspec #{rspec.root.name} #{spec_version} \r\n specification =#{specification} \r\n #{rspec} " 158 | 159 | end 160 | 161 | rescue Pod::StandardError => e 162 | # 没有从新的 source 找到对应版本组件,直接返回原 rspec 163 | 164 | # missing_binary_specs << rspec.spec if use_binary 165 | missing_binary_specs << rspec.spec 166 | rspec 167 | end 168 | 169 | rspec 170 | end.compact 171 | end 172 | 173 | if missing_binary_specs.any? 174 | missing_binary_specs.uniq.each do |spec| 175 | UI.message "【#{spec.name} | #{spec.version}】组件无对应二进制版本 , 将采用源码依赖." 176 | end 177 | Pod::Command::Bin::Archive.missing_binary_specs(missing_binary_specs) 178 | 179 | #缓存没有二进制组件到spec文件,local_psec_dir 目录 180 | sources_sepc = [] 181 | des_dir = CBin::Config::Builder.instance.local_psec_dir 182 | FileUtils.rm_f(des_dir) if File.exist?des_dir 183 | Dir.mkdir des_dir unless File.exist?des_dir 184 | missing_binary_specs.uniq.each do |spec| 185 | next if spec.name.include?('/') 186 | 187 | spec_git_res = false 188 | CBin::Config::Builder.instance.ignore_git_list.each do |ignore_git| 189 | spec_git_res = spec.source[:git] && spec.source[:git].include?(ignore_git) 190 | break if spec_git_res 191 | end 192 | next if spec_git_res 193 | 194 | #获取没有制作二进制版本的spec集合 195 | sources_sepc << spec 196 | unless spec.defined_in_file.nil? 197 | FileUtils.cp("#{spec.defined_in_file}", "#{des_dir}") 198 | end 199 | end 200 | end 201 | 202 | specs_by_target 203 | end 204 | end 205 | end 206 | 207 | if Pod.match_version?('~> 1.4.0') 208 | # 1.4.0 没有 spec_source 209 | class Specification 210 | class Set 211 | class LazySpecification < BasicObject 212 | attr_reader :spec_source 213 | 214 | old_initialize = instance_method(:initialize) 215 | define_method(:initialize) do |name, version, source| 216 | old_initialize.bind(self).call(name, version, source) 217 | 218 | @spec_source = source 219 | end 220 | 221 | def respond_to?(method, include_all = false) 222 | return super unless method == :spec_source 223 | 224 | true 225 | end 226 | end 227 | end 228 | end 229 | end 230 | end 231 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-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 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-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 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/native/sources_manager.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'cocoapods' 4 | require 'cocoapods-imy-bin/config/config' 5 | 6 | module Pod 7 | class Source 8 | class Manager 9 | # 源码 source 10 | def code_source 11 | source_with_name_or_url(CBin.config.code_repo_url) 12 | end 13 | 14 | # 二进制 source 15 | def binary_source 16 | source_with_name_or_url(CBin.config.binary_repo_url) 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/native/specification.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | require 'cocoapods-imy-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 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-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 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-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 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods-imy-bin/source_provider_hook.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods-imy-bin/native/sources_manager' 2 | require 'cocoapods-imy-bin/command/bin/repo/update' 3 | require 'cocoapods/user_interface' 4 | 5 | Pod::HooksManager.register('cocoapods-imy-bin', :pre_install) do |_context, _| 6 | require 'cocoapods-imy-bin/native' 7 | 8 | # pod bin repo update 更新二进制私有源 9 | Pod::Command::Bin::Repo::Update.new(CLAide::ARGV.new([])).run 10 | 11 | # 有插件/本地库 且是dev环境下,默认进入源码白名单 过滤 archive命令 12 | if _context.podfile.plugins.keys.include?('cocoapods-imy-bin') && _context.podfile.configuration_env == 'dev' 13 | dependencies = _context.podfile.dependencies 14 | dependencies.each do |d| 15 | next unless d.respond_to?(:external_source) && 16 | d.external_source.is_a?(Hash) && 17 | !d.external_source[:path].nil? && 18 | $ARGV[1] != 'archive' 19 | _context.podfile.set_use_source_pods d.name 20 | end 21 | end 22 | 23 | # 同步 BinPodfile 文件 24 | project_root = Pod::Config.instance.project_root 25 | path = File.join(project_root.to_s, 'BinPodfile') 26 | 27 | next unless File.exist?(path) 28 | 29 | contents = File.open(path, 'r:utf-8', &:read) 30 | podfile = Pod::Config.instance.podfile 31 | podfile.instance_eval do 32 | begin 33 | eval(contents, nil, path) 34 | rescue Exception => e 35 | message = "Invalid `#{path}` file: #{e.message}" 36 | raise Pod::DSLError.new(message, path, e, contents) 37 | end 38 | end 39 | end 40 | 41 | Pod::HooksManager.register('cocoapods-imy-bin', :source_provider) do |context, _| 42 | sources_manager = Pod::Config.instance.sources_manager 43 | podfile = Pod::Config.instance.podfile 44 | 45 | if podfile 46 | # 添加源码私有源 && 二进制私有源 47 | added_sources = [sources_manager.code_source] 48 | if podfile.use_binaries? || podfile.use_binaries_selector 49 | added_sources << sources_manager.binary_source 50 | added_sources.reverse! 51 | end 52 | added_sources.each { |source| context.add_source(source) } 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/lib/cocoapods_plugin.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods-imy-bin/gem_version.rb' 2 | require 'cocoapods-imy-bin/command' 3 | require 'cocoapods-imy-bin/source_provider_hook' 4 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/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 | -------------------------------------------------------------------------------- /cocoapods-imy-bin/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 | -------------------------------------------------------------------------------- /img/Podfile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MeetYouDevs/cocoapods-imy-bin/36b201004ea3610e3278429c48d3cf49b4b650d2/img/Podfile.png -------------------------------------------------------------------------------- /img/Podfile_local.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MeetYouDevs/cocoapods-imy-bin/36b201004ea3610e3278429c48d3cf49b4b650d2/img/Podfile_local.png -------------------------------------------------------------------------------- /img/time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MeetYouDevs/cocoapods-imy-bin/36b201004ea3610e3278429c48d3cf49b4b650d2/img/time.png -------------------------------------------------------------------------------- /使用教程.md: -------------------------------------------------------------------------------- 1 | ## 一、概要 2 | 3 | 4 | cocoapods-imy-bin功能点: 5 | 6 | 1. 组件二进制化,`无入侵式`支持组件二进制化,完全自动化,无需手动操作。致力于解决Ci打包速度慢、研发编译慢等编译问题。 7 | 2. 本地配置文件 - `Podfile_local` 8 | 3. 二进制源码调试`pod bin code`,类似[美团 iOS 工程 zsource 命令背后的那些事儿](https://juejin.im/post/6847897745987125262)的效果。 9 | 4. 命令快捷键`pod bin imy`,如游戏快捷键,根据配置会在特定目录执行特定命令(如任意终端目录下,执行某个特定目录的pod update --no-repo-update命令),减少其他繁琐操作。支持任意个快捷键。 10 | 11 | cocoapods-imy-bin插件所关联的组件二进制化策略: 12 | 13 | 预先将打包成 `.a` 的组件保存到静态服务器上,并在 `install` 时,去下载组件对应的二进制版本,以减少组件编译时间,达到加快 App 打包、组件发布等操作的目的。 14 | 15 | 16 | 17 | ## 二、准备工作 18 | 19 | 20 | 21 | ### 1、安装插件 22 | 23 | ```shell 24 | sudo gem install cocoapods-imy-bin 25 | ``` 26 | 27 | 28 | 29 | ## 三、使用二进制组件 30 | 31 | 32 | ### 1、环境搭建 33 | 34 |
35 | 36 | [环境搭建详细教程](https://github.com/su350380433/cocoapods-imy-bin-demo) 37 | 38 | 使用二进制时,本插件需要提供以下资源: 39 | 40 | - 静态资源服务器( [binary-server](https://github.com/su350380433/binary-server),附详细使用教程) 41 | - 二进制私有源仓库(保存组件二进制版本 podspec) 42 | 43 | 44 | 45 | ### 2、初始化插件 46 | 47 | ``` shell 48 | xx:Demo slj$ pod bin init 49 | 50 | ====== dev 环境 ======== 51 | 52 | 开始设置二进制化初始信息. 53 | 所有的信息都会保存在 /Users/slj/.cocoapods/bin_dev.yml 文件中. 54 | %w[bin_dev.yml bin_debug_iphoneos.yml bin_release_iphoneos.yml] 55 | 你可以在对应目录下手动添加编辑该文件. 文件包含的配置信息样式如下: 56 | 57 | --- 58 | configuration_env: dev 59 | code_repo_url: git@github.com:su350380433/example_spec_source.git 60 | binary_repo_url: git@github.com:su350380433/example_spec_bin_dev.git 61 | binary_download_url: http://localhost:8080/frameworks/%s/%s/zip 62 | download_file_type: zip 63 | 64 | 65 | 编译环境 66 | 可选值:[ dev / debug_iphoneos / release_iphoneos ] 67 | 旧值:dev 68 | ``` 69 | 70 | 按提示输入`所属环境`、源码私有源、二进制私有源、二进制下载地址、下载文件类型后,插件就配置完成了。其中 `binary_download_url` 需要预留组件名称与组件版本占位符,插件内部会依次替换 `%s` 为相应组件的值。 71 | 72 | `cococapods-imy-bin` 也支持从 url 下载配置文件,方便对多台机器进行配置: 73 | 74 | ```shell 75 | ➜ ~ pod bin init --bin-url=https://github.com/su350380433/cocoapods-imy-bin-configs/raw/master/bin_dev.yml 76 | ``` 77 | 78 | 配置文件模版内容如下,根据不同团队的需求定制即可: 79 | 80 | ```shell 81 | --- 82 | configuration_env: dev 83 | code_repo_url: git@github.com:su350380433/example_spec_source.git 84 | binary_repo_url: git@github.com:su350380433/example_spec_bin_dev.git 85 | binary_download_url: http://localhost:8080/frameworks/%s/%s/zip 86 | download_file_type: zip 87 | 88 | ``` 89 | 90 | 配置时,不需要手动添加源码和二进制私有源的 repo,插件在找不到对应 repo 时会主动 clone。 91 | 92 | 记得启动 `sudo mongod`服务,静态资源服务。 93 | 94 | 95 |
96 | 97 | ## 四、制作二进制组件 98 | 99 |
100 | 101 | [视频演示](https://github.com/MeetYouDevs/cocoapods-imy-bin/tree/master/%E6%BC%94%E7%A4%BA%E8%A7%86%E9%A2%91) 102 | 103 | ### 1、制作命令 104 | 105 | 可以直接使用插件的 `pod bin auto`命令,在插件初始化配置完成后,目录下只要有包含podspec文件,根据podspec文件的version版本号会自动化执行build、组装二进制组件、制作二进制podspec、上传二进制文件、上传二进制podspec到私有源仓库。 106 | 107 | ```shell 108 | pod bin auto 109 | ``` 110 | 111 |
112 | 113 | ### 2. 二进制Podspec 114 | 115 | 会自动生成、上传,无需关心。 116 | 117 | 118 |
119 | 120 | ### 3、查看结果 121 | 122 | 二进制存储服务:[http://localhost:8080/frameworks/](http://localhost:8080/frameworks/)(默认本地8080端口) 123 | 124 | 二进制私有源参考:[https://github.com/su350380433/example_spec_bin_dev](https://github.com/su350380433/example_spec_bin_dev)(自定义) 125 | 126 | 127 |
128 | 129 | ### 4、使用二进制 130 | 131 |
132 | 133 | [视频演示](https://github.com/MeetYouDevs/cocoapods-imy-bin/tree/master/%E6%BC%94%E7%A4%BA%E8%A7%86%E9%A2%91) 134 | 135 | 在Podfile文件中,加入这两行代码,对已经制作二进制的就会生效,自动转换二进制组件依赖。 136 | 137 | ``` ruby 138 | plugin 'cocoapods-imy-bin' 139 | use_binaries! 140 | ``` 141 | 142 | 143 |
144 | 145 | ## 五、扩展功能 146 | 147 |
148 | 149 | ### 1、本地配置文件 - Podfile_local 150 | 151 | 本地组件配置文件 Podfile_local,目前已支持Podfile下的大部分功能,可以把一些本地配置的语句放到Podfile_local。 152 | 153 | 154 | 155 | 场景: 156 | 157 | 1. 不希望把本地采用的源码/二进制配置、本地库传到远程仓库。 158 | 2. 避免直接修改Podfile文件,引起更新代码时冲突、或者误提交。 159 | 160 | 如Podfile本地库的写法: 161 | 162 | ```ruby 163 | pod YYModel :path => '../' #提交的时候往往要修改回来才提交,操作繁琐 164 | ``` 165 | 166 | 用法: 167 | 168 | 在与Podfile同级目录下,新增一个`Podfile_local`文件 169 | 170 | ```ruby 171 | #target 'Seeyou' do 不同的项目注意修改下Seeyou的值 172 | #:path => '../IMYYQHome',根据实际情况自行修改,与之前在podfile写法一致 173 | 174 | 175 | plugin 'cocoapods-imy-bin' 176 | #是否启用二进制插件,想开启把下面注释去掉 177 | # use_binaries! 178 | 179 | #设置使用【源码】版本的组件。 180 | #set_use_source_pods ['YYKit','SDWebImaage'] 181 | 182 | #需要替换Podfile里面的组件才写到这里 183 | #在这里面的所写的组件库依赖,默认切换为【源码】依赖 184 | target 'Seeyou' do 185 | #本地库引用 186 | #pod 'YYModel', :path => '../YYModel' 187 | 188 | #覆盖、自定义组件 189 | #pod 'YYCache', :podspec => 'http://覆盖、自定义/' 190 | end 191 | ``` 192 | 193 | ```ruby 194 | 以前的 pod update --no-repo-update 命令加个前缀 `bin` 变成 195 | ``` 196 | 197 | ```shell 198 | pod bin update --no-repo-update 199 | ``` 200 | 201 | or 202 | 203 | ```shell 204 | pod bin install 205 | ``` 206 | 207 | 支持 pod install/update 命令参数 208 | 209 | 并将其加入 .gitignore ,再也不用担心我误提交或者冲突了,Podfile_local 中的配置选项优先级比 Podfile 高,支持和 Podfile 相同的配置语句,同时支持**pre_install** or **post_install**。 210 | 211 | 212 | 如果您不习惯Podfile_local的使用方式,可以把命令写在Podfile里面,pod时不需要加bin,依旧是 pod update/install。 213 | 214 | 215 |
216 | 217 | ### 2、二进制源码调试 218 | 219 |
220 | 221 | [视频演示](https://github.com/MeetYouDevs/cocoapods-imy-bin/tree/master/%E6%BC%94%E7%A4%BA%E8%A7%86%E9%A2%91) 222 | 223 | 在项目根目录下,输入命令: 224 | 225 | ```ruby 226 | pod bin code YYModel 227 | ``` 228 | 229 | `YYModel`为需要源码调试的组件库名称。成功之后像平时一样单步调试,控制台打印变量。让我们同时拥有使用二进制的便利和源码调试的能力。 230 | 231 | ``` shell 232 | $ pod bin code --help [11:37:50] 233 | Usage: 234 | 235 | $ pod bin code [NAME] 236 | 237 | 通过将二进制对应源码放置在临时目录中,让二进制出现断点时可以跳到对应的源码,方便调试。 在不删除二进制的情况下为某个组件添加源码调试能力,多个组件名称用空格分隔 238 | 239 | Options: 240 | 241 | --all-clean 删除所有已经下载的源码 242 | --clean 删除所有指定下载的源码 243 | --list 展示所有一级下载的源码以及其大小 244 | --source 源码路径,本地路径,会去自动链接本地源码 245 | ``` 246 | 247 | 效果与演示参考[链接1](https://juejin.im/post/5eccceb9f265da76f30e4e13#heading-48)、[视频](https://github.com/MeetYouDevs/cocoapods-imy-bin/tree/master/%E6%BC%94%E7%A4%BA%E8%A7%86%E9%A2%91) 248 | 249 | 250 |
251 | 252 | ### 3、快捷键命令 253 | 254 |
255 | 256 | 在任意的终端执行命令,都能执行特定目录下特定命令 257 | 258 | 使用命令: 259 | 260 | ```shell 261 | pod bin imy 262 | ``` 263 | 264 | or 265 | 266 | ``` shell 267 | pod bin imy 2 #2 是自定义的快捷键 268 | ``` 269 | 270 | 使用场景: 271 | 272 | 1. 在任意目录下,执行项目A的pod update --no-repo-update命令 273 | 274 | 命令快捷键配置 275 | 276 | ```shell 277 | $ pod bin inithk [11:37:58] 278 | 279 | 开始设置快捷键 pod bin imy. 280 | 所有的信息都会保存在 /Users/ci/.cocoapods/hot_key_1.yml 文件中. 281 | %w[hot_key.yaml] 282 | 你可以在对应目录下手动添加编辑该文件. 文件包含的配置信息样式如下: 283 | 284 | --- 285 | hot_key_index: '1' 286 | hot_key_dir: '/User/ci/自定义目录' 287 | hot_key_cmd: pod bin update --no-repo-update 288 | 289 | 290 | 快捷键 291 | 可选值:[ 1 / 2 / 3... ] 292 | 旧值:1 293 | ``` 294 | 295 | 296 |
297 | 298 | ## 六、 DSL参数解释 299 | 300 | 301 | 302 | 首先,开发者需要在 Podfile 中需要使用 `plugin 'cocoapods-imy-bin'` 语句引入插件 303 | 304 | ```ruby 305 | plugin 'cocoapods-imy-bin' 306 | ``` 307 | 308 | 顺带可以删除 Podfile 中的 source ,因为插件内部会自动帮你添加两个私有源。 309 | 310 | `cocoapods-bin `插件提供二进制相关的配置语句有 `use_binaries!`、`use_binaries_with_spec_selector!` 以及 `set_use_source_pods`,下面会分别介绍。 311 | 312 | ##### use_binaries! 313 | 314 | 全部组件使用二进制版本。 315 | 316 | 支持传入布尔值控制是否使用二进制版本,比如 DEBUG 包使用二进制版本,正式包使用源码版本,Podfile 关联语句可以这样写: 317 | 318 | ```ruby 319 | use_binaries! (ENV['DEBUG'].nil? || ENV['DEBUG'] == 'true') 320 | ``` 321 | 322 | ##### set_use_source_pods 323 | 324 | 设置使用源码版本的组件。 325 | 326 | 实际开发中,可能需要查看 YYModel 组件的源码,这时候可以这么设置: 327 | 328 | ```ruby 329 | set_use_source_pods ['YYModel'] 330 | ``` 331 | 332 | 如果 CocoaPods 版本为 1.5.3 ,终端会输出以下内容,表示 YYModel 的参照源从二进制私有源切换到了源码私有源: 333 | 334 | ```ruby 335 | Analyzing dependencies 336 | Fetching podspec for `A` from `../` 337 | Downloading dependencies 338 | Using A (0.1.0) 339 | Installing YYModel 1.0.4.2 (source changed to `git@git.xxxxxx.net:ios/cocoapods-spec.git` from `git@git.xxxxxx.net:ios/cocoapods-spec-binary.git`) 340 | Generating Pods project 341 | Integrating client project 342 | Sending stats 343 | Pod installation complete! There is 1 dependency from the Podfile and 2 total pods installed. 344 | ``` 345 | 346 | ##### use_binaries_with_spec_selector! 347 | 348 | 过滤出需要使用二进制版本组件。 349 | 350 | 假如开发者只需要 `YYModel` 的二进制版本,那么他可以在 Podfile 中添加以下代码: 351 | 352 | ```ruby 353 | use_binaries_with_spec_selector! do |spec| 354 | spec.name == 'YYModel' 355 | end 356 | ``` 357 | 358 | **需要注意的是,如果组件有 subspec ,使用组件名作为判断条件应如下**: 359 | 360 | ```ruby 361 | use_binaries_with_spec_selector! do |spec| 362 | spec.name.start_with? == '组件名' 363 | end 364 | ``` 365 | 366 | 如果像上个代码块一样,**直接对比组件名,则插件会忽略此组件的所有 subspec,导致资源拉取错误**,这种场景下,最好通过 `set_use_source_pods` 语句配置依赖。 367 | 368 | 一个实际应用是,三方组件采用二进制版本,团队编写的组件依旧采用源码版本。如果三方组件都在 `cocoapods-repo` 组下,就可以使用以下代码过滤出三方组件: 369 | 370 | ```ruby 371 | use_binaries_with_spec_selector! do |spec| 372 | git = spec.source && spec.source['git'] 373 | git && git.include?('cocoapods-repo') 374 | end 375 | ``` 376 | 377 | ##### 切换Dev/Debug_iPhoneos/Release_iPhoneos环境初始化设置 378 | 379 | 380 | ```shell 381 | #dev 初始化插件配置 默认dev环境 382 | pod bin init --bin-url=https://gitlab.xxx.com/cocoapods-imy-bin-config/raw/master/bin_dev.yml 383 | 384 | #Debug_iPhoneos 初始化插件配置 385 | pod bin init --bin-url=https://gitlab.xxx.com/cocoapods-imy-bin-config/raw/master/bin_debug_iphoneos.yml 386 | 387 | 388 | #release_iPhoneos 初始化插件配置 389 | pod bin init --bin-url=https://gitlab.xxx.com/cocoapods-imy-bin-config/raw/master/bin_release_iphoneos.yml 390 | ``` 391 | 392 | 使用时在podfile 或者 podfile_local指定设置 393 | 394 | ```shell 395 | #在podfile 或者 podfile_local 文件下加这句话 396 | set_configuration_env('debug_iphoneos') 397 | ``` 398 | 399 | ##### 其他设置 400 | 401 | 插件默认开启多线程下载组件资源,如果要禁用这个功能,Podfile 添加以下代码即可: 402 | 403 | ```ruby 404 | install! 'cocoapods', { install_with_multi_threads: false } 405 | ``` 406 | 407 | 408 |
409 | 410 | ## 七、感谢 411 | 412 | 413 | 414 | **[cocoapods-bin](https://github.com/tripleCC/cocoapods-bin)** 415 | 416 | [美团 iOS 工程 zsource 命令背后的那些事儿](https://tech.meituan.com/2019/08/08/the-things-behind-the-ios-project-zsource-command.html) 417 | 418 | #### 您有什么更好的想法,可以提出来,我们一起来实现,共创一个强大的工具平台,同时也欢迎给我们提PR。 419 | 420 | -------------------------------------------------------------------------------- /演示视频/二进制源码调试演示.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MeetYouDevs/cocoapods-imy-bin/36b201004ea3610e3278429c48d3cf49b4b650d2/演示视频/二进制源码调试演示.mp4 -------------------------------------------------------------------------------- /演示视频/使用二进制.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MeetYouDevs/cocoapods-imy-bin/36b201004ea3610e3278429c48d3cf49b4b650d2/演示视频/使用二进制.mp4 -------------------------------------------------------------------------------- /演示视频/制作二进制组件.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MeetYouDevs/cocoapods-imy-bin/36b201004ea3610e3278429c48d3cf49b4b650d2/演示视频/制作二进制组件.mp4 --------------------------------------------------------------------------------