1717A traitgen generator that outputs a C++ package based on the
1818openassetio_traitgen PackageDefinition model.
1919"""
20+ import collections
21+ import itertools
22+
2023# TODO(DF): Refactor to pull out common code, then remove this
2124# suppression.
2225# pylint: disable=duplicate-code
@@ -180,12 +183,27 @@ def __render_namespace(
180183 )
181184 imports = []
182185
183- # Render a file per class (trait or specification).
184- for declaration in namespace .members :
186+ # We group multiple versions of the same trait (or
187+ # specification) together to render in the same header. Note
188+ # that declarations in a namespace are already sorted
189+ # appropriately by name, so we don't need to sort before
190+ # applying groupby.
191+ declarations_by_name = itertools .groupby (
192+ namespace .members ,
193+ lambda declaration : declaration .name if kind == "traits" else declaration .id ,
194+ )
195+
196+ # Render a file per trait/specification, containing all
197+ # versions of that trait/specification.
198+ for name , declarations in declarations_by_name :
185199 if kind == "traits" :
186- file_name = self .__render_trait (namespace , declaration , namespace_abs_path )
200+ file_name = self .__render_trait (
201+ namespace , name , tuple (declarations ), namespace_abs_path
202+ )
187203 else :
188- file_name = self .__render_specification (namespace , declaration , namespace_abs_path )
204+ file_name = self .__render_specification (
205+ namespace , name , tuple (declarations ), namespace_abs_path
206+ )
189207
190208 imports .append (f"{ namespace_name } /{ file_name } " )
191209
@@ -207,7 +225,8 @@ def __render_namespace(
207225 def __render_trait (
208226 self ,
209227 namespace : NamespaceDeclaration ,
210- declaration : TraitDeclaration ,
228+ name : str ,
229+ declarations : tuple [TraitDeclaration , ...],
211230 namespace_abs_path : str ,
212231 ) -> str :
213232 """
@@ -216,24 +235,25 @@ def __render_trait(
216235 Creates a single header file containing a single trait view
217236 class.
218237 """
219- cls_name = self .__env .filters ["to_cpp_class_name" ](declaration . name ) + "Trait"
238+ header_name = self .__env .filters ["to_cpp_class_name" ](name ) + "Trait"
220239 self .__render_template (
221240 "trait" ,
222- os .path .join (namespace_abs_path , f"{ cls_name } .hpp" ),
241+ os .path .join (namespace_abs_path , f"{ header_name } .hpp" ),
223242 {
224243 "package" : self .__package ,
225244 "namespace" : namespace ,
226- "trait " : declaration ,
245+ "versions " : declarations ,
227246 "openassetio_abi_version" : OPENASSETIO_ABI_VERSION ,
228247 "traitgen_abi_version" : TRAITGEN_ABI_VERSION ,
229248 },
230249 )
231- return f"{ cls_name } .hpp"
250+ return f"{ header_name } .hpp"
232251
233252 def __render_specification (
234253 self ,
235254 namespace : NamespaceDeclaration ,
236- declaration : SpecificationDeclaration ,
255+ name : str ,
256+ declarations : tuple [SpecificationDeclaration , ...],
237257 namespace_abs_path : str ,
238258 ) -> str :
239259 """
@@ -242,19 +262,38 @@ def __render_specification(
242262 Creates a single header file containing a single specification
243263 class.
244264 """
245- cls_name = self .__env .filters ["to_cpp_class_name" ](declaration .id ) + "Specification"
265+
266+ # Properties required to interpolate when constructing #include
267+ # directives.
268+ TraitHeaderPathTokens = collections .namedtuple (
269+ "TraitHeaderPathTokens" , ("package" , "namespace" , "name" )
270+ )
271+
272+ # All versions of a given trait live in a single header.
273+ # Extract fields required to #include the trait headers
274+ # referenced by all versions of this specification, de-duped.
275+ all_trait_header_path_tokens = sorted (
276+ {
277+ TraitHeaderPathTokens (trait_decl .package , trait_decl .namespace , trait_decl .name )
278+ for spec_decl in declarations
279+ for trait_decl in spec_decl .trait_set
280+ }
281+ )
282+
283+ header_name = self .__env .filters ["to_cpp_class_name" ](name ) + "Specification"
246284 self .__render_template (
247285 "specification" ,
248- os .path .join (namespace_abs_path , f"{ cls_name } .hpp" ),
286+ os .path .join (namespace_abs_path , f"{ header_name } .hpp" ),
249287 {
250288 "package" : self .__package ,
251289 "namespace" : namespace ,
252- "specification" : declaration ,
290+ "versions" : declarations ,
291+ "all_trait_header_path_tokens" : all_trait_header_path_tokens ,
253292 "openassetio_abi_version" : OPENASSETIO_ABI_VERSION ,
254293 "traitgen_abi_version" : TRAITGEN_ABI_VERSION ,
255294 },
256295 )
257- return f"{ cls_name } .hpp"
296+ return f"{ header_name } .hpp"
258297
259298 def __render_package_template (
260299 self , package_abs_path : str , name : str , docstring : str , imports : List [str ]
0 commit comments