Merge pull request #18059 from komakai:improve-swift-docs

pull/17993/head
Alexander Alekhin 4 years ago
commit 949fe93d5a
  1. 20
      modules/core/misc/objc/common/Mat.h
  2. 17
      modules/objc/generator/gen_objc.py
  3. 15
      modules/objc/generator/templates/cmakelists.template
  4. 87
      platforms/ios/build_docs.py
  5. 30
      platforms/ios/build_framework.py
  6. 32
      platforms/osx/build_docs.py
  7. 4
      platforms/osx/build_framework.py

@ -23,7 +23,19 @@
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
/** /**
* The class Mat represents an n-dimensional dense numerical single-channel or multi-channel array. The class Mat represents an n-dimensional dense numerical single-channel or multi-channel array.
####Swift Example
```swift
let mat = Mat(rows: 2, cols: 3, type: CvType.CV_8U)
try! mat.put(row: 0, col: 0, data: [2, 3, 4, 4, 5, 6] as [Int8])
print("mat: \(mat.dump())")
```
####Objective-C Example
```objc
Mat* mat = [[Mat alloc] initWithRows:2 cols:3 type: CV_8U];
[m1 put:0 col:0 data:@[@2, @3, @4, @3, @4, @5]];
NSLog(@"mat: %@", [m1 dump]);
```
*/ */
CV_EXPORTS @interface Mat : NSObject CV_EXPORTS @interface Mat : NSObject
@ -40,6 +52,12 @@ CV_EXPORTS @interface Mat : NSObject
+ (instancetype)fromNativePtr:(cv::Ptr<cv::Mat>)nativePtr; + (instancetype)fromNativePtr:(cv::Ptr<cv::Mat>)nativePtr;
+ (instancetype)fromNative:(cv::Mat&)nativeRef; + (instancetype)fromNative:(cv::Mat&)nativeRef;
#endif #endif
/**
Creates a Mat object with the specified number of rows and columns and Mat type
@param rows Number of rows
@param cols Number of columns
@param type Mat type (refer: `CvType`)
*/
- (instancetype)initWithRows:(int)rows cols:(int)cols type:(int)type; - (instancetype)initWithRows:(int)rows cols:(int)cols type:(int)type;
- (instancetype)initWithRows:(int)rows cols:(int)cols type:(int)type data:(NSData*)data; - (instancetype)initWithRows:(int)rows cols:(int)cols type:(int)type data:(NSData*)data;
- (instancetype)initWithRows:(int)rows cols:(int)cols type:(int)type data:(NSData*)data step:(long)step; - (instancetype)initWithRows:(int)rows cols:(int)cols type:(int)type data:(NSData*)data step:(long)step;

@ -716,7 +716,7 @@ class ObjectiveCWrapperGenerator(object):
namespace = self.classes[cname].namespace if self.classes.has_key(cname) else "cv" namespace = self.classes[cname].namespace if self.classes.has_key(cname) else "cv"
return namespace.replace(".", "::") + "::" return namespace.replace(".", "::") + "::"
def gen(self, srcfiles, module, output_path, output_objc_path, common_headers): def gen(self, srcfiles, module, output_path, output_objc_path, common_headers, manual_classes):
self.clear() self.clear()
self.module = module self.module = module
self.Module = module.capitalize() self.Module = module.capitalize()
@ -751,6 +751,7 @@ class ObjectiveCWrapperGenerator(object):
self.add_enum(decl) self.add_enum(decl)
else: # function else: # function
self.add_func(decl) self.add_func(decl)
self.classes[self.Module].member_classes += manual_classes
logging.info("\n\n===== Generating... =====") logging.info("\n\n===== Generating... =====")
package_path = os.path.join(output_objc_path, module) package_path = os.path.join(output_objc_path, module)
@ -1243,6 +1244,7 @@ def copy_objc_files(objc_files_dir, objc_base_path, module_path, include = False
if (not os.path.exists(dest)) or (os.stat(src).st_mtime - os.stat(dest).st_mtime > 1): if (not os.path.exists(dest)) or (os.stat(src).st_mtime - os.stat(dest).st_mtime > 1):
copyfile(src, dest) copyfile(src, dest)
updated_files += 1 updated_files += 1
return objc_files
def unescape(str): def unescape(str):
return str.replace("&lt;", "<").replace("&gt;", ">").replace("&amp;", "&") return str.replace("&lt;", "<").replace("&gt;", ">").replace("&amp;", "&")
@ -1298,6 +1300,7 @@ def sanitize_documentation_string(doc, type):
.replace("@param[in]", "@param") \ .replace("@param[in]", "@param") \
.replace("@param[out]", "@param") \ .replace("@param[out]", "@param") \
.replace("@ref", "REF:") \ .replace("@ref", "REF:") \
.replace("@note", "NOTE:") \
.replace("@returns", "@return") \ .replace("@returns", "@return") \
.replace("@sa ", "@see ") \ .replace("@sa ", "@see ") \
.replace("@snippet", "SNIPPET:") \ .replace("@snippet", "SNIPPET:") \
@ -1422,12 +1425,14 @@ if __name__ == "__main__":
module_imports += gen_type_dict.get("module_imports", []) module_imports += gen_type_dict.get("module_imports", [])
objc_files_dir = os.path.join(misc_location, 'common') objc_files_dir = os.path.join(misc_location, 'common')
copied_files = []
if os.path.exists(objc_files_dir): if os.path.exists(objc_files_dir):
copy_objc_files(objc_files_dir, objc_base_path, module, True) copied_files += copy_objc_files(objc_files_dir, objc_base_path, module, True)
if args.target == 'ios': if args.target == 'ios':
ios_files_dir = os.path.join(misc_location, 'ios') ios_files_dir = os.path.join(misc_location, 'ios')
if os.path.exists(ios_files_dir): if os.path.exists(ios_files_dir):
copy_objc_files(ios_files_dir, objc_base_path, module, True) copied_files += copy_objc_files(ios_files_dir, objc_base_path, module, True)
objc_test_files_dir = os.path.join(misc_location, 'test') objc_test_files_dir = os.path.join(misc_location, 'test')
if os.path.exists(objc_test_files_dir): if os.path.exists(objc_test_files_dir):
@ -1436,8 +1441,12 @@ if __name__ == "__main__":
if os.path.exists(objc_test_resources_dir): if os.path.exists(objc_test_resources_dir):
copy_tree(objc_test_resources_dir, os.path.join(objc_test_base_path, 'test', 'resources')) copy_tree(objc_test_resources_dir, os.path.join(objc_test_base_path, 'test', 'resources'))
manual_classes = filter(lambda x:type_dict.has_key(x),
map(lambda x: x[x.rfind('/')+1:-2],
filter(lambda x: x.endswith('.h'), copied_files)))
if len(srcfiles) > 0: if len(srcfiles) > 0:
generator.gen(srcfiles, module, dstdir, objc_base_path, common_headers) generator.gen(srcfiles, module, dstdir, objc_base_path, common_headers, manual_classes)
else: else:
logging.info("No generated code for module: %s", module) logging.info("No generated code for module: %s", module)
generator.finalize(objc_base_path) generator.finalize(objc_base_path)

@ -42,18 +42,3 @@ set_target_properties(opencv_objc_framework PROPERTIES
PUBLIC_HEADER "$${objc_headers}" PUBLIC_HEADER "$${objc_headers}"
DEFINE_SYMBOL CVAPI_EXPORTS DEFINE_SYMBOL CVAPI_EXPORTS
) )
find_program(JAZZY jazzy)
if(JAZZY)
add_custom_command(
OUTPUT "$${BUILD_ROOT}/modules/objc/doc_build/doc/index.html"
COMMAND $${JAZZY} --objc --author OpenCV --author_url http://opencv.org --github_url https://github.com/opencv/opencv --umbrella-header "$${BUILD_ROOT}/lib/$${CMAKE_BUILD_TYPE}/$framework.framework/Headers/$framework.h" --framework-root "$${BUILD_ROOT}/lib/$${CMAKE_BUILD_TYPE}/$framework.framework" --module $framework --sdk iphonesimulator --undocumented-text \"\"
WORKING_DIRECTORY "$${BUILD_ROOT}/modules/objc/doc_build"
COMMENT "Generating Documentation"
)
add_custom_target(opencv_objc_doc ALL DEPENDS "$${BUILD_ROOT}/modules/objc/doc_build/doc/index.html")
add_dependencies(opencv_objc_doc opencv_objc_framework)
else()
message("jazzy not found - documentation will not be generated!")
endif()

@ -0,0 +1,87 @@
#!/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()

@ -19,6 +19,10 @@ Script will create <outputdir>, if it's missing, and a few its subdirectories:
[cmake-generated build tree for iOS simulator] [cmake-generated build tree for iOS simulator]
{framework_name}.framework/ {framework_name}.framework/
[the framework content] [the framework content]
samples/
[sample projects]
docs/
[documentation]
The script should handle minor OpenCV updates efficiently The script should handle minor OpenCV updates efficiently
- it does not recompile the library from scratch each time. - it does not recompile the library from scratch each time.
@ -58,7 +62,7 @@ def getXCodeSetting(var, projectdir):
raise Exception("Failed to parse Xcode settings") raise Exception("Failed to parse Xcode settings")
class Builder: class Builder:
def __init__(self, opencv, contrib, dynamic, bitcodedisabled, exclude, disable, enablenonfree, targets, debug, debug_info, framework_name): def __init__(self, opencv, contrib, dynamic, bitcodedisabled, exclude, disable, enablenonfree, targets, debug, debug_info, framework_name, run_tests, build_docs):
self.opencv = os.path.abspath(opencv) self.opencv = os.path.abspath(opencv)
self.contrib = None self.contrib = None
if contrib: if contrib:
@ -77,6 +81,8 @@ class Builder:
self.debug = debug self.debug = debug
self.debug_info = debug_info self.debug_info = debug_info
self.framework_name = framework_name self.framework_name = framework_name
self.run_tests = run_tests
self.build_docs = build_docs
def getBD(self, parent, t): def getBD(self, parent, t):
@ -128,8 +134,20 @@ class Builder:
self.makeDynamicLib(mainBD) self.makeDynamicLib(mainBD)
self.makeFramework(outdir, dirs) self.makeFramework(outdir, dirs)
if self.build_objc_wrapper: 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"])
else:
print("To run tests call:") 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/test")
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) self.copy_samples(outdir)
def build(self, outdir): def build(self, outdir):
@ -370,11 +388,6 @@ class Builder:
d = os.path.join(framework_dir, *l[1]) d = os.path.join(framework_dir, *l[1])
os.symlink(s, d) os.symlink(s, d)
doc_path = os.path.join(builddirs[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"))
def copy_samples(self, outdir): def copy_samples(self, outdir):
return return
@ -431,6 +444,9 @@ if __name__ == "__main__":
parser.add_argument('--debug_info', default=False, dest='debug_info', action='store_true', help='Build with debug information (useful for Release mode: BUILD_WITH_DEBUG_INFO=ON)') parser.add_argument('--debug_info', default=False, dest='debug_info', action='store_true', help='Build with debug information (useful for Release mode: BUILD_WITH_DEBUG_INFO=ON)')
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('--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('--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')
args = parser.parse_args() args = parser.parse_args()
os.environ['IPHONEOS_DEPLOYMENT_TARGET'] = args.iphoneos_deployment_target os.environ['IPHONEOS_DEPLOYMENT_TARGET'] = args.iphoneos_deployment_target
@ -451,6 +467,6 @@ if __name__ == "__main__":
[ [
(iphoneos_archs, "iPhoneOS"), (iphoneos_archs, "iPhoneOS"),
(iphonesimulator_archs, "iPhoneSimulator"), (iphonesimulator_archs, "iPhoneSimulator"),
], args.debug, args.debug_info, args.framework_name) ], args.debug, args.debug_info, args.framework_name, args.run_tests, args.build_docs)
b.build(args.out) b.build(args.out)

@ -0,0 +1,32 @@
#!/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()

@ -47,6 +47,8 @@ if __name__ == "__main__":
parser.add_argument('--debug_info', action='store_true', help='Build with debug information (useful for Release mode: BUILD_WITH_DEBUG_INFO=ON)') parser.add_argument('--debug_info', action='store_true', help='Build with debug information (useful for Release mode: BUILD_WITH_DEBUG_INFO=ON)')
parser.add_argument('--framework_name', default='opencv2', dest='framework_name', action='store_true', help='Name of OpenCV framework (default: opencv2, will change to OpenCV in future version)') parser.add_argument('--framework_name', default='opencv2', dest='framework_name', action='store_true', 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('--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')
args = parser.parse_args() args = parser.parse_args()
@ -60,5 +62,5 @@ if __name__ == "__main__":
b = OSXBuilder(args.opencv, args.contrib, False, False, args.without, args.disable, args.enablenonfree, b = OSXBuilder(args.opencv, args.contrib, False, False, args.without, args.disable, args.enablenonfree,
[ [
(["x86_64"], "MacOSX") (["x86_64"], "MacOSX")
], args.debug, args.debug_info, args.framework_name) ], args.debug, args.debug_info, args.framework_name, args.run_tests, args.build_docs)
b.build(args.out) b.build(args.out)

Loading…
Cancel
Save