feat(bin/lmake): add lmake

lmake tries to find a makefile in the current working-directory or its
ancestors and invoke the makefile if found. If a makefile is not found,
it calls lean directly. This is also used for flycheck-lean.

Usage:

    $ lmake
    $ lmake all
    $ lmake clean
    $ lmake basic
    $ lmake basic.lean
    $ lmake /home/user/projects/lean/library/.../basic.lean
    $ lmake --flyinfo --flycheck basic
This commit is contained in:
Soonho Kong 2014-08-04 15:11:37 -07:00
parent b9fadeb86e
commit b2cb49fa1f
2 changed files with 158 additions and 0 deletions

2
bin/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
lean
lean.exe

156
bin/lmake Executable file
View file

@ -0,0 +1,156 @@
#!/usr/bin/env python2
#
# Copyright (c) 2014 Microsoft Corporation. All rights reserved.
# Released under Apache 2.0 license as described in the file LICENSE.
#
# Author: Soonho Kong
import os.path
import sys
import subprocess
import string
import argparse
def lean_exe_name():
""" Return a lean executable name """
import platform
if platform.system() == 'Windows':
return "lean.exe"
else:
return "lean"
def find_makefile(path, makefile_names):
""" Find makefile in a given directory.
Args:
path: a string of path to look up
makefile_names: a list of strings to search
Return:
When found, return the full path of a makefile
Otherwise, return False.
"""
for makefile in makefile_names:
makefile_pathname = os.path.join(path, makefile)
if os.path.isfile(makefile_pathname):
return makefile_pathname
return False
def find_makefile_upward(path, makefile_names):
""" Strating from a given directory, search upward to find
a makefile
Args:
path: a string of path to start the search
Return:
When found, return the full path of a makefile
Otherwise, return False.
"""
makefile = find_makefile(path, makefile_names)
if makefile:
return makefile
up = os.path.dirname(path)
if up != path:
return find_makefile_upward(up, makefile_names)
return False
def which(program):
""" Lookup program in a path """
import os
def is_exe(fpath):
return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
fpath, fname = os.path.split(program)
if fpath:
if is_exe(program):
return program
else:
for path in os.environ["PATH"].split(os.pathsep):
path = path.strip('"')
exe_file = os.path.join(path, program)
if is_exe(exe_file):
return exe_file
return None
def find_lean_exe():
""" Find a fullpath of Lean executable """
# First Look up environment variable
lean=os.environ.get('LEAN')
if lean:
return lean
# Otherwise, Look up in PATH
import platform
lean = which(lean_exe_name())
# Otherwise, look up the directory where lmake is at
script_dir = os.path.dirname(os.path.realpath(__file__))
lean = os.path.join(script_dir, lean_exe_name())
if os.path.isfile(lean):
return lean
return False
def find_lean_opt():
""" Return a list of lean options from env_var LEAN_OPTION"""
lean_opt = os.environ.get('LEAN_OPTION')
if lean_opt:
return string.split(lean_opt)
else:
return []
def call_lean(leanfile, options):
""" Call lean with options """
from collections import OrderedDict
lean_exe = find_lean_exe()
lean_opt = list(OrderedDict.fromkeys(find_lean_opt() + options))
subprocess.call([lean_exe] + lean_opt + [leanfile], stderr=subprocess.STDOUT)
def get_target(s):
""" Extract a target from an argument
if we have "xyz.lean", return "xyz.olean"
Otherwise, return s as it is. (it might be phony target such as 'clean')
"""
if os.path.isfile(s) and s[-5:] == ".lean":
leanfile = os.path.realpath(s)
return leanfile[:-5] + ".olean"
if os.path.isfile(s + ".lean"):
leanfile = os.path.realpath(s + ".lean")
return leanfile[:-5] + ".olean"
return s
def call_makefile(makefile, arg):
""" Call makefile with a target generated from a given arg """
env_copy = os.environ.copy()
env_copy['LEAN'] = find_lean_exe()
cmd = ["make", "-C", os.path.dirname(makefile)]
if arg:
target = get_target(arg)
cmd.append(target)
subprocess.call(cmd, stderr=subprocess.STDOUT, env=env_copy)
def parse_arg(argv):
""" Parse arguments """
parser = argparse.ArgumentParser(description='Process arguments.')
parser.add_argument('--flycheck', '-F', action='store_true', default=False, help="flycheck")
parser.add_argument('--flyinfo', '-I', action='store_true', default=False, help="flyinfo")
parser.add_argument('target', nargs='?')
args = parser.parse_args(argv)
options = []
if args.flycheck:
options.append("--flycheck")
if args.flyinfo:
options.append("--flyinfo")
return (options, args.target)
def main(argv=None):
if argv is None:
argv = sys.argv[1:]
(options, arg) = parse_arg(argv)
working_dir = os.getcwd()
makefile_names = ["Makefile", "makefile"]
makefile = find_makefile_upward(working_dir, makefile_names)
if makefile:
call_makefile(makefile, arg)
if arg and os.path.isfile(arg):
call_lean(arg, options)
if __name__ == "__main__":
sys.exit(main())