native_modules.rb 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. # This is a function which is used inside your Podfile.
  2. #
  3. # It uses `react-native config` to grab a list of dependencies, and pulls out
  4. # all of the ones which declare themselves to be iOS/macOS dependencies (by
  5. # virtue of having a Podspec) and automatically imports those into your current
  6. # target.
  7. #
  8. # See the `IOSNativeModulesConfig` interface in `cli-types/src/ios.ts` to
  9. # understand what the input data should look like. Be sure to update that file
  10. # in lock-step with additional data being used here.
  11. require 'pathname'
  12. require 'cocoapods'
  13. def use_native_modules!(config = nil)
  14. if (config.is_a? String)
  15. Pod::UI.warn("Passing custom root to use_native_modules! is deprecated.",
  16. [
  17. "CLI detects root of the project automatically. The \"#{config}\" argument was ignored.",
  18. ]);
  19. config = nil;
  20. end
  21. # Resolving the path the RN CLI. The `@react-native-community/cli` module may not be there for certain package managers, so we fall back to resolving it through `react-native` package, that's always present in RN projects
  22. cli_resolve_script = "try {console.log(require('@react-native-community/cli').bin);} catch (e) {console.log(require('react-native/cli').bin);}"
  23. cli_bin = Pod::Executable.execute_command("node", ["-e", cli_resolve_script], true).strip
  24. if (!config)
  25. json = []
  26. IO.popen(["node", cli_bin, "config"]) do |data|
  27. while line = data.gets
  28. json << line
  29. end
  30. end
  31. config = JSON.parse(json.join("\n"))
  32. end
  33. project_root = Pathname.new(config["project"]["ios"]["sourceDir"])
  34. packages = config["dependencies"]
  35. found_pods = []
  36. packages.each do |package_name, package|
  37. next unless package_config = package["platforms"]["ios"]
  38. podspec_path = package_config["podspecPath"]
  39. # Add a warning to the queue and continue to the next dependency if the podspec_path is nil/empty
  40. if podspec_path.nil? || podspec_path.empty?
  41. Pod::UI.warn("use_native_modules! skipped the react-native dependency '#{package["name"]}'. No podspec file was found.",
  42. [
  43. "Check to see if there is an updated version that contains the necessary podspec file",
  44. "Contact the library maintainers or send them a PR to add a podspec. The react-native-webview podspec is a good example of a package.json driven podspec. See https://github.com/react-native-community/react-native-webview/blob/master/react-native-webview.podspec",
  45. "If necessary, you can disable autolinking for the dependency and link it manually. See https://github.com/react-native-community/cli/blob/master/docs/autolinking.md#how-can-i-disable-autolinking-for-unsupported-library"
  46. ])
  47. end
  48. next if podspec_path.nil? || podspec_path.empty?
  49. spec = Pod::Specification.from_file(podspec_path)
  50. # Skip pods that do not support the platform of the current target.
  51. if platform = current_target_definition.platform
  52. next unless spec.supported_on_platform?(platform.name)
  53. else
  54. # TODO: In a future RN version we should update the Podfile template and
  55. # enable this assertion.
  56. #
  57. # raise Pod::Informative, "Cannot invoke `use_native_modules!` before defining the supported `platform`"
  58. end
  59. # We want to do a look up inside the current CocoaPods target
  60. # to see if it's already included, this:
  61. # 1. Gives you the chance to define it beforehand
  62. # 2. Ensures CocoaPods won't explode if it's included twice
  63. #
  64. this_target = current_target_definition
  65. existing_deps = current_target_definition.dependencies
  66. # Skip dependencies that the user already activated themselves.
  67. next if existing_deps.find do |existing_dep|
  68. existing_dep.name.split('/').first == spec.name
  69. end
  70. podspec_dir_path = Pathname.new(File.dirname(podspec_path))
  71. relative_path = podspec_dir_path.relative_path_from project_root
  72. pod spec.name, :path => relative_path.to_path
  73. if package_config["scriptPhases"] && !this_target.abstract?
  74. # Can be either an object, or an array of objects
  75. Array(package_config["scriptPhases"]).each do |phase|
  76. # see https://www.rubydoc.info/gems/cocoapods-core/Pod/Podfile/DSL#script_phase-instance_method
  77. # for the full object keys
  78. Pod::UI.puts "Adding a custom script phase for Pod #{spec.name}: #{phase["name"] || 'No name specified.'}"
  79. # Support passing in a path relative to the root of the package
  80. if phase["path"]
  81. phase["script"] = File.read(File.expand_path(phase["path"], package["root"]))
  82. phase.delete("path")
  83. end
  84. # Support converting the execution position into a symbol
  85. if phase["execution_position"]
  86. phase["execution_position"] = phase["execution_position"].to_sym
  87. end
  88. phase = Hash[phase.map { |k, v| [k.to_sym, v] }]
  89. script_phase phase
  90. end
  91. end
  92. found_pods.push spec
  93. end
  94. if found_pods.size > 0
  95. pods = found_pods.map { |p| p.name }.sort.to_sentence
  96. Pod::UI.puts "Auto-linking React Native #{"module".pluralize(found_pods.size)} for target `#{current_target_definition.name}`: #{pods}"
  97. end
  98. absolute_react_native_path = Pathname.new(config["reactNativePath"])
  99. { :reactNativePath => absolute_react_native_path.relative_path_from(project_root).to_s }
  100. end
  101. # You can run the tests for this file by running:
  102. # $ yarn jest packages/platform-ios/src/config/__tests__/native_modules.test.ts
  103. if $0 == __FILE__
  104. require "json"
  105. runInput = JSON.parse(ARGF.read)
  106. unless runInput["captureStdout"]
  107. Pod::Config.instance.silent = true
  108. end
  109. return_values = []
  110. podfile = Pod::Podfile.new do
  111. if runInput["podsActivatedByUser"]
  112. runInput["podsActivatedByUser"].each do |name|
  113. pod(name)
  114. end
  115. end
  116. target 'iOS Target' do
  117. platform :ios
  118. return_values[0] = use_native_modules!(runInput["dependencyConfig"])
  119. end
  120. target 'macOS Target' do
  121. platform :osx
  122. return_values[1] = use_native_modules!(runInput["dependencyConfig"])
  123. end
  124. end
  125. unless runInput["captureStdout"]
  126. puts podfile.to_hash.merge({ "return_values": return_values }).to_json
  127. end
  128. end