Commit f4064e72 authored by Miro Knejp's avatar Miro Knejp Committed by Jacob Potter

Allow the separation of public and private Objective-C files

The new options --objc-private-out and --objc-include-private-prefix control where the private Objective-C[++] files (i.e. +Private.h and .mm) are placed and which prefix is used to #import or #include them. This allows a clean separation of the public interface and the hidden private parts.

As a bonus this fixes the bug where the Objective-C generator was completely ignoring the --objc-include-prefix and --objc-include-cpp-prefix options.
parent 86e6e0d3
......@@ -158,19 +158,24 @@ you'll need to add calls to your own `JNI_OnLoad` and `JNI_OnUnload` functions.
##### Includes & Build Target
Generated file for Objective-C / C++ is as follows (assuming prefix is `DB`):
| Type | C++ header | C++ source | Objective-C header | Objective-C source |
|----------------|------------------------|----------------------------|----------------------------------|---------------------------|
| Enum | my\_enum.hpp | | DBMyEnum.h | DBMyEnumTranslator.mm |
| | | | DBMyEnumTranslator+Private.h | |
| Record | my\_record[\_base].hpp | my\_record[\_base].cpp (+) | DBMyRecord[Base].h | DBMyRecord[Base].mm |
| | | | DBMyRecord[Base]+Private.h | |
| Interface `+c` | my\_interface.hpp | my\_interface.cpp (+) | DBMyInterface.h | DBMyInterfaceCppProxy.mm |
| | | | DBMyInterfaceCppProxy+Private.h | |
| Interface `+o` | my\_interface.hpp | my\_interface.cpp (+) | DBMyInterface.h | DBMyInterfaceObjcProxy.mm |
| | | | DBMyInterfaceObjcProxy+Private.h | |
| Type | C++ header | C++ source | Objective-C header | Objective-C source |
|----------------|------------------------|----------------------------|--------------------------------------------|-------------------------------------|
| Enum | my\_enum.hpp | | ` public/`DBMyEnum.h | `private/`DBMyEnumTranslator.mm |
| | | | `private/`DBMyEnumTranslator+Private.h | |
| Record | my\_record[\_base].hpp | my\_record[\_base].cpp (+) | ` public/`DBMyRecord[Base].h | `private/`DBMyRecord[Base].mm |
| | | | `private/`DBMyRecord[Base]+Private.h | |
| Interface `+c` | my\_interface.hpp | my\_interface.cpp (+) | ` public/`DBMyInterface.h | `private/`DBMyInterfaceCppProxy.mm |
| | | | ` public/`DBMyInterfaceCppProxy.h | |
| | | | `private/`DBMyInterfaceCppProxy+Private.h | |
| Interface `+o` | my\_interface.hpp | my\_interface.cpp (+) | ` public/`DBMyInterface.h | `private/`DBMyInterfaceObjcProxy.mm |
| | | | `private/`DBMyInterfaceObjcProxy+Private.h | |
(+) Generated only for types that contain constants.
The folders `public` and `private` correspond to the options `--objc-out` and `--objc-private-out`
respecitvely, allowing you to isolate implementation headers and sources from the public files
exposed to Objective-C clients.
Add all generated files to your build target, as well as the contents of `support-lib/objc`.
Note that `+Private` headers can only be used with ObjC++ source (other headers are pure ObjC).
......
......@@ -35,7 +35,6 @@ object Main {
var javaPackage: Option[String] = None
var javaCppException: Option[String] = None
var javaAnnotation: Option[String] = None
var objcOutFolder: Option[File] = None
var jniOutFolder: Option[File] = None
var jniHeaderOutFolderOptional: Option[File] = None
var jniNamespace: String = "djinni_generated"
......@@ -51,11 +50,14 @@ object Main {
var javaIdentStyle = IdentStyle.javaDefault
var cppIdentStyle = IdentStyle.cppDefault
var cppTypeEnumIdentStyle: IdentConverter = null
var objcOutFolder: Option[File] = None
var objcPrivateOutFolderOptional: Option[File] = None
var objcExt: String = "mm"
var objcHeaderExt: String = "h"
var objcIdentStyle = IdentStyle.objcDefault
var objcTypePrefix: String = ""
var objcIncludePrefix: String = ""
var objcIncludePrivatePrefixOptional: Option[String] = None
var objcIncludeCppPrefix: String = ""
var objcFileIdentStyleOptional: Option[IdentConverter] = None
var objcppNamespace: String = "djinni_generated"
......@@ -120,6 +122,8 @@ object Main {
note("")
opt[File]("objc-out").valueName("<out-folder>").foreach(x => objcOutFolder = Some(x))
.text("The output folder for Objective-C files (Generator disabled if unspecified).")
opt[File]("objc-private-out").valueName("<out-folder>").foreach(x => objcPrivateOutFolderOptional = Some(x))
.text("The output folder for private Objective-C header and implementation files (default: the same as --objc-out)")
opt[String]("objc-ext").valueName("<ext>").foreach(objcExt = _)
.text("The filename extension for Objective-C files (default: \"mm\")")
opt[String]("objc-h-ext").valueName("<ext>").foreach(objcHeaderExt = _)
......@@ -128,6 +132,8 @@ object Main {
.text("The prefix for Objective-C data types (usually two or three letters)")
opt[String]("objc-include-prefix").valueName("<prefix>").foreach(objcIncludePrefix = _)
.text("The prefix for #import of header files from Objective-C files.")
opt[String]("objc-include-private-prefix").valueName("<prefix>").foreach(x => objcIncludePrivatePrefixOptional = Some(x))
.text("The prefix for #import and #include of private header files from Objective-C files (default: the same as --objc-include-prefix)")
opt[String]("objc-include-cpp-prefix").valueName("<prefix>").foreach(objcIncludeCppPrefix = _)
.text("The prefix for #include of the main header files from Objective-C files.")
opt[String]("objcpp-namespace").valueName("<prefix>").foreach(objcppNamespace = _)
......@@ -168,6 +174,8 @@ object Main {
val jniBaseLibClassIdentStyle = jniBaseLibClassIdentStyleOptional.getOrElse(jniClassIdentStyle)
val jniFileIdentStyle = jniFileIdentStyleOptional.getOrElse(cppFileIdentStyle)
var objcFileIdentStyle = objcFileIdentStyleOptional.getOrElse(objcIdentStyle.ty)
val objcPrivateOutFolder = if (objcPrivateOutFolderOptional.isDefined) objcPrivateOutFolderOptional else objcOutFolder
var objcIncludePrivatePrefix = objcIncludePrivatePrefixOptional.getOrElse(objcIncludePrefix)
// Add ObjC prefix to identstyle
objcIdentStyle = objcIdentStyle.copy(ty = IdentStyle.prefix(objcTypePrefix,objcIdentStyle.ty))
......@@ -223,11 +231,13 @@ object Main {
cppExt,
cppHeaderExt,
objcOutFolder,
objcPrivateOutFolder,
objcIdentStyle,
objcFileIdentStyle,
objcExt,
objcHeaderExt,
objcIncludePrefix,
objcIncludePrivatePrefix,
objcIncludeCppPrefix,
objcppNamespace,
objcBaseLibIncludePrefix)
......
......@@ -41,20 +41,20 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) {
header.add("#import <Foundation/Foundation.h>")
case d: MDef => d.defType match {
case DEnum =>
body.add("#import " + q(headerName(d.name)))
body.add("#import " + q(enumTranslatorHeaderName(d.name)))
header.add("#import " + q(headerName(d.name)))
body.add("#import " + q(spec.objcIncludePrefix + headerName(d.name)))
body.add("#import " + q(spec.objcIncludePrivatePrefix + enumTranslatorHeaderName(d.name)))
header.add("#import " + q(spec.objcIncludePrefix + headerName(d.name)))
case DInterface =>
header.add("#import <Foundation/Foundation.h>")
header.add("#import " + q(headerName(d.name)))
header.add("#import " + q(spec.objcIncludePrefix + headerName(d.name)))
val ext = d.body.asInstanceOf[Interface].ext
if (ext.cpp) body.add("#import " + q(privateHeaderName(d.name + "_cpp_proxy")))
if (ext.objc) body.add("#import " + q(privateHeaderName(d.name + "_objc_proxy")))
if (ext.cpp) body.add("#import " + q(spec.objcIncludePrivatePrefix + privateHeaderName(d.name + "_cpp_proxy")))
if (ext.objc) body.add("#import " + q(spec.objcIncludePrivatePrefix + privateHeaderName(d.name + "_objc_proxy")))
case DRecord =>
val r = d.body.asInstanceOf[Record]
val prefix = if (r.ext.objc) "../" else ""
header.add("@class " + idObjc.ty(d.name) + ";")
body.add("#import " + q(prefix + privateHeaderName(d.name)))
body.add("#import " + q(spec.objcIncludePrivatePrefix + prefix + privateHeaderName(d.name)))
}
case p: MParam =>
}
......@@ -67,17 +67,17 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) {
refs.header.add("#import <Foundation/Foundation.h>")
refs.privHeader.add("#import <Foundation/Foundation.h>")
refs.privHeader.add("!#import " + q(headerName(ident)))
refs.privHeader.add("!#import " + q(spec.objcIncludePrefix + headerName(ident)))
refs.privHeader.add("!#include " + q(spec.objcIncludeCppPrefix + spec.cppFileIdentStyle(ident) + "." + spec.cppHeaderExt))
refs.body.add("#import <Foundation/Foundation.h>")
refs.body.add("!#import " + q(enumTranslatorHeaderName(ident)))
refs.body.add("!#import " + q(spec.objcIncludePrivatePrefix + enumTranslatorHeaderName(ident)))
val self = idObjc.ty(ident)
val cppSelf = withNs(spec.cppNamespace, idCpp.enumType(ident))
val name = IdentStyle.camelUpper(ident)
val argName = idObjc.local(ident.name)
writeObjcFile(headerName(ident), origin, refs.header, w => {
writePublicObjcFile(headerName(ident), origin, refs.header, w => {
writeDoc(w, doc)
w.wl(s"typedef NS_ENUM(NSInteger, $self)")
w.bracedSemi {
......@@ -89,7 +89,7 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) {
}
})
writeObjcFile(enumTranslatorHeaderName(ident), origin, refs.privHeader, w => {
writePrivateObjcFile(enumTranslatorHeaderName(ident), origin, refs.privHeader, w => {
w.wl("@interface " + self + "Translator : NSObject")
w.wl
w.wl(s"+ ($self)cpp${name}ToObjc${name}:($cppSelf)$argName;")
......@@ -98,7 +98,7 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) {
w.wl("@end")
})
writeObjcFile(enumTranslatorName(ident), origin, refs.body, w => {
writePrivateObjcFile(enumTranslatorName(ident), origin, refs.body, w => {
w.wl(s"static_assert(__has_feature(objc_arc), " + q("Djinni requires ARC to be enabled for this file") + ");" )
w.wl
w.wl(s"@implementation " + self + "Translator")
......@@ -188,7 +188,7 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) {
refs.privHeader.add("#import <Foundation/Foundation.h>")
refs.privHeader.add("#include <memory>")
refs.privHeader.add("!#import " + q(headerName(ident)))
refs.privHeader.add("!#import " + q(spec.objcIncludePrefix + headerName(ident)))
refs.privHeader.add("!#include " + q(spec.objcIncludeCppPrefix + spec.cppFileIdentStyle(ident) + "." + spec.cppHeaderExt))
def writeObjcFuncDecl(method: Interface.Method, w: IndentWriter) {
......@@ -202,7 +202,7 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) {
}
}
writeObjcFile(headerName(ident), origin, refs.header, w => {
writePublicObjcFile(headerName(ident), origin, refs.header, w => {
writeDoc(w, doc)
for (c <- i.consts) {
writeDoc(w, c.doc)
......@@ -223,10 +223,10 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) {
})
refs.body.add("#include <vector>")
refs.body.add("#import " + q(headerName(ident)))
refs.body.add("#import " + q(spec.objcIncludePrefix + headerName(ident)))
if (i.consts.nonEmpty) {
writeObjcFile(bodyName(ident.name), origin, refs.body, w => {
writePrivateObjcFile(bodyName(ident.name), origin, refs.body, w => {
generateObjcConstants(w, i.consts, self)
})
}
......@@ -235,23 +235,23 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) {
val cppExtSelf = idObjc.ty(cppExtName)
val cppName = withNs(spec.cppNamespace, idCpp.ty(ident))
if (i.ext.cpp) {
refs.privHeader.add("!#import " + q(headerName(cppExtName)))
refs.privHeader.add("!#import " + q(spec.objcIncludePrefix + headerName(cppExtName)))
refs.privHeader.add("#import " + q(spec.objcBaseLibIncludePrefix + "DJICppWrapperCache+Private.h"))
refs.body.add("#include <utility>")
refs.body.add("#import " + q(spec.objcBaseLibIncludePrefix + "DJIError.h"))
refs.body.add("#import " + q(spec.objcBaseLibIncludePrefix + "DJIDate.h"))
refs.body.add("#include <exception>")
refs.body.add("!#import " + q(privateHeaderName(cppExtName)))
refs.body.add("!#import " + q(spec.objcIncludePrivatePrefix + privateHeaderName(cppExtName)))
writeObjcFile(headerName(cppExtName), origin, mutable.TreeSet[String](), w => {
w.wl("#import " + q(headerName(ident)))
writePublicObjcFile(headerName(cppExtName), origin, mutable.TreeSet[String](), w => {
w.wl("#import " + q(spec.objcIncludePrefix + headerName(ident)))
w.wl("#import <Foundation/Foundation.h>")
w.wl
w.wl(s"@interface $cppExtSelf : NSObject <$self>")
w.wl("@end")
})
writeObjcFile(privateHeaderName(cppExtName), origin, refs.privHeader, w => {
writePrivateObjcFile(privateHeaderName(cppExtName), origin, refs.privHeader, w => {
w.wl(s"@interface $cppExtSelf ()")
w.wl
w.wl(s"@property (nonatomic, readonly) std::shared_ptr<$cppName> cppRef;")
......@@ -261,7 +261,7 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) {
w.wl("@end")
})
writeObjcFile(bodyName(cppExtName), origin, refs.body, w => {
writePrivateObjcFile(bodyName(cppExtName), origin, refs.body, w => {
w.wl(s"static_assert(__has_feature(objc_arc), " + q("Djinni requires ARC to be enabled for this file") + ");" )
w.wl
w.wl(s"@interface $cppExtSelf ()")
......@@ -320,8 +320,8 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) {
val objcExtSelf = idCpp.ty(objcExtName)
if (i.ext.objc) {
refs.privHeader.add("#import " + q(spec.objcBaseLibIncludePrefix + "DJIObjcWrapperCache+Private.h"))
refs.body.add("!#import " + q(privateHeaderName(objcExtName)))
writeObjcFile(privateHeaderName(objcExtName), origin, refs.privHeader, w => {
refs.body.add("!#import " + q(spec.objcIncludePrivatePrefix + privateHeaderName(objcExtName)))
writePrivateObjcFile(privateHeaderName(objcExtName), origin, refs.privHeader, w => {
wrapNamespace(w, Some(spec.objcppNamespace), (w: IndentWriter) => {
w.wl(s"class $objcExtSelf final : public ${withNs(spec.cppNamespace, idCpp.ty(ident))}").bracedSemi {
w.wl("public:")
......@@ -340,7 +340,7 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) {
})
})
writeObjcFile(bodyName(objcExtName), origin, refs.body, w => {
writePrivateObjcFile(bodyName(objcExtName), origin, refs.body, w => {
wrapNamespace(w, Some(spec.objcppNamespace), (w: IndentWriter) => {
w.wl(s"$objcExtSelf::$objcExtSelf (id objcRef)")
w.wl(s" : _objcRef((assert([[objcRef class] conformsToProtocol:@protocol($self)]), objcRef)),")
......@@ -397,18 +397,18 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) {
refs.header.add("#import <Foundation/Foundation.h>")
refs.privHeader.add("#import <Foundation/Foundation.h>")
refs.privHeader.add("!#import " + q(headerName(objcName)))
refs.privHeader.add("!#import " + q(spec.objcIncludePrefix + headerName(objcName)))
refs.privHeader.add("!#include " + q(spec.objcIncludeCppPrefix + spec.cppFileIdentStyle(ident) + "." + spec.cppHeaderExt))
refs.body.add("#import <Foundation/Foundation.h>")
refs.body.add("#include <utility>")
refs.body.add("#include <vector>")
refs.body.add("!#import " + q(privateHeaderName(objcName)))
refs.body.add("!#import " + q(spec.objcIncludePrivatePrefix + privateHeaderName(objcName)))
refs.body.add("#import " + q(spec.objcBaseLibIncludePrefix + "DJIDate.h"))
if (r.ext.objc) {
refs.body.add("#import " + q("../" + headerName(ident)))
refs.privHeader.add("#import " + q("../" + headerName(ident)))
refs.body.add("#import " + q(spec.objcIncludePrefix + "../" + headerName(ident)))
refs.privHeader.add("#import " + q(spec.objcIncludePrefix + "../" + headerName(ident)))
refs.header.add(s"@class ${idObjc.ty(ident.name)};")
}
......@@ -419,7 +419,7 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) {
case _ => false
}
writeObjcFile(headerName(objcName), origin, refs.header, w => {
writePublicObjcFile(headerName(objcName), origin, refs.header, w => {
writeDoc(w, doc)
for (c <- r.consts) {
writeDoc(w, c.doc)
......@@ -455,7 +455,7 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) {
w.wl("@end")
})
writeObjcFile(privateHeaderName(objcName), origin, refs.privHeader, w => {
writePrivateObjcFile(privateHeaderName(objcName), origin, refs.privHeader, w => {
w.wl(s"@interface $self ()")
w.wl
// Deep copy constructor
......@@ -465,7 +465,7 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) {
w.wl("@end")
})
writeObjcFile(bodyName(objcName), origin, refs.body, w => {
writePrivateObjcFile(bodyName(objcName), origin, refs.body, w => {
if (r.consts.nonEmpty) generateObjcConstants(w, r.consts, noBaseSelf)
w.wl(s"static_assert(__has_feature(objc_arc), " + q("Djinni requires ARC to be enabled for this file") + ");" )
w.wl
......@@ -609,8 +609,14 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) {
})
}
def writeObjcFile(fileName: String, origin: String, refs: Iterable[String], f: IndentWriter => Unit) {
createFile(spec.objcOutFolder.get, fileName, (w: IndentWriter) => {
def writePublicObjcFile(fileName: String, origin: String, refs: Iterable[String], f: IndentWriter => Unit) =
writeObjcFile(spec.objcOutFolder, fileName, origin, refs, f)
def writePrivateObjcFile(fileName: String, origin: String, refs: Iterable[String], f: IndentWriter => Unit) =
writeObjcFile(spec.objcPrivateOutFolder, fileName, origin, refs, f)
def writeObjcFile(folder: Option[java.io.File], fileName: String, origin: String, refs: Iterable[String], f: IndentWriter => Unit) {
createFile(folder.get, fileName, (w: IndentWriter) => {
w.wl("// AUTOGENERATED FILE - DO NOT MODIFY!")
w.wl("// This file generated by Djinni from " + origin)
w.wl
......
......@@ -53,11 +53,13 @@ package object generatorTools {
cppExt: String,
cppHeaderExt: String,
objcOutFolder: Option[File],
objcPrivateOutFolder: Option[File],
objcIdentStyle: ObjcIdentStyle,
objcFileIdentStyle: IdentConverter,
objcExt: String,
objcHeaderExt: String,
objcIncludePrefix: String,
objcIncludePrivatePrefix: String,
objcIncludeCppPrefix: String,
objcppNamespace: String,
objcBaseLibIncludePrefix: String)
......@@ -165,6 +167,7 @@ package object generatorTools {
}
if (spec.objcOutFolder.isDefined) {
createFolder("Objective-C[++]", spec.objcOutFolder.get)
createFolder("Objective-C[++] private", spec.objcPrivateOutFolder.get)
new ObjcGenerator(spec).generate(idl)
}
None
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment