/- Copyright (c) 2019 Microsoft Corporation. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Leonardo de Moura -/ prelude import init.system.io import init.system.filepath import init.data.array import init.control.combinators import init.lean.name namespace Lean open System.FilePath (pathSeparator searchPathSeparator extSeparator) private def pathSep : String := toString pathSeparator private def searchPathSep : String := toString searchPathSeparator def realPathNormalized (fname : String) : IO String := do fname ← IO.realPath fname; pure (System.FilePath.normalizePath fname) def mkSearchPathRef : IO (IO.Ref (Array String)) := do curr ← realPathNormalized "."; IO.mkRef (Array.singleton curr) @[init mkSearchPathRef] constant searchPathRef : IO.Ref (Array String) := default _ def setSearchPath (s : List String) : IO Unit := do s ← s.mmap realPathNormalized; searchPathRef.set s.toArray def setSearchPathFromString (s : String) : IO Unit := setSearchPath (s.split searchPathSep) def getBuiltinSearchPath : IO (List String) := do appDir ← IO.appDir; let libDir := appDir ++ pathSep ++ ".." ++ pathSep ++ "library"; libDirExists ← IO.isDir libDir; if libDirExists then do path ← realPathNormalized libDir; pure [path] else do let installedLibDir := appDir ++ pathSep ++ ".." ++ pathSep ++ "lib" ++ pathSep ++ "lean" ++ pathSep ++ "library"; installedLibDirExists ← IO.isDir installedLibDir; if installedLibDirExists then do path ← realPathNormalized installedLibDir; pure [path] else pure [] def getSearchPathFromEnv : IO (List String) := do val ← IO.getEnv "LEAN_PATH"; match val with | none => pure ["."] -- If LEAN_PATH is not defined, use current directory | some val => pure (val.split searchPathSep) @[export lean.init_search_path_core] def initSearchPath (path : Option String := "") : IO Unit := match path with | some path => setSearchPathFromString path | none => do pathEnv ← getSearchPathFromEnv; builtinPath ← getBuiltinSearchPath; setSearchPath (pathEnv ++ builtinPath) def findFile (fname : String) (ext : String) : IO (Option String) := do let fname := System.FilePath.normalizePath fname; paths ← searchPathRef.get; paths.mfind $ fun path => do let curr := path ++ pathSep ++ fname; isDir ← IO.isDir curr; let curr := if isDir then curr ++ pathSep ++ "default" else curr; let curr := curr ++ toString extSeparator ++ ext; ex ← IO.fileExists curr; if ex then pure (some curr) else pure none def modNameToFileName : Name → String | (Name.mkString Name.anonymous h) := h | (Name.mkString p h) := modNameToFileName p ++ pathSep ++ h | Name.anonymous := "" | (Name.mkNumeral p _) := modNameToFileName p def addRel (baseDir : String) : Nat → String | 0 := baseDir | (n+1) := addRel n ++ pathSep ++ ".." def findLeanFile (modName : Name) (ext : String) : IO String := do let fname := modNameToFileName modName; some fname ← findFile fname ext | throw (IO.userError ("module '" ++ toString modName ++ "' not found")); realPathNormalized fname def findOLean (modName : Name) : IO String := findLeanFile modName "olean" @[export lean.find_lean_core] def findLean (modName : Name) : IO String := findLeanFile modName "lean" def findAtSearchPath (fname : String) : IO String := do fname ← realPathNormalized fname; paths ← searchPathRef.get; match paths.find (fun path => if path.isPrefixOf fname then some path else none) with | some r => pure r | none => throw (IO.userError ("file '" ++ fname ++ "' not in the search path " ++ repr paths)) @[export lean.module_name_of_file_core] def moduleNameOfFileName (fname : String) : IO Name := do path ← findAtSearchPath fname; fname ← realPathNormalized fname; let fnameSuffix := fname.drop path.length; let fnameSuffix := if fnameSuffix.get 0 == pathSeparator then fnameSuffix.drop 1 else fnameSuffix; if path ++ pathSep ++ fnameSuffix != fname then throw (IO.userError ("failed to convert file '" ++ fname ++ "' to module name, path is not a prefix of the given file")) else do some extPos ← pure (fnameSuffix.revPosOf '.') | throw (IO.userError ("failed to convert file '" ++ fname ++ "' to module name, extension is missing")); let modNameStr := fnameSuffix.extract 0 extPos; let extStr := fnameSuffix.extract (extPos + 1) fnameSuffix.bsize; let parts := modNameStr.split pathSep; let modName := parts.foldl Name.mkString Name.anonymous; fname' ← findLeanFile modName extStr; unless (fname == fname') $ throw (IO.userError ("failed to convert file '" ++ fname ++ "' to module name, module name '" ++ toString modName ++ "' resolves to '" ++ fname' ++ "'")); pure modName end Lean