Source code for curry.toolchain._findcurry

from ..exceptions import ModuleLookupError, PrerequisiteError
from .. import config
from . import _filenames
from ..tools.utility import make_exception
from ..utility import curryname, filesys, formatDocstring
import logging, os

__all__ = ['currentfile', 'import_path', 'imports_path', 'program_path']
logger = logging.getLogger(__name__)
SUBDIR = config.intermediate_subdir()

def program_path(name, currypath, is_sourcefile=False, **ignored):
  '''
  The search path of a program.  For a module given as a source file
  (``is_sourcefile``), the directory of the file comes first, then the
  entries of ``currypath``, as PAKCS searches: the imports of the program
  are found beside it without a CURRYPATH that names its directory.  For a
  module given by name the path is ``currypath`` as a list.  The tools
  (sprite-exec, sprite-make) and curry.import_ of a path search this path
  for the imports of the program, and the plan asks the steps about the
  files of the program under it.
  '''
  currypath = list(currypath)
  if is_sourcefile:
    directory = os.path.dirname(os.path.abspath(name))
    currypath = [directory] + [
        entry for entry in currypath if os.path.abspath(entry) != directory
      ]
  return currypath

def imports_path(currypath, is_sourcefile=False, **ignored):
  '''
  The search path of the imports of a module whose own search path is
  ``currypath`` (``program_path``), as the steps of a plan import them.
  For a program given as a source file it is that path, so the imports are
  found beside the program.  None, the path of the interpreter, for a
  module given by name: the path given with a name finds that module
  alone, and its imports are searched as before.
  '''
  return list(currypath) if is_sourcefile else None

def import_path(interp, currypath):
  '''
  The search path of the imports of a program on ``currypath``
  (``imports_path``): that path, then the entries of the path of ``interp``
  it lacks.  So the imports of a program are found beside it and on the
  path given for it, and the Prelude and the other modules of the library
  as before, whatever path a caller gave.  None, the path of the
  interpreter, without a path.
  '''
  if currypath is None:
    return None
  path = curryname.makeCurryPath(currypath)
  for entry in curryname.makeCurryPath(interp.path):
    if entry not in path:
      path.append(entry)
  return path

[docs] @formatDocstring(config.python_package_name()) def currentfile( plan, name, currypath, is_sourcefile=False, **ignored ): ''' Finds the newest prerequisite along the Curry build pipeline. The file returned is the newest among the Curry source file (suffix: .curry), the ICurry file (suffix: .icy), the JSON file (suffix: .json or .json.z), and the files of the backend (suffix: .py; or .cpp and .so), less the files that a step of the plan refuses (see ``Plan.prune_stale``) and the files made from them. The files are compared by modification time (see ``filesys.newer``), so a step that moves the change times of the inodes (a chmod, a rename, the prefix patch of a package manager) does not make a product stale. Args: plan: The compile plan. name: The module, source file, or package name. currypath: A sequence of paths to search (i.e., CURRYPATH split on ':'). is_sourcefile: Indicates whether to interpret the name as a source file. If True, the name should end with extension .curry. Otherwise the name is interpreted as a module or package name. plan: The compilation :class:`plan <{0}.toolchain.plan.Plan>` This indicates which files to consider as prerequisites. If None, a default plan Raises: ModuleLookupError: the module was not found. Returns: The name of an ICurry-JSON file (suffix: .json), Curry source file (suffix: .curry), Python file (suffix: .py), C++ source (suffix: .cpp) or object (suffix: .so), or directory. The JSON name may have an additional .z suffix. The JSON file is returned if it is up-to-date, otherwise, the Curry file is returned if it exists. If neither of those applies, the package directory name is returned, if it exists. ''' currypath = program_path(name, currypath, is_sourcefile) if not is_sourcefile: # If name is a module name, then search CURRYPATH for the source file or # (possibly zipped) JSON and set it as the name. source file and # is_sourcefile=True. It is acceptable to use a JSON file that has no # corresponding source file. This means a library could be installed as # JSON only, without needing to install its source. curryname.validateModulename(name) # Search for the JSON file first, then ICurry, then .curry. parts = name.split('.') package_path, name = os.sep.join(parts[:-1]), parts[-1] search_names = [ os.path.join(package_path, '.curry', SUBDIR, name + suffix) for suffix in reversed(plan.suffixes[1:]) ] search_names += [os.path.join(package_path, name + '.curry')] search_names += [os.path.join(package_path, name, '')] files = filesys.findfiles(currypath, search_names) try: name = next(files) except StopIteration: raise make_exception( ModuleLookupError , 'Curry module %r not found.' % name , hint=lambda:_fileNotFoundHint(name) ) if os.path.isdir(name): return name else: name = _filenames.curryfilename(name) # Find the newest prerequisite. curryfile = name if not curryfile.endswith('.curry'): raise ModuleLookupError('expected .curry extension in %r' % curryfile) curryfile = os.path.abspath(curryfile) filelist = plan.filelist(curryfile) # Force recompile, if requested. if config.force_recompile_cxx(): filelist = [ fn for fn in filelist if not any(fn.endswith(suffix) for suffix in ['.cpp', '.so']) ] # Disregard a file the toolchain no longer accepts, and everything made # from it: a .cpp file of another format, or a .so file compiled against # other runtime headers. The age of the runtime library does not count. filelist = plan.prune_stale( filelist, imports_path(currypath, is_sourcefile) ) prereq = os.path.abspath(filesys.newest(filelist)) if not os.path.exists(prereq): # If there is no prerequisite, then there is no Curry file or any of its # derivatives (it could be OK to ship Curry files with no source). Report # this as there being no source file. assert not os.path.exists(curryfile) raise PrerequisiteError('Curry file does not exist: %r' % curryfile) if not (os.access(prereq, os.R_OK) and os.path.isfile(prereq)): raise PrerequisiteError( 'prerequisite is the wrong type or is unreadable: %r' % prereq ) logger.debug('Prerequisite for compilation of %r is %r', name, prereq) return prereq
# Keyword defaults. def _fileNotFoundHint(name, **kwds): if os.path.exists(name + '.curry'): return 'Perhaps you meant "%s.curry"?' % name