mirror of https://github.com/opencv/opencv.git
Merge pull request #24456 from alexlyulkov:al/aar
Added scripts for creating an AAR package and a local Maven repository with OpenCV library #24456 Added scripts for creating an AAR package and a local Maven repository with OpenCV library. The build_java_shared_aar.py script creates AAR with Java + C++ shared libraries. The build_static_aar.py script creates AAR with static C++ libraries. The scripts use an Android project template. The project is almost a default Android AAR library project with empty Java code and one empty C++ library. Only build.gradle.template and CMakeLists.txt.template files contain significant changes. See README.md for more information.pull/24465/head
parent
ee0822dc4d
commit
30549d65c2
16 changed files with 855 additions and 0 deletions
@ -0,0 +1,88 @@ |
||||
plugins { |
||||
id 'com.android.library' |
||||
id 'maven-publish' |
||||
} |
||||
|
||||
android { |
||||
namespace 'org.opencv' |
||||
compileSdk ${COMPILE_SDK} |
||||
|
||||
defaultConfig { |
||||
minSdk ${MIN_SDK} |
||||
targetSdk ${TARGET_SDK} |
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" |
||||
externalNativeBuild { |
||||
cmake { |
||||
cppFlags "" |
||||
arguments "-DANDROID_STL=${LIB_TYPE}" |
||||
} |
||||
} |
||||
ndk { |
||||
abiFilters ${ABI_FILTERS} |
||||
} |
||||
} |
||||
|
||||
buildTypes { |
||||
release { |
||||
minifyEnabled false |
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' |
||||
} |
||||
} |
||||
compileOptions { |
||||
sourceCompatibility JavaVersion.VERSION_${JAVA_VERSION} |
||||
targetCompatibility JavaVersion.VERSION_${JAVA_VERSION} |
||||
} |
||||
externalNativeBuild { |
||||
cmake { |
||||
path file('src/main/cpp/CMakeLists.txt') |
||||
} |
||||
} |
||||
buildFeatures { |
||||
aidl true |
||||
prefabPublishing true |
||||
buildConfig true |
||||
} |
||||
prefab { |
||||
${LIB_NAME} { |
||||
headers "src/main/cpp/include" |
||||
} |
||||
} |
||||
sourceSets { |
||||
main { |
||||
java.srcDirs = ['src/main/java'] |
||||
//jniLibs.srcDirs = ['libs'] |
||||
aidl.srcDirs = ['src/main/java'] |
||||
} |
||||
} |
||||
|
||||
publishing { |
||||
singleVariant('release') { |
||||
withSourcesJar() |
||||
} |
||||
} |
||||
} |
||||
|
||||
publishing { |
||||
publications { |
||||
release(MavenPublication) { |
||||
groupId = 'org.opencv' |
||||
artifactId = '${PACKAGE_NAME}' |
||||
version = '${OPENCV_VERSION}' |
||||
artifact("opencv-release.aar") |
||||
|
||||
// afterEvaluate { |
||||
// from components.release |
||||
// } |
||||
} |
||||
} |
||||
repositories { |
||||
maven { |
||||
name = 'myrepo' |
||||
url = "${project.buildDir}/repo" |
||||
} |
||||
} |
||||
} |
||||
|
||||
dependencies { |
||||
} |
@ -0,0 +1,21 @@ |
||||
# Add project specific ProGuard rules here. |
||||
# You can control the set of applied configuration files using the |
||||
# proguardFiles setting in build.gradle. |
||||
# |
||||
# For more details, see |
||||
# http://developer.android.com/guide/developing/tools/proguard.html |
||||
|
||||
# If your project uses WebView with JS, uncomment the following |
||||
# and specify the fully qualified class name to the JavaScript interface |
||||
# class: |
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview { |
||||
# public *; |
||||
#} |
||||
|
||||
# Uncomment this to preserve the line number information for |
||||
# debugging stack traces. |
||||
#-keepattributes SourceFile,LineNumberTable |
||||
|
||||
# If you keep the line number information, uncomment this to |
||||
# hide the original source file name. |
||||
#-renamesourcefileattribute SourceFile |
@ -0,0 +1,4 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> |
||||
|
||||
</manifest> |
@ -0,0 +1,5 @@ |
||||
cmake_minimum_required(VERSION 3.6) |
||||
|
||||
project("opencv") |
||||
|
||||
add_library(${LIB_NAME} ${LIB_TYPE} native-lib.cpp) |
@ -0,0 +1 @@ |
||||
// This empty .h file is used for creating an AAR with empty C++ lib that will be replaced with OpenCV C++ lib
|
@ -0,0 +1 @@ |
||||
// This empty .cpp file is used for creating an AAR with empty C++ lib that will be replaced with OpenCV C++ lib
|
@ -0,0 +1,29 @@ |
||||
## Scripts for creating an AAR package and a local Maven repository with OpenCV libraries for Android |
||||
|
||||
### How to run the scripts |
||||
1. Set JAVA_HOME and ANDROID_HOME enviroment variables. For example: |
||||
``` |
||||
export JAVA_HOME=~/Android Studio/jbr |
||||
export ANDROID_HOME=~/Android/SDK |
||||
``` |
||||
2. Download OpenCV SDK for Android |
||||
3. Run build script for version with Java and a shared C++ library: |
||||
``` |
||||
python build_java_shared_aar.py "~/opencv-4.7.0-android-sdk/OpenCV-android-sdk" |
||||
``` |
||||
4. Run build script for version with static C++ libraries: |
||||
``` |
||||
python build_static_aar.py "~/opencv-4.7.0-android-sdk/OpenCV-android-sdk" |
||||
``` |
||||
The AAR libraries and the local Maven repository will be created in the **outputs** directory |
||||
### Technical details |
||||
The scripts consist of 5 steps: |
||||
1. Preparing Android AAR library project template |
||||
2. Adding Java code to the project. Adding C++ public headers for shared version to the project. |
||||
3. Compiling the project to build an AAR package |
||||
4. Adding C++ binary libraries to the AAR package. Adding C++ public headers for static version to the AAR package. |
||||
5. Creating Maven repository with the AAR package |
||||
|
||||
There are a few minor limitations: |
||||
1. Due to the AAR design the Java + shared C++ AAR package contains duplicates of C++ binary libraries, but the final user's Android application contains only one library instance. |
||||
2. The compile definitions from cmake configs are skipped, but it shouldn't affect the library because the script uses precompiled C++ binaries from SDK. |
@ -0,0 +1,4 @@ |
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules. |
||||
plugins { |
||||
id 'com.android.library' version '8.0.2' apply false |
||||
} |
@ -0,0 +1,21 @@ |
||||
# Project-wide Gradle settings. |
||||
# IDE (e.g. Android Studio) users: |
||||
# Gradle settings configured through the IDE *will override* |
||||
# any settings specified in this file. |
||||
# For more details on how to configure your build environment visit |
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html |
||||
# Specifies the JVM arguments used for the daemon process. |
||||
# The setting is particularly useful for tweaking memory settings. |
||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 |
||||
# When configured, Gradle will run in incubating parallel mode. |
||||
# This option should only be used with decoupled projects. More details, visit |
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects |
||||
# org.gradle.parallel=true |
||||
# AndroidX package structure to make it clearer which packages are bundled with the |
||||
# Android operating system, and which are packaged with your app's APK |
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn |
||||
android.useAndroidX=true |
||||
# Enables namespacing of each library's R class so that its R class includes only the |
||||
# resources declared in the library itself and none from the library's dependencies, |
||||
# thereby reducing the size of the R class for that library |
||||
android.nonTransitiveRClass=true |
Binary file not shown.
@ -0,0 +1,6 @@ |
||||
#Mon Jul 10 11:57:38 SGT 2023 |
||||
distributionBase=GRADLE_USER_HOME |
||||
distributionPath=wrapper/dists |
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip |
||||
zipStoreBase=GRADLE_USER_HOME |
||||
zipStorePath=wrapper/dists |
@ -0,0 +1,185 @@ |
||||
#!/usr/bin/env sh |
||||
|
||||
# |
||||
# Copyright 2015 the original author or authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# https://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
# |
||||
|
||||
############################################################################## |
||||
## |
||||
## Gradle start up script for UN*X |
||||
## |
||||
############################################################################## |
||||
|
||||
# Attempt to set APP_HOME |
||||
# Resolve links: $0 may be a link |
||||
PRG="$0" |
||||
# Need this for relative symlinks. |
||||
while [ -h "$PRG" ] ; do |
||||
ls=`ls -ld "$PRG"` |
||||
link=`expr "$ls" : '.*-> \(.*\)$'` |
||||
if expr "$link" : '/.*' > /dev/null; then |
||||
PRG="$link" |
||||
else |
||||
PRG=`dirname "$PRG"`"/$link" |
||||
fi |
||||
done |
||||
SAVED="`pwd`" |
||||
cd "`dirname \"$PRG\"`/" >/dev/null |
||||
APP_HOME="`pwd -P`" |
||||
cd "$SAVED" >/dev/null |
||||
|
||||
APP_NAME="Gradle" |
||||
APP_BASE_NAME=`basename "$0"` |
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. |
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' |
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value. |
||||
MAX_FD="maximum" |
||||
|
||||
warn () { |
||||
echo "$*" |
||||
} |
||||
|
||||
die () { |
||||
echo |
||||
echo "$*" |
||||
echo |
||||
exit 1 |
||||
} |
||||
|
||||
# OS specific support (must be 'true' or 'false'). |
||||
cygwin=false |
||||
msys=false |
||||
darwin=false |
||||
nonstop=false |
||||
case "`uname`" in |
||||
CYGWIN* ) |
||||
cygwin=true |
||||
;; |
||||
Darwin* ) |
||||
darwin=true |
||||
;; |
||||
MINGW* ) |
||||
msys=true |
||||
;; |
||||
NONSTOP* ) |
||||
nonstop=true |
||||
;; |
||||
esac |
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar |
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM. |
||||
if [ -n "$JAVA_HOME" ] ; then |
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then |
||||
# IBM's JDK on AIX uses strange locations for the executables |
||||
JAVACMD="$JAVA_HOME/jre/sh/java" |
||||
else |
||||
JAVACMD="$JAVA_HOME/bin/java" |
||||
fi |
||||
if [ ! -x "$JAVACMD" ] ; then |
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME |
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the |
||||
location of your Java installation." |
||||
fi |
||||
else |
||||
JAVACMD="java" |
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. |
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the |
||||
location of your Java installation." |
||||
fi |
||||
|
||||
# Increase the maximum file descriptors if we can. |
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then |
||||
MAX_FD_LIMIT=`ulimit -H -n` |
||||
if [ $? -eq 0 ] ; then |
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then |
||||
MAX_FD="$MAX_FD_LIMIT" |
||||
fi |
||||
ulimit -n $MAX_FD |
||||
if [ $? -ne 0 ] ; then |
||||
warn "Could not set maximum file descriptor limit: $MAX_FD" |
||||
fi |
||||
else |
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" |
||||
fi |
||||
fi |
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock |
||||
if $darwin; then |
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" |
||||
fi |
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java |
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then |
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"` |
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` |
||||
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"` |
||||
|
||||
# We build the pattern for arguments to be converted via cygpath |
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` |
||||
SEP="" |
||||
for dir in $ROOTDIRSRAW ; do |
||||
ROOTDIRS="$ROOTDIRS$SEP$dir" |
||||
SEP="|" |
||||
done |
||||
OURCYGPATTERN="(^($ROOTDIRS))" |
||||
# Add a user-defined pattern to the cygpath arguments |
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then |
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" |
||||
fi |
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh |
||||
i=0 |
||||
for arg in "$@" ; do |
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` |
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option |
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition |
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` |
||||
else |
||||
eval `echo args$i`="\"$arg\"" |
||||
fi |
||||
i=`expr $i + 1` |
||||
done |
||||
case $i in |
||||
0) set -- ;; |
||||
1) set -- "$args0" ;; |
||||
2) set -- "$args0" "$args1" ;; |
||||
3) set -- "$args0" "$args1" "$args2" ;; |
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;; |
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; |
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; |
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; |
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; |
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; |
||||
esac |
||||
fi |
||||
|
||||
# Escape application args |
||||
save () { |
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done |
||||
echo " " |
||||
} |
||||
APP_ARGS=`save "$@"` |
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules |
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" |
||||
|
||||
exec "$JAVACMD" "$@" |
@ -0,0 +1,89 @@ |
||||
@rem |
||||
@rem Copyright 2015 the original author or authors. |
||||
@rem |
||||
@rem Licensed under the Apache License, Version 2.0 (the "License"); |
||||
@rem you may not use this file except in compliance with the License. |
||||
@rem You may obtain a copy of the License at |
||||
@rem |
||||
@rem https://www.apache.org/licenses/LICENSE-2.0 |
||||
@rem |
||||
@rem Unless required by applicable law or agreed to in writing, software |
||||
@rem distributed under the License is distributed on an "AS IS" BASIS, |
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
@rem See the License for the specific language governing permissions and |
||||
@rem limitations under the License. |
||||
@rem |
||||
|
||||
@if "%DEBUG%" == "" @echo off |
||||
@rem ########################################################################## |
||||
@rem |
||||
@rem Gradle startup script for Windows |
||||
@rem |
||||
@rem ########################################################################## |
||||
|
||||
@rem Set local scope for the variables with windows NT shell |
||||
if "%OS%"=="Windows_NT" setlocal |
||||
|
||||
set DIRNAME=%~dp0 |
||||
if "%DIRNAME%" == "" set DIRNAME=. |
||||
set APP_BASE_NAME=%~n0 |
||||
set APP_HOME=%DIRNAME% |
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter. |
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi |
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. |
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" |
||||
|
||||
@rem Find java.exe |
||||
if defined JAVA_HOME goto findJavaFromJavaHome |
||||
|
||||
set JAVA_EXE=java.exe |
||||
%JAVA_EXE% -version >NUL 2>&1 |
||||
if "%ERRORLEVEL%" == "0" goto execute |
||||
|
||||
echo. |
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. |
||||
echo. |
||||
echo Please set the JAVA_HOME variable in your environment to match the |
||||
echo location of your Java installation. |
||||
|
||||
goto fail |
||||
|
||||
:findJavaFromJavaHome |
||||
set JAVA_HOME=%JAVA_HOME:"=% |
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe |
||||
|
||||
if exist "%JAVA_EXE%" goto execute |
||||
|
||||
echo. |
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% |
||||
echo. |
||||
echo Please set the JAVA_HOME variable in your environment to match the |
||||
echo location of your Java installation. |
||||
|
||||
goto fail |
||||
|
||||
:execute |
||||
@rem Setup the command line |
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar |
||||
|
||||
|
||||
@rem Execute Gradle |
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* |
||||
|
||||
:end |
||||
@rem End local scope for the variables with windows NT shell |
||||
if "%ERRORLEVEL%"=="0" goto mainEnd |
||||
|
||||
:fail |
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of |
||||
rem the _cmd.exe /c_ return code! |
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 |
||||
exit /b 1 |
||||
|
||||
:mainEnd |
||||
if "%OS%"=="Windows_NT" endlocal |
||||
|
||||
:omega |
@ -0,0 +1,16 @@ |
||||
pluginManagement { |
||||
repositories { |
||||
google() |
||||
mavenCentral() |
||||
gradlePluginPortal() |
||||
} |
||||
} |
||||
dependencyResolutionManagement { |
||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) |
||||
repositories { |
||||
google() |
||||
mavenCentral() |
||||
} |
||||
} |
||||
rootProject.name = "OpenCV" |
||||
include ':OpenCV' |
@ -0,0 +1,156 @@ |
||||
#!/usr/bin/env python |
||||
|
||||
import argparse |
||||
from os import path |
||||
import os |
||||
import re |
||||
import shutil |
||||
import string |
||||
import subprocess |
||||
|
||||
|
||||
COPY_FROM_SDK_TO_ANDROID_PROJECT = [ |
||||
["sdk/native/jni/include", "OpenCV/src/main/cpp/include"], |
||||
["sdk/java/src/org", "OpenCV/src/main/java/org"], |
||||
["sdk/java/res", "OpenCV/src/main/res"] |
||||
] |
||||
|
||||
COPY_FROM_SDK_TO_APK = [ |
||||
["sdk/native/libs/<ABI>/lib<LIB_NAME>.so", "jni/<ABI>/lib<LIB_NAME>.so"], |
||||
["sdk/native/libs/<ABI>/lib<LIB_NAME>.so", "prefab/modules/<LIB_NAME>/libs/android.<ABI>/lib<LIB_NAME>.so"], |
||||
] |
||||
|
||||
ANDROID_PROJECT_TEMPLATE_DIR = path.join(path.dirname(__file__), "aar-template") |
||||
TEMP_DIR = "build_java_shared" |
||||
ANDROID_PROJECT_DIR = path.join(TEMP_DIR, "AndroidProject") |
||||
COMPILED_AAR_PATH_1 = path.join(ANDROID_PROJECT_DIR, "OpenCV/build/outputs/aar/OpenCV-release.aar") # original package name |
||||
COMPILED_AAR_PATH_2 = path.join(ANDROID_PROJECT_DIR, "OpenCV/build/outputs/aar/opencv-release.aar") # lower case package name |
||||
AAR_UNZIPPED_DIR = path.join(TEMP_DIR, "aar_unzipped") |
||||
FINAL_AAR_PATH_TEMPLATE = "outputs/opencv_java_shared_<OPENCV_VERSION>.aar" |
||||
FINAL_REPO_PATH = "outputs/maven_repo" |
||||
MAVEN_PACKAGE_NAME = "opencv" |
||||
|
||||
def fill_template(src_path, dst_path, args_dict): |
||||
with open(src_path, "r") as f: |
||||
template_text = f.read() |
||||
template = string.Template(template_text) |
||||
text = template.safe_substitute(args_dict) |
||||
with open(dst_path, "w") as f: |
||||
f.write(text) |
||||
|
||||
def get_opencv_version(opencv_sdk_path): |
||||
version_hpp_path = path.join(opencv_sdk_path, "sdk/native/jni/include/opencv2/core/version.hpp") |
||||
with open(version_hpp_path, "rt") as f: |
||||
data = f.read() |
||||
major = re.search(r'^#define\W+CV_VERSION_MAJOR\W+(\d+)$', data, re.MULTILINE).group(1) |
||||
minor = re.search(r'^#define\W+CV_VERSION_MINOR\W+(\d+)$', data, re.MULTILINE).group(1) |
||||
revision = re.search(r'^#define\W+CV_VERSION_REVISION\W+(\d+)$', data, re.MULTILINE).group(1) |
||||
return "%(major)s.%(minor)s.%(revision)s" % locals() |
||||
|
||||
def get_compiled_aar_path(path1, path2): |
||||
if path.exists(path1): |
||||
return path1 |
||||
elif path.exists(path2): |
||||
return path2 |
||||
else: |
||||
raise Exception("Can't find compiled AAR path in [" + path1 + ", " + path2 + "]") |
||||
|
||||
def cleanup(paths_to_remove): |
||||
exists = False |
||||
for p in paths_to_remove: |
||||
if path.exists(p): |
||||
exists = True |
||||
if path.isdir(p): |
||||
shutil.rmtree(p) |
||||
else: |
||||
os.remove(p) |
||||
print("Removed", p) |
||||
if not exists: |
||||
print("Nothing to remove") |
||||
|
||||
def main(args): |
||||
opencv_version = get_opencv_version(args.opencv_sdk_path) |
||||
abis = os.listdir(path.join(args.opencv_sdk_path, "sdk/native/libs")) |
||||
lib_name = "opencv_java" + opencv_version.split(".")[0] |
||||
final_aar_path = FINAL_AAR_PATH_TEMPLATE.replace("<OPENCV_VERSION>", opencv_version) |
||||
|
||||
print("Removing data from previous runs...") |
||||
cleanup([TEMP_DIR, final_aar_path, path.join(FINAL_REPO_PATH, "org/opencv", MAVEN_PACKAGE_NAME)]) |
||||
|
||||
print("Preparing Android project...") |
||||
# ANDROID_PROJECT_TEMPLATE_DIR contains an Android project template that creates AAR |
||||
shutil.copytree(ANDROID_PROJECT_TEMPLATE_DIR, ANDROID_PROJECT_DIR) |
||||
|
||||
# Configuring the Android project to Java + shared C++ lib version |
||||
shutil.rmtree(path.join(ANDROID_PROJECT_DIR, "OpenCV/src/main/cpp/include")) |
||||
|
||||
fill_template(path.join(ANDROID_PROJECT_DIR, "OpenCV/build.gradle.template"), |
||||
path.join(ANDROID_PROJECT_DIR, "OpenCV/build.gradle"), |
||||
{"LIB_NAME": lib_name, |
||||
"LIB_TYPE": "c++_shared", |
||||
"PACKAGE_NAME": MAVEN_PACKAGE_NAME, |
||||
"OPENCV_VERSION": opencv_version, |
||||
"COMPILE_SDK": args.android_compile_sdk, |
||||
"MIN_SDK": args.android_min_sdk, |
||||
"TARGET_SDK": args.android_target_sdk, |
||||
"ABI_FILTERS": ", ".join(['"' + x + '"' for x in abis]), |
||||
"JAVA_VERSION": args.java_version, |
||||
}) |
||||
fill_template(path.join(ANDROID_PROJECT_DIR, "OpenCV/src/main/cpp/CMakeLists.txt.template"), |
||||
path.join(ANDROID_PROJECT_DIR, "OpenCV/src/main/cpp/CMakeLists.txt"), |
||||
{"LIB_NAME": lib_name, "LIB_TYPE": "SHARED"}) |
||||
|
||||
# Copying Java code and C++ public headers from SDK to the Android project |
||||
for src, dst in COPY_FROM_SDK_TO_ANDROID_PROJECT: |
||||
shutil.copytree(path.join(args.opencv_sdk_path, src), |
||||
path.join(ANDROID_PROJECT_DIR, dst)) |
||||
|
||||
print("Running gradle assembleRelease...") |
||||
# Running gradle to build the Android project |
||||
subprocess.run(["./gradlew", "assembleRelease"], |
||||
shell=False, |
||||
cwd=ANDROID_PROJECT_DIR, |
||||
check=True) |
||||
|
||||
print("Adding libs to AAR...") |
||||
# The created AAR package doesn't contain C++ shared libs. |
||||
# We need to add them manually. |
||||
# AAR package is just a zip archive. |
||||
complied_aar_path = get_compiled_aar_path(COMPILED_AAR_PATH_1, COMPILED_AAR_PATH_2) # two possible paths |
||||
shutil.unpack_archive(complied_aar_path, AAR_UNZIPPED_DIR, "zip") |
||||
|
||||
for abi in abis: |
||||
for src, dst in COPY_FROM_SDK_TO_APK: |
||||
src = src.replace("<ABI>", abi).replace("<LIB_NAME>", lib_name) |
||||
dst = dst.replace("<ABI>", abi).replace("<LIB_NAME>", lib_name) |
||||
shutil.copy(path.join(args.opencv_sdk_path, src), |
||||
path.join(AAR_UNZIPPED_DIR, dst)) |
||||
|
||||
# Creating final AAR zip archive |
||||
os.makedirs("outputs", exist_ok=True) |
||||
shutil.make_archive(final_aar_path, "zip", AAR_UNZIPPED_DIR, ".") |
||||
os.rename(final_aar_path + ".zip", final_aar_path) |
||||
|
||||
print("Creating local maven repo...") |
||||
|
||||
shutil.copy(final_aar_path, path.join(ANDROID_PROJECT_DIR, "OpenCV/opencv-release.aar")) |
||||
subprocess.run(["./gradlew", "publishReleasePublicationToMyrepoRepository"], |
||||
shell=False, |
||||
cwd=ANDROID_PROJECT_DIR, |
||||
check=True) |
||||
|
||||
os.makedirs(path.join(FINAL_REPO_PATH, "org/opencv"), exist_ok=True) |
||||
shutil.move(path.join(ANDROID_PROJECT_DIR, "OpenCV/build/repo/org/opencv", MAVEN_PACKAGE_NAME), |
||||
path.join(FINAL_REPO_PATH, "org/opencv", MAVEN_PACKAGE_NAME)) |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
parser = argparse.ArgumentParser(description="Builds AAR with Java and shared C++ libs from OpenCV SDK") |
||||
parser.add_argument('opencv_sdk_path') |
||||
parser.add_argument('--android_compile_sdk', default="26") |
||||
parser.add_argument('--android_min_sdk', default="21") |
||||
parser.add_argument('--android_target_sdk', default="26") |
||||
parser.add_argument('--java_version', default="1_8") |
||||
args = parser.parse_args() |
||||
|
||||
main(args) |
@ -0,0 +1,229 @@ |
||||
#!/usr/bin/env python |
||||
|
||||
import argparse |
||||
import json |
||||
from os import path |
||||
import os |
||||
import shutil |
||||
import subprocess |
||||
|
||||
from build_java_shared_aar import cleanup, fill_template, get_compiled_aar_path, get_opencv_version |
||||
|
||||
|
||||
ANDROID_PROJECT_TEMPLATE_DIR = path.join(path.dirname(__file__), "aar-template") |
||||
TEMP_DIR = "build_static" |
||||
ANDROID_PROJECT_DIR = path.join(TEMP_DIR, "AndroidProject") |
||||
COMPILED_AAR_PATH_1 = path.join(ANDROID_PROJECT_DIR, "OpenCV/build/outputs/aar/OpenCV-release.aar") # original package name |
||||
COMPILED_AAR_PATH_2 = path.join(ANDROID_PROJECT_DIR, "OpenCV/build/outputs/aar/opencv-release.aar") # lower case package name |
||||
AAR_UNZIPPED_DIR = path.join(TEMP_DIR, "aar_unzipped") |
||||
FINAL_AAR_PATH_TEMPLATE = "outputs/opencv_static_<OPENCV_VERSION>.aar" |
||||
FINAL_REPO_PATH = "outputs/maven_repo" |
||||
MAVEN_PACKAGE_NAME = "opencv-static" |
||||
|
||||
|
||||
def get_list_of_opencv_libs(sdk_dir): |
||||
files = os.listdir(path.join(sdk_dir, "sdk/native/staticlibs/arm64-v8a")) |
||||
libs = [f[3:-2] for f in files if f[:3] == "lib" and f[-2:] == ".a"] |
||||
return libs |
||||
|
||||
def get_list_of_3rdparty_libs(sdk_dir, abis): |
||||
libs = [] |
||||
for abi in abis: |
||||
files = os.listdir(path.join(sdk_dir, "sdk/native/3rdparty/libs/" + abi)) |
||||
cur_libs = [f[3:-2] for f in files if f[:3] == "lib" and f[-2:] == ".a"] |
||||
for lib in cur_libs: |
||||
if lib not in libs: |
||||
libs.append(lib) |
||||
return libs |
||||
|
||||
def add_printing_linked_libs(sdk_dir, opencv_libs): |
||||
""" |
||||
Modifies CMakeLists.txt file in Android project, so it prints linked libraries for each OpenCV library" |
||||
""" |
||||
sdk_jni_dir = sdk_dir + "/sdk/native/jni" |
||||
with open(path.join(ANDROID_PROJECT_DIR, "OpenCV/src/main/cpp/CMakeLists.txt"), "a") as f: |
||||
f.write('\nset(OpenCV_DIR "' + sdk_jni_dir + '")\n') |
||||
f.write('find_package(OpenCV REQUIRED)\n') |
||||
for lib_name in opencv_libs: |
||||
output_filename_prefix = "linkedlibs." + lib_name + "." |
||||
f.write('get_target_property(OUT "' + lib_name + '" INTERFACE_LINK_LIBRARIES)\n') |
||||
f.write('file(WRITE "' + output_filename_prefix + '${ANDROID_ABI}.txt" "${OUT}")\n') |
||||
|
||||
def read_linked_libs(lib_name, abis): |
||||
""" |
||||
Reads linked libs for each OpenCV library from files, that was generated by gradle. See add_printing_linked_libs() |
||||
""" |
||||
deps_lists = [] |
||||
for abi in abis: |
||||
with open(path.join(ANDROID_PROJECT_DIR, "OpenCV/src/main/cpp", f"linkedlibs.{lib_name}.{abi}.txt")) as f: |
||||
text = f.read() |
||||
linked_libs = text.split(";") |
||||
linked_libs = [x.replace("$<LINK_ONLY:", "").replace(">", "") for x in linked_libs] |
||||
deps_lists.append(linked_libs) |
||||
|
||||
return merge_dependencies_lists(deps_lists) |
||||
|
||||
def merge_dependencies_lists(deps_lists): |
||||
""" |
||||
One library may have different dependencies for different ABIS. |
||||
We need to merge them into one list with all the dependencies preserving the order. |
||||
""" |
||||
result = [] |
||||
for d_list in deps_lists: |
||||
for i in range(len(d_list)): |
||||
if d_list[i] not in result: |
||||
if i == 0: |
||||
result.append(d_list[i]) |
||||
else: |
||||
index = result.index(d_list[i-1]) |
||||
result = result[:index + 1] + [d_list[i]] + result[index + 1:] |
||||
|
||||
return result |
||||
|
||||
def convert_deps_list_to_prefab(linked_libs, opencv_libs, external_libs): |
||||
""" |
||||
Converting list of dependencies into prefab format. |
||||
""" |
||||
prefab_linked_libs = [] |
||||
for lib in linked_libs: |
||||
if (lib in opencv_libs) or (lib in external_libs): |
||||
prefab_linked_libs.append(":" + lib) |
||||
elif (lib[:3] == "lib" and lib[3:] in external_libs): |
||||
prefab_linked_libs.append(":" + lib[3:]) |
||||
elif lib == "ocv.3rdparty.android_mediandk": |
||||
prefab_linked_libs += ["-landroid", "-llog", "-lmediandk"] |
||||
print("Warning: manualy handled ocv.3rdparty.android_mediandk dependency") |
||||
elif lib == "ocv.3rdparty.flatbuffers": |
||||
print("Warning: manualy handled ocv.3rdparty.flatbuffers dependency") |
||||
elif lib.startswith("ocv.3rdparty"): |
||||
raise Exception("Unknown lib " + lib) |
||||
else: |
||||
prefab_linked_libs.append("-l" + lib) |
||||
return prefab_linked_libs |
||||
|
||||
def main(args): |
||||
opencv_version = get_opencv_version(args.opencv_sdk_path) |
||||
abis = os.listdir(path.join(args.opencv_sdk_path, "sdk/native/libs")) |
||||
final_aar_path = FINAL_AAR_PATH_TEMPLATE.replace("<OPENCV_VERSION>", opencv_version) |
||||
sdk_dir = args.opencv_sdk_path |
||||
|
||||
print("Removing data from previous runs...") |
||||
cleanup([TEMP_DIR, final_aar_path, path.join(FINAL_REPO_PATH, "org/opencv", MAVEN_PACKAGE_NAME)]) |
||||
|
||||
print("Preparing Android project...") |
||||
# ANDROID_PROJECT_TEMPLATE_DIR contains an Android project template that creates AAR |
||||
shutil.copytree(ANDROID_PROJECT_TEMPLATE_DIR, ANDROID_PROJECT_DIR) |
||||
|
||||
# Configuring the Android project to static C++ libs version |
||||
fill_template(path.join(ANDROID_PROJECT_DIR, "OpenCV/build.gradle.template"), |
||||
path.join(ANDROID_PROJECT_DIR, "OpenCV/build.gradle"), |
||||
{"LIB_NAME": "templib", |
||||
"LIB_TYPE": "c++_static", |
||||
"PACKAGE_NAME": MAVEN_PACKAGE_NAME, |
||||
"OPENCV_VERSION": opencv_version, |
||||
"COMPILE_SDK": args.android_compile_sdk, |
||||
"MIN_SDK": args.android_min_sdk, |
||||
"TARGET_SDK": args.android_target_sdk, |
||||
"ABI_FILTERS": ", ".join(['"' + x + '"' for x in abis]), |
||||
"JAVA_VERSION": args.java_version, |
||||
}) |
||||
fill_template(path.join(ANDROID_PROJECT_DIR, "OpenCV/src/main/cpp/CMakeLists.txt.template"), |
||||
path.join(ANDROID_PROJECT_DIR, "OpenCV/src/main/cpp/CMakeLists.txt"), |
||||
{"LIB_NAME": "templib", "LIB_TYPE": "STATIC"}) |
||||
|
||||
opencv_libs = get_list_of_opencv_libs(sdk_dir) |
||||
external_libs = get_list_of_3rdparty_libs(sdk_dir, abis) |
||||
|
||||
add_printing_linked_libs(sdk_dir, opencv_libs) |
||||
|
||||
print("Running gradle assembleRelease...") |
||||
# Running gradle to build the Android project |
||||
subprocess.run(["./gradlew", "assembleRelease"], |
||||
shell=False, |
||||
cwd=ANDROID_PROJECT_DIR, |
||||
check=True) |
||||
|
||||
# The created AAR package contains only one empty libtemplib.a library. |
||||
# We need to add OpenCV libraries manually. |
||||
# AAR package is just a zip archive |
||||
complied_aar_path = get_compiled_aar_path(COMPILED_AAR_PATH_1, COMPILED_AAR_PATH_2) # two possible paths |
||||
shutil.unpack_archive(complied_aar_path, AAR_UNZIPPED_DIR, "zip") |
||||
|
||||
print("Adding libs to AAR...") |
||||
|
||||
# Copying 3rdparty libs from SDK into the AAR |
||||
for lib in external_libs: |
||||
for abi in abis: |
||||
os.makedirs(path.join(AAR_UNZIPPED_DIR, "prefab/modules/" + lib + "/libs/android." + abi)) |
||||
if path.exists(path.join(sdk_dir, "sdk/native/3rdparty/libs/" + abi, "lib" + lib + ".a")): |
||||
shutil.copy(path.join(sdk_dir, "sdk/native/3rdparty/libs/" + abi, "lib" + lib + ".a"), |
||||
path.join(AAR_UNZIPPED_DIR, "prefab/modules/" + lib + "/libs/android." + abi, "lib" + lib + ".a")) |
||||
else: |
||||
# One OpenCV library may have different dependency lists for different ABIs, but we can write only one |
||||
# full dependency list for all ABIs. So we just add empty .a library if this ABI doesn't have this dependency. |
||||
shutil.copy(path.join(AAR_UNZIPPED_DIR, "prefab/modules/templib/libs/android." + abi, "libtemplib.a"), |
||||
path.join(AAR_UNZIPPED_DIR, "prefab/modules/" + lib + "/libs/android." + abi, "lib" + lib + ".a")) |
||||
shutil.copy(path.join(AAR_UNZIPPED_DIR, "prefab/modules/templib/libs/android." + abi + "/abi.json"), |
||||
path.join(AAR_UNZIPPED_DIR, "prefab/modules/" + lib + "/libs/android." + abi + "/abi.json")) |
||||
shutil.copy(path.join(AAR_UNZIPPED_DIR, "prefab/modules/templib/module.json"), |
||||
path.join(AAR_UNZIPPED_DIR, "prefab/modules/" + lib + "/module.json")) |
||||
|
||||
# Copying OpenV libs from SDK into the AAR |
||||
for lib in opencv_libs: |
||||
for abi in abis: |
||||
os.makedirs(path.join(AAR_UNZIPPED_DIR, "prefab/modules/" + lib + "/libs/android." + abi)) |
||||
shutil.copy(path.join(sdk_dir, "sdk/native/staticlibs/" + abi, "lib" + lib + ".a"), |
||||
path.join(AAR_UNZIPPED_DIR, "prefab/modules/" + lib + "/libs/android." + abi, "lib" + lib + ".a")) |
||||
shutil.copy(path.join(AAR_UNZIPPED_DIR, "prefab/modules/templib/libs/android." + abi + "/abi.json"), |
||||
path.join(AAR_UNZIPPED_DIR, "prefab/modules/" + lib + "/libs/android." + abi + "/abi.json")) |
||||
os.makedirs(path.join(AAR_UNZIPPED_DIR, "prefab/modules/" + lib + "/include/opencv2")) |
||||
shutil.copy(path.join(sdk_dir, "sdk/native/jni/include/opencv2/" + lib.replace("opencv_", "") + ".hpp"), |
||||
path.join(AAR_UNZIPPED_DIR, "prefab/modules/" + lib + "/include/opencv2/" + lib.replace("opencv_", "") + ".hpp")) |
||||
shutil.copytree(path.join(sdk_dir, "sdk/native/jni/include/opencv2/" + lib.replace("opencv_", "")), |
||||
path.join(AAR_UNZIPPED_DIR, "prefab/modules/" + lib + "/include/opencv2/" + lib.replace("opencv_", ""))) |
||||
|
||||
# Adding dependencies list |
||||
module_json_text = { |
||||
"export_libraries": convert_deps_list_to_prefab(read_linked_libs(lib, abis), opencv_libs, external_libs), |
||||
"android": {}, |
||||
} |
||||
with open(path.join(AAR_UNZIPPED_DIR, "prefab/modules/" + lib + "/module.json"), "w") as f: |
||||
json.dump(module_json_text, f) |
||||
|
||||
for h_file in ("cvconfig.h", "opencv.hpp", "opencv_modules.hpp"): |
||||
shutil.copy(path.join(sdk_dir, "sdk/native/jni/include/opencv2/" + h_file), |
||||
path.join(AAR_UNZIPPED_DIR, "prefab/modules/opencv_core/include/opencv2/" + h_file)) |
||||
|
||||
|
||||
shutil.rmtree(path.join(AAR_UNZIPPED_DIR, "prefab/modules/templib")) |
||||
|
||||
# Creating final AAR zip archive |
||||
os.makedirs("outputs", exist_ok=True) |
||||
shutil.make_archive(final_aar_path, "zip", AAR_UNZIPPED_DIR, ".") |
||||
os.rename(final_aar_path + ".zip", final_aar_path) |
||||
|
||||
print("Creating local maven repo...") |
||||
|
||||
shutil.copy(final_aar_path, path.join(ANDROID_PROJECT_DIR, "OpenCV/opencv-release.aar")) |
||||
|
||||
subprocess.run(["./gradlew", "publishReleasePublicationToMyrepoRepository"], |
||||
shell=False, |
||||
cwd=ANDROID_PROJECT_DIR, |
||||
check=True) |
||||
|
||||
os.makedirs(path.join(FINAL_REPO_PATH, "org/opencv"), exist_ok=True) |
||||
shutil.move(path.join(ANDROID_PROJECT_DIR, "OpenCV/build/repo/org/opencv", MAVEN_PACKAGE_NAME), |
||||
path.join(FINAL_REPO_PATH, "org/opencv", MAVEN_PACKAGE_NAME)) |
||||
print("Done") |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
parser = argparse.ArgumentParser(description="Builds AAR with static C++ libs from OpenCV SDK") |
||||
parser.add_argument('opencv_sdk_path') |
||||
parser.add_argument('--android_compile_sdk', default="26") |
||||
parser.add_argument('--android_min_sdk', default="21") |
||||
parser.add_argument('--android_target_sdk', default="26") |
||||
parser.add_argument('--java_version', default="1_8") |
||||
args = parser.parse_args() |
||||
|
||||
main(args) |
Loading…
Reference in new issue