docs: def compile(self, spec):
          """
          Generic step that compiles from a spec to build the specified
          things into the build directory `build_dir`, by gathering all
          the files and feed them through the transpilation process or by
          simple copying.
          """
  
          spec[EXPORT_MODULE_NAMES] = export_module_names = spec.get(
              EXPORT_MODULE_NAMES, [])
          if not isinstance(export_module_names, list):
              raise TypeError(
                  "spec provided a '%s' but it is not of type list "
                  "(got %r instead)" % (EXPORT_MODULE_NAMES, export_module_names)
              )
  
          def compile_entry(method, read_key, store_key):
              spec_read_key = read_key + self.sourcepath_suffix
              spec_modpath_key = store_key + self.modpath_suffix
              spec_target_key = store_key + self.targetpath_suffix
  
              if _check_key_exists(spec, [spec_modpath_key, spec_target_key]):
                  logger.error(
                      "aborting compile step %r due to existing key", entry,
                  )
                  return
  
              sourcepath_dict = spec.get(spec_read_key, {})
              entries = self._gen_modname_source_target_modpath(
                  spec, sourcepath_dict)
              (spec[spec_modpath_key], spec[spec_target_key],
                  new_module_names) = method(spec, entries)
              logger.debug(
                  "entry %r "
                  "wrote %d entries to spec[%r], "
                  "wrote %d entries to spec[%r], "
                  "added %d export_module_names",
                  entry,
                  len(spec[spec_modpath_key]), spec_modpath_key,
                  len(spec[spec_target_key]), spec_target_key,
                  len(new_module_names),
              )
              export_module_names.extend(new_module_names)
  
          for entry in self.compile_entries:
              if isinstance(entry, ToolchainSpecCompileEntry):
                  log = partial(
                      logging.getLogger(entry.logger).log,
                      entry.log_level,
                      (
                          entry.store_key + "%s['%s'] is being rewritten from "
                          "'%s' to '%s'; configuration may now be invalid"
                      ),
                  ) if entry.logger else None
                  compile_entry(partial(
                      toolchain_spec_compile_entries, self,
                      process_name=entry.process_name,
                      overwrite_log=log,
                  ), entry.read_key, entry.store_key)
                  continue
  
              m, read_key, store_key = entry
              if callable(m):
                  method = m
              else:
                  method = getattr(self, self.compile_prefix + m, None)
                  if not callable(method):
                      logger.error(
                          "'%s' not a callable attribute for %r from "
                          "compile_entries entry %r; skipping", m, self, entry
                      )
                      continue
  
              compile_entry(method, read_key, store_key)