2018-12-18 17:48:37 +08:00
|
|
|
#!/usr/bin/env python3
|
2013-07-15 20:21:24 +08:00
|
|
|
# -*- coding: utf-8 -*-
|
2018-01-30 17:07:12 +08:00
|
|
|
# ##########################################################################
|
|
|
|
#
|
2018-01-30 18:16:29 +08:00
|
|
|
# Copyright (C) 2015-2018 European Synchrotron Radiation Facility
|
2018-01-30 17:07:12 +08:00
|
|
|
#
|
|
|
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
# of this software and associated documentation files (the "Software"), to deal
|
|
|
|
# in the Software without restriction, including without limitation the rights
|
|
|
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
# copies of the Software, and to permit persons to whom the Software is
|
|
|
|
# furnished to do so, subject to the following conditions:
|
|
|
|
#
|
|
|
|
# The above copyright notice and this permission notice shall be included in
|
|
|
|
# all copies or substantial portions of the Software.
|
|
|
|
#
|
|
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
# THE SOFTWARE.
|
|
|
|
#
|
|
|
|
# ###########################################################################
|
2013-07-15 20:21:24 +08:00
|
|
|
"""
|
2017-05-18 17:42:15 +08:00
|
|
|
Bootstrap helps you to test scripts without installing them
|
2014-11-28 01:42:28 +08:00
|
|
|
by patching your PYTHONPATH on the fly
|
2013-07-15 20:21:24 +08:00
|
|
|
|
2017-05-18 17:42:15 +08:00
|
|
|
example: ./bootstrap.py ipython
|
2013-07-15 20:21:24 +08:00
|
|
|
"""
|
|
|
|
|
|
|
|
__authors__ = ["Frédéric-Emmanuel Picca", "Jérôme Kieffer"]
|
|
|
|
__contact__ = "jerome.kieffer@esrf.eu"
|
2017-05-15 22:05:10 +08:00
|
|
|
__license__ = "MIT"
|
2020-12-04 16:24:45 +08:00
|
|
|
__date__ = "04/12/2020"
|
2013-07-15 20:21:24 +08:00
|
|
|
|
|
|
|
import sys
|
|
|
|
import os
|
|
|
|
import distutils.util
|
|
|
|
import subprocess
|
2015-12-10 18:18:31 +08:00
|
|
|
import logging
|
2015-11-21 18:24:00 +08:00
|
|
|
|
2020-12-04 16:24:45 +08:00
|
|
|
logging.basicConfig(level=logging.INFO)
|
2017-05-18 17:42:15 +08:00
|
|
|
logger = logging.getLogger("bootstrap")
|
|
|
|
|
2015-11-21 18:24:00 +08:00
|
|
|
|
2013-07-15 20:21:24 +08:00
|
|
|
def _distutils_dir_name(dname="lib"):
|
|
|
|
"""
|
|
|
|
Returns the name of a distutils build directory
|
|
|
|
"""
|
|
|
|
platform = distutils.util.get_platform()
|
|
|
|
architecture = "%s.%s-%i.%i" % (dname, platform,
|
|
|
|
sys.version_info[0], sys.version_info[1])
|
|
|
|
return architecture
|
|
|
|
|
|
|
|
|
|
|
|
def _distutils_scripts_name():
|
|
|
|
"""Return the name of the distrutils scripts sirectory"""
|
|
|
|
f = "scripts-{version[0]}.{version[1]}"
|
|
|
|
return f.format(version=sys.version_info)
|
|
|
|
|
|
|
|
|
|
|
|
def _get_available_scripts(path):
|
|
|
|
res = []
|
|
|
|
try:
|
|
|
|
res = " ".join([s.rstrip('.py') for s in os.listdir(path)])
|
|
|
|
except OSError:
|
|
|
|
res = ["no script available, did you ran "
|
|
|
|
"'python setup.py build' before bootstrapping ?"]
|
|
|
|
return res
|
|
|
|
|
2014-11-27 20:56:37 +08:00
|
|
|
|
2016-03-22 16:41:41 +08:00
|
|
|
if sys.version_info[0] >= 3: # Python3
|
2020-12-04 16:24:45 +08:00
|
|
|
|
2017-05-18 17:42:15 +08:00
|
|
|
def execfile(fullpath, globals=None, locals=None):
|
2016-03-22 16:41:41 +08:00
|
|
|
"Python3 implementation for execfile"
|
|
|
|
with open(fullpath) as f:
|
2017-05-18 17:42:15 +08:00
|
|
|
try:
|
|
|
|
data = f.read()
|
|
|
|
except UnicodeDecodeError:
|
|
|
|
raise SyntaxError("Not a Python script")
|
|
|
|
code = compile(data, fullpath, 'exec')
|
|
|
|
exec(code, globals, locals)
|
2016-03-22 16:41:41 +08:00
|
|
|
|
2015-01-23 22:07:12 +08:00
|
|
|
|
2017-05-18 17:42:15 +08:00
|
|
|
def run_file(filename, argv):
|
|
|
|
"""
|
|
|
|
Execute a script trying first to use execfile, then a subprocess
|
|
|
|
|
|
|
|
:param str filename: Script to execute
|
|
|
|
:param list[str] argv: Arguments passed to the filename
|
|
|
|
"""
|
|
|
|
full_args = [filename]
|
|
|
|
full_args.extend(argv)
|
|
|
|
|
2015-01-23 22:07:12 +08:00
|
|
|
try:
|
2017-05-18 17:42:15 +08:00
|
|
|
logger.info("Execute target using exec")
|
|
|
|
# execfile is considered as a local call.
|
|
|
|
# Providing globals() as locals will force to feed the file into
|
|
|
|
# globals() (for examples imports).
|
|
|
|
# Without this any function call from the executed file loses imports
|
|
|
|
try:
|
|
|
|
old_argv = sys.argv
|
|
|
|
sys.argv = full_args
|
|
|
|
logger.info("Patch the sys.argv: %s", sys.argv)
|
|
|
|
logger.info("Executing %s.main()", filename)
|
|
|
|
print("########### EXECFILE ###########")
|
2022-09-01 20:36:26 +08:00
|
|
|
d = globals()
|
|
|
|
d["__file__"] = filename
|
|
|
|
execfile(filename, d, d)
|
2017-05-18 17:42:15 +08:00
|
|
|
finally:
|
|
|
|
sys.argv = old_argv
|
|
|
|
except SyntaxError as error:
|
|
|
|
logger.error(error)
|
|
|
|
logger.info("Execute target using subprocess")
|
2015-01-23 22:07:12 +08:00
|
|
|
env = os.environ.copy()
|
|
|
|
env.update({"PYTHONPATH": LIBPATH + os.pathsep + os.environ.get("PYTHONPATH", ""),
|
2017-05-18 17:42:15 +08:00
|
|
|
"PATH": os.environ.get("PATH", "")})
|
|
|
|
print("########### SUBPROCESS ###########")
|
|
|
|
run = subprocess.Popen(full_args, shell=False, env=env)
|
2015-01-23 22:07:12 +08:00
|
|
|
run.wait()
|
|
|
|
|
2016-06-23 20:30:01 +08:00
|
|
|
|
2017-05-18 17:42:15 +08:00
|
|
|
def run_entry_point(entry_point, argv):
|
2016-06-23 20:30:01 +08:00
|
|
|
"""
|
2017-05-18 17:42:15 +08:00
|
|
|
Execute an entry_point using the current python context
|
|
|
|
(http://setuptools.readthedocs.io/en/latest/setuptools.html#automatic-script-creation)
|
|
|
|
|
|
|
|
:param str entry_point: A string identifying a function from a module
|
|
|
|
(NAME = PACKAGE.MODULE:FUNCTION)
|
|
|
|
"""
|
|
|
|
import importlib
|
2017-09-05 00:28:50 +08:00
|
|
|
# Remove ending extra dependencies
|
|
|
|
entry_point = entry_point.split("[")[0]
|
2017-05-18 17:42:15 +08:00
|
|
|
elements = entry_point.split("=")
|
|
|
|
target_name = elements[0].strip()
|
|
|
|
elements = elements[1].split(":")
|
|
|
|
module_name = elements[0].strip()
|
|
|
|
function_name = elements[1].strip()
|
|
|
|
|
|
|
|
logger.info("Execute target %s (function %s from module %s) using importlib", target_name, function_name, module_name)
|
|
|
|
full_args = [target_name]
|
|
|
|
full_args.extend(argv)
|
|
|
|
try:
|
|
|
|
old_argv = sys.argv
|
|
|
|
sys.argv = full_args
|
|
|
|
print("########### IMPORTLIB ###########")
|
|
|
|
module = importlib.import_module(module_name)
|
|
|
|
if hasattr(module, function_name):
|
|
|
|
func = getattr(module, function_name)
|
|
|
|
func()
|
|
|
|
else:
|
|
|
|
logger.info("Function %s not found", function_name)
|
|
|
|
finally:
|
|
|
|
sys.argv = old_argv
|
|
|
|
|
|
|
|
|
|
|
|
def find_executable(target):
|
|
|
|
"""Find a filename from a script name.
|
|
|
|
|
|
|
|
- Check the script name as file path,
|
|
|
|
- Then checks if the name is a target of the setup.py
|
|
|
|
- Then search the script from the PATH environment variable.
|
|
|
|
|
|
|
|
:param str target: Name of the script
|
|
|
|
:returns: Returns a tuple: kind, name.
|
|
|
|
"""
|
|
|
|
if os.path.isfile(target):
|
|
|
|
return ("path", os.path.abspath(target))
|
|
|
|
|
|
|
|
# search the file from setup.py
|
|
|
|
import setup
|
|
|
|
config = setup.get_project_configuration(dry_run=True)
|
|
|
|
# scripts from project configuration
|
|
|
|
if "scripts" in config:
|
|
|
|
for script_name in config["scripts"]:
|
|
|
|
if os.path.basename(script) == target:
|
|
|
|
return ("path", os.path.abspath(script_name))
|
|
|
|
# entry-points from project configuration
|
|
|
|
if "entry_points" in config:
|
|
|
|
for kind in config["entry_points"]:
|
|
|
|
for entry_point in config["entry_points"][kind]:
|
|
|
|
elements = entry_point.split("=")
|
|
|
|
name = elements[0].strip()
|
|
|
|
if name == target:
|
|
|
|
return ("entry_point", entry_point)
|
|
|
|
|
|
|
|
# search the file from env PATH
|
|
|
|
for dirname in os.environ.get("PATH", "").split(os.pathsep):
|
|
|
|
path = os.path.join(dirname, target)
|
|
|
|
if os.path.isfile(path):
|
|
|
|
return ("path", path)
|
|
|
|
|
|
|
|
return None, None
|
2016-06-23 20:30:01 +08:00
|
|
|
|
2016-06-15 22:08:41 +08:00
|
|
|
|
2014-04-29 14:39:14 +08:00
|
|
|
home = os.path.dirname(os.path.abspath(__file__))
|
2016-06-23 20:30:01 +08:00
|
|
|
LIBPATH = os.path.join(home, 'build', _distutils_dir_name('lib'))
|
2016-03-22 16:41:41 +08:00
|
|
|
cwd = os.getcwd()
|
|
|
|
os.chdir(home)
|
2018-12-15 01:15:55 +08:00
|
|
|
build = subprocess.Popen([sys.executable, "setup.py", "build"], shell=False)
|
2016-08-02 18:32:55 +08:00
|
|
|
logger.info("Build process ended with rc= %s", build.wait())
|
2016-03-22 16:41:41 +08:00
|
|
|
os.chdir(cwd)
|
2013-07-15 20:21:24 +08:00
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
if len(sys.argv) < 2:
|
2017-05-18 17:42:15 +08:00
|
|
|
logger.warning("usage: ./bootstrap.py <script>\n")
|
2015-12-10 18:18:31 +08:00
|
|
|
script = None
|
|
|
|
else:
|
|
|
|
script = sys.argv[1]
|
|
|
|
|
|
|
|
if script:
|
2016-08-02 18:32:55 +08:00
|
|
|
logger.info("Executing %s from source checkout", script)
|
2015-12-10 18:18:31 +08:00
|
|
|
else:
|
|
|
|
logging.info("Running iPython by default")
|
2013-07-15 20:21:24 +08:00
|
|
|
sys.path.insert(0, LIBPATH)
|
2017-05-18 17:42:15 +08:00
|
|
|
logger.info("Patched sys.path with %s", LIBPATH)
|
2015-12-10 18:18:31 +08:00
|
|
|
|
|
|
|
if script:
|
2017-05-18 17:42:15 +08:00
|
|
|
argv = sys.argv[2:]
|
|
|
|
kind, target = find_executable(script)
|
|
|
|
if kind == "path":
|
|
|
|
run_file(target, argv)
|
|
|
|
elif kind == "entry_point":
|
|
|
|
run_entry_point(target, argv)
|
2016-06-15 21:59:47 +08:00
|
|
|
else:
|
2017-05-18 17:42:15 +08:00
|
|
|
logger.error("Script %s not found", script)
|
2014-04-29 14:39:14 +08:00
|
|
|
else:
|
2017-05-18 17:42:15 +08:00
|
|
|
logger.info("Patch the sys.argv: %s", sys.argv)
|
2015-12-10 18:18:31 +08:00
|
|
|
sys.path.insert(2, "")
|
|
|
|
try:
|
|
|
|
from IPython import embed
|
|
|
|
except Exception as err:
|
|
|
|
logger.error("Unable to execute iPython, using normal Python")
|
|
|
|
logger.error(err)
|
|
|
|
import code
|
|
|
|
code.interact()
|
2014-04-29 14:39:14 +08:00
|
|
|
else:
|
2015-12-10 18:18:31 +08:00
|
|
|
embed()
|