1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-21 19:33:03 +04:00

Merge pull request #28704 from komakai:replace-jazzy-with-docc

Use Xcode DocC tool for buiding Apple platform docs + add quick-start tutorial #28704

### Pull Request Readiness Checklist

- [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
- [x] There is a reference to the original bug report and related work

This PR resolves https://github.com/opencv/opencv/issues/28702 and https://github.com/opencv/opencv/issues/28703
Additionally it resolves build issues on Apple platforms by making build scripts run with Python 3
This commit is contained in:
Giles Payne
2026-05-14 17:22:04 +09:00
committed by GitHub
parent eadaab5fbb
commit 7cc6d8576c
36 changed files with 359 additions and 176 deletions
+69
View File
@@ -0,0 +1,69 @@
This is the documentation for the iOS OpenCV framework.
## Quick Start Tutorial
Follow the steps below to get a simple "Hello World" app running on iOS
* Open Xcode and create a new project from the **File** > **New** > **Project...** menu
![New Project menu](common-create-project.png)
* From template selection dialog select the `iOS` tab and then select `App`
![Template selection dialog](ios-create-app.png)
* Enter a name for the project and set **Interface** to **Storyboard** and **Language** to **Swift**
![App settings dialog](ios-create-app-settings.png)
* Choose a location to save the project
![Choose project location dialog](common-create-app-location.png)
* Create a new folder `Framework`
![Create new folder menu](ios-create-framework-folder.png)
* In a Finder window copy the framework file to the newly created folder. The framework should appear in the project navigator as below. (Note: in this case the framework has extension `.framework` but depending on how you built it or from where you obtained it your framework may have extension `.xcframework`)
![Add framework](ios-add-framework.png)
* In the project navigator select the `ViewController.swift` file
![Select ViewController file](ios-select-viewcontroller.png)
* Add an `import` for the OpenCV framework below the existing import for `UIKit`
```swift, copy
import opencv2
```
* Replace the implementation of the `viewDidLoad` function with the following code
```swift, copy
override func viewDidLoad() {
super.viewDidLoad()
let white = Scalar(255, 255, 255, 255)
let black = Scalar(0, 0, 0, 255)
let m = Mat(rows: 400, cols: Int32(view.frame.width), type: CvType.CV_8UC4, scalar: white)
Imgproc.putText(img: m, text: "Hello World", org: Point(x: 10, y: 150), fontFace: HersheyFonts.FONT_HERSHEY_DUPLEX, fontScale: 2, color: black)
let image = m.toUIImage()
let imageView = UIImageView(image: image)
self.view.addSubview(imageView)
}
```
* The `ViewController.swift` file should now look like this
![Updated ViewController code](ios-add-opencv-code.png)
* Launch the app
![Launch app button](ios-launch-app.png)
* The app home screen should look like this
![App screenshot](ios-app-screenshot.png)
If that has all worked well then congratulations. If not then feel free to reach out to the OpenCV community for help. Also feel free to contribute fixes and improvements.
+85
View File
@@ -0,0 +1,85 @@
This is the documentation for the macOS OpenCV framework.
## Quick Start Tutorial
Follow the steps below to get a simple "Hello World" app running on macOS
* Open Xcode and create a new project from the **File** > **New** > **Project...** menu
![New Project menu](common-create-project.png)
* From the template selection dialog select the **macOS** tab and then select **App**
![Template selection dialog](macos-create-app.png)
* Enter a name for the project and set **Interface** to **Storyboard** and **Language** to **Swift**
![App settings dialog](macos-create-app-settings.png)
* Choose a location to save the project
![Choose project location dialog](common-create-app-location.png)
* Create a new folder `Framework`
![Create new folder menu](macos-create-framework-folder.png)
* In a Finder window copy the framework file to the newly created folder. The framework should appear in the project navigator as below. (Note: in this case the framework has extension `.framework` but depending on how you built it or from where you obtained it your framework may have extension `.xcframework`)
![Add framework](macos-add-framework.png)
* Select the project root in the project navigator and on the **General** tab locate the **Frameworks, Libraries and Embedded Content** section
![Frameworks, Libraries and Embedded Content](macos-add-dependencies-1.png)
* In the **Choose frameworks and libraries to add** dialog add the following
* OpenCL.framework
* liblapack.tbd
* libblas.tbd
![Choose frameworks and libraries to add](macos-add-dependencies-2.png)
* The **Frameworks, Libraries and Embedded Content** section should now look like this
![Choose frameworks and libraries to add](macos-add-dependencies-3.png)
* In the project navigator select the `ViewController.swift` file
![Select ViewController file](macos-select-viewcontroller.png)
* Add an `import` for the OpenCV framework below the existing import for `Cocoa`
```swift, copy
import opencv2
```
* Replace the implementation of the `viewDidLoad` function with the following code
```swift, copy
override func viewDidLoad() {
super.viewDidLoad()
let white = Scalar(255, 255, 255, 255)
let black = Scalar(0, 0, 0, 255)
let m = Mat(rows: 400, cols: Int32(view.frame.width), type: CvType.CV_8UC4, scalar: white)
Imgproc.putText(img: m, text: "Hello World", org: Point(x: 10, y: 250), fontFace: HersheyFonts.FONT_HERSHEY_DUPLEX, fontScale: 2, color: black)
let image = m.toNSImage()
let imageView = NSImageView(frame: NSRect(x: 0, y: 0, width: Int(m.cols()), height: Int(m.rows())))
imageView.image = image
self.view.addSubview(imageView)
}
```
* The `ViewController.swift` file should now look like this
![Updated ViewController code](macos-add-opencv-code.png)
* Launch the app
![Launch app button](macos-launch-app.png)
* The app home screen should look like this
![App screenshot](macos-app-screenshot.png)
If that has all worked well then congratulations. If not then feel free to reach out to the OpenCV community for help. Also feel free to contribute fixes and improvements.
-13
View File
@@ -1,13 +0,0 @@
## About
This is the documentation for the Objective-C/Swift OpenCV wrapper
To get started: add the OpenCV framework to your project and add the following to your source
###Objective-C
#import <OpenCV/OpenCV.h>
###Swift
import OpenCV
Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

+64 -15
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
from __future__ import print_function, unicode_literals
import sys, re, os.path, errno, fnmatch
@@ -100,6 +100,9 @@ method_dict = {
("Mat", "dot") : "-dot:"
}
enum_value_lookup = {}
enums = set()
modules = []
@@ -708,12 +711,25 @@ def see_lookup(objc_class, see):
if (see_class, see_method) in method_dict:
method = method_dict[(see_class, see_method)]
if see_class == objc_class:
return method
return "``{}``".format(method[1:])
else:
return ("-" if method[0] == "-" else "") + "[" + see_class + " " + method[1:] + "]"
return "``{}/{}``".format(see_class, method[1:])
else:
return see
def hash_link(link_text, objc_class, function_name):
if link_text == function_name:
return "*{}*".format(link_text) # make bold
elif link_text in enums:
return "``{}``".format(link_text)
elif link_text in enum_value_lookup:
return "``{}/{}``".format(enum_value_lookup[link_text], link_text)
elif (objc_class, link_text) in method_dict:
return "``{}/{}``".format(objc_class, link_text)
elif ("Core", link_text) in method_dict:
return "``Core/{}``".format(link_text)
else:
return "#{}".format(link_text) # leave as is
class ObjectiveCWrapperGenerator(object):
def __init__(self):
@@ -1115,13 +1131,16 @@ class ObjectiveCWrapperGenerator(object):
if fi.docstring:
lines = fi.docstring.splitlines()
toWrite = []
last_line_was_param = False
for index, line in enumerate(lines):
line = re.sub(r"([(\s])#(.+?)([).,\s]|$)", lambda x: x.group(1) + hash_link(x.group(2), ci.objc_name, fi.name) + x.group(3), line)
p0 = line.find("@param")
if p0 != -1:
p0 += 7 # len("@param" + 1)
p1 = line.find(' ', p0)
p1 = len(line) if p1 == -1 else p1
name = line[p0:p1]
last_line_was_param = True
for arg in args:
if arg.name == name:
toWrite.append(re.sub(r'\*\s*@param ', '* @param ', line))
@@ -1129,10 +1148,14 @@ class ObjectiveCWrapperGenerator(object):
else:
s0 = line.find("@see")
if s0 != -1:
if last_line_was_param:
# separate from @param lines
toWrite.append(" *")
sees = line[(s0 + 5):].split(",")
toWrite.append(line[:(s0 + 5)] + ", ".join(["`" + see_lookup(ci.objc_name, see.strip()) + "`" for see in sees]))
toWrite.append(line[:s0] + "- SeeAlso " + ", ".join([see_lookup(ci.objc_name, see.strip()) for see in sees]))
else:
toWrite.append(line)
last_line_was_param = False
for line in toWrite:
method_declarations.write(line + "\n")
@@ -1317,6 +1340,10 @@ $unrefined_call$epilogue$ret
if ci.cname in enum_fix:
typeNameShort = enum_fix[ci.cname].get(typeNameShort, typeNameShort)
enums.add(typeNameShort)
for c in consts:
enum_value_lookup[c.name] = typeNameShort
ci.enum_declarations.write("""
// C++: enum {1} ({2})
typedef NS_ENUM(int, {1}) {{
@@ -1462,16 +1489,37 @@ typedef NS_ENUM(int, {1}) {{
self.save(opencv_modulemap_file, opencv_modulemap)
available_modules = " ".join(["-DAVAILABLE_" + m['name'].upper() for m in config['modules']])
cmakelist_template = read_contents(os.path.join(SCRIPT_DIR, 'templates/cmakelists.template'))
cmakelist = Template(cmakelist_template).substitute(modules = ";".join(modules), framework = framework_name, objc_target=objc_target, module_availability_defines=available_modules)
doc_catalog = output_objc_path + "/../Documentation.docc"
doc_resources = os.path.join(doc_catalog, "Resources")
cmakelist = Template(cmakelist_template).substitute(
modules = ";".join(modules),
framework = framework_name,
objc_target = objc_target,
module_availability_defines = available_modules,
)
self.save(os.path.join(dstdir, "CMakeLists.txt"), cmakelist)
mkdir_p(doc_resources)
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(os.path.join(output_objc_build_path, "doc_build/README.md"), "w") as readme_out:
readme_out.write(readme_body)
landing_page_body_in = ""
if objc_target == "ios" or objc_target == "osx":
with open(os.path.join(SCRIPT_DIR, '../doc/LandingPage_' + objc_target + '.md')) as landing_page_in:
landing_page_body_in = landing_page_in.read()
landing_page_body_out = "# ``" + framework_name + "`` Framework\n\n" + landing_page_body_in
landing_page_body_out += "\n\n\n## Modules\n\n" + ", ".join(["``" + m.capitalize() + "``" for m in modules]) + "\n"
with open(os.path.join(doc_catalog, "LandingPage.md"), "w") as landing_page_out:
landing_page_out.write(landing_page_body_out)
for m in config['modules']:
pic_files = os.path.join(ROOT_DIR, m['location'], 'doc', 'pics')
if os.path.exists(pic_files):
copy_tree(pic_files, doc_resources)
if objc_target == "ios":
copy_tree(os.path.join(SCRIPT_DIR, '../doc/pics/ios'), doc_resources)
if objc_target == "osx":
copy_tree(os.path.join(SCRIPT_DIR, '../doc/pics/osx'), doc_resources)
if objc_target == "ios" or objc_target == "osx":
copy_tree(os.path.join(SCRIPT_DIR, '../doc/pics/common'), doc_resources)
if framework_name != "OpenCV":
for dirname, dirs, files in os.walk(os.path.join(testdir, "test")):
if dirname.endswith('/resources'):
@@ -1541,10 +1589,11 @@ def sanitize_documentation_string(doc, type):
doc = re.sub(re.compile('\\\\f\\$(.*?)\\\\f\\$', re.DOTALL), lambda x: '`$$' + fix_tex(x.group(1)) + '$$`', doc)
doc = re.sub(re.compile('\\\\f\\[(.*?)\\\\f\\]', re.DOTALL), lambda x: '`$$' + fix_tex(x.group(1)) + '$$`', doc)
doc = re.sub(re.compile('\\\\f\\{(.*?)\\\\f\\}', re.DOTALL), lambda x: '`$$' + fix_tex(x.group(1)) + '$$`', doc)
doc = re.sub(r"!\[(.*)]\((?:.*/)*(.+)\.(?:png|jpg|svg)\)", "![\\1](\\2)", doc)
doc = doc.replace("@anchor", "") \
.replace("@brief ", "").replace("\\brief ", "") \
.replace("@cite", "CITE:") \
.replace("@cite", "*Cite:*") \
.replace("@code{.cpp}", "<code>") \
.replace("@code{.txt}", "<code>") \
.replace("@code", "<code>") \
@@ -1556,14 +1605,14 @@ def sanitize_documentation_string(doc, type):
.replace("@endcode", "</code>") \
.replace("@endinternal", "") \
.replace("@file", "") \
.replace("@include", "INCLUDE:") \
.replace("@include", "Include:") \
.replace("@ingroup", "") \
.replace("@internal", "") \
.replace("@overload", "") \
.replace("@param[in]", "@param") \
.replace("@param[out]", "@param") \
.replace("@ref", "REF:") \
.replace("@note", "NOTE:") \
.replace("@ref", "*Ref:*") \
.replace("@note", "*Note:*") \
.replace("@returns", "@return") \
.replace("@sa ", "@see ") \
.replace("@snippet", "SNIPPET:") \
@@ -22,7 +22,14 @@ else()
endif()
file(GLOB_RECURSE objc_headers "*\.h")
add_library($framework STATIC $${objc_sources})
set_source_files_properties(Documentation.docc PROPERTIES
XCODE_LAST_KNOWN_FILE_TYPE folder.documentationcatalog
)
add_library($framework STATIC
$${objc_sources}
Documentation.docc
)
set_target_properties($framework PROPERTIES LINKER_LANGUAGE CXX)
@@ -0,0 +1,11 @@
# $framework Documentation - How To View
## View Locally
* Start a web server in the directory where this HowTo file is located. For example ``python3 -m http.server 8000``
* In a browser navigate to URL http://127.0.0.1:8000/$hosting_base_path/documentation/$framework/
## Deploying to a server
* Upload the content of the directory where this HowTo file is located to the document root of the web server maintaining the same folder hierarchy
* In a browser navigate to URL https://{server-domain}/$hosting_base_path/documentation/$framework/
+26
View File
@@ -82,6 +82,7 @@ if __name__ == "__main__":
visionos_script_path = os.path.abspath(os.path.abspath(os.path.dirname(__file__))+'/../ios/build_visionos_framework.py')
build_folders = []
docs_build_folder_dict = {}
def get_or_create_build_folder(base_dir, platform):
build_folder = "{}/{}".format(base_dir, platform).replace(" ", "\\ ") # Escape spaces in output path
@@ -91,6 +92,7 @@ if __name__ == "__main__":
if iphoneos_archs:
build_folder = get_or_create_build_folder(args.out, "iphoneos")
build_folders.append(build_folder)
docs_build_folder_dict["ios"] = build_folder
command = ["python3", ios_script_path, build_folder, "--iphoneos_archs", iphoneos_archs, "--framework_name", args.framework_name, "--build_only_specified_archs"] + unknown_args
print_header("Building iPhoneOS frameworks")
print(command)
@@ -98,12 +100,15 @@ if __name__ == "__main__":
if iphonesimulator_archs:
build_folder = get_or_create_build_folder(args.out, "iphonesimulator")
build_folders.append(build_folder)
if not iphoneos_archs:
docs_build_folder_dict["ios"] = build_folder
command = ["python3", ios_script_path, build_folder, "--iphonesimulator_archs", iphonesimulator_archs, "--framework_name", args.framework_name, "--build_only_specified_archs"] + unknown_args
print_header("Building iPhoneSimulator frameworks")
execute(command, cwd=os.getcwd())
if visionos_archs:
build_folder = get_or_create_build_folder(args.out, "visionos")
build_folders.append(build_folder)
docs_build_folder_dict["visionos"] = build_folder
command = ["python3", visionos_script_path, build_folder, "--visionos_archs", visionos_archs, "--framework_name", args.framework_name, "--build_only_specified_archs"] + unknown_args
print_header("Building visionOS frameworks")
print(command)
@@ -111,18 +116,22 @@ if __name__ == "__main__":
if visionsimulator_archs:
build_folder = get_or_create_build_folder(args.out, "visionsimulator")
build_folders.append(build_folder)
if not visionos_archs:
docs_build_folder_dict["visionos"] = build_folder
command = ["python3", visionos_script_path, build_folder, "--visionsimulator_archs", visionsimulator_archs, "--framework_name", args.framework_name, "--build_only_specified_archs"] + unknown_args
print_header("Building visionSimulator frameworks")
execute(command, cwd=os.getcwd())
if macos_archs:
build_folder = get_or_create_build_folder(args.out, "macos")
build_folders.append(build_folder)
docs_build_folder_dict["macos"] = build_folder
command = ["python3", osx_script_path, build_folder, "--macos_archs", macos_archs, "--framework_name", args.framework_name, "--build_only_specified_archs"] + unknown_args
print_header("Building MacOS frameworks")
execute(command, cwd=os.getcwd())
if catalyst_archs:
build_folder = get_or_create_build_folder(args.out, "catalyst")
build_folders.append(build_folder)
docs_build_folder_dict["catalyst"] = build_folder
command = ["python3", osx_script_path, build_folder, "--catalyst_archs", catalyst_archs, "--framework_name", args.framework_name, "--build_only_specified_archs"] + unknown_args
print_header("Building Catalyst frameworks")
execute(command, cwd=os.getcwd())
@@ -150,6 +159,23 @@ if __name__ == "__main__":
print("")
print_header("Finished building {}".format(xcframework_path))
# Phase 3: copy documentation
print_header("Copying documentation")
for platform, build_folder in docs_build_folder_dict.items():
docs_src = "{}/docs".format(build_folder)
docs_dst = "{}/docs_{}".format(args.out, platform)
# Remove the docs folder if it exists
with contextlib.suppress(FileNotFoundError):
shutil.rmtree(docs_dst)
print("Removed existing documentation at {}".format(docs_dst))
shutil.copytree(docs_src, docs_dst)
print("")
print_header("Finished copying documentation")
except Exception as e:
print_error(e)
traceback.print_exc(file=sys.stderr)
+14
View File
@@ -63,3 +63,17 @@ def get_cmake_version():
return (int(m.group(1)), int(m.group(2)), int(m.group(3)))
else:
raise Exception("Failed to parse CMake version")
def get_current_branch(opencv_dir):
ret = check_output(["git", "branch", "--show-current"], cwd = opencv_dir).decode('utf-8').strip()
if ret != "":
return ret
else:
raise Exception("Failed to get current branch")
def find_directory(base_dir, search_dir):
dirs = check_output(["find", base_dir, "-type", "d", "-name", search_dir]).decode('utf-8').splitlines()
if dirs and len(dirs) > 0:
return dirs[0]
else:
raise Exception("Failed to find directory: " + search_dir)
-87
View File
@@ -1,87 +0,0 @@
#!/usr/bin/env python
"""
This script builds OpenCV docs for iOS.
"""
from __future__ import print_function
import os, sys, multiprocessing, argparse, traceback
from subprocess import check_call, check_output, CalledProcessError, Popen
def execute(cmd, cwd = None, output = None):
if not output:
print("Executing: %s in %s" % (cmd, cwd), file=sys.stderr)
print('Executing: ' + ' '.join(cmd))
retcode = check_call(cmd, cwd = cwd)
if retcode != 0:
raise Exception("Child returned:", retcode)
else:
with open(output, "a") as f:
f.flush()
p = Popen(cmd, cwd = cwd, stdout = f)
os.waitpid(p.pid, 0)
class DocBuilder:
def __init__(self, script_dir, framework_dir, output_dir, framework_header, framework_name, arch, target):
self.script_dir = script_dir
self.framework_dir = framework_dir
self.output_dir = output_dir
self.framework_header = framework_header
self.framework_name = framework_name
self.arch = arch
self.target = target
def _build(self):
if not os.path.isdir(self.output_dir):
os.makedirs(self.output_dir)
self.buildDocs()
def build(self):
try:
self._build()
except Exception as e:
print("="*60, file=sys.stderr)
print("ERROR: %s" % e, file=sys.stderr)
print("="*60, file=sys.stderr)
traceback.print_exc(file=sys.stderr)
sys.exit(1)
def getToolchain(self):
return None
def getSourceKitten(self):
ret = check_output(["gem", "which", "jazzy"])
if ret.find('ERROR:') == 0:
raise Exception("Failed to find jazzy")
else:
return os.path.join(ret[0:ret.rfind('/')], '../bin/sourcekitten')
def buildDocs(self):
sourceKitten = self.getSourceKitten()
sourceKittenSwiftDoc = [sourceKitten, "doc", "--module-name", self.framework_name, "--", "-project", self.framework_name + ".xcodeproj", "ARCHS=" + self.arch, "-sdk", self.target, "-configuration", "Release", "-parallelizeTargets", "-jobs", str(multiprocessing.cpu_count()), "-target", "opencv_objc_framework"]
execute(sourceKittenSwiftDoc, cwd = self.framework_dir, output = os.path.join(self.output_dir, "swiftDoc.json"))
sdk_dir = check_output(["xcrun", "--show-sdk-path", "--sdk", self.target]).rstrip()
sourceKittenObjcDoc = [sourceKitten, "doc", "--objc", self.framework_header, "--", "-x", "objective-c", "-isysroot", sdk_dir, "-fmodules"]
print(sourceKittenObjcDoc)
execute(sourceKittenObjcDoc, cwd = self.framework_dir, output = os.path.join(self.output_dir, "objcDoc.json"))
execute(["jazzy", "--author", "OpenCV", "--author_url", "http://opencv.org", "--github_url", "https://github.com/opencv/opencv", "--module", self.framework_name, "--undocumented-text", "\"\"", "--sourcekitten-sourcefile", "swiftDoc.json,objcDoc.json"], cwd = self.output_dir)
class iOSDocBuilder(DocBuilder):
def getToolchain(self):
return None
if __name__ == "__main__":
script_dir = os.path.abspath(os.path.dirname(sys.argv[0]))
parser = argparse.ArgumentParser(description='The script builds OpenCV docs for iOS.')
parser.add_argument('framework_dir', metavar='FRAMEWORK_DIR', help='folder where framework build files are located')
parser.add_argument('--output_dir', default=None, help='folder where docs will be built (default is "../doc_build" relative to framework_dir)')
parser.add_argument('--framework_header', default=None, help='umbrella header for OpenCV framework (default is "../../../lib/Release/{framework_name}.framework/Headers/{framework_name}.h")')
parser.add_argument('--framework_name', default='opencv2', help='Name of OpenCV framework (default: opencv2, will change to OpenCV in future version)')
args = parser.parse_args()
arch = "x86_64"
target = "iphonesimulator"
b = iOSDocBuilder(script_dir, args.framework_dir, args.output_dir if args.output_dir else os.path.join(args.framework_dir, "../doc_build"), args.framework_header if args.framework_header else os.path.join(args.framework_dir, "../../../lib/Release/" + args.framework_name + ".framework/Headers/" + args.framework_name + ".h"), args.framework_name, arch, target)
b.build()
+74 -20
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
"""
The script builds OpenCV.framework for iOS.
The built framework is universal, it can be used to build app and run it on either iOS simulator or real device.
@@ -42,7 +42,7 @@ else:
from distutils.dir_util import copy_tree
sys.path.insert(0, os.path.abspath(os.path.abspath(os.path.dirname(__file__))+'/../apple'))
from cv_build_utils import execute, print_error, get_xcode_major, get_xcode_setting, get_xcode_version, get_cmake_version
from cv_build_utils import execute, print_error, get_xcode_major, get_xcode_setting, get_xcode_version, get_cmake_version, get_current_branch, find_directory
IPHONEOS_DEPLOYMENT_TARGET='9.0' # default, can be changed via command line options or environment variable
@@ -50,7 +50,7 @@ CURRENT_FILE_DIR = os.path.dirname(__file__)
class Builder:
def __init__(self, opencv, contrib, dynamic, exclude, disable, enablenonfree, targets, debug, debug_info, framework_name, run_tests, build_docs, swiftdisabled):
def __init__(self, opencv, contrib, dynamic, exclude, disable, enablenonfree, targets, debug, debug_info, framework_name, run_tests, hosting_base_path, swiftdisabled):
self.opencv = os.path.abspath(opencv)
self.contrib = None
if contrib:
@@ -69,8 +69,15 @@ class Builder:
self.debug_info = debug_info
self.framework_name = framework_name
self.run_tests = run_tests
self.build_docs = build_docs
if hosting_base_path is None:
current_branch = get_current_branch(self.opencv)
objc_target = self.getObjcTarget(self.targets[0][1])
self.hosting_base_path = os.path.join(current_branch, "macos" if objc_target == "osx" else objc_target)
else:
self.hosting_base_path = hosting_base_path
self.swiftdisabled = swiftdisabled
self.docs_built = False
self.build_docs = False
def checkCMakeVersion(self):
if get_xcode_version() >= (12, 2):
@@ -95,12 +102,17 @@ class Builder:
dirs = []
xcode_ver = get_xcode_major()
xcode_supports_ios_32bit_arch = xcode_ver <= 13
self.build_docs = xcode_ver >= 13
# build each architecture separately
alltargets = []
for target_group in self.targets:
for arch in target_group[0]:
if arch in ["armv7", "armv7s", "i386"] and not xcode_supports_ios_32bit_arch:
print("Skipping unsupported architecture: " + arch)
continue
current = ( arch, target_group[1] )
alltargets.append(current)
@@ -149,20 +161,20 @@ class Builder:
self.makeFramework(outdir, dirs)
if self.build_objc_wrapper:
doc_output = os.path.join(outdir, "docs")
if os.path.exists(doc_output):
shutil.rmtree(doc_output)
doc_build_path = os.path.join(dirs[0], "lib", self.getConfiguration(), "docs")
if os.path.exists(doc_build_path):
copy_tree(doc_build_path, doc_output)
else:
print("Documentation not found at: " + doc_build_path);
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_bindings_generator/{}/test".format(self.getObjcTarget(target[1]))])
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_bindings_generator/{}/test".format(self.getObjcTarget(target[1])))
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")
if os.path.exists(doc_path):
shutil.copytree(doc_path, os.path.join(outdir, "docs"))
shutil.copyfile(os.path.join(self.opencv, "doc", "opencv.ico"), os.path.join(outdir, "docs", "favicon.ico"))
else:
print("To build docs call:")
print(sys.argv[0].replace("build_framework", "build_docs") + " " + dirs[0] + "/modules/objc/framework_build")
self.copy_samples(outdir)
if self.swiftdisabled:
swift_sources_dir = os.path.join(outdir, "SwiftSources")
@@ -245,6 +257,31 @@ class Builder:
return buildcmd
def getDocBuildCommand(self, base_build_dir, source_dir, framework_build_dir, docs_dir):
output_dir = docs_dir if self.hosting_base_path == "" else os.path.join(docs_dir, self.hosting_base_path)
if not os.path.exists(output_dir):
os.makedirs(output_dir)
symbol_graph_dir = find_directory(os.path.join(framework_build_dir, "build", self.framework_name + ".build"), "symbol-graph")
doc_buildcmd = [
"xcrun",
"docc",
"convert",
"--emit-lmdb-index",
"--fallback-display-name", self.framework_name,
"--fallback-bundle-identifier", "org.opencv." + self.framework_name,
"--fallback-bundle-version", "1",
"--output-dir", output_dir,
"--transform-for-static-hosting",
"--ide-console-output",
os.path.join(source_dir, "Documentation.docc"),
"--additional-symbol-graph-dir", symbol_graph_dir
]
if self.hosting_base_path != "":
doc_buildcmd += ["--hosting-base-path", self.hosting_base_path]
return doc_buildcmd
def getInfoPlist(self, builddirs):
return os.path.join(builddirs[0], "ios", "Info.plist")
@@ -320,16 +357,33 @@ 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_bindings_generator/{}/gen".format(self.getObjcTarget(target)), cmakeargs)
objc_source_dir = builddir + "/modules/objc_bindings_generator/{}/gen".format(self.getObjcTarget(target))
cmakecmd = self.makeCMakeCmd(arch, target, objc_source_dir, cmakeargs)
if self.swiftdisabled:
cmakecmd.append("-DSWIFT_DISABLED=1")
cmakecmd.append("-DBUILD_ROOT=%s" % builddir)
cmakecmd.append("-DCMAKE_INSTALL_NAME_TOOL=install_name_tool")
cmakecmd.append("--no-warn-unused-cli")
execute(cmakecmd, cwd = builddir + "/modules/objc/framework_build")
execute(buildcmd + ["-target", "ALL_BUILD", "build"], cwd = builddir + "/modules/objc/framework_build")
execute(["cmake", "-DBUILD_TYPE=%s" % self.getConfiguration(), "-DCMAKE_INSTALL_PREFIX=%s" % (builddir + "/install"), "-P", "cmake_install.cmake"], cwd = builddir + "/modules/objc/framework_build")
framework_build_dir = builddir + "/modules/objc/framework_build"
execute(cmakecmd, cwd = framework_build_dir)
execute(buildcmd + ["-target", "ALL_BUILD", "build"], cwd = framework_build_dir)
if self.build_docs and not self.docs_built:
# build the syntax graphs
execute(buildcmd + ["-target", "ALL_BUILD", "docbuild"], cwd = framework_build_dir)
# build the document catalog
docs_dir = os.path.join(builddir, "lib", self.getConfiguration(), "docs")
doc_buildcmd2 = self.getDocBuildCommand(builddir, objc_source_dir, framework_build_dir, docs_dir)
execute(doc_buildcmd2, cwd = objc_source_dir)
with open(os.path.join(self.opencv, "modules", "objc", "generator", "templates", "doc_howto.template"), "r") as f:
howto_template = f.read()
howto = string.Template(howto_template).substitute(
framework = self.framework_name,
hosting_base_path = self.hosting_base_path
)
with codecs.open(os.path.join(docs_dir, "HOWTO.md"), "w", "utf-8") as file:
file.write(howto)
self.docs_built = True
execute(["cmake", "-DBUILD_TYPE=%s" % self.getConfiguration(), "-DCMAKE_INSTALL_PREFIX=%s" % (builddir + "/install"), "-P", "cmake_install.cmake"], cwd = framework_build_dir)
def mergeLibs(self, builddir):
res = os.path.join(builddir, "lib", self.getConfiguration(), "libopencv_merged.a")
@@ -540,7 +594,7 @@ if __name__ == "__main__":
parser.add_argument('--framework_name', default='opencv2', dest='framework_name', help='Name of OpenCV framework (default: opencv2, will change to OpenCV in future version)')
parser.add_argument('--legacy_build', default=False, dest='legacy_build', action='store_true', help='Build legacy opencv2 framework (default: False, equivalent to "--framework_name=opencv2 --without=objc")')
parser.add_argument('--run_tests', default=False, dest='run_tests', action='store_true', help='Run tests')
parser.add_argument('--build_docs', default=False, dest='build_docs', action='store_true', help='Build docs')
parser.add_argument('--doc_hosting_base_path', default=None, dest='hosting_base_path', action='store_true', help='Documentation hosting base path')
parser.add_argument('--disable-swift', default=False, dest='swiftdisabled', action='store_true', help='Disable building of Swift extensions')
args, unknown_args = parser.parse_known_args()
@@ -595,6 +649,6 @@ if __name__ == "__main__":
if iphonesimulator_archs:
targets.append((iphonesimulator_archs, "iPhoneSimulator"))
b = iOSBuilder(args.opencv, args.contrib, args.dynamic, args.without, args.disable, args.enablenonfree, targets, args.debug, args.debug_info, args.framework_name, args.run_tests, args.build_docs, args.swiftdisabled)
b = iOSBuilder(args.opencv, args.contrib, args.dynamic, args.without, args.disable, args.enablenonfree, targets, args.debug, args.debug_info, args.framework_name, args.run_tests, args.hosting_base_path, args.swiftdisabled)
b.build(args.out)
+3 -3
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
"""
The script builds OpenCV.framework for visionOS.
"""
@@ -69,7 +69,7 @@ if __name__ == "__main__":
parser.add_argument('--framework_name', default='opencv2', dest='framework_name', help='Name of OpenCV framework (default: opencv2, will change to OpenCV in future version)')
parser.add_argument('--legacy_build', default=False, dest='legacy_build', action='store_true', help='Build legacy framework (default: False, equivalent to "--framework_name=opencv2 --without=objc")')
parser.add_argument('--run_tests', default=False, dest='run_tests', action='store_true', help='Run tests')
parser.add_argument('--build_docs', default=False, dest='build_docs', action='store_true', help='Build docs')
parser.add_argument('--doc_hosting_base_path', default=None, dest='hosting_base_path', action='store_true', help='Documentation hosting base path')
parser.add_argument('--disable-swift', default=False, dest='swiftdisabled', action='store_true', help='Disable building of Swift extensions')
args, unknown_args = parser.parse_known_args()
@@ -112,5 +112,5 @@ if __name__ == "__main__":
if visionsimulator_archs:
targets.append((visionsimulator_archs, "XRSimulator")),
b = visionOSBuilder(args.opencv, args.contrib, args.dynamic, args.without, args.disable, args.enablenonfree, targets, args.debug, args.debug_info, args.framework_name, args.run_tests, args.build_docs, args.swiftdisabled)
b = visionOSBuilder(args.opencv, args.contrib, args.dynamic, args.without, args.disable, args.enablenonfree, targets, args.debug, args.debug_info, args.framework_name, args.run_tests, args.hosting_base_path, args.swiftdisabled)
b.build(args.out)
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
"""
This script runs OpenCV.framework tests for iOS.
"""
-32
View File
@@ -1,32 +0,0 @@
#!/usr/bin/env python
"""
This script builds OpenCV docs for macOS.
"""
from __future__ import print_function
import os, sys, multiprocessing, argparse, traceback
from subprocess import check_call, check_output, CalledProcessError, Popen
# import common code
sys.path.insert(0, os.path.abspath(os.path.abspath(os.path.dirname(__file__))+'/../ios'))
from build_docs import DocBuilder
class OSXDocBuilder(DocBuilder):
def getToolchain(self):
return None
if __name__ == "__main__":
script_dir = os.path.abspath(os.path.dirname(sys.argv[0]))
parser = argparse.ArgumentParser(description='The script builds OpenCV docs for macOS.')
parser.add_argument('framework_dir', metavar='FRAMEWORK_DIR', help='folder where framework build files are located')
parser.add_argument('--output_dir', default=None, help='folder where docs will be built (default is "../doc_build" relative to framework_dir)')
parser.add_argument('--framework_header', default=None, help='umbrella header for OpenCV framework (default is "../../../lib/Release/{framework_name}.framework/Headers/{framework_name}.h")')
parser.add_argument('--framework_name', default='opencv2', help='Name of OpenCV framework (default: opencv2, will change to OpenCV in future version)')
args = parser.parse_args()
arch = "x86_64"
target = "macosx"
b = OSXDocBuilder(script_dir, args.framework_dir, args.output_dir if args.output_dir else os.path.join(args.framework_dir, "../doc_build"), args.framework_header if args.framework_header else os.path.join(args.framework_dir, "../../../lib/Release/" + args.framework_name + ".framework/Headers/" + args.framework_name + ".h"), args.framework_name, arch, target)
b.build()
+3 -3
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
"""
The script builds OpenCV.framework for OSX.
"""
@@ -76,7 +76,7 @@ if __name__ == "__main__":
parser.add_argument('--framework_name', default='opencv2', dest='framework_name', help='Name of OpenCV framework (default: opencv2, will change to OpenCV in future version)')
parser.add_argument('--legacy_build', default=False, dest='legacy_build', action='store_true', help='Build legacy framework (default: False, equivalent to "--framework_name=opencv2 --without=objc")')
parser.add_argument('--run_tests', default=False, dest='run_tests', action='store_true', help='Run tests')
parser.add_argument('--build_docs', default=False, dest='build_docs', action='store_true', help='Build docs')
parser.add_argument('--doc_hosting_base_path', default=None, dest='hosting_base_path', action='store_true', help='Documentation hosting base path')
parser.add_argument('--disable-swift', default=False, dest='swiftdisabled', action='store_true', help='Disable building of Swift extensions')
args, unknown_args = parser.parse_known_args()
@@ -128,5 +128,5 @@ if __name__ == "__main__":
if catalyst_archs:
targets.append((catalyst_archs, "Catalyst")),
b = OSXBuilder(args.opencv, args.contrib, args.dynamic, args.without, args.disable, args.enablenonfree, targets, args.debug, args.debug_info, args.framework_name, args.run_tests, args.build_docs, args.swiftdisabled)
b = OSXBuilder(args.opencv, args.contrib, args.dynamic, args.without, args.disable, args.enablenonfree, targets, args.debug, args.debug_info, args.framework_name, args.run_tests, args.hosting_base_path, args.swiftdisabled)
b.build(args.out)
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
"""
This script runs OpenCV.framework tests for OSX.
"""