docs: def build_by_builder(self, builder: Builder, stats: BuildProcessStats):
          """ run one builder, return statistics about the run """
          logger = logging.getLogger(__name__)
          target_signature = builder.get_signature()
          assert target_signature is not None, "builder signature is None"
          if self.cache.list_sig_ok(target_signature):
              logger.info("verifying [{}]".format(builder.get_name()))
              file_bad = 0
              file_correct = 0
              file_missing = 0
              file_total = 0
              list_filename = self.cache.get_list_filename(target_signature)
              for object_name, signature in Cache.iterate_objects(list_filename):
                  filename = self.cache.get_object_filename(signature)
                  if os.path.isfile(object_name):
                      object_name_signature = sha1_file(object_name)
                      if object_name_signature != signature:
                          # logger.info("file [{}] is incorrect. Getting from cache.".format(object_name))
                          copy_mkdir(filename, object_name)
                          stats.add_copy_sha1(filename, object_name)
                          file_bad += 1
                      else:
                          # logger.info("file [{}] is up to date".format(object_name))
                          stats.add_nop(filename, object_name)
                          file_correct += 1
                  else:
                      # logger.info("file [{}] is missing. Getting from cache.".format(object_name))
                      copy_mkdir(filename, object_name)
                      stats.add_copy_missing(filename, object_name)
                      file_missing += 1
                  file_total += 1
              if file_bad > 0 or file_missing > 0:
                  logger.info("Retrieved {} files from cache (bad/correct/missing = {}/{}/{}".format(
                      file_total, file_bad, file_correct, file_missing))
              else:
                  logger.info("ok [{}]".format(builder.get_name()))
          else:
              # this is one of the rare cases in which really want to catch all exceptions.
              # noinspection PyBroadException
              try:
                  logger.info("running [{}]".format(builder.get_name()))
                  builder.build()
                  logger.info("ok [{}]".format(builder.get_name()))
                  stats.add_builder_ok(builder)
                  # first lets build a list of what was constructed
                  targets = builder.get_targets()
                  targets.extend(builder.get_targets_post_build())
                  content = ""
                  for target in targets:
                      signature = sha1_file(target)
                      content += target + " " + signature + "\n"
                      self.cache.save_object_by_signature(signature, target)
                  self.cache.save_list_by_signature(target_signature, content)
              except Exception as e:
                  logger.info("failed [{}]".format(builder.get_name()))
                  logger.info("exception [{}]".format(e))
                  stats.add_builder_fail(builder, e)