opencv/platforms/android/build_static_aar.py
alexlyulkov 1e190b3094
Merge pull request #24849 from alexlyulkov:al/aar-javadoc
Modified AAR script: added javadoc to Android Maven #24849

Modified AAR script.
Now the script creates 2 maven repos. The first repo contains sources jar, javadoc jar and AAR without cpp libraries.
The second repo contains modified AAR with cpp libraries. The script merges two repos into one.

### Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch
- [ ] There is a reference to the original bug report and related work
- [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [x] The feature is well documented and sample code can be built with the project CMake

Co-authored-by: Alexander Lyulkov <alexander.lyulkov@opencv.ai>
Co-authored-by: Alexander Smorkalov <alexander.smorkalov@xperience.ai>
2024-01-12 14:06:12 +03:00

255 lines
12 KiB
Python
Executable File

#!/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)