├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── bin └── hcode ├── hcode.gemspec └── lib ├── cocoapods-core ├── core_ui.rb ├── gem_version.rb ├── github.rb ├── http.rb ├── lockfile.rb ├── metrics.rb ├── platform.rb ├── podfile.rb ├── podfile │ ├── dsl.rb │ └── target_definition.rb ├── requirement.rb ├── source.rb ├── source │ ├── acceptor.rb │ ├── aggregate.rb │ └── health_reporter.rb ├── specification.rb ├── specification │ ├── consumer.rb │ ├── dsl.rb │ ├── dsl │ │ ├── attribute.rb │ │ ├── attribute_support.rb │ │ ├── deprecations.rb │ │ └── platform_proxy.rb │ ├── json.rb │ ├── linter.rb │ ├── linter │ │ ├── analyzer.rb │ │ └── result.rb │ ├── root_attribute_accessors.rb │ ├── set.rb │ └── set │ │ └── presenter.rb ├── standard_error.rb ├── vendor.rb ├── vendor │ ├── requirement.rb │ └── version.rb ├── version.rb └── yaml_helper.rb ├── cocoapods.rb └── cocoapods ├── command.rb ├── command ├── cluster.rb ├── cluster │ ├── master.rb │ ├── schedule.rb │ ├── slave.rb │ └── status.rb ├── fpga.rb ├── fpga │ ├── program.rb │ └── resetpcie.rb ├── inter_process_communication.rb ├── ip.rb ├── ip │ ├── create.rb │ ├── get.rb │ ├── get_with_board.rb │ ├── get_with_shell.rb │ ├── install.rb │ └── make.rb ├── list.rb ├── outdated.rb ├── project.rb ├── repo.rb ├── repo │ ├── add.rb │ ├── lint.rb │ ├── list.rb │ ├── push.rb │ ├── remove.rb │ └── update.rb ├── search.rb ├── setup.rb ├── shell.rb ├── shell │ └── get.rb ├── spec.rb └── spec │ ├── cat.rb │ ├── create.rb │ ├── edit.rb │ ├── env_spec.rb │ ├── lint.rb │ └── which.rb ├── config.rb ├── downloader.rb ├── downloader ├── cache.rb ├── request.rb └── response.rb ├── executable.rb ├── external_sources.rb ├── external_sources ├── abstract_external_source.rb ├── downloader_source.rb ├── path_source.rb └── podspec_source.rb ├── gem_version.rb ├── generator ├── acknowledgements.rb ├── acknowledgements │ ├── markdown.rb │ └── plist.rb ├── bridge_support.rb ├── copy_resources_script.rb ├── dummy_source.rb ├── embed_frameworks_script.rb ├── header.rb ├── info_plist_file.rb ├── module_map.rb ├── prefix_header.rb ├── umbrella_header.rb ├── xcconfig.rb └── xcconfig │ ├── aggregate_xcconfig.rb │ ├── pod_xcconfig.rb │ └── xcconfig_helper.rb ├── hooks_manager.rb ├── installer.rb ├── installer ├── analyzer.rb ├── analyzer │ ├── analysis_result.rb │ ├── locking_dependency_analyzer.rb │ ├── pod_variant.rb │ ├── pod_variant_set.rb │ ├── sandbox_analyzer.rb │ ├── specs_state.rb │ ├── target_inspection_result.rb │ └── target_inspector.rb ├── file_references_installer.rb ├── installation_options.rb ├── migrator.rb ├── pod_source_installer.rb ├── pod_source_preparer.rb ├── podfile_validator.rb ├── post_install_hooks_context.rb ├── pre_install_hooks_context.rb ├── source_provider_hooks_context.rb ├── target_installer.rb ├── target_installer │ ├── aggregate_target_installer.rb │ └── pod_target_installer.rb ├── user_project_integrator.rb └── user_project_integrator │ ├── target_integrator.rb │ └── target_integrator │ └── xcconfig_integrator.rb ├── open-uri.rb ├── project.rb ├── resolver.rb ├── resolver └── lazy_specification.rb ├── sandbox.rb ├── sandbox ├── file_accessor.rb ├── headers_store.rb ├── path_list.rb ├── pod_dir_cleaner.rb └── podspec_finder.rb ├── sources_manager.rb ├── target.rb ├── target ├── aggregate_target.rb └── pod_target.rb ├── user_interface.rb ├── user_interface └── error_report.rb └── validator.rb /Gemfile: -------------------------------------------------------------------------------- 1 | # Declares a dependency to the git repo of CocoaPods gem. This declaration is 2 | # compatible with the local git repos feature of Bundler. 3 | # 4 | def cp_gem(name, repo_name, branch = 'master', path: false) 5 | opts = if path 6 | { :path => "../#{repo_name}" } 7 | else 8 | url = "https://github.com/CocoaPods/#{repo_name}.git" 9 | { :git => url, :branch => branch } 10 | end 11 | gem name, opts 12 | end 13 | 14 | source 'https://rubygems.org' 15 | 16 | gemspec 17 | 18 | # This is the version that ships with OS X 10.10, so be sure we test against it. 19 | # At the same time, the 1.7.7 version won't install cleanly on Ruby > 2.2, 20 | # so we use a fork that makes a trivial change to a macro invocation. 21 | gem 'json'#, :git => 'https://github.com/segiddins/json.git', :branch => 'seg-1.7.7-ruby-2.2' 22 | 23 | group :development do 24 | cp_gem 'claide', 'CLAide' 25 | #cp_gem 'cocoapods-core', 'Core' 26 | #cp_gem 'cocoapods-deintegrate', 'cocoapods-deintegrate' 27 | cp_gem 'cocoapods-downloader', 'cocoapods-downloader' 28 | #cp_gem 'cocoapods-plugins', 'cocoapods-plugins' 29 | #cp_gem 'cocoapods-search', 'cocoapods-search' 30 | cp_gem 'cocoapods-stats', 'cocoapods-stats' 31 | #cp_gem 'cocoapods-trunk', 'cocoapods-trunk' 32 | #cp_gem 'cocoapods-try', 'cocoapods-try' 33 | cp_gem 'molinillo', 'Molinillo' 34 | #cp_gem 'xcodeproj', 'Xcodeproj' 35 | 36 | #gem 'cocoapods-dependencies', '~> 1.0.beta.1' 37 | 38 | gem 'bacon' 39 | gem 'mocha' 40 | gem 'mocha-on-bacon' 41 | gem 'prettybacon' 42 | gem 'webmock' 43 | 44 | # Integration tests 45 | gem 'diffy' 46 | gem 'clintegracon' 47 | 48 | # Code Quality 49 | gem 'inch_by_inch' 50 | gem 'rubocop' 51 | 52 | gem 'jimson' 53 | gem 'terminal-table' 54 | end 55 | 56 | group :debugging do 57 | gem 'rb-fsevent' 58 | gem 'kicker' 59 | gem 'awesome_print' 60 | gem 'ruby-prof', :platforms => [:ruby] 61 | end 62 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GIT 2 | remote: https://github.com/CocoaPods/CLAide.git 3 | revision: 15b7e7de4605ee16dfa4def5e465317c06375a43 4 | branch: master 5 | specs: 6 | claide (0.9.1) 7 | 8 | GIT 9 | remote: https://github.com/CocoaPods/Molinillo.git 10 | revision: 6fb8e35da14b56e91cb9088cc62c9f58a36fe49c 11 | branch: master 12 | specs: 13 | molinillo (0.4.0) 14 | 15 | GIT 16 | remote: https://github.com/CocoaPods/cocoapods-downloader.git 17 | revision: 41eaf4702dda3fe205da0f82446af4c737a01041 18 | branch: master 19 | specs: 20 | cocoapods-downloader (0.9.3) 21 | 22 | GIT 23 | remote: https://github.com/CocoaPods/cocoapods-stats.git 24 | revision: 2cd4641aaadea0590233f9449552e8adb2398f0c 25 | branch: master 26 | specs: 27 | cocoapods-stats (0.6.2) 28 | 29 | PATH 30 | remote: . 31 | specs: 32 | hcode (1.0.0.beta.4) 33 | activesupport (>= 4.0.2) 34 | claide (~> 0.9.1) 35 | cocoapods-downloader (~> 0.9.3) 36 | cocoapods-stats (~> 0.6.2) 37 | colored (~> 1.2) 38 | escape (~> 0.0.4) 39 | fourflusher (~> 0.3.0) 40 | molinillo (~> 0.4.0) 41 | nap (~> 1.0) 42 | 43 | GEM 44 | remote: https://rubygems.org/ 45 | specs: 46 | activesupport (4.2.5.2) 47 | i18n (~> 0.7) 48 | json (~> 1.7, >= 1.7.7) 49 | minitest (~> 5.1) 50 | thread_safe (~> 0.3, >= 0.3.4) 51 | tzinfo (~> 1.1) 52 | addressable (2.4.0) 53 | ast (2.2.0) 54 | awesome_print (1.6.1) 55 | bacon (1.2.0) 56 | blankslate (3.1.3) 57 | clintegracon (0.8.1) 58 | colored (~> 1.2) 59 | diffy 60 | coderay (1.1.1) 61 | colored (1.2) 62 | crack (0.4.3) 63 | safe_yaml (~> 1.0.0) 64 | diffy (3.1.0) 65 | domain_name (0.5.20170404) 66 | unf (>= 0.0.5, < 1.0.0) 67 | escape (0.0.4) 68 | ffi (1.9.10) 69 | fourflusher (0.3.0) 70 | hashdiff (0.3.0) 71 | http-cookie (1.0.3) 72 | domain_name (~> 0.5) 73 | i18n (0.7.0) 74 | inch (0.7.0) 75 | pry 76 | sparkr (>= 0.2.0) 77 | term-ansicolor 78 | yard (~> 0.8.7.5) 79 | inch_by_inch (1.0.0) 80 | inch (~> 0.7.0) 81 | rake (~> 10.0) 82 | jimson (0.11.0) 83 | blankslate (~> 3.1, >= 3.1.3) 84 | multi_json (~> 1, >= 1.11.2) 85 | rack (~> 1, >= 1.4.5) 86 | rest-client (~> 1, >= 1.7.3) 87 | json (1.8.3) 88 | kicker (3.0.0) 89 | listen (~> 1.3.0) 90 | notify (~> 0.5.2) 91 | listen (1.3.1) 92 | rb-fsevent (>= 0.9.3) 93 | rb-inotify (>= 0.9) 94 | rb-kqueue (>= 0.2) 95 | metaclass (0.0.4) 96 | method_source (0.8.2) 97 | mime-types (2.99.3) 98 | minitest (5.8.4) 99 | mocha (1.1.0) 100 | metaclass (~> 0.0.1) 101 | mocha-on-bacon (0.2.2) 102 | mocha (>= 0.13.0) 103 | multi_json (1.12.1) 104 | nap (1.1.0) 105 | netrc (0.11.0) 106 | notify (0.5.2) 107 | parser (2.3.0.6) 108 | ast (~> 2.2) 109 | powerpack (0.1.1) 110 | prettybacon (0.0.2) 111 | bacon (~> 1.2) 112 | pry (0.10.3) 113 | coderay (~> 1.1.0) 114 | method_source (~> 0.8.1) 115 | slop (~> 3.4) 116 | rack (1.6.8) 117 | rainbow (2.1.0) 118 | rake (10.5.0) 119 | rb-fsevent (0.9.7) 120 | rb-inotify (0.9.7) 121 | ffi (>= 0.5.0) 122 | rb-kqueue (0.2.4) 123 | ffi (>= 0.5.0) 124 | rest-client (1.8.0) 125 | http-cookie (>= 1.0.2, < 2.0) 126 | mime-types (>= 1.16, < 3.0) 127 | netrc (~> 0.7) 128 | rubocop (0.37.2) 129 | parser (>= 2.3.0.4, < 3.0) 130 | powerpack (~> 0.1) 131 | rainbow (>= 1.99.1, < 3.0) 132 | ruby-progressbar (~> 1.7) 133 | unicode-display_width (~> 0.3) 134 | ruby-prof (0.15.9) 135 | ruby-progressbar (1.7.5) 136 | safe_yaml (1.0.4) 137 | slop (3.6.0) 138 | sparkr (0.4.1) 139 | term-ansicolor (1.3.2) 140 | tins (~> 1.0) 141 | terminal-table (1.6.0) 142 | thread_safe (0.3.5) 143 | tins (1.8.2) 144 | tzinfo (1.2.2) 145 | thread_safe (~> 0.1) 146 | unf (0.1.4) 147 | unf_ext 148 | unf_ext (0.0.7.4) 149 | unicode-display_width (0.3.1) 150 | webmock (1.24.2) 151 | addressable (>= 2.3.6) 152 | crack (>= 0.3.2) 153 | hashdiff 154 | yard (0.8.7.6) 155 | 156 | PLATFORMS 157 | ruby 158 | 159 | DEPENDENCIES 160 | awesome_print 161 | bacon 162 | bundler (~> 1.3) 163 | claide! 164 | clintegracon 165 | cocoapods-downloader! 166 | cocoapods-stats! 167 | diffy 168 | hcode! 169 | inch_by_inch 170 | jimson 171 | json 172 | kicker 173 | mocha 174 | mocha-on-bacon 175 | molinillo! 176 | prettybacon 177 | rake (~> 10.0) 178 | rb-fsevent 179 | rubocop 180 | ruby-prof 181 | terminal-table 182 | webmock 183 | 184 | BUNDLED WITH 185 | 1.15.0 186 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This project is licensed under the MIT license. 2 | 3 | Copyright (c) 2015 - 2016 Qian Zhao . 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ##What is hCODE 2 | The heterogeneous Computing Oriented Development Environment (hCODE) design method and tools are proposed to simplify the creation, sharing, and software integration of FPGA hardware accelerators. 3 | 4 | The project in this repository is the online IP manager part of hCODE platform. This hcode command line tool is modified from [CocoaPods](https://cocoapods.org/), which is a dependency manager for Swift and Objective-C Cocoa projects. Main modifications are adding hardware Specs and shell-and-ip design roles support for FPGA based acccelerators. 5 | 6 | ## Installation 7 | These steps are based on Ubuntu 14.04 8 | 9 | 1. Install build essentials 10 | *sudo apt-get -y install build-essential* 11 | 12 | 2. Install ruby2.0 and ruby2.0-dev 13 | *sudo add-apt-repository -y ppa:brightbox/ruby-ng* 14 | *sudo apt-get update* 15 | *sudo apt-get -y install ruby2.0 ruby2.0-dev* 16 | 17 | 3. Install git and bundler 18 | *sudo apt-get -y install git* 19 | *sudo gem install bundle* 20 | 21 | 4. Clone hCODE from Github 22 | *git clone https://github.com/hCODE-FPGA/hCODE* 23 | 24 | 5. Install dependencies with bundle 25 | *cd hCODE* 26 | *bundle* 27 | 28 | 6. Initialize setup the hCODE 29 | *cd bin* 30 | *./hcode setup* 31 | 32 | You can put the location of hcode executable script into PATH and use it for convenient. 33 | 34 | 35 | ## Commands List 36 | | Commands | Functions | 37 | | ------------------------------------------|:----------------------------------------------------------| 38 | | ./hcode setup | Setup: pull the repo to ~/.hcode. | 39 | | ./hcode list | List all hcode projects. | 40 | | ./hcode search [text] | Search hcode projects with [text] in their name. | 41 | | ./hcode search -full [text] | Search hcode projects with [text] in their name, summary or description field. | 42 | | ./hcode spec cat [test] | Print spec file of hcode project that name contains [text] | 43 | | ./hcode spec create [type] | Create a demo hcode.spec file of [type] | 44 | | ./hcode spec lint hcode.spec | Validate the json format of the SPEC file. | 45 | | ./hcode repo add [NAME] [URL] | add a private repo to ~/.hcode/repos/[NAME]/ | 46 | | ./hcode remove [NAME] | remove private repo located at ~/.hcode/repose/[NAME]/ | 47 | | ./hcode repo push [NAME] hcode.spec | push hcode.spec to ~/.hcode/repose/[NAME]/ repo | 48 | | ./hcode ip create [NAME] [SHELL] | Create ip template project of [NAME] using a [SHELL] | 49 | | ./hcode ip get [NAME] [TAG] | Download a IP and a compatable SHELL. | 50 | | ./hcode shell get [NAME] | Download a Shell project. | 51 | | ./hcode ip make [NAME] | Configure and make the downloaded [NAME] ip. The configuration is based on the platform section in hcode.spec of ip. The generated ip verilog files are then integrated with the shell project. The project will be opened in Vivado, changes and bitstream generation can then be performed. | 52 | | ./hcode fpga program [NAME] | Give a bitstream file or folder of [NAME]. This command find bitstream file and then program to FPGA. | 53 | | ./hcode fpga resetpci | Reset the PCIe connection after a reconfiguration. | 54 | 55 | ## License 56 | This project is licenced under the MIT license. 57 | -------------------------------------------------------------------------------- /bin/hcode: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | if Encoding.default_external != Encoding::UTF_8 4 | STDERR.puts <<-DOC 5 | \e[33mWARNING: CocoaPods requires your terminal to be using UTF-8 encoding. 6 | Consider adding the following to ~/.profile: 7 | 8 | export LANG=en_US.UTF-8 9 | \e[0m 10 | DOC 11 | end 12 | 13 | if $PROGRAM_NAME == __FILE__ && !ENV['COCOAPODS_NO_BUNDLER'] 14 | ENV['BUNDLE_GEMFILE'] = File.expand_path('../../Gemfile', __FILE__) 15 | require 'rubygems' 16 | require 'bundler/setup' 17 | $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) 18 | elsif ENV['COCOAPODS_NO_BUNDLER'] 19 | require 'rubygems' 20 | gem 'cocoapods' 21 | end 22 | 23 | STDOUT.sync = true if ENV['CP_STDOUT_SYNC'] == 'TRUE' 24 | 25 | require 'cocoapods' 26 | 27 | if profile_filename = ENV['PROFILE'] 28 | require 'ruby-prof' 29 | reporter = 30 | case (profile_extname = File.extname(profile_filename)) 31 | when '.txt' 32 | RubyProf::FlatPrinterWithLineNumbers 33 | when '.html' 34 | RubyProf::GraphHtmlPrinter 35 | when '.callgrind' 36 | RubyProf::CallTreePrinter 37 | else 38 | raise "Unknown profiler format indicated by extension: #{profile_extname}" 39 | end 40 | File.open(profile_filename, 'w') do |io| 41 | reporter.new(RubyProf.profile { Pod::Command.run(ARGV) }).print(io) 42 | end 43 | else 44 | Pod::Command.run(ARGV) 45 | end 46 | -------------------------------------------------------------------------------- /hcode.gemspec: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | require File.expand_path('../lib/cocoapods/gem_version', __FILE__) 3 | require 'date' 4 | 5 | Gem::Specification.new do |s| 6 | s.name = "hcode" 7 | s.version = Pod::VERSION 8 | s.date = Date.today 9 | s.license = "MIT" 10 | s.email = ["cho@arch.cs.kumamoto-u.ac.jp"] 11 | s.homepage = "https://github.com/hCODE-FPGA/hDevKit" 12 | s.authors = ["Qian ZHAO"] 13 | 14 | s.summary = "The devleopment kit tools of the hCODE project." 15 | s.description = "" 16 | s.files = Dir["lib/**/*.rb"] + %w{ bin/hcode README.md LICENSE CHANGELOG.md } 17 | 18 | s.executables = %w{ hcode } 19 | s.require_paths = %w{ lib } 20 | 21 | # Link with the version of CocoaPods-Core 22 | #s.add_runtime_dependency 'cocoapods-core', "= #{Pod::VERSION}" 23 | 24 | s.add_runtime_dependency 'claide', '~> 0.9.1' 25 | #s.add_runtime_dependency 'cocoapods-deintegrate', '>= 1.0.0.beta.1', '< 2.0' 26 | s.add_runtime_dependency 'cocoapods-downloader', '~> 0.9.3' 27 | #s.add_runtime_dependency 'cocoapods-plugins', '>= 1.0.0.beta.1', '< 2.0' 28 | #s.add_runtime_dependency 'cocoapods-search', '>= 1.0.0.beta.1', '< 2.0' 29 | s.add_runtime_dependency 'cocoapods-stats', '~> 0.6.2' 30 | #s.add_runtime_dependency 'cocoapods-trunk', '>= 1.0.0.beta.2', '< 2.0' 31 | #s.add_runtime_dependency 'cocoapods-try', '>= 1.0.0.beta.2', '< 2.0' 32 | s.add_runtime_dependency 'molinillo', '~> 0.4.0' 33 | #s.add_runtime_dependency 'xcodeproj', '>= 1.0.0.beta.3', '< 2.0' 34 | 35 | s.add_runtime_dependency 'activesupport', '>= 4.0.2' 36 | s.add_runtime_dependency 'colored', '~> 1.2' 37 | s.add_runtime_dependency 'escape', '~> 0.0.4' 38 | s.add_runtime_dependency 'fourflusher', '~> 0.3.0' 39 | s.add_runtime_dependency 'nap', '~> 1.0' 40 | 41 | s.add_development_dependency 'bacon', '~> 1.1' 42 | s.add_development_dependency 'bundler', '~> 1.3' 43 | s.add_development_dependency 'rake', '~> 10.0' 44 | 45 | ## Make sure you can build the gem on older versions of RubyGems too: 46 | s.rubygems_version = "1.6.2" 47 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= 48 | s.required_ruby_version = '>= 2.0.0' 49 | s.specification_version = 3 if s.respond_to? :specification_version 50 | end 51 | -------------------------------------------------------------------------------- /lib/cocoapods-core/core_ui.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | # Manages the UI output so dependent gems can customize it. 3 | # 4 | module CoreUI 5 | def self.puts(message) 6 | STDOUT.puts message 7 | end 8 | 9 | def self.warn(message) 10 | STDERR.puts message 11 | end 12 | 13 | #-------------------------------------------------------------------------# 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/cocoapods-core/gem_version.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | # The version of the cocoapods-core. 3 | # 4 | CORE_VERSION = '1.0.0.beta.4'.freeze unless defined? Pod::CORE_VERSION 5 | end 6 | -------------------------------------------------------------------------------- /lib/cocoapods-core/github.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | # Allows to access information about the GitHub repos. 3 | # 4 | # This class is stored in Core because it might be used by web services. 5 | # 6 | module GitHub 7 | # Returns the information of a user. 8 | # 9 | # @param [String] login 10 | # The name of the user. 11 | # 12 | # @return [Hash] The data of user. 13 | # 14 | def self.user(login) 15 | peform_request("https://api.github.com/users/#{login}") 16 | end 17 | 18 | # Returns the information of a repo. 19 | # 20 | # @param [String] url 21 | # The URL of the repo. 22 | # 23 | # @return [Hash] The hash containing the data as reported by GitHub. 24 | # 25 | def self.repo(url) 26 | if repo_id = normalized_repo_id(url) 27 | peform_request("https://api.github.com/repos/#{repo_id}") 28 | end 29 | end 30 | 31 | # Returns the tags of a repo. 32 | # 33 | # @param [String] url @see #repo 34 | # 35 | # @return [Array] The list of the tags. 36 | # 37 | def self.tags(url) 38 | if repo_id = normalized_repo_id(url) 39 | peform_request("https://api.github.com/repos/#{repo_id}/tags") 40 | end 41 | end 42 | 43 | # Returns the branches of a repo. 44 | # 45 | # @param [String] url @see #repo 46 | # 47 | # @return [Array] The list of the branches. 48 | # 49 | def self.branches(url) 50 | if repo_id = normalized_repo_id(url) 51 | peform_request("https://api.github.com/repos/#{repo_id}/branches") 52 | end 53 | end 54 | 55 | # Returns the contents of a file or directory in a repository. 56 | # 57 | # @param [String] url @see #repo 58 | # 59 | # @param [#to_s] path 60 | # The path for which the contents are needed. 61 | # 62 | # @param [String] branch 63 | # The branch for which to fetch the contents of the path. 64 | # 65 | # @return [Array] The list of the files and of the directories if the given 66 | # path is a directory. 67 | # 68 | # @return [Hash] The contents of the file (usually base64 encoded). 69 | # 70 | def self.contents(url, path = nil, branch = nil) 71 | if repo_id = normalized_repo_id(url) 72 | request_url = "https://api.github.com/repos/#{repo_id}/contents" 73 | request_url << "/#{path}" if path 74 | request_url << "?ref=#{branch}" if branch 75 | peform_request(request_url) 76 | end 77 | end 78 | 79 | private 80 | 81 | # @!group Private helpers 82 | #-------------------------------------------------------------------------# 83 | 84 | # Returns the repo ID as it is or converting a GitHub URL. 85 | # 86 | # @param [String] url_or_id 87 | # A repo ID or the URL of the repo. 88 | # 89 | # @return [String] the repo ID. 90 | # 91 | def self.normalized_repo_id(url_or_id) 92 | repo_id_from_url(url_or_id) || url_or_id 93 | end 94 | 95 | # Returns the repo ID given it's URL. 96 | # 97 | # @param [String] url 98 | # The URL of the repo. 99 | # 100 | # @return [String] the repo ID. 101 | # @return [Nil] if the given url is not a valid github repo url. 102 | # 103 | def self.repo_id_from_url(url) 104 | url[%r{github.com/([^/]*/[^/]*)\.*}, 1] 105 | end 106 | 107 | # Performs a get request with the given URL. 108 | # 109 | # @param [String] url 110 | # The URL of the resource. 111 | # 112 | # @return [Array, Hash] The information of the resource as Ruby objects. 113 | # 114 | def self.peform_request(url) 115 | require 'rest' 116 | require 'json' 117 | headers = { 'User-Agent' => 'CocoaPods' } 118 | response = REST.get(url, headers) 119 | body = JSON.parse(response.body) 120 | if response.ok? 121 | body 122 | else 123 | CoreUI.warn "Request to #{url} failed - #{response.status_code}" 124 | CoreUI.warn body['message'] 125 | nil 126 | end 127 | end 128 | 129 | #-------------------------------------------------------------------------# 130 | end 131 | end 132 | -------------------------------------------------------------------------------- /lib/cocoapods-core/http.rb: -------------------------------------------------------------------------------- 1 | require 'uri' 2 | 3 | module Pod 4 | # Handles HTTP requests 5 | # 6 | module HTTP 7 | # Resolve potential redirects and return the final URL. 8 | # 9 | # @return [string] 10 | # 11 | def self.get_actual_url(url) 12 | redirects = 0 13 | 14 | loop do 15 | response = perform_head_request(url) 16 | 17 | if [301, 302, 303, 307, 308].include? response.status_code 18 | location = response.headers['location'].first 19 | 20 | if location =~ %r{://} 21 | url = location 22 | else 23 | url = URI.join(url, location).to_s 24 | end 25 | 26 | redirects += 1 27 | else 28 | break 29 | end 30 | 31 | break unless redirects < MAX_HTTP_REDIRECTS 32 | end 33 | 34 | url 35 | end 36 | 37 | # Performs validation of a URL 38 | # 39 | # @return [REST::response] 40 | # 41 | def self.validate_url(url) 42 | return nil unless url =~ /^#{URI.regexp}$/ 43 | 44 | begin 45 | url = get_actual_url(url) 46 | resp = perform_head_request(url) 47 | rescue SocketError, URI::InvalidURIError, REST::Error, REST::DisconnectedError 48 | resp = nil 49 | end 50 | 51 | resp 52 | end 53 | 54 | #-------------------------------------------------------------------------# 55 | 56 | private 57 | 58 | # Does a HEAD request and in case of any errors a GET request 59 | # 60 | # @return [REST::response] 61 | # 62 | def self.perform_head_request(url) 63 | require 'rest' 64 | 65 | resp = ::REST.head(url, 'User-Agent' => USER_AGENT) 66 | 67 | if resp.status_code >= 400 68 | resp = ::REST.get(url, 'User-Agent' => USER_AGENT) 69 | end 70 | 71 | resp 72 | end 73 | 74 | MAX_HTTP_REDIRECTS = 3 75 | USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10) AppleWebKit/538.43.40 (KHTML, like Gecko) Version/8.0 Safari/538.43.40' 76 | 77 | #-------------------------------------------------------------------------# 78 | end 79 | end 80 | -------------------------------------------------------------------------------- /lib/cocoapods-core/metrics.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | # Allows to access metrics about pods. 3 | # 4 | # This class is stored in Core because it might be used by web services. 5 | # 6 | module Metrics 7 | # Returns the metrics of a pod. 8 | # 9 | # @param [String] name 10 | # The name of the pod. 11 | # 12 | # @return [Hash] The metrics for the pod. 13 | # 14 | def self.pod(name) 15 | peform_request("http://metrics.cocoapods.org/api/v1/pods/#{name}") 16 | end 17 | 18 | private 19 | 20 | # @!group Private helpers 21 | #-------------------------------------------------------------------------# 22 | 23 | # Performs a get request with the given URL. 24 | # 25 | # @param [String] url 26 | # The URL of the resource. 27 | # 28 | # @return [Array, Hash] The information of the resource as Ruby objects. 29 | # 30 | def self.peform_request(url) 31 | require 'rest' 32 | require 'json' 33 | headers = { 'User-Agent' => "CocoaPods #{Pod::CORE_VERSION}" } 34 | response = REST.get(url, headers) 35 | body = JSON.parse(response.body) 36 | if response.ok? 37 | body 38 | else 39 | CoreUI.warn "Request to #{url} failed - #{response.status_code}" 40 | CoreUI.warn body['message'] 41 | nil 42 | end 43 | end 44 | 45 | #-------------------------------------------------------------------------# 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /lib/cocoapods-core/requirement.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | # A Requirement is a set of one or more version restrictions of a 3 | # {Dependency}. 4 | # 5 | # It is based on the RubyGems class adapted to support CocoaPods specific 6 | # information. 7 | # 8 | # @todo Move support about external sources and head information here from 9 | # the Dependency class. 10 | # 11 | class Requirement < Pod::Vendor::Gem::Requirement 12 | quoted_operators = OPS.keys.map { |k| Regexp.quote k }.join '|' 13 | 14 | # @return [Regexp] The regular expression used to validate input strings. 15 | # 16 | PATTERN = /\A\s*(#{quoted_operators})?\s*(#{Version::VERSION_PATTERN})\s*\z/ 17 | 18 | DefaultRequirement = ['>=', Version.new(0)] # rubocop:disable Style/ConstantName 19 | 20 | #-------------------------------------------------------------------------# 21 | 22 | # Factory method to create a new requirement. 23 | # 24 | # @param [Requirement, Version, Array, String, Nil] input 25 | # The input used to create the requirement. 26 | # 27 | # @return [Requirement] A new requirement. 28 | # 29 | def self.create(input) 30 | case input 31 | when Requirement 32 | input 33 | when Version, Array 34 | new(input) 35 | else 36 | if input.respond_to? :to_str 37 | new([input.to_str]) 38 | else 39 | default 40 | end 41 | end 42 | end 43 | 44 | # @return [Requirement] The default requirement. 45 | # 46 | def self.default 47 | new('>= 0') 48 | end 49 | 50 | # Parses the given object returning a tuple where the first entry is an 51 | # operator and the second a version. If not operator is provided it 52 | # defaults to `=`. 53 | # 54 | # @param [String, Version] input 55 | # The input passed to create the requirement. 56 | # 57 | # @return [Array] A tuple representing the requirement. 58 | # 59 | def self.parse(input) 60 | return ['=', input] if input.is_a?(Version) 61 | 62 | unless PATTERN =~ input.to_s 63 | raise ArgumentError, "Illformed requirement `#{input.inspect}`" 64 | end 65 | 66 | operator = Regexp.last_match[1] || '=' 67 | version = Version.new(Regexp.last_match[2]) 68 | [operator, version] 69 | end 70 | 71 | # Constructs a requirement from `requirements`. 72 | # 73 | # @param [String, Version, Array, Array] requirements 74 | # The set of requirements 75 | # 76 | # @note Duplicate requirements are ignored. 77 | # 78 | # @note An empty set of `requirements` is the same as `">= 0"` 79 | # 80 | def initialize(*requirements) 81 | requirements = requirements.flatten 82 | requirements.compact! 83 | requirements.uniq! 84 | 85 | if requirements.empty? 86 | @requirements = [DefaultRequirement] 87 | else 88 | @requirements = requirements.map! { |r| self.class.parse r } 89 | end 90 | end 91 | 92 | # 93 | # @return [Bool] true if this pod has no requirements. 94 | # 95 | def none? 96 | if @requirements.size == 1 97 | @requirements[0] == DefaultRequirement 98 | else 99 | false 100 | end 101 | end 102 | #-------------------------------------------------------------------------# 103 | end 104 | end 105 | -------------------------------------------------------------------------------- /lib/cocoapods-core/specification/dsl/attribute_support.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Specification 3 | module DSL 4 | # @return [Array] The attributes of the class. 5 | # 6 | class << self 7 | attr_reader :attributes 8 | end 9 | 10 | # This module provides support for storing the runtime information of the 11 | # {Specification} DSL. 12 | # 13 | module AttributeSupport 14 | # Defines a root attribute for the extended class. 15 | # 16 | # Root attributes make sense only in root specification, they never are 17 | # multi-platform, they don't have inheritance, and they never have a 18 | # default value. 19 | # 20 | # @param [String] name 21 | # The name of the attribute. 22 | # 23 | # @param [Hash] options 24 | # The options used to initialize the attribute. 25 | # 26 | # @return [void] 27 | # 28 | def root_attribute(name, options = {}) 29 | options[:root_only] = true 30 | options[:multi_platform] = false 31 | store_attribute(name, options) 32 | end 33 | 34 | # Defines an attribute for the extended class. 35 | # 36 | # Regular attributes in general support inheritance and multi-platform 37 | # values, so resolving them for a given specification is not trivial. 38 | # 39 | # @param [String] name 40 | # The name of the attribute. 41 | # 42 | # @param [Hash] options 43 | # The options used to initialize the attribute. 44 | # 45 | # @return [void] 46 | # 47 | def attribute(name, options = {}) 48 | store_attribute(name, options) 49 | end 50 | 51 | #---------------------------------------------------------------------# 52 | 53 | # Creates an attribute with the given name and options and stores it in 54 | # the {DSL.attributes} hash. 55 | # 56 | # @param [String] name 57 | # The name of the attribute. 58 | # 59 | # @param [Hash] options 60 | # The options used to initialize the attribute. 61 | # 62 | # @return [void] 63 | # 64 | # @visibility private 65 | # 66 | def store_attribute(name, options) 67 | attr = Attribute.new(name, options) 68 | @attributes ||= {} 69 | @attributes[name] = attr 70 | end 71 | end 72 | end 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /lib/cocoapods-core/specification/dsl/deprecations.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Specification 3 | module DSL 4 | # Provides warning and errors for the deprecated attributes of the DSL. 5 | # 6 | module Deprecations 7 | DSL.attribute :xcconfig, 8 | :container => Hash, 9 | :inherited => true 10 | 11 | def xcconfig=(value) 12 | self.pod_target_xcconfig = value 13 | self.user_target_xcconfig = value 14 | CoreUI.warn "[#{self}] `xcconfig` has been split into "\ 15 | '`pod_target_xcconfig` and `user_target_xcconfig`.' 16 | end 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/cocoapods-core/specification/dsl/platform_proxy.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Specification 3 | module DSL 4 | # The PlatformProxy works in conjunction with Specification#_on_platform. 5 | # It provides support for a syntax like `spec.ios.source_files = 'file'`. 6 | # 7 | class PlatformProxy 8 | # @return [Specification] the specification for this platform proxy. 9 | # 10 | attr_accessor :spec 11 | 12 | # @return [Symbol] the platform described by this proxy. Can be either 13 | # `:ios` or `:osx`. 14 | # 15 | attr_reader :platform 16 | 17 | # @param [Specification] spec @see spec 18 | # @param [Symbol] platform @see platform 19 | # 20 | def initialize(spec, platform) 21 | @spec = spec 22 | @platform = platform 23 | end 24 | 25 | # Defines a setter method for each attribute of the specification 26 | # class, that forwards the message to the {#specification} using the 27 | # {Specification#on_platform} method. 28 | # 29 | # @return [void] 30 | # 31 | def method_missing(meth, *args, &block) 32 | return super unless attribute = attribute_for_method(meth) 33 | raise NoMethodError, "#{attribute} cannot be set per-platform" unless attribute.multi_platform? 34 | spec.store_attribute(attribute.name, args.first, platform) 35 | end 36 | 37 | # @!visibility private 38 | # 39 | def respond_to_missing?(method, include_all) 40 | attribute = attribute_for_method(method) 41 | (attribute && attribute.multi_platform?) || super 42 | end 43 | 44 | # Allows to add dependency for the platform. 45 | # 46 | # @return [void] 47 | # 48 | def dependency(*args) 49 | name, *version_requirements = args 50 | platform_name = platform.to_s 51 | platform_hash = spec.attributes_hash[platform_name] || {} 52 | platform_hash['dependencies'] ||= {} 53 | platform_hash['dependencies'][name] = version_requirements 54 | spec.attributes_hash[platform_name] = platform_hash 55 | end 56 | 57 | # Allows to set the deployment target for the platform. 58 | # 59 | # @return [void] 60 | # 61 | def deployment_target=(value) 62 | platform_name = platform.to_s 63 | spec.attributes_hash['platforms'] ||= {} 64 | spec.attributes_hash['platforms'][platform_name] = value 65 | end 66 | 67 | private 68 | 69 | def attribute_for_method(method) 70 | method = method.to_sym 71 | Specification::DSL.attributes.values.find do |attribute| 72 | if attribute.writer_name.to_sym == method 73 | true 74 | elsif attribute.writer_singular_form 75 | attribute.writer_singular_form.to_sym == method 76 | end 77 | end 78 | end 79 | end 80 | end 81 | end 82 | end 83 | -------------------------------------------------------------------------------- /lib/cocoapods-core/specification/json.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Specification 3 | module JSONSupport 4 | # @return [String] the json representation of the specification. 5 | # 6 | def to_json(*a) 7 | require 'json' 8 | to_hash.to_json(*a) << "\n" 9 | end 10 | 11 | # @return [String] the pretty json representation of the specification. 12 | # 13 | def to_pretty_json(*a) 14 | require 'json' 15 | JSON.pretty_generate(to_hash, *a) << "\n" 16 | end 17 | 18 | #-----------------------------------------------------------------------# 19 | 20 | # @return [Hash] the hash representation of the specification including 21 | # subspecs. 22 | # 23 | def to_hash 24 | hash = attributes_hash.dup 25 | if root? || available_platforms != parent.available_platforms 26 | platforms = Hash[available_platforms.map { |p| [p.name.to_s, p.deployment_target && p.deployment_target.to_s] }] 27 | hash['platforms'] = platforms 28 | end 29 | unless subspecs.empty? 30 | hash['subspecs'] = subspecs.map(&:to_hash) 31 | end 32 | hash 33 | end 34 | end 35 | 36 | # Configures a new specification from the given JSON representation. 37 | # 38 | # @param [String] the JSON encoded hash which contains the information of 39 | # the specification. 40 | # 41 | # 42 | # @return [Specification] the specification 43 | # 44 | def self.from_json(json) 45 | require 'json' 46 | hash = JSON.parse(json) 47 | from_hash(hash) 48 | end 49 | 50 | # Configures a new specification from the given hash. 51 | # 52 | # @param [Hash] the hash which contains the information of the 53 | # specification. 54 | # 55 | # @return [Specification] the specification 56 | # 57 | def self.from_hash(hash, parent = nil) 58 | spec = Spec.new(parent) 59 | attributes_hash = hash.dup 60 | subspecs = attributes_hash.delete('subspecs') 61 | spec.attributes_hash = attributes_hash 62 | if subspecs 63 | spec.subspecs = subspecs.map do |s_hash| 64 | Specification.from_hash(s_hash, spec) 65 | end 66 | end 67 | spec 68 | end 69 | 70 | #-----------------------------------------------------------------------# 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /lib/cocoapods-core/specification/linter/result.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Specification 3 | class Linter 4 | class Results 5 | public 6 | 7 | class Result 8 | # @return [Symbol] the type of result. 9 | # 10 | attr_reader :type 11 | 12 | # @return[String] the name of the attribute associated with result. 13 | # 14 | attr_reader :attribute_name 15 | 16 | # @return [String] the message associated with result. 17 | # 18 | attr_reader :message 19 | 20 | # @return [Boolean] whether the result only applies to public specs. 21 | # 22 | attr_reader :public_only 23 | alias_method :public_only?, :public_only 24 | 25 | # @param [Symbol] type @see type 26 | # @param [String] message @see message 27 | # 28 | def initialize(type, attribute_name, message, public_only = false) 29 | @type = type 30 | @attribute_name = attribute_name 31 | @message = message 32 | @public_only = public_only 33 | @platforms = [] 34 | end 35 | 36 | # @return [Array] the platforms where this result was 37 | # generated. 38 | # 39 | attr_reader :platforms 40 | 41 | # @return [String] a string representation suitable for UI output. 42 | # 43 | def to_s 44 | r = "[#{type.to_s.upcase}] [#{attribute_name}] #{message}" 45 | if platforms != Specification::PLATFORMS 46 | platforms_names = platforms.uniq.map do |p| 47 | Platform.string_name(p) 48 | end 49 | r << " [#{platforms_names * ' - '}]" unless platforms.empty? 50 | end 51 | r 52 | end 53 | end 54 | 55 | def initialize 56 | @results = [] 57 | @consumer = nil 58 | end 59 | 60 | include Enumerable 61 | 62 | def each 63 | results.each { |r| yield r } 64 | end 65 | 66 | def empty? 67 | results.empty? 68 | end 69 | 70 | # @return [Specification::Consumer] the current consumer. 71 | # 72 | attr_accessor :consumer 73 | 74 | # Adds an error result with the given message. 75 | # 76 | # @param [String] message 77 | # The message of the result. 78 | # 79 | # @return [void] 80 | # 81 | def add_error(attribute_name, message, public_only = false) 82 | add_result(:error, attribute_name, message, public_only) 83 | end 84 | 85 | # Adds a warning result with the given message. 86 | # 87 | # @param [String] message 88 | # The message of the result. 89 | # 90 | # @return [void] 91 | # 92 | def add_warning(attribute_name, message, public_only = false) 93 | add_result(:warning, attribute_name, message, public_only) 94 | end 95 | 96 | private 97 | 98 | # @return [Array] all of the generated results. 99 | # 100 | attr_reader :results 101 | 102 | # Adds a result of the given type with the given message. If there is a 103 | # current platform it is added to the result. If a result with the same 104 | # type and the same message is already available the current platform is 105 | # added to the existing result. 106 | # 107 | # @param [Symbol] type 108 | # The type of the result (`:error`, `:warning`). 109 | # 110 | # @param [String] message 111 | # The message of the result. 112 | # 113 | # @return [void] 114 | # 115 | def add_result(type, attribute_name, message, public_only) 116 | result = results.find do |r| 117 | r.type == type && r.attribute_name == attribute_name && r.message == message && r.public_only? == public_only 118 | end 119 | unless result 120 | result = Result.new(type, attribute_name, message, public_only) 121 | results << result 122 | end 123 | result.platforms << @consumer.platform_name if @consumer 124 | end 125 | end 126 | end 127 | end 128 | end 129 | -------------------------------------------------------------------------------- /lib/cocoapods-core/standard_error.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | # Namespaces all the errors raised by CocoaPods. 3 | # 4 | class StandardError < ::StandardError; end 5 | 6 | #-------------------------------------------------------------------------# 7 | 8 | # Wraps an exception raised by a DSL file in order to show to the user the 9 | # contents of the line that raised the exception. 10 | # 11 | class DSLError < Informative 12 | # @return [String] the description that should be presented to the user. 13 | # 14 | attr_reader :description 15 | 16 | # @return [String] the path of the dsl file that raised the exception. 17 | # 18 | attr_reader :dsl_path 19 | 20 | # @return [Exception] the exception raised by the 21 | # evaluation of the dsl file. 22 | # 23 | attr_reader :underlying_exception 24 | 25 | # @param [Exception] underlying_exception @see underlying_exception 26 | # @param [String] dsl_path @see dsl_path 27 | # 28 | def initialize(description, dsl_path, underlying_exception, contents = nil) 29 | @description = description 30 | @dsl_path = dsl_path 31 | @underlying_exception = underlying_exception 32 | @contents = contents 33 | end 34 | 35 | # @return [String] the contents of the DSL that cause the exception to 36 | # be raised. 37 | # 38 | def contents 39 | @contents ||= begin 40 | dsl_path && File.exist?(dsl_path) && File.read(dsl_path) 41 | end 42 | end 43 | 44 | # The message of the exception reports the content of podspec for the 45 | # line that generated the original exception. 46 | # 47 | # @example Output 48 | # 49 | # Invalid podspec at `RestKit.podspec` - undefined method 50 | # `exclude_header_search_paths=' for # 52 | # 53 | # from spec-repos/master/RestKit/0.9.3/RestKit.podspec:36 54 | # ------------------------------------------- 55 | # # because it would break: #import 56 | # > ns.exclude_header_search_paths = 'Code/RestKit.h' 57 | # end 58 | # ------------------------------------------- 59 | # 60 | # @return [String] the message of the exception. 61 | # 62 | def message 63 | @message ||= begin 64 | trace_line, description = parse_line_number_from_description 65 | 66 | m = "\n[!] #{description}.\n" 67 | m = m.red if m.respond_to?(:red) 68 | 69 | backtrace = underlying_exception.backtrace 70 | return m unless backtrace && dsl_path && contents 71 | 72 | trace_line = backtrace.find { |l| l.include?(dsl_path.to_s) } || trace_line 73 | return m unless trace_line 74 | line_numer = trace_line.split(':')[1].to_i - 1 75 | return m unless line_numer 76 | 77 | lines = contents.lines 78 | indent = ' # ' 79 | indicator = indent.tr('#', '>') 80 | first_line = (line_numer.zero?) 81 | last_line = (line_numer == (lines.count - 1)) 82 | 83 | m << "\n" 84 | m << "#{indent}from #{trace_line.gsub(/:in.*$/, '')}\n" 85 | m << "#{indent}-------------------------------------------\n" 86 | m << "#{indent}#{lines[line_numer - 1]}" unless first_line 87 | m << "#{indicator}#{lines[line_numer]}" 88 | m << "#{indent}#{lines[line_numer + 1]}" unless last_line 89 | m << "\n" unless m.end_with?("\n") 90 | m << "#{indent}-------------------------------------------\n" 91 | end 92 | end 93 | 94 | private 95 | 96 | def parse_line_number_from_description 97 | description = self.description 98 | if dsl_path && description =~ /((#{Regexp.quote File.expand_path(dsl_path)}|#{Regexp.quote dsl_path.to_s}):\d+)/ 99 | trace_line = Regexp.last_match[1] 100 | description = description.sub(/#{Regexp.quote trace_line}:\s*/, '') 101 | end 102 | [trace_line, description] 103 | end 104 | end 105 | end 106 | -------------------------------------------------------------------------------- /lib/cocoapods-core/vendor.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | # Namespaces the vendored modules. 3 | # 4 | module Vendor 5 | # Namespaces the classes of RubyGems used by CocoaPods. 6 | # 7 | # CocoaPods needs to vendor RubyGems because OS X ships with `v1.3.6` which 8 | # has a couple of bugs related to the comparison of pre-release versions. 9 | # 10 | # E.g. https://github.com/CocoaPods/CocoaPods/issues/398 11 | # 12 | # The following classes are copied from RubyGems `v2.4.6`. The changes 13 | # performed to the source files are the following: 14 | # 15 | # - Namespaced in `Pod::Vendor` 16 | # - commented all the `require` calls 17 | # - replaced `::Gem` with `Pod::Vendor::Gem` 18 | # 19 | module Gem 20 | require 'cocoapods-core/vendor/version' 21 | require 'cocoapods-core/vendor/requirement' 22 | 23 | #-----------------------------------------------------------------------# 24 | # RubyGems License # 25 | # https://github.com/rubygems/rubygems/blob/master/MIT.txt # 26 | #-----------------------------------------------------------------------# 27 | 28 | # Copyright (c) Chad Fowler, Rich Kilmer, Jim Weirich and others. 29 | # 30 | # Permission is hereby granted, free of charge, to any person obtaining 31 | # a copy of this software and associated documentation files (the 32 | # 'Software'), to deal in the Software without restriction, including 33 | # without limitation the rights to use, copy, modify, merge, publish, 34 | # distribute, sublicense, and/or sell copies of the Software, and to 35 | # permit persons to whom the Software is furnished to do so, subject to 36 | # the following conditions: 37 | # 38 | # The above copyright notice and this permission notice shall be 39 | # included in all copies or substantial portions of the Software. 40 | # 41 | # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 42 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 43 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 44 | # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 45 | # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 46 | # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 47 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /lib/cocoapods.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | #require 'xcodeproj' 3 | 4 | # It is very likely that we'll need these and as some of those paths will atm 5 | # result in a I18n deprecation warning, we load those here now so that we can 6 | # get rid of that warning. 7 | require 'active_support/core_ext/string/strip' 8 | require 'active_support/core_ext/string/inflections' 9 | require 'active_support/core_ext/array/conversions' 10 | # TODO: check what this actually does by the time we're going to add support for 11 | # other locales. 12 | require 'i18n' 13 | if I18n.respond_to?(:enforce_available_locales=) 14 | I18n.enforce_available_locales = false 15 | end 16 | 17 | module Pod 18 | require 'pathname' 19 | require 'tmpdir' 20 | 21 | require 'cocoapods/gem_version' 22 | require 'cocoapods-core/gem_version' 23 | 24 | # Indicates a runtime error **not** caused by a bug. 25 | # 26 | class PlainInformative < StandardError; end 27 | 28 | # Indicates a user error. 29 | # 30 | class Informative < PlainInformative; end 31 | 32 | require 'cocoapods/config' 33 | require 'cocoapods/downloader' 34 | require 'pathname' 35 | require 'cocoapods-core/vendor' 36 | 37 | 38 | 39 | # Loaded immediately after dependencies to ensure proper override of their 40 | # UI methods. 41 | # 42 | require 'cocoapods/user_interface' 43 | 44 | # Indicates an user error. This is defined in cocoapods-core. 45 | # 46 | class Informative < PlainInformative 47 | def message 48 | "[!] #{super}".red 49 | end 50 | end 51 | 52 | #Xcodeproj::PlainInformative.send(:include, CLAide::InformativeError) 53 | 54 | autoload :Version, 'cocoapods-core/version' 55 | autoload :Requirement, 'cocoapods-core/requirement' 56 | #autoload :Dependency, 'cocoapods-core/dependency' 57 | autoload :CoreUI, 'cocoapods-core/core_ui' 58 | autoload :DSLError, 'cocoapods-core/standard_error' 59 | autoload :GitHub, 'cocoapods-core/github' 60 | autoload :HTTP, 'cocoapods-core/http' 61 | autoload :Lockfile, 'cocoapods-core/lockfile' 62 | autoload :Metrics, 'cocoapods-core/metrics' 63 | autoload :Platform, 'cocoapods-core/platform' 64 | autoload :Podfile, 'cocoapods-core/podfile' 65 | autoload :Source, 'cocoapods-core/source' 66 | autoload :Specification, 'cocoapods-core/specification' 67 | autoload :StandardError, 'cocoapods-core/standard_error' 68 | autoload :YAMLHelper, 'cocoapods-core/yaml_helper' 69 | 70 | autoload :AggregateTarget, 'cocoapods/target/aggregate_target' 71 | autoload :Command, 'cocoapods/command' 72 | #autoload :Deintegrator, 'cocoapods_deintegrate' 73 | autoload :Executable, 'cocoapods/executable' 74 | autoload :ExternalSources, 'cocoapods/external_sources' 75 | autoload :Installer, 'cocoapods/installer' 76 | autoload :HooksManager, 'cocoapods/hooks_manager' 77 | autoload :PodTarget, 'cocoapods/target/pod_target' 78 | autoload :Project, 'cocoapods/project' 79 | autoload :Resolver, 'cocoapods/resolver' 80 | autoload :Sandbox, 'cocoapods/sandbox' 81 | autoload :SourcesManager, 'cocoapods/sources_manager' 82 | autoload :Target, 'cocoapods/target' 83 | autoload :Validator, 'cocoapods/validator' 84 | 85 | Spec = Specification 86 | 87 | module Generator 88 | autoload :Acknowledgements, 'cocoapods/generator/acknowledgements' 89 | autoload :Markdown, 'cocoapods/generator/acknowledgements/markdown' 90 | autoload :Plist, 'cocoapods/generator/acknowledgements/plist' 91 | autoload :BridgeSupport, 'cocoapods/generator/bridge_support' 92 | autoload :CopyResourcesScript, 'cocoapods/generator/copy_resources_script' 93 | autoload :DummySource, 'cocoapods/generator/dummy_source' 94 | autoload :EmbedFrameworksScript, 'cocoapods/generator/embed_frameworks_script' 95 | autoload :Header, 'cocoapods/generator/header' 96 | autoload :InfoPlistFile, 'cocoapods/generator/info_plist_file' 97 | autoload :ModuleMap, 'cocoapods/generator/module_map' 98 | autoload :PrefixHeader, 'cocoapods/generator/prefix_header' 99 | autoload :UmbrellaHeader, 'cocoapods/generator/umbrella_header' 100 | autoload :XCConfig, 'cocoapods/generator/xcconfig' 101 | end 102 | end 103 | -------------------------------------------------------------------------------- /lib/cocoapods/command.rb: -------------------------------------------------------------------------------- 1 | require 'colored' 2 | require 'claide' 3 | require 'molinillo/errors' 4 | 5 | module Molinillo 6 | class ResolverError 7 | include CLAide::InformativeError 8 | end 9 | end 10 | 11 | module Pod 12 | 13 | class PlainInformative 14 | include CLAide::InformativeError 15 | end 16 | 17 | class Command < CLAide::Command 18 | require 'cocoapods/command/ip' 19 | require 'cocoapods/command/shell' 20 | require 'cocoapods/command/fpga' 21 | require 'cocoapods/command/list' 22 | require 'cocoapods/command/repo' 23 | require 'cocoapods/command/setup' 24 | require 'cocoapods/command/spec' 25 | require 'cocoapods/command/search' 26 | require 'cocoapods/command/cluster' 27 | 28 | self.abstract_command = true 29 | self.command = 'hcode' 30 | self.version = VERSION 31 | self.description = 'hCODE development kit, the FPGA accelerator manager. Visit http://arch.cs.kumamoto-u.ac.jp/hcode for more details.' 32 | self.plugin_prefixes = %w(claide cocoapods) 33 | 34 | def self.options 35 | [ 36 | ['--silent', 'Show nothing'], 37 | ].concat(super) 38 | end 39 | 40 | def self.run(argv) 41 | #help! 'You cannot run hCODE as root.' if Process.uid == 0 42 | verify_xcode_license_approved! 43 | 44 | super(argv) 45 | ensure 46 | UI.print_warnings 47 | end 48 | 49 | def self.report_error(exception) 50 | case exception 51 | when Interrupt 52 | puts '[!] Cancelled'.red 53 | Config.instance.verbose? ? raise : exit(1) 54 | when SystemExit 55 | raise 56 | else 57 | if ENV['COCOA_PODS_ENV'] != 'development' 58 | puts UI::ErrorReport.report(exception) 59 | exit 1 60 | else 61 | raise exception 62 | end 63 | end 64 | end 65 | 66 | # @todo If a command is run inside another one some settings which where 67 | # true might return false. 68 | # 69 | # @todo We should probably not even load colored unless needed. 70 | # 71 | # @todo Move silent flag to CLAide. 72 | # 73 | # @note It is important that the commands don't override the default 74 | # settings if their flag is missing (i.e. their value is nil) 75 | # 76 | def initialize(argv) 77 | super 78 | config.silent = argv.flag?('silent', config.silent) 79 | config.verbose = self.verbose? unless verbose.nil? 80 | unless self.ansi_output? 81 | String.send(:define_method, :colorize) { |string, _| string } 82 | end 83 | end 84 | 85 | # Ensure that the master spec repo exists 86 | # 87 | # @return [void] 88 | # 89 | def ensure_master_spec_repo_exists! 90 | unless SourcesManager.master_repo_functional? 91 | Setup.new(CLAide::ARGV.new([])).run 92 | end 93 | end 94 | 95 | #-------------------------------------------------------------------------# 96 | 97 | include Config::Mixin 98 | 99 | private 100 | 101 | # Checks that the podfile exists. 102 | # 103 | # @raise If the podfile does not exists. 104 | # 105 | # @return [void] 106 | # 107 | def verify_podfile_exists! 108 | unless config.podfile 109 | raise Informative, "No `hCODE.conf' found in the project directory." 110 | end 111 | end 112 | 113 | # Checks that the lockfile exists. 114 | # 115 | # @raise If the lockfile does not exists. 116 | # 117 | # @return [void] 118 | # 119 | def verify_lockfile_exists! 120 | unless config.lockfile 121 | raise Informative, "No `hCODE.conf.lock' found in the project directory, run `hcode install'." 122 | end 123 | end 124 | 125 | def self.verify_xcode_license_approved! 126 | if `/usr/bin/xcrun clang 2>&1` =~ /license/ && !$?.success? 127 | raise Informative, 'You have not agreed to the Xcode license, which ' \ 128 | 'you must do to use CocoaPods. Agree to the license by running: ' \ 129 | '`xcodebuild -license`.' 130 | end 131 | end 132 | end 133 | end 134 | -------------------------------------------------------------------------------- /lib/cocoapods/command/cluster.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods/command/cluster/master' 2 | require 'cocoapods/command/cluster/slave' 3 | require 'cocoapods/command/cluster/status' 4 | require 'cocoapods/command/cluster/schedule' 5 | 6 | module Pod 7 | class Command 8 | class Cluster < Command 9 | self.abstract_command = true 10 | self.summary = 'Cluster management.' 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/cocoapods/command/cluster/master.rb: -------------------------------------------------------------------------------- 1 | require 'jimson' 2 | module Pod 3 | class Command 4 | class Cluster < Command 5 | #-----------------------------------------------------------------------# 6 | #Install a IP 7 | #-----------------------------------------------------------------------# 8 | class Master < Cluster 9 | extend Jimson::Handler 10 | 11 | self.summary = 'Start hCODE master server.' 12 | 13 | self.description = <<-DESC 14 | Setup and start hCODE master server. 15 | DESC 16 | 17 | def initialize(argv) 18 | super 19 | end 20 | 21 | def run 22 | client = Jimson::Client.new("http://127.0.0.1:8999") 23 | result = client.get_slave_conf 24 | puts result 25 | client.release_ip(0,1) 26 | result = client.get_slave_conf 27 | puts result 28 | end 29 | 30 | end 31 | end 32 | end 33 | end -------------------------------------------------------------------------------- /lib/cocoapods/command/fpga.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods/command/fpga/program' 2 | require 'cocoapods/command/fpga/resetpcie' 3 | 4 | module Pod 5 | class Command 6 | class Fpga < Command 7 | self.abstract_command = true 8 | self.summary = 'FPGA helper scripts set.' 9 | end 10 | end 11 | end -------------------------------------------------------------------------------- /lib/cocoapods/command/fpga/program.rb: -------------------------------------------------------------------------------- 1 | require 'jimson' 2 | module Pod 3 | class Command 4 | class Fpga < Command 5 | #-----------------------------------------------------------------------# 6 | #Burn an IP bitstream to FPGA 7 | #-----------------------------------------------------------------------# 8 | class Program < Fpga 9 | self.summary = 'Program an IP bitstream to FPGA.' 10 | 11 | self.description = <<-DESC 12 | Program a bitstream file to FPGA in local or remote. 13 | If a folder is given, find all *.bit files for you to choose to program. 14 | DESC 15 | 16 | self.arguments = [ 17 | CLAide::Argument.new('FOLDER_OF_BITSTREAM', true), 18 | CLAide::Argument.new('--fpga=FPGA_ID', false), 19 | CLAide::Argument.new('--remote=HOST_OR_IP', false), 20 | ] 21 | 22 | def initialize(argv) 23 | @bitstream_file = argv.shift_argument 24 | @fpga_id = argv.option('fpga') 25 | @remote_name = argv.option('remote') 26 | super 27 | end 28 | 29 | def validate! 30 | super 31 | help! 'A name of a bitstream file, or a folder contains bitstream files (*.bit) is required.' unless @bitstream_file 32 | end 33 | 34 | def run 35 | @burn_tcl = "" 36 | @fpga_id = 0 if !@fpga_id 37 | 38 | require 'find' 39 | bit_file_paths = [] 40 | Find.find(@bitstream_file) do |path| 41 | bit_file_paths << path if path =~ /.*\.bit$/ 42 | end 43 | hcode_file = "#{@bitstream_file}/hcode" 44 | if !File.file?(hcode_file) 45 | UI.puts "Bitstream defination hcode file is not found in #{@bitstream_file}." 46 | exit 47 | end 48 | if bit_file_paths.length > 1 49 | message = "Please choose a bitstream.".green 50 | bit_index = UI.choose_from_array(bit_file_paths, message) 51 | bit_file = bit_file_paths[bit_index] 52 | elsif bit_file_paths.length == 1 53 | UI.puts "Found bitstream #{bit_file_paths[0]}." 54 | bit_file = bit_file_paths[0] 55 | else 56 | UI.puts "No bitstream is found in folder #{@bitstream_file}.".red 57 | exit 58 | end 59 | burn_tcl = prepare_tcl(bit_file, @fpga_id) 60 | if(@remote_name) 61 | program_remote(bit_file, hcode_file, burn_tcl) 62 | else 63 | program_local(bit_file, hcode_file, burn_tcl) 64 | end 65 | end 66 | private 67 | #----------------------------------------# 68 | 69 | # !@group Private helpers 70 | 71 | extend Executable 72 | executable :git 73 | 74 | def prepare_tcl(bit_file, fpga_id) 75 | <<-SPEC 76 | 77 | open_hw 78 | connect_hw_server 79 | open_hw_target 80 | current_hw_device [lindex [get_hw_devices] #{fpga_id}] 81 | refresh_hw_device -update_hw_probes false [lindex [get_hw_devices] 0] 82 | set_property PROGRAM.FILE \{$DIR_HOME/.hcode/temp/bitstream.bit\} [lindex [get_hw_devices] 0] 83 | program_hw_devices [lindex [get_hw_devices] 0] 84 | refresh_hw_device [lindex [get_hw_devices] 0] 85 | 86 | SPEC 87 | end 88 | 89 | def program_local(bit_file, hcode_file, burn_tcl) 90 | system "cp #{bit_file} #{Dir.home}/.hcode/temp/bitstream.bit" 91 | File.write("#{Dir.home}/.hcode/temp/hcode.script.program.tcl", burn_tcl.gsub("$DIR_HOME","#{Dir.home}")) 92 | system "vivado -nolog -nojournal -mode batch -source #{Dir.home}/.hcode/temp/hcode.script.program.tcl" 93 | system "rm -rf #{Dir.home}/.hcode/temp/hcode.script.program.tcl" 94 | system "rm -rf #{Dir.home}/.hcode/temp/bitstream.bit" 95 | end 96 | 97 | def program_remote(bit_file, hcode_file, burn_tcl) 98 | require 'base64' 99 | hcode_spec = File.read(hcode_file) 100 | bitstream = File.binread(bit_file) 101 | bitstream_b64 = Base64.encode64(bitstream) 102 | 103 | param = Hash.new 104 | param["fpga_id"] = @fpga_id 105 | param["bit_file"] = bit_file 106 | param["burn_tcl"] = burn_tcl 107 | param["hcode"] = hcode_spec 108 | param["user"] = ENV['USER'] 109 | 110 | client = Jimson::Client.new("#{@remote_name}:8999") 111 | result = client.program_slave(param,bitstream_b64) 112 | puts result 113 | end 114 | 115 | end 116 | end 117 | end 118 | end 119 | -------------------------------------------------------------------------------- /lib/cocoapods/command/fpga/resetpcie.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Command 3 | class Fpga < Command 4 | #-----------------------------------------------------------------------# 5 | #Burn an IP bitstream to FPGA 6 | #-----------------------------------------------------------------------# 7 | class Resetpcie < Fpga 8 | self.summary = 'Reset the PCIe connection of FPGA.' 9 | 10 | self.description = <<-DESC 11 | The PCIe connection will be lost after FPGA reconfiguration. 12 | This reset bringing up accelerator without reboot of host PC. 13 | DESC 14 | 15 | def initialize(argv) 16 | super 17 | end 18 | 19 | 20 | def run 21 | @script_tcl = prepare_tcl() 22 | temp_folder = "#{Dir.home}/.hcode/temp" 23 | 24 | UI.puts "Start to reset the FPGA PCIe connection." 25 | File.write("#{temp_folder}/.hcode.script.resetpcie.sh", @burn_tcl) 26 | system "sudo sh #{temp_folder}/.hcode.script.resetpcie.sh" 27 | UI.puts "Success: PCIe is reset, your accelerator should be work now." 28 | system "rm -rf #{temp_folder}/.hcode.script.resetpcie.sh" 29 | end 30 | 31 | private 32 | #----------------------------------------# 33 | 34 | # !@group Private helpers 35 | 36 | extend Executable 37 | executable :git 38 | 39 | def prepare_tcl() 40 | <<-SPEC 41 | 42 | echo 1 |sudo tee /sys/bus/pci/devices/*$(lspci | grep Xilinx | awk '{print $1}')/remove 43 | echo 1 |sudo tee /sys/bus/pci/rescan 44 | 45 | SPEC 46 | end 47 | end 48 | end 49 | end 50 | end -------------------------------------------------------------------------------- /lib/cocoapods/command/inter_process_communication.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Command 3 | class IPC < Command 4 | self.abstract_command = true 5 | self.summary = 'Inter-process communication' 6 | 7 | def output_pipe 8 | STDOUT 9 | end 10 | 11 | #-----------------------------------------------------------------------# 12 | 13 | class Spec < IPC 14 | self.summary = 'Converts a podspec to JSON' 15 | self.description = 'Converts a podspec to JSON and prints it to STDOUT.' 16 | self.arguments = [ 17 | CLAide::Argument.new('PATH', true), 18 | ] 19 | 20 | def initialize(argv) 21 | @path = argv.shift_argument 22 | super 23 | end 24 | 25 | def validate! 26 | super 27 | help! 'A specification path is required.' unless @path 28 | end 29 | 30 | def run 31 | require 'json' 32 | spec = Specification.from_file(@path) 33 | output_pipe.puts(spec.to_pretty_json) 34 | end 35 | end 36 | 37 | #-----------------------------------------------------------------------# 38 | 39 | class Podfile < IPC 40 | self.summary = 'Converts a Podfile to YAML' 41 | self.description = 'Converts a Podfile to YAML and prints it to STDOUT.' 42 | self.arguments = [ 43 | CLAide::Argument.new('PATH', true), 44 | ] 45 | 46 | def initialize(argv) 47 | @path = argv.shift_argument 48 | super 49 | end 50 | 51 | def validate! 52 | super 53 | help! 'A Podfile path is required.' unless @path 54 | end 55 | 56 | def run 57 | require 'yaml' 58 | podfile = Pod::Podfile.from_file(@path) 59 | output_pipe.puts podfile.to_yaml 60 | end 61 | end 62 | 63 | #-----------------------------------------------------------------------# 64 | 65 | class List < IPC 66 | self.summary = 'Lists the specifications known to CocoaPods' 67 | self.description = <<-DESC 68 | Prints to STDOUT a YAML dictionary where the keys are the name of the 69 | specifications and each corresponding value is a dictionary with 70 | the following keys: 71 | 72 | - defined_in_file 73 | - version 74 | - authors 75 | - summary 76 | - description 77 | - platforms 78 | DESC 79 | 80 | def run 81 | require 'yaml' 82 | sets = SourcesManager.aggregate.all_sets 83 | result = {} 84 | sets.each do |set| 85 | begin 86 | spec = set.specification 87 | result[spec.name] = { 88 | 'authors' => spec.authors.keys, 89 | 'summary' => spec.summary, 90 | 'description' => spec.description, 91 | 'platforms' => spec.available_platforms.map { |p| p.name.to_s }, 92 | } 93 | rescue DSLError 94 | next 95 | end 96 | end 97 | output_pipe.puts result.to_yaml 98 | end 99 | end 100 | 101 | #-----------------------------------------------------------------------# 102 | 103 | class UpdateSearchIndex < IPC 104 | self.summary = 'Updates the search index' 105 | self.description = <<-DESC 106 | Updates the search index and prints its path to standard output. 107 | The search index is a YAML encoded dictionary where the keys 108 | are the names of the Pods and the values are a dictionary containing 109 | the following information: 110 | 111 | - version 112 | - summary 113 | - description 114 | - authors 115 | DESC 116 | 117 | def run 118 | SourcesManager.updated_search_index 119 | output_pipe.puts(SourcesManager.search_index_path) 120 | end 121 | end 122 | 123 | #-----------------------------------------------------------------------# 124 | 125 | class Repl < IPC 126 | END_OF_OUTPUT_SIGNAL = "\n\r" 127 | 128 | self.summary = 'The repl listens to commands on standard input' 129 | self.description = <<-DESC 130 | The repl listens to commands on standard input and prints their 131 | result to standard output. 132 | 133 | It accepts all the other ipc subcommands. The repl will signal the 134 | end of output with the the ASCII CR+LF `\\n\\r`. 135 | DESC 136 | 137 | def run 138 | print_version 139 | signal_end_of_output 140 | listen 141 | end 142 | 143 | def print_version 144 | output_pipe.puts "version: '#{Pod::VERSION}'" 145 | end 146 | 147 | def signal_end_of_output 148 | output_pipe.puts(END_OF_OUTPUT_SIGNAL) 149 | STDOUT.flush 150 | end 151 | 152 | def listen 153 | while repl_command = STDIN.gets 154 | execute_repl_command(repl_command) 155 | end 156 | end 157 | 158 | def execute_repl_command(repl_command) 159 | if (repl_command != "\n") 160 | repl_commands = repl_command.split 161 | subcommand = repl_commands.shift.capitalize 162 | arguments = repl_commands 163 | subcommand_class = Pod::Command::IPC.const_get(subcommand) 164 | subcommand_class.new(CLAide::ARGV.new(arguments)).run 165 | signal_end_of_output 166 | end 167 | end 168 | end 169 | 170 | #-----------------------------------------------------------------------# 171 | end 172 | end 173 | end 174 | -------------------------------------------------------------------------------- /lib/cocoapods/command/ip.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods/command/ip/create' 2 | require 'cocoapods/command/ip/get' 3 | require 'cocoapods/command/ip/get_with_shell' 4 | require 'cocoapods/command/ip/get_with_board' 5 | require 'cocoapods/command/ip/make' 6 | require 'cocoapods/command/ip/install' 7 | 8 | module Pod 9 | class Command 10 | class Ip < Command 11 | self.abstract_command = true 12 | self.summary = 'Develop with hardware IP' 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/cocoapods/command/ip/create.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Command 3 | class Ip < Command 4 | #-----------------------------------------------------------------------# 5 | #Create a new ip with a shell 6 | #-----------------------------------------------------------------------# 7 | class Create < Ip 8 | self.summary = 'Creates a new IP' 9 | 10 | self.description = <<-DESC 11 | Creates a scaffold for the development of a new IP named `NAME` 12 | according to the hCODE best practices. 13 | 14 | A repo url pointing to a `SHELL` is also required. You can find 15 | a proper hCODE shell by using the command `hcode search [keyword]`. 16 | 17 | If you are creating a hardware all on your own(not hCODE shell-ip way), 18 | you just need to type `hcode spec create app`, edit the hcode.spec, 19 | and sharing it by `hcode repo push [your-repo] hcode.spec` command. 20 | DESC 21 | 22 | self.arguments = [ 23 | CLAide::Argument.new('NAME', true), 24 | CLAide::Argument.new('SHELL', true), 25 | ] 26 | 27 | def initialize(argv) 28 | @name = argv.shift_argument 29 | @acc_name = "acc-#{@name}" 30 | @ip_name = "ip-#{@name}" 31 | @shell_name = argv.shift_argument 32 | super 33 | @additional_args = argv.remainder! 34 | end 35 | 36 | def validate! 37 | super 38 | help! 'A name for the IP is required.' unless @name 39 | help! 'The IP name cannot contain spaces.' if @name.match(/\s/) 40 | help! "The IP name cannot begin with a '.'" if @name[0, 1] == '.' 41 | help! 'A repo url of a shell is required.' unless @shell_name 42 | end 43 | 44 | def run 45 | make_project_dir 46 | @shell_url = repo_from_name(@shell_name)["git"] 47 | clone_template 48 | #configure_template 49 | print_info 50 | end 51 | 52 | private 53 | 54 | #----------------------------------------# 55 | 56 | # !@group Private helpers 57 | 58 | extend Executable 59 | executable :git 60 | 61 | TEMPLATE_REPO = 'https://github.com/jonsonxp/shell-vc707-xillybus-ap_fifo32.git' 62 | CREATE_NEW_POD_INFO_URL = 'http://www.arch.cs.kumamoto-u.ac.jp/hcode' 63 | 64 | def make_project_dir 65 | FileUtils.mkdir_p("#{@acc_name}") 66 | FileUtils.mkdir_p("#{@acc_name}/#{@ip_name}") 67 | FileUtils.mkdir_p("#{@acc_name}/#{@shell_name}") 68 | end 69 | 70 | def repo_from_name(name) 71 | set = SourcesManager.fuzzy_search_by_name(name) 72 | set.repo_url 73 | end 74 | 75 | # Clones the template from the remote in the working directory using 76 | # the name of the Pod. 77 | # 78 | # @return [void] 79 | # 80 | def clone_template 81 | UI.section("Cloning `#{template_repo_url}` into `#{@acc_name}`.") do 82 | git! ['clone', template_repo_url, "#{@acc_name}/#{@shell_name}"] 83 | end 84 | end 85 | 86 | # Runs the template configuration utilities. 87 | # 88 | # @return [void] 89 | # 90 | def configure_template 91 | UI.section("Configuring #{@name} template.") do 92 | Dir.chdir(@name) do 93 | if File.exist?('configure') 94 | system('./configure', @name, *@additional_args) 95 | else 96 | UI.warn 'Template does not have a configure file.' 97 | end 98 | end 99 | end 100 | end 101 | 102 | # Runs the template configuration utilities. 103 | # 104 | # @return [void] 105 | # 106 | def print_info 107 | UI.puts "\nTo learn more about the template see `#{template_repo_url}`." 108 | UI.puts "To learn more about creating a new pod, see `#{CREATE_NEW_POD_INFO_URL}`." 109 | end 110 | 111 | # Checks if a template URL is given else returns the TEMPLATE_REPO URL 112 | # 113 | # @return String 114 | # 115 | def template_repo_url 116 | @shell_url || TEMPLATE_REPO 117 | end 118 | end 119 | end 120 | end 121 | end 122 | -------------------------------------------------------------------------------- /lib/cocoapods/command/ip/get_with_board.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Command 3 | class Ip < Command 4 | #-----------------------------------------------------------------------# 5 | #Get a IP + shell from repository 6 | #-----------------------------------------------------------------------# 7 | class GetWithBoard < Ip 8 | self.summary = 'Download an IP and shell searched by BOARD and DRIVER.' 9 | 10 | self.description = <<-DESC 11 | Download the IP from a given IP_REPO_NAME. 12 | A IP_REPO_VERSION tag can also be specified. 13 | DESC 14 | 15 | self.arguments = [ 16 | CLAide::Argument.new('IP_REPO_NAME', true), 17 | CLAide::Argument.new('BOARD_NAME', true), 18 | CLAide::Argument.new('DRIVER_NAME', true), 19 | CLAide::Argument.new('IP_REPO_VERSION', false), 20 | ] 21 | 22 | def initialize(argv) 23 | @ip_name = argv.shift_argument 24 | @board_name = argv.shift_argument 25 | @driver_name = argv.shift_argument 26 | @ip_tag = argv.shift_argument 27 | super 28 | end 29 | 30 | def validate! 31 | super 32 | help! 'A url for the IP repo is required.' unless @ip_name 33 | end 34 | 35 | def run 36 | @acc_name = @ip_name 37 | url = repo_from_name(@ip_name, "") 38 | make_project_dir 39 | 40 | @ip_tag = url["tag"] unless @ip_tag 41 | 42 | UI.puts "Get ip #{@ip_name} ~> #{@ip_tag}".green 43 | clone(url["git"], @ip_tag, "#{@acc_name}/#{@ip_name}") 44 | specs_from_ip 45 | 46 | UI.puts "" 47 | UI.puts "Success: IP:#{@ip_name} and Shell:#{@shell_name} are downloaded." 48 | UI.puts "Next, you can use \"hcode ip make #{@acc_name}\" to compile." 49 | end 50 | 51 | private 52 | 53 | #----------------------------------------# 54 | 55 | # !@group Private helpers 56 | 57 | extend Executable 58 | executable :git 59 | 60 | def repo_from_name(name, option) 61 | UI.puts "#{name}#{option}" 62 | begin 63 | set = SourcesManager.fuzzy_search_by_name(name) 64 | set.repo_url 65 | rescue => e 66 | UI.puts "Error: Can not find repo for shell #{name}.".red 67 | nil 68 | end 69 | end 70 | 71 | def repo_from_name_op(name) 72 | begin 73 | set = SourcesManager.fuzzy_search_by_name(name) 74 | json = File.read(set.highest_version_spec_path.to_s) 75 | spec = JSON.parse(json) 76 | if(spec["hardware"]["board"] == @board_name && spec["driver"] == @driver_name) 77 | set.repo_url 78 | end 79 | rescue => e 80 | UI.puts "Error: Can not find repo for shell #{name}.".red 81 | nil 82 | end 83 | end 84 | 85 | def make_project_dir 86 | FileUtils.mkdir_p("#{@acc_name}") 87 | end 88 | 89 | def specs_from_ip 90 | UI.puts "Get specs for #{@ip_name} ~> #{@ip_tag}".green 91 | 92 | #Read hcode.spec and parse JSON 93 | json = File.read("#{@acc_name}/#{@ip_name}/hcode.spec") 94 | spec = JSON.parse(json) 95 | urls = Array.new 96 | 97 | if(spec["type"] == "shell") 98 | UI.puts "The target is a shell project, please use \"hCODE shell get\" command.".red 99 | exit 100 | end 101 | 102 | keys = spec["shell"].keys 103 | 104 | #get shell URLs, if support multiple shells, let user choose one 105 | spec["shell"].each_with_index{|shell, i| 106 | repo_url = repo_from_name_op(keys[i]) 107 | UI.puts "#{i+1} : #{repo_url}" if repo_url != nil 108 | urls.push repo_url if repo_url != nil 109 | } 110 | 111 | 112 | urls.push "No Shell" 113 | 114 | url_index = 0 115 | 116 | if url_index == urls.length - 1 117 | UI.puts "Only IP is donwloaded." 118 | exit 119 | end 120 | 121 | #Get IP configurations for selected shell 122 | @shell_name = keys[url_index] 123 | clone(urls[url_index]["git"], urls[url_index]["tag"], "#{@acc_name}/#{@shell_name}") 124 | end 125 | 126 | def getCompatibleShell(shellname) 127 | json = File.read(File.expand_path("#{Dir.home}/.hcode/compatible_shell.json")) 128 | shells = JSON.parse(json) 129 | return shells[shellname]["compatible_shell"] 130 | end 131 | 132 | # Clones the repo from the remote in the path directory using 133 | # the url, tag of IP repo. 134 | # 135 | # @return [void] 136 | # 137 | def clone(url, tag, dir) 138 | UI.section("Cloning #{url} of tag #{tag}.") do 139 | git! ['clone', url, dir] 140 | Dir.chdir(dir) { git!('checkout', "tags/#{tag}") } if tag 141 | end 142 | end 143 | 144 | # Runs the template configuration utilities. 145 | # 146 | # @return [void] 147 | # 148 | def print_info 149 | UI.puts "\nTo learn more about the ip see `#{@ip_url}`." 150 | end 151 | end 152 | end 153 | end 154 | end 155 | -------------------------------------------------------------------------------- /lib/cocoapods/command/ip/install.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Command 3 | class Ip < Command 4 | #-----------------------------------------------------------------------# 5 | #Install a IP 6 | #-----------------------------------------------------------------------# 7 | class Install < Ip 8 | self.summary = 'Install the IP driver.' 9 | 10 | self.description = <<-DESC 11 | Make the downloaded IP automaticlly. 12 | DESC 13 | 14 | self.arguments = [ 15 | CLAide::Argument.new('IP_REPO_NAME', true), 16 | ] 17 | 18 | def initialize(argv) 19 | @acc_name = argv.shift_argument 20 | super 21 | end 22 | 23 | def validate! 24 | super 25 | help! 'A name for the accelerator folder is required.' unless @acc_name 26 | end 27 | 28 | def run 29 | get_names 30 | system "cd #{@acc_name}/#{@ip_name} && ./make -driver" 31 | end 32 | 33 | def get_names 34 | @ip_name = "" 35 | @shell_name = "" 36 | 37 | require 'find' 38 | bit_file_paths = [] 39 | Find.find(@acc_name) do |path| 40 | bit_file_paths << path if path =~ /.hcode\.spec$/ 41 | end 42 | 43 | if bit_file_paths.length > 0 44 | bit_file_paths.each{|path| 45 | puts path 46 | require 'json' 47 | json = File.read(path) 48 | spec = JSON.parse(json) 49 | name = spec["name"] 50 | 51 | @ip_name = name if name.include?'ip-' 52 | @shell_name = name if name.include?'shell-' 53 | } 54 | if @ip_name == "" || @shell_name == "" 55 | UI.puts "Cannot find IP or Shell project, please check the acclerator project #{acc_name}.".red 56 | exit 57 | end 58 | else 59 | UI.puts "No bitstream is found in folder #{@bitstream_file}.".red 60 | exit 61 | end 62 | end 63 | end 64 | end 65 | end 66 | end -------------------------------------------------------------------------------- /lib/cocoapods/command/list.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Command 3 | class List < Command 4 | self.summary = 'List hCODE projects' 5 | self.description = 'Lists all available hCODE projects.' 6 | 7 | def self.options 8 | [ 9 | ['--update', 'Run `hcode repo update` before listing'], 10 | ['--stats', 'Show additional stats (like GitHub watchers and forks)'], 11 | ].concat(super) 12 | end 13 | 14 | def initialize(argv) 15 | @update = argv.flag?('update') 16 | @stats = argv.flag?('stats') 17 | super 18 | end 19 | 20 | def run 21 | update_if_necessary! 22 | 23 | sets = SourcesManager.aggregate.all_sets 24 | sets.each { |set| UI.pod(set, :name_and_version) } 25 | UI.puts "\n#{sets.count} hcode projects were found" 26 | end 27 | 28 | def update_if_necessary! 29 | if @update && config.verbose? 30 | UI.section("\nUpdating Spec Repositories\n".yellow) do 31 | Repo.new(ARGV.new(['update'])).run 32 | end 33 | end 34 | end 35 | 36 | #-----------------------------------------------------------------------# 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/cocoapods/command/outdated.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Command 3 | class Outdated < Command 4 | self.summary = 'Show outdated project dependencies' 5 | 6 | self.description = <<-DESC 7 | Shows the outdated pods in the current Podfile.lock, but only those from 8 | spec repos, not those from local/external sources or `:head` versions. 9 | DESC 10 | 11 | # Run the command 12 | # 13 | # @todo the command report new dependencies added to the Podfile as 14 | # updates. 15 | # 16 | # @todo fix. 17 | # 18 | def run 19 | if updates.empty? 20 | UI.puts 'No pod updates are available.'.yellow 21 | else 22 | UI.section 'The following pod updates are available:' do 23 | updates.each do |(name, from_version, matching_version, to_version)| 24 | UI.puts "- #{name} #{from_version} -> #{matching_version} " \ 25 | "(latest version #{to_version})" 26 | end 27 | end 28 | end 29 | 30 | if deprecated_pods.any? 31 | UI.section 'The following pods are deprecated:' do 32 | deprecated_pods.each do |spec| 33 | if spec.deprecated_in_favor_of 34 | UI.puts "- #{spec.name}" \ 35 | " (in favor of #{spec.deprecated_in_favor_of})" 36 | else 37 | UI.puts "- #{spec.name}" 38 | end 39 | end 40 | end 41 | end 42 | end 43 | 44 | private 45 | 46 | def analyzer 47 | @analyzer ||= begin 48 | verify_podfile_exists! 49 | Installer::Analyzer.new(config.sandbox, config.podfile, config.lockfile) 50 | end 51 | end 52 | 53 | def updates 54 | @updates ||= begin 55 | ensure_external_podspecs_present! 56 | spec_sets.map do |set| 57 | spec = set.specification 58 | source_version = set.versions.first 59 | pod_name = spec.root.name 60 | lockfile_version = lockfile.version(pod_name) 61 | if source_version > lockfile_version 62 | matching_spec = unlocked_pods.find { |s| s.name == pod_name } 63 | matching_version = 64 | matching_spec ? matching_spec.version : '(unused)' 65 | [pod_name, lockfile_version, matching_version, source_version] 66 | end 67 | end.compact.uniq 68 | end 69 | end 70 | 71 | def unlocked_pods 72 | @unlocked_pods ||= begin 73 | pods = [] 74 | UI.titled_section('Analyzing dependencies') do 75 | pods = Installer::Analyzer.new(config.sandbox, config.podfile). 76 | analyze(false). 77 | specs_by_target.values.flatten.uniq 78 | end 79 | pods 80 | end 81 | end 82 | 83 | def deprecated_pods 84 | @deprecated_pods ||= begin 85 | spec_sets.map(&:specification).select do |spec| 86 | spec.deprecated || spec.deprecated_in_favor_of 87 | end.compact.uniq 88 | end 89 | end 90 | 91 | def spec_sets 92 | @spec_sets ||= begin 93 | analyzer.send(:update_repositories) unless config.skip_repo_update? 94 | aggregate = Source::Aggregate.new(analyzer.sources.map(&:repo)) 95 | installed_pods.map do |pod_name| 96 | aggregate.search(Dependency.new(pod_name)) 97 | end.compact.uniq 98 | end 99 | end 100 | 101 | def installed_pods 102 | @installed_pods ||= begin 103 | verify_podfile_exists! 104 | 105 | lockfile.pod_names 106 | end 107 | end 108 | 109 | def lockfile 110 | @lockfile ||= begin 111 | verify_lockfile_exists! 112 | config.lockfile 113 | end 114 | end 115 | 116 | def ensure_external_podspecs_present! 117 | return unless config.podfile 118 | config.podfile.dependencies.each do |dep| 119 | next if dep.external_source.nil? 120 | unless config.sandbox.specification(dep.root_name) 121 | raise Informative, 'You must run `pod install` first to ensure that the ' \ 122 | "podspec for `#{dep.root_name}` has been fetched." 123 | end 124 | end 125 | end 126 | end 127 | end 128 | end 129 | -------------------------------------------------------------------------------- /lib/cocoapods/command/project.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Command 3 | # Provides support for commands to take a user-specified `project directory` 4 | # 5 | module ProjectDirectory 6 | module Options 7 | def options 8 | [ 9 | ['--project-directory=/project/dir/', 'The path to the root of the project directory'], 10 | ].concat(super) 11 | end 12 | end 13 | 14 | def self.included(base) 15 | base.extend(Options) 16 | end 17 | 18 | def initialize(argv) 19 | if project_directory = argv.option('project-directory') 20 | @project_directory = Pathname.new(project_directory).expand_path 21 | end 22 | config.installation_root = @project_directory 23 | super 24 | end 25 | 26 | def validate! 27 | super 28 | if @project_directory && !@project_directory.directory? 29 | raise Informative, 30 | "`#{@project_directory}` is not a valid directory." 31 | end 32 | end 33 | end 34 | 35 | # Provides support for the common behaviour of the `install` and `update` 36 | # commands. 37 | # 38 | module Project 39 | module Options 40 | def options 41 | [ 42 | ['--no-repo-update', 'Skip running `pod repo update` before install'], 43 | ].concat(super) 44 | end 45 | end 46 | 47 | def self.included(base) 48 | base.extend Options 49 | end 50 | 51 | def initialize(argv) 52 | config.skip_repo_update = !argv.flag?('repo-update', !config.skip_repo_update) 53 | super 54 | end 55 | 56 | # Runs the installer. 57 | # 58 | # @param [Hash, Boolean, nil] update 59 | # Pods that have been requested to be updated or true if all Pods 60 | # should be updated 61 | # 62 | # @return [void] 63 | # 64 | def run_install_with_update(update) 65 | installer = Installer.new(config.sandbox, config.podfile, config.lockfile) 66 | installer.update = update 67 | installer.install! 68 | end 69 | end 70 | 71 | #-------------------------------------------------------------------------# 72 | 73 | class Install < Command 74 | include Project 75 | 76 | self.summary = 'Install shell, ip, or app defined in hcode.conf file.' 77 | self.description = <<-DESC 78 | Downloads all shell, ip, or app defined in `hcode.conf`. 79 | DESC 80 | 81 | def run 82 | verify_podfile_exists! 83 | run_install_with_update(false) 84 | end 85 | end 86 | 87 | #-------------------------------------------------------------------------# 88 | 89 | 90 | end 91 | end 92 | -------------------------------------------------------------------------------- /lib/cocoapods/command/repo.rb: -------------------------------------------------------------------------------- 1 | require 'fileutils' 2 | require 'cocoapods/command/repo/add' 3 | require 'cocoapods/command/repo/lint' 4 | require 'cocoapods/command/repo/list' 5 | require 'cocoapods/command/repo/push' 6 | require 'cocoapods/command/repo/remove' 7 | require 'cocoapods/command/repo/update' 8 | 9 | module Pod 10 | class Command 11 | class Repo < Command 12 | self.abstract_command = true 13 | 14 | # @todo should not show a usage banner! 15 | # 16 | self.summary = 'Manage spec-repositories' 17 | self.default_subcommand = 'list' 18 | 19 | #-----------------------------------------------------------------------# 20 | 21 | extend Executable 22 | executable :git 23 | 24 | def dir 25 | config.repos_dir + @name 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/cocoapods/command/repo/add.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Command 3 | class Repo < Command 4 | class Add < Repo 5 | self.summary = 'Add a spec repo' 6 | 7 | self.description = <<-DESC 8 | Clones `URL` in the local spec-repos directory at `~/.hcode/repos/`. The 9 | remote can later be referred to by `NAME`. 10 | DESC 11 | 12 | self.arguments = [ 13 | CLAide::Argument.new('NAME', true), 14 | CLAide::Argument.new('URL', true), 15 | CLAide::Argument.new('BRANCH', false), 16 | ] 17 | 18 | def self.options 19 | [ 20 | ['--shallow', 'Create a shallow clone (fast clone, but no push capabilities)'], 21 | ].concat(super) 22 | end 23 | 24 | def initialize(argv) 25 | @shallow = argv.flag?('shallow', false) 26 | @name = argv.shift_argument 27 | @url = argv.shift_argument 28 | @branch = argv.shift_argument 29 | super 30 | end 31 | 32 | def validate! 33 | super 34 | unless @name && @url 35 | help! 'Adding a repo needs a `NAME` and a `URL`.' 36 | end 37 | end 38 | 39 | def run 40 | prefix = @shallow ? 'Creating shallow clone of' : 'Cloning' 41 | UI.section("#{prefix} spec repo `#{@name}` from `#{@url}`#{" (branch `#{@branch}`)" if @branch}") do 42 | create_repos_dir 43 | clone_repo 44 | checkout_branch 45 | SourcesManager.check_version_information(dir) 46 | end 47 | end 48 | 49 | private 50 | 51 | # Creates the repos directory specified in the configuration by 52 | # `config.repos_dir`. 53 | # 54 | # @return [void] 55 | # 56 | # @raise If the directory cannot be created due to a system error. 57 | # 58 | def create_repos_dir 59 | config.repos_dir.mkpath 60 | rescue => e 61 | raise Informative, "Could not create '#{config.repos_dir}', the CocoaPods repo cache directory.\n" \ 62 | "#{e.class.name}: #{e.message}" 63 | end 64 | 65 | # Clones the git spec-repo according to parameters passed to the 66 | # command. 67 | # 68 | # @return [void] 69 | # 70 | def clone_repo 71 | Dir.chdir(config.repos_dir) do 72 | command = ['clone', @url, @name] 73 | command << '--depth=1' if @shallow 74 | git!(command) 75 | end 76 | end 77 | 78 | # Checks out the branch of the git spec-repo if provided. 79 | # 80 | # @return [void] 81 | # 82 | def checkout_branch 83 | Dir.chdir(dir) { git!('checkout', @branch) } if @branch 84 | end 85 | end 86 | end 87 | end 88 | end 89 | -------------------------------------------------------------------------------- /lib/cocoapods/command/repo/lint.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Command 3 | class Repo < Command 4 | class Lint < Repo 5 | self.summary = 'Validates all specs in a repo' 6 | 7 | self.description = <<-DESC 8 | Lints the spec-repo `NAME`. If a directory is provided it is assumed 9 | to be the root of a repo. Finally, if `NAME` is not provided this 10 | will lint all the spec-repos known to CocoaPods. 11 | DESC 12 | 13 | self.arguments = [ 14 | CLAide::Argument.new(%w(NAME DIRECTORY), false), 15 | ] 16 | 17 | def self.options 18 | [ 19 | ['--only-errors', 'Lint presents only the errors'], 20 | ].concat(super) 21 | end 22 | 23 | def initialize(argv) 24 | @name = argv.shift_argument 25 | @only_errors = argv.flag?('only-errors') 26 | super 27 | end 28 | 29 | # Run the command 30 | # 31 | # @todo Part of this logic needs to be ported to cocoapods-core so web 32 | # services can validate the repo. 33 | # 34 | # @todo add UI.print and enable print statements again. 35 | # 36 | def run 37 | sources = if @name 38 | if File.exist?(@name) 39 | [Pathname.new(@name)] 40 | else 41 | SourcesManager.sources([@name]).map(&:repo) 42 | end 43 | else 44 | SourcesManager.aggregate.sources.map(&:repo) 45 | end 46 | 47 | sources.each do |source| 48 | SourcesManager.check_version_information(source) 49 | UI.puts "\nLinting spec repo `#{source.basename}`\n".yellow 50 | 51 | validator = Source::HealthReporter.new(source) 52 | validator.pre_check do |_name, _version| 53 | UI.print '.' 54 | end 55 | report = validator.analyze 56 | UI.puts 57 | UI.puts 58 | 59 | report.pods_by_warning.each do |message, versions_by_name| 60 | UI.puts "-> #{message}".yellow 61 | versions_by_name.each { |name, versions| UI.puts " - #{name} (#{versions * ', '})" } 62 | UI.puts 63 | end 64 | 65 | report.pods_by_error.each do |message, versions_by_name| 66 | UI.puts "-> #{message}".red 67 | versions_by_name.each { |name, versions| UI.puts " - #{name} (#{versions * ', '})" } 68 | UI.puts 69 | end 70 | 71 | UI.puts "Analyzed #{report.analyzed_paths.count} podspecs files.\n\n" 72 | if report.pods_by_error.count.zero? 73 | UI.puts 'All the specs passed validation.'.green << "\n\n" 74 | else 75 | raise Informative, "#{report.pods_by_error.count} podspecs failed validation." 76 | end 77 | end 78 | end 79 | end 80 | end 81 | end 82 | end 83 | -------------------------------------------------------------------------------- /lib/cocoapods/command/repo/list.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Command 3 | class Repo < Command 4 | class List < Repo 5 | self.summary = 'List repos' 6 | 7 | self.description = <<-DESC 8 | List the repos from the local spec-repos directory at `~/.hcode/repos/.` 9 | DESC 10 | 11 | def self.options 12 | [['--count-only', 'Show the total number of repos']].concat(super) 13 | end 14 | 15 | def initialize(argv) 16 | @count_only = argv.flag?('count-only') 17 | super 18 | end 19 | 20 | # @output Examples: 21 | # 22 | # master 23 | # - type: git (master) 24 | # - URL: https://github.com/CocoaPods/Specs.git 25 | # - path: /Users/lascorbe/.cocoapods/repos/master 26 | # 27 | # test 28 | # - type: local copy 29 | # - URL: file:///Users/lascorbe/.cocoapods/repos/test 30 | # - path: /Users/lascorbe/.cocoapods/repos/test 31 | # 32 | def run 33 | sources = SourcesManager.all 34 | print_sources(sources) unless @count_only 35 | print_count_of_sources(sources) 36 | end 37 | 38 | private 39 | 40 | # Pretty-prints the source at the given path. 41 | # 42 | # @param [Source] source 43 | # The source repository to be printed. 44 | # 45 | # @return [void] 46 | # 47 | def print_source(source) 48 | if SourcesManager.git_repo?(source.repo) 49 | Dir.chdir(source.repo) do 50 | branch_name = `git name-rev --name-only HEAD 2>/dev/null`.strip 51 | branch_name = 'unknown' if branch_name.empty? 52 | UI.puts "- Type: git (#{branch_name})" 53 | end 54 | else 55 | UI.puts '- Type: local' 56 | end 57 | 58 | UI.puts "- URL: #{source.url}" 59 | UI.puts "- Path: #{source.repo}" 60 | end 61 | 62 | # Pretty-prints the given sources. 63 | # 64 | # @param [Array] sources 65 | # The sources that should be printed. 66 | # 67 | # @return [void] 68 | # 69 | def print_sources(sources) 70 | sources.each do |source| 71 | UI.title source.name do 72 | print_source(source) 73 | end 74 | end 75 | UI.puts "\n" 76 | end 77 | 78 | # Pretty-prints the number of sources. 79 | # 80 | # @param [Array] sources 81 | # The sources whose count should be printed. 82 | # 83 | # @return [void] 84 | # 85 | def print_count_of_sources(sources) 86 | number_of_repos = sources.length 87 | repo_string = number_of_repos != 1 ? 'repos' : 'repo' 88 | UI.puts "#{number_of_repos} #{repo_string}".green 89 | end 90 | end 91 | end 92 | end 93 | end 94 | -------------------------------------------------------------------------------- /lib/cocoapods/command/repo/remove.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Command 3 | class Repo < Command 4 | class Remove < Repo 5 | self.summary = 'Remove a spec repo' 6 | 7 | self.description = <<-DESC 8 | Deletes the remote named `NAME` from the local spec-repos directory at `~/.cocoapods/repos/.` 9 | DESC 10 | 11 | self.arguments = [ 12 | CLAide::Argument.new('NAME', true), 13 | ] 14 | 15 | def initialize(argv) 16 | @name = argv.shift_argument 17 | super 18 | end 19 | 20 | def validate! 21 | super 22 | help! 'Deleting a repo needs a `NAME`.' unless @name 23 | help! "repo #{@name} does not exist" unless File.directory?(dir) 24 | help! "You do not have permission to delete the #{@name} repository." \ 25 | 'Perhaps try prefixing this command with sudo.' unless File.writable?(dir) 26 | end 27 | 28 | def run 29 | UI.section("Removing spec repo `#{@name}`") do 30 | FileUtils.rm_rf(dir) 31 | end 32 | end 33 | end 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/cocoapods/command/repo/update.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Command 3 | class Repo < Command 4 | class Update < Repo 5 | self.summary = 'Update a spec repo' 6 | 7 | self.description = <<-DESC 8 | Updates the local clone of the spec-repo `NAME`. If `NAME` is omitted 9 | this will update all spec-repos in `~/.hcode/repos`. 10 | DESC 11 | 12 | self.arguments = [ 13 | CLAide::Argument.new('NAME', false), 14 | ] 15 | 16 | def initialize(argv) 17 | @name = argv.shift_argument 18 | super 19 | end 20 | 21 | def run 22 | SourcesManager.update(@name, true) 23 | end 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/cocoapods/command/search.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Command 3 | class Search < Command 4 | self.summary = 'Search for hCODE projects.' 5 | 6 | self.description = <<-DESC 7 | Searches for pods, ignoring case, whose name matches `QUERY`. If the 8 | `--full` option is specified, this will also search in the summary and 9 | description of the pods. 10 | DESC 11 | 12 | self.arguments = [ 13 | CLAide::Argument.new('QUERY', true), 14 | ] 15 | 16 | def self.options 17 | [ 18 | ['--regex', 'Interpret the `QUERY` as a regular expression'], 19 | ['--full', 'Search by name, summary, and description'], 20 | ['--stats', 'Show additional stats (like GitHub watchers and forks)'], 21 | ['--ios', 'Restricts the search to Pods supported on iOS'], 22 | ['--osx', 'Restricts the search to Pods supported on OS X'], 23 | ['--watchos', 'Restricts the search to Pods supported on Watch OS'], 24 | ['--web', 'Searches on cocoapods.org'], 25 | ].concat(super.reject { |option, _| option == '--silent' }) 26 | end 27 | 28 | def initialize(argv) 29 | @use_regex = argv.flag?('regex') 30 | @full_text_search = argv.flag?('full') 31 | @stats = argv.flag?('stats') 32 | @supported_on_ios = argv.flag?('ios') 33 | @supported_on_osx = argv.flag?('osx') 34 | @supported_on_watchos = argv.flag?('watchos') 35 | @web = argv.flag?('web') 36 | @query = argv.arguments! unless argv.arguments.empty? 37 | config.silent = false 38 | super 39 | end 40 | 41 | def validate! 42 | super 43 | help! 'A search query is required.' unless @query 44 | 45 | unless @web || !@use_regex 46 | begin 47 | /#{@query.join(' ').strip}/ 48 | rescue RegexpError 49 | help! 'A valid regular expression is required.' 50 | end 51 | end 52 | end 53 | 54 | def run 55 | ensure_master_spec_repo_exists! 56 | local_search 57 | end 58 | 59 | 60 | def local_search 61 | query_regex = @query.join(' ').strip 62 | query_regex = Regexp.escape(query_regex) unless @use_regex 63 | 64 | sets = SourcesManager.search_by_name(query_regex, @full_text_search) 65 | if @supported_on_ios 66 | sets.reject! { |set| !set.specification.available_platforms.map(&:name).include?(:ios) } 67 | end 68 | if @supported_on_osx 69 | sets.reject! { |set| !set.specification.available_platforms.map(&:name).include?(:osx) } 70 | end 71 | if @supported_on_watchos 72 | sets.reject! { |set| !set.specification.available_platforms.map(&:name).include?(:watchos) } 73 | end 74 | 75 | sets.each do |set| 76 | begin 77 | if @stats 78 | UI.pod(set, :stats) 79 | else 80 | UI.pod(set, :normal) 81 | end 82 | rescue DSLError 83 | UI.warn "Skipping `#{set.name}` because the podspec contains errors." 84 | end 85 | end 86 | end 87 | end 88 | end 89 | end 90 | -------------------------------------------------------------------------------- /lib/cocoapods/command/setup.rb: -------------------------------------------------------------------------------- 1 | require 'fileutils' 2 | 3 | module Pod 4 | class Command 5 | class Setup < Command 6 | self.summary = 'Setup the hCODE development kit environment' 7 | 8 | self.description = <<-DESC 9 | Creates a directory at `~/.hcode/repos` which will hold your spec-repos. 10 | This is where it will create a clone of the public `master` spec-repo from: 11 | 12 | https://github.com/hCODE-FPGA/Specs 13 | 14 | If the clone already exists, it will ensure that it is up-to-date. 15 | DESC 16 | 17 | def self.options 18 | [ 19 | ['--no-shallow', 'Clone full history so push will work'], 20 | ].concat(super) 21 | end 22 | 23 | extend Executable 24 | executable :git 25 | 26 | def initialize(argv) 27 | @shallow = argv.flag?('shallow', true) 28 | super 29 | end 30 | 31 | def run 32 | UI.section 'Setting up hCODE master repo' do 33 | if master_repo_dir.exist? 34 | set_master_repo_url 35 | set_master_repo_branch 36 | update_master_repo 37 | else 38 | add_master_repo 39 | end 40 | gene_compatible_shell 41 | end 42 | system "mkdir #{Dir.home}/.hcode/temp" 43 | UI.puts 'Setup completed'.green 44 | end 45 | 46 | #--------------------------------------# 47 | 48 | # @!group Setup steps 49 | 50 | # Sets the url of the master repo according to whether it is push. 51 | # 52 | # @return [void] 53 | # 54 | def set_master_repo_url 55 | Dir.chdir(master_repo_dir) do 56 | git('remote', 'set-url', 'origin', url) 57 | end 58 | end 59 | 60 | # Adds the master repo from the remote. 61 | # 62 | # @return [void] 63 | # 64 | def add_master_repo 65 | cmd = ['master', url, 'master'] 66 | cmd << '--shallow' if @shallow 67 | Repo::Add.parse(cmd).run 68 | end 69 | 70 | # Updates the master repo against the remote. 71 | # 72 | # @return [void] 73 | # 74 | def update_master_repo 75 | SourcesManager.update('master', true) 76 | end 77 | 78 | # Sets the repo to the master branch. 79 | # 80 | # @note This is not needed anymore as it was used for CocoaPods 0.6 81 | # release candidates. 82 | # 83 | # @return [void] 84 | # 85 | def set_master_repo_branch 86 | Dir.chdir(master_repo_dir) do 87 | git %w(checkout master) 88 | end 89 | end 90 | 91 | #--------------------------------------# 92 | 93 | # @!group Private helpers 94 | 95 | # @return [String] the url to use according to whether push mode should 96 | # be enabled. 97 | # 98 | def url 99 | self.class.read_only_url 100 | end 101 | 102 | # @return [String] the read only url of the master repo. 103 | # 104 | def self.read_only_url 105 | 'https://github.com/hCODE-FPGA/Specs.git' 106 | end 107 | 108 | # @return [Pathname] the directory of the master repo. 109 | # 110 | def master_repo_dir 111 | SourcesManager.master_repo_dir 112 | end 113 | 114 | # Analysis SPEC files and generate compatible_shell list file under ~/.hcode 115 | def gene_compatible_shell 116 | require 'find' 117 | require 'json' 118 | shells = {} 119 | #Read shells' info from SPEC files 120 | Find.find(File.expand_path("#{Dir.home}/.hcode/repos/")) do |path| 121 | if path =~ /.*hcode\.spec$/ 122 | json = File.read(path) 123 | spec = JSON.parse(json) 124 | if(spec["type"] == "shell") 125 | shell = {} 126 | shell[:name] = spec["name"] 127 | shell[:compatible_shell] = Hash.new 128 | if (spec["compatible_shell"] != nil) 129 | spec["compatible_shell"].each{|k, v| 130 | shell[:compatible_shell][k] = v 131 | } 132 | end 133 | shells[shell[:name]] = shell 134 | end 135 | end 136 | end 137 | 138 | shells.each{|k_i,v_i| 139 | v_i[:compatible_shell].each{|k_j, v_j| 140 | if(shells[k_j] != nil) 141 | shells[k_j][:compatible_shell][k_i] = v_j 142 | else 143 | puts "No shell exist: #{k_j}" 144 | end 145 | } 146 | } 147 | 148 | File.open(File.expand_path("#{Dir.home}/.hcode/compatible_shell.json"), 'w') { |fo| 149 | fo.puts shells.to_json 150 | } 151 | end 152 | end 153 | end 154 | end 155 | -------------------------------------------------------------------------------- /lib/cocoapods/command/shell.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods/command/shell/get' 2 | 3 | module Pod 4 | class Command 5 | class Shell < Command 6 | self.abstract_command = true 7 | self.summary = 'Develop with hardware Shell' 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/cocoapods/command/shell/get.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Command 3 | class Shell < Command 4 | #-----------------------------------------------------------------------# 5 | #Get a IP + shell from repository 6 | #-----------------------------------------------------------------------# 7 | class Get < Shell 8 | self.summary = 'Download a shell.' 9 | 10 | self.description = <<-DESC 11 | Download the Shell from a given SHELL_REPO_NAME. 12 | A SHELL_REPO_VERSION tag can also be specified. 13 | DESC 14 | 15 | self.arguments = [ 16 | CLAide::Argument.new('SHELL_REPO_NAME', true), 17 | CLAide::Argument.new('SHELL_REPO_VERSION', false), 18 | ] 19 | 20 | def initialize(argv) 21 | @shell_name = argv.shift_argument 22 | @shell_tag = argv.shift_argument 23 | super 24 | end 25 | 26 | def validate! 27 | super 28 | help! 'A url for the Shell repo is required.' unless @shell_name 29 | end 30 | 31 | def run 32 | url = repo_from_name(@shell_name) 33 | 34 | @shell_tag = url["tag"] unless @shell_tag 35 | 36 | UI.puts "Get shell #{@shell_name} ~> #{@shell_tag}".green 37 | clone(url["git"], @shell_tag, "#{@shell_name}") 38 | 39 | UI.puts "" 40 | UI.puts "Success: Shell:#{@shell_name} is downloaded." 41 | end 42 | 43 | private 44 | 45 | #----------------------------------------# 46 | 47 | # !@group Private helpers 48 | 49 | extend Executable 50 | executable :git 51 | 52 | def repo_from_name(name) 53 | UI.puts name 54 | begin 55 | set = SourcesManager.fuzzy_search_by_name(name) 56 | set.repo_url 57 | rescue => e 58 | UI.puts "Error: Can not find repo for shell #{name}.".red 59 | nil 60 | end 61 | end 62 | 63 | # Clones the repo from the remote in the path directory using 64 | # the url, tag of IP repo. 65 | # 66 | # @return [void] 67 | # 68 | def clone(url, tag, dir) 69 | UI.section("Cloning #{url} of tag #{tag}.") do 70 | git! ['clone', url, dir] 71 | Dir.chdir(dir) { git!('checkout', "tags/#{tag}") } if tag 72 | end 73 | end 74 | end 75 | end 76 | end 77 | end -------------------------------------------------------------------------------- /lib/cocoapods/command/spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'active_support/core_ext/string/inflections' 4 | require 'cocoapods/command/spec/create' 5 | require 'cocoapods/command/spec/lint' 6 | require 'cocoapods/command/spec/which' 7 | require 'cocoapods/command/spec/cat' 8 | require 'cocoapods/command/spec/edit' 9 | 10 | module Pod 11 | class Command 12 | class Spec < Command 13 | self.abstract_command = true 14 | self.summary = 'Manage hCODE specs' 15 | 16 | #-----------------------------------------------------------------------# 17 | 18 | # @todo some of the following methods can probably move to one of the 19 | # subclasses. 20 | 21 | private 22 | 23 | # @param [String] query the regular expression string to validate 24 | # 25 | # @raise if the query is not a valid regular expression 26 | # 27 | def validate_regex!(query) 28 | /#{query}/ 29 | rescue RegexpError 30 | help! 'A valid regular expression is required.' 31 | end 32 | 33 | # @param [String] spec 34 | # The name of the specification. 35 | # 36 | # @param [Bool] show_all 37 | # Whether the paths for all the versions should be returned or 38 | # only the one for the last version. 39 | # 40 | # @return [Pathname] the absolute path or paths of the given podspec 41 | # 42 | def get_path_of_spec(spec, show_all = false) 43 | sets = SourcesManager.search_by_name(spec) 44 | 45 | if sets.count == 1 46 | set = sets.first 47 | elsif sets.map(&:name).include?(spec) 48 | set = sets.find { |s| s.name == spec } 49 | else 50 | names = sets.map(&:name) * ', ' 51 | raise Informative, "More than one spec found for '#{spec}':\n#{names}" 52 | end 53 | 54 | unless show_all 55 | best_spec, spec_source = spec_and_source_from_set(set) 56 | return pathname_from_spec(best_spec, spec_source) 57 | end 58 | 59 | all_paths_from_set(set) 60 | end 61 | 62 | # @return [Pathname] the absolute path of the given spec and source 63 | # 64 | def pathname_from_spec(spec, _source) 65 | Pathname(spec.defined_in_file) 66 | end 67 | 68 | # @return [String] of spec paths one on each line 69 | # 70 | def all_paths_from_set(set) 71 | paths = '' 72 | 73 | sources = set.sources 74 | 75 | sources.each do |source| 76 | versions = source.versions(set.name) 77 | 78 | versions.each do |version| 79 | spec = source.specification(set.name, version) 80 | paths += "#{pathname_from_spec(spec, source)}\n" 81 | end 82 | end 83 | 84 | paths 85 | end 86 | 87 | # @return [Specification, Source] the highest known specification with it's source of the given 88 | # set. 89 | # 90 | def spec_and_source_from_set(set) 91 | sources = set.sources 92 | 93 | best_source = best_version = nil 94 | sources.each do |source| 95 | versions = source.versions(set.name) 96 | versions.each do |version| 97 | if !best_version || version > best_version 98 | best_source = source 99 | best_version = version 100 | end 101 | end 102 | end 103 | 104 | if !best_source || !best_version 105 | raise Informative, "Unable to locate highest known specification for `#{set.name}'" 106 | end 107 | 108 | [best_source.specification(set.name, best_version), best_source] 109 | end 110 | end 111 | end 112 | end 113 | -------------------------------------------------------------------------------- /lib/cocoapods/command/spec/cat.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Command 3 | class Spec < Command 4 | class Cat < Spec 5 | self.summary = 'Prints a spec file' 6 | 7 | self.description = <<-DESC 8 | Prints the content of the podspec(s) whose name matches `QUERY` to standard output. 9 | DESC 10 | 11 | self.arguments = [ 12 | CLAide::Argument.new('QUERY', false), 13 | ] 14 | 15 | def self.options 16 | [ 17 | ['--regex', 'Interpret the `QUERY` as a regular expression'], 18 | ['--show-all', 'Pick from all versions of the given podspec'], 19 | ].concat(super) 20 | end 21 | 22 | def initialize(argv) 23 | @use_regex = argv.flag?('regex') 24 | @show_all = argv.flag?('show-all') 25 | @query = argv.shift_argument 26 | @query = @query.gsub('.spec', '') unless @query.nil? 27 | super 28 | end 29 | 30 | def validate! 31 | super 32 | help! 'A podspec name is required.' unless @query 33 | validate_regex!(@query) if @use_regex 34 | end 35 | 36 | def run 37 | query = @use_regex ? @query : Regexp.escape(@query) 38 | filepath = if @show_all 39 | specs = get_path_of_spec(query, @show_all).split(/\n/) 40 | index = UI.choose_from_array(specs, "Which spec would you like to print [1-#{specs.count}]? ") 41 | specs[index] 42 | else 43 | get_path_of_spec(query) 44 | end 45 | 46 | UI.puts File.read(filepath) 47 | end 48 | end 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /lib/cocoapods/command/spec/edit.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Command 3 | class Spec < Command 4 | class Edit < Spec 5 | self.summary = 'Edit a spec file' 6 | 7 | self.description = <<-DESC 8 | Opens the podspec matching `QUERY` to be edited. 9 | DESC 10 | 11 | self.arguments = [ 12 | CLAide::Argument.new('QUERY', false), 13 | ] 14 | 15 | def self.options 16 | [ 17 | ['--regex', 'Interpret the `QUERY` as a regular expression'], 18 | ['--show-all', 'Pick from all versions of the given podspec'], 19 | ].concat(super) 20 | end 21 | 22 | def initialize(argv) 23 | @use_regex = argv.flag?('regex') 24 | @show_all = argv.flag?('show-all') 25 | @query = argv.shift_argument 26 | @query = @query.gsub('.podspec', '') unless @query.nil? 27 | super 28 | end 29 | 30 | def validate! 31 | super 32 | help! 'A podspec name is required.' unless @query 33 | validate_regex!(@query) if @use_regex 34 | end 35 | 36 | def run 37 | query = @use_regex ? @query : Regexp.escape(@query) 38 | if @show_all 39 | specs = get_path_of_spec(query, @show_all).split(/\n/) 40 | message = "Which spec would you like to edit [1-#{specs.count}]? " 41 | index = UI.choose_from_array(specs, message) 42 | filepath = specs[index] 43 | else 44 | filepath = get_path_of_spec(query) 45 | end 46 | 47 | exec_editor(filepath.to_s) if File.exist? filepath 48 | raise Informative, "#{filepath} doesn't exist." 49 | end 50 | 51 | def which_editor 52 | editor = ENV['EDITOR'] 53 | # If an editor wasn't set, try to pick a sane default 54 | return editor unless editor.nil? 55 | 56 | editors = [ 57 | # Find Sublime Text 2 58 | 'subl', 59 | # Find Textmate 60 | 'mate', 61 | # Find BBEdit / TextWrangler 62 | 'edit', 63 | # Find Atom 64 | 'atom', 65 | # Default to vim 66 | 'vim', 67 | ] 68 | editor = editors.find { |e| Pod::Executable.which(e) } 69 | return editor if editor 70 | 71 | raise Informative, "Failed to open editor. Set your 'EDITOR' environment variable." 72 | end 73 | 74 | def exec_editor(*args) 75 | return if args.to_s.empty? 76 | safe_exec(which_editor, *args) 77 | end 78 | 79 | def safe_exec(cmd, *args) 80 | # This buys us proper argument quoting and evaluation 81 | # of environment variables in the cmd parameter. 82 | exec('/bin/sh', '-i', '-c', cmd + ' "$@"', '--', *args) 83 | end 84 | end 85 | end 86 | end 87 | end 88 | -------------------------------------------------------------------------------- /lib/cocoapods/command/spec/env_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../../spec_helper', __FILE__) 2 | 3 | module Pod 4 | describe Command::Env do 5 | describe 'In general' do 6 | before do 7 | @report = Command::Env 8 | end 9 | 10 | it 'returns a well-structured environment report' do 11 | expected = <<-EOS 12 | 13 | ### Stack 14 | 15 | ``` 16 | CocoaPods : #{Pod::VERSION} 17 | Ruby : #{RUBY_DESCRIPTION} 18 | RubyGems : #{Gem::VERSION} 19 | Host : :host_information 20 | Xcode : :xcode_information 21 | Git : :git_information 22 | Ruby lib dir : #{RbConfig::CONFIG['libdir']} 23 | Repositories : repo_1 24 | repo_2 25 | ``` 26 | 27 | ### Installation Source 28 | 29 | ``` 30 | Executable Path: /usr/bin/command 31 | ``` 32 | 33 | ### Plugins 34 | 35 | ``` 36 | cocoapods : #{Pod::VERSION} 37 | cocoapods-core : #{Pod::VERSION} 38 | cocoapods-plugins : 1.2.3 39 | ``` 40 | 41 | ### Podfile 42 | 43 | ```ruby 44 | 45 | ``` 46 | EOS 47 | 48 | @report.stubs(:actual_path).returns('/usr/bin/command') 49 | report.should == expected 50 | end 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /lib/cocoapods/command/spec/lint.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Command 3 | class Spec < Command 4 | class Lint < Spec 5 | self.summary = 'Validates a spec file' 6 | 7 | self.description = <<-DESC 8 | Validates `NAME.spec`. If a `DIRECTORY` is provided, it validates 9 | the podspec files found, including subfolders. In case 10 | the argument is omitted, it defaults to the current working dir. 11 | DESC 12 | 13 | self.arguments = [ 14 | CLAide::Argument.new(%w(NAME.spec DIRECTORY), false, true), 15 | ] 16 | 17 | def self.options 18 | [ 19 | ['--quick', 'Lint skips checks that would require to download and build the spec'], 20 | ['--allow-warnings', 'Lint validates even if warnings are present'], 21 | ['--subspec=NAME', 'Lint validates only the given subspec'], 22 | ['--no-subspecs', 'Lint skips validation of subspecs'], 23 | ['--no-clean', 'Lint leaves the build directory intact for inspection'], 24 | ['--fail-fast', 'Lint stops on the first failing platform or subspec'], 25 | ['--use-libraries', 'Lint uses static libraries to install the spec'], 26 | ['--sources=https://github.com/artsy/Specs,master', 'The sources from which to pull dependent pods ' \ 27 | '(defaults to https://github.com/CocoaPods/Specs.git). ' \ 28 | 'Multiple sources must be comma-delimited.'], 29 | ['--private', 'Lint skips checks that apply only to public specs'], 30 | ].concat(super) 31 | end 32 | 33 | def initialize(argv) 34 | @quick = argv.flag?('quick') 35 | @allow_warnings = argv.flag?('allow-warnings') 36 | @clean = argv.flag?('clean', true) 37 | @fail_fast = argv.flag?('fail-fast', false) 38 | @subspecs = argv.flag?('subspecs', true) 39 | @only_subspec = argv.option('subspec') 40 | @use_frameworks = !argv.flag?('use-libraries') 41 | @source_urls = argv.option('sources', 'https://github.com/hCODE-FPGA/Specs.git').split(',') 42 | @private = argv.flag?('private', false) 43 | @podspecs_paths = argv.arguments! 44 | super 45 | end 46 | 47 | def run 48 | UI.puts 49 | failure_reasons = [] 50 | podspecs_to_lint.each do |podspec| 51 | validator = Validator.new(podspec, @source_urls) 52 | validator.quick = @quick 53 | validator.no_clean = !@clean 54 | validator.fail_fast = @fail_fast 55 | validator.allow_warnings = @allow_warnings 56 | validator.no_subspecs = !@subspecs || @only_subspec 57 | validator.only_subspec = @only_subspec 58 | validator.use_frameworks = @use_frameworks 59 | validator.ignore_public_only_results = @private 60 | validator.validate 61 | failure_reasons << validator.failure_reason 62 | 63 | unless @clean 64 | UI.puts "Pods workspace available at `#{validator.validation_dir}/App.xcworkspace` for inspection." 65 | UI.puts 66 | end 67 | end 68 | 69 | count = podspecs_to_lint.count 70 | UI.puts "Analyzed #{count} #{'podspec'.pluralize(count)}.\n\n" 71 | 72 | failure_reasons.compact! 73 | if failure_reasons.empty? 74 | lint_passed_message = count == 1 ? "#{podspecs_to_lint.first.basename} passed validation." : 'All the specs passed validation.' 75 | UI.puts lint_passed_message.green << "\n\n" 76 | else 77 | raise Informative, if count == 1 78 | "The spec did not pass validation, due to #{failure_reasons.first}." 79 | else 80 | "#{failure_reasons.count} out of #{count} specs failed validation." 81 | end 82 | end 83 | podspecs_tmp_dir.rmtree if podspecs_tmp_dir.exist? 84 | end 85 | 86 | private 87 | 88 | def podspecs_to_lint 89 | @podspecs_to_lint ||= begin 90 | files = [] 91 | @podspecs_paths << '.' if @podspecs_paths.empty? 92 | @podspecs_paths.each do |path| 93 | if path =~ %r{https?://} 94 | #require 'cocoapods/open-uri' 95 | #output_path = podspecs_tmp_dir + File.basename(path) 96 | #output_path.dirname.mkpath 97 | #open(path) do |io| 98 | # output_path.open('w') { |f| f << io.read } 99 | #end 100 | #files << output_path 101 | elsif (pathname = Pathname.new(path)).directory? 102 | files += Pathname.glob(pathname + '**/*.spec{.json,}') 103 | raise Informative, 'No specs found in the current directory.' if files.empty? 104 | else 105 | files << (pathname = Pathname.new(path)) 106 | raise Informative, "Unable to find a spec named `#{path}'." unless pathname.exist? && path.include?('.spec') 107 | end 108 | end 109 | files 110 | end 111 | end 112 | 113 | def podspecs_tmp_dir 114 | Pathname.new(Dir.tmpdir) + 'CocoaPods/Lint_podspec' 115 | end 116 | end 117 | end 118 | end 119 | end 120 | -------------------------------------------------------------------------------- /lib/cocoapods/command/spec/which.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Command 3 | class Spec < Command 4 | class Which < Spec 5 | self.summary = 'Prints the path of the given spec' 6 | 7 | self.description = <<-DESC 8 | Prints the path of the .podspec file(s) whose name matches `QUERY` 9 | DESC 10 | 11 | self.arguments = [ 12 | CLAide::Argument.new('QUERY', false), 13 | ] 14 | 15 | def self.options 16 | [ 17 | ['--regex', 'Interpret the `QUERY` as a regular expression'], 18 | ['--show-all', 'Print all versions of the given podspec'], 19 | ].concat(super) 20 | end 21 | 22 | def initialize(argv) 23 | @use_regex = argv.flag?('regex') 24 | @show_all = argv.flag?('show-all') 25 | @query = argv.shift_argument 26 | @query = @query.gsub('.podspec', '') unless @query.nil? 27 | super 28 | end 29 | 30 | def validate! 31 | super 32 | help! 'A podspec name is required.' unless @query 33 | validate_regex!(@query) if @use_regex 34 | end 35 | 36 | def run 37 | query = @use_regex ? @query : Regexp.escape(@query) 38 | UI.puts get_path_of_spec(query, @show_all) 39 | end 40 | end 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/cocoapods/downloader/request.rb: -------------------------------------------------------------------------------- 1 | require 'digest' 2 | 3 | module Pod 4 | module Downloader 5 | # This class represents a download request for a given Pod. 6 | # 7 | class Request 8 | # @return [Specification,Nil] The specification for the pod whose download 9 | # is being requested. 10 | # 11 | attr_reader :spec 12 | 13 | # @return [Boolean] Whether this download request is for a released pod. 14 | # 15 | attr_reader :released_pod 16 | alias_method :released_pod?, :released_pod 17 | 18 | # @return [String] The name of the pod whose dowload is being requested. 19 | # 20 | attr_reader :name 21 | 22 | # @return [Hash] The download parameters for this request. 23 | # 24 | attr_reader :params 25 | 26 | # @return [Boolean] Whether the download request is for a head download. 27 | # 28 | attr_reader :head 29 | alias_method :head?, :head 30 | 31 | # Initialize a new instance 32 | # 33 | # @param [Specification,Nil] spec 34 | # see {#spec} 35 | # 36 | # @param [Boolean] released 37 | # see {#released_pod} 38 | # 39 | # @param [String,Nil] name 40 | # see {#name} 41 | # 42 | # @param [Hash,Nil] params 43 | # see {#params} 44 | # 45 | # @param [Boolean] head 46 | # see {#head} 47 | # 48 | def initialize(spec: nil, released: false, name: nil, params: false, head: false) 49 | @released_pod = released 50 | @spec = spec 51 | @params = spec ? (spec.source && spec.source.dup) : params 52 | @name = spec ? spec.name : name 53 | @head = head 54 | 55 | validate! 56 | end 57 | 58 | # @param [String] name 59 | # the name of the pod being downloaded. 60 | # 61 | # @param [Hash<#to_s, #to_s>] params 62 | # the download parameters of the pod being downloaded. 63 | # 64 | # @param [Specification] spec 65 | # the specification of the pod being downloaded. 66 | # 67 | # @return [String] The slug used to store the files resulting from this 68 | # download request. 69 | # 70 | def slug(name: self.name, params: self.params, spec: self.spec) 71 | checksum = spec && spec.checksum && '-' << spec.checksum[0, 5] 72 | if released_pod? 73 | "Release/#{name}/#{spec.version}#{checksum}" 74 | else 75 | opts = params.to_a.sort_by(&:first).map { |k, v| "#{k}=#{v}" }.join('-') 76 | digest = Digest::MD5.hexdigest(opts) 77 | "External/#{name}/#{digest}#{checksum}" 78 | end 79 | end 80 | 81 | private 82 | 83 | # Validates that the given request is well-formed. 84 | # 85 | # @return [Void] 86 | # 87 | def validate! 88 | raise ArgumentError, 'Requires a name' unless name 89 | raise ArgumentError, 'Must give a spec for a released download request' if released_pod? && !spec 90 | raise ArgumentError, 'Requires a version if released' if released_pod? && !spec.version 91 | raise ArgumentError, 'Requires params' unless params 92 | end 93 | end 94 | end 95 | end 96 | -------------------------------------------------------------------------------- /lib/cocoapods/downloader/response.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | module Downloader 3 | # A response to a download request. 4 | # 5 | # @attr [Pathname] location 6 | # the location where this downloaded pod is stored on disk. 7 | # 8 | # @attr [Specification] spec 9 | # the specification that describes this downloaded pod. 10 | # 11 | # @attr [Hash] checkout_options 12 | # the downloader parameters necessary to recreate this exact download. 13 | # 14 | Response = Struct.new(:location, :spec, :checkout_options) 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/cocoapods/external_sources.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods/external_sources/abstract_external_source' 2 | require 'cocoapods/external_sources/downloader_source' 3 | require 'cocoapods/external_sources/path_source' 4 | require 'cocoapods/external_sources/podspec_source' 5 | 6 | module Pod 7 | # Provides support for initializing the correct concrete class of an external 8 | # source. 9 | # 10 | module ExternalSources 11 | # Instantiate a matching {AbstractExternalSource} for a given dependency. 12 | # 13 | # @param [Dependency] dependency 14 | # the dependency 15 | # 16 | # @param [String] podfile_path 17 | # @see AbstractExternalSource#podfile_path 18 | # 19 | # @return [AbstractExternalSource] an initialized instance of the concrete 20 | # external source class associated with the option specified in the 21 | # hash. 22 | # 23 | def self.from_dependency(dependency, podfile_path) 24 | from_params(dependency.external_source, dependency, podfile_path) 25 | end 26 | 27 | def self.from_params(params, dependency, podfile_path) 28 | name = dependency.root_name 29 | if klass = concrete_class_from_params(params) 30 | klass.new(name, params, podfile_path) 31 | else 32 | msg = "Unknown external source parameters for `#{name}`: `#{params}`" 33 | raise Informative, msg 34 | end 35 | end 36 | 37 | # Get the class to represent the defined source type of a dependency 38 | # 39 | # @param [Array] params 40 | # the source params of the dependency 41 | # 42 | # @return [Class] 43 | # 44 | def self.concrete_class_from_params(params) 45 | if params.key?(:podspec) 46 | PodspecSource 47 | elsif params.key?(:path) 48 | PathSource 49 | elsif Downloader.strategy_from_options(params) 50 | DownloaderSource 51 | end 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /lib/cocoapods/external_sources/downloader_source.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | module ExternalSources 3 | # Provides support for fetching a specification file from a source handled 4 | # by the downloader. Supports all the options of the downloader 5 | # 6 | # @note The podspec must be in the root of the repository and should have a 7 | # name matching the one of the dependency. 8 | # 9 | class DownloaderSource < AbstractExternalSource 10 | # @see AbstractExternalSource#fetch 11 | # 12 | def fetch(sandbox) 13 | pre_download(sandbox) 14 | end 15 | 16 | # @see AbstractExternalSource#description 17 | # 18 | def description 19 | strategy = Downloader.strategy_from_options(params) 20 | options = params.dup 21 | url = options.delete(strategy) 22 | result = "from `#{url}`" 23 | options.each do |key, value| 24 | result << ", #{key} `#{value}`" 25 | end 26 | result 27 | end 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/cocoapods/external_sources/path_source.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | module ExternalSources 3 | # Provides support for fetching a specification file from a path local to 4 | # the machine running the installation. 5 | # 6 | class PathSource < AbstractExternalSource 7 | # @see AbstractExternalSource#fetch 8 | # 9 | def fetch(sandbox) 10 | title = "Fetching podspec for `#{name}` #{description}" 11 | UI.titled_section(title, :verbose_prefix => '-> ') do 12 | podspec = podspec_path 13 | unless podspec.exist? 14 | raise Informative, "No podspec found for `#{name}` in " \ 15 | "`#{declared_path}`" 16 | end 17 | store_podspec(sandbox, podspec, podspec.extname == '.json') 18 | is_absolute = absolute?(declared_path) 19 | sandbox.store_local_path(name, podspec.dirname, is_absolute) 20 | sandbox.remove_checkout_source(name) 21 | end 22 | end 23 | 24 | # @see AbstractExternalSource#description 25 | # 26 | def description 27 | "from `#{declared_path}`" 28 | end 29 | 30 | private 31 | 32 | # @!group Helpers 33 | 34 | # @return [String] The path as declared by the user. 35 | # 36 | def declared_path 37 | result = params[:path] 38 | result.to_s if result 39 | end 40 | 41 | # @return [Pathname] The absolute path of the podspec. 42 | # 43 | def podspec_path 44 | path = Pathname(normalized_podspec_path(declared_path)) 45 | path.exist? ? path : Pathname("#{path}.json") 46 | end 47 | 48 | # @return [Bool] 49 | # 50 | def absolute?(path) 51 | Pathname(path).absolute? || path.to_s.start_with?('~') 52 | end 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /lib/cocoapods/external_sources/podspec_source.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | module ExternalSources 3 | # Provides support for fetching a specification file from an URL. Can be 4 | # http, file, etc. 5 | # 6 | class PodspecSource < AbstractExternalSource 7 | # @see AbstractExternalSource#fetch 8 | # 9 | def fetch(sandbox) 10 | title = "Fetching podspec for `#{name}` #{description}" 11 | UI.titled_section(title, :verbose_prefix => '-> ') do 12 | podspec_path = Pathname(podspec_uri) 13 | is_json = podspec_path.extname == '.json' 14 | if podspec_path.exist? 15 | store_podspec(sandbox, podspec_path, is_json) 16 | else 17 | require 'cocoapods/open-uri' 18 | begin 19 | open(podspec_uri) { |io| store_podspec(sandbox, io.read, is_json) } 20 | rescue OpenURI::HTTPError => e 21 | status = e.io.status.join(' ') 22 | raise Informative, "Failed to fetch podspec for `#{name}` at `#{podspec_uri}`.\n Error: #{status}" 23 | end 24 | end 25 | end 26 | end 27 | 28 | # @see AbstractExternalSource#description 29 | # 30 | def description 31 | "from `#{params[:podspec]}`" 32 | end 33 | 34 | private 35 | 36 | # @!group Helpers 37 | 38 | # @return [String] The uri of the podspec appending the name of the file 39 | # and expanding it if necessary. 40 | # 41 | # @note If the declared path is expanded only if the represents a path 42 | # relative to the file system. 43 | # 44 | def podspec_uri 45 | declared_path = params[:podspec].to_s 46 | if declared_path.match(%r{^.+://}) 47 | declared_path 48 | else 49 | normalized_podspec_path(declared_path) 50 | end 51 | end 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /lib/cocoapods/gem_version.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | # The version of the CocoaPods command line tool. 3 | # 4 | VERSION = '1.0.0.beta.4'.freeze unless defined? Pod::VERSION 5 | end 6 | -------------------------------------------------------------------------------- /lib/cocoapods/generator/acknowledgements.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | module Generator 3 | class Acknowledgements 4 | # @return [Array] The classes of the acknowledgements generator 5 | # subclasses. 6 | # 7 | def self.generators 8 | [Plist, Markdown] 9 | end 10 | 11 | # @return [Array] the list of the file accessors 12 | # for the specs of the target that needs to generate the 13 | # acknowledgements. 14 | # 15 | attr_reader :file_accessors 16 | 17 | # @param [Array] @see file_accessors. 18 | # 19 | def initialize(file_accessors) 20 | @file_accessors = file_accessors 21 | end 22 | 23 | #-----------------------------------------------------------------------# 24 | 25 | # !@group Configuration 26 | 27 | # @return [String] The title of the acknowledgements file. 28 | # 29 | def header_title 30 | 'Acknowledgements' 31 | end 32 | 33 | # @return [String] A text to present before listing the acknowledgements. 34 | # 35 | def header_text 36 | 'This application makes use of the following third party libraries:' 37 | end 38 | 39 | # @return [String] The title of the foot notes. 40 | # 41 | def footnote_title 42 | '' 43 | end 44 | 45 | # @return [String] the foot notes. 46 | # 47 | def footnote_text 48 | 'Generated by CocoaPods - https://cocoapods.org' 49 | end 50 | 51 | #-----------------------------------------------------------------------# 52 | 53 | private 54 | 55 | # !@group Private methods 56 | 57 | # @return [Array] The root specifications for which the 58 | # acknowledgements should be generated. 59 | # 60 | def specs 61 | file_accessors.map { |accessor| accessor.spec.root }.uniq 62 | end 63 | 64 | # Returns the text of the license for the given spec. 65 | # 66 | # @param [Specification] spec 67 | # the specification for which license is needed. 68 | # 69 | # @return [String] The text of the license. 70 | # @return [Nil] If not license text could be found. 71 | # 72 | def license_text(spec) 73 | return nil unless spec.license 74 | text = spec.license[:text] 75 | unless text 76 | if license_file = file_accessor(spec).license 77 | if license_file.exist? 78 | text = IO.read(license_file) 79 | else 80 | UI.warn "Unable to read the license file `#{license_file}` " \ 81 | "for the spec `#{spec}`" 82 | end 83 | end 84 | end 85 | text 86 | end 87 | 88 | protected 89 | 90 | # Returns the file accessor for the given spec. 91 | # 92 | # @param [Specification] spec 93 | # the specification for which the file accessor is needed. 94 | # 95 | # @return [Sandbox::FileAccessor] The file accessor. 96 | # 97 | def file_accessor(spec) 98 | file_accessors.find { |accessor| accessor.spec.root == spec } 99 | end 100 | 101 | #-----------------------------------------------------------------------# 102 | end 103 | end 104 | end 105 | -------------------------------------------------------------------------------- /lib/cocoapods/generator/acknowledgements/markdown.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | module Generator 3 | class Markdown < Acknowledgements 4 | def self.path_from_basepath(path) 5 | Pathname.new(path.dirname + "#{path.basename}.markdown") 6 | end 7 | 8 | def save_as(path) 9 | file = File.new(path, 'w') 10 | file.write(licenses) 11 | file.close 12 | end 13 | 14 | def title_from_string(string, level) 15 | unless string.empty? 16 | '#' * level << " #{string}" 17 | end 18 | end 19 | 20 | def string_for_spec(spec) 21 | if (license_text = license_text(spec)) 22 | "\n" << title_from_string(spec.name, 2) << "\n\n" << license_text << "\n" 23 | end 24 | end 25 | 26 | def licenses 27 | licenses_string = "#{title_from_string(header_title, 1)}\n#{header_text}\n" 28 | specs.each do |spec| 29 | if (license = string_for_spec(spec)) 30 | license = license.force_encoding('UTF-8') if license.respond_to?(:force_encoding) 31 | licenses_string += license 32 | end 33 | end 34 | licenses_string += "#{title_from_string(footnote_title, 2)}#{footnote_text}\n" 35 | end 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/cocoapods/generator/acknowledgements/plist.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | module Generator 3 | class Plist < Acknowledgements 4 | def self.path_from_basepath(path) 5 | Pathname.new(path.dirname + "#{path.basename}.plist") 6 | end 7 | 8 | def save_as(path) 9 | Xcodeproj::Plist.write_to_path(plist, path) 10 | end 11 | 12 | def plist 13 | { 14 | :Title => plist_title, 15 | :StringsTable => plist_title, 16 | :PreferenceSpecifiers => licenses, 17 | } 18 | end 19 | 20 | def plist_title 21 | 'Acknowledgements' 22 | end 23 | 24 | def licenses 25 | licences_array = [header_hash] 26 | specs.each do |spec| 27 | if (hash = hash_for_spec(spec)) 28 | licences_array << hash 29 | end 30 | end 31 | licences_array << footnote_hash 32 | end 33 | 34 | def hash_for_spec(spec) 35 | if (license = license_text(spec)) 36 | { 37 | :Type => 'PSGroupSpecifier', 38 | :Title => sanitize_encoding(spec.name), 39 | :FooterText => sanitize_encoding(license), 40 | } 41 | end 42 | end 43 | 44 | def header_hash 45 | { 46 | :Type => 'PSGroupSpecifier', 47 | :Title => sanitize_encoding(header_title), 48 | :FooterText => sanitize_encoding(header_text), 49 | } 50 | end 51 | 52 | def footnote_hash 53 | { 54 | :Type => 'PSGroupSpecifier', 55 | :Title => sanitize_encoding(footnote_title), 56 | :FooterText => sanitize_encoding(footnote_text), 57 | } 58 | end 59 | 60 | #-----------------------------------------------------------------------# 61 | 62 | private 63 | 64 | # !@group Private methods 65 | 66 | # Returns the sanitized text with UTF-8 invalid characters eliminated. 67 | # 68 | # @param [String] text 69 | # the text we want to sanitize. 70 | # 71 | # @return [String] The sanitized UTF-8 text. 72 | # 73 | def sanitize_encoding(text) 74 | text.encode('UTF-8', :invalid => :replace, :undef => :replace, :replace => '') 75 | end 76 | 77 | #-----------------------------------------------------------------------# 78 | end 79 | end 80 | end 81 | -------------------------------------------------------------------------------- /lib/cocoapods/generator/bridge_support.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | module Generator 3 | class BridgeSupport 4 | extend Executable 5 | executable :gen_bridge_metadata 6 | 7 | attr_reader :headers 8 | 9 | def initialize(headers) 10 | @headers = headers 11 | end 12 | 13 | def search_paths 14 | @headers.map { |header| "-I '#{header.dirname}'" }.uniq 15 | end 16 | 17 | def save_as(pathname) 18 | gen_bridge_metadata('-c', search_paths.join(' '), '-o', pathname, *headers) 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/cocoapods/generator/dummy_source.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | module Generator 3 | class DummySource 4 | attr_reader :class_name 5 | 6 | def initialize(class_name_identifier) 7 | validated_class_name_identifier = class_name_identifier.gsub(/[^0-9a-z_]/i, '_') 8 | @class_name = "PodsDummy_#{validated_class_name_identifier}" 9 | end 10 | 11 | def save_as(pathname) 12 | pathname.open('w') do |source| 13 | source.puts '#import ' 14 | source.puts "@interface #{class_name} : NSObject" 15 | source.puts '@end' 16 | source.puts "@implementation #{class_name}" 17 | source.puts '@end' 18 | end 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/cocoapods/generator/header.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | module Generator 3 | # Generates a header file. 4 | # 5 | # According to the platform the header imports `UIKit/UIKit.h` or 6 | # `Cocoa/Cocoa.h`. 7 | # 8 | class Header 9 | # @return [Symbol] the platform for which the prefix header will be 10 | # generated. 11 | # 12 | attr_reader :platform 13 | 14 | # @return [Array] The list of the headers to import. 15 | # 16 | attr_accessor :imports 17 | 18 | # @return [Array] The list of the modules to import. 19 | # 20 | attr_accessor :module_imports 21 | 22 | # Initialize a new instance 23 | # 24 | # @param [Symbol] platform 25 | # @see platform 26 | # 27 | def initialize(platform) 28 | @platform = platform 29 | @imports = [] 30 | @module_imports = [] 31 | end 32 | 33 | # Generates the contents of the header according to the platform. 34 | # 35 | # @note If the platform is iOS an import call to `UIKit/UIKit.h` is 36 | # added to the top of the prefix header. For OS X `Cocoa/Cocoa.h` 37 | # is imported. 38 | # 39 | # @return [String] 40 | # 41 | def generate 42 | result = '' 43 | result << generate_platform_import_header 44 | 45 | result << "\n" 46 | 47 | imports.each do |import| 48 | result << %(#import "#{import}"\n) 49 | end 50 | 51 | unless module_imports.empty? 52 | module_imports.each do |import| 53 | result << %(\n@import #{import}) 54 | end 55 | result << "\n" 56 | end 57 | 58 | result 59 | end 60 | 61 | # Generates and saves the header to the given path. 62 | # 63 | # @param [Pathname] path 64 | # The path where the header should be stored. 65 | # 66 | # @return [void] 67 | # 68 | def save_as(path) 69 | path.open('w') { |header| header.write(generate) } 70 | end 71 | 72 | #-----------------------------------------------------------------------# 73 | 74 | protected 75 | 76 | # Generates the contents of the header according to the platform. 77 | # 78 | # @note If the platform is iOS an import call to `UIKit/UIKit.h` is 79 | # added to the top of the header. For OS X `Cocoa/Cocoa.h` is 80 | # imported. 81 | # 82 | # @return [String] 83 | # 84 | def generate_platform_import_header 85 | case platform.name 86 | when :ios then "#import \n" 87 | when :tvos then "#import \n" 88 | when :osx then "#import \n" 89 | else "#import \n" 90 | end 91 | end 92 | end 93 | end 94 | end 95 | -------------------------------------------------------------------------------- /lib/cocoapods/generator/info_plist_file.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | module Generator 3 | # Generates Info.plist files. A Info.plist file is generated for each 4 | # Pod and for each Pod target definition, that requires to be built as 5 | # framework. It states public attributes. 6 | # 7 | class InfoPlistFile 8 | # @return [Target] the target represented by this Info.plist. 9 | # 10 | attr_reader :target 11 | 12 | # @return [Symbol] the CFBundlePackageType of the target this Info.plist 13 | # file is for. 14 | # 15 | attr_reader :bundle_package_type 16 | 17 | # Initialize a new instance 18 | # 19 | # @param [Target] target @see target 20 | # 21 | # @param [Symbol] bundle_package_type @see bundle_package_type 22 | # 23 | def initialize(target, bundle_package_type: :fmwk) 24 | @target = target 25 | @bundle_package_type = bundle_package_type 26 | end 27 | 28 | # Generates and saves the Info.plist to the given path. 29 | # 30 | # @param [Pathname] path 31 | # the path where the prefix header should be stored. 32 | # 33 | # @return [void] 34 | # 35 | def save_as(path) 36 | contents = generate 37 | path.open('w') do |f| 38 | f.write(contents) 39 | end 40 | end 41 | 42 | # The version associated with the current target 43 | # 44 | # @note Will return 1.0.0 for the AggregateTarget 45 | # 46 | # @return [String] 47 | # 48 | def target_version 49 | if target && target.respond_to?(:root_spec) 50 | version = target.root_spec.version 51 | [version.major, version.minor, version.patch].join('.') 52 | else 53 | '1.0.0' 54 | end 55 | end 56 | 57 | # Generates the contents of the Info.plist 58 | # 59 | # @return [String] 60 | # 61 | def generate 62 | to_plist(info) 63 | end 64 | 65 | private 66 | 67 | def header 68 | <<-PLIST 69 | 70 | 71 | 72 | PLIST 73 | end 74 | 75 | def footer 76 | <<-PLIST 77 | 78 | PLIST 79 | end 80 | 81 | def to_plist(root) 82 | serialize(root, header) << footer 83 | end 84 | 85 | def serialize(value, output, indentation = 0) 86 | indent = ' ' * indentation 87 | case value 88 | when Array 89 | output << indent << "\n" 90 | value.each { |v| serialize(v, output, indentation + 2) } 91 | output << indent << "\n" 92 | when Hash 93 | output << indent << "\n" 94 | value.to_a.sort_by(&:first).each do |key, v| 95 | output << indent << ' ' << "#{key}\n" 96 | serialize(v, output, indentation + 2) 97 | end 98 | output << indent << "\n" 99 | when String 100 | output << indent << "#{value}\n" 101 | end 102 | output 103 | end 104 | 105 | def info 106 | info = { 107 | 'CFBundleIdentifier' => '${PRODUCT_BUNDLE_IDENTIFIER}', 108 | 'CFBundleInfoDictionaryVersion' => '6.0', 109 | 'CFBundleName' => '${PRODUCT_NAME}', 110 | 'CFBundlePackageType' => bundle_package_type.to_s.upcase, 111 | 'CFBundleShortVersionString' => target_version, 112 | 'CFBundleSignature' => '????', 113 | 'CFBundleVersion' => '${CURRENT_PROJECT_VERSION}', 114 | 'NSPrincipalClass' => '', 115 | 'CFBundleDevelopmentRegion' => 'en', 116 | } 117 | 118 | info['CFBundleExecutable'] = '${EXECUTABLE_NAME}' if bundle_package_type != :bndl 119 | info['UIRequiredDeviceCapabilities'] = %w(arm64) if target.platform.name == :tvos 120 | 121 | info 122 | end 123 | end 124 | end 125 | end 126 | -------------------------------------------------------------------------------- /lib/cocoapods/generator/module_map.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | module Generator 3 | # Generates LLVM module map files. A module map file is generated for each 4 | # Pod and for each Pod target definition that is built as a framework. It 5 | # specifies a different umbrella header than usual to avoid name conflicts 6 | # with existing headers of the podspec. 7 | # 8 | class ModuleMap 9 | # @return [PodTarget] the target represented by this Info.plist. 10 | # 11 | attr_reader :target 12 | 13 | # Initialize a new instance 14 | # 15 | # @param [PodTarget] target @see target 16 | # 17 | def initialize(target) 18 | @target = target 19 | end 20 | 21 | # Generates and saves the Info.plist to the given path. 22 | # 23 | # @param [Pathname] path 24 | # the path where the prefix header should be stored. 25 | # 26 | # @return [void] 27 | # 28 | def save_as(path) 29 | contents = generate 30 | path.open('w') do |f| 31 | f.write(contents) 32 | end 33 | end 34 | 35 | # Generates the contents of the module.modulemap file. 36 | # 37 | # @return [String] 38 | # 39 | def generate 40 | <<-MODULE_MAP.strip_heredoc 41 | framework module #{target.product_module_name} { 42 | umbrella header "#{target.umbrella_header_path.basename}" 43 | 44 | export * 45 | module * { export * } 46 | } 47 | MODULE_MAP 48 | end 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /lib/cocoapods/generator/prefix_header.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | module Generator 3 | # Generates a prefix header file for a Pods library. The prefix header is 4 | # generated according to the platform of the target and the pods. 5 | # 6 | # According to the platform the prefix header imports `UIKit/UIKit.h` or 7 | # `Cocoa/Cocoa.h`. 8 | # 9 | class PrefixHeader < Header 10 | # @return [Array] The file accessors for which to generate 11 | # the prefix header. 12 | # 13 | attr_reader :file_accessors 14 | 15 | # Initialize a new instance 16 | # 17 | # @param [Array] file_accessors 18 | # @see file_accessors 19 | # 20 | # @param [Platform] platform 21 | # @see platform 22 | # 23 | def initialize(file_accessors, platform) 24 | @file_accessors = file_accessors 25 | super platform 26 | end 27 | 28 | # Generates the contents of the prefix header according to the platform 29 | # and the pods. 30 | # 31 | # @note Only unique prefix_header_contents are added to the prefix 32 | # header. 33 | # 34 | # @return [String] 35 | # 36 | # @todo Subspecs can specify prefix header information too. 37 | # @todo Check to see if we have a similar duplication issue with 38 | # file_accessor.prefix_header. 39 | # 40 | def generate 41 | result = super 42 | 43 | unique_prefix_header_contents = file_accessors.map do |file_accessor| 44 | file_accessor.spec_consumer.prefix_header_contents 45 | end.compact.uniq 46 | 47 | unique_prefix_header_contents.each do |prefix_header_contents| 48 | result << prefix_header_contents 49 | result << "\n" 50 | end 51 | 52 | file_accessors.map(&:prefix_header).compact.uniq.each do |prefix_header| 53 | result << Pathname(prefix_header).read 54 | end 55 | 56 | result 57 | end 58 | 59 | protected 60 | 61 | # Generates the contents of the header according to the platform. 62 | # 63 | # @return [String] 64 | # 65 | def generate_platform_import_header 66 | result = "#ifdef __OBJC__\n" 67 | result << super 68 | result << "#endif\n" 69 | end 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /lib/cocoapods/generator/umbrella_header.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | module Generator 3 | # Generates an umbrella header file for clang modules, which are used by 4 | # dynamic frameworks on iOS 8 and OSX 10.10 under the hood. 5 | # 6 | # If the target is a +PodTarget+, then the umbrella header is required 7 | # to make all public headers in a convenient manner available without the 8 | # need to write out header declarations for every library header. 9 | # 10 | class UmbrellaHeader < Header 11 | # @return [Target] 12 | # the target, which provides the product name 13 | attr_reader :target 14 | 15 | # Initialize a new instance 16 | # 17 | # @param [Target] target 18 | # @see target 19 | # 20 | def initialize(target) 21 | super(target.platform) 22 | @target = target 23 | end 24 | 25 | # Generates the contents of the umbrella header according to the included 26 | # pods. 27 | # 28 | # @return [String] 29 | # 30 | def generate 31 | result = super 32 | 33 | result << "\n" 34 | 35 | result << <<-eos.strip_heredoc 36 | FOUNDATION_EXPORT double #{target.product_module_name}VersionNumber; 37 | FOUNDATION_EXPORT const unsigned char #{target.product_module_name}VersionString[]; 38 | eos 39 | 40 | result << "\n" 41 | 42 | result 43 | end 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /lib/cocoapods/generator/xcconfig.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | module Generator 3 | # Generates Xcode configuration files. A configuration file is generated 4 | # for each Pod and for each Pod target definition. The aggregates the 5 | # configurations of the Pods and define target specific settings. 6 | # 7 | module XCConfig 8 | autoload :AggregateXCConfig, 'cocoapods/generator/xcconfig/aggregate_xcconfig' 9 | autoload :PodXCConfig, 'cocoapods/generator/xcconfig/pod_xcconfig' 10 | autoload :XCConfigHelper, 'cocoapods/generator/xcconfig/xcconfig_helper' 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/cocoapods/generator/xcconfig/pod_xcconfig.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | module Generator 3 | module XCConfig 4 | # Generates the private xcconfigs for the pod targets. 5 | # 6 | # The xcconfig file for a Pod target merges the pod target 7 | # configuration values with the default configuration values 8 | # required by CocoaPods. 9 | # 10 | class PodXCConfig 11 | # @return [Target] the target represented by this xcconfig. 12 | # 13 | attr_reader :target 14 | 15 | # Initialize a new instance 16 | # 17 | # @param [Target] target @see target 18 | # 19 | def initialize(target) 20 | @target = target 21 | end 22 | 23 | # @return [Xcodeproj::Config] The generated xcconfig. 24 | # 25 | attr_reader :xcconfig 26 | 27 | # Generates and saves the xcconfig to the given path. 28 | # 29 | # @param [Pathname] path 30 | # the path where the prefix header should be stored. 31 | # 32 | # @return [void] 33 | # 34 | def save_as(path) 35 | generate.save_as(path) 36 | end 37 | 38 | # Generates the xcconfig. 39 | # 40 | # @return [Xcodeproj::Config] 41 | # 42 | def generate 43 | target_search_paths = target.build_headers.search_paths(target.platform) 44 | sandbox_search_paths = target.sandbox.public_headers.search_paths(target.platform) 45 | search_paths = target_search_paths.concat(sandbox_search_paths).uniq 46 | 47 | config = { 48 | 'FRAMEWORK_SEARCH_PATHS' => '$(inherited) ', 49 | 'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) COCOAPODS=1', 50 | 'HEADER_SEARCH_PATHS' => XCConfigHelper.quote(search_paths), 51 | 'LIBRARY_SEARCH_PATHS' => '$(inherited) ', 52 | 'OTHER_LDFLAGS' => XCConfigHelper.default_ld_flags(target), 53 | 'PODS_ROOT' => '${SRCROOT}', 54 | 'PRODUCT_BUNDLE_IDENTIFIER' => 'org.cocoapods.${PRODUCT_NAME:rfc1034identifier}', 55 | 'SKIP_INSTALL' => 'YES', 56 | # 'USE_HEADERMAP' => 'NO' 57 | } 58 | 59 | @xcconfig = Xcodeproj::Config.new(config) 60 | 61 | XCConfigHelper.add_settings_for_file_accessors_of_target(target, @xcconfig) 62 | target.file_accessors.each do |file_accessor| 63 | @xcconfig.merge!(file_accessor.spec_consumer.pod_target_xcconfig) 64 | end 65 | XCConfigHelper.add_target_specific_settings(target, @xcconfig) 66 | @xcconfig.merge! XCConfigHelper.settings_for_dependent_targets(target, target.dependent_targets) 67 | @xcconfig 68 | end 69 | 70 | #-----------------------------------------------------------------------# 71 | end 72 | end 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /lib/cocoapods/hooks_manager.rb: -------------------------------------------------------------------------------- 1 | require 'active_support/core_ext/hash/indifferent_access' 2 | 3 | module Pod 4 | # Provides support for the hook system of CocoaPods. The system is designed 5 | # especially for plugins. Interested clients can register to notifications by 6 | # name. 7 | # 8 | # The blocks, to prevent compatibility issues, will receive 9 | # one and only one argument: a context object. This object should be simple 10 | # storage of information (a typed hash). Notifications senders are 11 | # responsible to indicate the class of the object associated with their 12 | # notification name. 13 | # 14 | # Context object should not remove attribute accessors to not break 15 | # compatibility with the plugins (this promise will be honoured strictly 16 | # from CocoaPods 1.0). 17 | # 18 | module HooksManager 19 | # Represents a single registered hook. 20 | # 21 | class Hook 22 | # @return [String] 23 | # The name of the plugin that registered the hook. 24 | # 25 | attr_reader :plugin_name 26 | 27 | # @return [String] 28 | # The name of the hook. 29 | # 30 | attr_reader :name 31 | 32 | # @return [Proc] 33 | # The block. 34 | # 35 | attr_reader :block 36 | 37 | # Initialize a new instance 38 | # 39 | # @param [String] name @see {#name}. 40 | # 41 | # @param [String] plugin_name @see {#plugin_name}. 42 | # 43 | # @param [Proc] block @see {#block}. 44 | # 45 | def initialize(name, plugin_name, block) 46 | raise ArgumentError, 'Missing name' unless name 47 | raise ArgumentError, 'Missing plugin_name' unless plugin_name 48 | raise ArgumentError, 'Missing block' unless block 49 | 50 | @name = name 51 | @plugin_name = plugin_name 52 | @block = block 53 | end 54 | end 55 | 56 | class << self 57 | # @return [Hash{Symbol => Array}] The list of the hooks that are 58 | # registered for each hook name. 59 | # 60 | attr_reader :registrations 61 | 62 | # Registers a block for the hook with the given name. 63 | # 64 | # @param [String] plugin_name 65 | # The name of the plugin the hook comes from. 66 | # 67 | # @param [Symbol] hook_name 68 | # The name of the notification. 69 | # 70 | # @param [Proc] block 71 | # The block. 72 | # 73 | def register(plugin_name, hook_name, &block) 74 | @registrations ||= {} 75 | @registrations[hook_name] ||= [] 76 | @registrations[hook_name] << Hook.new(hook_name, plugin_name, block) 77 | end 78 | 79 | # Runs all the registered blocks for the hook with the given name. 80 | # 81 | # @param [Symbol] name 82 | # The name of the hook. 83 | # 84 | # @param [Object] context 85 | # The context object which should be passed to the blocks. 86 | # 87 | # @param [Hash] whitelisted_plugins 88 | # The plugins that should be run, in the form of a hash keyed by 89 | # plugin name, where the values are the custom options that should 90 | # be passed to the hook's block if it supports taking a second 91 | # argument. 92 | # 93 | def run(name, context, whitelisted_plugins = nil) 94 | raise ArgumentError, 'Missing name' unless name 95 | raise ArgumentError, 'Missing options' unless context 96 | 97 | if registrations 98 | hooks = registrations[name] 99 | if hooks 100 | UI.message "- Running #{name.to_s.tr('_', ' ')} hooks" do 101 | hooks.each do |hook| 102 | next if whitelisted_plugins && !whitelisted_plugins.key?(hook.plugin_name) 103 | UI.message "- #{hook.plugin_name} from " \ 104 | "`#{hook.block.source_location.first}`" do 105 | block = hook.block 106 | if block.arity > 1 107 | user_options = whitelisted_plugins[hook.plugin_name] 108 | user_options = user_options.with_indifferent_access if user_options 109 | block.call(context, user_options) 110 | else 111 | block.call(context) 112 | end 113 | end 114 | end 115 | end 116 | end 117 | end 118 | end 119 | end 120 | end 121 | end 122 | -------------------------------------------------------------------------------- /lib/cocoapods/installer/analyzer/analysis_result.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Installer 3 | class Analyzer 4 | class AnalysisResult 5 | # @return [SpecsState] the states of the Podfile specs. 6 | # 7 | attr_accessor :podfile_state 8 | 9 | # @return [Hash{TargetDefinition => Array}] the 10 | # specifications grouped by target. 11 | # 12 | attr_accessor :specs_by_target 13 | 14 | # @return [Array] the specifications of the resolved 15 | # version of Pods that should be installed. 16 | # 17 | attr_accessor :specifications 18 | 19 | # @return [SpecsState] the states of the {Sandbox} respect the resolved 20 | # specifications. 21 | # 22 | attr_accessor :sandbox_state 23 | 24 | # @return [Array] The aggregate targets created for each 25 | # {TargetDefinition} from the {Podfile}. 26 | # 27 | attr_accessor :targets 28 | 29 | # @return [Hash{TargetDefinition => Array}] the 30 | # results of inspecting the user targets 31 | attr_accessor :target_inspections 32 | 33 | # @return [Hash{String=>Symbol}] A hash representing all the user build 34 | # configurations across all integration targets. Each key 35 | # corresponds to the name of a configuration and its value to 36 | # its type (`:debug` or `:release`). 37 | # 38 | def all_user_build_configurations 39 | targets.reduce({}) do |result, target| 40 | result.merge(target.user_build_configurations) 41 | end 42 | end 43 | end 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /lib/cocoapods/installer/analyzer/locking_dependency_analyzer.rb: -------------------------------------------------------------------------------- 1 | require 'molinillo/dependency_graph' 2 | 3 | module Pod 4 | class Installer 5 | class Analyzer 6 | # Generates dependencies that require the specific version of the Pods 7 | # that haven't changed in the {Lockfile}. 8 | module LockingDependencyAnalyzer 9 | # Generates dependencies that require the specific version of the Pods 10 | # that haven't changed in the {Lockfile}. 11 | # 12 | # These dependencies are passed to the {Resolver}, unless the installer 13 | # is in update mode, to prevent it from upgrading the Pods that weren't 14 | # changed in the {Podfile}. 15 | # 16 | # @return [Molinillo::DependencyGraph] the dependencies 17 | # generated by the lockfile that prevent the resolver to update 18 | # a Pod. 19 | # 20 | def self.generate_version_locking_dependencies(lockfile, pods_to_update) 21 | dependency_graph = Molinillo::DependencyGraph.new 22 | 23 | if lockfile 24 | explicit_dependencies = lockfile.to_hash['DEPENDENCIES'] || [] 25 | explicit_dependencies.each do |string| 26 | dependency = Dependency.new(string) 27 | dependency_graph.add_vertex(dependency.name, nil, true) 28 | end 29 | 30 | pods = lockfile.to_hash['PODS'] || [] 31 | pods.each do |pod| 32 | add_to_dependency_graph(pod, [], dependency_graph) 33 | end 34 | 35 | pods_to_update = pods_to_update.flat_map do |u| 36 | root_name = Specification.root_name(u).downcase 37 | dependency_graph.vertices.keys.select { |n| Specification.root_name(n).downcase == root_name } 38 | end 39 | 40 | pods_to_update.each do |u| 41 | dependency_graph.detach_vertex_named(u) 42 | end 43 | end 44 | 45 | dependency_graph 46 | end 47 | 48 | # Generates a completely 'unlocked' dependency graph. 49 | # 50 | # @return [Molinillo::DependencyGraph] an empty dependency 51 | # graph 52 | # 53 | def self.unlocked_dependency_graph 54 | Molinillo::DependencyGraph.new 55 | end 56 | 57 | private 58 | 59 | def self.add_child_vertex_to_graph(dependency_string, parents, dependency_graph) 60 | dependency = Dependency.from_string(dependency_string) 61 | dependency_graph.add_child_vertex(dependency.name, parents.empty? ? dependency : nil, parents, nil) 62 | dependency 63 | end 64 | 65 | def self.add_to_dependency_graph(object, parents, dependency_graph) 66 | case object 67 | when String 68 | add_child_vertex_to_graph(object, parents, dependency_graph) 69 | when Hash 70 | object.each do |key, value| 71 | dependency = add_child_vertex_to_graph(key, parents, dependency_graph) 72 | value.each { |v| add_to_dependency_graph(v, [dependency.name], dependency_graph) } 73 | end 74 | end 75 | end 76 | end 77 | end 78 | end 79 | end 80 | -------------------------------------------------------------------------------- /lib/cocoapods/installer/analyzer/pod_variant.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Installer 3 | class Analyzer 4 | # Bundles the information needed to setup a {PodTarget}. 5 | class PodVariant 6 | # @return [Array] the spec and subspecs for the target 7 | # 8 | attr_accessor :specs 9 | 10 | # @return [Platform] the platform 11 | # 12 | attr_accessor :platform 13 | 14 | # @return [Bool] whether this pod should be built as framework 15 | # 16 | attr_accessor :requires_frameworks 17 | alias_method :requires_frameworks?, :requires_frameworks 18 | 19 | # @return [Specification] the root specification 20 | # 21 | def root_spec 22 | specs.first.root 23 | end 24 | 25 | # Initialize a new instance from its attributes. 26 | # 27 | # @param [Array] specs @see #specs 28 | # @param [Platform] platform @see #platform 29 | # @param [Bool] requires_frameworks @see #requires_frameworks? 30 | # 31 | def initialize(specs, platform, requires_frameworks = false) 32 | self.specs = specs 33 | self.platform = platform 34 | self.requires_frameworks = requires_frameworks 35 | end 36 | 37 | # @return [Bool] whether the {PodVariant} is equal to another taking all 38 | # all their attributes into account 39 | # 40 | def ==(other) 41 | self.class == other.class && 42 | specs == other.specs && 43 | platform == other.platform && 44 | requires_frameworks == other.requires_frameworks 45 | end 46 | alias_method :eql?, :== 47 | 48 | # Hashes the instance by all its attributes. 49 | # 50 | # This adds support to make instances usable as Hash keys. 51 | # 52 | # @!visibility private 53 | def hash 54 | [specs, platform, requires_frameworks].hash 55 | end 56 | end 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /lib/cocoapods/installer/analyzer/specs_state.rb: -------------------------------------------------------------------------------- 1 | require 'set' 2 | 3 | module Pod 4 | class Installer 5 | class Analyzer 6 | # This class represents the state of a collection of Pods. 7 | # 8 | # @note The names of the pods stored by this class are always the **root** 9 | # name of the specification. 10 | # 11 | # @note The motivation for this class is to ensure that the names of the 12 | # subspecs are added instead of the name of the Pods. 13 | # 14 | class SpecsState 15 | # Initialize a new instance 16 | # 17 | # @param [Hash{Symbol=>String}] pods_by_state 18 | # The name of the pods grouped by their state 19 | # (`:added`, `:removed`, `:changed` or `:unchanged`). 20 | # 21 | def initialize(pods_by_state = nil) 22 | @added = Set.new 23 | @deleted = Set.new 24 | @changed = Set.new 25 | @unchanged = Set.new 26 | 27 | if pods_by_state 28 | { 29 | :added => :added, 30 | :changed => :changed, 31 | :removed => :deleted, 32 | :unchanged => :unchanged, 33 | }.each do |state, spec_state| 34 | Array(pods_by_state[state]).each do |name| 35 | add_name(name, spec_state) 36 | end 37 | end 38 | end 39 | end 40 | 41 | # @return [Set] the names of the pods that were added. 42 | # 43 | attr_accessor :added 44 | 45 | # @return [Set] the names of the pods that were changed. 46 | # 47 | attr_accessor :changed 48 | 49 | # @return [Set] the names of the pods that were deleted. 50 | # 51 | attr_accessor :deleted 52 | 53 | # @return [Set] the names of the pods that were unchanged. 54 | # 55 | attr_accessor :unchanged 56 | 57 | # Displays the state of each pod. 58 | # 59 | # @return [void] 60 | # 61 | def print 62 | added .sort.each { |pod| UI.message('A'.green + " #{pod}", '', 2) } 63 | deleted .sort.each { |pod| UI.message('R'.red + " #{pod}", '', 2) } 64 | changed .sort.each { |pod| UI.message('M'.yellow + " #{pod}", '', 2) } 65 | unchanged.sort.each { |pod| UI.message('-' + " #{pod}", '', 2) } 66 | end 67 | 68 | # Adds the name of a Pod to the give state. 69 | # 70 | # @param [String] name 71 | # the name of the Pod. 72 | # 73 | # @param [Symbol] state 74 | # the state of the Pod. 75 | # 76 | # @return [void] 77 | # 78 | def add_name(name, state) 79 | send(state) << Specification.root_name(name) 80 | end 81 | end 82 | end 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /lib/cocoapods/installer/analyzer/target_inspection_result.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Installer 3 | class Analyzer 4 | class TargetInspectionResult 5 | # @return [TargetDefinition] the target definition, whose project was 6 | # inspected 7 | # 8 | attr_accessor :target_definition 9 | 10 | # @return [Pathname] the path of the user project that the 11 | # #target_definition should integrate 12 | # 13 | attr_accessor :project_path 14 | 15 | # @return [Array] the uuid of the user's targets 16 | # 17 | attr_accessor :project_target_uuids 18 | 19 | # @return [Hash{String=>Symbol}] A hash representing the user build 20 | # configurations where each key corresponds to the name of a 21 | # configuration and its value to its type (`:debug` or 22 | # `:release`). 23 | # 24 | attr_accessor :build_configurations 25 | 26 | # @return [Platform] the platform of the user targets 27 | # 28 | attr_accessor :platform 29 | 30 | # @return [Array] the architectures used by user's targets 31 | # 32 | attr_accessor :archs 33 | 34 | # @return [Bool] whether frameworks are recommended for the integration 35 | # due to the presence of Swift source in the user's targets 36 | # 37 | attr_accessor :recommends_frameworks 38 | 39 | # @return [Xcodeproj::Project] the user's Xcode project 40 | # 41 | attr_accessor :project 42 | end 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /lib/cocoapods/installer/installation_options.rb: -------------------------------------------------------------------------------- 1 | require 'active_support/hash_with_indifferent_access' 2 | 3 | module Pod 4 | class Installer 5 | # Represents the installation options the user can customize via a 6 | # `Podfile`. 7 | # 8 | class InstallationOptions 9 | # Parses installation options from a podfile. 10 | # 11 | # @param [Podfile] podfile the podfile to parse installation options 12 | # from. 13 | # 14 | # @raise [Informative] if `podfile` does not specify a `CocoaPods` 15 | # install. 16 | # 17 | # @return [Self] 18 | # 19 | def self.from_podfile(podfile) 20 | name, options = podfile.installation_method 21 | unless name.downcase == 'cocoapods' 22 | raise Informative, "Currently need to specify a `cocoapods` install, you chose `#{name}`." 23 | end 24 | new(options) 25 | end 26 | 27 | # Defines a new installation option. 28 | # 29 | # @param [#to_s] name the name of the option. 30 | # 31 | # @param default the default value for the option. 32 | # 33 | # @param [Boolean] boolean whether the option has a boolean value. 34 | # 35 | # @return [void] 36 | # 37 | # @!macro [attach] option 38 | # 39 | # @note this option defaults to $2. 40 | # 41 | # @return the $1 $0 for installation. 42 | # 43 | def self.option(name, default, boolean: true) 44 | name = name.to_s 45 | raise ArgumentError, "The `#{name}` option is already defined" if defaults.key?(name) 46 | defaults[name] = default 47 | attr_accessor name 48 | alias_method "#{name}?", name if boolean 49 | end 50 | 51 | # @return [Hash] all known installation options and their 52 | # default values. 53 | # 54 | def self.defaults 55 | @defaults ||= {} 56 | end 57 | 58 | # @return [Array] the names of all known installation options. 59 | # 60 | def self.all_options 61 | defaults.keys 62 | end 63 | 64 | # Initializes the installation options with a hash of options from a 65 | # Podfile. 66 | # 67 | # @param [Hash] options the options to parse. 68 | # 69 | # @raise [Informative] if `options` contains any unknown keys. 70 | # 71 | def initialize(options = {}) 72 | options = ActiveSupport::HashWithIndifferentAccess.new(options) 73 | unknown_keys = options.keys - self.class.all_options.map(&:to_s) 74 | raise Informative, "Unknown installation options: #{unknown_keys.to_sentence}." unless unknown_keys.empty? 75 | self.class.defaults.each do |key, default| 76 | value = options.fetch(key, default) 77 | send("#{key}=", value) 78 | end 79 | end 80 | 81 | # @param [Boolean] include_defaults whether values that match the default 82 | # for their option should be included. Defaults to `true`. 83 | # 84 | # @return [Hash] the options, keyed by option name. 85 | # 86 | def to_h(include_defaults: true) 87 | self.class.defaults.reduce(ActiveSupport::HashWithIndifferentAccess.new) do |hash, (option, default)| 88 | value = send(option) 89 | hash[option] = value if include_defaults || value != default 90 | hash 91 | end 92 | end 93 | 94 | def ==(other) 95 | other.is_a?(self.class) && to_h == other.to_h 96 | end 97 | 98 | alias_method :eql, :== 99 | 100 | def hash 101 | to_h.hash 102 | end 103 | 104 | option :clean, true 105 | option :deduplicate_targets, true 106 | option :deterministic_uuids, true 107 | option :integrate_targets, true 108 | option :lock_pod_sources, true 109 | 110 | module Mixin 111 | module ClassMethods 112 | # Delegates the creation of {#installation_options} to the `Podfile` 113 | # returned by the given block. 114 | # 115 | # @param blk a block that returns the `Podfile` to create 116 | # installation options from. 117 | # 118 | # @return [Void] 119 | # 120 | def delegate_installation_options(&blk) 121 | define_method(:installation_options) do 122 | @installation_options ||= InstallationOptions.from_podfile(instance_eval(&blk)) 123 | end 124 | end 125 | 126 | # Delegates the installation options attributes directly to 127 | # {#installation_options}. 128 | # 129 | # @return [Void] 130 | # 131 | def delegate_installation_option_attributes! 132 | define_method(:respond_to_missing?) do |name, *args| 133 | installation_options.respond_to?(name, *args) || super 134 | end 135 | 136 | define_method(:method_missing) do |name, *args, &blk| 137 | if installation_options.respond_to?(name) 138 | installation_options.send(name, *args, &blk) 139 | else 140 | super 141 | end 142 | end 143 | end 144 | end 145 | 146 | # @return [InstallationOptions] The installation options. 147 | # 148 | attr_accessor :installation_options 149 | 150 | def self.included(mod) 151 | mod.extend(ClassMethods) 152 | end 153 | end 154 | end 155 | end 156 | end 157 | -------------------------------------------------------------------------------- /lib/cocoapods/installer/migrator.rb: -------------------------------------------------------------------------------- 1 | require 'fileutils' 2 | 3 | module Pod 4 | class Installer 5 | # Migrates installations performed by previous versions of CocoaPods. 6 | # 7 | class Migrator 8 | class << self 9 | # Performs the migration. 10 | # 11 | # @param [Sandbox] sandbox 12 | # The sandbox which should be migrated. 13 | # 14 | def migrate(sandbox) 15 | return unless sandbox.manifest 16 | end 17 | 18 | # @!group Migration Steps 19 | 20 | # @!group Private helpers 21 | 22 | # Check whether a migration is required 23 | # 24 | # @param [#to_s] target_version 25 | # See Version#new. 26 | # 27 | # @param [Sandbox] sandbox 28 | # The sandbox 29 | # 30 | # @return [void] 31 | # 32 | def installation_minor?(target_version, sandbox) 33 | sandbox.manifest.cocoapods_version < Version.new(target_version) 34 | end 35 | 36 | # Makes a path creating any intermediate directory and printing an UI 37 | # message. 38 | # 39 | # @path [#to_s] path 40 | # The path. 41 | # 42 | # @return [void] 43 | # 44 | def make_path(path) 45 | return if path.exist? 46 | UI.message "- Making path #{UI.path(path)}" do 47 | path.mkpath 48 | end 49 | end 50 | 51 | # Moves a path to another one printing an UI message. 52 | # 53 | # @path [#to_s] source 54 | # The path to move. 55 | # 56 | # @path [#to_s] destination 57 | # The destination path. 58 | # 59 | # @return [void] 60 | # 61 | def move(source, destination) 62 | return unless source.exist? 63 | make_path(destination.dirname) 64 | UI.message "- Moving #{UI.path(source)} to #{UI.path(destination)}" do 65 | FileUtils.mv(source.to_s, destination.to_s) 66 | end 67 | end 68 | 69 | # Deletes a path, including non empty directories, printing an UI 70 | # message. 71 | # 72 | # @path [#to_s] path 73 | # The path. 74 | # 75 | # @return [void] 76 | # 77 | def delete(path) 78 | return unless path.exist? 79 | UI.message "- Deleting #{UI.path(path)}" do 80 | FileUtils.rm_rf(path) 81 | end 82 | end 83 | end 84 | end 85 | end 86 | end 87 | -------------------------------------------------------------------------------- /lib/cocoapods/installer/pod_source_preparer.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Installer 3 | # Controller class responsible of executing the prepare command 4 | # of a single Pod. 5 | # 6 | class PodSourcePreparer 7 | # @return [Specification] the root specification of the Pod. 8 | # 9 | attr_reader :spec 10 | 11 | # @return [Pathname] the folder where the source of the Pod is located. 12 | # 13 | attr_reader :path 14 | 15 | # Initialize a new instance 16 | # 17 | # @param [Specification] spec the root specification of the Pod. 18 | # @param [Pathname] path the folder where the source of the Pod is located. 19 | # 20 | def initialize(spec, path) 21 | raise "Given spec isn't a root spec, but must be." unless spec.root? 22 | @spec = spec 23 | @path = path 24 | end 25 | 26 | #-----------------------------------------------------------------------# 27 | 28 | public 29 | 30 | # @!group Preparation 31 | 32 | # Executes the prepare command if there is one. 33 | # 34 | # @return [void] 35 | # 36 | def prepare! 37 | run_prepare_command 38 | end 39 | 40 | #-----------------------------------------------------------------------# 41 | 42 | private 43 | 44 | # @!group Preparation Steps 45 | 46 | extend Executable 47 | executable :bash 48 | 49 | # Runs the prepare command bash script of the spec. 50 | # 51 | # @note Unsets the `CDPATH` env variable before running the 52 | # shell script to avoid issues with relative paths 53 | # (issue #1694). 54 | # 55 | # @return [void] 56 | # 57 | def run_prepare_command 58 | return unless spec.prepare_command 59 | UI.section(' > Running prepare command', '', 1) do 60 | Dir.chdir(path) do 61 | begin 62 | ENV.delete('CDPATH') 63 | ENV['COCOAPODS_VERSION'] = Pod::VERSION 64 | prepare_command = spec.prepare_command.strip_heredoc.chomp 65 | full_command = "\nset -e\n" + prepare_command 66 | bash!('-c', full_command) 67 | ensure 68 | ENV.delete('COCOAPODS_VERSION') 69 | end 70 | end 71 | end 72 | end 73 | 74 | #-----------------------------------------------------------------------# 75 | end 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /lib/cocoapods/installer/podfile_validator.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Installer 3 | # Validate the podfile before installing to catch errors and 4 | # problems 5 | # 6 | class PodfileValidator 7 | # @return [Podfile] The podfile being validated 8 | # 9 | attr_reader :podfile 10 | 11 | # @return [Array] any errors that have occured during the validation 12 | # 13 | attr_reader :errors 14 | 15 | # @return [Array] any warnings that have occured during the validation 16 | # 17 | attr_reader :warnings 18 | 19 | # Initialize a new instance 20 | # @param [Podfile] podfile 21 | # The podfile to validate 22 | # 23 | def initialize(podfile) 24 | @podfile = podfile 25 | @errors = [] 26 | @warnings = [] 27 | @validated = false 28 | end 29 | 30 | # Validate the podfile 31 | # Errors are added to the errors array 32 | # 33 | def validate 34 | validate_pod_directives 35 | validate_no_abstract_only_pods! 36 | validate_dependencies_are_present! 37 | 38 | @validated = true 39 | end 40 | 41 | # Wether the podfile is valid is not 42 | # NOTE: Will execute `validate` if the podfile 43 | # has not yet been validated 44 | # 45 | def valid? 46 | validate unless @validated 47 | 48 | @validated && errors.empty? 49 | end 50 | 51 | # A message describing any errors in the 52 | # validation 53 | # 54 | def message 55 | errors.join("\n") 56 | end 57 | 58 | private 59 | 60 | def add_error(error) 61 | errors << error 62 | end 63 | 64 | def add_warning(warning) 65 | warnings << warning 66 | end 67 | 68 | def validate_pod_directives 69 | dependencies = podfile.target_definitions.flat_map do |_, target| 70 | target.dependencies 71 | end.uniq 72 | 73 | dependencies.each do |dependency| 74 | validate_conflicting_external_sources!(dependency) 75 | end 76 | end 77 | 78 | def validate_conflicting_external_sources!(dependency) 79 | external_source = dependency.external_source 80 | return false if external_source.nil? 81 | 82 | available_downloaders = Downloader.downloader_class_by_key.keys 83 | specified_downloaders = external_source.select { |key| available_downloaders.include?(key) } 84 | if specified_downloaders.size > 1 85 | add_error "The dependency `#{dependency.name}` specifies more than one download strategy(#{specified_downloaders.keys.join(',')})." \ 86 | 'Only one is allowed' 87 | end 88 | 89 | pod_spec_or_path = external_source[:podspec].present? || external_source[:path].present? 90 | if pod_spec_or_path && specified_downloaders.size > 0 91 | add_error "The dependency `#{dependency.name}` specifies `podspec` or `path` in combination with other" \ 92 | ' download strategies. This is not allowed' 93 | end 94 | end 95 | 96 | # Warns the user if the podfile is empty. 97 | # 98 | # @note The workspace is created in any case and all the user projects 99 | # are added to it, however the projects are not integrated as 100 | # there is no way to discern between target definitions which are 101 | # empty and target definitions which just serve the purpose to 102 | # wrap other ones. This is not an issue because empty target 103 | # definitions generate empty libraries. 104 | # 105 | # @return [void] 106 | # 107 | def validate_dependencies_are_present! 108 | if podfile.target_definitions.values.all?(&:empty?) 109 | add_warning 'The Podfile does not contain any dependencies.' 110 | end 111 | end 112 | 113 | # Verifies that no dependencies in the Podfile will end up not being built 114 | # at all. In other words, all dependencies _must_ belong to a non-abstract 115 | # target, or be inherited by a target where `inheritance == complete`. 116 | # 117 | def validate_no_abstract_only_pods! 118 | all_dependencies = podfile.dependencies 119 | concrete_dependencies = podfile.target_definition_list.reject(&:abstract?).flat_map(&:dependencies).uniq 120 | abstract_only_dependencies = all_dependencies - concrete_dependencies 121 | abstract_only_dependencies.each do |dep| 122 | add_error "The dependency `#{dep}` is not used in any concrete target." 123 | end 124 | end 125 | end 126 | end 127 | end 128 | -------------------------------------------------------------------------------- /lib/cocoapods/installer/post_install_hooks_context.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Installer 3 | # Context object designed to be used with the HooksManager which describes 4 | # the context of the installer. 5 | # 6 | class PostInstallHooksContext 7 | # @return [String] The path to the sandbox root (`Pods` directory). 8 | # 9 | attr_accessor :sandbox_root 10 | 11 | # @return [Project] The Pods Xcode project. 12 | # 13 | attr_accessor :pods_project 14 | 15 | # @return [Sandbox] The Sandbox for the project. 16 | # 17 | attr_accessor :sandbox 18 | 19 | # @return [Array] The list of 20 | # the CocoaPods umbrella targets generated by the installer. 21 | # 22 | attr_accessor :umbrella_targets 23 | 24 | # @return [PostInstallHooksContext] Convenience class generator method 25 | # 26 | # @param [Sandbox] sandbox 27 | # The sandbox 28 | # 29 | # @param [Array] aggregate_targets 30 | # The aggregate targets, which will been presented by an adequate 31 | # {UmbrellaTargetDescription} in the generated context. 32 | # 33 | # @return [HooksContext] Convenience class method to generate the 34 | # static context. 35 | # 36 | def self.generate(sandbox, aggregate_targets) 37 | umbrella_targets_descriptions = [] 38 | aggregate_targets.each do |umbrella| 39 | desc = UmbrellaTargetDescription.new 40 | desc.user_project = umbrella.user_project 41 | desc.user_targets = umbrella.user_targets 42 | desc.specs = umbrella.specs 43 | desc.platform_name = umbrella.platform.name 44 | desc.platform_deployment_target = umbrella.platform.deployment_target.to_s 45 | desc.cocoapods_target_label = umbrella.label 46 | umbrella_targets_descriptions << desc 47 | end 48 | 49 | result = new 50 | result.sandbox_root = sandbox.root.to_s 51 | result.pods_project = sandbox.project 52 | result.sandbox = sandbox 53 | result.umbrella_targets = umbrella_targets_descriptions 54 | result 55 | end 56 | 57 | # Pure data class which describes and umbrella target. 58 | # 59 | class UmbrellaTargetDescription 60 | # @return [Xcodeproj::Project] The user project into which this target 61 | # is integrated. 62 | # 63 | attr_accessor :user_project 64 | 65 | # @return [String] The path of the user project 66 | # integrated by this target. 67 | # 68 | def user_project_path 69 | user_project.path if user_project 70 | end 71 | 72 | # @return [Array] 73 | # The list of user targets integrated by this umbrella target. 74 | # 75 | attr_accessor :user_targets 76 | 77 | # @return [Array] The list of the UUIDs of the 78 | # user targets integrated by this umbrella 79 | # target. They can be used to find the 80 | # targets opening the project They can be used 81 | # to find the targets opening the project with 82 | # Xcodeproj. 83 | # 84 | def user_target_uuids 85 | user_targets.map(&:uuid) 86 | end 87 | 88 | # @return [Array] The list of the 89 | # specifications of the target. 90 | # 91 | attr_accessor :specs 92 | 93 | # @return [Symbol] The platform (either `:ios`, `:watchos`, `:tvos`, or `:osx`). 94 | # 95 | attr_accessor :platform_name 96 | 97 | # @return [String] The deployment target. 98 | # 99 | attr_accessor :platform_deployment_target 100 | 101 | # @return [String] The label for the target. 102 | # 103 | attr_accessor :cocoapods_target_label 104 | end 105 | end 106 | end 107 | end 108 | -------------------------------------------------------------------------------- /lib/cocoapods/installer/pre_install_hooks_context.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Installer 3 | # Context object designed to be used with the HooksManager which describes 4 | # the context of the installer before analysis has been completed. 5 | # 6 | class PreInstallHooksContext 7 | # @return [String] The path to the sandbox root (`Pods` directory). 8 | # 9 | attr_accessor :sandbox_root 10 | 11 | # @return [Podfile] The Podfile for the project. 12 | # 13 | attr_accessor :podfile 14 | 15 | # @return [Sandbox] The Sandbox for the project. 16 | # 17 | attr_accessor :sandbox 18 | 19 | # @return [Lockfile] The Lockfile for the project. 20 | # 21 | attr_accessor :lockfile 22 | 23 | # @param [Sandbox] sandbox see {#sandbox} 24 | # 25 | # @param [Podfile] podfile see {#podfile} 26 | # 27 | # @param [Lockfile] lockfile see {#lockfile} 28 | # 29 | # @return [PreInstallHooksContext] Convenience class method to generate the 30 | # static context. 31 | # 32 | def self.generate(sandbox, podfile, lockfile) 33 | result = new 34 | result.podfile = podfile 35 | result.sandbox = sandbox 36 | result.sandbox_root = sandbox.root.to_s 37 | result.lockfile = lockfile 38 | result 39 | end 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/cocoapods/installer/source_provider_hooks_context.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Installer 3 | # Context object designed to be used with the HooksManager which describes 4 | # the context of the installer before spec sources have been created 5 | # 6 | class SourceProviderHooksContext 7 | # @return [Array] The source objects to send to the installer 8 | # 9 | attr_reader :sources 10 | 11 | # @return [SourceProviderHooksContext] Convenience class method to generate the 12 | # static context. 13 | # 14 | def self.generate 15 | result = new 16 | result 17 | end 18 | 19 | def initialize 20 | @sources = [] 21 | end 22 | 23 | # @param [Source] Source object to be added to the installer 24 | # 25 | def add_source(source) 26 | unless source.nil? 27 | @sources << source 28 | end 29 | end 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/cocoapods/open-uri.rb: -------------------------------------------------------------------------------- 1 | # rubocop:disable Style/FileName 2 | 3 | require 'open-uri' 4 | 5 | # Allow OpenURI to follow http to https redirects. 6 | # 7 | module OpenURI 8 | # Whether {#open} should follow a redirect. 9 | # 10 | # Inspiration from: https://gist.github.com/1271420 11 | # Relevant issue: http://redmine.ruby-lang.org/issues/3719 12 | # Source here: https://github.com/ruby/ruby/blob/trunk/lib/open-uri.rb 13 | # 14 | # This test is intended to forbid a redirection from http://... to 15 | # file:///etc/passwd, file:///dev/zero, etc. CVE-2011-1521 16 | # https to http redirect is also forbidden intentionally. 17 | # It avoids sending secure cookie or referrer by non-secure HTTP protocol. 18 | # (RFC 2109 4.3.1, RFC 2965 3.3, RFC 2616 15.1.3) 19 | # However this is ad hoc. It should be extensible/configurable. 20 | # 21 | # @param [URI::Generic] uri1 22 | # the origin uri from where the redirect origins 23 | # 24 | # @param [URI::Generic] uri2 25 | # the target uri where to where the redirect points to 26 | # 27 | # @return [Bool] 28 | # 29 | def self.redirectable?(uri1, uri2) 30 | uri1.scheme.downcase == uri2.scheme.downcase || 31 | (/\A(?:http|ftp)\z/i =~ uri1.scheme && /\A(?:https?|ftp)\z/i =~ uri2.scheme) 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/cocoapods/resolver/lazy_specification.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Specification 3 | class Set 4 | class LazySpecification < BasicObject 5 | attr_reader :name, :version, :source 6 | 7 | def initialize(name, version, source) 8 | @name = name 9 | @version = version 10 | @source = source 11 | end 12 | 13 | def method_missing(method, *args, &block) 14 | specification.send(method, *args, &block) 15 | end 16 | 17 | def respond_to_missing?(method, include_all = false) 18 | specification.respond_to?(method, include_all) 19 | end 20 | 21 | def subspec_by_name(name = nil, raise_if_missing = true) 22 | if !name || name == self.name 23 | self 24 | else 25 | specification.subspec_by_name(name, raise_if_missing) 26 | end 27 | end 28 | 29 | def specification 30 | @specification ||= source.specification(name, version.version) 31 | end 32 | end 33 | 34 | class External 35 | def all_specifications 36 | [specification] 37 | end 38 | end 39 | 40 | def all_specifications 41 | @all_specifications ||= begin 42 | sources_by_version = {} 43 | versions_by_source.each do |source, versions| 44 | versions.each { |v| (sources_by_version[v] ||= []) << source } 45 | sources_by_version 46 | end 47 | 48 | duplicate_versions = sources_by_version.select { |_version, sources| sources.count > 1 } 49 | 50 | duplicate_versions.each do |version, sources| 51 | UI.warn "Found multiple specifications for `#{name} (#{version})`:\n" + 52 | sources. 53 | map { |s| s.specification_path(name, version) }. 54 | map { |v| "- #{v}" }.join("\n") 55 | end 56 | 57 | versions_by_source.flat_map do |source, versions| 58 | versions.map { |version| LazySpecification.new(name, version, source) } 59 | end 60 | end 61 | end 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /lib/cocoapods/sandbox/headers_store.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Sandbox 3 | # Provides support for managing a header directory. It also keeps track of 4 | # the header search paths. 5 | # 6 | class HeadersStore 7 | # @return [Pathname] the absolute path of this header directory. 8 | # 9 | def root 10 | sandbox.headers_root + @relative_path 11 | end 12 | 13 | # @return [Sandbox] the sandbox where this header directory is stored. 14 | # 15 | attr_reader :sandbox 16 | 17 | # @param [Sandbox] @see sandbox 18 | # 19 | # @param [String] relative_path 20 | # the relative path to the sandbox root and hence to the Pods 21 | # project. 22 | # 23 | def initialize(sandbox, relative_path) 24 | @sandbox = sandbox 25 | @relative_path = relative_path 26 | @search_paths = [] 27 | end 28 | 29 | # @param [Platform] platform 30 | # the platform for which the header search paths should be 31 | # returned 32 | # 33 | # @return [Array] All the search paths of the header directory in 34 | # xcconfig format. The paths are specified relative to the pods 35 | # root with the `${PODS_ROOT}` variable. 36 | # 37 | def search_paths(platform) 38 | platform_search_paths = @search_paths.select { |entry| entry[:platform] == platform.name } 39 | 40 | headers_dir = root.relative_path_from(sandbox.root).dirname 41 | ["${PODS_ROOT}/#{headers_dir}/#{@relative_path}"] + platform_search_paths.uniq.map { |entry| "${PODS_ROOT}/#{headers_dir}/#{entry[:path]}" } 42 | end 43 | 44 | # Removes the directory as it is regenerated from scratch during each 45 | # installation. 46 | # 47 | # @return [void] 48 | # 49 | def implode! 50 | root.rmtree if root.exist? 51 | end 52 | 53 | #-----------------------------------------------------------------------# 54 | 55 | public 56 | 57 | # @!group Adding headers 58 | 59 | # Adds headers to the directory. 60 | # 61 | # @param [Pathname] namespace 62 | # the path where the header file should be stored relative to the 63 | # headers directory. 64 | # 65 | # @param [Array] relative_header_paths 66 | # the path of the header file relative to the Pods project 67 | # (`PODS_ROOT` variable of the xcconfigs). 68 | # 69 | # @note This method does _not_ add the files to the search paths. 70 | # 71 | # @return [Array] 72 | # 73 | def add_files(namespace, relative_header_paths) 74 | relative_header_paths.map do |relative_header_path| 75 | add_file(namespace, relative_header_path, relative_header_path.basename) 76 | end 77 | end 78 | 79 | # Adds a header to the directory under different name. 80 | # 81 | # @param [Pathname] namespace 82 | # the path where the header file should be stored relative to the 83 | # headers directory. 84 | # 85 | # @param [Pathname] relative_header_path 86 | # the path of the header file relative to the Pods project 87 | # (`PODS_ROOT` variable of the xcconfigs). 88 | # 89 | # @param [String] final_name 90 | # the name under which the file should be available in the 91 | # headers directory. 92 | # 93 | # @note This method does _not_ add the file to the search paths. 94 | # 95 | # @return [Pathname] 96 | # 97 | def add_file(namespace, relative_header_path, final_name) 98 | namespaced_path = root + namespace 99 | namespaced_path.mkpath unless File.exist?(namespaced_path) 100 | 101 | absolute_source = (sandbox.root + relative_header_path) 102 | source = absolute_source.relative_path_from(namespaced_path) 103 | Dir.chdir(namespaced_path) do 104 | FileUtils.ln_sf(source, final_name) 105 | end 106 | namespaced_path + relative_header_path.basename 107 | end 108 | 109 | # Adds an header search path to the sandbox. 110 | # 111 | # @param [Pathname] path 112 | # the path tho add. 113 | # 114 | # @param [String] platform 115 | # the platform the search path applies to 116 | # 117 | # @return [void] 118 | # 119 | def add_search_path(path, platform) 120 | @search_paths << { :platform => platform.name, :path => (Pathname.new(@relative_path) + path) } 121 | end 122 | 123 | #-----------------------------------------------------------------------# 124 | end 125 | end 126 | end 127 | -------------------------------------------------------------------------------- /lib/cocoapods/sandbox/pod_dir_cleaner.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Sandbox 3 | class PodDirCleaner 4 | attr_reader :root 5 | attr_reader :specs_by_platform 6 | 7 | def initialize(root, specs_by_platform) 8 | @root = root 9 | @specs_by_platform = specs_by_platform 10 | end 11 | 12 | # Removes all the files not needed for the installation according to the 13 | # specs by platform. 14 | # 15 | # @return [void] 16 | # 17 | def clean! 18 | clean_paths.each { |path| FileUtils.rm_rf(path) } if root.exist? 19 | end 20 | 21 | private 22 | 23 | # @return [Array] the file accessors for all the 24 | # specifications on their respective platform. 25 | # 26 | def file_accessors 27 | @file_accessors ||= specs_by_platform.flat_map do |platform, specs| 28 | specs.flat_map { |spec| Sandbox::FileAccessor.new(path_list, spec.consumer(platform)) } 29 | end 30 | end 31 | 32 | # @return [Sandbox::PathList] The path list for this Pod. 33 | # 34 | def path_list 35 | @path_list ||= Sandbox::PathList.new(root) 36 | end 37 | 38 | # Finds the absolute paths, including hidden ones, of the files 39 | # that are not used by the pod and thus can be safely deleted. 40 | # 41 | # @note Implementation detail: Don't use `Dir#glob` as there is an 42 | # unexplained issue (#568, #572 and #602). 43 | # 44 | # @todo The paths are down-cased for the comparison as issues similar 45 | # to #602 lead the files not being matched and so cleaning all 46 | # the files. This solution might create side effects. 47 | # 48 | # @return [Array] The paths that can be deleted. 49 | # 50 | def clean_paths 51 | cached_used = used_files 52 | glob_options = File::FNM_DOTMATCH | File::FNM_CASEFOLD 53 | files = Pathname.glob(root + '**/*', glob_options).map(&:to_s) 54 | 55 | files.reject do |candidate| 56 | candidate = candidate.downcase 57 | candidate.end_with?('.', '..') || cached_used.any? do |path| 58 | path = path.downcase 59 | path.include?(candidate) || candidate.include?(path) 60 | end 61 | end 62 | end 63 | 64 | # @return [Array] The absolute path of all the files used by the 65 | # specifications (according to their platform) of this Pod. 66 | # 67 | def used_files 68 | files = [ 69 | file_accessors.map(&:vendored_frameworks), 70 | file_accessors.map(&:vendored_libraries), 71 | file_accessors.map(&:resource_bundle_files), 72 | file_accessors.map(&:license), 73 | file_accessors.map(&:prefix_header), 74 | file_accessors.map(&:preserve_paths), 75 | file_accessors.map(&:readme), 76 | file_accessors.map(&:resources), 77 | file_accessors.map(&:source_files), 78 | file_accessors.map(&:module_map), 79 | ] 80 | 81 | files.flatten.compact.map(&:to_s).uniq 82 | end 83 | end 84 | end 85 | end 86 | -------------------------------------------------------------------------------- /lib/cocoapods/sandbox/podspec_finder.rb: -------------------------------------------------------------------------------- 1 | module Pod 2 | class Sandbox 3 | class PodspecFinder 4 | attr_reader :root 5 | 6 | def initialize(root) 7 | @root = root 8 | end 9 | 10 | def podspecs 11 | return @specs_by_name if @specs_by_name 12 | @specs_by_name = {} 13 | spec_files = Pathname.glob(root + '{,*}.podspec{,.json}') 14 | spec_files.sort_by { |p| -p.to_path.split(File::SEPARATOR).size }.each do |file| 15 | begin 16 | spec = Specification.from_file(file) 17 | spec.validate_cocoapods_version 18 | @specs_by_name[spec.name] = spec 19 | rescue => e 20 | UI.warn "Unable to load a podspec from `#{file.basename}`, skipping:\n\n#{e}" 21 | end 22 | end 23 | @specs_by_name 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/cocoapods/user_interface/error_report.rb: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | 3 | require 'rbconfig' 4 | require 'cgi' 5 | 6 | module Pod 7 | module UserInterface 8 | module ErrorReport 9 | class << self 10 | def report(exception) 11 | <<-EOS 12 | 13 | #{'――― MARKDOWN TEMPLATE ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――'.reversed} 14 | 15 | ### Command 16 | 17 | ``` 18 | #{original_command} 19 | ``` 20 | 21 | #{report_instructions} 22 | 23 | #{stack} 24 | ### Plugins 25 | 26 | ``` 27 | #{plugins_string} 28 | ``` 29 | #{markdown_podfile} 30 | ### Error 31 | 32 | ``` 33 | #{exception.class} - #{exception.message} 34 | #{exception.backtrace.join("\n")} 35 | ``` 36 | 37 | #{'――― TEMPLATE END ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――'.reversed} 38 | 39 | #{'[!] Oh no, an error occurred.'.red} 40 | #{error_from_podfile(exception)} 41 | #{'Search for existing GitHub issues similar to yours:'.yellow} 42 | #{issues_url(exception)} 43 | 44 | #{'If none exists, create a ticket, with the template displayed above, on:'.yellow} 45 | https://github.com/CocoaPods/CocoaPods/issues/new 46 | 47 | #{'Be sure to first read the contributing guide for details on how to properly submit a ticket:'.yellow} 48 | https://github.com/CocoaPods/CocoaPods/blob/master/CONTRIBUTING.md 49 | 50 | Don't forget to anonymize any private data! 51 | 52 | EOS 53 | end 54 | 55 | def report_instructions 56 | <<-EOS 57 | ### Report 58 | 59 | * What did you do? 60 | 61 | * What did you expect to happen? 62 | 63 | * What happened instead? 64 | EOS 65 | end 66 | 67 | def stack 68 | parts = { 69 | 'CocoaPods' => Pod::VERSION, 70 | 'Ruby' => RUBY_DESCRIPTION, 71 | 'RubyGems' => Gem::VERSION, 72 | 'Host' => host_information, 73 | 'Xcode' => xcode_information, 74 | 'Git' => git_information, 75 | 'Ruby lib dir' => RbConfig::CONFIG['libdir'], 76 | 'Repositories' => repo_information, 77 | } 78 | justification = parts.keys.map(&:size).max 79 | 80 | str = <<-EOS 81 | ### Stack 82 | 83 | ``` 84 | EOS 85 | parts.each do |name, value| 86 | str << name.rjust(justification) 87 | str << ' : ' 88 | str << Array(value).join("\n" << (' ' * (justification + 3))) 89 | str << "\n" 90 | end 91 | 92 | str << "```\n" 93 | end 94 | 95 | def plugins_string 96 | plugins = installed_plugins 97 | max_name_length = plugins.keys.map(&:length).max 98 | plugins.map do |name, version| 99 | "#{name.ljust(max_name_length)} : #{version}" 100 | end.sort.join("\n") 101 | end 102 | 103 | def markdown_podfile 104 | return '' unless Config.instance.podfile_path && Config.instance.podfile_path.exist? 105 | <<-EOS 106 | 107 | ### Podfile 108 | 109 | ```ruby 110 | #{Config.instance.podfile_path.read.strip} 111 | ``` 112 | EOS 113 | end 114 | 115 | private 116 | 117 | def `(other) 118 | super 119 | rescue Errno::ENOENT => e 120 | "Unable to find an executable (#{e})" 121 | end 122 | 123 | def pathless_exception_message(message) 124 | message.gsub(/- \(.*\):/, '-') 125 | end 126 | 127 | def error_from_podfile(error) 128 | if error.message =~ /Podfile:(\d*)/ 129 | "\nIt appears to have originated from your Podfile at line #{Regexp.last_match[1]}.\n" 130 | end 131 | end 132 | 133 | def remove_color(string) 134 | string.gsub(/\e\[(\d+)m/, '') 135 | end 136 | 137 | def issues_url(exception) 138 | message = remove_color(pathless_exception_message(exception.message)) 139 | 'https://github.com/CocoaPods/CocoaPods/search?q=' \ 140 | "#{CGI.escape(message)}&type=Issues" 141 | end 142 | 143 | def host_information 144 | product, version, build = `sw_vers`.strip.split("\n").map { |line| line.split(':').last.strip } 145 | "#{product} #{version} (#{build})" 146 | end 147 | 148 | def xcode_information 149 | version, build = `xcodebuild -version`.strip.split("\n").map { |line| line.split(' ').last } 150 | "#{version} (#{build})" 151 | end 152 | 153 | def git_information 154 | `git --version`.strip.split("\n").first 155 | end 156 | 157 | def installed_plugins 158 | CLAide::Command::PluginManager.specifications. 159 | reduce({}) { |hash, s| hash.tap { |h| h[s.name] = s.version.to_s } } 160 | end 161 | 162 | def repo_information 163 | SourcesManager.all.map do |source| 164 | next unless source.type == 'file system' 165 | repo = source.repo 166 | Dir.chdir(repo) do 167 | url = `git config --get remote.origin.url 2>&1`.strip 168 | sha = `git rev-parse HEAD 2>&1`.strip 169 | "#{repo.basename} - #{url} @ #{sha}" 170 | end 171 | end 172 | end 173 | 174 | def original_command 175 | "#{$PROGRAM_NAME} #{ARGV.join(' ')}" 176 | end 177 | end 178 | end 179 | end 180 | end 181 | --------------------------------------------------------------------------------