lean4-htt/src/util/lean_path.cpp

311 lines
10 KiB
C++

/*
Copyright (c) 2017 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura, Gabriel Ebner
*/
#include "util/lean_path.h"
#include <string>
#include <cstdlib>
#include <fstream>
#include <vector>
#include "runtime/sstream.h"
#ifndef LEAN_DEFAULT_MODULE_FILE_NAME
#define LEAN_DEFAULT_MODULE_FILE_NAME "default"
#endif
namespace lean {
lean_file_not_found_exception::lean_file_not_found_exception(std::string const & fname):
exception(sstream() << "file '" << fname << "' not found in the LEAN_PATH"),
m_fname(fname) {}
static char const * g_default_file_name = LEAN_DEFAULT_MODULE_FILE_NAME;
static bool exists(std::string const & fn) {
return !!std::ifstream(fn);
}
optional<std::string> get_leanpkg_path_file() {
auto dir = lrealpath(".");
while (true) {
auto fn = dir + get_dir_sep() + "leanpkg.path";
if (exists(fn)) return optional<std::string>(fn);
auto i = dir.rfind(get_dir_sep());
if (i == std::string::npos) {
return optional<std::string>();
} else {
dir = dir.substr(0, i);
}
}
}
std::string get_user_leanpkg_path() {
// TODO(gabriel): check if this works on windows
if (auto home = getenv("HOME")) {
return std::string(home) + get_dir_sep() + ".lean" + get_dir_sep() + "leanpkg.path";
} else {
return "/could-not-find-home";
}
}
static optional<std::string> begins_with(std::string const & s, std::string const & prefix) {
if (prefix.size() <= s.size() && s.substr(0, prefix.size()) == prefix) {
return optional<std::string>(s.substr(prefix.size(), s.size()));
} else {
return optional<std::string>();
}
}
search_path parse_leanpkg_path(std::string const & fn) {
std::ifstream in(fn);
if (!in) throw exception(sstream() << "cannot open " << fn);
auto fn_dir = dirname(fn);
search_path path;
while (!in.eof()) {
std::string line;
std::getline(in, line);
if (auto rest = begins_with(line, "path "))
path.push_back(lrealpath(resolve(*rest, fn_dir)));
if (line == "builtin_path") {
auto builtin = get_builtin_search_path();
path.insert(path.end(), builtin.begin(), builtin.end());
}
}
return path;
}
optional<search_path> get_lean_path_from_env() {
if (auto r = getenv("LEAN_PATH")) {
auto lean_path = normalize_path(r);
unsigned i = 0;
unsigned j = 0;
unsigned sz = static_cast<unsigned>(lean_path.size());
search_path path;
for (; j < sz; j++) {
if (is_path_sep(lean_path[j])) {
if (j > i)
path.push_back(lrealpath(lean_path.substr(i, j - i)));
i = j + 1;
}
}
if (j > i)
path.push_back(lrealpath(lean_path.substr(i, j - i)));
return optional<search_path>(path);
} else {
return optional<search_path>();
}
}
search_path get_builtin_search_path() {
search_path path;
#if !defined(LEAN_EMSCRIPTEN)
std::string exe_path = dirname(get_exe_location());
auto lib_path = exe_path + get_dir_sep() + ".." + get_dir_sep() + "library";
if (exists(lib_path))
path.push_back(lrealpath(lib_path));
auto installed_lib_path = exe_path + get_dir_sep() + ".." + get_dir_sep() + "lib" + get_dir_sep() + "lean" + get_dir_sep() + "library";
if (exists(installed_lib_path))
path.push_back(lrealpath(installed_lib_path));
#endif
return path;
}
standard_search_path::standard_search_path() {
m_builtin = get_builtin_search_path();
m_from_env = get_lean_path_from_env();
m_leanpkg_path_fn = get_leanpkg_path_file();
m_user_leanpkg_path_fn = get_user_leanpkg_path();
if (m_leanpkg_path_fn) {
m_from_leanpkg_path = parse_leanpkg_path(*m_leanpkg_path_fn);
} else if (exists(m_user_leanpkg_path_fn)) {
m_from_leanpkg_path = parse_leanpkg_path(m_user_leanpkg_path_fn);
}
}
search_path standard_search_path::get_path() const {
if (m_from_env) return *m_from_env;
if (m_from_leanpkg_path) return *m_from_leanpkg_path;
return m_builtin;
}
bool is_lean_file(std::string const & fname) {
return has_file_ext(fname, ".lean");
}
bool is_olean_file(std::string const & fname) {
return has_file_ext(fname, ".olean");
}
bool is_known_file_ext(std::string const & fname) {
return is_lean_file(fname) || is_olean_file(fname);
}
optional<std::string> check_file_core(std::string file, char const * ext) {
if (ext)
file += ext;
std::ifstream ifile(file);
if (ifile)
return optional<std::string>(lrealpath(file));
else
return optional<std::string>();
}
optional<std::string> check_file(std::string const & path, std::string const & fname, char const * ext = nullptr) {
std::string file = path + get_dir_sep() + fname;
if (is_directory(file.c_str())) {
std::string default_file = file + get_dir_sep() + g_default_file_name;
if (auto r1 = check_file_core(default_file, ext)) {
if (auto r2 = check_file_core(file, ext))
throw exception(sstream() << "ambiguous import, it can be '" << *r1 << "' or '" << *r2 << "'");
return r1;
}
}
return check_file_core(file, ext);
}
std::string name_to_file(name const & fname) {
return fname.to_string(get_dir_sep());
}
static std::string find_file(search_path const & paths, std::string fname, std::initializer_list<char const *> const & extensions) {
bool is_known = is_known_file_ext(fname);
fname = normalize_path(fname);
buffer<std::string> results;
for (auto & path : paths) {
if (is_known) {
if (auto r = check_file(path, fname))
results.push_back(*r);
} else {
for (auto ext : extensions) {
if (auto r = check_file(path, fname, ext))
results.push_back(*r);
}
}
}
if (results.size() == 0)
throw lean_file_not_found_exception(fname);
else if (results.size() > 1)
throw exception(sstream() << "ambiguous import, it can be '" << results[0] << "' or '" << results[1] << "'");
else
return results[0];
}
std::string find_file(search_path const & paths, std::string const & base, optional<unsigned> const & rel, name const & fname,
std::initializer_list<char const *> const & extensions) {
if (!rel) {
return find_file(paths, fname.to_string(get_dir_sep()), extensions);
} else {
auto path = base;
for (unsigned i = 0; i < *rel; i++) {
path += get_dir_sep();
path += "..";
}
for (auto ext : extensions) {
if (auto r = check_file(path, fname.to_string(get_dir_sep()), ext))
return *r;
}
throw lean_file_not_found_exception(fname.to_string());
}
}
std::string find_file(search_path const & paths,
std::string const & base, optional<unsigned> const & k, name const & fname, char const * ext) {
return find_file(paths, base, k, fname, {ext});
}
std::string find_file(search_path const & paths, std::string fname) {
return find_file(paths, fname, {".olean", ".lean"});
}
std::string find_file(search_path const & paths, name const & fname) {
return find_file(paths, fname.to_string(get_dir_sep()));
}
std::string find_file(search_path const & paths, name const & fname, std::initializer_list<char const *> const & exts) {
return find_file(paths, fname.to_string(get_dir_sep()), exts);
}
name module_name_of_file(search_path const & paths, std::string const & fname0) {
auto fname = normalize_path(fname0);
lean_assert(is_lean_file(fname));
fname = fname.substr(0, fname.size() - std::string(".lean").size());
for (auto & path : paths) {
if (path.size() < fname.size() && fname.substr(0, path.size()) == path) {
size_t pos = path.size();
if (fname[pos] == get_dir_sep_ch())
pos++;
name n;
while (pos < fname.size()) {
auto sep_pos = fname.find(get_dir_sep_ch(), pos);
n = name(n, fname.substr(pos, sep_pos - pos).c_str());
pos = sep_pos;
if (pos != std::string::npos)
pos++;
}
return n;
}
}
throw exception(sstream() << "file '" << fname0 << "' not part of any known Lean packages");
}
module_name absolutize_module_name(search_path const & path, std::string const & base, rel_module_name const & rel) {
// TODO(Sebastian): Should make sure that the result of `find_file` is still in the same package as `base`
return module_name_of_file(path, find_file(path, base, rel.m_updirs, rel.m_name, ".lean"));
}
void find_imports_core(std::string const & base, optional<unsigned> const & k,
std::vector<pair<std::string, std::string>> & imports) {
std::vector<std::string> files;
find_files(base, ".lean", files);
find_files(base, ".olean", files);
for (auto const & file : files) {
auto import = file.substr(base.size() + 1, file.rfind('.') - (base.size() + 1));
std::replace(import.begin(), import.end(), get_dir_sep_ch(), '.');
if (k)
import = std::string(*k + 1, '.') + import;
auto n = import.rfind(".default");
if (n != static_cast<unsigned>(-1) && n == import.size() - std::string(".default").size())
import = import.substr(0, n);
imports.push_back({import, file});
}
}
void find_imports(search_path const & paths, std::string const & base, optional<unsigned> const & k,
std::vector<pair<std::string, std::string>> & imports) {
if (!k) {
for (auto & base : paths)
if (is_dir(base))
find_imports_core(base, k, imports);
} else {
auto path = base;
for (unsigned i = 0; i < *k; i++) {
path += get_dir_sep();
path += "..";
}
find_imports_core(path, k, imports);
}
}
std::string olean_of_lean(std::string const & lean_fn) {
if (lean_fn.size() > 5 && lean_fn.substr(lean_fn.size() - 5) == ".lean") {
return lean_fn.substr(0, lean_fn.size() - 5) + ".olean";
} else {
throw exception(sstream() << "not a .lean file: " << lean_fn);
}
}
std::string olean_file_to_lean_file(std::string const &olean) {
lean_assert(is_olean_file(olean));
std::string lean = olean;
lean.erase(lean.size() - std::string("olean").size(), 1);
return lean;
}
}