├── .gitignore ├── Example ├── Gemfile ├── Gemfile.lock └── podfile.package ├── LICENSE.txt ├── README.md ├── Rakefile ├── cocoapods-framework.gemspec ├── lib ├── cocoapods-framework.rb ├── cocoapods-framework │ ├── command.rb │ ├── command │ │ ├── framework.rb │ │ └── muti_framework.rb │ ├── config.rb │ ├── frameworker.rb │ ├── gem_version.rb │ ├── muti_frameworker.rb │ ├── util.rb │ ├── util │ │ ├── cmd.rb │ │ ├── dir_util.rb │ │ ├── error_util.rb │ │ ├── git_util.rb │ │ └── pod_util.rb │ ├── xbuilder.rb │ └── xbuilder │ │ ├── xcode_xbuild.rb │ │ └── xcodeproj_helper.rb └── cocoapods_plugin.rb ├── pic └── allplatform.png ├── pkg ├── cocoapods-xcframework-0.2.1.gem ├── cocoapods-xcframework-0.2.4.gem └── cocoapods-xcframework-0.2.5.gem └── spec ├── command └── framework_spec.rb └── spec_helper.rb /.gitignore: -------------------------------------------------------------------------------- 1 | Example/* -------------------------------------------------------------------------------- /Example/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | gem "fastlane" 3 | gem "cocoapods", '1.10.0' 4 | gem 'rest-client', '2.1.0' 5 | gem 'mail', '2.7.1' 6 | gem 'json', '2.3.1' 7 | gem 'Martin', :path => '/Users/daiyichao/Desktop/work/martin/martin-ruby' 8 | gem 'ipa_analyzer', :git=>'ssh://git@g.hz.netease.com:22222/cloudmusic-ruby/ipa_analyzer.git' 9 | gem 'cocoapods-codegen', :git=>'ssh://git@g.hz.netease.com:22222/cloudmusic-ruby/cocoapods-codegen.git' 10 | gem 'cocoapods-xcframework',:path=>'/Users/daiyichao/Desktop/work/cocoapods-framework' 11 | gem 'ffi', '1.14.2' -------------------------------------------------------------------------------- /Example/Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: /Users/daiyichao/Desktop/work/cocoapods-framework 3 | specs: 4 | cocoapods-xcframework (0.1.0) 5 | cocoapods (>= 1.10.0, < 2.0) 6 | 7 | PATH 8 | remote: /Users/daiyichao/Desktop/work/martin/martin-ruby 9 | specs: 10 | Martin (3.0.0) 11 | cocoapods (= 1.10.0) 12 | cocoapods-xcframework 13 | fastlane 14 | json (~> 2.1) 15 | rest-client 16 | rqrcode 17 | rubyzip 18 | xcodeproj 19 | 20 | GIT 21 | remote: ssh://git@g.hz.netease.com:22222/cloudmusic-ruby/cocoapods-codegen.git 22 | revision: f330a2e222e1a9dcca593289ea496466d4681a79 23 | specs: 24 | cocoapods-codegen (0.1.3) 25 | cocoapods 26 | colored 27 | xcodeproj 28 | 29 | GIT 30 | remote: ssh://git@g.hz.netease.com:22222/cloudmusic-ruby/ipa_analyzer.git 31 | revision: ca854fa0763fa2ce90c283a56d381350b1ab75bf 32 | specs: 33 | ipa_analyzer (0.1.0) 34 | plist (~> 3.1, >= 3.1.0) 35 | rubyzip 36 | 37 | GEM 38 | remote: https://rubygems.org/ 39 | specs: 40 | CFPropertyList (3.0.3) 41 | activesupport (5.2.5) 42 | concurrent-ruby (~> 1.0, >= 1.0.2) 43 | i18n (>= 0.7, < 2) 44 | minitest (~> 5.1) 45 | tzinfo (~> 1.1) 46 | addressable (2.7.0) 47 | public_suffix (>= 2.0.2, < 5.0) 48 | algoliasearch (1.27.5) 49 | httpclient (~> 2.8, >= 2.8.3) 50 | json (>= 1.5.1) 51 | artifactory (3.0.15) 52 | atomos (0.1.3) 53 | aws-eventstream (1.1.1) 54 | aws-partitions (1.446.0) 55 | aws-sdk-core (3.114.0) 56 | aws-eventstream (~> 1, >= 1.0.2) 57 | aws-partitions (~> 1, >= 1.239.0) 58 | aws-sigv4 (~> 1.1) 59 | jmespath (~> 1.0) 60 | aws-sdk-kms (1.43.0) 61 | aws-sdk-core (~> 3, >= 3.112.0) 62 | aws-sigv4 (~> 1.1) 63 | aws-sdk-s3 (1.93.1) 64 | aws-sdk-core (~> 3, >= 3.112.0) 65 | aws-sdk-kms (~> 1) 66 | aws-sigv4 (~> 1.1) 67 | aws-sigv4 (1.2.3) 68 | aws-eventstream (~> 1, >= 1.0.2) 69 | babosa (1.0.4) 70 | chunky_png (1.4.0) 71 | claide (1.0.3) 72 | cocoapods (1.10.0) 73 | addressable (~> 2.6) 74 | claide (>= 1.0.2, < 2.0) 75 | cocoapods-core (= 1.10.0) 76 | cocoapods-deintegrate (>= 1.0.3, < 2.0) 77 | cocoapods-downloader (>= 1.4.0, < 2.0) 78 | cocoapods-plugins (>= 1.0.0, < 2.0) 79 | cocoapods-search (>= 1.0.0, < 2.0) 80 | cocoapods-trunk (>= 1.4.0, < 2.0) 81 | cocoapods-try (>= 1.1.0, < 2.0) 82 | colored2 (~> 3.1) 83 | escape (~> 0.0.4) 84 | fourflusher (>= 2.3.0, < 3.0) 85 | gh_inspector (~> 1.0) 86 | molinillo (~> 0.6.6) 87 | nap (~> 1.0) 88 | ruby-macho (~> 1.4) 89 | xcodeproj (>= 1.19.0, < 2.0) 90 | cocoapods-core (1.10.0) 91 | activesupport (> 5.0, < 6) 92 | addressable (~> 2.6) 93 | algoliasearch (~> 1.0) 94 | concurrent-ruby (~> 1.1) 95 | fuzzy_match (~> 2.0.4) 96 | nap (~> 1.0) 97 | netrc (~> 0.11) 98 | public_suffix 99 | typhoeus (~> 1.0) 100 | cocoapods-deintegrate (1.0.4) 101 | cocoapods-downloader (1.4.0) 102 | cocoapods-plugins (1.0.0) 103 | nap 104 | cocoapods-search (1.0.0) 105 | cocoapods-trunk (1.5.0) 106 | nap (>= 0.8, < 2.0) 107 | netrc (~> 0.11) 108 | cocoapods-try (1.2.0) 109 | colored (1.2) 110 | colored2 (3.1.2) 111 | commander-fastlane (4.4.6) 112 | highline (~> 1.7.2) 113 | concurrent-ruby (1.1.8) 114 | declarative (0.0.20) 115 | digest-crc (0.6.3) 116 | rake (>= 12.0.0, < 14.0.0) 117 | domain_name (0.5.20190701) 118 | unf (>= 0.0.5, < 1.0.0) 119 | dotenv (2.7.6) 120 | emoji_regex (3.2.2) 121 | escape (0.0.4) 122 | ethon (0.12.0) 123 | ffi (>= 1.3.0) 124 | excon (0.80.1) 125 | faraday (1.4.1) 126 | faraday-excon (~> 1.1) 127 | faraday-net_http (~> 1.0) 128 | faraday-net_http_persistent (~> 1.1) 129 | multipart-post (>= 1.2, < 3) 130 | ruby2_keywords (>= 0.0.4) 131 | faraday-cookie_jar (0.0.7) 132 | faraday (>= 0.8.0) 133 | http-cookie (~> 1.0.0) 134 | faraday-excon (1.1.0) 135 | faraday-net_http (1.0.1) 136 | faraday-net_http_persistent (1.1.0) 137 | faraday_middleware (1.0.0) 138 | faraday (~> 1.0) 139 | fastimage (2.2.3) 140 | fastlane (2.181.0) 141 | CFPropertyList (>= 2.3, < 4.0.0) 142 | addressable (>= 2.3, < 3.0.0) 143 | artifactory (~> 3.0) 144 | aws-sdk-s3 (~> 1.0) 145 | babosa (>= 1.0.3, < 2.0.0) 146 | bundler (>= 1.12.0, < 3.0.0) 147 | colored 148 | commander-fastlane (>= 4.4.6, < 5.0.0) 149 | dotenv (>= 2.1.1, < 3.0.0) 150 | emoji_regex (>= 0.1, < 4.0) 151 | excon (>= 0.71.0, < 1.0.0) 152 | faraday (~> 1.0) 153 | faraday-cookie_jar (~> 0.0.6) 154 | faraday_middleware (~> 1.0) 155 | fastimage (>= 2.1.0, < 3.0.0) 156 | gh_inspector (>= 1.1.2, < 2.0.0) 157 | google-api-client (>= 0.37.0, < 0.39.0) 158 | google-cloud-storage (>= 1.15.0, < 2.0.0) 159 | highline (>= 1.7.2, < 2.0.0) 160 | json (< 3.0.0) 161 | jwt (>= 2.1.0, < 3) 162 | mini_magick (>= 4.9.4, < 5.0.0) 163 | multipart-post (~> 2.0.0) 164 | naturally (~> 2.2) 165 | plist (>= 3.1.0, < 4.0.0) 166 | rubyzip (>= 2.0.0, < 3.0.0) 167 | security (= 0.1.3) 168 | simctl (~> 1.6.3) 169 | slack-notifier (>= 2.0.0, < 3.0.0) 170 | terminal-notifier (>= 2.0.0, < 3.0.0) 171 | terminal-table (>= 1.4.5, < 2.0.0) 172 | tty-screen (>= 0.6.3, < 1.0.0) 173 | tty-spinner (>= 0.8.0, < 1.0.0) 174 | word_wrap (~> 1.0.0) 175 | xcodeproj (>= 1.13.0, < 2.0.0) 176 | xcpretty (~> 0.3.0) 177 | xcpretty-travis-formatter (>= 0.0.3) 178 | ffi (1.14.2) 179 | fourflusher (2.3.1) 180 | fuzzy_match (2.0.4) 181 | gh_inspector (1.1.3) 182 | google-api-client (0.38.0) 183 | addressable (~> 2.5, >= 2.5.1) 184 | googleauth (~> 0.9) 185 | httpclient (>= 2.8.1, < 3.0) 186 | mini_mime (~> 1.0) 187 | representable (~> 3.0) 188 | retriable (>= 2.0, < 4.0) 189 | signet (~> 0.12) 190 | google-apis-core (0.3.0) 191 | addressable (~> 2.5, >= 2.5.1) 192 | googleauth (~> 0.14) 193 | httpclient (>= 2.8.1, < 3.0) 194 | mini_mime (~> 1.0) 195 | representable (~> 3.0) 196 | retriable (>= 2.0, < 4.0) 197 | rexml 198 | signet (~> 0.14) 199 | webrick 200 | google-apis-iamcredentials_v1 (0.3.0) 201 | google-apis-core (~> 0.1) 202 | google-apis-storage_v1 (0.3.0) 203 | google-apis-core (~> 0.1) 204 | google-cloud-core (1.6.0) 205 | google-cloud-env (~> 1.0) 206 | google-cloud-errors (~> 1.0) 207 | google-cloud-env (1.5.0) 208 | faraday (>= 0.17.3, < 2.0) 209 | google-cloud-errors (1.1.0) 210 | google-cloud-storage (1.31.0) 211 | addressable (~> 2.5) 212 | digest-crc (~> 0.4) 213 | google-apis-iamcredentials_v1 (~> 0.1) 214 | google-apis-storage_v1 (~> 0.1) 215 | google-cloud-core (~> 1.2) 216 | googleauth (~> 0.9) 217 | mini_mime (~> 1.0) 218 | googleauth (0.16.1) 219 | faraday (>= 0.17.3, < 2.0) 220 | jwt (>= 1.4, < 3.0) 221 | memoist (~> 0.16) 222 | multi_json (~> 1.11) 223 | os (>= 0.9, < 2.0) 224 | signet (~> 0.14) 225 | highline (1.7.10) 226 | http-accept (1.7.0) 227 | http-cookie (1.0.3) 228 | domain_name (~> 0.5) 229 | httpclient (2.8.3) 230 | i18n (1.8.10) 231 | concurrent-ruby (~> 1.0) 232 | jmespath (1.4.0) 233 | json (2.3.1) 234 | jwt (2.2.3) 235 | mail (2.7.1) 236 | mini_mime (>= 0.1.1) 237 | memoist (0.16.2) 238 | mime-types (3.3.1) 239 | mime-types-data (~> 3.2015) 240 | mime-types-data (3.2021.0225) 241 | mini_magick (4.11.0) 242 | mini_mime (1.1.0) 243 | minitest (5.14.4) 244 | molinillo (0.6.6) 245 | multi_json (1.15.0) 246 | multipart-post (2.0.0) 247 | nanaimo (0.3.0) 248 | nap (1.1.0) 249 | naturally (2.2.1) 250 | netrc (0.11.0) 251 | os (1.1.1) 252 | plist (3.6.0) 253 | public_suffix (4.0.6) 254 | rake (13.0.3) 255 | representable (3.1.1) 256 | declarative (< 0.1.0) 257 | trailblazer-option (>= 0.1.1, < 0.2.0) 258 | uber (< 0.2.0) 259 | rest-client (2.1.0) 260 | http-accept (>= 1.7.0, < 2.0) 261 | http-cookie (>= 1.0.2, < 2.0) 262 | mime-types (>= 1.16, < 4.0) 263 | netrc (~> 0.8) 264 | retriable (3.1.2) 265 | rexml (3.2.5) 266 | rouge (2.0.7) 267 | rqrcode (1.2.0) 268 | chunky_png (~> 1.0) 269 | rqrcode_core (~> 0.2) 270 | rqrcode_core (0.2.0) 271 | ruby-macho (1.4.0) 272 | ruby2_keywords (0.0.4) 273 | rubyzip (2.3.0) 274 | security (0.1.3) 275 | signet (0.15.0) 276 | addressable (~> 2.3) 277 | faraday (>= 0.17.3, < 2.0) 278 | jwt (>= 1.5, < 3.0) 279 | multi_json (~> 1.10) 280 | simctl (1.6.8) 281 | CFPropertyList 282 | naturally 283 | slack-notifier (2.3.2) 284 | terminal-notifier (2.0.0) 285 | terminal-table (1.8.0) 286 | unicode-display_width (~> 1.1, >= 1.1.1) 287 | thread_safe (0.3.6) 288 | trailblazer-option (0.1.1) 289 | tty-cursor (0.7.1) 290 | tty-screen (0.8.1) 291 | tty-spinner (0.9.3) 292 | tty-cursor (~> 0.7) 293 | typhoeus (1.4.0) 294 | ethon (>= 0.9.0) 295 | tzinfo (1.2.9) 296 | thread_safe (~> 0.1) 297 | uber (0.1.0) 298 | unf (0.1.4) 299 | unf_ext 300 | unf_ext (0.0.7.7) 301 | unicode-display_width (1.7.0) 302 | webrick (1.7.0) 303 | word_wrap (1.0.0) 304 | xcodeproj (1.19.0) 305 | CFPropertyList (>= 2.3.3, < 4.0) 306 | atomos (~> 0.1.3) 307 | claide (>= 1.0.2, < 2.0) 308 | colored2 (~> 3.1) 309 | nanaimo (~> 0.3.0) 310 | xcpretty (0.3.0) 311 | rouge (~> 2.0.7) 312 | xcpretty-travis-formatter (1.0.1) 313 | xcpretty (~> 0.2, >= 0.0.7) 314 | 315 | PLATFORMS 316 | ruby 317 | 318 | DEPENDENCIES 319 | Martin! 320 | cocoapods (= 1.10.0) 321 | cocoapods-codegen! 322 | cocoapods-xcframework! 323 | fastlane 324 | ffi (= 1.14.2) 325 | ipa_analyzer! 326 | json (= 2.3.1) 327 | mail (= 2.7.1) 328 | rest-client (= 2.1.0) 329 | 330 | BUNDLED WITH 331 | 2.1.4 332 | -------------------------------------------------------------------------------- /Example/podfile.package: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "NKOpusModule", 4 | "git_url": "ssh://git@g.hz.netease.com:22222/cloudmusic-ios/NKOpusModule.git", 5 | "git_branch" : "feature/ktvroom" 6 | } 7 | ] -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 戴易超 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cocoapods-framework 2 | 3 | - 你是不是发现 `cocoapods-packager` 不能支持 `Swift`? 4 | - 你是不是发现 `cocoapods-packager` 不能支持 `m1`? 5 | - 你是不是发现 `cocoapods-packager` 不能支持 `GCC module`? 6 | - 你是不是发现 `cocoapods-packager` 不能支持 `watch` 和 `tv`? 7 | - 那你不妨试试我们这个插件~,美滋滋哦~ 8 | 本插件可以帮助开发者快速的创建一个`OC`/`Swift`的`xcframework`。 9 | 10 | ## feat 11 | - 支持 `Swift`/`Object-C` 生成 `framework` 12 | - 支持 `Xcode` 新特性: `xcframework` 13 | - 支持 `cocoapods` 绝大多数属性 14 | - 支持 `subspec` 打包 15 | ## Installation 16 | 17 | $ gem install cocoapods-xcframework 18 | $ gem install cocoapods-xcframework-*.gem //本地安装 19 | 20 | ## Usage 21 | $ pod framework NAME [SOURCE] 22 | 更多请看 23 | $pod framework --help 24 | 25 | ## Q&A 26 | ### 而且本插件支持`apple`的全平台的`framework`创建,如下图 27 | 28 | ![all](pic/allplatform.png) 29 | 30 | 所以如果我们的库不是全平台都支持的话,可以选取支持的平台来打包,如下 31 | 32 | ``` 33 | Pod::Spec do |spec| 34 | # @param [Array] args 35 | # A tuple where the first value is the name of the platform, 36 | # (either `:ios` or `:osx`) and the second is the deployment 37 | # target. 38 | spec.platform = :osx, '10.8' 39 | end 40 | ``` 41 | ### 动态库和静态库 42 | 43 | ``` 44 | Pod::Spec do |spec| 45 | spec.static_framework = true 46 | #spec.static_framework = false 47 | end 48 | ``` 49 | 50 | ### 其他 51 | 请提`issue`,我会尽力更新。 52 | -------------------------------------------------------------------------------- /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-framework.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-framework/gem_version.rb' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = 'cocoapods-xcframework' 8 | spec.version = CocoapodsFramework::VERSION 9 | spec.authors = ['戴易超'] 10 | spec.email = ['daiyichao@corp.netease.com'] 11 | spec.description = %q{把podspec打包成xcframework的小工具。packager pod to a xcframework} 12 | spec.summary = %q{把podspec打包成xcframework的小工具。packager pod to a xcframework} 13 | spec.homepage = 'https://github.com/TyrantDante/cocoapods-framework' 14 | spec.license = 'MIT' 15 | 16 | spec.files = `git ls-files`.split($/) 17 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 18 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 19 | spec.require_paths = ['lib'] 20 | spec.add_dependency "cocoapods", '>= 1.10.0', '< 2.0' 21 | 22 | spec.add_development_dependency 'bundler', '~> 1.3' 23 | spec.add_development_dependency 'rake' 24 | end 25 | -------------------------------------------------------------------------------- /lib/cocoapods-framework.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods-framework/gem_version' 2 | -------------------------------------------------------------------------------- /lib/cocoapods-framework/command.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods-framework/command/framework' 2 | require 'cocoapods-framework/command/muti_framework' 3 | -------------------------------------------------------------------------------- /lib/cocoapods-framework/command/framework.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Command 3 | # This is an example of a cocoapods plugin adding a top-level subcommand 4 | # to the 'pod' command. 5 | # 6 | # You can also create subcommands of existing or new commands. Say you 7 | # wanted to add a subcommand to `list` to show newly deprecated pods, 8 | # (e.g. `pod list deprecated`), there are a few things that would need 9 | # to change. 10 | # 11 | # - move this file to `lib/pod/command/list/deprecated.rb` and update 12 | # the class to exist in the the Pod::Command::List namespace 13 | # - change this class to extend from `List` instead of `Command`. This 14 | # tells the plugin system that it is a subcommand of `list`. 15 | # - edit `lib/cocoapods_plugins.rb` to require this file 16 | # 17 | # @todo Create a PR to add your plugin to CocoaPods/cocoapods.org 18 | # in the `plugins.json` file, once your plugin is released. 19 | # 20 | class Framework < Command 21 | self.summary = 'Package a podspec into a xcframework.' 22 | self.arguments = [ 23 | CLAide::Argument.new('NAME', true), 24 | CLAide::Argument.new('SOURCE', false) 25 | ] 26 | include Config::Mixin 27 | 28 | def self.options 29 | [ 30 | ['--no-force', 'Overwrite existing files.'], 31 | ['--configuration', 'Build the specified configuration (e.g. Debug). Defaults to Release'], 32 | ['--spec-sources=private,https://github.com/CocoaPods/Specs.git', 'The sources to pull dependent pods from (defaults to https://github.com/CocoaPods/Specs.git)'], 33 | ['--subspecs', 'Only include the given subspecs'], 34 | ['--use-modular-headers', 'pakcage uses modular headers during packaging'], 35 | ['--no-static-library', 'package not use static library'], 36 | ['--enable-bitcode', 'package enable bitcode'] 37 | ].concat super 38 | end 39 | 40 | def initialize(argv) 41 | @name = argv.shift_argument 42 | @source = argv.shift_argument 43 | @spec_sources = argv.option('spec-sources', 'https://github.com/CocoaPods/Specs.git').split(',') 44 | subspecs = argv.option('subspecs') 45 | @subspecs = subspecs.split(',') unless subspecs.nil? 46 | @configuration = argv.option('configuration', 'Release') 47 | @use_modular_headers = argv.option('use-modular-headers', true) 48 | @force = argv.flag?('force', true) 49 | @use_static_library = argv.flag?('static-library',true) 50 | @enable_bitcode = argv.flag?('enable-bitcode',false) 51 | config.static_library_enable = @use_static_library 52 | super 53 | end 54 | 55 | def validate! 56 | super 57 | help! 'A Pod name is required.' unless @name 58 | end 59 | 60 | def run 61 | frameworker = Frameworker.new(@name, @source, @spec_sources, @subspecs, @configuration, @force, @use_modular_headers, @enable_bitcode) 62 | frameworker.run 63 | end 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /lib/cocoapods-framework/command/muti_framework.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Command 3 | # This is an example of a cocoapods plugin adding a top-level subcommand 4 | # to the 'pod' command. 5 | # 6 | # You can also create subcommands of existing or new commands. Say you 7 | # wanted to add a subcommand to `list` to show newly deprecated pods, 8 | # (e.g. `pod list deprecated`), there are a few things that would need 9 | # to change. 10 | # 11 | # - move this file to `lib/pod/command/list/deprecated.rb` and update 12 | # the class to exist in the the Pod::Command::List namespace 13 | # - change this class to extend from `List` instead of `Command`. This 14 | # tells the plugin system that it is a subcommand of `list`. 15 | # - edit `lib/cocoapods_plugins.rb` to require this file 16 | # 17 | # @todo Create a PR to add your plugin to CocoaPods/cocoapods.org 18 | # in the `plugins.json` file, once your plugin is released. 19 | # 20 | class MutiFramework < Command 21 | self.summary = 'Package some podspec into a xcframework.' 22 | self.arguments = [ 23 | CLAide::Argument.new('NAME', true), 24 | CLAide::Argument.new('SOURCE', false) 25 | ] 26 | include Config::Mixin 27 | 28 | def self.options 29 | [ 30 | ['--no-force', 'Overwrite existing files.'], 31 | ['--configuration', 'Build the specified configuration (e.g. Debug). Defaults to Release'], 32 | ['--spec-sources=private,https://github.com/CocoaPods/Specs.git', 'The sources to pull dependent pods from (defaults to https://github.com/CocoaPods/Specs.git)'], 33 | ['--use-modular-headers', 'pakcage uses modular headers during packaging'], 34 | ['--no-static-library', 'package not use static library'] 35 | ].concat super 36 | end 37 | 38 | def initialize(argv) 39 | @name = argv.shift_argument 40 | @source = argv.shift_argument 41 | @spec_sources = argv.option('spec-sources', 'https://github.com/CocoaPods/Specs.git').split(',') 42 | @configuration = argv.option('configuration', 'Release') 43 | @use_modular_headers = argv.option('use-modular-headers', true) 44 | @force = argv.flag?('force', true) 45 | @use_static_library = argv.flag?('static-library', true) 46 | config.static_library_enable = @use_static_library 47 | super 48 | end 49 | 50 | def validate! 51 | super 52 | help! 'A file written some pods need package is needed' unless @name 53 | end 54 | 55 | def run 56 | frameworker = MutiFrameworker.new(@name, @source, @spec_sources, @configuration, @force, @use_modular_headers) 57 | frameworker.run 58 | end 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /lib/cocoapods-framework/config.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Config 3 | attr_accessor :xcframework_enable 4 | alias_method :xcframework_enable?, :xcframework_enable 5 | attr_accessor :static_library_enable 6 | alias_method :static_library_enable?, :static_library_enable 7 | end 8 | end -------------------------------------------------------------------------------- /lib/cocoapods-framework/frameworker.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Frameworker 3 | include PodUtil 4 | include DirUtil 5 | include Config::Mixin 6 | def initialize(name, source, spec_sources, subspecs, configuration, force, use_modular_headers, enable_bitcode) 7 | @name = name 8 | @source = source 9 | @spec_sources = spec_sources 10 | @subspecs = subspecs 11 | @configuration = configuration 12 | @force = force 13 | @use_modular_headers = use_modular_headers 14 | @enable_bitcode = enable_bitcode 15 | end 16 | 17 | def run 18 | spec = spec_with_path @name 19 | @is_spec_from_path = true if spec 20 | spec ||= spec_with_name @name 21 | 22 | target_dir, work_dir = create_working_directory_by_spec spec, @force 23 | build_framework spec, work_dir, target_dir 24 | end 25 | 26 | def build_framework spec, work_dir, target_dir 27 | build_in_sandbox(work_dir, spec, target_dir) 28 | end 29 | 30 | def build_in_sandbox work_dir, spec, target_dir 31 | config.installation_root = Pathname.new work_dir 32 | config.sandbox_root = "#{work_dir}/Pods" 33 | sandbox = build_static_sandbox 34 | 35 | sandbox_installer = installation_root( 36 | sandbox, 37 | spec, 38 | @subspecs, 39 | @spec_sources, 40 | true, 41 | @use_modular_headers, 42 | @enable_bitcode 43 | ) 44 | 45 | perform_build( 46 | sandbox, 47 | sandbox_installer, 48 | spec, 49 | target_dir 50 | ) 51 | end 52 | 53 | # def perform_build platform, sandbox, installer, spec 54 | def perform_build sandbox, installer, spec, target_dir 55 | sandbox_root = config.sandbox_root.to_s 56 | builder = Pod::XBuilder.new( 57 | installer, 58 | Dir.pwd, 59 | sandbox_root, 60 | spec, 61 | @configuration 62 | ) 63 | builder.build 64 | builder.outputs target_dir 65 | target_dir 66 | end 67 | end 68 | end -------------------------------------------------------------------------------- /lib/cocoapods-framework/gem_version.rb: -------------------------------------------------------------------------------- 1 | module CocoapodsFramework 2 | VERSION = "0.2.5" 3 | end 4 | -------------------------------------------------------------------------------- /lib/cocoapods-framework/muti_frameworker.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class MutiFrameworker 3 | include Pod::PodUtil 4 | include Pod::GitUtil 5 | include Pod::DirUtil 6 | include Config::Mixin 7 | def initialize(name, source, spec_sources, configuration, force, use_modular_headers) 8 | @name = name 9 | @source = source 10 | @spec_sources = spec_sources 11 | @configuration = configuration 12 | @force = force 13 | @use_modular_headers = use_modular_headers 14 | end 15 | 16 | def run 17 | configs = muti_config_with_file @name 18 | target_dir, work_dir = create_working_directory_by_spec "xcframeworks", @force 19 | prepare_git_with_configs configs, work_dir 20 | build_frameworks configs, work_dir, target_dir 21 | end 22 | 23 | def build_frameworks configs, work_dir, target_dir 24 | config.installation_root = Pathname.new work_dir 25 | config.sandbox_root = "#{work_dir}/Pods" 26 | sandbox = build_static_sandbox 27 | 28 | sandbox_installer = installation_root_muti( 29 | sandbox, 30 | configs, 31 | @spec_sources, 32 | @use_modular_headers 33 | ) 34 | perform_build( 35 | sandbox, 36 | sandbox_installer, 37 | configs, 38 | target_dir 39 | ) 40 | end 41 | 42 | def perform_build sandbox, installer, configs, target_dir 43 | sandbox_root = config.sandbox_root.to_s 44 | builder = Pod::XBuilder.new( 45 | installer, 46 | Dir.pwd, 47 | sandbox_root, 48 | configs, 49 | @configuration 50 | ) 51 | builder.build 52 | builder.outputs_muti target_dir 53 | end 54 | 55 | 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /lib/cocoapods-framework/util.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods-framework/util/error_util' 2 | require 'cocoapods-framework/util/cmd' 3 | require 'cocoapods-framework/util/git_util' 4 | require 'cocoapods-framework/util/pod_util' 5 | require 'cocoapods-framework/util/dir_util' 6 | -------------------------------------------------------------------------------- /lib/cocoapods-framework/util/cmd.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Cmmd 3 | class << self 4 | def sh! command 5 | UI.puts command 6 | output = `#{command}`.lines.to_a 7 | if $?.exitstatus != 0 8 | Pod::ErrorUtil.error_report command,output 9 | Process.exit -1 10 | end 11 | output 12 | end 13 | 14 | def sh? command 15 | UI.puts command 16 | output = `#{command}`.lines.to_a 17 | if $?.exitstatus != 0 18 | Pod::ErrorUtil.error_report command,output 19 | end 20 | output 21 | end 22 | end 23 | end 24 | end -------------------------------------------------------------------------------- /lib/cocoapods-framework/util/dir_util.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | module DirUtil 3 | def create_target_directory_path_by_spec spec,force 4 | target_dir = "#{Dir.pwd}/#{spec.name}-#{spec.version}" 5 | 6 | if File.exist? target_dir 7 | if @force 8 | Pathname.new(target_dir).rmtree 9 | else 10 | UI.warn "Target directory '#{target_dir}' already exists." 11 | end 12 | end 13 | target_dir 14 | end 15 | 16 | def create_working_directory_by_spec spec,force 17 | if spec.is_a? String 18 | return create_working_directory_by_name spec,force 19 | end 20 | target_dir = create_target_directory_path_by_spec spec,force 21 | # Pathname.new(target_dir).mkdir 22 | work_dir = Dir.tmpdir + '/frameworks-' + Array.new(8) { rand(36).to_s(36) }.join 23 | 24 | Pathname.new(work_dir).mkdir 25 | [target_dir, work_dir] 26 | end 27 | 28 | def create_target_directory_path_by_name name, force 29 | target_dir = "#{Dir.pwd}/#{name}-muti" 30 | 31 | if File.exist? target_dir 32 | if @force 33 | Pathname.new(target_dir).rmtree 34 | else 35 | UI.warn "Target directory '#{target_dir}' already exists." 36 | end 37 | end 38 | target_dir 39 | end 40 | 41 | def create_working_directory_by_name name, force 42 | target_dir = create_target_directory_path_by_name name,force 43 | # Pathname.new(target_dir).mkdir 44 | work_dir = Dir.tmpdir + '/frameworks-' + Array.new(8) { rand(36).to_s(36) }.join 45 | 46 | Pathname.new(work_dir).mkdir 47 | [target_dir, work_dir] 48 | end 49 | end 50 | end -------------------------------------------------------------------------------- /lib/cocoapods-framework/util/error_util.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | module ErrorUtil 3 | class << self 4 | def error_report(command, output) 5 | UI.puts "<<-EOF 6 | Build command failed: #{command} 7 | Output: 8 | #{output.map { |line| " #{line}" }.join} 9 | EOF" 10 | end 11 | end 12 | end 13 | end -------------------------------------------------------------------------------- /lib/cocoapods-framework/util/git_util.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | module GitUtil 3 | def prepare_git_with_configs configs, work_dir 4 | index = 0 5 | configs.each do |config| 6 | name = config["name"] 7 | git_url = config["git_url"] 8 | git_branch = config["git_branch"] 9 | command = "git clone #{git_url} -b #{git_branch} #{work_dir}/#{name}" 10 | Cmmd.sh! command 11 | end 12 | end 13 | end 14 | end -------------------------------------------------------------------------------- /lib/cocoapods-framework/util/pod_util.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | module PodUtil 3 | include Config::Mixin 4 | 5 | def to_native_platform name 6 | case name 7 | when 'iphoneos' then 'ios' 8 | when 'macOS' then 'osx' 9 | when 'appletvos' then 'tvos' 10 | when 'watchos' then 'watchos' 11 | when 'ios' then 'ios' 12 | when 'macos' then 'osx' 13 | when 'tvos' then 'tvos' 14 | else 15 | name 16 | end 17 | end 18 | 19 | def muti_config_with_file(path) 20 | return nil if path.nil? 21 | path = Pathname.new(path) 22 | path = Pathname.new(Dir.pwd).join(path) unless path.absolute? 23 | @path = path.expand_path 24 | content = File.open(path, 'rb').read 25 | result = JSON.parse content 26 | if not result.is_a? Array 27 | UI.error "#{path} format not support" 28 | exit -1 29 | end 30 | result 31 | end 32 | 33 | def spec_with_path(path) 34 | return if path.nil? 35 | path = Pathname.new(path) 36 | path = Pathname.new(Dir.pwd).join(path) unless path.absolute? 37 | return unless path.exist? 38 | @path = path.expand_path 39 | 40 | if @path.directory? 41 | raise @path + ': is a directory.' 42 | return 43 | end 44 | 45 | unless ['.podspec', '.json'].include? @path.extname 46 | raise @path + ': is not a podspec.' 47 | return 48 | end 49 | 50 | Specification.from_file(@path) 51 | end 52 | 53 | def spec_with_name(name) 54 | return if name.nil? 55 | 56 | set = Pod::Config.instance.sources_manager.search(Dependency.new(name)) 57 | return nil if set.nil? 58 | 59 | set.specification.root 60 | end 61 | 62 | def build_static_sandbox 63 | Sandbox.new(config.sandbox_root) 64 | end 65 | 66 | def installation_root sandbox, spec, subspecs, sources,use_frameworks = true,use_modular_headers = true, enable_bitcode = false 67 | podfile = podfile_from_spec( 68 | @path, 69 | spec, 70 | subspecs, 71 | sources, 72 | use_frameworks, 73 | use_modular_headers 74 | ) 75 | 76 | installer = Installer.new(sandbox, podfile) 77 | installer.repo_update = true 78 | installer.install! 79 | 80 | unless installer.nil? 81 | installer.pods_project.targets.each do |target| 82 | target.build_configurations.each do |configuration| 83 | if enable_bitcode && configuration.name == "Release" 84 | configuration.build_settings['ENABLE_BITCODE'] = 'YES' 85 | configuration.build_settings['BITCODE_GENERATION_MODE'] = 'bitcode' 86 | end 87 | end 88 | if target.name == spec.name 89 | target.build_configurations.each do |configuration| 90 | configuration.build_settings['CLANG_MODULES_AUTOLINK'] = 'NO' 91 | end 92 | end 93 | end 94 | installer.pods_project.save 95 | end 96 | installer 97 | end 98 | 99 | def installation_root_muti sandbox, configs, sources, use_frameworks = true, use_modular_headers = true, enable_bitcode = false 100 | podfile = podfile_from_muti_configs( 101 | configs, 102 | sources, 103 | use_frameworks, 104 | use_modular_headers 105 | ) 106 | installer = Installer.new(sandbox, podfile) 107 | installer.repo_update = true 108 | installer.install! 109 | 110 | specs = configs.map do |cfg| 111 | cfg["name"] 112 | end 113 | unless installer.nil? 114 | installer.pods_project.targets.each do |target| 115 | target.build_configurations.each do |configuration| 116 | if enable_bitcode && configuration.name == "Release" 117 | configuration.build_settings['ENABLE_BITCODE'] = 'YES' 118 | configuration.build_settings['BITCODE_GENERATION_MODE'] = 'bitcode' 119 | end 120 | end 121 | if specs.include? target.name 122 | target.build_configurations.each do |configuration| 123 | configuration.build_settings['CLANG_MODULES_AUTOLINK'] = 'NO' 124 | end 125 | end 126 | end 127 | installer.pods_project.save 128 | end 129 | installer 130 | end 131 | 132 | def podfile_from_spec path, spec, subspecs, sources, use_frameworks = true, use_modular_headers=true 133 | options = Hash.new 134 | options[:podspec] = path.to_s 135 | options[:subspecs] = spec.subspecs.map do |sub| 136 | sub.base_name 137 | end 138 | options[:subspecs] = subspecs if subspecs 139 | # 非常奇怪,如果传一个空的数组过去就会出问题!! 140 | if options[:subspecs].length == 0 141 | options[:subspecs] = nil 142 | end 143 | static_library_enable = config.static_library_enable? 144 | Pod::Podfile.new do 145 | sources.each {|s| source s} 146 | spec.available_platforms.each do |plt| 147 | target "#{spec.name}-#{plt.name}" do 148 | platform(plt.name, spec.deployment_target(plt.name)) 149 | pod(spec.name, options) 150 | end 151 | end 152 | 153 | install!('cocoapods',:integrate_targets => false,:deterministic_uuids => false) 154 | if static_library_enable 155 | use_frameworks! :linkage => :static if use_frameworks 156 | else 157 | use_frameworks! if use_frameworks 158 | end 159 | use_modular_headers! if use_modular_headers 160 | end 161 | end 162 | 163 | def podfile_from_muti_configs configs, sources, use_frameworks = true, use_modular_headers = true 164 | installation_root = config.installation_root.to_s 165 | static_library_enable = config.static_library_enable? 166 | Pod::Podfile.new do 167 | sources.each {|s| source s} 168 | configs.each do |cfg| 169 | pod_spec_path = installation_root + "/#{cfg["name"]}/#{cfg["name"]}.podspec" 170 | pod_spec_json_path = pod_spec_path + ".json" 171 | (Pathname.glob(pod_spec_path) + Pathname.glob(pod_spec_json_path)).each do |real_path| 172 | spec = Pod::Specification.from_file real_path.to_s 173 | options = Hash.new 174 | options[:podspec] = real_path.to_s 175 | if cfg["subspecs"] 176 | options[:subspecs] = cfg["subspecs"] 177 | else 178 | options[:subspecs] = spec.subspecs.map do |sub| 179 | sub.base_name 180 | end 181 | end 182 | # 非常奇怪,如果传一个空的数组过去就会出问题!! 183 | if options[:subspecs].length == 0 184 | options[:subspecs] = nil 185 | end 186 | spec.available_platforms.each do |plt| 187 | target "#{spec.name}-#{plt.name}" do 188 | puts "#{plt.name} #{spec.name} #{options}" 189 | platform(plt.name, spec.deployment_target(plt.name)) 190 | pod(spec.name, options) 191 | end 192 | end 193 | end 194 | end 195 | install!('cocoapods', 196 | :integrate_targets => false, 197 | :deterministic_uuids => false) 198 | 199 | if static_library_enable 200 | use_frameworks! :linkage => :static if use_frameworks 201 | else 202 | use_frameworks! if use_frameworks 203 | end 204 | use_modular_headers! if use_modular_headers 205 | end 206 | end 207 | 208 | def generic_new_podspec_hash spec 209 | spec_hash = spec.to_hash 210 | [ 211 | "source_files", 212 | "resources", 213 | "resource_bundles", 214 | "prefix_header_contents", 215 | "prefix_header_file", 216 | "header_dir", 217 | "header_mappings_dir", 218 | "script_phase", 219 | "public_header_files", 220 | "private_header_files", 221 | "vendored_frameworks", 222 | "vendored_libraries", 223 | "exclude_files", 224 | "preserve_paths", 225 | "module_map", 226 | "subspec" 227 | ].each do |key| 228 | spec_hash.delete "#{key}" 229 | end 230 | spec_hash 231 | end 232 | 233 | def fix_header_file spec_hash, xcframework_path 234 | puts "Dir.glob(#{xcframework_path}/*/) : #{Dir.glob("#{xcframework_path}/*/")}" 235 | Dir.glob("#{xcframework_path}/*/").each do |file| 236 | ['ios','macos','tvos', 'watchos'].each do |prefix| 237 | last_path = file.split("/").last 238 | if last_path =~ /#{prefix}/ and not last_path =~ /simulator/ 239 | platform = to_native_platform prefix 240 | if spec_hash[platform] 241 | spec_hash[platform]["public_header_files"] = "#{spec_hash[:vendored_frameworks]}/#{last_path}/*/Headers/*.h" 242 | spec_hash[platform]["source_files"] = "#{spec_hash[:vendored_frameworks]}/#{last_path}/*/Headers/*.h" 243 | else 244 | spec_hash[platform] = { 245 | "public_header_files" => "#{spec_hash[:vendored_frameworks]}/#{last_path}/*/Headers/*.h", 246 | "source_files" => "#{spec_hash[:vendored_frameworks]}/#{last_path}/*/Headers/*.h" 247 | } 248 | end 249 | end 250 | end 251 | end 252 | puts spec_hash.to_json 253 | spec_hash 254 | end 255 | end 256 | end 257 | -------------------------------------------------------------------------------- /lib/cocoapods-framework/xbuilder.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods-framework/xbuilder/xcode_xbuild' 2 | require 'cocoapods-framework/xbuilder/xcodeproj_helper' 3 | module Pod 4 | class XBuilder 5 | include XcodeXBuilder 6 | include XcodeProjHelper 7 | include PodUtil 8 | include Config::Mixin 9 | def initialize(installer, source_dir, sandbox_root, spec, configuration) 10 | # def initialize(platform, installer, source_dir, sandbox_root, spec, config) 11 | # @platform = platform 12 | @installer = installer 13 | @source_dir = source_dir 14 | @sandbox_root = sandbox_root 15 | @spec = spec 16 | @muti = @spec.is_a? Array 17 | @configs = @spec if @muti 18 | @spec = "muti" if @muti 19 | 20 | @configuration = configuration 21 | @outputs = Hash.new 22 | end 23 | 24 | def build 25 | UI.puts("Building framework #{@spec} with configuration #{@configuration}") 26 | UI.puts "Work dir is :#{@sandbox_root}" 27 | # defines = "GCC_PREPROCESSOR_DEFINITIONS='$(inherited) PodsDummy_Pods_#{@spec.name}=PodsDummy_PodPackage_#{@spec.name}'" 28 | defines = "" 29 | if @configuration == 'Debug' 30 | defines << 'GCC_GENERATE_DEBUGGING_SYMBOLS=YES ONLY_ACTIVE_ARCH=NO' 31 | else 32 | defines << "GCC_GENERATE_DEBUGGING_SYMBOLS=NO" 33 | end 34 | 35 | build_all_device defines 36 | 37 | collect_xc_frameworks 38 | 39 | collect_bundles 40 | end 41 | 42 | def collect_xc_frameworks 43 | if @muti 44 | collect_muti_xcframworks 45 | else 46 | collect_single_xcframeworks 47 | end 48 | end 49 | 50 | def collect_muti_xcframworks 51 | @outputs[:xcframework] = Hash.new 52 | @configs.each do |cfg| 53 | export_dir = "#{@sandbox_root}/export/**/#{cfg["name"]}.framework" 54 | frameworks = Pathname.glob(export_dir) 55 | @outputs[:xcframework][cfg["name"]] = create_xc_framework_by_frameworks frameworks, cfg["name"] 56 | end 57 | end 58 | 59 | def collect_single_xcframeworks 60 | export_dir = "#{@sandbox_root}/export/**/#{@spec.name}.framework" 61 | frameworks = Pathname.glob(export_dir) 62 | @outputs[:xcframework] = create_xc_framework_by_frameworks frameworks, @spec.name 63 | end 64 | 65 | def collect_bundles 66 | if @muti 67 | colelct_muti_bundles 68 | else 69 | collect_single_bundles 70 | end 71 | end 72 | 73 | def colelct_muti_bundles 74 | @outputs[:bundle] = Hash.new 75 | @configs.each do |cfg| 76 | # "" 这个是用来代表mac os的 macos 没有后缀奇怪吧 77 | ["iphoneos","","appletvos","watchos"].each do |plat| 78 | export_dir = "#{@sandbox_root}/export/*-#{plat}/**/#{cfg["name"]}.bundle/**" 79 | Pathname.glob(export_dir).each do |bundle| 80 | if bundle.to_s.include? "#{@spec.name}.bundle/Info.plist" 81 | return 82 | end 83 | target_path = "#{@sandbox_root}/bundle/#{cfg["name"]}" 84 | @outputs[:bundle][cfg["name"]] = target_path 85 | native_platform = to_native_platform plat 86 | path = Pathname.new "#{target_path}/#{native_platform}" 87 | if not path.exist? 88 | path.mkpath 89 | end 90 | FileUtils.cp_r(Dir["#{bundle}"],"#{path}") 91 | end 92 | end 93 | end 94 | end 95 | 96 | def collect_single_bundles 97 | # "" 这个是用来代表mac os的 macos 没有后缀奇怪吧 98 | ["iphoneos","","appletvos","watchos"].each do |plat| 99 | export_dir = "#{@sandbox_root}/export/*-#{plat}/**/#{@spec.name}.bundle/**" 100 | Pathname.glob(export_dir).each do |bundle| 101 | if bundle.to_s.include? "#{@spec.name}.bundle/Info.plist" 102 | return 103 | end 104 | @outputs[:bundle] = "#{@sandbox_root}/bundle" 105 | native_platform = to_native_platform plat 106 | path = Pathname.new "#{@sandbox_root}/bundle/#{native_platform}" 107 | if not path.exist? 108 | path.mkpath 109 | end 110 | FileUtils.cp_r(Dir["#{bundle}"],"#{path}") 111 | end 112 | end 113 | end 114 | 115 | def create_xc_framework_by_frameworks frameworks, spec_name 116 | command = 'xcodebuild -create-xcframework ' 117 | frameworks.each do |framework| 118 | command << "-framework $(readlink -f '#{framework}') " 119 | end 120 | command << "-output #{@sandbox_root}/#{spec_name}.xcframework 2>&1" 121 | output = `#{command}`.lines.to_a 122 | if $?.exitstatus != 0 123 | Pod::ErrorUtil.error_report command,output 124 | Process.exit -1 125 | end 126 | "#{@sandbox_root}/#{spec_name}.xcframework" 127 | end 128 | 129 | def build_all_device defines 130 | # build general first because simulator will exchange SDKROOT to simulat sdk 131 | build_general_device defines 132 | build_simulator_device defines 133 | end 134 | 135 | def build_general_device defines 136 | UI.puts("--- Building framework #{@spec} with general device") 137 | xcode_xbuild( 138 | defines, 139 | @configuration, 140 | @sandbox_root 141 | ) 142 | end 143 | 144 | def build_simulator_device defines 145 | UI.puts("--- Building framework #{@spec} with simulator device") 146 | modify_xcode_project_sdk_to_simullator "#{@sandbox_root}/Pods.xcodeproj" 147 | xcode_xbuild( 148 | defines, 149 | @configuration, 150 | @sandbox_root 151 | ) 152 | end 153 | 154 | def outputs target_dir 155 | if not File.exist? target_dir 156 | Pathname.new(target_dir).mkdir 157 | end 158 | outputs_xcframework target_dir 159 | outputs_bundle target_dir 160 | new_spec_hash = generic_new_podspec_hash @spec 161 | new_spec_hash[:vendored_frameworks] = "#{@spec.name}.xcframework" 162 | new_spec_hash = fix_header_file new_spec_hash, "#{target_dir}/#{@spec.name}.xcframework" 163 | find_bundles(target_dir).each do |plat, value| 164 | if new_spec_hash[plat] 165 | new_spec_hash[plat]["resource_bundles"] = value 166 | else 167 | new_spec_hash[plat] = { 168 | "resource_bundles" => value 169 | } 170 | end 171 | end 172 | require 'json' 173 | spec_json = JSON.pretty_generate(new_spec_hash) << "\n" 174 | File.open("#{target_dir}/#{@spec.name}.podspec.json",'wb+') do |f| 175 | f.write(spec_json) 176 | end 177 | UI.puts "result export at :#{target_dir}" 178 | target_dir 179 | end 180 | 181 | def find_bundles target_dir 182 | bundle_root = "#{target_dir}/bundle/" 183 | pattern = "#{bundle_root}*" 184 | result = {} 185 | Pathname.glob(pattern).each do |bundle| 186 | bundle_relative_path = bundle.to_s.gsub(bundle_root, "") 187 | plat = bundle_relative_path 188 | result[plat] = { 189 | "#{@spec.name}" => "bundle/" + bundle_relative_path + "/*" 190 | } 191 | end 192 | result 193 | end 194 | 195 | def outputs_xcframework target_dir 196 | command = "cp -rp #{@outputs[:xcframework]} #{target_dir} 2>&1" 197 | output = `#{command}`.lines.to_a 198 | if $?.exitstatus != 0 199 | Pod::ErrorUtil.error_report command,output 200 | Process.exit -1 201 | end 202 | end 203 | 204 | def outputs_bundle target_dir 205 | if @outputs[:bundle] 206 | FileUtils.cp_r(Dir[@outputs[:bundle]],target_dir) 207 | end 208 | end 209 | 210 | def outputs_muti target_dir 211 | if not File.exist? target_dir 212 | Pathname.new(target_dir).mkdir 213 | end 214 | outputs_xcframework_muti target_dir 215 | outputs_bundle_muti target_dir 216 | generic_new_podspec_hash_muti target_dir 217 | end 218 | 219 | def generic_new_podspec_hash_muti target_dir 220 | work_dir = config.installation_root 221 | @configs.map do |cfg| 222 | podspec_path = "#{work_dir}/#{cfg["name"]}/#{cfg["name"]}.podspec" 223 | if not File.exist? podspec_path 224 | podspec_path = "#{podspec_path}.json" 225 | end 226 | podspec = Pod::Specification.from_file podspec_path 227 | new_spec_hash = generic_new_podspec_hash podspec 228 | new_spec_hash[:vendored_frameworks] = "#{podspec.name}.xcframework" 229 | new_spec_hash = fix_header_file new_spec_hash, "#{target_dir}/#{@spec.name}.xcframework" 230 | find_bundles("#{target_dir}/#{podspec.name}").each do |plat, value| 231 | if new_spec_hash[plat] 232 | new_spec_hash[plat]["resource_bundles"] = value 233 | else 234 | new_spec_hash[plat] = { 235 | "resource_bundles" => value 236 | } 237 | end 238 | end 239 | require 'json' 240 | spec_json = JSON.pretty_generate(new_spec_hash) << "\n" 241 | File.open("#{target_dir}/#{podspec.name}/#{podspec.name}.podspec.json",'wb+') do |f| 242 | f.write(spec_json) 243 | end 244 | UI.puts "result export at :#{target_dir}/#{podspec.name}" 245 | "#{target_dir}/#{podspec.name}" 246 | end 247 | end 248 | 249 | def outputs_xcframework_muti target_dir 250 | @outputs[:xcframework].each do |name, path| 251 | target_dir_path = "#{target_dir}/#{name}/" 252 | Pathname.new(target_dir_path).mkpath 253 | FileUtils.cp_r(path, target_dir_path) 254 | end 255 | end 256 | 257 | def outputs_bundle_muti target_dir 258 | @outputs[:bundle].each do |name, path| 259 | target_dir_path = "#{target_dir}/#{name}/bundle/" 260 | Pathname.new(target_dir_path).mkpath 261 | FileUtils.cp_r(path, target_dir_path) 262 | end 263 | end 264 | 265 | end 266 | end 267 | -------------------------------------------------------------------------------- /lib/cocoapods-framework/xbuilder/xcode_xbuild.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class XBuilder 3 | module XcodeXBuilder 4 | def xcode_xbuild(defines, configuration, work_dir, build_dir = 'export') 5 | if defined?(Pod::DONT_CODESIGN) 6 | defines = "#{defines} CODE_SIGN_IDENTITY=\"\" CODE_SIGNING_REQUIRED=NO" 7 | end 8 | pwd = Pathname.pwd 9 | Dir.chdir work_dir 10 | command = "xcodebuild #{defines} BUILD_DIR=#{build_dir} BUILD_LIBRARY_FOR_DISTRIBUTION=YES clean build -configuration #{configuration} -alltargets 2>&1" 11 | output = `#{command}`.lines.to_a 12 | Dir.chdir pwd 13 | if $?.exitstatus != 0 14 | Pod::ErrorUtil.error_report command,output 15 | Process.exit -1 16 | end 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/cocoapods-framework/xbuilder/xcodeproj_helper.rb: -------------------------------------------------------------------------------- 1 | require 'xcodeproj' 2 | module Pod 3 | class XBuilder 4 | module XcodeProjHelper 5 | include PodUtil 6 | def modify_xcode_project_sdk_to_simullator path 7 | sdks = xcode_sdks 8 | project = Xcodeproj::Project.open path 9 | 10 | project.targets.each do |target| 11 | simulator_sdk = to_native_simulator_platform target.sdk 12 | if not simulator_sdk.nil? 13 | canonicalName = sdks[simulator_sdk]["canonicalName"] 14 | target.build_configurations.each do |configuration| 15 | configuration.build_settings["SDKROOT"] = canonicalName 16 | end 17 | end 18 | end 19 | project.save 20 | end 21 | 22 | private 23 | def xcode_sdks 24 | return @x_sdks if @x_sdks 25 | outputs = `xcodebuild -showsdks -json` 26 | sdks = JSON.parse outputs 27 | @x_sdks = {} 28 | sdks.each do |sdk| 29 | @x_sdks[sdk["platform"]] = sdk 30 | end 31 | @x_sdks 32 | end 33 | 34 | def to_native_simulator_platform name 35 | case name 36 | when 'iphoneos' then 'iphonesimulator' 37 | when 'macOS' then nil 38 | when 'appletvos' then 'appletvsimulator' 39 | when 'watchos' then 'watchsimulator' 40 | else 41 | name 42 | end 43 | end 44 | end 45 | end 46 | end -------------------------------------------------------------------------------- /lib/cocoapods_plugin.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods' 2 | require 'cocoapods-framework/config' 3 | require 'cocoapods-framework/util' 4 | require 'cocoapods-framework/xbuilder' 5 | require 'cocoapods-framework/frameworker' 6 | require 'cocoapods-framework/muti_frameworker' 7 | 8 | 9 | #这个放在最后一个 10 | require 'cocoapods-framework/command' 11 | -------------------------------------------------------------------------------- /pic/allplatform.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TyrantDante/cocoapods-xcframework/5397ca08580c80307da54acbaed2af7e627cee83/pic/allplatform.png -------------------------------------------------------------------------------- /pkg/cocoapods-xcframework-0.2.1.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TyrantDante/cocoapods-xcframework/5397ca08580c80307da54acbaed2af7e627cee83/pkg/cocoapods-xcframework-0.2.1.gem -------------------------------------------------------------------------------- /pkg/cocoapods-xcframework-0.2.4.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TyrantDante/cocoapods-xcframework/5397ca08580c80307da54acbaed2af7e627cee83/pkg/cocoapods-xcframework-0.2.4.gem -------------------------------------------------------------------------------- /pkg/cocoapods-xcframework-0.2.5.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TyrantDante/cocoapods-xcframework/5397ca08580c80307da54acbaed2af7e627cee83/pkg/cocoapods-xcframework-0.2.5.gem -------------------------------------------------------------------------------- /spec/command/framework_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../../spec_helper', __FILE__) 2 | 3 | module Pod 4 | describe Command::Framework do 5 | describe 'CLAide' do 6 | it 'registers it self' do 7 | Command.parse(%w{ framework }).should.be.instance_of Command::Framework 8 | end 9 | end 10 | end 11 | end 12 | 13 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'pathname' 2 | ROOT = Pathname.new(File.expand_path('../../', __FILE__)) 3 | $:.unshift((ROOT + 'lib').to_s) 4 | $:.unshift((ROOT + 'spec').to_s) 5 | 6 | require 'bundler/setup' 7 | require 'bacon' 8 | require 'mocha-on-bacon' 9 | require 'pretty_bacon' 10 | require 'pathname' 11 | require 'cocoapods' 12 | 13 | Mocha::Configuration.prevent(:stubbing_non_existent_method) 14 | 15 | require 'cocoapods_plugin' 16 | 17 | #-----------------------------------------------------------------------------# 18 | 19 | module Pod 20 | 21 | # Disable the wrapping so the output is deterministic in the tests. 22 | # 23 | UI.disable_wrap = true 24 | 25 | # Redirects the messages to an internal store. 26 | # 27 | module UI 28 | @output = '' 29 | @warnings = '' 30 | 31 | class << self 32 | attr_accessor :output 33 | attr_accessor :warnings 34 | 35 | def puts(message = '') 36 | @output << "#{message}\n" 37 | end 38 | 39 | def warn(message = '', actions = []) 40 | @warnings << "#{message}\n" 41 | end 42 | 43 | def print(message) 44 | @output << message 45 | end 46 | end 47 | end 48 | end 49 | 50 | #-----------------------------------------------------------------------------# 51 | --------------------------------------------------------------------------------