| // 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 <memory> |
| #include <string> |
| |
| #include <base/command_line.h> |
| #include <base/files/file_path.h> |
| #include <base/files/file_util.h> |
| #include <base/json/json_reader.h> |
| #include <base/logging.h> |
| #include <base/strings/string_util.h> |
| #include <base/values.h> |
| #include <brillo/syslog_logging.h> |
| |
| #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" |
| |
| using chromeos_dbus_bindings::AdaptorGenerator; |
| using chromeos_dbus_bindings::MethodNameGenerator; |
| using chromeos_dbus_bindings::ProxyGenerator; |
| using chromeos_dbus_bindings::ServiceConfig; |
| |
| namespace switches { |
| |
| static const char kHelp[] = "help"; |
| static const char kMethodNames[] = "method-names"; |
| static const char kAdaptor[] = "adaptor"; |
| static const char kProxy[] = "proxy"; |
| static const char kMock[] = "mock"; |
| static const char kProxyPathForMocks[] = "proxy-path-in-mocks"; |
| static const char kServiceConfig[] = "service-config"; |
| static const char kHelpMessage[] = "\n" |
| "generate-chromeos-dbus-bindings itf1.xml [itf2.xml...] [switches]\n" |
| " itf1.xml, ... = the input interface file(s) [mandatory].\n" |
| "Available Switches: \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 name containing the DBus adaptor class.\n" |
| " --proxy=<proxy header filename>\n" |
| " The output header file name containing the DBus proxy class.\n" |
| " --mock=<mock header filename>\n" |
| " The output header file name containing the DBus proxy mock class.\n" |
| " --service-config=<config.json>\n" |
| " The DBus service configuration file for the generator.\n"; |
| |
| } // namespace switches |
| |
| namespace { |
| // GYP sometimes enclosed the target file name in extra set of quotes like: |
| // generate-chromeos-dbus-bindings in.xml "--adaptor=\"out.h\"" |
| // So, this function helps us to remove them. |
| base::FilePath RemoveQuotes(const std::string& path) { |
| std::string unquoted; |
| base::TrimString(path, "\"'", &unquoted); |
| return base::FilePath{unquoted}; |
| } |
| |
| // Makes a canonical path by making the path absolute and by removing any |
| // '..' which makes base::ReadFileToString() to fail. |
| base::FilePath SanitizeFilePath(const std::string& path) { |
| base::FilePath path_in = RemoveQuotes(path); |
| base::FilePath path_out = base::MakeAbsoluteFilePath(path_in); |
| if (path_out.value().empty()) { |
| LOG(WARNING) << "Failed to canonicalize '" << path << "'"; |
| path_out = path_in; |
| } |
| return path_out; |
| } |
| |
| |
| // Load the service configuration from the provided JSON file. |
| bool LoadConfig(const base::FilePath& path, ServiceConfig *config) { |
| std::string contents; |
| if (!base::ReadFileToString(path, &contents)) |
| return false; |
| |
| std::unique_ptr<base::Value> json{base::JSONReader::Read(contents).release()}; |
| if (!json) |
| return false; |
| |
| base::DictionaryValue* dict = nullptr; // Aliased with |json|. |
| if (!json->GetAsDictionary(&dict)) |
| return false; |
| |
| dict->GetStringWithoutPathExpansion("service_name", &config->service_name); |
| |
| base::DictionaryValue* om_dict = nullptr; // Owned by |dict|. |
| if (dict->GetDictionaryWithoutPathExpansion("object_manager", &om_dict)) { |
| if (!om_dict->GetStringWithoutPathExpansion("name", |
| &config->object_manager.name) && |
| !config->service_name.empty()) { |
| config->object_manager.name = config->service_name + ".ObjectManager"; |
| } |
| om_dict->GetStringWithoutPathExpansion("object_path", |
| &config->object_manager.object_path); |
| if (config->object_manager.name.empty()) { |
| LOG(ERROR) << "Object manager name is missing."; |
| return false; |
| } |
| } |
| |
| base::ListValue* list = nullptr; // Owned by |dict|. |
| if (dict->GetListWithoutPathExpansion("ignore_interfaces", &list)) { |
| config->ignore_interfaces.reserve(list->GetSize()); |
| for (const auto& item : *list) { |
| std::string interface_name; |
| if (!item->GetAsString(&interface_name)) { |
| LOG(ERROR) << "Invalid interface name in [ignore_interfaces] section"; |
| return false; |
| } |
| config->ignore_interfaces.push_back(interface_name); |
| } |
| } |
| |
| return true; |
| } |
| |
| } // anonymous namespace |
| |
| int main(int argc, char** argv) { |
| base::CommandLine::Init(argc, argv); |
| base::CommandLine* cl = base::CommandLine::ForCurrentProcess(); |
| |
| // Setup logging to stderr. This also parses some implicit flags using the |
| // CommandLine singleton. |
| brillo::InitLog(brillo::kLogToStderr | brillo::kLogHeader); |
| |
| if (cl->HasSwitch(switches::kHelp)) { |
| LOG(INFO) << switches::kHelpMessage; |
| return 0; |
| } |
| |
| auto input_files = cl->GetArgs(); |
| if (input_files.empty()) { |
| LOG(ERROR) << "At least one file must be specified."; |
| LOG(ERROR) << switches::kHelpMessage; |
| return 1; |
| } |
| |
| ServiceConfig config; |
| if (cl->HasSwitch(switches::kServiceConfig)) { |
| std::string config_file = cl->GetSwitchValueASCII(switches::kServiceConfig); |
| if (!config_file.empty() && |
| !LoadConfig(SanitizeFilePath(config_file), &config)) { |
| LOG(ERROR) << "Failed to load DBus service config file " << config_file; |
| return 1; |
| } |
| } |
| |
| chromeos_dbus_bindings::XmlInterfaceParser parser; |
| for (const auto& input : input_files) { |
| std::string contents; |
| if (!base::ReadFileToString(SanitizeFilePath(input), &contents)) { |
| LOG(ERROR) << "Failed to read file " << input; |
| return 1; |
| } |
| if (!parser.ParseXmlInterfaceFile(contents, config.ignore_interfaces)) { |
| LOG(ERROR) << "Failed to parse interface file " << input; |
| return 1; |
| } |
| } |
| |
| if (cl->HasSwitch(switches::kMethodNames)) { |
| std::string method_name_file = |
| cl->GetSwitchValueASCII(switches::kMethodNames); |
| VLOG(1) << "Outputting method names to " << method_name_file; |
| if (!MethodNameGenerator::GenerateMethodNames( |
| parser.interfaces(), |
| RemoveQuotes(method_name_file))) { |
| LOG(ERROR) << "Failed to output method names."; |
| return 1; |
| } |
| } |
| |
| if (cl->HasSwitch(switches::kAdaptor)) { |
| std::string adaptor_file = cl->GetSwitchValueASCII(switches::kAdaptor); |
| VLOG(1) << "Outputting adaptor to " << adaptor_file; |
| if (!AdaptorGenerator::GenerateAdaptors(parser.interfaces(), |
| RemoveQuotes(adaptor_file))) { |
| LOG(ERROR) << "Failed to output adaptor."; |
| return 1; |
| } |
| } |
| |
| base::FilePath proxy_path; // Used by both Proxy and Mock generation. |
| if (cl->HasSwitch(switches::kProxy)) { |
| std::string proxy_file = cl->GetSwitchValueASCII(switches::kProxy); |
| proxy_path = RemoveQuotes(proxy_file); |
| base::NormalizeFilePath(proxy_path, &proxy_path); |
| VLOG(1) << "Outputting proxy to " << proxy_path.value(); |
| if (!ProxyGenerator::GenerateProxies(config, parser.interfaces(), |
| proxy_path)) { |
| LOG(ERROR) << "Failed to output proxy."; |
| return 1; |
| } |
| } |
| |
| base::FilePath proxy_include_path = proxy_path; |
| bool use_literal_include_path = false; |
| if (cl->HasSwitch(switches::kProxyPathForMocks)) { |
| std::string proxy_file_in_mocks = |
| cl->GetSwitchValueASCII(switches::kProxyPathForMocks); |
| proxy_include_path = RemoveQuotes(proxy_file_in_mocks); |
| use_literal_include_path = true; |
| } |
| |
| if (cl->HasSwitch(switches::kMock)) { |
| std::string mock_file = cl->GetSwitchValueASCII(switches::kMock); |
| base::FilePath mock_path = RemoveQuotes(mock_file); |
| base::NormalizeFilePath(mock_path, &mock_path); |
| VLOG(1) << "Outputting mock to " << mock_path.value(); |
| if (!ProxyGenerator::GenerateMocks(config, parser.interfaces(), mock_path, |
| proxy_include_path, |
| use_literal_include_path)) { |
| LOG(ERROR) << "Failed to output mock."; |
| return 1; |
| } |
| } |
| |
| return 0; |
| } |