Skip to content

Add support for IOS #4

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .github/workflows/build_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -241,3 +241,43 @@ jobs:
prerelease: false
files: |
build/publish/libopencv_dart-macos-arm64.tar.gz
build-ios:
name: build-ios
runs-on: macos-14
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: setup
run: |
brew install --force --overwrite ninja ccache nasm
python3 -m pip install conan
conan profile detect -f
cd ${{github.workspace}}
- name: build
run: |
echo "tools.cmake.cmaketoolchain:user_toolchain=[\"`pwd`/profiles/ios.toolchain.cmake\"]" >> profiles/ios-armv8
conan build . -b missing -pr:h profiles/ios-armv8

echo "tools.cmake.cmaketoolchain:user_toolchain=[\"`pwd`/profiles/ios.toolchain.cmake\"]" >> profiles/ios-x86_64
conan build . -b missing -pr:h profiles/ios-x86_64

- uses: actions/upload-artifact@v4
name: upload
with:
path: build/publish/libopencv_dart-ios-arm64.tar.gz
name: libopencv_dart-ios-arm64.tar.gz
- uses: actions/upload-artifact@v4
name: upload x64
with:
path: build/publish/libopencv_dart-ios-x64.tar.gz
name: libopencv_dart-ios-x64.tar.gz
- name: Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
draft: true
prerelease: false
files: |
build/publish/libopencv_dart-ios-arm64.tar.gz
build/publish/libopencv_dart-ios-x64.tar.gz
3 changes: 3 additions & 0 deletions .metadata
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ migration:
- platform: android
create_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
base_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
- platform: ios
create_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
base_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
- platform: linux
create_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
base_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
Expand Down
12 changes: 5 additions & 7 deletions .pubignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,12 @@ coverage/
*.so
*.dylib
*.lib
src/opencv/.github/
src/opencv/
src/opencv_contrib/
src/CMakeLists.txt
src/constants.h
src/opencv_dart.cpp
src/opencv_dart.h
pkgs/
src/*.cpp
src/*.h
images/
CMakeUserPresets*
ffigen*

.idea/
.vscode/
Expand Down
19 changes: 18 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ if(ANDROID)
list(APPEND OpenCV_LIBS camera2ndk mediandk -landroid)
endif(ANDROID)

if(IOS)
list(APPEND OpenCV_LIBS
"-framework CoreFoundation"
"-framework CoreImage"
"-framework Foundation"
)
endif(IOS)

message(STATUS "OpenCV library status:")
message(STATUS " config: ${OpenCV_DIR}")
message(STATUS " version: ${OpenCV_VERSION}")
Expand All @@ -42,15 +50,24 @@ target_include_directories(${library_name} SYSTEM PUBLIC
${OpenCV_INCLUDE_DIRS}
)
target_link_libraries(${library_name} PRIVATE ${OpenCV_LIBS})
set_target_properties(${library_name} PROPERTIES

set_target_properties(${library_name} PROPERTIES
# PUBLIC_HEADER ${GOCV_HEADERS}
OUTPUT_NAME ${library_name}
CXX_VISIBILITY_PRESET default
C_VISIBILITY_PRESET default
LINK_FLAGS_RELEASE -s
)

if(IOS)
set_target_properties(${library_name} PROPERTIES
FRAMEWORK TRUE
FRAMEWORK_VERSION CXX
MACOSX_FRAMEWORK_IDENTIFIER dev.rainyl.opencv_dart
)
endif(IOS)


if(WIN32)
set_target_properties(${library_name} PROPERTIES
WINDOWS_EXPORT_ALL_SYMBOLS ON
Expand Down
29 changes: 17 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,34 @@ OpenCV Bindings for Dart Language.
| Platform | Supported | Tested | Prebuilt Binaries |
| -------- | ------------------ | ----------------------- | ------------------------------ |
| Android | :white_check_mark: | :ballot_box_with_check: | x86_64, arm64-v8a, armeabi-v7a |
| iOS | :x: | :x: | :x: |
| iOS | :white_check_mark: | :ballot_box_with_check: | arm64, x64(Simulator) |
| Linux | :white_check_mark: | :white_check_mark: | x64 |
| Windows | :white_check_mark: | :white_check_mark: | x64 |
| macOS | :white_check_mark: | :white_check_mark: | x64, arm64 |

- I have no Apple devices, so iOS and ~~macOS are not supported yet~~ macOS compiled by Github Workflow available now, try it!
- Theorically the dart codes will work for iOS, you can compile binaries by yourself, contributions are welcome!

## IMPORTANT

After added to `pubspec.yaml` or install by commandline,
please run `dart run opencv_dart:setup <platform> --arch <arch>` to download
prebuilt binaries.
Please run

```bash
dart run opencv_dart:setup <platform> --arch <arch>
```

to download prebuilt binaries.

- `platform`: `auto` `android` `linux` `windows` `macos`
- `arch`: `auto` `x86` `x64` `arm64`(macOS only) `x86_64`(android only) `arm64-v8a`(android only) `armeabi-v7a`(android only)
- **platform**: `auto` `android` `linux` `windows` `macos`
- for **Windows**, arch: `x64`
- for **Linux**, arch: `x64`
- for **macOS**, arch: `x64` `arm64`
- for **IOS**, arch: `x64` `arm64`
- for **Android**, arch: `x86_64` `arm64-v8a` `armeabi-v7a`
- run `dart run opencv_dart:setup -h` to see more options

**Please use v0.3.0 and later version.**

## Example

![example](images/example.png)
![example](https://raw.githubusercontent.com/rainyl/opencv_dart/main/images/example.png)

## Status

Expand Down Expand Up @@ -81,13 +86,13 @@ More examples are on the way...
### TODO

- [x] ~~compile libs for android, linux~~
- [ ] support for iOS, ~~macOS~~
- [ ] ~~support for iOS, macOS~~
- [ ] add more examples
- [ ] documentation
- [ ] modify C wrapper to catch exceptions
- [ ] Native Assets
- [ ] async?
- [x] ~~directly include opencv source code,~~ refactor cmakelists.txt
- [x] ~~directly include opencv source code, refactor cmakelists.txt~~

## For Developers

Expand Down
2 changes: 1 addition & 1 deletion bin/setup.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ void main(List<String> args) async {
..addCommand(LinuxSetupCommand())
..addCommand(MacOsSetupCommand())
..addCommand(AndroidSetupCommand())
// ..addCommand(IosSetupCommand())
..addCommand(IosSetupCommand())
..run(args);
}
38 changes: 27 additions & 11 deletions bin/setup_commands.dart
Original file line number Diff line number Diff line change
Expand Up @@ -59,17 +59,20 @@ abstract class BaseSetupCommand extends Command {
final opencvRoot = packageConfigFile.uri.resolve(pkg['rootUri'] ?? '');
print('Using package:$setupPkgName from ${opencvRoot.toFilePath()}');

final doc = loadYaml(File("${opencvRoot.toFilePath()}/pubspec.yaml").readAsStringSync());
final doc = loadYaml(
File("${opencvRoot.toFilePath()}/pubspec.yaml").readAsStringSync());
final _version = doc["version"] as String;
final libTarName = "libopencv_dart-$os-$arch.tar.gz";
final version = _version.replaceAll(RegExp(r"\-dev.*"), "");

print('Downloading prebuilt binary...');
String url = "https://github.com/rainyl/opencv_dart/releases/download/v$version/$libTarName";
String url =
"https://github.com/rainyl/opencv_dart/releases/download/v$version/$libTarName";

final cacheTarPath = p.join(opencvRoot.toFilePath(), ".cache", libTarName);
final saveFile = File(cacheTarPath);
if (!saveFile.parent.existsSync()) saveFile.parent.createSync(recursive: true);
if (!saveFile.parent.existsSync())
saveFile.parent.createSync(recursive: true);

print("Downloading $url");
final request = await HttpClient().getUrl(Uri.parse(url));
Expand All @@ -92,7 +95,8 @@ abstract class BaseSetupCommand extends Command {
extractPath = p.join(opencvRoot.toFilePath(), "linux");
break;
case OS.android:
extractPath = p.join(opencvRoot.toFilePath(), "android", "src", "main", "jniLibs", arch);
extractPath = p.join(
opencvRoot.toFilePath(), "android", "src", "main", "jniLibs", arch);
case OS.macos:
extractPath = p.join(opencvRoot.toFilePath(), "macos");
case OS.fuchsia:
Expand All @@ -101,10 +105,12 @@ abstract class BaseSetupCommand extends Command {
default:
throw UnsupportedError("Platform $os not supported");
}
if (!Directory(extractPath).existsSync()) Directory(extractPath).createSync(recursive: true);
if (!Directory(extractPath).existsSync())
Directory(extractPath).createSync(recursive: true);
final tarBytes = GZipDecoder().decodeBytes(saveFile.readAsBytesSync());
final archive = TarDecoder().decodeBytes(tarBytes);
extractArchiveToDisk(archive, extractPath, bufferSize: 1024 * 1024 * 10); // 10MB
extractArchiveToDisk(archive, extractPath,
bufferSize: 1024 * 1024 * 10); // 10MB
}

@override
Expand All @@ -124,7 +130,12 @@ class MacOsSetupCommand extends BaseSetupCommand {
String get name => "macos";

MacOsSetupCommand() {
argParser.addOption("arch", allowed: ["auto", "x64", "arm64"], defaultsTo: "auto");
argParser.addOption(
"arch",
abbr: "a",
allowed: ["auto", "x64", "arm64"],
defaultsTo: "auto",
);
}
}

Expand All @@ -136,7 +147,7 @@ class WindowsSetupCommand extends BaseSetupCommand {
String get name => "windows";

WindowsSetupCommand() {
argParser.addOption("arch", allowed: ["x64"], defaultsTo: "x64");
argParser.addOption("arch", abbr: "a", allowed: ["x64"], defaultsTo: "x64");
}
}

Expand All @@ -148,7 +159,7 @@ class LinuxSetupCommand extends BaseSetupCommand {
String get name => "linux";

LinuxSetupCommand() {
argParser.addOption("arch", allowed: ["x64"], defaultsTo: "x64");
argParser.addOption("arch", abbr: "a", allowed: ["x64"], defaultsTo: "x64");
}
}

Expand All @@ -162,6 +173,7 @@ class AndroidSetupCommand extends BaseSetupCommand {
AndroidSetupCommand() {
argParser.addOption(
"arch",
abbr: "a",
allowed: ["auto", "x86_64", "arm64-v8a", "armeabi-v7a"],
defaultsTo: "auto",
);
Expand All @@ -176,8 +188,12 @@ class IosSetupCommand extends BaseSetupCommand {
String get name => "ios";

IosSetupCommand() {
// argParser.addOption("arch", allowed: ["auto", "x64", "arm64"], defaultsTo: "auto");
throw UnimplementedError();
argParser.addOption(
"arch",
abbr: "a",
allowed: ["auto", "x64", "arm64"],
defaultsTo: "auto",
);
}
}

Expand Down
41 changes: 30 additions & 11 deletions conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@
"x86_64": "x64",
"armv8": "arm64",
},
"ios": {
"x86_64": "x64",
"armv8": "arm64",
}
}

# (name, enabled)
Expand Down Expand Up @@ -205,6 +209,22 @@ def __init__(self, display_name=""):

def generate(self):
tc: CMakeToolchain = CMakeToolchain(self)
if self.settings.os == "iOS":
platform_map = {
"armv8": "OS64",
"x86_64": "SIMULATOR64"
# TODO: maybe need a conf var to support "SIMULATORARM64" and more
}
platform = platform_map[str(self.settings.arch)]
block = tc.blocks["user_toolchain"]
block.template = (
f"set(PLATFORM {platform})\n"
"set(ENABLE_ARC FALSE)\n"
"set(ENABLE_BITCODE FALSE)\n"
f"{block.template}"
)
# tc.variables["CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM"] = "rainyl"
# tc.variables["CODE_SIGNING_ALLOWED"] = "NO"
tc.variables["BUILD_CUDA_STUBS"] = False
tc.variables["BUILD_DOCS"] = False
tc.variables["BUILD_EXAMPLES"] = False
Expand Down Expand Up @@ -256,7 +276,8 @@ def generate(self):
tc.variables["WITH_ITT"] = False
tc.variables["WITH_LIBREALSENSE"] = False
tc.variables["WITH_MFX"] = False
tc.variables["WITH_OPENCL"] = self.get_bool("with_opencl", False)
# opencl fails on ios
tc.variables["WITH_OPENCL"] = False if self.settings.os == "iOS" else self.get_bool("with_opencl", False)
tc.variables["WITH_OPENCLAMDBLAS"] = False
tc.variables["WITH_OPENCLAMDFFT"] = False
tc.variables["WITH_OPENCL_SVM"] = False
Expand Down Expand Up @@ -340,8 +361,9 @@ def requirements(self):
)

def build_requirements(self):
self.tool_requires("cmake/3.22.6")
self.tool_requires("cmake/3.28.1")
self.tool_requires("nasm/2.16.01")
# self.tool_requires("ccache/4.9.1")
if self.settings.os != "Windows":
self.tool_requires("ninja/1.11.1")

Expand Down Expand Up @@ -389,7 +411,8 @@ def build(self):
"CMAKE_CXX_VISIBILITY_PRESET": "hidden",
},
)
cmake.build()
tool_args = ["CODE_SIGNING_ALLOWED=NO"] if self.settings.os == "iOS" else None
cmake.build(build_tool_args=tool_args)
cmake.install(cli_args=["--strip"])

self.post_build()
Expand All @@ -405,9 +428,9 @@ def post_build(self):
if not fname.parent.exists():
fname.parent.mkdir(parents=True)
with tarfile.open(fname, mode="w:gz") as tar:
for file in install_dir.glob("*"):
if file.is_file():
tar.add(file, arcname=file.name)
for file in install_dir.glob("**/*"):
print(f"Adding {file}...")
tar.add(file, arcname=file.name)
print(f"published: {fname}")
dst = self.publish_folder.parent.parent / os
if os == "android":
Expand All @@ -427,14 +450,10 @@ def publish_folder(self) -> Path:
def opencv_dir(self, dir: Path) -> str:
if self.settings.os == "Windows":
return str(dir)
elif self.settings.os == "Linux":
return str(dir / "lib" / "cmake" / "opencv4")
elif self.settings.os == "Macos":
elif self.settings.os in ["Linux", "Macos", "iOS"]:
return str(dir / "lib" / "cmake" / "opencv4")
elif self.settings.os == "Android":
return str(dir / "sdk" / "native" / "jni")
elif self.settings.os == "iOS":
raise NotImplementedError
else:
raise ConanInvalidConfiguration

Expand Down
Loading