diff --git a/.gitignore b/.gitignore index 747bb762be9..db3550072b0 100644 --- a/.gitignore +++ b/.gitignore @@ -105,7 +105,7 @@ package-lock.json /React/FBReactNativeSpec/FBReactNativeSpec /packages/react-native-codegen/lib /ReactCommon/react/renderer/components/rncore/ -/packages/rn-tester/NativeModuleExample/ScreenshotManagerSpec +/packages/rn-tester/NativeModuleExample/ScreenshotManagerSpec* # Visual studio .vscode diff --git a/React/FBReactNativeSpec/FBReactNativeSpec.podspec b/React/FBReactNativeSpec/FBReactNativeSpec.podspec index bdbf1023319..e386b2b66d0 100644 --- a/React/FBReactNativeSpec/FBReactNativeSpec.podspec +++ b/React/FBReactNativeSpec/FBReactNativeSpec.podspec @@ -32,7 +32,7 @@ Pod::Spec.new do |s| s.platforms = { :ios => "11.0" } s.compiler_flags = folly_compiler_flags + ' -Wno-nullability-completeness' s.source = source - s.source_files = "**/*.{c,h,m,mm,cpp}" + s.source_files = "**/FBReactNativeSpec*.{h,mm}" s.header_dir = "FBReactNativeSpec" s.pod_target_xcconfig = { @@ -52,9 +52,7 @@ Pod::Spec.new do |s| :react_native_path => react_native_path, :js_srcs_dir => "#{react_native_path}/Libraries", :library_name => "FBReactNativeSpec", - :output_dir => "#{react_native_path}/React/FBReactNativeSpec", - :component_library_name => "rncore", - # TODO: component_output_dir should be programmatically specified, and may change with use_frameworks! support. - :component_output_dir => "#{react_native_path}/ReactCommon/react/renderer/components/" + :library_type => "modules", + :output_dir => "#{react_native_path}/React/FBReactNativeSpec/FBReactNativeSpec" }) end diff --git a/ReactCommon/React-Fabric.podspec b/ReactCommon/React-Fabric.podspec index 0ce618ec6f5..b60780ccb31 100644 --- a/ReactCommon/React-Fabric.podspec +++ b/ReactCommon/React-Fabric.podspec @@ -20,6 +20,7 @@ folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 folly_version = '2021.06.28.00' folly_dep_name = 'RCT-Folly/Fabric' boost_compiler_flags = '-Wno-documentation' +react_native_path = ".." Pod::Spec.new do |s| s.name = "React-Fabric" @@ -341,4 +342,12 @@ Pod::Spec.new do |s| ss.header_dir = "react/utils" ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/RCT-Folly\"" } end + + use_react_native_codegen!(s, { + :react_native_path => react_native_path, + :js_srcs_dir => "#{react_native_path}/Libraries", + :library_name => "rncore", + :library_type => "components", + :output_dir => "#{react_native_path}/ReactCommon", + }) end diff --git a/packages/rn-tester/NativeModuleExample/ScreenshotManager.podspec b/packages/rn-tester/NativeModuleExample/ScreenshotManager.podspec index bfa423f63e0..bb0d1818aa7 100644 --- a/packages/rn-tester/NativeModuleExample/ScreenshotManager.podspec +++ b/packages/rn-tester/NativeModuleExample/ScreenshotManager.podspec @@ -34,6 +34,7 @@ Pod::Spec.new do |s| use_react_native_codegen!(s, { :react_native_path => "../../..", :js_srcs_dir => "./", + :library_type => "modules", :output_dir => "./" }) end diff --git a/packages/rn-tester/Podfile.lock b/packages/rn-tester/Podfile.lock index 265d84a1855..5c65ed7e3b1 100644 --- a/packages/rn-tester/Podfile.lock +++ b/packages/rn-tester/Podfile.lock @@ -872,7 +872,7 @@ SPEC CHECKSUMS: CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 DoubleConversion: 831926d9b8bf8166fd87886c4abab286c2422662 FBLazyVector: b81a2b70c72d8b0aefb652cea22c11e9ffd02949 - FBReactNativeSpec: ce190a91d6e289e5fb1fb2414c90d16cb8567ab8 + FBReactNativeSpec: 55e1b4e2c593197159844fc5c7f88e68e8cb9980 Flipper: 30e8eeeed6abdc98edaf32af0cda2f198be4b733 Flipper-Boost-iOSX: fd1e2b8cbef7e662a122412d7ac5f5bea715403c Flipper-DoubleConversion: 57ffbe81ef95306cc9e69c4aa3aeeeeb58a6a28c diff --git a/scripts/generate-specs-cli.js b/scripts/generate-specs-cli.js index 427eff56104..1c008d6fb6f 100644 --- a/scripts/generate-specs-cli.js +++ b/scripts/generate-specs-cli.js @@ -79,6 +79,24 @@ function generateSpec( fs.renameSync(`${outputDirectory}/${f}`, `${jniOutputDirectory}/${f}`); }); } + + if (platform === 'ios') { + const files = fs.readdirSync(outputDirectory); + const componentsOutputDirectory = `${outputDirectory}/react/renderer/components/${libraryName}`; + mkdirp.sync(componentsOutputDirectory); + files + .filter( + f => + (f.endsWith('.h') && !f.startsWith(libraryName)) || + f.endsWith('.cpp'), + ) + .forEach(f => { + fs.renameSync( + `${outputDirectory}/${f}`, + `${componentsOutputDirectory}/${f}`, + ); + }); + } } function main() { diff --git a/scripts/generate-specs.sh b/scripts/generate-specs.sh deleted file mode 100755 index d3ea448e175..00000000000 --- a/scripts/generate-specs.sh +++ /dev/null @@ -1,132 +0,0 @@ -#!/bin/bash -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -# This script collects the JavaScript spec definitions for -# native modules and components, then uses react-native-codegen -# to generate native code. -# -# Usage: -# ./scripts/generate-specs.sh -# - -# shellcheck disable=SC2038 - -set -e - -THIS_DIR=$(cd -P "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo "${BASH_SOURCE[0]}")")" && pwd) -TEMP_DIR=$(mktemp -d /tmp/react-native-codegen-XXXXXXXX) -RN_DIR=$(cd "$THIS_DIR/.." && pwd) - -# Find path to Node -# shellcheck source=/dev/null -source "$RN_DIR/scripts/find-node.sh" - -NODE_BINARY="${NODE_BINARY:-$(command -v node || true)}" - -cleanup () { - set +e - rm -rf "$TEMP_DIR" - set -e -} - -describe () { - printf "\\n\\n>>>>> %s\\n\\n\\n" "$1" >&2 -} - -print_usage () { - printf "\\nNAME\\n\\t%s -- generate specs\\n" "$1" >&2 - printf "\\nSYNOPSIS\\n" >&2 - printf "\\t%s javascript_sources_directory specs_library_name output_directory\\n" "$1" >&2 - printf "\\t%s javascript_sources_directory specs_library_name output_directory component_library_name [component_output_directory]\\n" "$1" >&2 - printf "\\n\\nDESCRIPTION\\n\\tIn the first synopsis form, this script collects native module and native component JavaScript spec definitions in javascript_sources_directory, then uses react-native-codegen to generate the native interface code into a library named specs_library_name, which is copied to the destination output_directory.\\n" >&2 - printf "\\n\\tIn the second synopsis form, the component_library_name will be used as the name of the component native interface code library. If provided, the component output will be copied to the component_output_directory, otherwise it will be copied to the output_directory.\\n" >&2 -} - -main() { - MIN_ARG_NUM=3 - if [ "$#" -eq 0 ]; then - print_usage "$0" - exit 1 - fi - - if [ -z "$NODE_BINARY" ]; then - echo "Error: Could not find node. Make sure it is in bash PATH or set the NODE_BINARY environment variable." 1>&2 - exit 1 - fi - - if [ "$#" -lt "$MIN_ARG_NUM" ]; then - echo "Error: Expected $MIN_ARG_NUM arguments, got $# instead. Run $0 with no arguments to learn more." 1>&2 - exit 1 - fi - - SRCS_DIR=$1 - LIBRARY_NAME=$2 - OUTPUT_DIR=$3 - COMPONENT_LIBRARY_NAME_OVERRIDE=$4 - COMPONENT_OUTPUT_DIR_OVERRIDE=$5 - - PLATFORM="ios" - TEMP_OUTPUT_DIR="$TEMP_DIR/out" - SCHEMA_FILE="$TEMP_DIR/schema.json" - - CODEGEN_REPO_PATH="$RN_DIR/packages/react-native-codegen" - CODEGEN_NPM_PATH="$RN_DIR/../react-native-codegen" - - if [ -d "$CODEGEN_REPO_PATH" ]; then - CODEGEN_PATH=$(cd "$CODEGEN_REPO_PATH" && pwd) - elif [ -d "$CODEGEN_NPM_PATH" ]; then - CODEGEN_PATH=$(cd "$CODEGEN_NPM_PATH" && pwd) - else - echo "Error: Could not determine react-native-codegen location. Try running 'yarn install' or 'npm install' in your project root." 1>&2 - exit 1 - fi - - if [ ! -d "$CODEGEN_PATH/lib" ]; then - describe "Building react-native-codegen package" - bash "$CODEGEN_PATH/scripts/oss/build.sh" - fi - - describe "Generating schema from Flow types" - "$NODE_BINARY" "$CODEGEN_PATH/lib/cli/combine/combine-js-to-schema-cli.js" "$SCHEMA_FILE" "$SRCS_DIR" - - describe "Generating native code from schema ($PLATFORM)" - pushd "$RN_DIR" >/dev/null - "$NODE_BINARY" scripts/generate-specs-cli.js "$PLATFORM" "$SCHEMA_FILE" "$TEMP_OUTPUT_DIR" "$LIBRARY_NAME" - popd >/dev/null - - - mkdir -p "$OUTPUT_DIR" - - if [ -z "$COMPONENT_LIBRARY_NAME_OVERRIDE" ]; then - # Copy all output to output_dir - cp -R "$TEMP_OUTPUT_DIR/" "$OUTPUT_DIR" - echo >&2 "$LIBRARY_NAME output has been written to $OUTPUT_DIR:" - ls -1 "$OUTPUT_DIR" 2>&1 - else - # Copy modules output to output_dir - cp "$TEMP_OUTPUT_DIR/$LIBRARY_NAME.h" "$TEMP_OUTPUT_DIR/$LIBRARY_NAME-generated.mm" "$OUTPUT_DIR" - echo >&2 "$LIBRARY_NAME output has been written to $OUTPUT_DIR:" - ls -1 "$OUTPUT_DIR" 2>&1 - - # Rename library name used in components output files - find "$TEMP_OUTPUT_DIR" -type f | xargs sed -i.bak "s/$LIBRARY_NAME/$COMPONENT_LIBRARY_NAME_OVERRIDE/g" - - if [ -n "$COMPONENT_OUTPUT_DIR_OVERRIDE" ]; then - # Components codegen output to be moved to separate directory - mkdir -p "$COMPONENT_OUTPUT_DIR_OVERRIDE" - OUTPUT_DIR="$COMPONENT_OUTPUT_DIR_OVERRIDE" - fi - - find "$TEMP_OUTPUT_DIR" -type f -not -iname "$LIBRARY_NAME.h" -not -iname "$LIBRARY_NAME-generated.mm" -not -iname "*.bak" -exec cp '{}' "$OUTPUT_DIR/" ';' - echo >&2 "$COMPONENT_LIBRARY_NAME_OVERRIDE output has been written to $OUTPUT_DIR:" - ls -1 "$OUTPUT_DIR" 2>&1 - fi - - echo >&2 'Done.' -} - -trap cleanup EXIT -main "$@" diff --git a/scripts/react_native_pods.rb b/scripts/react_native_pods.rb index 0853df7e45b..49c2c64cf06 100644 --- a/scripts/react_native_pods.rb +++ b/scripts/react_native_pods.rb @@ -159,56 +159,173 @@ def use_react_native_codegen!(spec, options={}) # The prefix to react-native prefix = options[:react_native_path] ||= "../.." - # The path to JavaScript files - js_srcs = options[:js_srcs_dir] ||= "#{prefix}/Libraries" - # Library name (e.g. FBReactNativeSpec) library_name = options[:library_name] ||= "#{spec.name}Spec" # Output dir, relative to podspec that invoked this method - output_dir = options[:output_dir] ||= "#{prefix}/React/#{library_name}" + output_dir = options[:output_dir] ||= "#{library_name}" + components_output_dir = "#{output_dir}/react/renderer/components/#{library_name}" - generated_dirs = [ "#{output_dir}/#{library_name}" ] - generated_filenames = [ "#{library_name}.h", "#{library_name}-generated.mm" ] - generated_files = generated_filenames.map { |filename| "#{output_dir}/#{library_name}/#{filename}" } + codegen_config = { + "modules" => { + :js_srcs_pattern => "Native*.js", + :generated_dir => output_dir, + :generated_files => [ + "#{library_name}.h", + "#{library_name}-generated.mm" + ] + }, + "components" => { + :js_srcs_pattern => "*NativeComponent.js", + :generated_dir => components_output_dir, + :generated_files => [ + "ComponentDescriptors.h", + "EventEmitters.cpp", + "EventEmitters.h", + "Props.cpp", + "Props.h", + "RCTComponentViewHelpers.h", + "ShadowNodes.cpp", + "ShadowNodes.h" + ] + } + } - # Run the codegen as part of the Xcode build pipeline. - codegen_script_args = [ "'${PODS_TARGET_SRCROOT}/#{js_srcs}'" ] - codegen_script_args.push "'#{library_name}'" - codegen_script_args.push "'${PODS_TARGET_SRCROOT}/#{output_dir}/#{library_name}'" + # The path to JavaScript files + js_srcs_dir = options[:js_srcs_dir] ||= "./" + library_type = options[:library_type] - # We use a different library name for components, as well as an additional set of files. - # Eventually, we want these to be part of the same library as #{library_name} above. - component_library_name = options[:component_library_name] ||= library_name - component_output_dir = options[:component_output_dir] ||= output_dir - generated_dirs.push "#{component_output_dir}/#{component_library_name}" - codegen_script_args.push "'#{component_library_name}'" - codegen_script_args.push "'${PODS_TARGET_SRCROOT}/#{component_output_dir}/#{component_library_name}'" - components_generated_filenames = [ - "ComponentDescriptors.h", - "EventEmitters.cpp", - "EventEmitters.h", - "Props.cpp", - "Props.h", - "RCTComponentViewHelpers.h", - "ShadowNodes.cpp", - "ShadowNodes.h" - ] - generated_files = generated_files.concat(components_generated_filenames.map { |filename| "#{component_output_dir}/#{component_library_name}/#{filename}" }) + if library_type + js_srcs_pattern = codegen_config[library_type][:js_srcs_pattern] + end + + if library_type + generated_dirs = [ codegen_config[library_type][:generated_dir] ] + generated_files = codegen_config[library_type][:generated_files].map { |filename| "#{codegen_config[library_type][:generated_dir]}/#{filename}" } + else + generated_dirs = [ codegen_config["modules"][:generated_dir], codegen_config["components"][:generated_dir] ] + generated_files = codegen_config["modules"][:generated_files].map { |filename| "#{codegen_config["modules"][:generated_dir]}/#{filename}" } + generated_files = generated_files.concat(codegen_config["components"][:generated_files].map { |filename| "#{codegen_config["components"][:generated_dir]}/#{filename}" }) + end + + if js_srcs_pattern + file_list = `find #{js_srcs_dir} -type f -name #{js_srcs_pattern}`.split("\n").sort + input_files = file_list.map { |filename| "${PODS_TARGET_SRCROOT}/#{filename}" } + else + input_files = [ js_srcs_dir ] + end # Prepare filesystem by creating empty files that will be picked up as references by CocoaPods. prepare_command = "mkdir -p #{generated_dirs.join(" ")} && touch -a #{generated_files.join(" ")}" - system(prepare_command) # Always run prepare_command when a podspec uses the codegen, as CocoaPods may skip invoking this command in certain scenarios + system(prepare_command) # Always run prepare_command when a podspec uses the codegen, as CocoaPods may skip invoking this command in certain scenarios. Replace with pre_integrate_hook after updating to CocoaPods 1.11 spec.prepare_command = prepare_command spec.script_phase = { :name => 'Generate Specs', - :input_files => [ "${PODS_TARGET_SRCROOT}/#{js_srcs}" ], # This also needs to be relative to Xcode + :input_files => input_files, # This also needs to be relative to Xcode :output_files => ["${DERIVED_FILE_DIR}/codegen-#{library_name}.log"].concat(generated_files.map { |filename| " ${PODS_TARGET_SRCROOT}/#{filename}"} ), # The final generated files will be created when this script is invoked at Xcode build time. :script => %{set -o pipefail +set -e -bash -l -c '$\{PODS_TARGET_SRCROOT\}/#{prefix}/scripts/generate-specs.sh #{codegen_script_args.join(" ")}' 2>&1 | tee "${SCRIPT_OUTPUT_FILE_0}" +RN_DIR=$(cd "$\{PODS_TARGET_SRCROOT\}/#{prefix}" && pwd) + +GENERATED_SRCS_DIR="$\{DERIVED_FILE_DIR\}/generated/source/codegen" +GENERATED_SCHEMA_FILE="$GENERATED_SRCS_DIR/schema.json" +TEMP_OUTPUT_DIR="$GENERATED_SRCS_DIR/out" + +LIBRARY_NAME="#{library_name}" +OUTPUT_DIR="$\{PODS_TARGET_SRCROOT\}/#{output_dir}" + +CODEGEN_REPO_PATH="$RN_DIR/packages/react-native-codegen" +CODEGEN_NPM_PATH="$RN_DIR/../react-native-codegen" +CODEGEN_CLI_PATH="" + +# Determine path to react-native-codegen +if [ -d "$CODEGEN_REPO_PATH" ]; then + CODEGEN_CLI_PATH=$(cd "$CODEGEN_REPO_PATH" && pwd) +elif [ -d "$CODEGEN_NPM_PATH" ]; then + CODEGEN_CLI_PATH=$(cd "$CODEGEN_NPM_PATH" && pwd) +else + echo "error: Could not determine react-native-codegen location. Try running 'yarn install' or 'npm install' in your project root." >> "${SCRIPT_OUTPUT_FILE_0}" 2>&1 + exit 1 +fi + +find_node () { + source "$RN_DIR/scripts/find-node.sh" + + NODE_BINARY="${NODE_BINARY:-$(command -v node || true)}" + if [ -z "$NODE_BINARY" ]; then + echo "error: Could not find node. Make sure it is in bash PATH or set the NODE_BINARY environment variable." >> "${SCRIPT_OUTPUT_FILE_0}" 2>&1 + exit 1 + fi +} + +setup_dirs () { + set +e + rm -rf "$GENERATED_SRCS_DIR" + set -e + + mkdir -p "$GENERATED_SRCS_DIR" "$TEMP_OUTPUT_DIR" + + # Clear output files + > "${SCRIPT_OUTPUT_FILE_0}" +} + +describe () { + printf "\\n\\n>>>>> %s\\n\\n\\n" "$1" >> "${SCRIPT_OUTPUT_FILE_0}" 2>&1 +} + +buildCodegenCLI () { + if [ ! -d "$CODEGEN_CLI_PATH/lib" ]; then + describe "Building react-native-codegen package" + bash "$CODEGEN_CLI_PATH/scripts/oss/build.sh" + fi +} + +generateCodegenSchemaFromJavaScript () { + describe "Generating codegen schema from JavaScript" + + SRCS_PATTERN="#{js_srcs_pattern}" + SRCS_DIR="#{js_srcs_dir}" + if [ $SRCS_PATTERN ]; then + JS_SRCS=$(find "$\{PODS_TARGET_SRCROOT\}"/$SRCS_DIR -type f -name "$SRCS_PATTERN" -print0 | xargs -0) + echo "#{file_list}" >> "${SCRIPT_OUTPUT_FILE_0}" 2>&1 + else + JS_SRCS="$\{PODS_TARGET_SRCROOT\}/$SRCS_DIR" + echo "#{js_srcs_dir}" >> "${SCRIPT_OUTPUT_FILE_0}" 2>&1 + fi + + "$NODE_BINARY" "$CODEGEN_CLI_PATH/lib/cli/combine/combine-js-to-schema-cli.js" "$GENERATED_SCHEMA_FILE" $JS_SRCS +} + +generateCodegenArtifactsFromSchema () { + describe "Generating codegen artifacts from schema" + pushd "$RN_DIR" >/dev/null || exit 1 + "$NODE_BINARY" "scripts/generate-specs-cli.js" ios "$GENERATED_SCHEMA_FILE" "$TEMP_OUTPUT_DIR" "$LIBRARY_NAME" + popd >/dev/null || exit 1 +} + +moveOutputs () { + mkdir -p "$OUTPUT_DIR" + + # Copy all output to output_dir + cp -R "$TEMP_OUTPUT_DIR/" "$OUTPUT_DIR" || exit 1 + echo "$LIBRARY_NAME output has been written to $OUTPUT_DIR:" >> "${SCRIPT_OUTPUT_FILE_0}" 2>&1 + ls -1 "$OUTPUT_DIR" >> "${SCRIPT_OUTPUT_FILE_0}" 2>&1 +} + +main () { + setup_dirs + find_node + buildCodegenCLI + generateCodegenSchemaFromJavaScript + generateCodegenArtifactsFromSchema + moveOutputs +} + +main "$@" +echo 'Done.' >> "${SCRIPT_OUTPUT_FILE_0}" 2>&1 }, :execution_position => :before_compile, :show_env_vars_in_log => true