From 9e84b860f233f3acad6de325f214b6bd62fc5a3f Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Sat, 14 Nov 2020 07:16:13 +0000 Subject: [PATCH] cmake: update objc generator scripts - allow to run generator without strong requirement of building 'objc' module --- modules/objc/CMakeLists.txt | 15 ++- modules/objc/generator/CMakeLists.txt | 95 +++++++++++++------ modules/objc/generator/gen_objc.py | 19 ++-- .../generator/templates/cmakelists.template | 2 +- platforms/ios/build_framework.py | 10 +- platforms/osx/build_framework.py | 4 + 6 files changed, 106 insertions(+), 39 deletions(-) diff --git a/modules/objc/CMakeLists.txt b/modules/objc/CMakeLists.txt index d4ea6e3563..8cf24de56e 100644 --- a/modules/objc/CMakeLists.txt +++ b/modules/objc/CMakeLists.txt @@ -1,6 +1,19 @@ -if(OPENCV_INITIAL_PASS AND APPLE_FRAMEWORK AND NOT (BUILD_opencv_objc STREQUAL "OFF")) +if(OPENCV_INITIAL_PASS) # generator for Objective-C source code and documentation signatures add_subdirectory(generator) endif() +if(NOT APPLE_FRAMEWORK) + return() +endif() + +set(the_description "The Objective-C bindings") +ocv_add_module(objc BINDINGS opencv_core opencv_imgproc PRIVATE_REQUIRED opencv_objc_bindings_generator) + +add_custom_target(${the_module} + ALL + COMMENT "Objective-C framework" +) +add_dependencies(${the_module} gen_opencv_objc_source) + #include(${CMAKE_CURRENT_SOURCE_DIR}/common.cmake) diff --git a/modules/objc/generator/CMakeLists.txt b/modules/objc/generator/CMakeLists.txt index dd6f58db6d..b3cbbd3f5f 100644 --- a/modules/objc/generator/CMakeLists.txt +++ b/modules/objc/generator/CMakeLists.txt @@ -1,16 +1,18 @@ -set(MODULE_NAME "objc") +set(MODULE_NAME "objc_bindings_generator") set(OPENCV_MODULE_IS_PART_OF_WORLD FALSE) ocv_add_module(${MODULE_NAME} INTERNAL opencv_core opencv_imgproc) -set(OPENCV_OBJC_SIGNATURES_FILE "${CMAKE_CURRENT_BINARY_DIR}/opencv_objc_signatures.json" CACHE INTERNAL "") +#set(OPENCV_OBJC_SIGNATURES_FILE "${CMAKE_CURRENT_BINARY_DIR}/opencv_objc_signatures.json" CACHE INTERNAL "") set(OPENCV_OBJC_BINDINGS_DIR "${CMAKE_CURRENT_BINARY_DIR}" CACHE INTERNAL "") -file(REMOVE_RECURSE "${OPENCV_OBJC_BINDINGS_DIR}/gen") -file(REMOVE "${OPENCV_DEPHELPER}/gen_opencv_objc_source") # force re-run after CMake +file(REMOVE_RECURSE "${OPENCV_OBJC_BINDINGS_DIR}/osx") +file(REMOVE "${OPENCV_DEPHELPER}/gen_opencv_objc_source_osx") # force re-run after CMake +file(REMOVE_RECURSE "${OPENCV_OBJC_BINDINGS_DIR}/ios") +file(REMOVE "${OPENCV_DEPHELPER}/gen_opencv_objc_source_ios") # force re-run after CMake # This file is included from a subdirectory set(OBJC_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/..") -include(${OBJC_SOURCE_DIR}/common.cmake) +include(${OBJC_SOURCE_DIR}/common.cmake) # fill OPENCV_OBJC_MODULES # common files file(GLOB_RECURSE deps "${CMAKE_CURRENT_SOURCE_DIR}/templates/*") @@ -30,15 +32,21 @@ foreach(m ${OPENCV_OBJC_MODULES}) set(__modules_config "${__modules_config} { \"name\": \"${m_}\", \"location\": \"${rel_path}\" }") endforeach(m) +if(HAVE_opencv_objc) + set(__objc_build_dir "\"objc_build_dir\": \"${CMAKE_CURRENT_BINARY_DIR}/../objc\",") +endif() + set(CONFIG_FILE "${CMAKE_CURRENT_BINARY_DIR}/gen_objc.json") set(__config_str "{ \"rootdir\": \"${OpenCV_SOURCE_DIR}\", + ${__objc_build_dir} \"modules\": [ ${__modules_config} ] } ") +#TODO: ocv_update_file("${CONFIG_FILE}" "${__config_str}" ON_CHANGE_REMOVE "${OPENCV_DEPHELPER}/gen_opencv_objc_source") if(EXISTS "${CONFIG_FILE}") file(READ "${CONFIG_FILE}" __content) else() @@ -52,33 +60,66 @@ unset(__config_str) set(objc_generated_files # "${OPENCV_OBJC_SIGNATURES_FILE}" - "${OPENCV_DEPHELPER}/gen_opencv_objc_source" ) string(REPLACE "opencv_" "" MODULES "${OPENCV_OBJC_MODULES}") -if(IOS) - set(TARGET "ios") -else() - set(TARGET "osx") +if(NOT DEFINED OPENCV_OBJC_TARGET AND APPLE_FRAMEWORK) + if(IOS) + set(OPENCV_OBJC_TARGET "ios") + else() + set(OPENCV_OBJC_TARGET "osx") + endif() endif() -add_custom_command( - OUTPUT ${objc_generated_files} - COMMAND ${PYTHON_DEFAULT_EXECUTABLE} "${OBJC_SOURCE_DIR}/generator/gen_objc.py" -p "${OBJC_SOURCE_DIR}/../python/src2/gen2.py" -c "${CONFIG_FILE}" -t "${TARGET}" -f "${FRAMEWORK_NAME}" - COMMAND ${CMAKE_COMMAND} -E touch "${OPENCV_DEPHELPER}/gen_opencv_objc_source" - WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" - DEPENDS "${OBJC_SOURCE_DIR}/generator/gen_objc.py" - "${OBJC_SOURCE_DIR}/../python/src2/gen2.py" - "${OBJC_SOURCE_DIR}/../python/src2/hdr_parser.py" - # don't, result of file(WRITE): "${CMAKE_CURRENT_BINARY_DIR}/gen_objc.json" - ${deps} - # not allowed (file(WRITE) result): "${CONFIG_FILE}" - COMMENT "Generate files for Objective-C bindings" -) +if(NOT DEFINED OPENCV_OBJC_FRAMEWORK_NAME) + if(DEFINED FRAMEWORK_NAME) + set(OPENCV_OBJC_FRAMEWORK_NAME "${FRAMEWORK_NAME}") + else() + set(OPENCV_OBJC_FRAMEWORK_NAME "opencv2") + endif() +endif() -add_custom_target(gen_opencv_objc_source ALL DEPENDS ${objc_generated_files} - SOURCES "${OBJC_SOURCE_DIR}/generator/gen_objc.py" - "${OBJC_SOURCE_DIR}/generator/templates/cmakelists.template" - "${CMAKE_CURRENT_BINARY_DIR}/gen_objc.json" +set(objc_generated_targets "") + +macro(ocv_add_objc_generated_target TARGET) + set(objc_${TARGET}_generated_output_dependecy "${OPENCV_DEPHELPER}/gen_opencv_objc_source_${TARGET}") + file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}") + add_custom_command( + OUTPUT ${objc_generated_files} "${objc_${TARGET}_generated_output_dependecy}" + COMMAND ${PYTHON_DEFAULT_EXECUTABLE} "${OBJC_SOURCE_DIR}/generator/gen_objc.py" + -p "${OBJC_SOURCE_DIR}/../python/src2/gen2.py" + -c "${CONFIG_FILE}" + -t "${TARGET}" + -f "${OPENCV_OBJC_FRAMEWORK_NAME}" + COMMAND ${CMAKE_COMMAND} -E touch "${objc_${TARGET}_generated_output_dependecy}" + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}" + DEPENDS "${OpenCV_SOURCE_DIR}/modules/objc/generator/gen_objc.py" + "${OpenCV_SOURCE_DIR}/modules/python/src2/gen2.py" + "${OpenCV_SOURCE_DIR}/modules/python/src2/hdr_parser.py" + # don't, result of file(WRITE): "${CMAKE_CURRENT_BINARY_DIR}/gen_objc.json" + ${deps} + # not allowed (file(WRITE) result): "${CONFIG_FILE}" + COMMENT "Generate files for Objective-C bindings (${TARGET})" + ) + add_custom_target(gen_opencv_objc_source_${TARGET} + # excluded from all: ALL + DEPENDS ${objc_generated_files} ${objc_${TARGET}_generated_output_dependecy} + SOURCES "${OBJC_SOURCE_DIR}/generator/gen_objc.py" + "${OBJC_SOURCE_DIR}/generator/templates/cmakelists.template" + "${CMAKE_CURRENT_BINARY_DIR}/gen_objc.json" + ) + list(APPEND objc_generated_targets gen_opencv_objc_source_${TARGET}) +endmacro() + +if(OPENCV_OBJC_TARGET) + ocv_add_objc_generated_target(${OPENCV_OBJC_TARGET}) +else() + ocv_add_objc_generated_target(osx) + ocv_add_objc_generated_target(ios) +endif() + +add_custom_target(gen_opencv_objc_source + # excluded from all: ALL + DEPENDS ${objc_generated_targets} ) diff --git a/modules/objc/generator/gen_objc.py b/modules/objc/generator/gen_objc.py index e6637a7c4c..c20251d261 100755 --- a/modules/objc/generator/gen_objc.py +++ b/modules/objc/generator/gen_objc.py @@ -1342,7 +1342,7 @@ typedef NS_ENUM(int, {2}) {{ return "Ptr<" + fullname + ">" return fullname - def finalize(self, output_objc_path): + def finalize(self, objc_target, output_objc_path, output_objc_build_path): opencv_header_file = os.path.join(output_objc_path, framework_name + ".h") opencv_header = "#import \n\n" opencv_header += "// ! Project version number\nFOUNDATION_EXPORT double " + framework_name + "VersionNumber;\n\n" @@ -1356,15 +1356,15 @@ typedef NS_ENUM(int, {2}) {{ opencv_modulemap += "\n export *\n module * {export *}\n}\n" self.save(opencv_modulemap_file, opencv_modulemap) cmakelist_template = read_contents(os.path.join(SCRIPT_DIR, 'templates/cmakelists.template')) - cmakelist = Template(cmakelist_template).substitute(modules = ";".join(modules), framework = framework_name) + cmakelist = Template(cmakelist_template).substitute(modules = ";".join(modules), framework = framework_name, objc_target=objc_target) self.save(os.path.join(dstdir, "CMakeLists.txt"), cmakelist) - mkdir_p("./framework_build") - mkdir_p("./test_build") - mkdir_p("./doc_build") + mkdir_p(os.path.join(output_objc_build_path, "framework_build")) + mkdir_p(os.path.join(output_objc_build_path, "test_build")) + mkdir_p(os.path.join(output_objc_build_path, "doc_build")) with open(os.path.join(SCRIPT_DIR, '../doc/README.md')) as readme_in: readme_body = readme_in.read() readme_body += "\n\n\n##Modules\n\n" + ", ".join(["`" + m.capitalize() + "`" for m in modules]) - with open("./doc_build/README.md", "w") as readme_out: + with open(os.path.join(output_objc_build_path, "doc_build/README.md"), "w") as readme_out: readme_out.write(readme_body) if framework_name != "OpenCV": for dirname, dirs, files in os.walk(os.path.join(testdir, "test")): @@ -1513,6 +1513,11 @@ if __name__ == "__main__": config = json.load(f) ROOT_DIR = config['rootdir']; assert os.path.exists(ROOT_DIR) + if 'objc_build_dir' in config: + objc_build_dir = config['objc_build_dir'] + assert os.path.exists(objc_build_dir), objc_build_dir + else: + objc_build_dir = os.getcwd() dstdir = "./gen" testdir = "./test" @@ -1608,6 +1613,6 @@ if __name__ == "__main__": generator.gen(srcfiles, module, dstdir, objc_base_path, common_headers, manual_classes) else: logging.info("No generated code for module: %s", module) - generator.finalize(objc_base_path) + generator.finalize(args.target, objc_base_path, objc_build_dir) print('Generated files: %d (updated %d)' % (total_files, updated_files)) diff --git a/modules/objc/generator/templates/cmakelists.template b/modules/objc/generator/templates/cmakelists.template index 2cfc2474cd..67cacbbfa4 100644 --- a/modules/objc/generator/templates/cmakelists.template +++ b/modules/objc/generator/templates/cmakelists.template @@ -24,7 +24,7 @@ target_include_directories($framework PRIVATE "$${BUILD_ROOT}") target_include_directories($framework PRIVATE "$${BUILD_ROOT}/install/include") target_include_directories($framework PRIVATE "$${BUILD_ROOT}/install/include/opencv2") foreach(m $${MODULES}) - target_include_directories($framework PRIVATE "$${BUILD_ROOT}/modules/objc/gen/objc/$${m}") + target_include_directories($framework PRIVATE "$${BUILD_ROOT}/modules/objc_bindings_generator/$objc_target/gen/objc/$${m}") endforeach() install(TARGETS $framework LIBRARY DESTINATION lib) diff --git a/platforms/ios/build_framework.py b/platforms/ios/build_framework.py index e759072825..5965cd0a96 100755 --- a/platforms/ios/build_framework.py +++ b/platforms/ios/build_framework.py @@ -128,10 +128,10 @@ class Builder: self.makeFramework(outdir, dirs) if self.build_objc_wrapper: if self.run_tests: - check_call([sys.argv[0].replace("build_framework", "run_tests"), "--framework_dir=" + outdir, "--framework_name=" + self.framework_name, dirs[0] + "/modules/objc/test"]) + check_call([sys.argv[0].replace("build_framework", "run_tests"), "--framework_dir=" + outdir, "--framework_name=" + self.framework_name, dirs[0] + "/modules/objc_bindings_generator/{}/test".format(self.getObjcTarget())]) else: print("To run tests call:") - print(sys.argv[0].replace("build_framework", "run_tests") + " --framework_dir=" + outdir + " --framework_name=" + self.framework_name + " " + dirs[0] + "/modules/objc/test") + print(sys.argv[0].replace("build_framework", "run_tests") + " --framework_dir=" + outdir + " --framework_name=" + self.framework_name + " " + dirs[0] + "/modules/objc_bindings_generator/{}/test".format(self.getObjcTarget())) if self.build_docs: check_call([sys.argv[0].replace("build_framework", "build_docs"), dirs[0] + "/modules/objc/framework_build"]) doc_path = os.path.join(dirs[0], "modules", "objc", "doc_build", "docs") @@ -216,6 +216,10 @@ class Builder: def getInfoPlist(self, builddirs): return os.path.join(builddirs[0], "ios", "Info.plist") + def getObjcTarget(self): + # Obj-C generation target + return 'ios' + def makeCMakeCmd(self, arch, target, dir, cmakeargs = []): toolchain = self.getToolchain(arch, target) cmakecmd = self.getCMakeArgs(arch, target) + \ @@ -255,7 +259,7 @@ class Builder: execute(buildcmd + ["-target", "ALL_BUILD", "build"], cwd = builddir) execute(["cmake", "-DBUILD_TYPE=%s" % self.getConfiguration(), "-P", "cmake_install.cmake"], cwd = builddir) if self.build_objc_wrapper: - cmakecmd = self.makeCMakeCmd(arch, target, builddir + "/modules/objc/gen", cmakeargs) + cmakecmd = self.makeCMakeCmd(arch, target, builddir + "/modules/objc_bindings_generator/{}/gen".format(self.getObjcTarget()), cmakeargs) cmakecmd.append("-DBUILD_ROOT=%s" % builddir) cmakecmd.append("-DCMAKE_INSTALL_NAME_TOOL=install_name_tool") cmakecmd.append("--no-warn-unused-cli") diff --git a/platforms/osx/build_framework.py b/platforms/osx/build_framework.py index ccca582615..de13e665fa 100755 --- a/platforms/osx/build_framework.py +++ b/platforms/osx/build_framework.py @@ -14,6 +14,10 @@ MACOSX_DEPLOYMENT_TARGET='10.12' # default, can be changed via command line opt class OSXBuilder(Builder): + def getObjcTarget(self): + # Obj-C generation target + return 'osx' + def getToolchain(self, arch, target): return None