summaryrefslogtreecommitdiff
path: root/chromeos-dbus-bindings
diff options
context:
space:
mode:
authorPaul Stewart <pstew@chromium.org>2014-10-01 05:30:18 -0700
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2014-10-04 21:08:42 +0000
commit1dce1ae6ca266668e37d6bc4009692bbc12e2bb6 (patch)
treef011b54360c1d19b5250ca5143aed75628db325c /chromeos-dbus-bindings
parent99010b6c94b0bdf45cca07e60cb6795dca94d031 (diff)
downloaddbus-binding-generator-1dce1ae6ca266668e37d6bc4009692bbc12e2bb6.tar.gz
chromeos-dbus-bindings: Create proxy generator
Consolidate common functions to a parent HeaderGenerator class, and create a new ProxyGenerator class. This new class converts from native C++ method calls to D-Bus method invocations. It also takes an interface class (populated with default null implementations) for all signals supported by the interface. CQ-DEPEND=CL:221015 BUG=chromium:404505 TEST=New unit test Change-Id: Id6ed1f6f6e0868f32911a96e23e3dc74c9df0212 Reviewed-on: https://chromium-review.googlesource.com/221365 Reviewed-by: Alex Vakulenko <avakulenko@chromium.org> Commit-Queue: Paul Stewart <pstew@chromium.org> Tested-by: Paul Stewart <pstew@chromium.org>
Diffstat (limited to 'chromeos-dbus-bindings')
-rw-r--r--chromeos-dbus-bindings/adaptor_generator.cc70
-rw-r--r--chromeos-dbus-bindings/adaptor_generator.h22
-rw-r--r--chromeos-dbus-bindings/adaptor_generator_unittest.cc4
-rw-r--r--chromeos-dbus-bindings/chromeos-dbus-bindings.gyp3
-rw-r--r--chromeos-dbus-bindings/generate_chromeos_dbus_bindings.cc23
-rw-r--r--chromeos-dbus-bindings/header_generator.cc74
-rw-r--r--chromeos-dbus-bindings/header_generator.h52
-rw-r--r--chromeos-dbus-bindings/method_name_generator.cc1
-rw-r--r--chromeos-dbus-bindings/method_name_generator.h7
-rw-r--r--chromeos-dbus-bindings/method_name_generator_unittest.cc3
-rw-r--r--chromeos-dbus-bindings/proxy_generator.cc299
-rw-r--r--chromeos-dbus-bindings/proxy_generator.h63
-rw-r--r--chromeos-dbus-bindings/proxy_generator_unittest.cc248
13 files changed, 775 insertions, 94 deletions
diff --git a/chromeos-dbus-bindings/adaptor_generator.cc b/chromeos-dbus-bindings/adaptor_generator.cc
index ea220c3..df77629 100644
--- a/chromeos-dbus-bindings/adaptor_generator.cc
+++ b/chromeos-dbus-bindings/adaptor_generator.cc
@@ -6,11 +6,7 @@
#include <string>
-#include <base/file_util.h>
-#include <base/files/file_path.h>
#include <base/logging.h>
-#include <base/strings/string_split.h>
-#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
#include "chromeos-dbus-bindings/dbus_signature.h"
@@ -23,31 +19,26 @@ using std::vector;
namespace chromeos_dbus_bindings {
-namespace {
-const int kScopeOffset = 1;
-const int kBlockOffset = 2;
-const int kLineContinuationOffset = 4;
-} // namespace
-
+// static
bool AdaptorGenerator::GenerateAdaptor(
const Interface& interface,
const base::FilePath& output_file) {
IndentedText text;
text.AddLine(StringPrintf("// Automatic generation of interface for %s",
interface.name.c_str()));
- string header_guard = GenerateHeaderGuard(output_file.value(),
- interface.name);
+ string header_guard = GenerateHeaderGuard(output_file, interface.name);
text.AddLine(StringPrintf("#ifndef %s", header_guard.c_str()));
text.AddLine(StringPrintf("#define %s", header_guard.c_str()));
text.AddLine("#include <string>");
text.AddLine("#include <vector>");
- text.AddLine("");
+ text.AddBlankLine();
text.AddLine("#include <base/macros.h>");
text.AddLine("#include <dbus/object_path.h>");
+ text.AddLine("#include <chromeos/any.h>");
text.AddLine("#include <chromeos/dbus/dbus_object.h>");
text.AddLine("#include <chromeos/dbus/exported_object_manager.h>");
text.AddLine("#include <chromeos/variant_dictionary.h>");
- text.AddLine("");
+ text.AddBlankLine();
vector<string> namespaces;
string class_name;
@@ -55,7 +46,7 @@ bool AdaptorGenerator::GenerateAdaptor(
for (const auto& space : namespaces) {
text.AddLine(StringPrintf("namespace %s {", space.c_str()));
}
- text.AddLine("");
+ text.AddBlankLine();
string adaptor_name = StringPrintf("%sAdaptor", class_name.c_str());
text.AddLine(StringPrintf("class %s {", adaptor_name.c_str()));
@@ -91,36 +82,14 @@ bool AdaptorGenerator::GenerateAdaptor(
text.PopOffset();
text.AddLine("};");
- text.AddLine("");
+ text.AddBlankLine();
for (auto it = namespaces.rbegin(); it != namespaces.rend(); ++it) {
text.AddLine(StringPrintf("} // namespace %s", it->c_str()));
}
text.AddLine(StringPrintf("#endif // %s", header_guard.c_str()));
- string contents = text.GetContents();
- int expected_write_return = contents.size();
- if (base::WriteFile(output_file, contents.c_str(), contents.size()) !=
- expected_write_return) {
- LOG(ERROR) << "Failed to write file " << output_file.value();
- return false;
- }
- return true;
-}
-
-// static
-string AdaptorGenerator::GenerateHeaderGuard(
- const string& filename, const string& interface_name) {
- string guard = StringPrintf("____chrome_dbus_binding___%s__%s",
- interface_name.c_str(), filename.c_str());
- for (auto& c : guard) {
- if (IsAsciiAlpha(c)) {
- c = base::ToUpperASCII(c);
- } else if (!IsAsciiDigit(c)) {
- c = '_';
- }
- }
- return guard;
+ return WriteTextToFile(output_file, text);
}
// static
@@ -214,27 +183,4 @@ void AdaptorGenerator::AddMethodInterface(const Interface& interface,
text->AddBlock(block);
}
-// static
-bool AdaptorGenerator::GetNamespacesAndClassName(
- const string& interface_name,
- vector<string>* namespaces,
- string* class_name) {
- vector<string> split_namespaces;
- base::SplitString(interface_name, '.', &split_namespaces);
- if (split_namespaces.size() < 2) {
- LOG(ERROR) << "Interface name must have both a domain and object part "
- << "separated by '.'. Got " << interface_name << " instead.";
- return false;
- }
- *class_name = split_namespaces.back();
- split_namespaces.pop_back();
- namespaces->swap(split_namespaces);
- return true;
-}
-
-// static
-bool AdaptorGenerator::IsIntegralType(const string& type) {
- return type.find("::") == std::string::npos;
-}
-
} // namespace chromeos_dbus_bindings
diff --git a/chromeos-dbus-bindings/adaptor_generator.h b/chromeos-dbus-bindings/adaptor_generator.h
index 250557c..fe32409 100644
--- a/chromeos-dbus-bindings/adaptor_generator.h
+++ b/chromeos-dbus-bindings/adaptor_generator.h
@@ -10,6 +10,7 @@
#include <base/macros.h>
+#include "chromeos-dbus-bindings/header_generator.h"
#include "chromeos-dbus-bindings/indented_text.h"
namespace base {
@@ -23,21 +24,14 @@ namespace chromeos_dbus_bindings {
class IndentedText;
struct Interface;
-class AdaptorGenerator {
+class AdaptorGenerator : public HeaderGenerator {
public:
- AdaptorGenerator() = default;
- virtual ~AdaptorGenerator() = default;
-
- bool GenerateAdaptor(const Interface& interface,
- const base::FilePath& output_file);
+ static bool GenerateAdaptor(const Interface& interface,
+ const base::FilePath& output_file);
private:
friend class AdaptorGeneratorTest;
- // Create a unique header guard string to protect multiple includes of header.
- static std::string GenerateHeaderGuard(const std::string& filename,
- const std::string& interface_name);
-
// Generates the constructor for the adaptor.
static void AddConstructor(const Interface& interface,
const std::string& class_name,
@@ -47,14 +41,6 @@ class AdaptorGenerator {
static void AddMethodInterface(const Interface& interface,
IndentedText *text);
- // Returns a vector of nesting namepsaces.
- static bool GetNamespacesAndClassName(const std::string& interface_name,
- std::vector<std::string>* namespaces,
- std::string* class_name);
-
- // Used to decide whether the argument should be a const reference.
- static bool IsIntegralType(const std::string& type);
-
DISALLOW_COPY_AND_ASSIGN(AdaptorGenerator);
};
diff --git a/chromeos-dbus-bindings/adaptor_generator_unittest.cc b/chromeos-dbus-bindings/adaptor_generator_unittest.cc
index edd1e3a..58e0863 100644
--- a/chromeos-dbus-bindings/adaptor_generator_unittest.cc
+++ b/chromeos-dbus-bindings/adaptor_generator_unittest.cc
@@ -42,6 +42,7 @@ const char kExpectedContent[] = R"literal_string(
#include <base/macros.h>
#include <dbus/object_path.h>
+#include <chromeos/any.h>
#include <chromeos/dbus/dbus_object.h>
#include <chromeos/dbus/exported_object_manager.h>
#include <chromeos/variant_dictionary.h>
@@ -126,7 +127,6 @@ class AdaptorGeneratorTest : public Test {
}
base::ScopedTempDir temp_dir_;
- AdaptorGenerator generator_;
};
TEST_F(AdaptorGeneratorTest, GenerateAdaptors) {
@@ -151,7 +151,7 @@ TEST_F(AdaptorGeneratorTest, GenerateAdaptors) {
{"", kMethod3Return0},
{"", kMethod3Return1}});
base::FilePath output_path = temp_dir_.path().Append("output.h");
- EXPECT_TRUE(generator_.GenerateAdaptor(interface, output_path));
+ EXPECT_TRUE(AdaptorGenerator::GenerateAdaptor(interface, output_path));
string contents;
EXPECT_TRUE(base::ReadFileToString(output_path, &contents));
// The header guards contain the (temporary) filename, so we search for
diff --git a/chromeos-dbus-bindings/chromeos-dbus-bindings.gyp b/chromeos-dbus-bindings/chromeos-dbus-bindings.gyp
index 1d92c87..5b729d4 100644
--- a/chromeos-dbus-bindings/chromeos-dbus-bindings.gyp
+++ b/chromeos-dbus-bindings/chromeos-dbus-bindings.gyp
@@ -26,8 +26,10 @@
'sources': [
'adaptor_generator.cc',
'dbus_signature.cc',
+ 'header_generator.cc',
'indented_text.cc',
'method_name_generator.cc',
+ 'proxy_generator.cc',
'xml_interface_parser.cc',
],
'variables': {
@@ -77,6 +79,7 @@
'dbus_signature_unittest.cc',
'indented_text_unittest.cc',
'method_name_generator_unittest.cc',
+ 'proxy_generator_unittest.cc',
'xml_interface_parser_unittest.cc',
],
},
diff --git a/chromeos-dbus-bindings/generate_chromeos_dbus_bindings.cc b/chromeos-dbus-bindings/generate_chromeos_dbus_bindings.cc
index 7da9faf..5bf35e1 100644
--- a/chromeos-dbus-bindings/generate_chromeos_dbus_bindings.cc
+++ b/chromeos-dbus-bindings/generate_chromeos_dbus_bindings.cc
@@ -11,6 +11,7 @@
#include "chromeos-dbus-bindings/adaptor_generator.h"
#include "chromeos-dbus-bindings/method_name_generator.h"
+#include "chromeos-dbus-bindings/proxy_generator.h"
#include "chromeos-dbus-bindings/xml_interface_parser.h"
namespace switches {
@@ -19,6 +20,7 @@ static const char kHelp[] = "help";
static const char kInput[] = "input";
static const char kMethodNames[] = "method-names";
static const char kAdaptor[] = "adaptor";
+static const char kProxy[] = "proxy";
static const char kHelpMessage[] = "\n"
"Available Switches: \n"
" --input=<interface>\n"
@@ -26,7 +28,9 @@ static const char kHelpMessage[] = "\n"
" --method-names=<method name header filename>\n"
" The output header file with string constants for each method name.\n"
" --adaptor=<adaptor header filename>\n"
- " The output header file with DBus adaptor class.\n";
+ " The output header file name containing the DBus adaptor class.\n"
+ " --proxy=<proxy header filename>\n"
+ " The output header file name containing the DBus proxy class.\n";
} // namespace switches
@@ -61,8 +65,7 @@ int main(int argc, char** argv) {
std::string method_name_file =
cl->GetSwitchValueASCII(switches::kMethodNames);
VLOG(1) << "Outputting method names to " << method_name_file;
- chromeos_dbus_bindings::MethodNameGenerator method_name_generator;
- if (!method_name_generator.GenerateMethodNames(
+ if (!chromeos_dbus_bindings::MethodNameGenerator::GenerateMethodNames(
parser.interface(),
base::FilePath(method_name_file))) {
LOG(ERROR) << "Failed to output method names.";
@@ -73,8 +76,7 @@ int main(int argc, char** argv) {
if (cl->HasSwitch(switches::kAdaptor)) {
std::string adaptor_file = cl->GetSwitchValueASCII(switches::kAdaptor);
VLOG(1) << "Outputting adaptor to " << adaptor_file;
- chromeos_dbus_bindings::AdaptorGenerator adaptor_generator;
- if (!adaptor_generator.GenerateAdaptor(
+ if (!chromeos_dbus_bindings::AdaptorGenerator::GenerateAdaptor(
parser.interface(),
base::FilePath(adaptor_file))) {
LOG(ERROR) << "Failed to output adaptor.";
@@ -82,5 +84,16 @@ int main(int argc, char** argv) {
}
}
+ if (cl->HasSwitch(switches::kProxy)) {
+ std::string proxy_file = cl->GetSwitchValueASCII(switches::kProxy);
+ LOG(INFO) << "Outputting proxy to " << proxy_file;
+ if (!chromeos_dbus_bindings::ProxyGenerator::GenerateProxy(
+ parser.interface(),
+ base::FilePath(proxy_file))) {
+ LOG(ERROR) << "Failed to output proxy.";
+ return 1;
+ }
+ }
+
return 0;
}
diff --git a/chromeos-dbus-bindings/header_generator.cc b/chromeos-dbus-bindings/header_generator.cc
new file mode 100644
index 0000000..c339c13
--- /dev/null
+++ b/chromeos-dbus-bindings/header_generator.cc
@@ -0,0 +1,74 @@
+// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos-dbus-bindings/header_generator.h"
+
+#include <string>
+
+#include <base/file_util.h>
+#include <base/files/file_path.h>
+#include <base/strings/string_split.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+
+#include "chromeos-dbus-bindings/indented_text.h"
+
+using std::string;
+using std::vector;
+
+namespace chromeos_dbus_bindings {
+
+// static
+string HeaderGenerator::GenerateHeaderGuard(
+ const base::FilePath& output_file, const string& interface_name) {
+ string guard = base::StringPrintf("____chromeos_dbus_binding___%s__%s",
+ interface_name.c_str(),
+ output_file.value().c_str());
+ for (auto& c : guard) {
+ if (IsAsciiAlpha(c)) {
+ c = base::ToUpperASCII(c);
+ } else if (!IsAsciiDigit(c)) {
+ c = '_';
+ }
+ }
+ return guard;
+}
+
+// static
+bool HeaderGenerator::GetNamespacesAndClassName(
+ const string& interface_name,
+ vector<string>* namespaces,
+ string* class_name) {
+ vector<string> split_namespaces;
+ base::SplitString(interface_name, '.', &split_namespaces);
+ if (split_namespaces.size() < 2) {
+ LOG(ERROR) << "Interface name must have both a domain and object part "
+ << "separated by '.'. Got " << interface_name << " instead.";
+ return false;
+ }
+ *class_name = split_namespaces.back();
+ split_namespaces.pop_back();
+ namespaces->swap(split_namespaces);
+ return true;
+}
+
+// static
+bool HeaderGenerator::IsIntegralType(const string& type) {
+ return type.find("::") == std::string::npos;
+}
+
+// static
+bool HeaderGenerator::WriteTextToFile(
+ const base::FilePath& output_file, const IndentedText &text) {
+ string contents = text.GetContents();
+ int expected_write_return = contents.size();
+ if (base::WriteFile(output_file, contents.c_str(), contents.size()) !=
+ expected_write_return) {
+ LOG(ERROR) << "Failed to write file " << output_file.value();
+ return false;
+ }
+ return true;
+}
+
+} // namespace chromeos_dbus_bindings
diff --git a/chromeos-dbus-bindings/header_generator.h b/chromeos-dbus-bindings/header_generator.h
new file mode 100644
index 0000000..7be8d5b
--- /dev/null
+++ b/chromeos-dbus-bindings/header_generator.h
@@ -0,0 +1,52 @@
+// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_DBUS_BINDINGS_HEADER_GENERATOR_H_
+#define CHROMEOS_DBUS_BINDINGS_HEADER_GENERATOR_H_
+
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+
+namespace base {
+
+class FilePath;
+
+};
+
+namespace chromeos_dbus_bindings {
+
+struct Interface;
+class IndentedText;
+
+class HeaderGenerator {
+ protected:
+ // Create a unique header guard string to protect multiple includes of header.
+ static std::string GenerateHeaderGuard(const base::FilePath& output_file,
+ const std::string& interface_name);
+
+ // Returns a vector of nesting namepsaces.
+ static bool GetNamespacesAndClassName(const std::string& interface_name,
+ std::vector<std::string>* namespaces,
+ std::string* class_name);
+
+ // Used to decide whether the argument should be a const reference.
+ static bool IsIntegralType(const std::string& type);
+
+ // Writes indented text to a file.
+ static bool WriteTextToFile(const base::FilePath& output_file,
+ const IndentedText& text);
+
+ static const int kScopeOffset = 1;
+ static const int kBlockOffset = 2;
+ static const int kLineContinuationOffset = 4;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HeaderGenerator);
+};
+
+} // namespace chromeos_dbus_bindings
+
+#endif // CHROMEOS_DBUS_BINDINGS_HEADER_GENERATOR_H_
diff --git a/chromeos-dbus-bindings/method_name_generator.cc b/chromeos-dbus-bindings/method_name_generator.cc
index b2d49f4..4b4154e 100644
--- a/chromeos-dbus-bindings/method_name_generator.cc
+++ b/chromeos-dbus-bindings/method_name_generator.cc
@@ -23,6 +23,7 @@ string MethodNameGenerator::GenerateMethodNameConstant(
return "k" + method_name + "Method";
}
+// static
bool MethodNameGenerator::GenerateMethodNames(
const Interface& interface,
const base::FilePath& output_file) {
diff --git a/chromeos-dbus-bindings/method_name_generator.h b/chromeos-dbus-bindings/method_name_generator.h
index af2576f..59ca890 100644
--- a/chromeos-dbus-bindings/method_name_generator.h
+++ b/chromeos-dbus-bindings/method_name_generator.h
@@ -21,11 +21,8 @@ struct Interface;
class MethodNameGenerator {
public:
- MethodNameGenerator() = default;
- virtual ~MethodNameGenerator() = default;
-
- virtual bool GenerateMethodNames(const Interface &interface,
- const base::FilePath& output_file);
+ static bool GenerateMethodNames(const Interface &interface,
+ const base::FilePath& output_file);
static std::string GenerateMethodNameConstant(const std::string& method_name);
private:
diff --git a/chromeos-dbus-bindings/method_name_generator_unittest.cc b/chromeos-dbus-bindings/method_name_generator_unittest.cc
index e73b8ba..dc461d6 100644
--- a/chromeos-dbus-bindings/method_name_generator_unittest.cc
+++ b/chromeos-dbus-bindings/method_name_generator_unittest.cc
@@ -45,7 +45,6 @@ class MethodNameGeneratorTest : public Test {
}
base::ScopedTempDir temp_dir_;
- MethodNameGenerator generator_;
};
TEST_F(MethodNameGeneratorTest, GnerateMethodNames) {
@@ -54,7 +53,7 @@ TEST_F(MethodNameGeneratorTest, GnerateMethodNames) {
interface.methods.emplace_back(kMethodName1);
interface.methods.emplace_back(kMethodName2);
base::FilePath output_path = temp_dir_.path().Append("output.h");
- EXPECT_TRUE(generator_.GenerateMethodNames(interface, output_path));
+ EXPECT_TRUE(MethodNameGenerator::GenerateMethodNames(interface, output_path));
string contents;
EXPECT_TRUE(base::ReadFileToString(output_path, &contents));
EXPECT_STREQ(kExpectedOutput, contents.c_str());
diff --git a/chromeos-dbus-bindings/proxy_generator.cc b/chromeos-dbus-bindings/proxy_generator.cc
new file mode 100644
index 0000000..34b44c0
--- /dev/null
+++ b/chromeos-dbus-bindings/proxy_generator.cc
@@ -0,0 +1,299 @@
+// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos-dbus-bindings/proxy_generator.h"
+
+#include <base/logging.h>
+#include <base/strings/stringprintf.h>
+
+#include "chromeos-dbus-bindings/dbus_signature.h"
+#include "chromeos-dbus-bindings/indented_text.h"
+
+using base::StringPrintf;
+using std::string;
+using std::vector;
+
+namespace chromeos_dbus_bindings {
+
+// static
+bool ProxyGenerator::GenerateProxy(
+ const Interface& interface,
+ const base::FilePath& output_file) {
+ IndentedText text;
+ text.AddLine(StringPrintf("// Automatic generation of interface for %s",
+ interface.name.c_str()));
+ string header_guard = GenerateHeaderGuard(output_file, interface.name);
+ text.AddLine(StringPrintf("#ifndef %s", header_guard.c_str()));
+ text.AddLine(StringPrintf("#define %s", header_guard.c_str()));
+ text.AddLine("#include <string>");
+ text.AddLine("#include <vector>");
+ text.AddBlankLine();
+ text.AddLine("#include <base/bind.h>");
+ text.AddLine("#include <base/callback.h>");
+ text.AddLine("#include <base/logging.h>");
+ text.AddLine("#include <base/macros.h>");
+ text.AddLine("#include <base/memory/ref_counted.h>");
+ text.AddLine("#include <chromeos/any.h>");
+ text.AddLine("#include <chromeos/dbus/dbus_method_invoker.h>");
+ text.AddLine("#include <chromeos/dbus/dbus_signal_handler.h>");
+ text.AddLine("#include <chromeos/errors/error.h>");
+ text.AddLine("#include <dbus/bus.h>");
+ text.AddLine("#include <dbus/message.h>");
+ text.AddLine("#include <dbus/object_path.h>");
+ text.AddLine("#include <dbus/object_proxy.h>");
+ text.AddBlankLine();
+
+ vector<string> namespaces;
+ string class_name;
+ CHECK(GetNamespacesAndClassName(interface.name, &namespaces, &class_name));
+ for (const auto& space : namespaces) {
+ text.AddLine(StringPrintf("namespace %s {", space.c_str()));
+ }
+ text.AddBlankLine();
+
+ string proxy_name = StringPrintf("%sProxy", class_name.c_str());
+ text.AddLine(StringPrintf("class %s {", proxy_name.c_str()));
+ text.AddLineWithOffset("public:", kScopeOffset);
+ text.PushOffset(kBlockOffset);
+ AddMethodInterface(interface, &text);
+ AddConstructor(interface, proxy_name, &text);
+ AddDestructor(proxy_name, &text);
+ AddSignalConnectedCallback(&text);
+ for (const auto& method : interface.methods) {
+ AddMethodProxy(method, interface.name, &text);
+ }
+
+ text.PopOffset();
+ text.AddBlankLine();
+ text.AddLineWithOffset("private:", kScopeOffset);
+
+ text.PushOffset(kBlockOffset);
+ text.AddLine("scoped_refptr<dbus::Bus> bus_;");
+ text.AddLine("std::string service_name_;");
+ text.AddLine("dbus::ObjectPath object_path_;");
+ text.AddLine("dbus::ObjectProxy* dbus_object_proxy_;");
+ text.AddBlankLine();
+
+ text.AddLine(StringPrintf(
+ "DISALLOW_COPY_AND_ASSIGN(%s);", proxy_name.c_str()));
+ text.PopOffset();
+ text.AddLine("};");
+
+ text.AddBlankLine();
+
+ for (auto it = namespaces.rbegin(); it != namespaces.rend(); ++it) {
+ text.AddLine(StringPrintf("} // namespace %s", it->c_str()));
+ }
+ text.AddLine(StringPrintf("#endif // %s", header_guard.c_str()));
+
+ return WriteTextToFile(output_file, text);
+}
+
+// static
+void ProxyGenerator::AddConstructor(const Interface& interface,
+ const string& class_name,
+ IndentedText* text) {
+ IndentedText block;
+ block.AddLine(StringPrintf("%s(", class_name.c_str()));
+ block.PushOffset(kLineContinuationOffset);
+ block.AddLine("const scoped_refptr<dbus::Bus>& bus,");
+ block.AddLine("const std::string& service_name,");
+ block.AddLine("const std::string& object_path,");
+ block.AddLine("SignalReceiver* signal_receiver)");
+ block.AddLine(": bus_(bus),");
+ block.PushOffset(kBlockOffset);
+ block.AddLine("service_name_(service_name),");
+ block.AddLine("object_path_(object_path),");
+ block.AddLine("dbus_object_proxy_(");
+ block.AddLineWithOffset(
+ "bus_->GetObjectProxy(service_name_, object_path_)) {",
+ kLineContinuationOffset);
+ block.PopOffset();
+ block.PopOffset();
+ block.PushOffset(kBlockOffset);
+ for (const auto& signal : interface.signals) {
+ block.AddLine("chromeos::dbus_utils::ConnectToSignal(");
+ block.PushOffset(kLineContinuationOffset);
+ block.AddLine("dbus_object_proxy_,");
+ block.AddLine(StringPrintf("\"%s\",", interface.name.c_str()));
+ block.AddLine(StringPrintf("\"%s\",", signal.name.c_str()));
+ block.AddLine("base::Bind(");
+ block.PushOffset(kLineContinuationOffset);
+ block.AddLine(StringPrintf(
+ "&SignalReceiver::%s,",
+ GetHandlerNameForSignal(signal.name).c_str()));
+ block.AddLine("base::Unretained(signal_receiver)),");
+ block.PopOffset();
+ block.AddLine("base::Bind(");
+ block.PushOffset(kLineContinuationOffset);
+ block.AddLine(StringPrintf(
+ "&%s::OnDBusSignalConnected,", class_name.c_str()));
+ block.AddLine("base::Unretained(this)));");
+ block.PopOffset();
+ block.PopOffset();
+ }
+ block.PopOffset();
+ block.AddLine("}");
+
+ text->AddBlock(block);
+}
+
+// static
+void ProxyGenerator::AddDestructor(const string& class_name,
+ IndentedText* text) {
+ IndentedText block;
+ block.AddLine(StringPrintf("virtual ~%s() {", class_name.c_str()));
+ block.PushOffset(kBlockOffset);
+ block.AddLine("dbus_object_proxy_->Detach();");
+ block.AddLine(
+ "bus_->RemoveObjectProxy(service_name_, object_path_, base::Closure());");
+ block.PopOffset();
+ block.AddLine("}");
+ text->AddBlock(block);
+}
+
+// static
+void ProxyGenerator::AddSignalConnectedCallback(IndentedText* text) {
+ IndentedText block;
+ block.AddLine("void OnDBusSignalConnected(");
+ block.PushOffset(kLineContinuationOffset);
+ block.AddLine("const std::string& interface,");
+ block.AddLine("const std::string& signal,");
+ block.AddLine("bool success) {");
+ block.PopOffset();
+ block.PushOffset(kBlockOffset);
+ block.AddLine("if (!success) {");
+ block.PushOffset(kBlockOffset);
+ block.AddLine("LOG(ERROR)");
+ block.PushOffset(kLineContinuationOffset);
+ block.AddLine("<< \"Failed to connect to \" << interface << \".\" << signal");
+ block.AddLine("<< \" for \" << service_name_ << \" at \"");
+ block.AddLine("<< object_path_.value();");
+ block.PopOffset();
+ block.PopOffset();
+ block.AddLine("}");
+ block.PopOffset();
+ block.AddLine("}");
+ text->AddBlock(block);
+}
+
+// static
+void ProxyGenerator::AddMethodInterface(const Interface& interface,
+ IndentedText* text) {
+ IndentedText block;
+ block.AddLine("class SignalReceiver {");
+ block.AddLineWithOffset("public:", kScopeOffset);
+ block.PushOffset(kBlockOffset);
+ DbusSignature signature;
+ for (const auto& signal : interface.signals) {
+ if (signal.arguments.empty()) {
+ block.AddLine(StringPrintf("virtual void %s() {}",
+ GetHandlerNameForSignal(signal.name).c_str()));
+ continue;
+ }
+ block.AddLine(StringPrintf("virtual void %s(",
+ GetHandlerNameForSignal(signal.name).c_str()));
+ block.PushOffset(kLineContinuationOffset);
+ string last_argument;
+ vector<string> argument_types;
+ for (const auto& argument : signal.arguments) {
+ if (!last_argument.empty()) {
+ block.AddLine(StringPrintf("%s,", last_argument.c_str()));
+ }
+ CHECK(signature.Parse(argument.type, &last_argument));
+ if (!IsIntegralType(last_argument)) {
+ last_argument = StringPrintf("const %s&", last_argument.c_str());
+ }
+ }
+ block.AddLine(StringPrintf("%s) {}", last_argument.c_str()));
+ block.PopOffset();
+ }
+ block.PopOffset();
+ block.AddLine("};");
+
+ text->AddBlock(block);
+}
+
+// static
+void ProxyGenerator::AddMethodProxy(const Interface::Method& method,
+ const string& interface_name,
+ IndentedText* text) {
+ IndentedText block;
+ string return_type("void");
+ bool is_void_method = true;
+ DbusSignature signature;
+ if (!method.output_arguments.empty()) {
+ if (method.output_arguments.size() > 1) {
+ LOG(WARNING) << "Method " << method.name << " has "
+ << method.output_arguments.size()
+ << " output arguments which is unsupported.";
+ return;
+ }
+ CHECK(signature.Parse(method.output_arguments[0].type, &return_type));
+ is_void_method = false;
+ }
+ block.AddLine(StringPrintf("virtual %s %s(",
+ return_type.c_str(), method.name.c_str()));
+ block.PushOffset(kLineContinuationOffset);
+ vector<string> argument_names;
+ int argument_number = 0;
+ for (const auto& argument : method.input_arguments) {
+ string argument_type;
+ CHECK(signature.Parse(argument.type, &argument_type));
+ if (!IsIntegralType(argument_type)) {
+ argument_type = StringPrintf("const %s&", argument_type.c_str());
+ }
+ ++argument_number;
+ string argument_prefix(argument.name.empty() ?
+ StringPrintf("argument%d", argument_number) :
+ argument.name);
+ string argument_name(StringPrintf("%s_in", argument_prefix.c_str()));
+ argument_names.push_back(argument_name);
+ block.AddLine(StringPrintf(
+ "%s %s,", argument_type.c_str(), argument_name.c_str()));
+ }
+ block.AddLine("chromeos::ErrorPtr* error) {");
+ block.PopOffset();
+ block.PushOffset(kBlockOffset);
+
+ block.AddLine("auto response = chromeos::dbus_utils::CallMethodAndBlock(");
+ block.PushOffset(kLineContinuationOffset);
+ block.AddLine("dbus_object_proxy_,");
+ block.AddLine(StringPrintf("\"%s\",", interface_name.c_str()));
+ block.AddLine(StringPrintf("\"%s\",", method.name.c_str()));
+ string last_argument = "error";
+ for (const auto& argument_name : argument_names) {
+ block.AddLine(StringPrintf("%s,", last_argument.c_str()));
+ last_argument = argument_name;
+ }
+ block.AddLine(StringPrintf("%s);", last_argument.c_str()));
+ block.PopOffset();
+
+ if (!is_void_method) {
+ block.AddLine(StringPrintf("%s result{};", return_type.c_str()));
+ }
+ block.AddLine("if (!response) {");
+ block.AddLineWithOffset(StringPrintf(
+ "return%s;", is_void_method ? "" : " result"), kBlockOffset);
+ block.AddLine("}");
+ block.AddLine("chromeos::dbus_utils::ExtractMethodCallResults(");
+ block.AddLineWithOffset(StringPrintf("response.get(), error%s);",
+ is_void_method ? "" : ", &result"),
+ kLineContinuationOffset);
+ if (!is_void_method) {
+ block.AddLine("return result;");
+ }
+
+ block.PopOffset();
+ block.AddLine("}");
+
+ text->AddBlock(block);
+}
+
+// static
+string ProxyGenerator::GetHandlerNameForSignal(const string& signal) {
+ return StringPrintf("On%sSignal", signal.c_str());
+}
+
+} // namespace chromeos_dbus_bindings
diff --git a/chromeos-dbus-bindings/proxy_generator.h b/chromeos-dbus-bindings/proxy_generator.h
new file mode 100644
index 0000000..66dd8ba
--- /dev/null
+++ b/chromeos-dbus-bindings/proxy_generator.h
@@ -0,0 +1,63 @@
+// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_DBUS_BINDINGS_PROXY_GENERATOR_H_
+#define CHROMEOS_DBUS_BINDINGS_PROXY_GENERATOR_H_
+
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+
+#include "chromeos-dbus-bindings/header_generator.h"
+#include "chromeos-dbus-bindings/indented_text.h"
+#include "chromeos-dbus-bindings/interface.h"
+
+namespace base {
+
+class FilePath;
+
+} // namespace base
+
+namespace chromeos_dbus_bindings {
+
+class IndentedText;
+struct Interface;
+
+class ProxyGenerator : public HeaderGenerator {
+ public:
+ static bool GenerateProxy(const Interface& interface,
+ const base::FilePath& output_file);
+
+ private:
+ friend class ProxyGeneratorTest;
+
+ // Generates the constructor and destructor for the proxy.
+ static void AddConstructor(const Interface& interface,
+ const std::string& class_name,
+ IndentedText* text);
+ static void AddDestructor(const std::string& class_name,
+ IndentedText* text);
+
+ // Generates a callback for signal receiver registration completion.
+ static void AddSignalConnectedCallback(IndentedText *text);
+
+ // Generates the method signatures for signal receivers.
+ static void AddMethodInterface(const Interface& interface,
+ IndentedText* text);
+
+ // Generates a native C++ method which calls a D-Bus method on the proxy.
+ static void AddMethodProxy(const Interface::Method& interface,
+ const std::string& interface_name,
+ IndentedText* text);
+
+ // Generates the signal handler name for a given signal name.
+ static std::string GetHandlerNameForSignal(const std::string& signal);
+
+ DISALLOW_COPY_AND_ASSIGN(ProxyGenerator);
+};
+
+} // namespace chromeos_dbus_bindings
+
+#endif // CHROMEOS_DBUS_BINDINGS_PROXY_GENERATOR_H_
diff --git a/chromeos-dbus-bindings/proxy_generator_unittest.cc b/chromeos-dbus-bindings/proxy_generator_unittest.cc
new file mode 100644
index 0000000..1149de9
--- /dev/null
+++ b/chromeos-dbus-bindings/proxy_generator_unittest.cc
@@ -0,0 +1,248 @@
+// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos-dbus-bindings/proxy_generator.h"
+
+#include <string>
+#include <vector>
+
+#include <base/file_util.h>
+#include <base/files/file_path.h>
+#include <base/files/scoped_temp_dir.h>
+#include <gtest/gtest.h>
+
+#include "chromeos-dbus-bindings/interface.h"
+
+using std::string;
+using std::vector;
+using testing::Test;
+
+namespace chromeos_dbus_bindings {
+
+namespace {
+
+const char kInterfaceName[] = "org.chromium.TestInterface";
+const char kMethod1Name[] = "Elements";
+const char kMethod1Return[] = "s";
+const char kMethod1Argument1[] = "s";
+const char kMethod1ArgumentName1[] = "space_walk";
+const char kMethod1Argument2[] = "ao";
+const char kMethod1ArgumentName2[] = "ramblin_man";
+const char kMethod2Name[] = "ReturnToPatagonia";
+const char kMethod2Return[] = "x";
+const char kMethod3Name[] = "NiceWeatherForDucks";
+const char kMethod3Argument1[] = "b";
+const char kMethod4Name[] = "ExperimentNumberSix";
+const char kSignal1Name[] = "Closer";
+const char kSignal2Name[] = "TheCurseOfKaZar";
+const char kSignal2Argument1[] = "as";
+const char kSignal2Argument2[] = "y";
+const char kExpectedContent[] = R"literal_string(
+#include <string>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/callback.h>
+#include <base/logging.h>
+#include <base/macros.h>
+#include <base/memory/ref_counted.h>
+#include <chromeos/any.h>
+#include <chromeos/dbus/dbus_method_invoker.h>
+#include <chromeos/dbus/dbus_signal_handler.h>
+#include <chromeos/errors/error.h>
+#include <dbus/bus.h>
+#include <dbus/message.h>
+#include <dbus/object_path.h>
+#include <dbus/object_proxy.h>
+
+namespace org {
+namespace chromium {
+
+class TestInterfaceProxy {
+ public:
+ class SignalReceiver {
+ public:
+ virtual void OnCloserSignal() {}
+ virtual void OnTheCurseOfKaZarSignal(
+ const std::vector<std::string>&,
+ uint8_t) {}
+ };
+ TestInterfaceProxy(
+ const scoped_refptr<dbus::Bus>& bus,
+ const std::string& service_name,
+ const std::string& object_path,
+ SignalReceiver* signal_receiver)
+ : bus_(bus),
+ service_name_(service_name),
+ object_path_(object_path),
+ dbus_object_proxy_(
+ bus_->GetObjectProxy(service_name_, object_path_)) {
+ chromeos::dbus_utils::ConnectToSignal(
+ dbus_object_proxy_,
+ "org.chromium.TestInterface",
+ "Closer",
+ base::Bind(
+ &SignalReceiver::OnCloserSignal,
+ base::Unretained(signal_receiver)),
+ base::Bind(
+ &TestInterfaceProxy::OnDBusSignalConnected,
+ base::Unretained(this)));
+ chromeos::dbus_utils::ConnectToSignal(
+ dbus_object_proxy_,
+ "org.chromium.TestInterface",
+ "TheCurseOfKaZar",
+ base::Bind(
+ &SignalReceiver::OnTheCurseOfKaZarSignal,
+ base::Unretained(signal_receiver)),
+ base::Bind(
+ &TestInterfaceProxy::OnDBusSignalConnected,
+ base::Unretained(this)));
+ }
+ virtual ~TestInterfaceProxy() {
+ dbus_object_proxy_->Detach();
+ bus_->RemoveObjectProxy(service_name_, object_path_, base::Closure());
+ }
+ void OnDBusSignalConnected(
+ const std::string& interface,
+ const std::string& signal,
+ bool success) {
+ if (!success) {
+ LOG(ERROR)
+ << "Failed to connect to " << interface << "." << signal
+ << " for " << service_name_ << " at "
+ << object_path_.value();
+ }
+ }
+ virtual std::string Elements(
+ const std::string& space_walk_in,
+ const std::vector<dbus::ObjectPath>& ramblin_man_in,
+ chromeos::ErrorPtr* error) {
+ auto response = chromeos::dbus_utils::CallMethodAndBlock(
+ dbus_object_proxy_,
+ "org.chromium.TestInterface",
+ "Elements",
+ error,
+ space_walk_in,
+ ramblin_man_in);
+ std::string result{};
+ if (!response) {
+ return result;
+ }
+ chromeos::dbus_utils::ExtractMethodCallResults(
+ response.get(), error, &result);
+ return result;
+ }
+ virtual int64_t ReturnToPatagonia(
+ chromeos::ErrorPtr* error) {
+ auto response = chromeos::dbus_utils::CallMethodAndBlock(
+ dbus_object_proxy_,
+ "org.chromium.TestInterface",
+ "ReturnToPatagonia",
+ error);
+ int64_t result{};
+ if (!response) {
+ return result;
+ }
+ chromeos::dbus_utils::ExtractMethodCallResults(
+ response.get(), error, &result);
+ return result;
+ }
+ virtual void NiceWeatherForDucks(
+ bool argument1_in,
+ chromeos::ErrorPtr* error) {
+ auto response = chromeos::dbus_utils::CallMethodAndBlock(
+ dbus_object_proxy_,
+ "org.chromium.TestInterface",
+ "NiceWeatherForDucks",
+ error,
+ argument1_in);
+ if (!response) {
+ return;
+ }
+ chromeos::dbus_utils::ExtractMethodCallResults(
+ response.get(), error);
+ }
+ virtual void ExperimentNumberSix(
+ chromeos::ErrorPtr* error) {
+ auto response = chromeos::dbus_utils::CallMethodAndBlock(
+ dbus_object_proxy_,
+ "org.chromium.TestInterface",
+ "ExperimentNumberSix",
+ error);
+ if (!response) {
+ return;
+ }
+ chromeos::dbus_utils::ExtractMethodCallResults(
+ response.get(), error);
+ }
+
+ private:
+ scoped_refptr<dbus::Bus> bus_;
+ std::string service_name_;
+ dbus::ObjectPath object_path_;
+ dbus::ObjectProxy* dbus_object_proxy_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestInterfaceProxy);
+};
+
+} // namespace chromium
+} // namespace org
+)literal_string";
+
+} // namespace
+
+class ProxyGeneratorTest : public Test {
+ public:
+ void SetUp() override {
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ }
+
+ protected:
+ base::FilePath CreateInputFile(const string& contents) {
+ base::FilePath path;
+ EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(), &path));
+ EXPECT_EQ(contents.size(),
+ base::WriteFile(path, contents.c_str(), contents.size()));
+ return path;
+ }
+
+ base::ScopedTempDir temp_dir_;
+};
+
+TEST_F(ProxyGeneratorTest, GenerateAdaptors) {
+ Interface interface;
+ interface.name = kInterfaceName;
+ interface.methods.emplace_back(
+ kMethod1Name,
+ vector<Interface::Argument>{
+ {kMethod1ArgumentName1, kMethod1Argument1},
+ {kMethod1ArgumentName2, kMethod1Argument2}},
+ vector<Interface::Argument>{{"", kMethod1Return}});
+ interface.methods.emplace_back(
+ kMethod2Name,
+ vector<Interface::Argument>{},
+ vector<Interface::Argument>{{"", kMethod2Return}});
+ interface.methods.emplace_back(
+ kMethod3Name,
+ vector<Interface::Argument>{{"", kMethod3Argument1}},
+ vector<Interface::Argument>{});
+ interface.methods.emplace_back(kMethod4Name);
+ interface.signals.emplace_back(kSignal1Name);
+ interface.signals.emplace_back(
+ kSignal2Name,
+ vector<Interface::Argument>{
+ {"", kSignal2Argument1},
+ {"", kSignal2Argument2}});
+ base::FilePath output_path = temp_dir_.path().Append("output.h");
+ EXPECT_TRUE(ProxyGenerator::GenerateProxy(interface, output_path));
+ string contents;
+ EXPECT_TRUE(base::ReadFileToString(output_path, &contents));
+ // The header guards contain the (temporary) filename, so we search for
+ // the content we need within the string.
+ EXPECT_NE(string::npos, contents.find(kExpectedContent))
+ << "Expected to find the following content...\n"
+ << kExpectedContent << "...within content...\n" << contents;
+}
+
+} // namespace chromeos_dbus_bindings