cocoapods-generate-minlison 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/LICENSE.md +9 -0
- data/README.md +131 -0
- data/VERSION +1 -0
- data/lib/cocoapods/command/gen.rb +118 -0
- data/lib/cocoapods/generate.rb +10 -0
- data/lib/cocoapods/generate/configuration.rb +342 -0
- data/lib/cocoapods/generate/installer.rb +347 -0
- data/lib/cocoapods/generate/podfile_generator.rb +378 -0
- data/lib/cocoapods_plugin.rb +7 -0
- metadata +108 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a7c911d9b5f53ae4a8ae418965e638901f93f6b11ea44996043771b2856046e8
|
4
|
+
data.tar.gz: a53b6c9a1f70684ef0e283fa558ba426ce7ecd5a760ac0ca3a1b58cfb0a55909
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ffa21baf0cfcec3474430ce033331db762aa9bf654ababc844aa5ef7412cad8d95a6d4074a892c3b0b749ac045cb196fdd4a669ee402100e026b7b2e4d88d801
|
7
|
+
data.tar.gz: a11298fa62d3702675871604b4c9a38a2b0de00f543fff85e99c306e4b2ca126c2ff072d10130bc10f8e392544f255109403a3ef5b6dd8231894631030b46d99
|
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
In the interest of fostering an open and welcoming environment, we as
|
6
|
+
contributors and maintainers pledge to making participation in our project and
|
7
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
8
|
+
size, disability, ethnicity, gender identity and expression, level of experience,
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity and
|
10
|
+
orientation.
|
11
|
+
|
12
|
+
## Our Standards
|
13
|
+
|
14
|
+
Examples of behavior that contributes to creating a positive environment
|
15
|
+
include:
|
16
|
+
|
17
|
+
* Using welcoming and inclusive language
|
18
|
+
* Being respectful of differing viewpoints and experiences
|
19
|
+
* Gracefully accepting constructive criticism
|
20
|
+
* Focusing on what is best for the community
|
21
|
+
* Showing empathy towards other community members
|
22
|
+
|
23
|
+
Examples of unacceptable behavior by participants include:
|
24
|
+
|
25
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
26
|
+
advances
|
27
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
28
|
+
* Public or private harassment
|
29
|
+
* Publishing others' private information, such as a physical or electronic
|
30
|
+
address, without explicit permission
|
31
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
32
|
+
professional setting
|
33
|
+
|
34
|
+
## Our Responsibilities
|
35
|
+
|
36
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
37
|
+
behavior and are expected to take appropriate and fair corrective action in
|
38
|
+
response to any instances of unacceptable behavior.
|
39
|
+
|
40
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
41
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
42
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
43
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
44
|
+
threatening, offensive, or harmful.
|
45
|
+
|
46
|
+
## Scope
|
47
|
+
|
48
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
49
|
+
when an individual is representing the project or its community. Examples of
|
50
|
+
representing a project or community include using an official project e-mail
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
53
|
+
further defined and clarified by project maintainers.
|
54
|
+
|
55
|
+
## Enforcement
|
56
|
+
|
57
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
58
|
+
reported by contacting the project team at segiddins@squareup.com. All
|
59
|
+
complaints will be reviewed and investigated and will result in a response that
|
60
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
61
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
62
|
+
Further details of specific enforcement policies may be posted separately.
|
63
|
+
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
65
|
+
faith may face temporary or permanent repercussions as determined by other
|
66
|
+
members of the project's leadership.
|
67
|
+
|
68
|
+
## Attribution
|
69
|
+
|
70
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
71
|
+
available at [http://contributor-covenant.org/version/1/4][version]
|
72
|
+
|
73
|
+
[homepage]: http://contributor-covenant.org
|
74
|
+
[version]: http://contributor-covenant.org/version/1/4/
|
data/LICENSE.md
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2019
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
6
|
+
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
8
|
+
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
# `cocoapods-generate`
|
2
|
+
|
3
|
+
A [CocoaPods](https://cocoapods.org/) plugin that allows you to easily generate a workspace from a podspec.
|
4
|
+
|
5
|
+
Whether you want to completely remove all Xcode projects from your library's repository or you want to be able to focus on a small piece of a monorepo, a single `pod gen` command will build up a workspace suitable for writing, running, testing, and debugging in Xcode.
|
6
|
+
|
7
|
+
When you're done working, you don't have to do anything -- and that means no merge conflicts, no managing Xcode projects, and no tearing down a sample app setup. `pod gen` manages all that for you.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem 'cocoapods-generate'
|
15
|
+
```
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
$ gem install cocoapods-generate
|
24
|
+
|
25
|
+
## CLI
|
26
|
+
|
27
|
+
The main point of interaction with the plugin is via the `pod gen` command.
|
28
|
+
It takes in a list of podspecs (or directories) as arguments,
|
29
|
+
as well as many options that modify how `gen` will create your workspace.
|
30
|
+
|
31
|
+
<!-- begin cli usage -->
|
32
|
+
```
|
33
|
+
Usage:
|
34
|
+
|
35
|
+
$ pod gen [PODSPEC|DIR ...]
|
36
|
+
|
37
|
+
Generates Xcode workspaces from a podspec.
|
38
|
+
|
39
|
+
pod gen allows you to keep your Podfile and podspecs as the single source of
|
40
|
+
truth for pods under development. By generating throw-away workspaces capable of
|
41
|
+
building, running, and testing a pod, you can focus on library development
|
42
|
+
without worrying about other code or managing an Xcode project.
|
43
|
+
|
44
|
+
pod gen works particularly well for monorepo projects, since it is capable of
|
45
|
+
using your existing settings when generating the workspace, making each gen'ed
|
46
|
+
project truly a small slice of the larger application.
|
47
|
+
|
48
|
+
Options:
|
49
|
+
|
50
|
+
--silent Show nothing
|
51
|
+
--verbose Show more debugging information
|
52
|
+
--no-ansi Show output without ANSI codes
|
53
|
+
--help Show help banner of specified command
|
54
|
+
--podfile-path=PATH Path to podfile to use
|
55
|
+
--use-podfile Whether restrictions should be copied from
|
56
|
+
the podfile
|
57
|
+
--use-podfile-plugins Whether plugins should be copied from the
|
58
|
+
podfile
|
59
|
+
--use-lockfile Whether the lockfile should be used to
|
60
|
+
discover transitive dependencies
|
61
|
+
--use-lockfile-versions Whether versions from the lockfile should
|
62
|
+
be used
|
63
|
+
--use-libraries Whether to use libraries instead of
|
64
|
+
frameworks
|
65
|
+
--generate-multiple-pod-projects Whether to generate multiple Xcode projects
|
66
|
+
--incremental-installation Whether to use incremental installation
|
67
|
+
--gen-directory=PATH Path to generate workspaces in
|
68
|
+
--auto-open Whether to automatically open the generated
|
69
|
+
workspaces
|
70
|
+
--clean Whether to clean the generated directories
|
71
|
+
before generating
|
72
|
+
--app-host-source-dir=DIR A directory containing sources to use for
|
73
|
+
the app host
|
74
|
+
--sources=SOURCE1,SOURCE2 The sources from which to pull dependent
|
75
|
+
pods (defaults to all repos in the podfile
|
76
|
+
if using the podfile, else all available
|
77
|
+
repos). Can be a repo name or URL. Multiple
|
78
|
+
sources must be comma-delimited.
|
79
|
+
--local-sources=SOURCE1,SOURCE2 Paths from which to find local podspecs for
|
80
|
+
transitive dependencies. Multiple
|
81
|
+
local-sources must be comma-delimited.
|
82
|
+
--repo-update Force running `pod repo update` before
|
83
|
+
install
|
84
|
+
--use-default-plugins Whether installation should activate
|
85
|
+
default plugins
|
86
|
+
--deterministic-uuids Whether installation should use
|
87
|
+
deterministic UUIDs for pods projects
|
88
|
+
--share-schemes-for-development-pods Whether installation should share schemes
|
89
|
+
for development pods
|
90
|
+
--warn-for-multiple-pod-sources Whether installation should warn when a pod
|
91
|
+
is found in multiple sources
|
92
|
+
--use-modular-headers Whether the target should be generated as a
|
93
|
+
clang module, treating dependencies as
|
94
|
+
modules, as if `use_modular_headers!` were
|
95
|
+
specified. Will error if both this option
|
96
|
+
and a podfile are specified
|
97
|
+
```
|
98
|
+
<!-- end cli usage -->
|
99
|
+
|
100
|
+
## `.gen_config.yml`
|
101
|
+
|
102
|
+
All of the command line options above can also be specified in a `.gen_config.yml` file.
|
103
|
+
|
104
|
+
For example, the equivalent of running `pod gen --no-deterministic-uuids --sources=a,b --gen-directory=/tmp/gen --use-libraries` would be
|
105
|
+
|
106
|
+
```yaml
|
107
|
+
deterministic_uuids: false
|
108
|
+
sources:
|
109
|
+
- a
|
110
|
+
- b
|
111
|
+
gen_directory: /tmp/gen
|
112
|
+
use_libraries: true
|
113
|
+
```
|
114
|
+
|
115
|
+
## Development
|
116
|
+
|
117
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/rake spec` to run the tests.
|
118
|
+
|
119
|
+
You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
120
|
+
|
121
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
122
|
+
|
123
|
+
To release a new version, update the version number in `VERSION`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
124
|
+
|
125
|
+
## Contributing
|
126
|
+
|
127
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/square/cocoapods-generate. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
128
|
+
|
129
|
+
## Code of Conduct
|
130
|
+
|
131
|
+
Everyone interacting in the cocoapods-generate project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/square/cocoapods-generate/blob/master/CODE_OF_CONDUCT.md).
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.5.0
|
@@ -0,0 +1,118 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cocoapods/generate'
|
4
|
+
|
5
|
+
module Pod
|
6
|
+
class Command
|
7
|
+
class Gen < Command
|
8
|
+
self.summary = 'Generates Xcode workspaces from a podspec.'
|
9
|
+
|
10
|
+
self.description = <<-DESC.strip_heredoc
|
11
|
+
#{summary}
|
12
|
+
|
13
|
+
pod gen allows you to keep your Podfile and podspecs as the single source of
|
14
|
+
truth for pods under development. By generating throw-away workspaces capable of
|
15
|
+
building, running, and testing a pod, you can focus on library development
|
16
|
+
without worrying about other code or managing an Xcode project.
|
17
|
+
|
18
|
+
pod gen works particularly well for monorepo projects, since it is capable of
|
19
|
+
using your existing settings when generating the workspace, making each gen'ed
|
20
|
+
project truly a small slice of the larger application.
|
21
|
+
DESC
|
22
|
+
|
23
|
+
def self.options
|
24
|
+
super.concat(Generate::Configuration.options.map do |option|
|
25
|
+
next unless option.cli_name
|
26
|
+
|
27
|
+
flag = "--#{option.cli_name}"
|
28
|
+
flag += "=#{option.arg_name}" if option.arg_name
|
29
|
+
[flag, option.message]
|
30
|
+
end.compact)
|
31
|
+
end
|
32
|
+
|
33
|
+
self.arguments = [
|
34
|
+
CLAide::Argument.new(%w[PODSPEC DIR], false, true)
|
35
|
+
]
|
36
|
+
|
37
|
+
# @return [Configuration]
|
38
|
+
# the configuration used when generating workspaces
|
39
|
+
#
|
40
|
+
attr_reader :configuration
|
41
|
+
|
42
|
+
def initialize(argv)
|
43
|
+
options_hash = Generate::Configuration.options.each_with_object({}) do |option, options|
|
44
|
+
value =
|
45
|
+
if option.name == :podspec_paths
|
46
|
+
argv.arguments!
|
47
|
+
elsif option.flag?
|
48
|
+
argv.flag?(option.cli_name)
|
49
|
+
else
|
50
|
+
argv.option(option.cli_name)
|
51
|
+
end
|
52
|
+
|
53
|
+
next if value.nil?
|
54
|
+
options[option.name] = option.coerce(value)
|
55
|
+
end
|
56
|
+
@configuration = merge_configuration(options_hash)
|
57
|
+
super
|
58
|
+
end
|
59
|
+
|
60
|
+
def run
|
61
|
+
UI.puts "[pod gen] Running with #{configuration.to_s.gsub("\n", " \n")}" if configuration.pod_config.verbose?
|
62
|
+
|
63
|
+
# this is done here rather than in the installer so we only update sources once,
|
64
|
+
# even if there are multiple podspecs
|
65
|
+
update_sources if configuration.repo_update?
|
66
|
+
|
67
|
+
Generate::PodfileGenerator.new(configuration).podfiles_by_spec.each do |spec, podfile|
|
68
|
+
Generate::Installer.new(configuration, spec, podfile).install!
|
69
|
+
end
|
70
|
+
|
71
|
+
remove_warnings(UI.warnings)
|
72
|
+
end
|
73
|
+
|
74
|
+
def validate!
|
75
|
+
super
|
76
|
+
|
77
|
+
config_errors = configuration.validate
|
78
|
+
help! config_errors.join("\n ") if config_errors.any?
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def merge_configuration(options)
|
84
|
+
# must use #to_enum explicitly since descend doesn't return an enumerator on 2.1
|
85
|
+
config_hashes = Pathname.pwd.to_enum(:descend).map do |dir|
|
86
|
+
path = dir + '.gen_config.yml'
|
87
|
+
next unless path.file?
|
88
|
+
Pod::Generate::Configuration.from_file(path)
|
89
|
+
end
|
90
|
+
|
91
|
+
env = Generate::Configuration.from_env(ENV)
|
92
|
+
config_hashes.insert(-2, env)
|
93
|
+
config_hashes << options
|
94
|
+
|
95
|
+
configuration = config_hashes.compact.each_with_object({}) { |e, h| h.merge!(e) }
|
96
|
+
Pod::Generate::Configuration.new(pod_config: config, **configuration)
|
97
|
+
end
|
98
|
+
|
99
|
+
def remove_warnings(warnings)
|
100
|
+
warnings.reject! do |warning|
|
101
|
+
warning[:message].include? 'Automatically assigning platform'
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def update_sources
|
106
|
+
UI.title 'Updating specs repos' do
|
107
|
+
configuration.sources.each do |source|
|
108
|
+
source = config.sources_manager.source_with_name_or_url(source)
|
109
|
+
UI.titled_section "Updating spec repo `#{source.name}`" do
|
110
|
+
source.update(config.verbose?)
|
111
|
+
source.verify_compatibility!
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Pod
|
5
|
+
module Generate
|
6
|
+
autoload :Configuration, 'cocoapods/generate/configuration'
|
7
|
+
autoload :Installer, 'cocoapods/generate/installer'
|
8
|
+
autoload :PodfileGenerator, 'cocoapods/generate/podfile_generator'
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,342 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pod
|
4
|
+
module Generate
|
5
|
+
class Configuration
|
6
|
+
@options = []
|
7
|
+
class << self
|
8
|
+
# @return [Array<Option>]
|
9
|
+
# all of the options available in the configuration
|
10
|
+
#
|
11
|
+
attr_reader :options
|
12
|
+
end
|
13
|
+
|
14
|
+
Option = Struct.new(:name, :type, :default, :message, :arg_name, :validator, :coercer) do
|
15
|
+
def validate(value)
|
16
|
+
return if value.nil?
|
17
|
+
errors = []
|
18
|
+
if (exceptions = Array(value).grep(Exception)) && exceptions.any?
|
19
|
+
errors << "Error computing #{name}"
|
20
|
+
errors.concat exceptions.map(&:message)
|
21
|
+
else
|
22
|
+
errors << "got type #{value.class}, expected object of type #{Array(type).join('|')}" unless Array(type).any? { |t| t === value }
|
23
|
+
validator_errors = begin
|
24
|
+
validator && validator[value]
|
25
|
+
rescue StandardError
|
26
|
+
"failed to run validator (#{$ERROR_INFO})"
|
27
|
+
end
|
28
|
+
errors.concat Array(validator_errors) if validator_errors
|
29
|
+
errors.unshift "#{value.inspect} invalid for #{name}" if errors.any?
|
30
|
+
end
|
31
|
+
errors.join(', ') unless errors.empty?
|
32
|
+
end
|
33
|
+
|
34
|
+
def cli_name
|
35
|
+
return unless message
|
36
|
+
@cli_name ||= name.to_s.tr '_', '-'
|
37
|
+
end
|
38
|
+
|
39
|
+
def flag?
|
40
|
+
arg_name.nil?
|
41
|
+
end
|
42
|
+
|
43
|
+
def coerce(value)
|
44
|
+
coercer ? coercer[value] : value
|
45
|
+
end
|
46
|
+
end
|
47
|
+
private_constant :Option
|
48
|
+
|
49
|
+
# Declares a new option
|
50
|
+
#
|
51
|
+
# @!macro [attach] $0
|
52
|
+
# @attribute [r] $1
|
53
|
+
# @return [$2] $4
|
54
|
+
# defaults to `$3`
|
55
|
+
#
|
56
|
+
def self.option(*args)
|
57
|
+
options << Option.new(*args)
|
58
|
+
end
|
59
|
+
private_class_method :option
|
60
|
+
|
61
|
+
# @visibility private
|
62
|
+
#
|
63
|
+
# Implements `===` to do type checking against an array.
|
64
|
+
#
|
65
|
+
class ArrayOf
|
66
|
+
attr_reader :types
|
67
|
+
|
68
|
+
def initialize(*types)
|
69
|
+
@types = types
|
70
|
+
end
|
71
|
+
|
72
|
+
def to_s
|
73
|
+
"Array<#{types.join('|')}>"
|
74
|
+
end
|
75
|
+
|
76
|
+
# @return [Boolean] whether the given object is an array with elements all of the given types
|
77
|
+
#
|
78
|
+
def ===(other)
|
79
|
+
other.is_a?(Array) && other.all? { |o| types.any? { |t| t === o } }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
private_constant :ArrayOf
|
83
|
+
|
84
|
+
# @visibility private
|
85
|
+
#
|
86
|
+
# Implements `===` to do type checking against a hash.
|
87
|
+
#
|
88
|
+
class HashOf
|
89
|
+
attr_reader :key_types, :value_types
|
90
|
+
|
91
|
+
def initialize(keys:, values:)
|
92
|
+
@key_types = keys
|
93
|
+
@value_types = values
|
94
|
+
end
|
95
|
+
|
96
|
+
def to_s
|
97
|
+
"Hash<#{key_types.join('|')} => #{value_types.join('|')}}>"
|
98
|
+
end
|
99
|
+
|
100
|
+
# @return [Boolean] whether the given object is a hash with elements all of the given types
|
101
|
+
#
|
102
|
+
def ===(other)
|
103
|
+
other.is_a?(Hash) && other.all? do |key, value|
|
104
|
+
key_types.any? { |t| t === key } &&
|
105
|
+
value_types.any? { |t| t === value }
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
private_constant :HashOf
|
110
|
+
|
111
|
+
coerce_to_bool = lambda do |value|
|
112
|
+
if value.is_a?(String)
|
113
|
+
value =
|
114
|
+
case value.downcase
|
115
|
+
when ''
|
116
|
+
nil
|
117
|
+
when 'true'
|
118
|
+
true
|
119
|
+
when 'false'
|
120
|
+
false
|
121
|
+
end
|
122
|
+
end
|
123
|
+
value
|
124
|
+
end
|
125
|
+
|
126
|
+
coerce_to_pathname = lambda do |path|
|
127
|
+
path && Pathname(path).expand_path
|
128
|
+
end
|
129
|
+
|
130
|
+
BOOLEAN = [TrueClass, FalseClass].freeze
|
131
|
+
private_constant :BOOLEAN
|
132
|
+
|
133
|
+
option :pod_config, Config, 'Pod::Config.instance', nil
|
134
|
+
|
135
|
+
option :podfile_path, [String, Pathname], 'pod_config.podfile_path', 'Path to podfile to use', 'PATH', ->(path) { 'file does not exist' unless path.file? }, coerce_to_pathname
|
136
|
+
option :podfile, [Podfile], 'Podfile.from_file(podfile_path) if (podfile_path && File.file?(File.expand_path(podfile_path)))'
|
137
|
+
option :use_podfile, BOOLEAN, '!!podfile', 'Whether restrictions should be copied from the podfile', nil, nil, coerce_to_bool
|
138
|
+
option :use_podfile_plugins, BOOLEAN, 'use_podfile', 'Whether plugins should be copied from the podfile', nil, nil, coerce_to_bool
|
139
|
+
option :podfile_plugins, HashOf.new(keys: [String], values: [NilClass, HashOf.new(keys: [String], values: [TrueClass, FalseClass, NilClass, String, Hash, Array])]),
|
140
|
+
'(use_podfile && podfile) ? podfile.plugins : {}',
|
141
|
+
nil,
|
142
|
+
nil,
|
143
|
+
nil,
|
144
|
+
->(hash) { Hash[hash] }
|
145
|
+
|
146
|
+
option :lockfile, [Pod::Lockfile], 'pod_config.lockfile', nil
|
147
|
+
option :use_lockfile, BOOLEAN, '!!lockfile', 'Whether the lockfile should be used to discover transitive dependencies', nil, nil, coerce_to_bool
|
148
|
+
option :use_lockfile_versions, BOOLEAN, 'use_lockfile', 'Whether versions from the lockfile should be used', nil, nil, coerce_to_bool
|
149
|
+
|
150
|
+
option :use_libraries, BOOLEAN, 'false', 'Whether to use libraries instead of frameworks', nil, nil, coerce_to_bool
|
151
|
+
|
152
|
+
option :generate_multiple_pod_projects, BOOLEAN, 'false', 'Whether to generate multiple Xcode projects', nil, nil, coerce_to_bool
|
153
|
+
option :incremental_installation, BOOLEAN, 'false', 'Whether to use incremental installation', nil, nil, coerce_to_bool
|
154
|
+
|
155
|
+
option :gen_directory, [String, Pathname], 'Pathname("gen").expand_path', 'Path to generate workspaces in', 'PATH', ->(path) { 'path is file' if path.file? }, coerce_to_pathname
|
156
|
+
option :auto_open, BOOLEAN, 'false', 'Whether to automatically open the generated workspaces', nil, nil, coerce_to_bool
|
157
|
+
option :clean, BOOLEAN, 'false', 'Whether to clean the generated directories before generating', nil, nil, coerce_to_bool
|
158
|
+
|
159
|
+
option :app_host_source_dir, [String, Pathname], 'nil',
|
160
|
+
'A directory containing sources to use for the app host',
|
161
|
+
'DIR',
|
162
|
+
->(dir) { 'not a directory' unless dir.directory? },
|
163
|
+
coerce_to_pathname
|
164
|
+
|
165
|
+
option :podspec_paths, ArrayOf.new(String, Pathname, URI),
|
166
|
+
'[Pathname(?.)]',
|
167
|
+
nil,
|
168
|
+
nil,
|
169
|
+
->(paths) { ('paths do not exist' unless paths.all? { |p| p.is_a?(URI) || p.exist? }) },
|
170
|
+
->(paths) { paths && paths.map { |path| path.to_s =~ %r{https?://} ? URI(path) : Pathname(path).expand_path } }
|
171
|
+
option :podspecs, ArrayOf.new(Pod::Specification),
|
172
|
+
'self.class.podspecs_from_paths(podspec_paths)',
|
173
|
+
nil,
|
174
|
+
nil,
|
175
|
+
->(specs) { 'no podspecs found' if specs.empty? },
|
176
|
+
->(paths) { paths && paths.map { |path| Pathname(path).expand_path } }
|
177
|
+
|
178
|
+
# installer options
|
179
|
+
option :sources, ArrayOf.new(String),
|
180
|
+
'if use_podfile && podfile then ::Pod::Installer::Analyzer.new(:sandbox, podfile).sources.map(&:url) else pod_config.sources_manager.all.map(&:url) end',
|
181
|
+
'The sources from which to pull dependent pods (defaults to all repos in the podfile if using the podfile, else all available repos). Can be a repo name or URL. Multiple sources must be comma-delimited.',
|
182
|
+
'SOURCE1,SOURCE2',
|
183
|
+
->(_) { nil },
|
184
|
+
->(sources) { Array(sources).flat_map { |s| s.split(',') } }
|
185
|
+
option :local_sources, ArrayOf.new(String),
|
186
|
+
[],
|
187
|
+
'Paths from which to find local podspecs for transitive dependencies. Multiple local-sources must be comma-delimited.',
|
188
|
+
'SOURCE1,SOURCE2',
|
189
|
+
->(_) { nil },
|
190
|
+
->(local_sources) { Array(local_sources).flat_map { |s| s.split(',') } }
|
191
|
+
option :repo_update, BOOLEAN, 'false', 'Force running `pod repo update` before install', nil, nil, coerce_to_bool
|
192
|
+
option :use_default_plugins, BOOLEAN, 'false', 'Whether installation should activate default plugins', nil, nil, coerce_to_bool
|
193
|
+
option :deterministic_uuids, BOOLEAN, 'false', 'Whether installation should use deterministic UUIDs for pods projects', nil, nil, coerce_to_bool
|
194
|
+
option :share_schemes_for_development_pods, BOOLEAN, 'true', 'Whether installation should share schemes for development pods', nil, nil, coerce_to_bool
|
195
|
+
option :warn_for_multiple_pod_sources, BOOLEAN, 'false', 'Whether installation should warn when a pod is found in multiple sources', nil, nil, coerce_to_bool
|
196
|
+
option :use_modular_headers, BOOLEAN, 'false', 'Whether the target should be generated as a clang module, treating dependencies as modules, as if `use_modular_headers!` were specified. Will error if both this option and a podfile are specified', nil, nil, coerce_to_bool
|
197
|
+
|
198
|
+
options.freeze
|
199
|
+
options.each do |o|
|
200
|
+
attr_reader o.name
|
201
|
+
alias_method :"#{o.name}?", o.name if o.type == BOOLEAN
|
202
|
+
end
|
203
|
+
|
204
|
+
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
205
|
+
# @!visibility private
|
206
|
+
def initialize(
|
207
|
+
#{options.map { |o| "#{o.name}: (begin (#{o.default}); rescue => e; e; end)" }.join(', ')}
|
208
|
+
)
|
209
|
+
#{options.map { |o| "@#{o.name} = #{o.name}" }.join('; ')}
|
210
|
+
end
|
211
|
+
RUBY
|
212
|
+
|
213
|
+
# @return [Hash<Symbol,Object>] the configuration hash parsed from the given file
|
214
|
+
#
|
215
|
+
# @param [Pathname] path
|
216
|
+
#
|
217
|
+
# @raises [Informative] if the file does not exist or is not a YAML hash
|
218
|
+
#
|
219
|
+
def self.from_file(path)
|
220
|
+
raise Informative, "No cocoapods-generate configuration found at #{UI.path path}" unless path.file?
|
221
|
+
require 'yaml'
|
222
|
+
yaml = YAML.load_file(path)
|
223
|
+
unless yaml.is_a?(Hash)
|
224
|
+
unless path.read.strip.empty?
|
225
|
+
raise Informative, "Hash not found in configuration at #{UI.path path} -- got #{yaml.inspect}"
|
226
|
+
end
|
227
|
+
yaml = {}
|
228
|
+
end
|
229
|
+
yaml = yaml.with_indifferent_access
|
230
|
+
|
231
|
+
Dir.chdir(path.dirname) do
|
232
|
+
options.each_with_object({}) do |option, config|
|
233
|
+
next unless yaml.key?(option.name)
|
234
|
+
config[option.name] = option.coerce yaml[option.name]
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
# @return [Hash<Symbol,Object>] the configuration hash parsed from the env
|
240
|
+
#
|
241
|
+
# @param [ENV,Hash<String,String>] env
|
242
|
+
#
|
243
|
+
def self.from_env(env = ENV)
|
244
|
+
options.each_with_object({}) do |option, config|
|
245
|
+
next unless (value = env["COCOAPODS_GENERATE_#{option.name.upcase}"])
|
246
|
+
config[option.name] = option.coerce(value)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
# @return [Array<String>] errors in the configuration
|
251
|
+
#
|
252
|
+
def validate
|
253
|
+
hash = to_h
|
254
|
+
self.class.options.map do |option|
|
255
|
+
option.validate(hash[option.name])
|
256
|
+
end.compact
|
257
|
+
end
|
258
|
+
|
259
|
+
# @return [Configuration] a new configuration object with the given changes applies
|
260
|
+
#
|
261
|
+
# @param [Hash<Symbol,Object>] changes
|
262
|
+
#
|
263
|
+
def with_changes(changes)
|
264
|
+
self.class.new(**to_h.merge(changes))
|
265
|
+
end
|
266
|
+
|
267
|
+
# @return [Hash<Symbol,Object>]
|
268
|
+
# a hash where the keys are option names and values are the non-nil set values
|
269
|
+
#
|
270
|
+
def to_h
|
271
|
+
self.class.options.each_with_object({}) do |option, hash|
|
272
|
+
value = send(option.name)
|
273
|
+
next if value.nil?
|
274
|
+
hash[option.name] = value
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
# @return [Boolean] whether this configuration is equivalent to other
|
279
|
+
#
|
280
|
+
def ==(other)
|
281
|
+
self.class == other.class &&
|
282
|
+
to_h == other.to_h
|
283
|
+
end
|
284
|
+
|
285
|
+
# @return [String] a string describing the configuration, suitable for UI presentation
|
286
|
+
#
|
287
|
+
def to_s
|
288
|
+
hash = to_h
|
289
|
+
hash.delete(:pod_config)
|
290
|
+
hash.each_with_index.each_with_object('`pod gen` configuration {'.dup) do |((k, v), i), s|
|
291
|
+
s << ',' unless i.zero?
|
292
|
+
s << "\n" << ' ' << k.to_s << ': ' << v.to_s.gsub(/:0x\h+/, '')
|
293
|
+
end << ' }'
|
294
|
+
end
|
295
|
+
|
296
|
+
# @return [Pathname] the directory for installation of the generated workspace
|
297
|
+
#
|
298
|
+
# @param [String] name the name of the pod
|
299
|
+
#
|
300
|
+
def gen_dir_for_pod(name)
|
301
|
+
gen_directory.join(name)
|
302
|
+
end
|
303
|
+
|
304
|
+
# @return [Boolean] whether gen should install with dynamic frameworks
|
305
|
+
#
|
306
|
+
def use_frameworks?
|
307
|
+
!use_libraries?
|
308
|
+
end
|
309
|
+
|
310
|
+
# @return [Array<Specification>] the podspecs found at the given paths.
|
311
|
+
# This method will download specs from URLs and traverse a directory's children.
|
312
|
+
#
|
313
|
+
# @param [Array<Pathname,URI>] paths
|
314
|
+
# the paths to search for podspecs
|
315
|
+
#
|
316
|
+
def self.podspecs_from_paths(paths)
|
317
|
+
paths = [Pathname('.')] if paths.empty?
|
318
|
+
paths.flat_map do |path|
|
319
|
+
if path.is_a?(URI)
|
320
|
+
require 'cocoapods/open-uri'
|
321
|
+
begin
|
322
|
+
contents = open(path.to_s).read
|
323
|
+
rescue StandardError => e
|
324
|
+
next e
|
325
|
+
end
|
326
|
+
begin
|
327
|
+
Pod::Specification.from_string contents, path.to_s
|
328
|
+
rescue StandardError
|
329
|
+
$ERROR_INFO
|
330
|
+
end
|
331
|
+
elsif path.directory?
|
332
|
+
glob = Pathname.glob(path + '*.podspec{.json,}')
|
333
|
+
next StandardError.new "no specs found in #{UI.path path}" if glob.empty?
|
334
|
+
glob.map { |f| Pod::Specification.from_file(f) }
|
335
|
+
else
|
336
|
+
Pod::Specification.from_file(path)
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end
|