diff --git a/.github/actions/build_android/action.yml b/.github/actions/build_android/action.yml new file mode 100644 index 0000000..7c78d53 --- /dev/null +++ b/.github/actions/build_android/action.yml @@ -0,0 +1,34 @@ +name: 'Build Android Library' +description: 'Builds the Android library using Fastlane' + +runs: + using: 'composite' + + steps: + - name: Set up JDK + uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: 17 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '2.7' + + - name: Install Fastlane + run: gem install fastlane + shell: bash + + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version-file: .nvmrc + + - name: Build Android Library with Fastlane + run: cd android && fastlane android build_mendix_native + shell: bash + + - name: Copy Android Library to Shared Directory + run: mkdir -p ${{ github.workspace }}/shared/libs/android && cp ./artifacts/aar/mendixnative-release.aar ${{ github.workspace }}/shared/libs/android/mendixnative-release.aar + shell: bash diff --git a/.github/actions/build_ios/action.yml b/.github/actions/build_ios/action.yml new file mode 100644 index 0000000..800ee2b --- /dev/null +++ b/.github/actions/build_ios/action.yml @@ -0,0 +1,23 @@ +name: 'Build iOS Library' +description: 'Builds the iOS library using Fastlane' + +runs: + using: 'composite' + + steps: + - name: Install Fastlane + run: gem install fastlane + shell: bash + + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version-file: .nvmrc + + - name: Build iOS Library with Fastlane + run: cd ios && fastlane ios build_mendix_native + shell: bash + + - name: Copy iOS Library to Shared Directory + run: mkdir -p ${{ github.workspace }}/shared/libs/ios && cp -R ./ios/build/mendixnative/mendixnative.xcframework ${{ github.workspace }}/shared/libs/ios/mendixnative.xcframework + shell: bash diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml deleted file mode 100644 index fb98c79..0000000 --- a/.github/actions/setup/action.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Setup -description: Setup Node.js and install dependencies - -runs: - using: composite - steps: - - name: Setup Node.js - uses: actions/setup-node@v3 - with: - node-version-file: .nvmrc - - - name: Cache dependencies - id: yarn-cache - uses: actions/cache@v3 - with: - path: | - **/node_modules - .yarn/install-state.gz - key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }}-${{ hashFiles('**/package.json', '!node_modules/**') }} - restore-keys: | - ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} - ${{ runner.os }}-yarn- - - - name: Install dependencies - if: steps.yarn-cache.outputs.cache-hit != 'true' - run: yarn install --immutable - shell: bash diff --git a/.github/workflows/BuildLibrary.yml b/.github/workflows/BuildLibrary.yml new file mode 100644 index 0000000..6756581 --- /dev/null +++ b/.github/workflows/BuildLibrary.yml @@ -0,0 +1,49 @@ +name: 'Build @mendix/native' + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + build-android: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Build Android Library + uses: ./.github/actions/build_android + + build-ios: + runs-on: macos-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Build iOS Library + uses: ./.github/actions/build_ios + + create-release: + needs: [build-android, build-ios] + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Zip Libraries + run: | + cd ${{ github.workspace }}/shared/libs + zip -r libraries.zip . + + - name: Upload Libraries to Artifacts + uses: actions/upload-artifact@v4 + with: + name: mendixnative-archive + path: ${{ github.workspace }}/shared/libs/libraries.zip diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 6297ef1..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,154 +0,0 @@ -name: CI -on: - push: - branches: - - main - pull_request: - branches: - - main - -jobs: - lint: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Setup - uses: ./.github/actions/setup - - - name: Lint files - run: yarn lint - - - name: Typecheck files - run: yarn typecheck - - test: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Setup - uses: ./.github/actions/setup - - - name: Run unit tests - run: yarn test --maxWorkers=2 --coverage - - build-library: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Setup - uses: ./.github/actions/setup - - - name: Build package - run: yarn prepare - - build-android: - runs-on: ubuntu-latest - env: - TURBO_CACHE_DIR: .turbo/android - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Setup - uses: ./.github/actions/setup - - - name: Cache turborepo for Android - uses: actions/cache@v3 - with: - path: ${{ env.TURBO_CACHE_DIR }} - key: ${{ runner.os }}-turborepo-android-${{ hashFiles('yarn.lock') }} - restore-keys: | - ${{ runner.os }}-turborepo-android- - - - name: Check turborepo cache for Android - run: | - TURBO_CACHE_STATUS=$(node -p "($(yarn turbo run build:android --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json)).tasks.find(t => t.task === 'build:android').cache.status") - - if [[ $TURBO_CACHE_STATUS == "HIT" ]]; then - echo "turbo_cache_hit=1" >> $GITHUB_ENV - fi - - - name: Install JDK - if: env.turbo_cache_hit != 1 - uses: actions/setup-java@v3 - with: - distribution: 'zulu' - java-version: '17' - - - name: Finalize Android SDK - if: env.turbo_cache_hit != 1 - run: | - /bin/bash -c "yes | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --licenses > /dev/null" - - - name: Cache Gradle - if: env.turbo_cache_hit != 1 - uses: actions/cache@v3 - with: - path: | - ~/.gradle/wrapper - ~/.gradle/caches - key: ${{ runner.os }}-gradle-${{ hashFiles('example/android/gradle/wrapper/gradle-wrapper.properties') }} - restore-keys: | - ${{ runner.os }}-gradle- - - - name: Build example for Android - env: - JAVA_OPTS: "-XX:MaxHeapSize=6g" - run: | - yarn turbo run build:android --cache-dir="${{ env.TURBO_CACHE_DIR }}" - - build-ios: - runs-on: macos-14 - env: - TURBO_CACHE_DIR: .turbo/ios - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Setup - uses: ./.github/actions/setup - - - name: Cache turborepo for iOS - uses: actions/cache@v3 - with: - path: ${{ env.TURBO_CACHE_DIR }} - key: ${{ runner.os }}-turborepo-ios-${{ hashFiles('yarn.lock') }} - restore-keys: | - ${{ runner.os }}-turborepo-ios- - - - name: Check turborepo cache for iOS - run: | - TURBO_CACHE_STATUS=$(node -p "($(yarn turbo run build:ios --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json)).tasks.find(t => t.task === 'build:ios').cache.status") - - if [[ $TURBO_CACHE_STATUS == "HIT" ]]; then - echo "turbo_cache_hit=1" >> $GITHUB_ENV - fi - - - name: Cache cocoapods - if: env.turbo_cache_hit != 1 - id: cocoapods-cache - uses: actions/cache@v3 - with: - path: | - **/ios/Pods - key: ${{ runner.os }}-cocoapods-${{ hashFiles('example/ios/Podfile.lock') }} - restore-keys: | - ${{ runner.os }}-cocoapods- - - - name: Install cocoapods - if: env.turbo_cache_hit != 1 && steps.cocoapods-cache.outputs.cache-hit != 'true' - run: | - cd example/ios - pod install - env: - NO_FLIPPER: 1 - - - name: Build example for iOS - run: | - yarn turbo run build:ios --cache-dir="${{ env.TURBO_CACHE_DIR }}" diff --git a/.gitignore b/.gitignore index d741fcd..263fdf7 100644 --- a/.gitignore +++ b/.gitignore @@ -69,9 +69,6 @@ android/keystores/debug.keystore # Turborepo .turbo/ -# generated by bob -lib/ - # fastlane specific **/fastlane/report.xml @@ -86,3 +83,5 @@ lib/ # Fastlane.swift runner binary **/fastlane/FastlaneRunner + +/artifacts/* diff --git a/ios/fastlane/Fastfile b/ios/fastlane/Fastfile index 28638a6..ba52e73 100644 --- a/ios/fastlane/Fastfile +++ b/ios/fastlane/Fastfile @@ -8,9 +8,9 @@ fastlane_version "2.213.0" default_platform :ios platform :ios do - + desc "Build a new version of MendixNative lib" - lane :build_mendixnative do |options| + lane :build_mendix_native do |options| sh("npm", "ci", "--legacy-peer-deps") cocoapods diff --git a/ios/fastlane/report.xml b/ios/fastlane/report.xml deleted file mode 100644 index c048edb..0000000 --- a/ios/fastlane/report.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/.gitignore b/lib/.gitignore new file mode 100644 index 0000000..149f722 --- /dev/null +++ b/lib/.gitignore @@ -0,0 +1,5 @@ +.npmrc +androidlib/*.aar +ios/mendixnative.xcframework/* +node_modules +artifacts diff --git a/lib/.release-it.js b/lib/.release-it.js new file mode 100644 index 0000000..5e47f1a --- /dev/null +++ b/lib/.release-it.js @@ -0,0 +1,34 @@ +module.exports = { + npm: { + publish: true, + }, + git: { + push: false, + requireUpstream: false, + tag: false, + requireCleanWorkingDir: false, + commitMessage: + "[RELEASE] Bump @mendix/native library version to v${version}", + }, + publishConfig: { + registry: + "https://nexus.staging.not-rnd.mendix.com/repository/npm-hosted", + }, + plugins: { + "@release-it/keep-a-changelog": { + filename: "../CHANGELOG.mendixnative.md", + strictLatest: false, + keepUnreleased: process.env.VERSION === "prerelease", + addUnreleased: true, + }, + }, + hooks: { + "before:init": [ + "git fetch origin", + "git checkout " + process.env.BRANCH, + "git pull --rebase origin " + process.env.BRANCH + " --autostash", + ], + "before:git:release": ["git add ../CHANGELOG.mendixnative.md"], + "after:release": ["git push origin " + process.env.BRANCH], + }, +}; diff --git a/lib/LICENSE.md b/lib/LICENSE.md new file mode 100644 index 0000000..fde8b1b --- /dev/null +++ b/lib/LICENSE.md @@ -0,0 +1,19 @@ +Copyright (c) Mendix + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/androidlib/build.gradle b/lib/androidlib/build.gradle new file mode 100644 index 0000000..7028549 --- /dev/null +++ b/lib/androidlib/build.gradle @@ -0,0 +1,2 @@ +configurations.maybeCreate("default") +artifacts.add("default", file("mendixnative-release.aar")) diff --git a/lib/androidlib/mendix.gradle b/lib/androidlib/mendix.gradle new file mode 100644 index 0000000..693da9b --- /dev/null +++ b/lib/androidlib/mendix.gradle @@ -0,0 +1,438 @@ +import groovy.json.JsonSlurper + +def LOG_PREFIX = ":Mendix: " + +def rootDir = buildscript.sourceFile.toString().split("node_modules")[0] +def cliBinPath = "${rootDir}/node_modules/.bin/react-native${System.properties['os.name'].toLowerCase().contains('windows') ? ".cmd" : ""}" + +def generatedFilePackage = "com.mendix.nativetemplate" +def mainActivityObserverFileName = "MendixActivityObserver.java" +def mainActivityObserverTemplate = """package $generatedFilePackage; + +import android.content.Context; + +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleObserver; +import androidx.lifecycle.OnLifecycleEvent; +{{imports}} + +public class MendixActivityObserver implements LifecycleObserver { + private final Context context; + + public MendixActivityObserver(Context activity) { + this.context = activity; + } + + @OnLifecycleEvent(Lifecycle.Event.ON_CREATE) + void onCreate() { + {{onCreate}} + } + + @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) + void onResume() { + {{onResume}} + } + + @OnLifecycleEvent(Lifecycle.Event.ON_START) + void onStart() { + {{onStart}} + } + + @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) + void onPause() { + {{onPause}} + } + + @OnLifecycleEvent(Lifecycle.Event.ON_STOP) + void onStop() { + {{onStop}} + } + + @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) + void onDestroy() { + {{onDestroy}} + } +} +""" +def mendixPackageListFileName = "MendixPackageList.java" +def mendixPackageListTemplate = """package $generatedFilePackage; + +import android.app.Application; +import android.content.Context; +import android.content.res.Resources; + +import com.facebook.react.ReactPackage; +import com.facebook.react.shell.MainPackageConfig; +import com.facebook.react.shell.MainReactPackage; +import java.util.Arrays; +import java.util.ArrayList; + +{{imports}} + +public class MendixPackageList { + private Application application; + + public MendixPackageList(Application application) { + this.application = application; + } + + private Resources getResources() { + return this.getApplication().getResources(); + } + + private Application getApplication() { + return this.application; + } + + private Context getApplicationContext() { + return this.getApplication().getApplicationContext(); + } + + public ArrayList getPackages() { + return new ArrayList<>(Arrays.asList( + {{packageClassInstances}} + )); + } +} +""" + +class MendixModules { + private static String LINE_ENDING_CHAR = "\n" + + private String cliBinPath + private String rootDir + private String logPrefix + private Logger logger + private ArrayList> reactNativeModules + private Map dependenciesConfig = [:] + private File capabilitiesConfigFile + private File projectCapabilitiesFile + private File nodeModulesDependenciesConfigFile + + MendixModules(File capabilitiesConfigFile, File nodeModulesDependenciesConfigFile, File projectCapabilitiesFile, String cliBinPath, String rootDir, Logger logger, String logPrefix) { + this.logger = logger + this.rootDir = rootDir + this.cliBinPath = cliBinPath + this.logPrefix = logPrefix + this.capabilitiesConfigFile = capabilitiesConfigFile + this.nodeModulesDependenciesConfigFile = nodeModulesDependenciesConfigFile + this.projectCapabilitiesFile = projectCapabilitiesFile + + def (nativeModules) = this.getReactNativeConfig() + this.reactNativeModules = nativeModules + parseDependenciesConfig() + } + + void printDependencies() { + this.reactNativeModules.each { + logDebug(it["name"]) + } + } + + void parseDependenciesConfig() { + def dependenciesConfig = [:] + def capabilitiesConfig = [:] + + try { + capabilitiesConfig = new JsonSlurper().parse(this.capabilitiesConfigFile) + def projectCapabilities = new JsonSlurper().parse(this.projectCapabilitiesFile) + capabilitiesConfig.retainAll { capabilityConfig -> + projectCapabilities.find { enabledCapability -> + enabledCapability.key == capabilityConfig.key && enabledCapability.value == true + } && capabilityConfig.value["android"] != null + } + } catch (ignored) { + this.logLifecycle("Failed parsing the capabilities file. Error?") + } + + if (this.nodeModulesDependenciesConfigFile.exists()) { + try { + dependenciesConfig = new JsonSlurper().parse(this.nodeModulesDependenciesConfigFile) + (dependenciesConfig as Map).retainAll { dependencyConfig -> + this.reactNativeModules.find { nativeModule -> + nativeModule.get("name") == dependencyConfig.key + } && dependencyConfig.value["android"] != null + } + } catch (ignored) { + this.logLifecycle("Failed parsing the configuration for unlinked node_modules. Error?") + } + } + + this.dependenciesConfig = capabilitiesConfig + dependenciesConfig + printDependencies() + } + + void generateMainActivityObserver(File outDir, String fileName, String template) { + def activityImports = [] + def activityOnCreateEntries = [] + def activityOnStartEntries = [] + def activityOnResumeEntries = [] + def activityOnPauseEntries = [] + def activityOnStopEntries = [] + def activityOnDestroyEntries = [] + + dependenciesConfig.each { + def mainActivityDelegateEntry = it.value["android"]["MainActivity"] + if (!mainActivityDelegateEntry) + return + + def imports = mainActivityDelegateEntry.get("imports") + if (imports) + activityImports.addAll(imports) + + def onCreateEntries = mainActivityDelegateEntry.get("onCreate") + if (onCreateEntries) + activityOnCreateEntries.addAll(onCreateEntries) + + def onStartEntries = mainActivityDelegateEntry.get("onStart") + if (onStartEntries) + activityOnStartEntries.addAll(onStartEntries) + + def onResumeEntries = mainActivityDelegateEntry.get("onResume") + if (onResumeEntries) + activityOnResumeEntries.addAll(onResumeEntries) + + def onPauseEntries = mainActivityDelegateEntry.get("onPause") + if (onPauseEntries) + activityOnPauseEntries.addAll(onPauseEntries) + + def onStopEntries = mainActivityDelegateEntry.get("onStop") + if (onStopEntries) + activityOnStopEntries.addAll(onStopEntries) + + def onDestroyEntries = mainActivityDelegateEntry.get("onDestroy") + if (onDestroyEntries) + activityOnDestroyEntries.addAll(onDestroyEntries) + } + + String CODE_PADDING = "${LINE_ENDING_CHAR} " + String generatedFileContents = template + .replace("{{imports}}", activityImports.join(LINE_ENDING_CHAR)) + .replace("{{onCreate}}", activityOnCreateEntries.join(CODE_PADDING)) + .replace("{{onStart}}", activityOnStartEntries.join(CODE_PADDING)) + .replace("{{onResume}}", activityOnResumeEntries.join(CODE_PADDING)) + .replace("{{onPause}}", activityOnPauseEntries.join(CODE_PADDING)) + .replace("{{onStop}}", activityOnStopEntries.join(CODE_PADDING)) + .replace("{{onDestroy}}", activityOnDestroyEntries.join(CODE_PADDING)) + + outDir.mkdirs() + new FileTreeBuilder(outDir).file(fileName).newWriter().withWriter { + w -> + w << generatedFileContents + } + } + + void generateMendixPackageList(File outDir, String fileName, String template) { + String CODE_PADDING = "${LINE_ENDING_CHAR} " + def imports = [] + def entries = [] + def entrySeparator = "," + CODE_PADDING + dependenciesConfig.each { + def packageListEntry = it.value["android"]["packageListEntries"] + if (packageListEntry) { + def importsEntry = packageListEntry["imports"] + def packageClassInstances = packageListEntry["packageClassInstances"] + if (importsEntry) + imports.addAll(importsEntry) + if (packageClassInstances) + entries.addAll(packageClassInstances) + } + } + + String generatedFileContents = template.replace("{{imports}}", imports.join(CODE_PADDING)).replace("{{packageClassInstances}}", entries.join(entrySeparator)) + + outDir.mkdirs() + new FileTreeBuilder(outDir).file(fileName).newWriter().withWriter { + w -> + w << generatedFileContents + } + } + + void addClassPaths(Project project) { + project.buildscript { + dependencies { + dependenciesConfig.each { + def gradle = (it.value as Object)["android"]["gradle"] + if (!gradle) { + return + } + def customClassPaths = gradle.get("classpaths") as ArrayList + customClassPaths.each { customClassPath -> + this.logLifecycle("Adding classPath ${customClassPath}") + classpath(customClassPath) + } + } + } + } + } + + void addExtraDependencies(Project project) { + project.dependencies { + dependenciesConfig.each { + def dependencies = it.value["android"]["externalDependencies"] as ArrayList + dependencies.each { dependency -> + this.logLifecycle("Registering extra library ${dependency}") + implementation(dependency) + } + } + } + } + + void addAndroidPlugins(Project project) { + dependenciesConfig.each { + def gradleConfig = it.value["android"]["gradle"] + if (!gradleConfig) + return + + def dependencies = gradleConfig["plugins"] as ArrayList + if (!dependencies) + return + + dependencies.each { plugin -> + this.logLifecycle("Adding plugin ${plugin}") + project.getPluginManager().apply(plugin) + } + } + } + + void logDebug(String message) { + this.logger.debug("${this.logPrefix}${message}") + } + + void logLifecycle(String message) { + this.logger.lifecycle("${this.logPrefix}${message}") + } + + void logError(String message) { + this.logger.error("${this.logPrefix}${message}") + } + + /** + * Runs a specified command using Runtime exec() in a specified directory. + * Throws when the command result is empty. + */ + String getCommandOutput(String[] command, File directory) { + try { + def output = "" + def cmdProcess = Runtime.getRuntime().exec(command, null, directory) + def bufferedReader = new BufferedReader(new InputStreamReader(cmdProcess.getInputStream())) + def buff = "" + def readBuffer = new StringBuffer() + while ((buff = bufferedReader.readLine()) != null) { + readBuffer.append(buff) + } + output = readBuffer.toString() + if (!output) { + this.logger.error("${logPrefix}Unexpected empty result of running '${command}' command.") + def bufferedErrorReader = new BufferedReader(new InputStreamReader(cmdProcess.getErrorStream())) + def errBuff = "" + def readErrorBuffer = new StringBuffer() + while ((errBuff = bufferedErrorReader.readLine()) != null) { + readErrorBuffer.append(errBuff) + } + throw new Exception(readErrorBuffer.toString()) + } + return output + } catch (Exception exception) { + this.logError("Running '${command}' command failed.") + throw exception + } + } + + /** + * Runs a process to call the React Native CLI Config command and parses the output + */ + ArrayList> getReactNativeConfig() { + if (this.reactNativeModules != null) return this.reactNativeModules + + ArrayList> reactNativeModules = new ArrayList>() + + String[] reactNativeConfigCommand = [this.cliBinPath, "config"] + def reactNativeConfigOutput = this.getCommandOutput(reactNativeConfigCommand, new File(this.rootDir)) + + def json + try { + json = new JsonSlurper().parseText(reactNativeConfigOutput) + } catch (Exception exception) { + throw new Exception("Calling `${reactNativeConfigCommand}` finished with an exception. Error message: ${exception.toString()}. Output: ${reactNativeConfigOutput}"); + } + def dependencies = json["dependencies"] + def project = json["project"]["android"] + + if (project == null) { + throw new Exception("React Native CLI failed to determine Android project configuration. This is likely due to misconfiguration. Config output:\n${json.toMapString()}") + } + + dependencies.each { name, value -> + def platformsConfig = value["platforms"]; + def androidConfig = platformsConfig["android"] + + if (androidConfig != null && androidConfig["sourceDir"] != null) { + this.logger.info("${logPrefix}Automatically adding native module '${name}'") + + HashMap reactNativeModuleConfig = new HashMap() + reactNativeModuleConfig.put("name", name) + reactNativeModuleConfig.put("nameCleansed", name.replaceAll('^@([\\w-]+)/', '$1_')) + reactNativeModuleConfig.put("androidSourceDir", androidConfig["sourceDir"]) + reactNativeModuleConfig.put("packageInstance", androidConfig["packageInstance"]) + reactNativeModuleConfig.put("packageImportPath", androidConfig["packageImportPath"]) + this.logger.trace("${logPrefix}'${name}': ${reactNativeModuleConfig.toMapString()}") + + reactNativeModules.add(reactNativeModuleConfig) + } else { + this.logger.info("${logPrefix}Skipping native module '${name}'") + } + } + + return [reactNativeModules, json["project"]["android"]["packageName"]]; + } +} + +def generatedSrcDir = new File(buildDir, "generated/mendix/src/main/java") +def generatedCodeDir = new File(generatedSrcDir, generatedFilePackage.replace('.', '/')) + +def capabilitiesConfig = new File("${rootDir}capabilities-setup-config.json") +def unlinkedDependenciesConfigFile = new File("${rootDir}unlinked-dependency-config.json") +def capabilitiesFile = new File("${rootDir}capabilities.android.json") +def mendixModules = new MendixModules(capabilitiesConfig, unlinkedDependenciesConfigFile, capabilitiesFile, cliBinPath, rootDir, logger, LOG_PREFIX) + +def logLifecycle = { String message -> logger.lifecycle("${LOG_PREFIX}${message}") } + + +ext.applyMendixGradle = { Project project -> + logLifecycle("Registering extra dependencies") + mendixModules.addExtraDependencies(project) + + logLifecycle("Registering plugins") + mendixModules.addAndroidPlugins(project) + task generateMendixDependencies { + doLast { + logLifecycle("Executing Mendix Module Generator") + logLifecycle("App root: ${rootDir}") + logLifecycle("CLI path: ${cliBinPath}") + + logLifecycle("Generating ${mainActivityObserverFileName}") + mendixModules.generateMainActivityObserver(generatedCodeDir, mainActivityObserverFileName, mainActivityObserverTemplate) + + logLifecycle("Generating ${mendixPackageListFileName}") + mendixModules.generateMendixPackageList(generatedCodeDir, mendixPackageListFileName, mendixPackageListTemplate) + } + } + + preBuild.dependsOn generateMendixDependencies + + android { + sourceSets { + main { + java { + srcDirs += generatedSrcDir + } + } + } + } +} + +ext.applyMendixClassPaths = { Project project -> + logLifecycle("Registering class paths") + mendixModules.addClassPaths(project) +} diff --git a/lib/ios/.keep b/lib/ios/.keep new file mode 100644 index 0000000..e69de29 diff --git a/lib/package.json b/lib/package.json new file mode 100644 index 0000000..2a96c4b --- /dev/null +++ b/lib/package.json @@ -0,0 +1,18 @@ +{ + "name": "@mendix/native", + "version": "3.0.1", + "copyright": "© Mendix Technology BV. All rights reserved.", + "files": [ + "androidlib/", + "ios/", + "LICENSE" + ], + "scripts": { + "release": "release-it" + }, + "devDependencies": { + "release-it": "^15.1.0", + "@release-it/conventional-changelog": "^5.0.0", + "@release-it/keep-a-changelog": "^3.0.0" + } +}