126 lines
3.6 KiB
Python
126 lines
3.6 KiB
Python
|
#!/usr/bin/env python
|
||
|
# -*- coding: utf-8 -*-
|
||
|
#
|
||
|
# Copyright (c) 2015 Microsoft Corporation. All rights reserved.
|
||
|
# Released under Apache 2.0 license as described in the file LICENSE.
|
||
|
#
|
||
|
# Authors: Soonho Kong, Leonardo de Moura
|
||
|
import os
|
||
|
import subprocess
|
||
|
import platform
|
||
|
|
||
|
g_lean_exec_name = "lean"
|
||
|
if platform.system() == "Windows" or platform.system().startswith("MSYS"):
|
||
|
g_lean_exec_name = "lean.exe"
|
||
|
|
||
|
if platform.system().startswith("MSYS"):
|
||
|
# In MSYS platform, realpath has a strange behavior.
|
||
|
# os.path.realpath("c:\a\b\c") => \:\a\b\c
|
||
|
g_leanutil_path = os.path.abspath(os.path.normpath(__file__))
|
||
|
else:
|
||
|
g_leanutil_path = os.path.abspath(os.path.realpath(__file__))
|
||
|
|
||
|
g_lean_bin_dir = os.path.dirname(g_leanutil_path)
|
||
|
|
||
|
g_lean_path = os.path.join(g_lean_bin_dir, g_lean_exec_name)
|
||
|
|
||
|
if not os.path.isfile(g_lean_path):
|
||
|
error("cannot find lean executable at " + os.path.abspath(g_lean_path))
|
||
|
|
||
|
class lean_exception(Exception):
|
||
|
def __init__(self, value):
|
||
|
self.value = value
|
||
|
def __str__(self):
|
||
|
return repr(self.value)
|
||
|
|
||
|
def normalize_drive_name(name):
|
||
|
if platform.system() == "Windows":
|
||
|
drive, path = os.path.splitdrive(name)
|
||
|
if drive == None:
|
||
|
return name
|
||
|
else:
|
||
|
# Leo: return drive.lower() + path
|
||
|
return path
|
||
|
else:
|
||
|
return name
|
||
|
|
||
|
def is_olean(fname):
|
||
|
return fname.endswith(".olean")
|
||
|
|
||
|
def is_lean(fname):
|
||
|
return fname.endswith(".lean")
|
||
|
|
||
|
def is_hlean(fname):
|
||
|
return fname.endswith(".hlean")
|
||
|
|
||
|
LEAN_KIND=0
|
||
|
HLEAN_KIND=1
|
||
|
OLEAN_KIND=2
|
||
|
|
||
|
def get_lean_file_kind(fname):
|
||
|
if is_lean(fname):
|
||
|
return LEAN_KIND
|
||
|
elif is_hlean(fname):
|
||
|
return HLEAN_KIND
|
||
|
elif is_olean(fname):
|
||
|
return OLEAN_KIND
|
||
|
else:
|
||
|
raise lean_exception("unknown file kind: " + fname)
|
||
|
|
||
|
def olean_to_lean(fname, kind):
|
||
|
if kind == LEAN_KIND:
|
||
|
return fname[:-5] + "lean"
|
||
|
elif kind == HLEAN_KIND:
|
||
|
return fname[:-5] + "hlean"
|
||
|
else:
|
||
|
raise lean_exception("unsupported file kind: " + kind)
|
||
|
|
||
|
def lean_to_olean(fname):
|
||
|
if is_lean(fname):
|
||
|
return fname[:-4] + "olean"
|
||
|
elif is_hlean(fname):
|
||
|
return fname[:-5] + "olean"
|
||
|
else:
|
||
|
raise lean_exception("file '%s' is not a lean source file" % fname)
|
||
|
|
||
|
def get_timestamp(fname):
|
||
|
try:
|
||
|
return os.path.getmtime(fname)
|
||
|
except OSError:
|
||
|
return 0
|
||
|
|
||
|
def is_uptodate(fname):
|
||
|
return get_timestamp(lean_to_olean(fname)) > get_timestamp(fname)
|
||
|
|
||
|
def lean_direct_deps(lean_file):
|
||
|
deps = []
|
||
|
proc = subprocess.Popen([g_lean_path, "--deps", lean_file],
|
||
|
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||
|
output = proc.communicate()[0].decode('utf-8').replace('\r\n', '\n')
|
||
|
if not proc.returncode == 0:
|
||
|
raise lean_exception(str(output))
|
||
|
for olean_file in output.strip().splitlines():
|
||
|
if olean_file:
|
||
|
deps.append(normalize_drive_name(os.path.abspath(olean_file)))
|
||
|
return deps
|
||
|
|
||
|
def lean_deps_core(lean_file, visited, deps, kind):
|
||
|
if not lean_file in visited:
|
||
|
visited[lean_file] = True
|
||
|
deps.append(lean_file)
|
||
|
for d in lean_direct_deps(lean_file):
|
||
|
d = str(d)
|
||
|
if is_olean(d):
|
||
|
d = olean_to_lean(d, kind)
|
||
|
lean_deps_core(d, visited, deps, kind)
|
||
|
|
||
|
def lean_deps(lean_file):
|
||
|
visited = dict()
|
||
|
deps = []
|
||
|
lean_deps_core(lean_file, visited, deps, get_lean_file_kind(lean_file))
|
||
|
return deps
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
for fname in lean_deps("/Users/leodemoura/projects/lean/library/data/nat/default.lean"):
|
||
|
print("%s is ok: %s" % (fname, is_uptodate(fname)))
|