/
3bbd303.patch
207 lines (191 loc) · 6.8 KB
/
3bbd303.patch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
From 3bbd303c8b94b20244d102eae095ffcbaa4c550d Mon Sep 17 00:00:00 2001
From: Jeremy Nimmer <jeremy.nimmer@tri.global>
Date: Thu, 7 May 2020 19:09:57 -0400
Subject: [PATCH] Simplify data embedding (#270)
This is an internal-only refactoring that changes the mechanics of how
the sdf/** file data is embedded into the library.
For one, it solves the "static initialization order fiasco" for the
embedded data. The data is only loaded into memory upon first use,
instead of as the library is being loaded.
It also simplifies the ruby embedding code, making it more concise.
I hope this is easier to maintain, but it also helps me when sharing
Drake with consumers that always build from source, but will not
install ruby as a compile-time prerequisite
The EmbeddedSdf codegen now emits the cc file with the data; the header
is stored in git. This provides an easier way to use a function to
retrieve the embedded strings as static locals.
The embedded SDF data is now combined into a single map. Embedding
files should be boring, ala the WIP std::embed specification. It
should not include application-layer logic encoding upgrade paths.
This will also make it easier to avoid the "static destruction fiasco"
in future commits, due to fewer functions to repair.
diff --git a/src/Converter.cc b/src/Converter.cc
index 4e5abcbf..4dc66a1d 100644
--- src/Converter.cc
+++ src/Converter.cc
@@ -29,12 +29,18 @@
#include "sdf/Converter.hh"
#include "sdf/SDFImpl.hh"
#include "sdf/Types.hh"
-
-// This include file is generated at configure time.
-#include "sdf/EmbeddedSdf.hh"
+#include "EmbeddedSdf.hh"
using namespace sdf;
+namespace {
+bool EndsWith(const std::string& _a, const std::string& _b)
+{
+ return (_a.size() >= _b.size()) &&
+ (_a.compare(_a.size() - _b.size(), _b.size(), _b) == 0);
+}
+}
+
/////////////////////////////////////////////////
bool Converter::Convert(TiXmlDocument *_doc, const std::string &_toVersion,
bool _quiet)
@@ -73,28 +79,36 @@ bool Converter::Convert(TiXmlDocument *_doc, const std::string &_toVersion,
elem->SetAttribute("version", _toVersion);
- // The conversionMap in EmbeddedSdf.hh has keys that represent a version
- // of SDF to convert from. The values in conversionmap are pairs, where
- // the first element is the SDF version that the second element will
- // convert to. For example, the following will convert from 1.4 to 1.5
- // according to "conversion_xml":
- //
- // {"1.4", {"1.5", "conversion_xml"}}
- std::map<std::string, std::pair<std::string, std::string> >::const_iterator
- fromIter = conversionMap.find(origVersion);
-
- std::string toVer = "";
+ // The conversion recipes within the embedded files database are named, e.g.,
+ // "1.8/1_7.convert" to upgrade from 1.7 to 1.8.
+ const std::map<std::string, std::string> &embedded = GetEmbeddedSdf();
- // Starting with the original SDF version, perform all the conversions
- // necessary in order to reach the _toVersion.
- while (fromIter != conversionMap.end() && fromIter->first != _toVersion)
+ // Apply the conversions one at a time until we reach the desired _toVersion.
+ std::string curVersion = origVersion;
+ while (curVersion != _toVersion)
{
- // Get the SDF to version.
- toVer = fromIter->second.first;
+ // Find the (at most one) file named, e.g., ".../1_7.convert".
+ std::string snakeVersion = curVersion;
+ std::replace(snakeVersion.begin(), snakeVersion.end(), '.', '_');
+ const std::string suffix = "/" + snakeVersion + ".convert";
+ const char* convertXml = nullptr;
+ for (const auto& [pathname, data] : embedded)
+ {
+ if (EndsWith(pathname, suffix))
+ {
+ curVersion = pathname.substr(0, pathname.size() - suffix.size());
+ convertXml = data.c_str();
+ break;
+ }
+ }
+ if (convertXml == nullptr)
+ {
+ break;
+ }
// Parse and apply the conversion XML.
TiXmlDocument xmlDoc;
- xmlDoc.Parse(fromIter->second.second.c_str());
+ xmlDoc.Parse(convertXml);
if (xmlDoc.Error())
{
sdferr << "Error parsing XML from string: "
@@ -102,13 +116,10 @@ bool Converter::Convert(TiXmlDocument *_doc, const std::string &_toVersion,
return false;
}
ConvertImpl(elem, xmlDoc.FirstChildElement("convert"));
-
- // Get the next conversion XML map element.
- fromIter = conversionMap.find(toVer);
}
// Check that we actually converted to the desired final version.
- if (toVer != _toVersion)
+ if (curVersion != _toVersion)
{
sdferr << "Unable to convert from SDF version " << origVersion
<< " to " << _toVersion << "\n";
diff --git a/src/EmbeddedSdf.hh b/src/EmbeddedSdf.hh
new file mode 100755
index 00000000..b0c447b1
--- /dev/null
+++ src/EmbeddedSdf.hh
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2020 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+
+#ifndef SDF_EMBEDDEDSDF_HH_
+#define SDF_EMBEDDEDSDF_HH_
+
+#include <map>
+#include <string>
+
+#include "sdf/Types.hh"
+
+namespace sdf
+{
+ // Inline bracket to help doxygen filtering.
+ inline namespace SDF_VERSION_NAMESPACE {
+ //
+
+ /// \internal
+
+ /// A map where the keys are a source-relative pathnames within the "sdf"
+ /// directory such as "1.8/root.sdf", and the values are the contents of
+ /// that source file.
+ const std::map<std::string, std::string> &GetEmbeddedSdf();
+}
+}
+#endif
diff --git a/src/SDF.cc b/src/SDF.cc
index 48c1943b..20dcd4c6 100644
--- src/SDF.cc
+++ src/SDF.cc
@@ -31,9 +31,7 @@
#include "sdf/SDFImpl.hh"
#include "SDFImplPrivate.hh"
#include "sdf/sdf_config.h"
-
-// This include file is generated at configure time.
-#include "sdf/EmbeddedSdf.hh"
+#include "EmbeddedSdf.hh"
namespace sdf
{
@@ -453,7 +451,8 @@ const std::string &SDF::EmbeddedSpec(
{
try
{
- return embeddedSdf.at(SDF::Version()).at(_filename);
+ const std::string pathname = SDF::Version() + "/" + _filename;
+ return GetEmbeddedSdf().at(pathname);
}
catch(const std::out_of_range &)
{
@@ -461,6 +460,9 @@ const std::string &SDF::EmbeddedSpec(
sdferr << "Unable to find SDF filename[" << _filename << "] with "
<< "version " << SDF::Version() << "\n";
}
+
+ // An empty SDF string is returned if a query into the embeddedSdf map fails.
+ static const std::string emptySdfString;
return emptySdfString;
}
}
--
2.17.1