docs: def build(self, recursive=True):
          """
          Building an assembly buffers the :meth:`components` and :meth:`constraints`.
  
  
          Running ``build()`` is optional, it's automatically run when requesting
          :meth:`components` or :meth:`constraints`.
  
          Mostly it's used to test that there aren't any critical runtime
          issues with its construction, but doing anything like *displaying* or
          *exporting* will ultimately run a build anyway.
  
          :param recursive: if set, iterates through child components and builds
                            those as well.
          :type recursive: :class:`bool`
          """
  
          # initialize values
          self._components = {}
          self._constraints = []
  
          def genwrap(obj, name, iter_type=None):
              # Force obj to act like a generator.
              # this wrapper will always yield at least once.
              if isinstance(obj, GeneratorType):
                  for i in obj:
                      if (iter_type is not None) and (not isinstance(i, iter_type)):
                          raise TypeError("%s must yield a %r" % (name, iter_type))
                      yield i
              else:
                  if (iter_type is not None) and (not isinstance(obj, iter_type)):
                      raise TypeError("%s must return a %r" % (name, iter_type))
                  yield obj
  
          # Make Components
          components_iter = genwrap(self.make_components(), "make_components", dict)
          new_components = next(components_iter)
          self.verify_components(new_components)
          self._components.update(new_components)
  
          # Make Constraints
          constraints_iter = genwrap(self.make_constraints(), "make_components", list)
          new_constraints = next(constraints_iter)
          self.verify_constraints(new_constraints)
          self._constraints += new_constraints
  
          # Run solver : sets components' world coordinates
          self.solve()
  
          # Make Alterations
          alterations_iter = genwrap(self.make_alterations(), "make_alterations")
          next(alterations_iter)  # return value is ignored
  
          while True:
              (s1, s2, s3) = (True, True, True)  # stages
              # Make Components
              new_components = None
              try:
                  new_components = next(components_iter)
                  self.verify_components(new_components)
                  self._components.update(new_components)
              except StopIteration:
                  s1 = False
  
              # Make Constraints
              new_constraints = None
              try:
                  new_constraints = next(constraints_iter)
                  self.verify_constraints(new_constraints)
                  self._constraints += new_constraints
              except StopIteration:
                  s2 = False
  
              # Run solver : sets components' world coordinates
              if new_components or new_constraints:
                  self.solve()
  
              # Make Alterations
              try:
                  next(alterations_iter)  # return value is ignored
              except StopIteration:
                  s3 = False
  
              # end loop when all iters are finished
              if not any((s1, s2, s3)):
                  break
  
          if recursive:
              for (name, component) in self._components.items():
                  component.build(recursive=recursive)