app.rb 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. # frozen_string_literal: true
  2. # Copyright 2022 Google LLC
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. require 'cocoapods'
  16. require 'digest'
  17. require 'optparse'
  18. require 'plist'
  19. require 'tmpdir'
  20. require 'xcodeproj'
  21. DEFAULT_TESTAPP_TARGET = "testApp"
  22. # Default sources of min iOS version
  23. SOURCES=["https://cdn.cocoapods.org/"]
  24. MIN_IOS_VERSION="12.0"
  25. NOTICES_OUTPUT_PATH="./CoreOnly/NOTICES"
  26. SEARCH_LOCAL_POD_VERSION=false
  27. @options = {
  28. sources: SOURCES,
  29. min_ios_version: MIN_IOS_VERSION,
  30. output_path: NOTICES_OUTPUT_PATH,
  31. search_local_pod_version: false
  32. }
  33. begin
  34. OptionParser.new do |opts|
  35. opts.banner = "Usage: app.rb [options]"
  36. opts.on('-p', '--pods PODS', 'Pods seperated by space or comma.') { |v| @options[:pods] = v.split(/[ ,]/) }
  37. opts.on('-s', '--sources SOURCES', 'Sources of Pods') { |v| @options[:sources] = v.split(/[ ,]/) }
  38. opts.on('-m', '--min_ios_version MIN_IOS_VERSION', 'Minimum iOS version') { |v| @options[:min_ios_version] = v }
  39. opts.on('-n', '--notices_path OUTPUT_PATH', 'The output path of NOTICES') { |v| @options[:output_path] = v }
  40. opts.on('-v', '--search_local_pod_version', 'Attach the latest pod version to a pod in Podfile') { |v| @options[:search_local_pod_version] = true }
  41. end.parse!
  42. raise OptionParser::MissingArgument if @options[:pods].nil?
  43. rescue OptionParser::MissingArgument
  44. puts "Argument `--pods` should not be empty."
  45. raise
  46. end
  47. PODS = @options[:pods]
  48. SOURCES = @options[:sources]
  49. MIN_IOS_VERSION = @options[:min_ios_version]
  50. NOTICES_OUTPUT_PATH = @options[:output_path]
  51. SEARCH_LOCAL_POD_VERSION = @options[:search_local_pod_version]
  52. def create_podfile(path: , sources: , target: , pods: [], min_ios_version: , search_local_pod_version: )
  53. output = ""
  54. for source in sources do
  55. output += "source \'#{source}\'\n"
  56. end
  57. if search_local_pod_version
  58. for source in sources do
  59. if source == "https://cdn.cocoapods.org/"
  60. next
  61. end
  62. `pod repo add #{Digest::MD5.hexdigest source} #{source}`
  63. end
  64. end
  65. output += "use_frameworks! :linkage => :static\n"
  66. output += "platform :ios, #{min_ios_version}\n"
  67. output += "target \'#{target}\' do\n"
  68. for pod in pods do
  69. if search_local_pod_version
  70. # `pod search` will search a pod locally and generate a corresonding pod
  71. # config in a Podfile with `grep`, e.g.
  72. # pod search Firebase | grep "pod.*" -m 1
  73. # will generate
  74. # pod 'Firebase', '~> 9.0.0'
  75. output += `pod search "#{pod}" | grep "pod.*" -m 1`
  76. else
  77. output += "pod \'#{pod}\'\n"
  78. end
  79. end
  80. output += "end\n"
  81. # Remove default footers and headers generated by CocoaPods.
  82. output += "
  83. class ::Pod::Generator::Acknowledgements
  84. def header_text
  85. ''
  86. end
  87. def header_title
  88. ''
  89. end
  90. def footnote_text
  91. ''
  92. end
  93. end
  94. "
  95. puts "------Podfile------\n#{output}\n-------------------\n"
  96. podfile = File.new("#{path}/Podfile", "w")
  97. podfile.puts(output)
  98. podfile.close
  99. end
  100. def generate_notices_content(sources: SOURCES, pods: PODS, min_ios_version: MIN_IOS_VERSION)
  101. content = ""
  102. Dir.mktmpdir do |temp_dir|
  103. Dir.chdir(temp_dir) do
  104. project_path = "#{temp_dir}/barebone_app.xcodeproj"
  105. project_path = "barebone_app.xcodeproj"
  106. project = Xcodeproj::Project.new(project_path)
  107. project.new_target(:application, DEFAULT_TESTAPP_TARGET, :ios)
  108. project.save()
  109. create_podfile(path: temp_dir, sources: sources, target: DEFAULT_TESTAPP_TARGET,pods: pods, min_ios_version: min_ios_version, search_local_pod_version: SEARCH_LOCAL_POD_VERSION)
  110. pod_install_result = `pod install --allow-root`
  111. puts pod_install_result
  112. licenses = Plist.parse_xml("Pods/Target Support Files/Pods-testApp/Pods-testApp-acknowledgements.plist")
  113. existing_licenses={}
  114. for license in licenses["PreferenceSpecifiers"] do
  115. if existing_licenses.include?(license["FooterText"])
  116. existing_licenses.store(license["FooterText"], existing_licenses.fetch(license["FooterText"])+"\n"+license["Title"])
  117. next
  118. end
  119. existing_licenses.store(license["FooterText"], license["Title"])
  120. end
  121. existing_licenses.each{ |license, title|
  122. # The NOTICES format is like:
  123. # ```
  124. # ${title}
  125. # ${license}
  126. #
  127. # ${title}
  128. # ${license}
  129. # ...
  130. # ```
  131. content += "#{title}\n#{license}\n\n"
  132. }
  133. end
  134. end
  135. return content.strip
  136. end
  137. def main()
  138. content = generate_notices_content(sources: SOURCES, pods: PODS, min_ios_version: MIN_IOS_VERSION)
  139. notices = File.new(NOTICES_OUTPUT_PATH, "w")
  140. notices.puts(content)
  141. notices.close
  142. end
  143. main()