DllImport with .a file for iOS in MAUI - ios

I'm trying to add iOS support in my library. In the library I use functions from native binaries via DllImport attributes. So for example:
[DllImport("libraryname")]
private static extern int Foo();
It works without any problems on Windows (via libraryname.dll) and macOS (via libraryname.dylib). From what I've learned from the discussion with the library user, we need .a file for iOS. The user has prepared a simple test MAUI solution: Testify.zip. In the MainPage.xaml.cs we have this declaration:
[DllImport("fat.a", ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
private static extern int Foo();
fat.a has been built from this simple C code:
int Foo() { return 234; }
Here you can get the file: fat.zip. The build process:
xcrun --sdk iphoneos --verbose clang -c code.c -o code_arm64.o -arch arm64
ar -rv code_arm64.a code_arm64.o
xcrun --sdk iphoneos --verbose clang -c code.c -o code_x86_64.o -arch x86_64
ar -rv code_x86_64.a code_x86_64.o
lipo code_x86_64.a code_arm64.a -output fat.a -create
lipo -info fat.a
Also the file is put to the Resources/Raw folder with Copy to Output Directory set to Copy always. We see the file in proper place in the built package on a target device or iOS simulator. But calling Foo we get the exception in runtime: System.DllNotFoundException: fat.a. Please see this comment for more details.
So the question is how to use DllImport with native binaries for iOS in a MAUI project? Is it possible at all?

Well, in this comment I got an answer:
"__Internal" must be used in DllImport attributes.
Following element must be added to the csproj file:
<ItemGroup>
<NativeReference Include="path/to/fat.a" Kind="Static" />
</ItemGroup>

Related

Build C Library from makefile for ios and not macos

I have a makefile that builds some C files and if I run it on an M1 mac the resulting library has the architecture arm64 which I thought it what is necessary for them to compile with an Xcode project for iOS. I discovered I can run the command otool -l libf2c.a | grep platform which should tell me what it was compiled for and in my case it returns platform1 which indicates macOS. Based on this, I think I need a value of platform2 for iOS.
The reason this is an issue is because in Xcode I get the error ld: building for iOS, but linking in object file built for macOS, file '/Users/e.../close.o' for architecture arm64.
Based on what I have been researching it seems iOS and macOS have the same architecture (arm64) but are a different 'platform'? But, I am not sure how the platform is determined. Is there some setting in my makefile I need to specify the platform? I am assuming that if I am able to get the platform to be iOS then Xcode will cooperate and be able to build the library I have generated.
The preferred way to compile for iOS via command line would probably be to use the xcrun command. This will allow you to specify the correct SDK for the platform you actually want to run on. For example:
prompt$ xcrun --sdk iphoneos --toolchain iphoneos clang -c test.c -o test.o -arch arm64
prompt$ otool -v -l test.o | grep platform
platform IOS
TL;DR: change your compiler invocation from plain clang to xcrun --sdk iphoneos --toolchain iphoneos clang.

How to compile resource files into a custom framework or static library or Mach-O file?

How to compile resource files into a custom framework or static library (meaning into a Mach-O file)? Thanks for any tips.
Assuming by "resource files" you mean arbitrary binary files, you can include them into any binary by creating an assembly file (e.g. resources.S) like so:
.section __RESOURCE,__file1
_symbol_file1:
.incbin "file1.zip"
.no_dead_strip _symbol_file1
.section __WHATEVER,__file2
_symbol_file2:
.incbin "file2.bin"
.no_dead_strip _symbol_file2
// etc, you get the pattern
If you don't have a project in which you can include this file already, then you can turn it into an iOS arm64 framework by itself like so:
mkdir MyResources.framework
xcrun -sdk iphoneos clang -arch arm64 -shared -o MyResources.framework/MyResources resources.S

Undefined symbols for architecture x86_64 using Swagger API:

I am having this error in my Xcode project....So basically what I am doing is creating a static library that uses a Swagger Api that I have installed using CocoaPods. I am able to import the header files and use the header files in the library, but when I put the static library in another Xcode project I receive this error where It says _OBJC_CLASS_$_SWG....", referenced from: objc-class-ref in Sendable.o.... which is basically the class where I am using the Swagger header files.....
Things I have attempted:
1: Delete Derived Data folder, cleaned and rebuilt
2: made sure POD, library, and project using library have the same architectures as well as
3: made sure architecture build active is set to NO for all
4: made sure $(inherited) is in both library and project using library
5: installed pod file again in library
Things I noticed:
1: the library itself does not have POD in the header search path
2: I get the following warnings but ignored them
3: using Pod update gives me error saying GitHub is offline make sure you have internet connection, only removing and installing seem to work successfully.
My guess is that you are building the library for a single architecture - you are building it either for a Generic iOS Device, or some speciffic iOS device. Either way, you will not be able to use the library on a simulator (x86_64 architecture). Vice versa, if you build the library for a simulator, you will not be able to use it on a device.
If you want to use the same build on both devices and simulator, you need to create an universal library using a tool called lipo, either manually or by Build Phase script. Check out this article. Short summary:
Add an Aggregate target to your library project
Add a Run Script Build Phase
Paste this script into it:
# define output folder environment variable
UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal
# Step 1. Build Device and Simulator versions
xcodebuild -target ${PROJECT_NAME} ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}"
xcodebuild -target ${PROJECT_NAME} -configuration ${CONFIGURATION} -sdk iphonesimulator -arch x86_64 -arch i386 -arch armv7 -arch armv7s -arch arm64 BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}"
# make sure the output directory exists
mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"
# Step 2. Create universal binary file using lipo
lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/lib${PROJECT_NAME}.a" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/lib${PROJECT_NAME}.a" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/lib${PROJECT_NAME}.a"
echo "Universal library can be found here:"
echo ${UNIVERSAL_OUTPUTFOLDER}/lib${PROJECT_NAME}.a
# Last touch. copy the header files. Just for convenience
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/include" "${UNIVERSAL_OUTPUTFOLDER}/"
Build your Aggregate target and an universal library will be built

Need Clear concept of Framework

Attached code : http://speedy.sh/xTEp8/XYZFrameworkDemo.zip
I have create a framework name ABCFramework.framework having my Constant.h file where i put my MACRO used in my code.
For Ex:
#define WEBURL #"www.google.com/api"
Not i have following question.
1)
Please explain how do i distribute my ABCFramework.framework so it can be used in both simmulater and Iphone device,as till now i have to use both seperately with example code in which i want to use it.
2)
Please if a developer import my ABCFramework.framework in his code and he want to change the Constant.h WEBURL with his own WEBURL how to do it?
#define WEBURL #"www.google.com/api" //SDK URL
#define WEBURL #"www.yahoo.com/api" //USER URL TO BE ADD?
On adding aggregate target and run script
xcodebuild -target ABCFramework -sdk Debug-iphonesimulator
xcodebuild -target ABCFramework -sdk Debug-iphoneos
rm -rf "$SRCROOT/products" mkdir -p "$SRCROOT/products/ABCFramework"
lipo -create "$SOURCE_ROOT/build/Release-iphonesimulator/ABCFramework" "$SOURCE_ROOT/build/Release-iphoneos/ABCFramework" -o "$SOURCE_ROOT/products/ABCFramework/ABCFramework"
cp -r "$SOURCE_ROOT/build/Release-iphoneos/include/ABCFramework" "$SOURCE_ROOT/products/ABCFramework/include"
I am getting this error /
xcodebuild: error: SDK "Debug-iphonesimulator" cannot be located.
xcodebuild: error: SDK "Debug-iphoneos" cannot be located.
fatal error: lipo: can't open input file: /Users/xxxxxxxx/build/Release-iphonesimulator/ABCFramework (No such file or directory)
cp: /Users/xxxxxxxx/build//build/Release-iphoneos/include/ABCFramework: No such file or directory
NOTE: My project in which i create ABCFramework is XYZFrameworkDemo
1) To create a Universal framework that works on Simulator and device you have to build them separately and then use the lipo command to merge them. You do this by creating a Aggregate target and building the code once in simulator and once for device. You add a Run script phase to the aggregate target to build both simulator and device version which will go into their respective folders and then merge them using lipo command.
xcodebuild -target ABCFramework -sdk iphonesimulator
xcodebuild -target ABCFramework -sdk iphoneos
rm -rf "$SRCROOT/products"
mkdir -p "$SRCROOT/products/ABCFramework"
lipo -create "$SOURCE_ROOT/build/Release-iphonesimulator/ABCFramework" "$SOURCE_ROOT/build/Release-iphoneos/ABCFramework" -o "$SOURCE_ROOT/products/ABCFramework/ABCFramework"
cp -r "$SOURCE_ROOT/build/Release-iphoneos/include/ABCFramework" "$SOURCE_ROOT/products/ABCFramework/include"
2) You cannot change the macros in the framework after its built. You have to provide a api or a exported variable to set this property from the app that's integrating your framework.

How to build Boost-Libraries for iPhone

Can someone tell me, where to find a detailed guide, how to build the Boost-Libraries for using it on the iPhone-Device.
I've allready build the libs for Mac and can use them in my project (only on iPhone-Simulator). While building the project for iPhone-Device, XCode haunts me a warning: "file is not of required architecture" ond some other errors.
Please Help
Start a new project in Xcode using the iPhone Static Library project template.
Then import the source and headers, and compile it that way. The result should be an iPhone compatible static library
I started here:
http://lists.boost.org/boost-build/2009/02/21326.php
With most of Boost you probably don't need to actually compile it, just include the useful headers. In my case, I just did the compiler define in my own Xcode project.
Hey I have updated Pete Goodliffes script in my openFrameworks addon:
It currently has arm64, armv7, i386, x86_64
Boost 1.59.0 or previous
libc++ / std=c++11 -- Now optional release for libstdc++
Precompiled and Script to build yourself (so if you need libstdc++ quite easy to change)
Supports Xcode 7
[https://github.com/danoli3/ofxiOSBoost][1]
For boost libraries which have only headers files (.hpp) you can just set header search path from your project to them.
For boost libraries with sources you can build static libraries for both ios phone/simulator with next simple steps:
Download and unpack a boost release archive (from https://www.boost.org/users/download/) e.g.: https://boostorg.jfrog.io/artifactory/main/release/1.77.0/source/boost_1_77_0.tar.bz2
Run bootstrap.sh with needed libraries to build for instance 'context' (format =library1,library2,...):
./bootstrap.sh --with-libraries=context
Add toolsets with correct paths to installed SDKs to project-config.jam:
# IOS ARM64
using clang : iphoneos
: xcrun clang -arch arm64 -stdlib=libc++ -std=c++11 -miphoneos-version-min=12.0 -fvisibility-inlines-hidden -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk
;
# IOS x86_64
using clang : iphonesimulator
: xcrun clang -arch x86_64 -stdlib=libc++ -std=c++11 -miphoneos-version-min=12.0 -fvisibility-inlines-hidden -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk
;
Create and run build.sh script (where lib name is libboost_<name>.a):
lib=libboost_context.a
dir='stage/lib'
# Build arm64
./b2 -a -j4 toolset=clang-iphoneos binary-format=mach-o abi=aapcs link=static stage
mv $dir/$lib $dir/arm64_$lib
# Build x86_64
./b2 -a -j4 toolset=clang-iphonesimulator binary-format=mach-o abi=sysv link=static stage
mv $dir/$lib $dir/x86_64_$lib
# Make fat
lipo -create $dir/arm64_$lib $dir/x86_64_$lib -output $dir/$lib
Now you have next compiled static libraries in "/stage/lib" dir for boost context: arm64_libboost_context.a, x86_64_libboost_context.a and fat one libboost_context.a.
We use boost too. To simplify its inclusion into new applications I have created a Xcode project you can drop into your workspace to include boost. It is based on a Makefile so you need the Xcode commandline tools installed.
The project is here https://github.com/Cogosense/iOSBoostFramework.
Clone the project into your workspace, then click on Menu File->"Add Files to workspace". Select iOSBoostFramework/iOSBoostFramework.xcodeproj in the file finder and click add.
The Makefile in the iOSBoostFramework directory controls what is built and how it is built. There is support for Xcode workspace dependencies, bitcode generation, and only the target architectures selected by Xcode are built.
The following libraries are built test, thread, atomic, signals, filesystem, regex, program_options, system date_time, serialization, exception, locale, and random.
All the separate libraries and architectures are combined, the final build output is a FAT boost.framework Framework bundle which can be linked into the application.
The version of boost is specified in the Makefile (currently 1.64.0), it is downloaded, built for all active architectures and installed in the BUILT_PRODUCTS_DIR specified by xcode.
The previous answer helped me when I wanted to build boost for the arm simulator. When you have a Mac with M1 processor and want to use the simulator, you cannot use the arm64 build for the iPhone.
I added this to the project-config.jam:
# IOS Arm Simulator
using clang : iphonesimulatorarm64
: xcrun clang -arch arm64 -stdlib=libc++ -std=c++11 -miphoneos-version-min=10.0 -fvisibility-inlines-hidden -target arm64-apple-ios10.0-simulator -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk ;
Then pass toolset=clang-iphonesimulatorarm64 to the b2 command.

Resources