AutoAPMS
Resilient Robot Mission Management
Loading...
Searching...
No Matches
create_node_manifest.cpp
Go to the documentation of this file.
1// Copyright 2024 Robin Müller
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include <filesystem>
16#include <iostream>
17#include <set>
18
24
25using namespace auto_apms_behavior_tree;
26
27int main(int argc, char** argv)
28{
29 if (argc < 5)
30 {
31 std::cerr << "create_node_manifest: Missing inputs! The program requires: \n\t1.) the yaml "
32 "node manifest files (separated by ';').\n\t2.) Build information for nodes supposed to be "
33 "registered during build time (List of '<class_name>@<library_build_path>' "
34 "separated by ';').\n\t3.) The name of the package that provides the build targets.\n\t4.) Output "
35 "file for the complete node plugin manifest.\n\t";
36 std::cerr << "Usage: create_node_manifest <manifest_files> <build_infos> <build_package_name> "
37 "<output_file>\n";
38 return EXIT_FAILURE;
39 }
40
41 try
42 {
43 std::vector<std::string> manifest_files;
44 for (const auto& path : auto_apms_core::util::splitString(argv[1], ";"))
45 {
46 manifest_files.push_back(std::filesystem::absolute(path).string());
47 }
48 const std::vector<std::string> build_infos = auto_apms_core::util::splitString(argv[2], ";");
49 const std::string build_package_name = argv[3];
50 const std::filesystem::path output_file{ std::filesystem::absolute(argv[4]) };
51
52 // Ensure that arguments are not empty
53 if (manifest_files.empty())
54 {
55 throw std::runtime_error("Argument manifest_files must not be empty");
56 }
57 if (output_file.empty())
58 {
59 throw std::runtime_error("Argument output_file must not be empty.");
60 }
61
62 // Ensure correct extensions
63 if (output_file.extension() != ".yaml")
64 {
65 throw std::runtime_error("Output file '" + output_file.string() + "' has wrong extension. Must be '.yaml'.");
66 }
67
68 // Retrieve plugin library paths from build info
69 std::map<std::string, std::string> build_lib_paths;
70 for (const auto& build_info : build_infos)
71 {
72 std::vector<std::string> parts = auto_apms_core::util::splitString(build_info, "@");
73 if (parts.size() != 2)
74 {
75 throw std::runtime_error("Invalid build info entry ('" + build_info + "').");
76 }
77 const std::string& class_name = parts[0];
78 const std::string& build_path = parts[1];
79 if (build_lib_paths.find(class_name) != build_lib_paths.end())
80 {
81 throw std::runtime_error("Node class '" + class_name + "' is specified multiple times in build infos.");
82 }
83 build_lib_paths[class_name] = build_path; // {class_name: build_path}
84 }
85
86 auto output_manifest = NodeManifest::fromFiles(manifest_files);
87 auto all_but_build_package =
88 auto_apms_core::getAllPackagesWithResource(_AUTO_APMS_BEHAVIOR_TREE__RESOURCE_TYPE_NAME__NODE);
89 all_but_build_package.erase(build_package_name);
90
97 NodePluginClassLoader loader(all_but_build_package);
98 for (const auto& [node_name, params] : output_manifest.getInternalMap())
99 {
100 try
101 {
102 output_manifest[node_name] = NodeManifest({ { node_name, params } }).autoComplete(loader)[node_name];
103 }
105 {
106 if (build_lib_paths.find(params.class_name) == build_lib_paths.end())
107 {
108 throw std::runtime_error("Node '" + node_name + "' (Class '" + params.class_name +
109 "') cannot be found. It's not being built by this package (" + build_package_name +
110 ") and is also not provided by any other package. For a node to be discoverable, "
111 "one must register it using auto_apms_behavior_tree_register_nodes() in the "
112 "CMakeLists.txt of a ROS 2 package.");
113 }
114 // Store the temporary library path to be used during build time until the install is available
115 output_manifest[node_name].library = build_lib_paths[params.class_name];
116 output_manifest[node_name].package = build_package_name;
117 }
118 }
119
120 // Save the manifest
121 output_manifest.toFile(output_file);
122
123 // Print unique list of libraries to stdout
124 std::set<std::string> paths;
125 for (const auto& [node_name, params] : output_manifest.getInternalMap())
126 {
127 const auto& path = params.library;
128 if (const auto& [_, success] = paths.insert(path); success)
129 {
130 std::cout << path << ';';
131 }
132 }
133 }
134 catch (const std::exception& e)
135 {
136 std::cerr << "ERROR (create_node_manifest): " << e.what() << "\n";
137 return EXIT_FAILURE;
138 }
139 return EXIT_SUCCESS;
140}
Data structure for resource lookup data and configuration parameters required for loading and registe...
static NodeManifest fromFiles(const std::vector< std::string > &file_paths)
Create a node plugin manifest from multiple files. They are loaded in the given order.
Version of pluginlib::ClassLoader specifically for loading installed behavior tree node plugins.
int main(int argc, char **argv)
std::vector< std::string > splitString(const std::string &str, const std::string &delimiter, bool preserve_empty=true)
Split a string into multiple tokens using a specific delimiter string (Delimiter may consist of multi...
Definition string.cpp:24
std::set< std::string > getAllPackagesWithResource(const std::string &resource_type)
Collect all package names that register a certain type of ament_index resources.
Definition resources.cpp:25