1

TL;DR

I have this C function in meaning_of_life.c:

#define FFI_PLUGIN_EXPORT

FFI_PLUGIN_EXPORT int getMeaningOfLife(){
    return 42;
}

I tried to compile it to a shared library (.so) using alot of ways but I keep getting errors when I try to use it on android like incompatible target and file in wrong format. Can you tell me the right way to do it?

What I tried:

  1. create the above C file
  2. create a flutter_ffi plugin that will use it like this(this is somewhat irrelevant here):
add_library(meaningoflife SHARED IMPORTED )
set_target_properties(meaningoflife
        PROPERTIES
        IMPORTED_LOCATION
        /home/haidar/dev_haidar/c++/meaning_of_life/Src/libLife.so
        )

target_link_libraries(my_plugin meaningoflife)
  1. Read alot, alot... of docs, and searched alot here on SO but most related questions and answers are very old and use old ways ( they don't use cmake )
  2. Create a flutter app that will use the plugin
  3. Create a .so shared library from the plugin, first I tried the method on this page (using the NDK compilers and toolchain):
cd /home/haidar/dev_haidar/c++/meaning_of_life/Src
comp=/home/haidar/Android/Sdk/ndk/25.1.8937393/toolchains/llvm/prebuilt/linux-x86_64/bin
$comp/aarch64-linux-android26-clang  -fPIC meaning_of_life.c -o libLife.so -shared // also tried with other compilers as armv7a-linux-androideabi26-clang

The library is generated, but when I use it, I get one of the errors mentioned above (depending on the compiler used to create it), for example, here is some part of the error:

[        ] FAILURE: Build failed with an exception.
[        ] * What went wrong:
[        ] Execution failed for task ':image_magick_ffi:buildCMakeDebug[armeabi-v7a]'.
[        ] > Build command failed.
[        ]   Error while executing process /home/haidar/Android/Sdk/cmake/3.18.1/bin/ninja with arguments {-C /home/haidar/dev_haidar/flutter/image_magick_ffi/android/.cxx/Debug/b5x10294/armeabi-v7a image_magick_ffi}
[        ]   ninja: Entering directory `/home/haidar/dev_haidar/flutter/image_magick_ffi/android/.cxx/Debug/b5x10294/armeabi-v7a'
[        ]   [1/1] Linking C shared library /home/haidar/dev_haidar/flutter/magick_app/build/image_magick_ffi/intermediates/cxx/Debug/b5x10294/obj/armeabi-v7a/libimage_magick_ffi.so
[        ]   FAILED: /home/haidar/dev_haidar/flutter/magick_app/build/image_magick_ffi/intermediates/cxx/Debug/b5x10294/obj/armeabi-v7a/libimage_magick_ffi.so 
[        ]   : && /home/haidar/Android/Sdk/ndk/21.1.6352462/toolchains/llvm/prebuilt/linux-x86_64/bin/clang --target=armv7-none-linux-androideabi16 --gcc-toolchain=/home/haidar/Android/Sdk/ndk/21.1.6352462/toolchains/llvm/prebuilt/linux-x86_64 --sysroot=/home/haidar/Android/Sdk/ndk/21.1.6352462/toolchains/llvm/prebuilt/linux-x86_64/sysroot -fPIC -g -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 -march=armv7-a -mthumb -Wformat -Werror=format-security  -O0 -fno-limit-debug-info  -Wl,--exclude-libs,libgcc.a -Wl,--exclude-libs,libgcc_real.a -Wl,--exclude-libs,libatomic.a -static-libstdc++ -Wl,--build-id -Wl,--fatal-warnings -Wl,--exclude-libs,libunwind.a -Wl,--no-undefined -Qunused-arguments -shared -Wl,-soname,libimage_magick_ffi.so -o /home/haidar/dev_haidar/flutter/magick_app/build/image_magick_ffi/intermediates/cxx/Debug/b5x10294/obj/armeabi-v7a/libimage_magick_ffi.so CMakeFiles/image_magick_ffi.dir/image_magick_ffi.c.o  /home/haidar/dev_haidar/c++/meaning_of_life/Src/libLife.so  -latomic -lm && :
[        ]   /home/haidar/Android/Sdk/ndk/21.1.6352462/toolchains/llvm/prebuilt/linux-x86_64/lib/gcc/arm-linux-androideabi/4.9.x/../../../../arm-linux-androideabi/bin/ld: error: /home/haidar/dev_haidar/c++/meaning_of_life/Src/libLife.so: incompatible target
[        ]   clang: error: linker command failed with exit code 1 (use -v to see invocation)
[        ]   ninja: build stopped: subcommand failed.
[        ] * Try:
[        ] > Run with --debug option to get more log output.
[        ] > Run with --scan to get full insights.
[        ] * Exception is:
[        ] org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':image_magick_ffi:buildCMakeDebug[armeabi-v7a]'.
[        ]  at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.lambda$executeIfValid$1(ExecuteActionsTaskExecuter.java:147)
[        ]  at org.gradle.internal.Try$Failure.ifSuccessfulOrElse(Try.java:282)
[        ]  at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:145)
[        ]  at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:133)
[        ]  at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:77)
[        ]  at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
[        ]  at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:51)
[        ]  at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
[        ]  at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:56)
[        ]  at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
[        ]  at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
[        ]  at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
[        ]  at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
[        ]  at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
[        ]  at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:199)
[        ]  at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
[        ]  at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
[        ]  at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
[        ]  at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
[        ]  at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
[        ]  at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:73)
[        ]  at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
[        ]  at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:74)
[        ]  at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:333)
[        ]  at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:320)
[        ]  at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:313)
[        ]  at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:299)
[        ]  at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.lambda$run$0(DefaultPlanExecutor.java:143)
[        ]  at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:227)
[        ]  at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.executeNextNode(DefaultPlanExecutor.java:218)
[        ]  at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:140)
[        ]  at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
[        ]  at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
[        ]  at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
[        ]  at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
[        ]  at java.base/java.lang.Thread.run(Thread.java:829)
[        ] Caused by: org.gradle.internal.UncheckedException: Build command failed.
[        ] Error while executing process /home/haidar/Android/Sdk/cmake/3.18.1/bin/ninja with arguments {-C /home/haidar/dev_haidar/flutter/image_magick_ffi/android/.cxx/Debug/b5x10294/armeabi-v7a image_magick_ffi}
[        ] ninja: Entering directory `/home/haidar/dev_haidar/flutter/image_magick_ffi/android/.cxx/Debug/b5x10294/armeabi-v7a'
[        ] [1/1] Linking C shared library /home/haidar/dev_haidar/flutter/magick_app/build/image_magick_ffi/intermediates/cxx/Debug/b5x10294/obj/armeabi-v7a/libimage_magick_ffi.so
[        ] FAILED: /home/haidar/dev_haidar/flutter/magick_app/build/image_magick_ffi/intermediates/cxx/Debug/b5x10294/obj/armeabi-v7a/libimage_magick_ffi.so 
[        ] : && /home/haidar/Android/Sdk/ndk/21.1.6352462/toolchains/llvm/prebuilt/linux-x86_64/bin/clang --target=armv7-none-linux-androideabi16 --gcc-toolchain=/home/haidar/Android/Sdk/ndk/21.1.6352462/toolchains/llvm/prebuilt/linux-x86_64 --sysroot=/home/haidar/Android/Sdk/ndk/21.1.6352462/toolchains/llvm/prebuilt/linux-x86_64/sysroot -fPIC -g -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 -march=armv7-a -mthumb -Wformat -Werror=format-security  -O0 -fno-limit-debug-info  -Wl,--exclude-libs,libgcc.a -Wl,--exclude-libs,libgcc_real.a -Wl,--exclude-libs,libatomic.a -static-libstdc++ -Wl,--build-id -Wl,--fatal-warnings -Wl,--exclude-libs,libunwind.a -Wl,--no-undefined -Qunused-arguments -shared -Wl,-soname,libimage_magick_ffi.so -o /home/haidar/dev_haidar/flutter/magick_app/build/image_magick_ffi/intermediates/cxx/Debug/b5x10294/obj/armeabi-v7a/libimage_magick_ffi.so CMakeFiles/image_magick_ffi.dir/image_magick_ffi.c.o  /home/haidar/dev_haidar/c++/meaning_of_life/Src/libLife.so  -latomic -lm && :
[        ] /home/haidar/Android/Sdk/ndk/21.1.6352462/toolchains/llvm/prebuilt/linux-x86_64/lib/gcc/arm-linux-androideabi/4.9.x/../../../../arm-linux-androideabi/bin/ld: error: /home/haidar/dev_haidar/c++/meaning_of_life/Src/libLife.so: incompatible target
[        ] clang: error: linker command failed with exit code 1 (use -v to see invocation)
[        ] ninja: build stopped: subcommand failed.
[        ]  at org.gradle.internal.UncheckedException.throwAsUncheckedException(UncheckedException.java:68)
[        ]  at org.gradle.internal.UncheckedException.throwAsUncheckedException(UncheckedException.java:41)
[        ]  at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:107)
[        ]  at org.gradle.api.internal.project.taskfactory.StandardTaskAction.doExecute(StandardTaskAction.java:58)
[        ]  at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:51)
[        ]  at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:29)
[        ]  at org.gradle.api.internal.tasks.execution.TaskExecution$3.run(TaskExecution.java:242)
[        ]  at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
[        ]  at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
[        ]  at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
[        ]  at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
[        ]  at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
[        ]  at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
[        ]  at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:47)
[        ]  at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:68)
[        ]  at org.gradle.api.internal.tasks.execution.TaskExecution.executeAction(TaskExecution.java:227)
[        ]  at org.gradle.api.internal.tasks.execution.TaskExecution.executeActions(TaskExecution.java:210)
[        ]  at org.gradle.api.internal.tasks.execution.TaskExecution.executeWithPreviousOutputFiles(TaskExecution.java:193)
[        ]  at org.gradle.api.internal.tasks.execution.TaskExecution.execute(TaskExecution.java:171)
[        ]  at org.gradle.internal.execution.steps.ExecuteStep.executeInternal(ExecuteStep.java:89)
[        ]  at org.gradle.internal.execution.steps.ExecuteStep.access$000(ExecuteStep.java:40)
[        ]  at org.gradle.internal.execution.steps.ExecuteStep$1.call(ExecuteStep.java:53)
[        ]  at org.gradle.internal.execution.steps.ExecuteStep$1.call(ExecuteStep.java:50)
[        ]  at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
[        ]  at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:199)
[        ]  at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
[        ]  at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
[        ]  at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
[        ]  at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
[        ]  at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
[        ]  at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:73)
[        ]  at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:50)
[        ]  at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:40)
[        ]  at org.gradle.internal.execution.steps.RemovePreviousOutputsStep.execute(RemovePreviousOutputsStep.java:68)
[        ]  at org.gradle.internal.execution.steps.RemovePreviousOutputsStep.execute(RemovePreviousOutputsStep.java:38)
[        ]  at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:48)
[        ]  at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:36)
[        ]  at org.gradle.internal.execution.steps.CancelExecutionStep.execute(CancelExecutionStep.java:41)
[        ]  at org.gradle.internal.execution.steps.TimeoutStep.executeWithoutTimeout(TimeoutStep.java:74)
[        ]  at org.gradle.internal.execution.steps.TimeoutStep.execute(TimeoutStep.java:55)
[        ]  at org.gradle.internal.execution.steps.CreateOutputsStep.execute(CreateOutputsStep.java:51)
[        ]  at org.gradle.internal.execution.steps.CreateOutputsStep.execute(CreateOutputsStep.java:29)
[        ]  at org.gradle.internal.execution.steps.CaptureStateAfterExecutionStep.execute(CaptureStateAfterExecutionStep.java:61)
[        ]  at org.gradle.internal.execution.steps.CaptureStateAfterExecutionStep.execute(CaptureStateAfterExecutionStep.java:42)
[        ]  at org.gradle.internal.execution.steps.BroadcastChangingOutputsStep.execute(BroadcastChangingOutputsStep.java:60)
[        ]  at org.gradle.internal.execution.steps.BroadcastChangingOutputsStep.execute(BroadcastChangingOutputsStep.java:27)
[        ]  at org.gradle.internal.execution.steps.BuildCacheStep.executeWithoutCache(BuildCacheStep.java:180)
[        ]  at org.gradle.internal.execution.steps.BuildCacheStep.lambda$execute$1(BuildCacheStep.java:75)
[        ]  at org.gradle.internal.Either$Right.fold(Either.java:175)
[        ]  at org.gradle.internal.execution.caching.CachingState.fold(CachingState.java:59)
[        ]  at org.gradle.internal.execution.steps.BuildCacheStep.execute(BuildCacheStep.java:73)
[        ]  at org.gradle.internal.execution.steps.BuildCacheStep.execute(BuildCacheStep.java:48)
[        ]  at org.gradle.internal.execution.steps.StoreExecutionStateStep.execute(StoreExecutionStateStep.java:36)
[        ]  at org.gradle.internal.execution.steps.StoreExecutionStateStep.execute(StoreExecutionStateStep.java:25)
[        ]  at org.gradle.internal.execution.steps.RecordOutputsStep.execute(RecordOutputsStep.java:36)
[        ]  at org.gradle.internal.execution.steps.RecordOutputsStep.execute(RecordOutputsStep.java:22)
[        ]  at org.gradle.internal.execution.steps.SkipUpToDateStep.executeBecause(SkipUpToDateStep.java:110)
[        ]  at org.gradle.internal.execution.steps.SkipUpToDateStep.lambda$execute$2(SkipUpToDateStep.java:56)
[        ]  at java.base/java.util.Optional.orElseGet(Optional.java:369)
[        ]  at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:56)
[        ]  at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:38)
[        ]  at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:73)
[        ]  at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:44)
[        ]  at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:37)
[        ]  at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:27)
[        ]  at org.gradle.internal.execution.steps.ResolveCachingStateStep.execute(ResolveCachingStateStep.java:89)
[        ]  at org.gradle.internal.execution.steps.ResolveCachingStateStep.execute(ResolveCachingStateStep.java:50)
[        ]  at org.gradle.internal.execution.steps.ValidateStep.execute(ValidateStep.java:114)
[        ]  at org.gradle.internal.execution.steps.ValidateStep.execute(ValidateStep.java:57)
[        ]  at org.gradle.internal.execution.steps.CaptureStateBeforeExecutionStep.execute(CaptureStateBeforeExecutionStep.java:76)
[        ]  at org.gradle.internal.execution.steps.CaptureStateBeforeExecutionStep.execute(CaptureStateBeforeExecutionStep.java:50)
[        ]  at org.gradle.internal.execution.steps.SkipEmptyWorkStep.executeWithNoEmptySources(SkipEmptyWorkStep.java:249)
[        ]  at org.gradle.internal.execution.steps.SkipEmptyWorkStep.execute(SkipEmptyWorkStep.java:86)
[        ]  at org.gradle.internal.execution.steps.SkipEmptyWorkStep.execute(SkipEmptyWorkStep.java:54)
[        ]  at org.gradle.internal.execution.steps.RemoveUntrackedExecutionStateStep.execute(RemoveUntrackedExecutionStateStep.java:32)
[        ]  at org.gradle.internal.execution.steps.RemoveUntrackedExecutionStateStep.execute(RemoveUntrackedExecutionStateStep.java:21)
[        ]  at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsStartedStep.execute(MarkSnapshottingInputsStartedStep.java:38)
[        ]  at org.gradle.internal.execution.steps.LoadPreviousExecutionStateStep.execute(LoadPreviousExecutionStateStep.java:43)
[        ]  at org.gradle.internal.execution.steps.LoadPreviousExecutionStateStep.execute(LoadPreviousExecutionStateStep.java:31)
[        ]  at org.gradle.internal.execution.steps.AssignWorkspaceStep.lambda$execute$0(AssignWorkspaceStep.java:40)
[        ]  at org.gradle.api.internal.tasks.execution.TaskExecution$4.withWorkspace(TaskExecution.java:287)
[        ]  at org.gradle.internal.execution.steps.AssignWorkspaceStep.execute(AssignWorkspaceStep.java:40)
[        ]  at org.gradle.internal.execution.steps.AssignWorkspaceStep.execute(AssignWorkspaceStep.java:30)
[        ]  at org.gradle.internal.execution.steps.IdentityCacheStep.execute(IdentityCacheStep.java:37)
[        ]  at org.gradle.internal.execution.steps.IdentityCacheStep.execute(IdentityCacheStep.java:27)
[        ]  at org.gradle.internal.execution.steps.IdentifyStep.execute(IdentifyStep.java:44)
[        ]  at org.gradle.internal.execution.steps.IdentifyStep.execute(IdentifyStep.java:33)
[        ]  at org.gradle.internal.execution.impl.DefaultExecutionEngine$1.execute(DefaultExecutionEngine.java:76)
[        ]  at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:144)
[        ]  at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:133)
[        ]  at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:77)
[        ]  at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
[        ]  at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:51)
[        ]  at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
[        ]  at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:56)
[        ]  at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
[        ]  at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
[        ]  at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
[        ]  at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
[        ]  at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
[        ]  at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:199)
[        ]  at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
[        ]  at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
[        ]  at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
[        ]  at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
[        ]  at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
[        ]  at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:73)
[        ]  at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
[        ]  at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:74)
[        ]  at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:333)
[        ]  at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:320)
[        ]  at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:313)
[        ]  at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:299)
[        ]  at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.lambda$run$0(DefaultPlanExecutor.java:143)
[        ]  at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:227)
[        ]  at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.executeNextNode(DefaultPlanExecutor.java:218)
[        ]  at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:140)
[        ]  at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
[        ]  at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
[        ]  at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
[        ]  at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
[        ]  at java.base/java.lang.Thread.run(Thread.java:829)
[        ] Caused by: Build command failed.
...
  1. Create a .so shared library from the plugin, This time I tried the method on the official android docs page, I used this command which generated a json file with another command to compile the C file above, but I don't understand how to create the shared library in this case (the template of the command is from the same page):
cmake -H/home/haidar/dev_haidar/c++/meaning_of_life -DCMAKE_FIND_ROOT_PATH=/home/haidar/dev_haidar/c++/meaning_of_life -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=/home/haidar/Android/Sdk/ndk/25.1.8937393/build/cmake/android.toolchain.cmake -DANDROID_ABI=armeabi-v7a -DANDROID_NDK=/home/haidar/Android/Sdk/ndk/25.1.8937393 -DANDROID_PLATFORM=android-23 -DCMAKE_ANDROID_ARCH_ABI=arm64-v8a -DCMAKE_ANDROID_NDK=/home/haidar/Android/Sdk/ndk/25.1.8937393 -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=/home/haidar/dev_haidar/c++/meaning_of_life -DCMAKE_RUNTIME_OUTPUT_DIRECTORY=/home/haidar/dev_haidar/c++/meaning_of_life -DCMAKE_MAKE_PROGRAM=/home/haidar/Android/Sdk/cmake/3.22.1/bin/ninja -DCMAKE_SYSTEM_NAME=Android -DCMAKE_SYSTEM_VERSION=26 -B/home/haidar/dev_haidar/c++/meaning_of_life -GNinja

Note: The device I tried to run the flutter app (that uses the plugin, that uses the .so library) on is a Huawei p10 lite, which has a CPU that supports arm63-v8a, armeabi-v7a, armeabi.

Listing the steps to solve this or tips would be much appreciated. Thanks.

Update

I found this shell script on github (which is very similar to what I did in step 6 above), I tweaked it to fit my paths and it successfully generated the .so files for each architecture. I created a pure android app and called it from the app and everything worked fine.

add_library(getnumber SHARED IMPORTED)
set_target_properties(getnumber PROPERTIES IMPORTED_LOCATION
        /home/haidar/dev_haidar/c++/getnumber/Src/prebuilt/release/arm64-v8a/libgetnumber.so
        )
target_link_libraries(imagemagicknativeapp getnumber)

and everything worked fine.

When I depended on it in the flutter plugin in the exact same way

add_library(getnumber SHARED IMPORTED )
set_target_properties(getnumber
        PROPERTIES
        IMPORTED_LOCATION
        /home/haidar/dev_haidar/flutter/image_magick_ffi/src/Dependencies/ImageMagick/lib/android/arm64-v8a/libgetnumber.so
        )

target_link_libraries(image_magick_ffi getnumber)

I still get an error

ld: error: /home/haidar/dev_haidar/flutter/image_magick_ffi/src/Dependencies/ImageMagick/lib/android/arm64-v8a/libgetnumber.so is incompatible with armelf_linux_eabi

I know the problem is related to the compilation/linking target being different than the actual target (my phone), but how can I tell flutter to change this behavior?

HII
  • 3,420
  • 1
  • 14
  • 35
  • From this [and your prior/deleted(?) question], the errors you're getting indicate that if the lib is built, it is built for the wrong architecture. Either it's _not_ `arm` and/or it's build for [loosely] `arm/linux` or `arm/standalone` instead of `arm/android`. You have to find the full path to the lib that the make process is using. You could run it under `strace` if necessary or turn on some verbosity in the make itself. Then, if you do `file /path/to/my.so`, it will tell you the arch it is built for. You'll have to ensure this is the same as what `FFI` expects. – Craig Estey Oct 14 '22 at 00:35
  • Note that the build requests that you rerun with `--debug` and/or `--scan`. So, have you done that? – Craig Estey Oct 14 '22 at 00:40
  • @CraigEstey I know that the problem is the wrong architecture the library is being built to target, but the question is what am I doing wrong?? Also the logs aboveare from building with -v flag in android studio when I run the flutter app, where else can I put these flags you are talking about? – HII Oct 14 '22 at 00:47
  • @CraigEstey if you look at the updated question, you can see that I am generating the `.so` files correctly and for the correct architecture, the problem seems either in the flutter app that uses the flutter plugin or in the flutter plugin itself – HII Oct 14 '22 at 16:32
  • As stated in comments on your previous questions, simply add the *source* of the 3rd party open-source code and let CMake build it. – Richard Heap Oct 14 '22 at 19:00
  • @RichardHeap but I really would like to know what's wrong with adding ready libraries through cmake, not all of the time you will have access to the source code. – HII Oct 14 '22 at 20:28

1 Answers1

4

Thanks to Piero5W11 from FlutterDev on discord, I was able to solve this particular problem.

The solution is to remove the part related to adding the library from CMake (don't add the library from CMake, we will add it from somewhere else), so remove this part:

add_library(getnumber SHARED IMPORTED)
set_target_properties(getnumber PROPERTIES IMPORTED_LOCATION
        /home/haidar/dev_haidar/c++/getnumber/Src/prebuilt/release/arm64-v8a/libgetnumber.so
        )
target_link_libraries(imagemagicknativeapp getnumber)

from CMakeLists.txt, and instead, copy the shared library to {PLUGIN_PROJECT_ROOT}/android/src/main/jniLibs/{ABI_VERSION}/libgetnumber.so (replace the wildcards)

Just do this and do zero other configuration in the CMakeLists or gradle files.

As for the dart side, this library will be bundled with the app as any other library so you can load it by its name getnumber using the dart dynamic loader, and then use it to lookup symbols in it.

This is how my plugin's project structure looks like now:

enter image description here

and you can how see the generated apk contains the library just bcz it was put in jniLibs:

enter image description here

Note: I generated libgetnumber using the script from the link in the question, it is perfect for creating ABIs for all targets, just tweak it to fit your paths.

Update: Even better, You can use CMake:

To add the shared libraries through CMakeLists.txt of the flutter ffi plugin:

  • first remove the shared libraries from the jniLibs folder (source)
  • Put the libraries anywhere you want inside your plugin project, for me, I put them in the same directory as CMakeLists.txt, in ${CMAKE_CURRENT_SOURCE_DIR}/Dependencies/ImageMagick/lib/android/${ANDROID_ABI}/libc++_shared.so (put your own paths instead)
  • add this to android/build.gradle in the plugin:
        ndk {
            abiFilters "arm64-v8a" // put only the architectures that your lib is targeting
        }

note: if you have .so versions for all ABIs, then you will not need this step.

  • Now you can add your .so files as libraries in CMake and link them to your flutter plugin library:

CMakeLists.txt:

project(my_ffi_library VERSION 0.0.1 LANGUAGES C)

add_library(my_ffi_library SHARED
        "myclass.c"
        )
...
# Add your library
add_library(my-lib SHARED IMPORTED)
set_target_properties(
        my-lib
        PROPERTIES IMPORTED_LOCATION
        path/to/your/lib.so
)
# Link your library to the flutter plugin library
target_link_libraries(my_ffi_library my-lib)
  • And done, now you can use the implementations from the prebuilt shared library.
HII
  • 3,420
  • 1
  • 14
  • 35
  • 2
    Congrats on solving your issue. +1 for posting the solution as I'm sure it will help others in the future. – Craig Estey Oct 14 '22 at 21:55
  • Thanks for the tips. This saved me a lot of time. I found the first solution to just copy the lib into jniLibs is much simpler than the second. – blaize Jul 27 '23 at 07:44