diff --git a/.github/workflows/compat.yml b/.github/workflows/compat.yml index 94a6fe01..ee7c4daa 100644 --- a/.github/workflows/compat.yml +++ b/.github/workflows/compat.yml @@ -55,5 +55,6 @@ jobs: --builder-bob-version ${{ matrix.bob-version }} \ --rn-version ${{ matrix.rn-version }} \ --packgage-json-mixin integration/fixtures/compat/package.json \ + --react-native-config integration/fixtures/compat/react-native.config.js \ --${{ matrix.runner == 'macos-latest' && 'ios' || 'android' }} \ ../turbo-module diff --git a/crates/ubrn_cli/src/android.rs b/crates/ubrn_cli/src/android.rs index 794ed05c..b42df382 100644 --- a/crates/ubrn_cli/src/android.rs +++ b/crates/ubrn_cli/src/android.rs @@ -39,6 +39,9 @@ pub(crate) struct AndroidConfig { #[serde(default = "AndroidConfig::default_package_name")] pub(crate) package_name: String, + + #[serde(default = "AndroidConfig::default_codegen_output_dir")] + pub(crate) codegen_output_dir: String, } impl Default for AndroidConfig { @@ -82,6 +85,10 @@ impl AndroidConfig { fn default_jni_libs() -> String { "src/main/jniLibs".to_string() } + + fn default_codegen_output_dir() -> String { + workspace::package_json().android_codegen_output_dir() + } } impl AndroidConfig { @@ -89,6 +96,10 @@ impl AndroidConfig { project_root.join(&self.directory) } + pub(crate) fn codegen_output_dir(&self, project_root: &Utf8Path) -> Utf8PathBuf { + project_root.join(&self.codegen_output_dir) + } + pub(crate) fn jni_libs(&self, project_root: &Utf8Path) -> Utf8PathBuf { self.directory(project_root).join(&self.jni_libs) } diff --git a/crates/ubrn_cli/src/codegen/mod.rs b/crates/ubrn_cli/src/codegen/mod.rs index d71f7ab5..45b76dd5 100644 --- a/crates/ubrn_cli/src/codegen/mod.rs +++ b/crates/ubrn_cli/src/codegen/mod.rs @@ -470,6 +470,7 @@ mod tests { cargo_extras: ExtraArgs::default(), api_level: 21, package_name: "com.tester".to_string(), + codegen_output_dir: "android/generated".to_string(), }; let ios = IOsConfig { directory: "ios".to_string(), @@ -477,6 +478,7 @@ mod tests { xcodebuild_extras: ExtraArgs::default(), targets: Default::default(), cargo_extras: ExtraArgs::default(), + codegen_output_dir: "ios/generated".to_string(), }; let bindings = BindingsConfig { cpp: "cpp/bindings".to_string(), diff --git a/crates/ubrn_cli/src/codegen/templates/build.kt.gradle b/crates/ubrn_cli/src/codegen/templates/build.kt.gradle index 6e37fff2..ad9d0a8d 100644 --- a/crates/ubrn_cli/src/codegen/templates/build.kt.gradle +++ b/crates/ubrn_cli/src/codegen/templates/build.kt.gradle @@ -117,8 +117,11 @@ android { main { if (isNewArchitectureEnabled()) { java.srcDirs += [ - "generated/java", - "generated/jni" + {%- let root = self.project_root() %} + {%- let dir = self.config.project.android.codegen_output_dir(root) %} + {%- let codegen = self.relative_to(root, dir) %} + "{{ codegen }}/java", + "{{ codegen }}/jni" ] } } diff --git a/crates/ubrn_cli/src/codegen/templates/module-template.podspec b/crates/ubrn_cli/src/codegen/templates/module-template.podspec index 0999ca89..a39d3194 100644 --- a/crates/ubrn_cli/src/codegen/templates/module-template.podspec +++ b/crates/ubrn_cli/src/codegen/templates/module-template.podspec @@ -24,11 +24,13 @@ Pod::Spec.new do |s| {%- let framework = self.relative_to(root, dir) %} {%- let dir = self.config.project.ios.directory(root) %} {%- let ios = self.relative_to(root, dir) %} + {%- let dir = self.config.project.ios.codegen_output_dir(root) %} + {%- let codegen = self.relative_to(root, dir) %} {%- let dir = self.config.project.tm.cpp_path(root) %} {%- let tm = self.relative_to(root, dir) %} {%- let dir = self.config.project.bindings.cpp_path(root) %} {%- let bindings = self.relative_to(root, dir) -%} - s.source_files = "{{ ios }}/**/*.{h,m,mm}", "{{ tm }}/**/*.{hpp,cpp,c,h}", "{{ bindings }}/**/*.{hpp,cpp,c,h}" + s.source_files = "{{ ios }}/**/*.{h,m,mm}", "{{ codegen }}/**/*.{h,m,mm}", "{{ tm }}/**/*.{hpp,cpp,c,h}", "{{ bindings }}/**/*.{hpp,cpp,c,h}" s.vendored_frameworks = "{{ framework }}" # Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0. diff --git a/crates/ubrn_cli/src/config/npm.rs b/crates/ubrn_cli/src/config/npm.rs index 956b193f..6c522192 100644 --- a/crates/ubrn_cli/src/config/npm.rs +++ b/crates/ubrn_cli/src/config/npm.rs @@ -47,6 +47,14 @@ impl PackageJson { } } + pub(crate) fn android_codegen_output_dir(&self) -> String { + self.codegen_config.output_dir.android.clone() + } + + pub(crate) fn ios_codegen_output_dir(&self) -> String { + self.codegen_config.output_dir.ios.clone() + } + pub(crate) fn repo(&self) -> &PackageJsonRepo { &self.repository } @@ -69,6 +77,8 @@ pub(crate) struct RnCodegenConfig { pub(crate) js_srcs_dir: String, #[serde(default)] android: RnAndroidCodegenConfig, + #[serde(default)] + output_dir: RnOutputDirCodegenConfig, } impl Default for RnCodegenConfig { @@ -82,3 +92,29 @@ impl Default for RnCodegenConfig { struct RnAndroidCodegenConfig { java_package_name: Option, } + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct RnOutputDirCodegenConfig { + #[serde(default = "default_ios_codegen_output_dir")] + ios: String, + #[serde(default = "default_android_codegen_output_dir")] + android: String, +} + +impl Default for RnOutputDirCodegenConfig { + fn default() -> Self { + Self { + ios: default_ios_codegen_output_dir(), + android: default_android_codegen_output_dir(), + } + } +} + +fn default_android_codegen_output_dir() -> String { + "android/generated".to_string() +} + +fn default_ios_codegen_output_dir() -> String { + "ios/generated".to_string() +} diff --git a/crates/ubrn_cli/src/ios.rs b/crates/ubrn_cli/src/ios.rs index 992acd82..eaa33e2f 100644 --- a/crates/ubrn_cli/src/ios.rs +++ b/crates/ubrn_cli/src/ios.rs @@ -37,6 +37,9 @@ pub(crate) struct IOsConfig { #[serde(default = "IOsConfig::default_cargo_extras")] pub(crate) cargo_extras: ExtraArgs, + + #[serde(default = "IOsConfig::default_codegen_output_dir")] + pub(crate) codegen_output_dir: String, } impl IOsConfig { @@ -74,6 +77,10 @@ impl IOsConfig { let args: &[&str] = &["aarch64-apple-ios", sim_target]; args.iter().map(|s| Target::from_str(s).unwrap()).collect() } + + fn default_codegen_output_dir() -> String { + workspace::package_json().ios_codegen_output_dir() + } } impl Default for IOsConfig { @@ -87,6 +94,10 @@ impl IOsConfig { project_root.join(&self.directory) } + pub(crate) fn codegen_output_dir(&self, project_root: &Utf8Path) -> Utf8PathBuf { + project_root.join(&self.codegen_output_dir) + } + pub(crate) fn framework_path(&self, project_root: &Utf8Path) -> Utf8PathBuf { let filename = format!("{}.xcframework", self.framework_name); project_root.join(filename) diff --git a/docs/src/reference/config-yaml.md b/docs/src/reference/config-yaml.md index a4ab07bb..6f208782 100644 --- a/docs/src/reference/config-yaml.md +++ b/docs/src/reference/config-yaml.md @@ -77,6 +77,7 @@ android: apiLevel: 21 jniLibs: src/main/jniLibs packageName: + codegenOutputDir: ``` The `directory` is the location of the Android project, relative to the root of the React Native library project. @@ -91,10 +92,16 @@ The `directory` is the location of the Android project, relative to the root of Reducing the number of targets to build for will speed up the edit-compile-run cycle. ``` -`packageName` is the name of the Android package that Codegen used to generate the TurboModule. This is derived from the `package.json` file, and can almost always be left. +`packageName` is the name of the Android package that Codegen used to generate the TurboModule. `codegenOutputDir` is the path under which Codegen stores its generated files. Both are derived from the `package.json` file, and can almost always be left. To customize the `packageName`, you should edit or add the entry at the path `codegenConfig`/`android`/`javaPackageName` in `package.json`. +To customize the `codegenOutputDir`, you should edit or add the entry at the path `codegenConfig`/`outputDir`/`android` in `package.json`. + +```admonish warning +Note that for Android the `outputDir` value in `package.json` needs to have a matching entry under `dependency`/`platforms`/`android`/`cmakeListsPath` in `react-native.config.js`. For example, if you set the Android output directory in `package.json` to `android/tmp`, the `cmakeListsPath` value in `react-native.config.js` needs to be set to `tmp/jni/CMakeLists.txt`. +``` + ## `ios` This is to configure the build steps for the Rust, the bindings, and the turbo-module code for iOS. @@ -110,6 +117,7 @@ ios: - aarch64-apple-ios-sim xcodebuildExtras: [] frameworkName: build/MyFramework + codegenOutputDir: ``` @@ -121,6 +129,14 @@ The `directory` is the location of the iOS project, relative to the root of the `xcodebuildExtras` is a list of extra arguments passed directly to the `xcodebuild` command. +`codegenOutputDir` is the path under which Codegen stores its generated files. This is derived from the `package.json` file, and can almost always be left. + +To customize the `codegenOutputDir`, you should edit or add the entry at the path `codegenConfig`/`outputDir`/`ios` in `package.json`. + +```admonish warning +Note that for Android the `outputDir` value in `package.json` needs to have a matching entry under `dependency`/`platforms`/`android`/`cmakeListsPath` in `react-native.config.js`. For example, if you set the Android output directory in `package.json` to `android/tmp`, the `cmakeListsPath` value in `react-native.config.js` needs to be set to `tmp/jni/CMakeLists.txt`. +``` + ## `turboModule` This section configures the location of the Typescript and C++ files generated by the `generate turbo-module` command. diff --git a/integration/fixtures/compat/react-native.config.js b/integration/fixtures/compat/react-native.config.js new file mode 100644 index 00000000..ee0de016 --- /dev/null +++ b/integration/fixtures/compat/react-native.config.js @@ -0,0 +1,9 @@ +module.exports = { + dependency: { + platforms: { + android: { + cmakeListsPath: "tmp/jni/CMakeLists.txt", + }, + }, + }, +}; diff --git a/scripts/test-turbo-modules.sh b/scripts/test-turbo-modules.sh index d1c09ba6..37a17b4a 100755 --- a/scripts/test-turbo-modules.sh +++ b/scripts/test-turbo-modules.sh @@ -15,6 +15,7 @@ reset_args() { SKIP_ANDROID=true UBRN_CONFIG= PACKAGE_JSON_MIXIN= + REACT_NATIVE_CONFIG= APP_TSX= } @@ -26,6 +27,7 @@ usage() { echo " -I, --ios Build for iOS." echo " -C, --ubrn-config Use a ubrn config file." echo " -P, --packgage-json-mixin Merge another JSON file into package.json" + echo " -R, --react-native-config Use a react-native.config.js file" echo " -T, --app-tsx Use a App.tsx file." echo echo " -s, --slug PROJECT_SLUG Specify the project slug (default: my-test-library)." @@ -105,6 +107,10 @@ parse_cli_options() { PACKAGE_JSON_MIXIN=$(join_paths "$PWD" "$2") shift ;; + -R|--react-native-config) + REACT_NATIVE_CONFIG=$(join_paths "$PWD" "$2") + shift + ;; -T|--app-tsx) APP_TSX=$(join_paths "$PWD" "$2") shift @@ -302,6 +308,9 @@ generate_turbo_module_for_compiling() { jq -s '.[0] * .[1]' ./package.json "$PACKAGE_JSON_MIXIN" > ./package.json.new mv ./package.json.new ./package.json fi + if [ -f "$REACT_NATIVE_CONFIG" ] ; then + cp "$REACT_NATIVE_CONFIG" ./react-native.config.js + fi if [ -f "$APP_TSX" ] ; then cp "$APP_TSX" ./example/src/App.tsx fi