cocoapods-pack 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rubocop.yml +43 -0
  4. data/Gemfile +8 -0
  5. data/Gemfile.lock +149 -0
  6. data/LICENSE +202 -0
  7. data/README.md +116 -0
  8. data/Rakefile +8 -0
  9. data/cocoapods-pack.gemspec +34 -0
  10. data/endToEndPackMySample.sh +13 -0
  11. data/lib/cocoapods-pack/command/pack.rb +497 -0
  12. data/lib/cocoapods-pack/command.rb +19 -0
  13. data/lib/cocoapods-pack/env_parser.rb +23 -0
  14. data/lib/cocoapods-pack/find_follow.rb +106 -0
  15. data/lib/cocoapods-pack/gem_version.rb +21 -0
  16. data/lib/cocoapods-pack/spec_generator.rb +154 -0
  17. data/lib/cocoapods-pack/xcode_builder.rb +193 -0
  18. data/lib/cocoapods-pack/zip_file_generator.rb +56 -0
  19. data/lib/cocoapods_pack.rb +19 -0
  20. data/lib/cocoapods_plugin.rb +19 -0
  21. data/local_pod.rb +7 -0
  22. data/packMySample.sh +8 -0
  23. data/samples/CLIConsumer/CLIConsumer/dummy.swift +1 -0
  24. data/samples/CLIConsumer/CLIConsumer/main.m +10 -0
  25. data/samples/CLIConsumer/CLIConsumer.xcodeproj/project.pbxproj +329 -0
  26. data/samples/CLIConsumer/CLIConsumer.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  27. data/samples/CLIConsumer/CLIConsumer.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  28. data/samples/CLIConsumer/CLIConsumer.xcodeproj/xcshareddata/xcschemes/CLIConsumer.xcscheme +88 -0
  29. data/samples/CLIConsumer/CLIConsumer.xcworkspace/contents.xcworkspacedata +10 -0
  30. data/samples/CLIConsumer/CLIConsumer.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  31. data/samples/CLIConsumer/Podfile +9 -0
  32. data/samples/CLIConsumer/build.sh +13 -0
  33. data/samples/MySample/MIT +1 -0
  34. data/samples/MySample/MySample/Internal/MySample_Internal.h +1 -0
  35. data/samples/MySample/MySample/MyObjCInSwiftSample.swift +7 -0
  36. data/samples/MySample/MySample/MySample.h +7 -0
  37. data/samples/MySample/MySample/MySample.m +10 -0
  38. data/samples/MySample/MySample/MySwiftSample.swift +7 -0
  39. data/samples/MySample/MySample.podspec +45 -0
  40. data/samples/MySample/MySample.xcodeproj/project.pbxproj +379 -0
  41. data/samples/MySample/MySample.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  42. data/samples/MySample/MySampleTests/Info.plist +24 -0
  43. data/samples/MySample/python/a.py +1 -0
  44. data/samples/MySample/python/subpython/b.py +1 -0
  45. data/samples/MySample/resources/a.png +1 -0
  46. data/samples/MySample/resources/lets/go/nuts/b.png +1 -0
  47. data/samples/MySample/resources/lets/go/runit.png +0 -0
  48. data/spec/acceptance_spec.rb +22 -0
  49. data/spec/env_parser_spec.rb +18 -0
  50. data/spec/spec_generator_spec.rb +247 -0
  51. data/spec/spec_helper.rb +47 -0
  52. data/spec/xcode_builder_spec.rb +113 -0
  53. metadata +207 -0
@@ -0,0 +1,154 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright 2021 Square, Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'cocoapods-pack/gem_version'
20
+ require 'set'
21
+ require 'digest'
22
+
23
+ class SpecGenerator
24
+ ROOT_ATTRIBUTES = %w[name version summary license authors homepage description social_media_url
25
+ docset_url documentation_url screenshots frameworks libraries requires_arc
26
+ deployment_target xcconfig pod_target_xcconfig user_target_xcconfig source vendored_frameworks
27
+ vendored_libraries resource_bundles resources preserve_paths cocoapods_version swift_versions].freeze
28
+ PLATFORM_ATTRIBUTES = %w[frameworks libraries requires_arc xcconfig pod_target_xcconfig user_target_xcconfig].freeze
29
+
30
+ attr_reader :podspec_path, :platforms, :artifact_repo_url
31
+
32
+ def initialize(source_podspec, artifact_repo_url, zip_output_path)
33
+ @podspec_path = source_podspec
34
+ @artifact_repo_url = artifact_repo_url
35
+ @zip_output_path = zip_output_path
36
+ @platforms = []
37
+ end
38
+
39
+ def generate_ruby_string
40
+ root_attributes = hash_to_dsl(@podspec_path.attributes_hash.merge('source' => artifact_repo_hash), ROOT_ATTRIBUTES)
41
+ sections = [root_attributes]
42
+ sections << dependency_section
43
+ sections.push(*platforms_sections)
44
+ sections_str = sections.reject(&:empty?).map do |section|
45
+ section.map { |lines| lines + "\n" }.join('')
46
+ end.join("\n")
47
+ "#{header}\n#{open}\n#{sections_str}#{close}\n"
48
+ end
49
+
50
+ def generate
51
+ Pod::Specification.from_string(generate_ruby_string, "#{@podspec_path.name}.podspec")
52
+ end
53
+
54
+ def add_platform(platform, executable_name)
55
+ @platforms << [platform, executable_name]
56
+ end
57
+
58
+ private
59
+
60
+ def platform_spec(platform, executable_name, has_one_platform)
61
+ ordered_keys = %w[platform source_files header_mappings_dir module_map vendored_frameworks vendored_libraries]
62
+ platform_attributes_hash = @podspec_path.attributes_hash[platform.name.to_s] || {}
63
+ platform_vendored_frameworks = Array(platform_attributes_hash['vendored_frameworks'])
64
+ platform_vendored_libraries = Array(platform_attributes_hash['vendored_libraries'])
65
+ hash = platform_spec_hash(platform.name, executable_name, platform_vendored_frameworks, platform_vendored_libraries)
66
+ platform_prefix = "#{platform.name}."
67
+ platform_section = []
68
+ unless has_one_platform
69
+ version = platform.deployment_target ? platform.deployment_target.version : nil
70
+ platform_section << spec_line('deployment_target', version, platform_prefix)
71
+ end
72
+ platform_section.push(*hash_to_dsl(hash, ordered_keys, platform_prefix))
73
+ platform_section.push(*hash_to_dsl(platform_attributes_hash, PLATFORM_ATTRIBUTES, platform_prefix)) if platform_attributes_hash
74
+ platform_section
75
+ end
76
+
77
+ def platform_spec_hash(platform_name, product_name, vendored_frameworks, vendored_libraries)
78
+ {
79
+ 'vendored_frameworks' => ["#{platform_name}/#{product_name}"] + vendored_frameworks,
80
+ 'vendored_libraries' => vendored_libraries
81
+ }
82
+ end
83
+
84
+ def platforms_sections
85
+ ret = []
86
+ has_one_platform = (@platforms.size == 1)
87
+ if has_one_platform
88
+ platform, = @platforms.first
89
+ ret << [spec_line('platform', platform_spec_line(platform))]
90
+ end
91
+ ret.push(*@platforms.map { |p, e| platform_spec(p, e, has_one_platform) })
92
+ ret
93
+ end
94
+
95
+ def dependency_section
96
+ @podspec_path.dependencies.map { |dependency| dependency_line(dependency) }
97
+ end
98
+
99
+ def platform_spec_line(platform)
100
+ return [platform.symbolic_name] unless platform.deployment_target
101
+
102
+ [platform.symbolic_name, platform.deployment_target.to_s]
103
+ end
104
+
105
+ def header
106
+ "# Generated by cocoapods-pack #{CocoapodsPack::VERSION} - Do not manually modify."
107
+ end
108
+
109
+ def open
110
+ 'Pod::Spec.new do |s|'
111
+ end
112
+
113
+ def close
114
+ 'end'
115
+ end
116
+
117
+ def artifact_repo_hash
118
+ { http: artifact_repo_url, sha256: Digest::SHA2.file(@zip_output_path).hexdigest }
119
+ end
120
+
121
+ def str(str)
122
+ "'#{str.gsub(/\n/, '\n')}'"
123
+ end
124
+
125
+ def hash_to_dsl(hash, ordered_keys, extra_prefix = '')
126
+ ret = []
127
+ ordered_keys.each do |k|
128
+ v = hash[k]
129
+ ret << spec_line(k, v, extra_prefix) unless v.nil? || v == []
130
+ end
131
+ ret
132
+ end
133
+
134
+ def spec_line(key, value, extra_prefix = '')
135
+ [' ', 's.', extra_prefix, key.to_s, ' = ', value_of(value)].join('')
136
+ end
137
+
138
+ def dependency_line(dependency)
139
+ name = dependency.name
140
+ reqstr = dependency.requirement.as_list.map { |s| value_of(s) }.join(', ')
141
+ [' ', 's.dependency ', value_of(name), ', ', reqstr].join('')
142
+ end
143
+
144
+ def value_of(value)
145
+ return str(quote_quotes(value)) if value.is_a?(String)
146
+ return value.map { |x| value_of(x) }.join(', ') if value.is_a?(Array)
147
+
148
+ quote_quotes(value.inspect)
149
+ end
150
+
151
+ def quote_quotes(str)
152
+ str.gsub(/'/, "\\\\'")
153
+ end
154
+ end
@@ -0,0 +1,193 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright 2021 Square, Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'cocoapods-pack/env_parser'
20
+ require 'English'
21
+
22
+ class XcodeBuilder
23
+ include EnvParser
24
+ attr_reader :xcodeproject_path, :xcodebuild_outdir, :user_interface
25
+
26
+ class BuildError < StandardError; end
27
+ private :user_interface
28
+
29
+ def initialize(xcodeproject_path, xcodebuild_opts, xcodebuild_outdir, user_interface, verbose = false)
30
+ @xcodeproject_path = xcodeproject_path
31
+ @xcodebuild_opts = xcodebuild_opts
32
+ @xcodebuild_outdir = xcodebuild_outdir
33
+ @user_interface = user_interface
34
+ @verbose = verbose
35
+ end
36
+
37
+ def build(platform, target, xcodebuild_args = nil)
38
+ return build_ios(target, xcodebuild_args) if platform == :ios
39
+ return build_osx(target, xcodebuild_args) if platform == :osx
40
+ return build_watchos(target, xcodebuild_args) if platform == :watchos
41
+ return build_tvos(target, xcodebuild_args) if platform == :tvos
42
+
43
+ raise "Unknown platform: '#{platform}'"
44
+ end
45
+
46
+ def build_settings(platform, target, type = nil)
47
+ return build_ios_settings(target, type) if platform == :ios
48
+ return build_watchos_settings(target, type) if platform == :watchos
49
+ return build_tvos_settings(target, type) if platform == :tvos
50
+
51
+ build_osx_settings(target) if platform == :osx
52
+ end
53
+
54
+ private
55
+
56
+ def verbose?
57
+ @verbose
58
+ end
59
+
60
+ def build_ios(target, xcodebuild_args)
61
+ user_interface.puts "\nBuilding #{target} for iOS...\n".yellow
62
+ run(create_build_command(target, ios_sim_args, xcodebuild_args, :simulator))
63
+ run(create_build_command(target, ios_device_args, xcodebuild_args, :device))
64
+ user_interface.puts 'iOS build successful.'.green << "\n\n"
65
+ end
66
+
67
+ def build_osx(target, xcodebuild_args)
68
+ user_interface.puts "\nBuilding #{target} for macOS...\n".yellow
69
+ run(create_build_command(target, macos_args, xcodebuild_args))
70
+ user_interface.puts 'macOS build successful.'.green << "\n\n"
71
+ end
72
+
73
+ def build_watchos(target, xcodebuild_args)
74
+ user_interface.puts "\nBuilding #{target} for watchOS...\n".yellow
75
+ run(create_build_command(target, watchos_sim_args, xcodebuild_args, :simulator))
76
+ run(create_build_command(target, watchos_device_args, xcodebuild_args, :device))
77
+ user_interface.puts 'watchOS build successful.'.green << "\n\n"
78
+ end
79
+
80
+ def build_tvos(target, xcodebuild_args)
81
+ user_interface.puts "\nBuilding #{target} for tvOS...\n".yellow
82
+ run(create_build_command(target, tvos_sim_args, xcodebuild_args, :simulator))
83
+ run(create_build_command(target, tvos_device_args, xcodebuild_args, :device))
84
+ user_interface.puts 'tvOS build successful.'.green << "\n\n"
85
+ end
86
+
87
+ def build_ios_settings(target, type)
88
+ type_args = args_for_ios_type(type)
89
+ command = create_build_command(target, type_args, '-showBuildSettings', type)
90
+ parse_build_settings(command)
91
+ end
92
+
93
+ def build_osx_settings(target)
94
+ parse_build_settings(create_build_command(target, macos_args, '-showBuildSettings'))
95
+ end
96
+
97
+ def build_watchos_settings(target, type)
98
+ type_args = args_for_watchos_type(type)
99
+ command = create_build_command(target, type_args, '-showBuildSettings', type)
100
+ parse_build_settings(command)
101
+ end
102
+
103
+ def build_tvos_settings(target, type)
104
+ type_args = args_for_tvos_type(type)
105
+ command = create_build_command(target, type_args, '-showBuildSettings', type)
106
+ parse_build_settings(command)
107
+ end
108
+
109
+ def parse_build_settings(command)
110
+ parse_env(run(command))
111
+ end
112
+
113
+ def args_for_ios_type(type)
114
+ return ios_device_args if type == :device
115
+ return ios_sim_args if type == :simulator
116
+
117
+ raise "Unknown type for iOS: '#{type}'"
118
+ end
119
+
120
+ def args_for_watchos_type(type)
121
+ return watchos_device_args if type == :device
122
+ return watchos_sim_args if type == :simulator
123
+
124
+ raise "Unknown type for watchOS: '#{type}'"
125
+ end
126
+
127
+ def args_for_tvos_type(type)
128
+ return tvos_device_args if type == :device
129
+ return tvos_sim_args if type == :simulator
130
+
131
+ raise "Unknown type for tvOS: '#{type}'"
132
+ end
133
+
134
+ def run(command)
135
+ user_interface.puts command if verbose?
136
+ output, process_status = shellout(command)
137
+ user_interface.puts output if verbose?
138
+ return output if process_status.success?
139
+
140
+ warn output
141
+ raise BuildError, "Failed to execute '#{command}'. Exit status: #{process_status.exitstatus}"
142
+ end
143
+
144
+ def shellout(command)
145
+ output = `#{command}`
146
+ [output, $CHILD_STATUS]
147
+ end
148
+
149
+ def create_build_command(target, sdk_args, xcodebuild_args, type = nil)
150
+ args = [base_args(target), sdk_args]
151
+ args << xcodebuild_args if !xcodebuild_args.nil? && !xcodebuild_args.strip.empty?
152
+ args << @xcodebuild_opts unless @xcodebuild_opts.nil?
153
+ args << 'archive'
154
+ archive_path = +"-archivePath #{xcodebuild_outdir}/#{target}"
155
+ archive_path << "-#{type}" if type
156
+ archive_path << '.xcarchive'
157
+ args << archive_path
158
+ args.join(' ')
159
+ end
160
+
161
+ def base_args(target)
162
+ "xcodebuild ONLY_ACTIVE_ARCH=NO SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES -project #{@xcodeproject_path}" \
163
+ " -scheme \"#{target}\" -configuration Release EXCLUDED_SOURCE_FILE_NAMES=*-dummy.m"
164
+ end
165
+
166
+ def ios_sim_args
167
+ '-destination "generic/platform=iOS Simulator"'
168
+ end
169
+
170
+ def ios_device_args
171
+ '-destination "generic/platform=iOS"'
172
+ end
173
+
174
+ def macos_args
175
+ '-destination "generic/platform=macOS"'
176
+ end
177
+
178
+ def watchos_sim_args
179
+ '-destination "generic/platform=watchOS Simulator"'
180
+ end
181
+
182
+ def watchos_device_args
183
+ '-destination "generic/platform=watchOS"'
184
+ end
185
+
186
+ def tvos_sim_args
187
+ '-destination "generic/platform=tvOS Simulator"'
188
+ end
189
+
190
+ def tvos_device_args
191
+ '-destination "generic/platform=tvOS"'
192
+ end
193
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright 2021 Square, Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'zip'
20
+
21
+ class ZipFileGenerator
22
+ def initialize(input_dir, output_file)
23
+ @input_dir = input_dir
24
+ @output_file = output_file
25
+ end
26
+
27
+ def write(&skip_filter_block)
28
+ entries = Dir.entries(@input_dir)
29
+ entries.delete('.')
30
+ entries.delete('..')
31
+ begin
32
+ io = Zip::File.open(@output_file, Zip::File::CREATE)
33
+ write_entries(entries, '', io, &skip_filter_block)
34
+ ensure
35
+ io.close
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def write_entries(entries, path, io, &skip_filter_block)
42
+ entries.each do |e|
43
+ zip_file_path = path == '' ? e : File.join(path, e)
44
+ disk_file_path = File.join(@input_dir, zip_file_path)
45
+ if File.directory?(disk_file_path)
46
+ io.mkdir(zip_file_path)
47
+ subdir = Dir.entries(disk_file_path)
48
+ subdir.delete('.')
49
+ subdir.delete('..')
50
+ write_entries(subdir, zip_file_path, io, &skip_filter_block)
51
+ elsif skip_filter_block.nil? || !yield(disk_file_path)
52
+ io.get_output_stream(zip_file_path) { |f| f.write(IO.binread(disk_file_path)) }
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright 2021 Square, Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'cocoapods-pack/gem_version'
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright 2021 Square, Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'cocoapods-pack/command'
data/local_pod.rb ADDED
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'cocoapods'
5
+ require 'cocoapods-pack/command'
6
+
7
+ load Gem.bin_path('cocoapods', 'pod')
data/packMySample.sh ADDED
@@ -0,0 +1,8 @@
1
+ #!/bin/bash
2
+ set -o errexit
3
+ set -o pipefail
4
+ set -o nounset
5
+ set -o xtrace
6
+
7
+ rm -rf out/
8
+ bundle exec ruby local_pod.rb pack --verbose --allow-warnings samples/MySample/MySample.podspec --out-dir=out https://example.com --no-repo-update $@
@@ -0,0 +1 @@
1
+ import Foundation
@@ -0,0 +1,10 @@
1
+ @import Foundation;
2
+ @import MySample;
3
+
4
+ int main(int argc, const char * argv[]) {
5
+ @autoreleasepool {
6
+ NSString* str = MySample.new.print;
7
+ printf("%s", [[@"Calling MySample: " stringByAppendingFormat: @"%@\n", str] UTF8String]);
8
+ }
9
+ return 0;
10
+ }