cocoapods-pack 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }