|
|
|
#!/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"})
|
|
|
|
|
|
|
|
local_props = ""
|
|
|
|
if args.ndk_location:
|
|
|
|
local_props += "ndk.dir=" + args.ndk_location + "\n"
|
|
|
|
if args.cmake_location:
|
|
|
|
local_props += "cmake.dir=" + args.cmake_location + "\n"
|
|
|
|
|
|
|
|
if local_props:
|
|
|
|
with open(path.join(ANDROID_PROJECT_DIR, "local.properties"), "wt") as f:
|
|
|
|
f.write(local_props)
|
|
|
|
|
|
|
|
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"))
|
|
|
|
|
|
|
|
print("Creating a maven repo from project sources (with sources jar and javadoc jar)...")
|
|
|
|
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("Creating a maven repo from modified AAR (with cpp libraries)...")
|
|
|
|
subprocess.run(["./gradlew", "publishModifiedPublicationToMyrepoRepository"],
|
|
|
|
shell=False,
|
|
|
|
cwd=ANDROID_PROJECT_DIR,
|
|
|
|
check=True)
|
|
|
|
|
|
|
|
# Replacing AAR from the first maven repo with modified AAR from the second maven repo
|
|
|
|
shutil.copytree(path.join(ANDROID_PROJECT_DIR, "OpenCV/build/repo/org/opencv", MAVEN_PACKAGE_NAME),
|
|
|
|
path.join(FINAL_REPO_PATH, "org/opencv", MAVEN_PACKAGE_NAME),
|
|
|
|
dirs_exist_ok=True)
|
|
|
|
|
|
|
|
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="31")
|
|
|
|
parser.add_argument('--android_min_sdk', default="21")
|
|
|
|
parser.add_argument('--android_target_sdk', default="31")
|
|
|
|
parser.add_argument('--java_version', default="1_8")
|
|
|
|
parser.add_argument('--ndk_location', default="")
|
|
|
|
parser.add_argument('--cmake_location', default="")
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
|
|
|
main(args)
|