Architectures¶
An architecture groups entities into a tree-like structure.
Architectures can be built manually or automatically with plugins. A manual
architecture is static and can be created from the API with
Ent.add_to_arch. Automatic architectures
are dynamic and update with every analysis.
Discovery¶
Use Db.root_archs to discover the architectures
(manual and automatic) that have been created for the project. Use
Arch.automatic_arch to discover
which automatic architecture and options were used to generate a particular
root architecture.
Use AutomaticArch.list to discover
available automatic architectures. The AutomaticArch
objects returned can be used to query information about the automatic
architecture such as AutomaticArch.name and
AutomaticArch.description.
Automatic architectures support options.
Use AutomaticArch.options followed by
Options.list to discover available options.
List root architectures already in the project:
import understand
db = understand.open("/path/to/myproject.und")
for root in db.root_archs():
print(root.name())
List automatic-architecture plugins available for the database (built-in and
.upy definitions):
import understand
db = understand.open("/path/to/myproject.und")
for plugin in understand.AutomaticArch.list(db):
print(plugin.name(), "-", plugin.description())
For a root architecture built by a plugin, Arch.automatic_arch returns the catalog entry and saved options,
or None for a manual root:
import understand
db = understand.open("/path/to/myproject.und")
root = db.root_archs()[0]
pair = root.automatic_arch()
if pair:
auto, _ = pair
print(auto.name())
Generation¶
A temporary automatic architecture can be created from the Python API. The
architecture lasts until the project is closed. Use
Db.automatic_arch to create it. Once
created, it is listed in Db.root_archs and
findable with Db.lookup_arch.
Run an automatic-architecture plugin and add a new root (pass a catalog object or the plugin display name as a string):
import understand
db = understand.open("/path/to/myproject.und")
plugin = understand.AutomaticArch.list(db)[0]
db.automatic_arch(plugin)
Plugin writing¶
An Automatic Architecture plugin must have a name function and a build function.
Automatic architectures are different from most plugins because there can be multiple instances of a single architecture plugin. So the name function is only the name of the automatic architecture plugin as it appears in the “New Automatic Architecture” list. Each instance of the architecture is named by the user.
The build function should use the add method
of the AutomaticArchContext parameter to add
entities to the architecture. Architecture plugins also support
options which can be set from define_options
and then read in the build function.
A sample architecture plugin is provided below that groups entities by long name.
def name():
""" Required, the name of the automatic architecture """
return "Long Name"
def description():
""" Optional, a description of the automatic architecture. """
return '''An architecture for grouping entities by long name.
<p>By default, this architecture groups class entities by long name
assuming "::" as a separator, appropriate for C++ projects. The entity
kind string and separator can be configured. For example,
the kind string can be set to "functions" to group functions instead of
classes, or the separator can be set to "." for C# instead of C++.</p>
'''
def tags():
""" Optional, tags for the automatic architecture """
return [
'Language: Any',
'Sample Template'
]
def test_global(db):
""" Optional, return true if applicable to the db. Assumed true."""
return True
def define_options(arch):
""" Optional, define options """
arch.options().text("kinds", "Kind String", "class ~unresolved ~member")
arch.options().text("sep", "Separator", "::")
def build(arch, db):
""" Required, generate the architecture """
sep = arch.options().lookup("sep")
for ent in db.ents(arch.options().lookup("kinds")):
parts = ent.longname().split(sep)
if len(parts) > 1:
# The add method is the heart of automatic architectures. Use '/'
# separated fields to create sub architectures, or leave the name
# empty to add the entity directly to the root.
arch.add(ent, '/'.join(parts[:-1]))