Skip to content

Commit

Permalink
Merge pull request #77 from kif/meson
Browse files Browse the repository at this point in the history
Meson
  • Loading branch information
kif authored Dec 4, 2023
2 parents 50ea7f6 + 6847a0c commit 62c3692
Show file tree
Hide file tree
Showing 85 changed files with 1,223 additions and 2,004 deletions.
23 changes: 23 additions & 0 deletions .github/workflows/pylint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Pylint

on: [push]

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10"]
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pylint
- name: Analysing the code with pylint
run: |
pylint $(git ls-files '*.py')
7 changes: 4 additions & 3 deletions .github/workflows/python-package-mac.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ jobs:
runs-on: macos-latest
strategy:
matrix:
python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"]
xcode-version: [latest-stable, 11]
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
xcode-version: [latest-stable, 14]

steps:
- uses: actions/checkout@v2
Expand All @@ -30,6 +30,7 @@ jobs:
python -m pip install --upgrade pip
pip install flake8
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
if [ -f requirements-dev.txt ]; then pip install -r requirements-dev.txt; fi
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
Expand All @@ -41,7 +42,7 @@ jobs:
pip install .
- name: Test of run_tests.py
run: |
python setup.py build test
python run_tests.py
- name: Run end to end tests with e2etest.py
run: |
mkdir empty
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/python-package-ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"]
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]

steps:
- uses: actions/checkout@v2
Expand Down Expand Up @@ -45,7 +45,7 @@ jobs:
pip install .
- name: Test of run_tests.py
run: |
python setup.py build test
python run_tests.py
- name: Run end to end tests with e2etest.py
run: |
mkdir empty
Expand Down
252 changes: 110 additions & 142 deletions bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,73 +10,94 @@
__authors__ = ["Frédéric-Emmanuel Picca", "Jérôme Kieffer"]
__contact__ = "[email protected]"
__license__ = "MIT"
__date__ = "09/07/2020"
__date__ = "03/03/2023"

import sys
import os
import distutils.util
import subprocess
import logging
import collections
from argparse import ArgumentParser

if sys.version_info[:2] < (3, 11):
import tomli
else:
import tomllib as tomli
logging.basicConfig()
logger = logging.getLogger("bootstrap")


def is_debug_python():
"""Returns true if the Python interpreter is in debug mode."""
try:
import sysconfig
except ImportError: # pragma nocover
# Python < 2.7
import distutils.sysconfig as sysconfig
def get_project_name(root_dir):
"""Retrieve project name by running python setup.py --name in root_dir.
if sysconfig.get_config_var("Py_DEBUG"):
return True
:param str root_dir: Directory where to run the command.
:return: The name of the project stored in root_dir
"""
logger.debug("Getting project name in %s", root_dir)
with open("pyproject.toml") as f:
pyproject = tomli.loads(f.read())
return pyproject.get("project", {}).get("name")

return hasattr(sys, "gettotalrefcount")

def build_project(name, root_dir):
"""Build locally the project using meson
def _distutils_dir_name(dname="lib"):
"""
Returns the name of a distutils build directory
:param str name: Name of the project.
:param str root_dir: Root directory of the project
:return: The path to the directory were build was performed
"""
platform = distutils.util.get_platform()
architecture = "%s.%s-%i.%i" % (dname, platform,
sys.version_info[0], sys.version_info[1])
if is_debug_python():
architecture += "-pydebug"
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)
extra = []
libdir = "lib"
if sys.platform == "win32":
libdir = "Lib"
# extra = ["--buildtype", "plain"]

build = os.path.join(root_dir, "build")
if not(os.path.isdir(build) and os.path.isdir(os.path.join(build, name))):
p = subprocess.Popen(["meson", "setup", "build"],
shell=False, cwd=root_dir, env=os.environ)
p.wait()
p = subprocess.Popen(["meson", "configure", "--prefix", "/"] + extra,
shell=False, cwd=build, env=os.environ)
p.wait()
p = subprocess.Popen(["meson", "install", "--destdir", "."],
shell=False, cwd=build, env=os.environ)
logger.debug("meson install ended with rc= %s", p.wait())

home = None
if os.environ.get("PYBUILD_NAME") == name:
# we are in the debian packaging way
home = os.environ.get("PYTHONPATH", "").split(os.pathsep)[-1]
if not home:
if os.environ.get("BUILDPYTHONPATH"):
home = os.path.abspath(os.environ.get("BUILDPYTHONPATH", ""))
else:
if sys.platform == "win32":
home = os.path.join(build, libdir, "site-packages")
else:
python_version = f"python{sys.version_info.major}.{sys.version_info.minor}"
home = os.path.join(build, libdir, python_version, "site-packages")
home = os.path.abspath(home)

cnt = 0
while not os.path.isdir(home):
cnt += 1
home = os.path.split(home)[0]
for _ in range(cnt):
n = os.listdir(home)[0]
home = os.path.join(home, n)

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
logger.warning("Building %s to %s", name, home)

return home

if sys.version_info[0] >= 3: # Python3

def execfile(fullpath, globals=None, locals=None):
"Python3 implementation for execfile"
with open(fullpath) as f:
try:
data = f.read()
except UnicodeDecodeError:
raise SyntaxError("Not a Python script")
code = compile(data, fullpath, 'exec')
exec(code, globals, locals)
def execfile(fullpath, globals=None, locals=None):
"Python3 implementation for execfile"
with open(fullpath) as f:
try:
data = f.read()
except UnicodeDecodeError:
raise SyntaxError("Not a Python script")
code = compile(data, fullpath, 'exec')
exec(code, globals, locals)


def run_file(filename, argv):
Expand Down Expand Up @@ -117,21 +138,18 @@ def run_file(filename, argv):
run.wait()


def run_entry_point(entry_point, argv):
def run_entry_point(target_name, entry_point, argv):
"""
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 [EXTRA])
(NAME = PACKAGE.MODULE:FUNCTION)
:param argv: list of arguments
"""
import importlib
elements = entry_point.split("=")
target_name = elements[0].strip()
elements = elements[1].split(":")
elements = entry_point.split(":")
module_name = elements[0].strip()
# Take care of entry_point optional "extra" requirements declaration
function_name = elements[1].split()[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]
Expand Down Expand Up @@ -163,74 +181,49 @@ def find_executable(target):
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_name) == 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)
# search the executable in pyproject.toml
with open(os.path.join(PROJECT_DIR, "pyproject.toml")) as f:
pyproject = tomli.loads(f.read())

scripts = {}
scripts.update(pyproject.get("project", {}).get("scripts", {}))
scripts.update(pyproject.get("project", {}).get("gui-scripts", {}))

for script, entry_point in scripts.items():
if script == target:
print(script, entry_point)
return ("entry_point", target, entry_point)
return None, None


def main(argv):
parser = ArgumentParser(prog="bootstrap", usage="./bootstrap.py <script>",
description=__doc__)
parser.add_argument("script", nargs="*")
parser.add_argument("-m", help="run library module as a script (terminates option list)")
PROJECT_DIR = os.path.dirname(os.path.abspath(__file__))
PROJECT_NAME = get_project_name(PROJECT_DIR)
logger.info("Project name: %s", PROJECT_NAME)

Options = collections.namedtuple("Options", ["script", "module"])
if len(argv) == 1:
options = Options(script=None, module=None)
if __name__ == "__main__":
LIBPATH = build_project(PROJECT_NAME, PROJECT_DIR)
if len(sys.argv) < 2:
logger.warning("usage: ./bootstrap.py <script>\n")
script = None
else:
if argv[1] in ["-h", "--help"]:
parser.print_help()
return
if argv[1] == "-m":
if len(argv) < 3:
parser.parse_args(argv[1:])
return
options = Options(script=None, module=argv[2:])
else:
options = Options(script=argv[1:], module=None)

if options.script is not None:
logger.info("Executing %s from source checkout", options.script)
script = options.script[0]
argv = options.script[1:]
kind, target = find_executable(script)
if kind == "path":
run_file(target, argv)
elif kind == "entry_point":
run_entry_point(target, argv)
script = sys.argv[1]

if script:
logger.info("Executing %s from source checkout", script)
else:
logging.info("Running iPython by default")
sys.path.insert(0, LIBPATH)
logger.info("Patched sys.path with %s", LIBPATH)

if script:
argv = sys.argv[2:]
res = find_executable(script)
if res[0] == "path":
run_file(res[1], argv)
elif res[0] == "entry_point":
run_entry_point(res[1], res[2], argv)
else:
logger.error("Script %s not found", options.script)
elif options.module is not None:
logging.info("Running module %s", options.module)
import runpy
module = options.module[0]
try:
old = sys.argv
sys.argv = [None] + options.module[1:]
runpy.run_module(module, run_name="__main__", alter_sys=True)
finally:
sys.argv = old
logger.error("Script %s not found", script)
else:
logging.info("Running IPython by default")
logger.info("Patch the sys.argv: %s", sys.argv)
Expand All @@ -245,28 +238,3 @@ def main(argv):
else:
embed()


if __name__ == "__main__":
home = os.path.dirname(os.path.abspath(__file__))
LIBPATH = os.path.join(home, 'build', _distutils_dir_name('lib'))
cwd = os.getcwd()
os.chdir(home)
build = subprocess.Popen([sys.executable, "setup.py", "build"],
shell=False, cwd=os.path.dirname(os.path.abspath(__file__)))
build_rc = build.wait()
if not os.path.exists(LIBPATH):
logger.warning("`lib` directory does not exist, trying common Python3 lib")
LIBPATH = os.path.join(os.path.split(LIBPATH)[0], "lib")
os.chdir(cwd)

if build_rc == 0:
logger.info("Build process ended.")
else:
logger.error("Build process ended with rc=%s", build_rc)
sys.exit(-1)

sys.path.insert(0, LIBPATH)
logger.info("Patched sys.path with %s", LIBPATH)

main(sys.argv)

Loading

0 comments on commit 62c3692

Please sign in to comment.