StateGroupGenerator.cpp
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * Copyright (C) 2011-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved.
5 *
6 * ArmarX is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * ArmarX is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * @package
19 * @author
20 * @date
21 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
22 * GNU General Public License
23 */
24#include "StateGroupGenerator.h"
25
26#include <time.h>
27
28#include <algorithm>
29#include <fstream>
30
31#include <boost/format.hpp>
32
33#include <SimoxUtility/algorithm/string/string_tools.h>
34
40
42
43using namespace armarx;
44
45Ice::StringSeq
46StatechartGroupGenerator::findStatechartGroupFiles(const std::string& statechartsPath)
47{
48 Ice::StringSeq groups;
49
50 for (std::filesystem::recursive_directory_iterator end, dir(statechartsPath); dir != end; ++dir)
51 {
52 // search for all statechart group xml files
53 if (dir->path().extension() == ".scgxml" &&
54 dir->path().string().find("deprecated") == std::string::npos)
55 {
56 groups.push_back(dir->path().string());
57 ARMARX_DEBUG << dir->path().string();
58 }
59 }
60 return groups;
61}
62
63bool
64StatechartGroupGenerator::generateStateFile(const std::string& statechartGroupXmlFilePath,
65 const std::string& statePath,
66 const std::string& packagePath,
67 const std::optional<std::string>& packageIncludePath)
68{
70 reader->readXml(std::filesystem::path(statechartGroupXmlFilePath));
71 //ARMARX_INFO_S << reader->getPackageName();
72 CMakePackageFinder finder(reader->getPackageName(), packagePath, false, false);
74 << reader->getPackageName() << " at " << packagePath;
75 std::filesystem::path buildDir = finder.getBuildDir();
76
77 VariantInfoPtr variantInfo =
78 VariantInfo::ReadInfoFilesRecursive(reader->getPackageName(), packagePath, false);
79
80 std::filesystem::path boostStatePath(statePath);
81 return generateStateFile(boostStatePath.filename().replace_extension().string(),
82 RapidXmlReader::FromFile(statePath),
83 buildDir,
84 reader->getPackageName(),
85 reader->getGroupName(),
86 reader->getProxies(),
87 reader->contextGenerationEnabled(),
88 variantInfo,
89 false,
90 packageIncludePath.value_or(reader->getPackageName()));
91}
92
93std::string
94packageToIncludePath(const std::string& packageName)
95{
96 const auto splits = simox::alg::split(packageName, "_");
97 return simox::alg::join(splits, "/");
98}
99
100bool
102 RapidXmlReaderPtr reader,
103 std::filesystem::path buildDir,
104 const std::string& packageName,
105 const std::string& groupName,
106 const std::vector<std::string>& proxies,
107 bool contextGenerationEnabled,
108 const VariantInfoPtr& variantInfo,
109 bool forceRewrite,
110 const std::optional<std::string>& packageIncludePath)
111{
112 std::filesystem::path outputPath = buildDir / "source" /
113 packageIncludePath.value_or(packageName) / "statecharts" /
114 groupName / (stateName + ".generated.h");
115
116 std::filesystem::path dir = outputPath;
117 dir.remove_filename();
118 std::filesystem::create_directories(dir);
119 // ARMARX_INFO_S << "generating into " << outputPath.string();
120
121 std::vector<std::string> namespaces({"armarx", groupName});
123 namespaces, reader, proxies, contextGenerationEnabled, groupName, variantInfo);
124
125 time_t rawtime;
126 struct tm* timeinfo;
127 char buffer[80];
128 time(&rawtime);
129 timeinfo = localtime(&rawtime);
130 strftime(buffer, 80, "%Y-%m-%d %H:%M:%S%n", timeinfo);
131 writeFileContents(outputPath.string() + ".touch", std::string(buffer));
132
133 if (cpp.empty())
134 {
135 ARMARX_WARNING << "Could not generate state file!";
136 return false;
137 }
138
139 if (forceRewrite)
140 {
141 writeFileContents(outputPath.string(), cpp);
142 return true;
143 }
144
145 writeFileContentsIfChanged(outputPath.string(), cpp);
146 return true;
147
148 //ARMARX_INFO_S << "written " << cpp.length() << " bytes to " << outputPath.string();
149}
150
151bool
153 const std::string& statechartGroupXmlFilePath,
154 const std::string& packagePath,
155 const std::optional<std::string>& packageIncludePath)
156{
158 reader->readXml(std::filesystem::path(statechartGroupXmlFilePath));
159
160 if (!reader->contextGenerationEnabled())
161 {
162 throw LocalException("Will not generate context for ")
163 << statechartGroupXmlFilePath << ". Context generation is not enabled for this group.";
164 }
165
166 VariantInfoPtr variantInfo =
167 VariantInfo::ReadInfoFilesRecursive(reader->getPackageName(), packagePath, false);
168
169 CMakePackageFinder finder(reader->getPackageName(), packagePath, false, false);
171 << reader->getPackageName() << " at " << packagePath;
172 std::filesystem::path buildDir = finder.getBuildDir();
173
174 return generateStatechartContextFile(buildDir,
175 reader->getPackageName(),
176 reader->getGroupName(),
177 reader->getProxies(),
178 variantInfo,
179 getVariantTypesOfStatesWithNoCpp(reader, variantInfo),
180 false,
181 packageIncludePath);
182}
183
184bool
186 std::filesystem::path buildDir,
187 const std::string& packageName,
188 const std::string& groupName,
189 const std::vector<std::string>& proxies,
190 const VariantInfoPtr& variantInfo,
191 const std::set<std::string>& usedVariantTypes,
192 bool forceRewrite,
193 const std::optional<std::string>& packageIncludePath)
194{
195 std::filesystem::path outputPath =
196 buildDir / "source" / packageIncludePath.value_or(packageName) / "statecharts" / groupName;
197 std::filesystem::path baseClassPath =
198 outputPath / (groupName + "StatechartContextBase.generated.h");
199 outputPath /= (groupName + "StatechartContext.generated.h");
200
201 std::filesystem::path dir = outputPath;
202
203 dir.remove_filename();
204 std::filesystem::create_directories(dir);
205
206 // ARMARX_INFO_S << "generating into " << outputPath.string();
207 std::vector<std::string> namespaces({"armarx", groupName});
208 std::string cpp, cppBase;
209 std::tie(cpp, cppBase) = XmlContextBaseClassGenerator::GenerateCpp(
210 namespaces, proxies, groupName, variantInfo, usedVariantTypes);
211
212 time_t rawtime;
213 struct tm* timeinfo;
214 char buffer[80];
215 time(&rawtime);
216 timeinfo = localtime(&rawtime);
217 strftime(buffer, 80, "%Y-%m-%d %H:%M:%S%n", timeinfo);
218 writeFileContents(outputPath.string() + ".touch", std::string(buffer));
219 writeFileContents(baseClassPath.string() + ".touch", std::string(buffer));
220
221 ARMARX_DEBUG << "Filename: " << outputPath.string();
222 ARMARX_DEBUG << "Content: \n" << cpp;
223
224 if (cpp.empty())
225 {
226 ARMARX_WARNING << "Failed to generate statechart context file!";
227 return false;
228 }
229
230 if (forceRewrite)
231 {
232 writeFileContents(outputPath.string(), cpp);
233 writeFileContents(baseClassPath.string(), cppBase);
234 return true;
235 }
236
237 bool result = writeFileContentsIfChanged(outputPath.string(), cpp);
238 writeFileContentsIfChanged(baseClassPath.string(), cppBase) || result;
239
240 return true;
241
242 //ARMARX_INFO_S << "written " << cpp.length() << " bytes to " << outputPath.string();
243}
244
245bool
248 const std::filesystem::path& buildDir,
249 VariantInfoPtr variantInfo,
250 bool forceRewrite,
251 const std::optional<std::string>& packageIncludePath,
252 const bool nextGenBehavior)
253{
254 ARMARX_DEBUG << "Generating cmake file for " << reader->getGroupName();
255
256 ARMARX_CHECK_EXPRESSION(std::filesystem::exists(buildDir)) << buildDir.string();
257 auto groupName = reader->getGroupName();
258
259 auto packageName = reader->getPackageName();
260
261 std::filesystem::path outputPath =
262 buildDir / "source" / packageIncludePath.value_or(packageName) / "statecharts" / groupName;
263 std::filesystem::path generatedFileName = outputPath / (groupName + "Files.generated.cmake");
264 std::filesystem::path dir = outputPath;
265 dir.remove_filename();
266 std::filesystem::create_directories(dir);
267
268
269 Ice::StringSeq xmlFiles, headerFiles, sourceFiles;
270 xmlFiles = reader->getStateFilepaths();
271
272
273 Ice::StringSeq libs;
274 // add libs required for all statecharts:
275 libs.push_back("ArmarXCore");
276 libs.push_back("ArmarXCoreStatechart");
277 libs.push_back("ArmarXCoreObservers");
278
279 for (std::string& xmlFile : xmlFiles)
280 {
281 auto stateNode = RapidXmlReader::FromFile(xmlFile);
283 stateNode->getRoot("State"), variantInfo);
284 // ARMARX_INFO << "types: " << (Ice::StringSeq(types.begin(), types.end()));
285
286 for (std::string& lib :
287 variantInfo->findLibNames(Ice::StringSeq(types.begin(), types.end())))
288 {
289 auto libName = lib;
290 if (!Contains(libs, libName))
291 {
292 libs.push_back(libName);
293 }
294 }
295 }
296
297 auto proxies = reader->getProxies();
298
299 for (VariantInfo::LibEntryPtr lib : variantInfo->getLibs())
300 {
301 auto libName = lib->getName();
302 for (VariantInfo::ProxyEntryPtr proxy : lib->getProxies())
303 {
304 std::string proxyMemberName = proxy->getMemberName();
305
306 std::string proxyId = boost::str(boost::format("%s.%s") % libName % proxyMemberName);
307 if (std::find(proxies.begin(), proxies.end(), proxyId) != proxies.end())
308 {
309 if (!Contains(libs, libName))
310 {
311 libs.push_back(libName);
312 }
313
314 for (auto& additionalLibName : proxy->getLibraries())
315 {
316 if (!Contains(libs, additionalLibName))
317 {
318 libs.push_back(additionalLibName);
319 }
320 }
321 }
322 }
323 }
324
325 // ARMARX_INFO << VAROUT(libs);
326
327
328 std::filesystem::path groupFilePath(reader->getGroupDefinitionFilePath());
329 std::filesystem::path groupDir = groupFilePath.parent_path();
330
331 for (std::string& xmlFile : xmlFiles)
332 {
333
334 auto headerFilePath = std::filesystem::path(xmlFile).replace_extension("h");
335 // ARMARX_INFO << "Checking " << headerFilePath;
336 if (std::filesystem::exists(headerFilePath))
337 {
338 headerFiles.push_back(
339 "./" + ArmarXDataPath::relativeTo(groupDir.string(), headerFilePath.string()));
340 }
341 auto sourceFilePath = std::filesystem::path(xmlFile).replace_extension("cpp");
342 if (std::filesystem::exists(sourceFilePath))
343 {
344 sourceFiles.push_back(
345 "./" + ArmarXDataPath::relativeTo(groupDir.string(), sourceFilePath.string()));
346 }
347 xmlFile = "./" + ArmarXDataPath::relativeTo(groupDir.string(), xmlFile);
348 }
349
350 const std::string cmakeFileContent = [&]() -> std::string
351 {
352 if (nextGenBehavior)
353 {
355 groupName, xmlFiles, sourceFiles, headerFiles, libs);
356 }
357
359 groupName, xmlFiles, sourceFiles, headerFiles, libs);
360 }();
361
362 time_t rawtime;
363 struct tm* timeinfo;
364 char buffer[80];
365 time(&rawtime);
366 timeinfo = localtime(&rawtime);
367 strftime(buffer, 80, "%Y-%m-%d %H:%M:%S%n", timeinfo);
368
369 ARMARX_DEBUG << "Writing file " << outputPath;
370
371 writeFileContents(outputPath.string() + ".touch", std::string(buffer));
372 // writeFileContents(baseClassPath.string() + ".touch", std::string(buffer));
373
374 if (std::string(buffer).empty())
375 {
376 ARMARX_WARNING << "Failed to generate statechart context file!";
377 return false;
378 }
379
380 if (forceRewrite)
381 {
382 writeFileContents(outputPath.string(), cmakeFileContent);
383 return true;
384 }
385
386 writeFileContentsIfChanged(generatedFileName.string(), cmakeFileContent);
387
388 return true;
389}
390
391bool
393 const std::string& statechartGroupXmlFilePath,
394 const std::filesystem::path& buildDir,
395 bool forceRewrite,
396 const std::optional<std::string>& packageIncludePath,
397 const bool nextGenBehavior)
398{
399
401 reader->readXml(std::filesystem::path(statechartGroupXmlFilePath));
402
403 VariantInfoPtr variantInfo =
404 VariantInfo::ReadInfoFilesRecursive(reader->getPackageName(), buildDir.string(), false);
406 reader, buildDir, variantInfo, forceRewrite, packageIncludePath, nextGenBehavior);
407}
408
409bool
411 const std::string& packageName,
412 const std::string& statechartsDir,
413 const std::filesystem::path& buildDir,
414 bool forceRewrite,
415 const std::string& dataDir,
416 const std::map<std::string, std::string>& dependencies,
417 const std::optional<std::string>& packageIncludePath,
418 const bool nextGenBehavior)
419{
420 bool written = false;
421 VariantInfoPtr variantInfo =
422 readVariantInfoWithPaths(packageName, buildDir.string(), dataDir, dependencies);
423 auto groupFiles = findStatechartGroupFiles(statechartsDir);
424 for (auto& group : groupFiles)
425 {
427 reader->readXml(std::filesystem::path(group));
428
430 reader, buildDir, variantInfo, forceRewrite, packageIncludePath, nextGenBehavior);
431 }
432 return written;
433}
434
436StatechartGroupGenerator::readVariantInfoWithPaths(
437 const std::string& packageName,
438 const std::string& buildDir,
439 const std::string& dataDir,
440 const std::map<std::string, std::string>& dependencies)
441{
442 VariantInfoPtr variantInfo(new VariantInfo());
443 std::filesystem::path variantInfoFile(dataDir);
444 variantInfoFile /= packageName;
445 variantInfoFile /= "VariantInfo-" + packageName + ".xml";
446
447 if (!variantInfo->isPackageLoaded(packageName) && std::filesystem::exists(variantInfoFile))
448 {
449 RapidXmlReaderPtr xmlReader = RapidXmlReader::FromFile(variantInfoFile.string());
450 variantInfo->readVariantInfo(xmlReader, buildDir, packageName);
451 ARMARX_DEBUG_S << "Read " << variantInfoFile.string();
452 }
453 for (auto& dep : dependencies)
454 {
455 variantInfo =
456 VariantInfo::ReadInfoFilesRecursive(dep.first, dep.second, false, variantInfo);
457 }
458 return variantInfo;
459}
460
461bool
463 const std::string& packageName,
464 const std::string& statechartGroupXmlFilePath,
465 const std::filesystem::path& buildDir,
466 bool forceRewrite,
467 const std::string& dataDir,
468 const std::map<std::string, std::string>& dependencies,
469 const std::optional<std::string>& packageIncludePath,
470 const bool nextGenBehavior)
471{
472 auto variantInfo =
473 readVariantInfoWithPaths(packageName, buildDir.string(), dataDir, dependencies);
474
476 reader->readXml(std::filesystem::path(statechartGroupXmlFilePath));
477
479 reader, buildDir, variantInfo, forceRewrite, packageIncludePath, nextGenBehavior);
480}
481
482bool
484 const std::string& contents)
485{
486 bool exists = std::filesystem::exists(std::filesystem::path(path));
487 auto oldContent = exists ? RapidXmlReader::ReadFileContents(path) : "";
488 if (exists && oldContent == contents)
489 {
490 return false;
491 }
492 ARMARX_DEBUG << "Writing " << path << std::endl;
493 writeFileContents(path, contents);
494 // if (!oldContent.empty())
495 // {
496 // writeFileContents(path + ".old", oldContent);
497 // }
498 return true;
499}
500
501void
502StatechartGroupGenerator::writeFileContents(const std::string& path, const std::string& contents)
503{
504 std::filesystem::create_directories(std::filesystem::path(path).parent_path());
505 std::ofstream file;
506 file.open(path.c_str());
507
508 if (!file.is_open())
509 {
511 }
512 ARMARX_CHECK_EXPRESSION(!contents.empty()) << path;
513 ARMARX_DEBUG << "Writing to " << path;
514 file << contents;
515 file.flush();
516 file.close();
517}
518
519std::set<std::string>
520StatechartGroupGenerator::getVariantTypesOfStatesWithNoCpp(StatechartGroupXmlReaderPtr groupReader,
521 const VariantInfoPtr& variantInfo)
522{
523 std::set<std::string> variantTypes;
524 std::set<std::string> alreadyLinkedTypes;
525 for (auto& statefile : groupReader->getStateFilepaths())
526 {
527 std::set<std::string>* currentTypeSet = &variantTypes;
528 RapidXmlReaderPtr stateReader = RapidXmlReader::FromFile(statefile);
529 auto headerFilePath = std::filesystem::path(statefile);
530 headerFilePath.replace_extension(std::filesystem::path("h"));
531 if (std::filesystem::exists(headerFilePath))
532 {
533 currentTypeSet = &alreadyLinkedTypes;
534 }
535 RapidXmlReaderNode stateNode = stateReader->getRoot("State");
536 std::set<std::string> types =
538 currentTypeSet->insert(types.begin(), types.end());
539 }
540 std::set<std::string> results;
541 std::set_difference(variantTypes.begin(),
542 variantTypes.end(),
543 alreadyLinkedTypes.begin(),
544 alreadyLinkedTypes.end(),
545 std::inserter(results, results.end()));
546 return results;
547}
548
549std::set<std::string>
550StatechartGroupGenerator::getVariantTypesOfStates(StatechartGroupXmlReaderPtr groupReader,
551 const VariantInfoPtr& variantInfo)
552{
553 std::set<std::string> variantTypes;
554
555 for (auto& statefile : groupReader->getStateFilepaths())
556 {
557 RapidXmlReaderPtr stateReader = RapidXmlReader::FromFile(statefile);
558 ARMARX_INFO << "Getting Variant types for " << statefile;
559 RapidXmlReaderNode stateNode = stateReader->getRoot("State");
560 std::set<std::string> types =
562 ARMARX_INFO << "Found variant types: " << Ice::StringSeq(types.begin(), types.end());
563 variantTypes.insert(types.begin(), types.end());
564 }
565
566 return variantTypes;
567}
std::string packageToIncludePath(const std::string &packageName)
static std::string relativeTo(const std::string &from, const std::string &to)
Transform an absolute filepath into a relative path of the other absolute filepath.
The CMakePackageFinder class provides an interface to the CMake Package finder capabilities.
std::string getBuildDir() const
bool packageFound() const
Returns whether or not this package was found with cmake.
static std::string GenerateCMakeFile(const std::string &groupName, const std::vector< std::string > &stateXMLFiles, std::vector< std::string > const &stateSourceFiles, std::vector< std::string > const &stateHeaderFiles, const std::vector< std::string > &libs)
static std::string GenerateCMakeFileNextGen(const std::string &groupName, const std::vector< std::string > &stateXMLFiles, std::vector< std::string > const &stateSourceFiles, std::vector< std::string > const &stateHeaderFiles, const std::vector< std::string > &libs)
static std::string ReadFileContents(const std::string &path)
static RapidXmlReaderPtr FromFile(const std::string &path)
static void writeFileContents(const std::string &path, const std::string &contents)
static bool generateStatechartGroupCMakeSourceListFiles(const std::string &packageName, const std::string &statechartGroupXmlFilePath, const std::filesystem::path &buildDir, bool forceRewrite, const std::string &dataDir, const std::map< std::string, std::string > &dependencies, const std::optional< std::string > &packageIncludePath=std::nullopt, bool nextGenBehavior=false)
static Ice::StringSeq findStatechartGroupFiles(const std::string &statechartsPath)
static bool generateStateFile(const std::string &statechartGroupXmlFilePath, const std::string &statePath, const std::string &packagePath, const std::optional< std::string > &packageIncludePath=std::nullopt)
static bool generateStatechartContextFile(const std::string &statechartGroupXmlFilePath, const std::string &packagePath, const std::optional< std::string > &packageIncludePath=std::nullopt)
static bool generateStatechartGroupCMakeSourceListFile(const std::string &statechartGroupXmlFilePath, const std::filesystem::path &buildDir, bool forceRewrite, const std::optional< std::string > &packageIncludePath=std::nullopt, bool nextGenBehavior=false)
static bool writeFileContentsIfChanged(const std::string &path, const std::string &contents)
static VariantInfoPtr ReadInfoFilesRecursive(const std::string &rootPackageName, const std::string &rootPackagePath, bool showErrors, VariantInfoPtr variantInfo=VariantInfoPtr())
std::shared_ptr< LibEntry > LibEntryPtr
std::shared_ptr< ProxyEntry > ProxyEntryPtr
Definition VariantInfo.h:76
static std::tuple< std::string, std::string > GenerateCpp(std::vector< std::string > namespaces, std::vector< std::string > proxies, std::string groupName, VariantInfoPtr variantInfo, const std::set< std::string > &usedVariantTypes)
static std::string GenerateCpp(std::vector< std::string > namespaces, RapidXmlReaderPtr reader, const std::vector< std::string > &proxies, bool contextGenerationEnabled, std::string groupName, VariantInfoPtr variantInfo)
static std::set< std::string > GetUsedInnerNonBasicVariantTypes(RapidXmlReaderNode stateNode, VariantInfoPtr variantInfo)
#define ARMARX_CHECK_EXPRESSION(expression)
This macro evaluates the expression and if it turns out to be false it will throw an ExpressionExcept...
#define ARMARX_DEBUG_S
The logging level for output that is only interesting while debugging.
Definition Logging.h:205
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
Definition Logging.h:184
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
This file offers overloads of toIce() and fromIce() functions for STL container types.
std::shared_ptr< StatechartGroupXmlReader > StatechartGroupXmlReaderPtr
std::shared_ptr< RapidXmlReader > RapidXmlReaderPtr
std::shared_ptr< VariantInfo > VariantInfoPtr
Definition VariantInfo.h:39
std::shared_ptr< class StatechartProfile > StatechartProfilePtr
bool Contains(const ContainerType &container, const ElementType &searchElement)
Definition algorithm.h:330