nitro-codegen
Version:
The code-generator for react-native-nitro-modules.
174 lines (146 loc) • 5.4 kB
text/typescript
import { NitroConfig } from '../../config/NitroConfig.js'
import { createCppHybridObjectRegistration } from '../../syntax/c++/CppHybridObjectRegistration.js'
import { includeHeader } from '../../syntax/c++/includeNitroHeader.js'
import {
createFileMetadataString,
isNotDuplicate,
} from '../../syntax/helpers.js'
import { getJNINativeRegistrations } from '../../syntax/kotlin/JNINativeRegistrations.js'
import { createJNIHybridObjectRegistration } from '../../syntax/kotlin/KotlinHybridObjectRegistration.js'
import type { SourceFile, SourceImport } from '../../syntax/SourceFile.js'
import { indent } from '../../utils.js'
import { getBuildingWithGeneratedCmakeDefinition } from './createCMakeExtension.js'
export function createHybridObjectIntializer(): SourceFile[] {
const cxxNamespace = NitroConfig.getCxxNamespace('c++')
const cppLibName = NitroConfig.getAndroidCxxLibName()
const javaNamespace = NitroConfig.getAndroidPackage('java/kotlin')
const autolinkingClassName = `${NitroConfig.getAndroidCxxLibName()}OnLoad`
const jniRegistrations = getJNINativeRegistrations()
.map((r) => `${r.namespace}::${r.className}::registerNatives();`)
.filter(isNotDuplicate)
const autolinkedHybridObjects = NitroConfig.getAutolinkedHybridObjects()
const cppHybridObjectImports: SourceImport[] = []
const cppRegistrations: string[] = []
for (const hybridObjectName of Object.keys(autolinkedHybridObjects)) {
const config = autolinkedHybridObjects[hybridObjectName]
if (config?.cpp != null) {
// Autolink a C++ HybridObject!
const { cppCode, requiredImports } = createCppHybridObjectRegistration({
hybridObjectName: hybridObjectName,
cppClassName: config.cpp,
})
cppHybridObjectImports.push(...requiredImports)
cppRegistrations.push(cppCode)
}
if (config?.kotlin != null) {
// Autolink a Kotlin HybridObject through JNI/C++!
const { cppCode, requiredImports } = createJNIHybridObjectRegistration({
hybridObjectName: hybridObjectName,
jniClassName: config.kotlin,
})
cppHybridObjectImports.push(...requiredImports)
cppRegistrations.push(cppCode)
}
}
const buildingWithDefinition = getBuildingWithGeneratedCmakeDefinition()
const includes = [
...getJNINativeRegistrations().map((r) => includeHeader(r.import)),
...cppHybridObjectImports.map((i) => includeHeader(i)),
]
.filter(isNotDuplicate)
.join('\n')
const hppCode = `
${createFileMetadataString(`${autolinkingClassName}.hpp`)}
#include <jni.h>
#include <NitroModules/NitroDefines.hpp>
namespace ${cxxNamespace} {
/**
* Initializes the native (C++) part of ${cppLibName}, and autolinks all Hybrid Objects.
* Call this in your \`JNI_OnLoad\` function (probably inside \`cpp-adapter.cpp\`).
* Example:
* \`\`\`cpp (cpp-adapter.cpp)
* JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
* return ${cxxNamespace}::initialize(vm);
* }
* \`\`\`
*/
int initialize(JavaVM* vm);
} // namespace ${cxxNamespace}
`
const cppCode = `
${createFileMetadataString(`${autolinkingClassName}.cpp`)}
#ifndef ${buildingWithDefinition}
#error ${autolinkingClassName}.cpp is not being built with the autogenerated CMakeLists.txt project. Is a different CMakeLists.txt building this?
#endif
#include "${autolinkingClassName}.hpp"
#include <jni.h>
#include <fbjni/fbjni.h>
#include <NitroModules/HybridObjectRegistry.hpp>
${includes}
namespace ${cxxNamespace} {
int initialize(JavaVM* vm) {
using namespace margelo::nitro;
using namespace ${cxxNamespace};
using namespace facebook;
return facebook::jni::initialize(vm, [] {
// Register native JNI methods
${indent(jniRegistrations.join('\n'), ' ')}
// Register Nitro Hybrid Objects
${indent(cppRegistrations.join('\n'), ' ')}
});
}
} // namespace ${cxxNamespace}
`.trim()
const kotlinCode = `
${createFileMetadataString(`${autolinkingClassName}.kt`)}
package ${javaNamespace}
import android.util.Log
internal class ${autolinkingClassName} {
companion object {
private const val TAG = "${autolinkingClassName}"
private var didLoad = false
/**
* Initializes the native part of "${cppLibName}".
* This method is idempotent and can be called more than once.
*/
@JvmStatic
fun initializeNative() {
if (didLoad) return
try {
Log.i(TAG, "Loading ${cppLibName} C++ library...")
System.loadLibrary("${cppLibName}")
Log.i(TAG, "Successfully loaded ${cppLibName} C++ library!")
didLoad = true
} catch (e: Error) {
Log.e(TAG, "Failed to load ${cppLibName} C++ library! Is it properly installed and linked? " +
"Is the name correct? (see \`CMakeLists.txt\`, at \`add_library(...)\`)", e)
throw e
}
}
}
}
`.trim()
return [
{
content: hppCode,
language: 'c++',
name: `${autolinkingClassName}.hpp`,
platform: 'android',
subdirectory: [],
},
{
content: cppCode,
language: 'c++',
name: `${autolinkingClassName}.cpp`,
platform: 'android',
subdirectory: [],
},
{
content: kotlinCode,
language: 'kotlin',
name: `${autolinkingClassName}.kt`,
platform: 'android',
subdirectory: ['kotlin', ...javaNamespace.split('.')],
},
]
}