c++-impl: gen server stub boilerplate
-Lc++-impl now generates a default implementation for all
the interfaces in the specified package. The method
implementations are all left empty.
The default implementation also contains a special function,
HIDL_FETCH_IFOO(), which is used to retrieve an instance of
a particular interface in pass-through mode. For that to work,
the default implementation must live in a shared-library with
a well-known name, eg android.hardware.tests.foo@1.0.impl.so.
Sample invocation:
hidl-gen -o tmp/impl/ -Lc++-impl -randroid.hardware:hardware/interfaces
android.hardware.tests.foo@1.0
Bug: 31228745
Change-Id: I747aa47cb76931eed85179b8131d2efd3f887471
diff --git a/AST.h b/AST.h
index f62425a..3040c22 100644
--- a/AST.h
+++ b/AST.h
@@ -72,6 +72,7 @@
void addImportedAST(AST *ast);
status_t generateCpp(const std::string &outputPath) const;
+ status_t generateCppImpl(const std::string &outputPath) const;
status_t generateJava(
const std::string &outputPath, const char *limitToType) const;
@@ -146,9 +147,14 @@
enum MethodLocation {
PROXY_HEADER,
- STUB_HEADER
+ STUB_HEADER,
+ IMPL_HEADER,
+ IMPL_SOURCE
};
+ status_t generateStubImplHeader(const std::string &outputPath) const;
+ status_t generateStubImplSource(const std::string &outputPath) const;
+
status_t generateMethods(Formatter &out,
const std::string &className,
MethodLocation type,
@@ -157,10 +163,20 @@
const std::string &className,
const Method *method,
bool specifyNamespaces) const;
- status_t generateProxyMethod(Formatter &out,
- const std::string &className,
- const Method *method,
- bool specifyNamespaces) const;
+ status_t generateProxyDeclaration(Formatter &out,
+ const std::string &className,
+ const Method *method,
+ bool specifyNamespaces) const;
+ status_t generateStubImplDeclaration(Formatter &out,
+ const std::string &className,
+ const Method *method,
+ bool specifyNamespaces) const;
+ status_t generateStubImplMethod(Formatter &out,
+ const std::string &className,
+ const Method *method,
+ bool specifyNamespaces) const;
+
+ void generateFetchSymbol(Formatter &out, const std::string &ifaceName) const;
status_t generateProxySource(
Formatter &out, const std::string &baseName) const;
diff --git a/Android.mk b/Android.mk
index ce597f8..ab8efaf 100644
--- a/Android.mk
+++ b/Android.mk
@@ -15,6 +15,7 @@
Formatter.cpp \
FQName.cpp \
generateCpp.cpp \
+ generateCppImpl.cpp \
generateJava.cpp \
generateVts.cpp \
GenericBinder.cpp \
diff --git a/FQName.cpp b/FQName.cpp
index fcdaf20..bc47c46 100644
--- a/FQName.cpp
+++ b/FQName.cpp
@@ -193,6 +193,28 @@
return out;
}
+const FQName FQName::getTopLevelType() const {
+ auto idx = mName.find('.');
+
+ if (idx == std::string::npos) {
+ return *this;
+ }
+
+ return FQName(mPackage, mVersion, mName.substr(0, idx));
+}
+
+std::string FQName::tokenName() const {
+ std::vector<std::string> components;
+ getPackageAndVersionComponents(&components, true /* cpp_compatible */);
+
+ std::vector<std::string> nameComponents;
+ SplitString(mName, '.', &nameComponents);
+
+ components.insert(components.end(), nameComponents.begin(), nameComponents.end());
+
+ return JoinStrings(components, "_");
+}
+
std::string FQName::cppNamespace() const {
std::vector<std::string> components;
getPackageAndVersionComponents(&components, true /* cpp_compatible */);
diff --git a/FQName.h b/FQName.h
index 8a0ad30..2fe7963 100644
--- a/FQName.h
+++ b/FQName.h
@@ -77,6 +77,18 @@
bool operator<(const FQName &other) const;
bool operator==(const FQName &other) const;
+ // the following comments all assume that the FQName
+ // is ::android::hardware::Foo::V1_0::IBar::Baz
+
+ // returns highest type in the hidl namespace, i.e.
+ // ::android::hardware::Foo::V1_0::IBar
+ const FQName getTopLevelType() const;
+
+ // returns an unambiguous fully qualified name which can be
+ // baked into a token, i.e.
+ // android_hardware_Foo_V1_0_IBar_Baz
+ std::string tokenName() const;
+
// Returns an absolute C++ namespace prefix, i.e.
// ::android::hardware::Foo::V1_0.
std::string cppNamespace() const;
@@ -86,14 +98,14 @@
std::string cppLocalName() const;
// Returns a fully qualified absolute C++ type name, i.e.
- // ::android::hardware::Foo::V1_0::bar::baz.
+ // ::android::hardware::Foo::V1_0::IBar::Baz.
std::string cppName() const;
// Returns the java package name, i.e. "android.hardware.Foo.V1_0".
std::string javaPackage() const;
// Returns the fully qualified java type name,
- // i.e. "android.hardware.Foo.V1_0.IBar"
+ // i.e. "android.hardware.Foo.V1_0.IBar.Baz"
std::string javaName() const;
bool endsWith(const FQName &other) const;
diff --git a/generateCpp.cpp b/generateCpp.cpp
index 3668fd6..f11137c 100644
--- a/generateCpp.cpp
+++ b/generateCpp.cpp
@@ -74,15 +74,8 @@
}
std::string AST::makeHeaderGuard(const std::string &baseName) const {
- std::vector<std::string> packageComponents;
- getPackageAndVersionComponents(
- &packageComponents, true /* cpp_compatible */);
-
- std::string guard = "HIDL_GENERATED";
- for (const auto &component : packageComponents) {
- guard += "_";
- guard += component;
- }
+ std::string guard = "HIDL_GENERATED_";
+ guard += mPackage.tokenName();
guard += "_";
guard += baseName;
@@ -456,10 +449,10 @@
return OK;
}
-status_t AST::generateProxyMethod(Formatter &out,
- const std::string &className,
- const Method *method,
- bool specifyNamespaces) const {
+status_t AST::generateProxyDeclaration(Formatter &out,
+ const std::string &className,
+ const Method *method,
+ bool specifyNamespaces) const {
method->generateCppSignature(out,
className,
@@ -500,12 +493,25 @@
specifyNamespaces);
break;
case PROXY_HEADER:
- err = generateProxyMethod(out,
- className,
- method,
- specifyNamespaces);
+ err = generateProxyDeclaration(out,
+ className,
+ method,
+ specifyNamespaces);
+ break;
+ case IMPL_HEADER:
+ err = generateStubImplDeclaration(out,
+ className,
+ method,
+ specifyNamespaces);
+ break;
+ case IMPL_SOURCE:
+ err = generateStubImplMethod(out,
+ className,
+ method,
+ specifyNamespaces);
break;
default:
+ LOG(ERROR) << "Unkown method type: " << type;
err = UNKNOWN_ERROR;
}
@@ -587,10 +593,15 @@
out.unindent();
out.unindent();
- generateMethods(out,
- "" /* class name */,
- MethodLocation::STUB_HEADER,
- true /* specify namespaces */);
+ status_t err = generateMethods(out,
+ "" /* class name */,
+ MethodLocation::STUB_HEADER,
+ true /* specify namespaces */);
+
+ if (err != OK) {
+ return err;
+ }
+
out.unindent();
out << "};\n\n";
@@ -662,10 +673,14 @@
out << "virtual bool isRemote() const { return true; }\n\n";
- generateMethods(out,
- "" /* class name */,
- MethodLocation::PROXY_HEADER,
- true /* generate specify namespaces */);
+ status_t err = generateMethods(out,
+ "" /* class name */,
+ MethodLocation::PROXY_HEADER,
+ true /* generate specify namespaces */);
+
+ if (err != OK) {
+ return err;
+ }
out.unindent();
@@ -745,7 +760,13 @@
}
if (err == OK && isInterface) {
- out << "IMPLEMENT_REGISTER_AND_GET_SERVICE(" << baseName << ")\n";
+ const Interface *iface = mRootScope->getInterface();
+
+ out << "IMPLEMENT_REGISTER_AND_GET_SERVICE("
+ << baseName << ", "
+ << "\"" << iface->fqName().package()
+ << iface->fqName().version() << ".impl.so\""
+ << ")\n";
}
enterLeaveNamespace(out, false /* enter */);
diff --git a/generateCppImpl.cpp b/generateCppImpl.cpp
new file mode 100644
index 0000000..6c23e90
--- /dev/null
+++ b/generateCppImpl.cpp
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "AST.h"
+
+#include "Coordinator.h"
+#include "EnumType.h"
+#include "Formatter.h"
+#include "Interface.h"
+#include "Method.h"
+#include "ScalarType.h"
+#include "Scope.h"
+
+#include <algorithm>
+#include <android-base/logging.h>
+#include <string>
+#include <vector>
+#include <set>
+
+
+namespace android {
+
+static std::string upcase(const std::string &in) {
+ std::string out{in};
+
+ for (auto &ch : out) {
+ ch = toupper(ch);
+ }
+
+ return out;
+}
+
+status_t AST::generateCppImpl(const std::string &outputPath) const {
+ status_t err = generateStubImplHeader(outputPath);
+
+ if (err == OK) {
+ err = generateStubImplSource(outputPath);
+ }
+
+ return err;
+}
+
+void AST::generateFetchSymbol(Formatter &out, const std::string& ifaceName) const {
+ out << "HIDL_FETCH_" << ifaceName;
+}
+
+status_t AST::generateStubImplMethod(Formatter &out,
+ const std::string &className,
+ const Method *method,
+ bool specifyNamespaces) const {
+
+ method->generateCppSignature(out, className, specifyNamespaces);
+
+ out << " {\n";
+
+ out.indent();
+ out << "// TODO implement\n";
+
+ const TypedVar *elidedReturn = method->canElideCallback();
+
+ if (elidedReturn == nullptr) {
+ out << "return Void();\n";
+ } else {
+ std::string extra;
+ out << "return "
+ << elidedReturn->type().getCppResultType(&extra)
+ << " {};\n";
+ }
+
+ out.unindent();
+
+ out << "}\n\n";
+
+ return OK;
+}
+
+status_t AST::generateStubImplDeclaration(Formatter &out,
+ const std::string &className,
+ const Method *method,
+ bool specifyNamespaces) const {
+
+ method->generateCppSignature(out,
+ className,
+ specifyNamespaces);
+ out << " override;\n";
+
+ return OK;
+}
+
+status_t AST::generateStubImplHeader(const std::string &outputPath) const {
+ std::string ifaceName;
+ if (!AST::isInterface(&ifaceName)) {
+ // types.hal does not get a stub header.
+ return OK;
+ }
+
+ const Interface *iface = mRootScope->getInterface();
+
+ // cut off the leading 'I'.
+ const std::string baseName = ifaceName.substr(1);
+
+ std::string path = outputPath;
+ path.append(baseName);
+ path.append(".h");
+
+ CHECK(Coordinator::MakeParentHierarchy(path));
+ FILE *file = fopen(path.c_str(), "w");
+
+ if (file == NULL) {
+ return -errno;
+ }
+
+ Formatter out(file);
+
+ const std::string guard = makeHeaderGuard(baseName);
+
+ out << "#ifndef " << guard << "\n";
+ out << "#define " << guard << "\n\n";
+
+ std::vector<std::string> packageComponents;
+ getPackageAndVersionComponents(
+ &packageComponents, false /* cpp_compatible */);
+
+ out << "#include <";
+ for (const auto &component : packageComponents) {
+ out << component << "/";
+ }
+ out << "I" << baseName << ".h>\n";
+
+ out << "#include <hidl/Status.h>\n\n";
+
+ enterLeaveNamespace(out, true /* enter */);
+ out << "namespace implementation {\n\n";
+
+ // this is namespace aware code and doesn't require post-processing
+ out.setNamespace("");
+
+ const Interface *ancestor = iface;
+ std::vector<const Interface *> chain;
+ while (ancestor != NULL) {
+ chain.push_back(ancestor);
+ ancestor = ancestor->superType();
+ }
+
+ std::set<const FQName> usedTypes{};
+
+ for (auto it = chain.rbegin(); it != chain.rend(); ++it) {
+ const Interface *superInterface = *it;
+
+ superInterface->addNamedTypesToSet(usedTypes);
+
+ for (const auto &method : superInterface->methods()) {
+ for(const auto & arg : method->args()) {
+ arg->type().addNamedTypesToSet(usedTypes);
+ }
+ for(const auto & results : method->results()) {
+ results->type().addNamedTypesToSet(usedTypes);
+ }
+ }
+ }
+
+ std::set<const FQName> topLevelTypes{};
+
+ for (const auto &name : usedTypes) {
+ topLevelTypes.insert(name.getTopLevelType());
+ }
+
+ for (const FQName &name : topLevelTypes) {
+ out << "using " << name.cppName() << ";\n";
+ }
+
+ out << "using ::android::hardware::Return;\n";
+ out << "using ::android::hardware::Void;\n";
+ out << "using ::android::hardware::hidl_vec;\n";
+ out << "using ::android::hardware::hidl_string;\n";
+ out << "using ::android::sp;\n";
+
+ out << "\n";
+
+ out << "struct "
+ << baseName
+ << " : public "
+ << ifaceName
+ << " {\n";
+
+ out.indent();
+
+ status_t err = generateMethods(out,
+ "", /* class name */
+ MethodLocation::IMPL_HEADER,
+ false /* specify namespaces */);
+
+ if (err != OK) {
+ return err;
+ }
+
+ out.unindent();
+
+ out << "};\n\n";
+
+ out << "extern \"C\" "
+ << ifaceName
+ << "* ";
+ generateFetchSymbol(out, ifaceName);
+ out << "(const char* name);\n\n";
+
+ out << "} // namespace implementation\n";
+ enterLeaveNamespace(out, false /* leave */);
+
+ out << "\n#endif // " << guard << "\n";
+
+ return OK;
+}
+
+status_t AST::generateStubImplSource(const std::string &outputPath) const {
+ std::string ifaceName;
+ if (!AST::isInterface(&ifaceName)) {
+ // types.hal does not get a stub header.
+ return OK;
+ }
+
+ // cut off the leading 'I'.
+ const std::string baseName = ifaceName.substr(1);
+
+ std::string path = outputPath;
+ path.append(baseName);
+ path.append(".cpp");
+
+ CHECK(Coordinator::MakeParentHierarchy(path));
+ FILE *file = fopen(path.c_str(), "w");
+
+ if (file == NULL) {
+ return -errno;
+ }
+
+ Formatter out(file);
+
+ out << "#include \"" << baseName << ".h\"\n\n";
+
+ enterLeaveNamespace(out, true /* enter */);
+ out << "namespace implementation {\n\n";
+
+ // this is namespace aware code and doesn't require post-processing
+ out.setNamespace("");
+
+ generateMethods(out,
+ baseName,
+ MethodLocation::IMPL_SOURCE,
+ false /* specify namespaces */);
+
+ const Interface *iface = mRootScope->getInterface();
+
+ out << ifaceName
+ << "* ";
+ generateFetchSymbol(out, ifaceName);
+ out << "(const char* /* name */) {\n";
+ out.indent();
+ out << "return new " << baseName << "();\n";
+ out.unindent();
+ out << "}\n\n";
+
+ out << "} // namespace implementation\n";
+ enterLeaveNamespace(out, false /* leave */);
+
+ return OK;
+}
+
+} // namespace android
diff --git a/main.cpp b/main.cpp
index e9c07b7..6e5d3dc 100644
--- a/main.cpp
+++ b/main.cpp
@@ -77,6 +77,9 @@
if (lang == "c++") {
return ast->generateCpp(outputDir);
}
+ if (lang == "c++-impl") {
+ return ast->generateCppImpl(outputDir);
+ }
if (lang == "java") {
return ast->generateJava(outputDir, limitToType);
}
@@ -466,6 +469,27 @@
}
},
+ {"c++-impl",
+ true /* mNeedsOutputDir */,
+ validateForSource,
+ [](const FQName &fqName,
+ const char *hidl_gen, Coordinator *coordinator,
+ const std::string &outputDir) -> status_t {
+ if (fqName.isFullyQualified()) {
+ return generateSourcesForFile(fqName,
+ hidl_gen,
+ coordinator,
+ outputDir, "c++-impl");
+ } else {
+ return generateSourcesForPackage(fqName,
+ hidl_gen,
+ coordinator,
+ outputDir, "c++-impl");
+ }
+ }
+ },
+
+
{"java",
true /* mNeedsOutputDir */,
validateForSource,