Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

require "test/unit"
require "shellwords"
require_relative "../rndependencies.rb"
require_relative "./test_utils/SpecMock.rb"

class RNDependenciesTests < Test::Unit::TestCase

# A pod that exports a Swift compatibility header ships this path, and the
# directory name contains spaces.
SWIFT_HEADER = "${PODS_CONFIGURATION_BUILD_DIR}/MyPod/Swift Compatibility Header"

def teardown
ReactNativeDependenciesUtils.class_variable_set(:@@build_from_source, true)
end

# Xcode joins an array setting with spaces, then splits it back on
# whitespace while honouring quotes. This is what the compiler ends up with.
def resolved_paths(xcconfig)
value = xcconfig["HEADER_SEARCH_PATHS"]
Shellwords.shellsplit(value.is_a?(Array) ? value.join(" ") : value)
end

# ================================== #
# TEST - append_header_search_paths #
# ================================== #

def test_appendHeaderSearchPaths_whenUnset_quotesTheAddedPaths
xcconfig = {}

ReactNativeDependenciesUtils.append_header_search_paths(xcconfig, ["$(PODS_ROOT)/glog"])

assert_equal(["\"$(PODS_ROOT)/glog\""], xcconfig["HEADER_SEARCH_PATHS"])
end

def test_appendHeaderSearchPaths_whenStringHasQuotedPathWithSpaces_keepsItIntact
xcconfig = {"HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/DoubleConversion\" \"#{SWIFT_HEADER}\""}

ReactNativeDependenciesUtils.append_header_search_paths(xcconfig, ["$(PODS_ROOT)/glog"])

assert_equal(["$(PODS_ROOT)/DoubleConversion", SWIFT_HEADER, "$(PODS_ROOT)/glog"], resolved_paths(xcconfig))
end

def test_appendHeaderSearchPaths_whenArrayHasQuotedPathWithSpaces_keepsItIntact
xcconfig = {"HEADER_SEARCH_PATHS" => ["\"$(PODS_ROOT)/DoubleConversion\"", "\"#{SWIFT_HEADER}\""]}

ReactNativeDependenciesUtils.append_header_search_paths(xcconfig, ["$(PODS_ROOT)/glog"])

assert_equal(["$(PODS_ROOT)/DoubleConversion", SWIFT_HEADER, "$(PODS_ROOT)/glog"], resolved_paths(xcconfig))
end

def test_appendHeaderSearchPaths_whenCalledTwice_doesNotDuplicateEntries
xcconfig = {"HEADER_SEARCH_PATHS" => "\"#{SWIFT_HEADER}\""}

ReactNativeDependenciesUtils.append_header_search_paths(xcconfig, ["$(PODS_ROOT)/glog"])
ReactNativeDependenciesUtils.append_header_search_paths(xcconfig, ["$(PODS_ROOT)/glog"])

assert_equal([SWIFT_HEADER, "$(PODS_ROOT)/glog"], resolved_paths(xcconfig))
end

# ======================================= #
# TEST - add_rn_third_party_dependencies #
# ======================================= #

def test_addRNThirdPartyDependencies_whenBuildingFromSource_keepsQuotedPathWithSpaces
spec = SpecMock.new
spec.pod_target_xcconfig = {"HEADER_SEARCH_PATHS" => "\"#{SWIFT_HEADER}\""}

add_rn_third_party_dependencies(spec)

paths = resolved_paths(spec.pod_target_xcconfig)
assert_equal(SWIFT_HEADER, paths.first)
assert(paths.include?("$(PODS_ROOT)/RCT-Folly"))
end

def test_addRNThirdPartyDependencies_whenUsingPrebuiltDeps_keepsQuotedPathWithSpaces
ReactNativeDependenciesUtils.class_variable_set(:@@build_from_source, false)
spec = SpecMock.new
spec.pod_target_xcconfig = {"HEADER_SEARCH_PATHS" => "\"#{SWIFT_HEADER}\""}

add_rn_third_party_dependencies(spec)

assert_equal([SWIFT_HEADER, "$(PODS_ROOT)/ReactNativeDependencies/Headers"], resolved_paths(spec.pod_target_xcconfig))
end
end
56 changes: 31 additions & 25 deletions packages/react-native/scripts/cocoapods/rndependencies.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
require "json"
require 'net/http'
require 'rexml/document'
require 'shellwords'

require_relative './utils.rb'

Expand Down Expand Up @@ -34,38 +33,26 @@ def add_rn_third_party_dependencies(s)
s.dependency "RCT-Folly/Fabric"
end

header_search_paths = current_pod_target_xcconfig["HEADER_SEARCH_PATHS"] || []

if header_search_paths.is_a?(String)
header_search_paths = Shellwords.shellsplit(header_search_paths)
end

header_search_paths << "$(PODS_ROOT)/glog"
header_search_paths << "$(PODS_ROOT)/boost"
header_search_paths << "$(PODS_ROOT)/DoubleConversion"
header_search_paths << "$(PODS_ROOT)/fast_float/include"
header_search_paths << "$(PODS_ROOT)/fmt/include"
header_search_paths << "$(PODS_ROOT)/SocketRocket"
header_search_paths << "$(PODS_ROOT)/RCT-Folly"

# uniq so a second call on the same spec can't duplicate entries.
current_pod_target_xcconfig["HEADER_SEARCH_PATHS"] = header_search_paths.uniq
ReactNativeDependenciesUtils.append_header_search_paths(current_pod_target_xcconfig, [
"$(PODS_ROOT)/glog",
"$(PODS_ROOT)/boost",
"$(PODS_ROOT)/DoubleConversion",
"$(PODS_ROOT)/fast_float/include",
"$(PODS_ROOT)/fmt/include",
"$(PODS_ROOT)/SocketRocket",
"$(PODS_ROOT)/RCT-Folly",
])
else
# Prebuilt-deps mode: this pod SELF-SERVES the third-party headers from its
# own xcframework (incl. SocketRocket - sole supplier in this mode). See
# scripts/cocoapods/__docs__/prebuilt-deps.md for the full contract.
s.dependency "ReactNativeDependencies"

header_search_paths = current_pod_target_xcconfig["HEADER_SEARCH_PATHS"] || []
if header_search_paths.is_a?(String)
header_search_paths = Shellwords.shellsplit(header_search_paths)
end
# Artifact headers are flattened into the pod-local Headers/ by the podspec
# prepare_command (see __docs__/prebuilt-deps.md).
header_search_paths << "$(PODS_ROOT)/ReactNativeDependencies/Headers"

# uniq so a second call on the same spec can't duplicate entries.
current_pod_target_xcconfig["HEADER_SEARCH_PATHS"] = header_search_paths.uniq
ReactNativeDependenciesUtils.append_header_search_paths(current_pod_target_xcconfig, [
"$(PODS_ROOT)/ReactNativeDependencies/Headers",
])
end

s.pod_target_xcconfig = current_pod_target_xcconfig
Expand Down Expand Up @@ -148,6 +135,25 @@ def self.setup_react_native_dependencies(react_native_path, react_native_version
end
end

# Xcode splits HEADER_SEARCH_PATHS on whitespace, so every path we add is
# quoted - PODS_ROOT can expand to a directory with spaces in its name.
# Paths already in the xcconfig are left untouched: they carry the podspec
# author's own quoting, and re-quoting them would break it.
def self.append_header_search_paths(xcconfig, paths)
quoted = paths.map { |path| "\"#{path}\"" }
existing = xcconfig["HEADER_SEARCH_PATHS"]

# reject so a second call on the same spec can't duplicate entries.
case existing
when nil
xcconfig["HEADER_SEARCH_PATHS"] = quoted
when Array
xcconfig["HEADER_SEARCH_PATHS"] = existing + quoted.reject { |path| existing.include?(path) }
else
xcconfig["HEADER_SEARCH_PATHS"] = ([existing] + quoted.reject { |path| existing.include?(path) }).join(" ")
end
end

def self.abort_if_use_local_rndeps_with_no_file()
if !File.exist?(ENV["RCT_USE_LOCAL_RN_DEP"])
abort("RCT_USE_LOCAL_RN_DEP is set to #{ENV["RCT_USE_LOCAL_RN_DEP"]} but the file does not exist!")
Expand Down
Loading