2020-02-07 10:16:01 +01:00
|
|
|
#!/usr/bin/env python
|
|
|
|
|
import logging
|
|
|
|
|
import sys
|
|
|
|
|
import re
|
|
|
|
|
import os
|
|
|
|
|
import json
|
2020-11-18 15:52:37 +00:00
|
|
|
import shutil
|
2024-02-02 11:50:11 +01:00
|
|
|
|
2020-02-07 10:16:01 +01:00
|
|
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
2020-11-18 15:52:37 +00:00
|
|
|
from setup_support import SetupApp, Config
|
2020-02-07 10:16:01 +01:00
|
|
|
|
|
|
|
|
PYTHON_DATA_SCRIPT = """
|
|
|
|
|
import json
|
2024-04-25 15:02:22 +02:00
|
|
|
from sysconfig import get_config_vars, get_path, get_path_names
|
|
|
|
|
if "platinclude" in get_path_names():
|
|
|
|
|
plat_include_key = "platinclude"
|
|
|
|
|
else:
|
|
|
|
|
plat_include_key = "include"
|
2020-02-07 10:16:01 +01:00
|
|
|
result = {'config_vars': get_config_vars(),
|
2024-04-25 15:02:22 +02:00
|
|
|
'python_inc': get_path("include"),
|
|
|
|
|
'python_inc_plat': get_path(plat_include_key),
|
|
|
|
|
'prefix': get_path("data")}
|
2020-02-07 10:16:01 +01:00
|
|
|
print(json.dumps(result))
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def fetch_python_config(config):
|
2024-02-02 11:50:11 +01:00
|
|
|
logging.info("Fetch Python information...")
|
|
|
|
|
python_output = config.run(
|
|
|
|
|
config.data["python_exec"], "-c", PYTHON_DATA_SCRIPT, grab=True
|
|
|
|
|
)
|
2020-02-07 10:16:01 +01:00
|
|
|
python_data = json.loads(python_output)
|
2024-02-02 11:50:11 +01:00
|
|
|
config_vars = python_data["config_vars"]
|
2020-02-07 10:16:01 +01:00
|
|
|
python_version = config_vars["VERSION"]
|
|
|
|
|
python_ldversion = config_vars.get("LDVERSION", python_version)
|
2024-02-02 11:50:11 +01:00
|
|
|
logging.info(" %-24s %s", "Python version:", python_version)
|
2020-02-07 10:16:01 +01:00
|
|
|
|
|
|
|
|
# Current python location
|
2024-02-02 11:50:11 +01:00
|
|
|
current_prefix = python_data["prefix"]
|
2020-02-07 10:16:01 +01:00
|
|
|
|
|
|
|
|
# Fetch prefix during the build process. Some paths of interest might
|
|
|
|
|
# still reference a location used during the Python build process.
|
2024-02-02 11:50:11 +01:00
|
|
|
build_prefix = (
|
|
|
|
|
[sys.prefix]
|
|
|
|
|
+ re.findall(r"'--prefix=([^']+)'", config_vars.get("CONFIG_ARGS", ""))
|
|
|
|
|
)[-1]
|
2020-02-07 10:16:01 +01:00
|
|
|
|
|
|
|
|
def relocate(path):
|
|
|
|
|
if os.path.isabs(path):
|
|
|
|
|
rel_path = os.path.relpath(path, build_prefix)
|
|
|
|
|
if not rel_path.startswith(os.pardir):
|
|
|
|
|
# If the input path is relative to the original build
|
|
|
|
|
# directory, replace build prefix by the current one.
|
|
|
|
|
return os.path.join(current_prefix, rel_path)
|
|
|
|
|
else:
|
|
|
|
|
# Otherwise, return it unchanged
|
|
|
|
|
return path
|
|
|
|
|
else:
|
|
|
|
|
# The input path is relative so assume it's relative to the current
|
|
|
|
|
# python prefix.
|
|
|
|
|
return os.path.join(current_prefix, path)
|
|
|
|
|
|
|
|
|
|
# Retrieve cflags, and linker flags
|
2024-02-02 11:50:11 +01:00
|
|
|
static_dir = relocate(config_vars.get("LIBPL", "libs"))
|
|
|
|
|
logging.info(" %-24s %s", "Static dir", static_dir)
|
2020-02-07 10:16:01 +01:00
|
|
|
|
2024-02-02 11:50:11 +01:00
|
|
|
shared_dir = relocate(config_vars.get("LIBDIR", "."))
|
|
|
|
|
logging.info(" %-24s %s", "Shared dir", shared_dir)
|
2020-02-07 10:16:01 +01:00
|
|
|
|
|
|
|
|
# Add first relocated include dirs followed by non-relocated version.
|
|
|
|
|
# Indeed, when using venv and maybe virtualenv, includes are not copied.
|
2024-02-02 11:50:11 +01:00
|
|
|
cflags = "-I" + relocate(python_data["python_inc"])
|
|
|
|
|
if python_data["python_inc"] != python_data["python_inc_plat"]:
|
|
|
|
|
cflags += " -I" + relocate(python_data["python_inc_plat"])
|
|
|
|
|
cflags += " -I" + python_data["python_inc_plat"]
|
|
|
|
|
cflags += " -I" + python_data["python_inc"]
|
2020-02-07 10:16:01 +01:00
|
|
|
|
2024-02-02 11:50:11 +01:00
|
|
|
logging.info(" %-24s %s", "CFLAGS", cflags)
|
2020-02-07 10:16:01 +01:00
|
|
|
|
2024-02-02 11:50:11 +01:00
|
|
|
if python_version.startswith("3"):
|
2020-02-07 10:16:01 +01:00
|
|
|
# In python 3.x MODLIBS seems to drag in too many libraries
|
2024-02-02 11:50:11 +01:00
|
|
|
python_libs = [
|
|
|
|
|
config_vars[v]
|
|
|
|
|
for v in ("LIBS", "SYSLIBS")
|
|
|
|
|
if v in config_vars and config_vars[v]
|
|
|
|
|
]
|
2020-02-07 10:16:01 +01:00
|
|
|
else:
|
2024-02-02 11:50:11 +01:00
|
|
|
python_libs = [
|
|
|
|
|
config_vars[v]
|
|
|
|
|
for v in ("LIBS", "SYSLIBS", "MODLIBS")
|
|
|
|
|
if v in config_vars and config_vars[v]
|
|
|
|
|
]
|
2020-02-07 10:16:01 +01:00
|
|
|
python_libs = " ".join(python_libs)
|
|
|
|
|
|
2024-02-02 11:50:11 +01:00
|
|
|
python_shared_libs = "-L%s -lpython%s %s" % (
|
|
|
|
|
shared_dir,
|
|
|
|
|
python_ldversion,
|
|
|
|
|
python_libs,
|
|
|
|
|
)
|
2020-11-18 15:52:37 +00:00
|
|
|
python_static_libs = python_libs
|
2020-02-07 10:16:01 +01:00
|
|
|
libpython_a = os.path.join(
|
2024-02-02 11:50:11 +01:00
|
|
|
static_dir, config_vars.get("LIBRARY", "libpython%s.a" % python_version)
|
|
|
|
|
)
|
2020-02-07 10:16:01 +01:00
|
|
|
|
|
|
|
|
if os.path.isfile(libpython_a):
|
2024-02-02 11:50:11 +01:00
|
|
|
config.set_data("GNATCOLL_PYTHON_STATIC_LIB", libpython_a, sub="gprbuild")
|
2021-05-26 10:56:34 +01:00
|
|
|
else:
|
2024-02-02 11:50:11 +01:00
|
|
|
logging.info("static python library not found")
|
2020-11-18 15:52:37 +00:00
|
|
|
|
2024-02-02 11:50:11 +01:00
|
|
|
if sys.platform.startswith("linux"):
|
2020-02-07 10:16:01 +01:00
|
|
|
# On Linux platform, even when linking with the static libpython,
|
|
|
|
|
# symbols not used by the application itself should be exported so
|
|
|
|
|
# that shared library present in Python can use the Python C API.
|
2024-02-02 11:50:11 +01:00
|
|
|
python_static_libs += " -export-dynamic"
|
|
|
|
|
python_shared_libs += " -export-dynamic"
|
2020-02-07 10:16:01 +01:00
|
|
|
|
2024-02-02 11:50:11 +01:00
|
|
|
logging.info(" %-24s %s", "Shared linker flags", python_shared_libs)
|
|
|
|
|
logging.info(" %-24s %s", "Static linker flags", python_static_libs)
|
2020-02-07 10:16:01 +01:00
|
|
|
|
|
|
|
|
# User does not have the choice between linking with static libpython
|
|
|
|
|
# and shared libpython. If --enable-shared or --enable-framework was
|
|
|
|
|
# passed to Python's configure during Python build, then we should
|
|
|
|
|
# link with the shared libpython, otherwise with the static one.
|
|
|
|
|
# Indeed otherwise some C modules might not work as expected or even
|
|
|
|
|
# crash. On Windows always link with shared version of libpython
|
|
|
|
|
# (if the static is present, this is just an indirection to the shared)
|
2024-02-02 11:50:11 +01:00
|
|
|
if (
|
|
|
|
|
"--enable-shared" in config_vars.get("CONFIG_ARGS", "")
|
|
|
|
|
or "--enable-framework" in config_vars.get("CONFIG_ARGS", "")
|
|
|
|
|
or sys.platform.startswith("win")
|
|
|
|
|
):
|
|
|
|
|
logging.info("Force link to shared python library")
|
|
|
|
|
config.set_data("GNATCOLL_PYTHON_LIBS", python_shared_libs, sub="gprbuild")
|
|
|
|
|
config.set_data("GNATCOLL_LIBPYTHON_KIND", "shared", sub="gprbuild")
|
2020-02-07 10:16:01 +01:00
|
|
|
else:
|
2024-02-02 11:50:11 +01:00
|
|
|
logging.info("Force link to static python library")
|
|
|
|
|
config.set_data("GNATCOLL_PYTHON_LIBS", python_static_libs, sub="gprbuild")
|
|
|
|
|
config.set_data("GNATCOLL_LIBPYTHON_KIND", "static", sub="gprbuild")
|
|
|
|
|
config.set_data("GNATCOLL_PYTHON_CFLAGS", cflags, sub="gprbuild")
|
2020-02-07 10:16:01 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class GNATCollPython(SetupApp):
|
2024-02-02 11:50:11 +01:00
|
|
|
name = "gnatcoll_python"
|
|
|
|
|
project = "gnatcoll_python.gpr"
|
|
|
|
|
description = "GNATColl Python bindings"
|
2020-02-07 10:16:01 +01:00
|
|
|
|
|
|
|
|
def create(self):
|
|
|
|
|
super(GNATCollPython, self).create()
|
|
|
|
|
self.build_cmd.add_argument(
|
2024-02-02 11:50:11 +01:00
|
|
|
"--python-exec",
|
|
|
|
|
help="set python executable location",
|
|
|
|
|
metavar="PATH",
|
|
|
|
|
default=sys.executable,
|
|
|
|
|
)
|
2020-02-07 10:16:01 +01:00
|
|
|
self.build_cmd.add_argument(
|
2024-02-02 11:50:11 +01:00
|
|
|
"--debug",
|
|
|
|
|
help="build project in debug mode",
|
2020-02-07 10:16:01 +01:00
|
|
|
action="store_true",
|
2024-02-02 11:50:11 +01:00
|
|
|
default=False,
|
|
|
|
|
)
|
2020-02-07 10:16:01 +01:00
|
|
|
|
|
|
|
|
def update_config(self, config, args):
|
|
|
|
|
# Fetch python information
|
2024-02-02 11:50:11 +01:00
|
|
|
config.set_data("python_exec", args.python_exec)
|
2020-02-07 10:16:01 +01:00
|
|
|
fetch_python_config(config)
|
|
|
|
|
|
2024-02-02 11:50:11 +01:00
|
|
|
logging.info(
|
|
|
|
|
"%-26s %s", "Libraries kind", ", ".join(config.data["library_types"])
|
|
|
|
|
)
|
2020-02-07 10:16:01 +01:00
|
|
|
|
|
|
|
|
# Set library version
|
2024-02-02 11:50:11 +01:00
|
|
|
with open(
|
|
|
|
|
os.path.join(config.source_dir, "..", "version_information"), "r"
|
|
|
|
|
) as fd:
|
2020-02-07 10:16:01 +01:00
|
|
|
version = fd.read().strip()
|
2024-02-02 11:50:11 +01:00
|
|
|
config.set_data("GNATCOLL_VERSION", version, sub="gprbuild")
|
|
|
|
|
logging.info("%-26s %s", "Version", version)
|
2020-02-07 10:16:01 +01:00
|
|
|
|
|
|
|
|
# Set build mode
|
2024-02-02 11:50:11 +01:00
|
|
|
config.set_data("BUILD", "DEBUG" if args.debug else "PROD", sub="gprbuild")
|
|
|
|
|
logging.info("%-26s %s", "Build mode", config.data["gprbuild"]["BUILD"])
|
2020-02-07 10:16:01 +01:00
|
|
|
|
|
|
|
|
# Set GNATCOLL_OS
|
2024-02-02 11:50:11 +01:00
|
|
|
if "darwin" in config.data["canonical_target"]:
|
|
|
|
|
gnatcoll_os = "osx"
|
|
|
|
|
elif "windows" in config.data["canonical_target"]:
|
|
|
|
|
gnatcoll_os = "windows"
|
2020-02-07 10:16:01 +01:00
|
|
|
else:
|
|
|
|
|
# Assume this is an Unix system
|
2024-02-02 11:50:11 +01:00
|
|
|
gnatcoll_os = "unix"
|
|
|
|
|
config.set_data("GNATCOLL_OS", gnatcoll_os, sub="gprbuild")
|
2020-02-07 10:16:01 +01:00
|
|
|
|
|
|
|
|
def variants(self, config, cmd):
|
|
|
|
|
result = []
|
2024-02-02 11:50:11 +01:00
|
|
|
for library_type in config.data["library_types"]:
|
|
|
|
|
gpr_vars = {
|
|
|
|
|
"LIBRARY_TYPE": library_type,
|
|
|
|
|
"XMLADA_BUILD": library_type,
|
|
|
|
|
"GPR_BUILD": library_type,
|
|
|
|
|
}
|
|
|
|
|
if cmd == "install":
|
|
|
|
|
result.append(
|
|
|
|
|
(
|
|
|
|
|
["--build-name=%s" % library_type, "--build-var=LIBRARY_TYPE"],
|
|
|
|
|
gpr_vars,
|
|
|
|
|
)
|
|
|
|
|
)
|
2020-02-07 10:16:01 +01:00
|
|
|
else:
|
|
|
|
|
result.append(([], gpr_vars))
|
|
|
|
|
return result
|
|
|
|
|
|
2020-11-18 15:52:37 +00:00
|
|
|
def install(self, args):
|
|
|
|
|
config = Config()
|
2021-05-26 10:56:34 +01:00
|
|
|
has_static_python = "GNATCOLL_PYTHON_STATIC_LIB" in config.data["gprbuild"]
|
2024-02-02 11:51:46 +01:00
|
|
|
|
|
|
|
|
if not has_static_python:
|
|
|
|
|
super(GNATCollPython, self).install(args)
|
|
|
|
|
else:
|
2021-05-26 10:56:34 +01:00
|
|
|
python_la = config.data["gprbuild"]["GNATCOLL_PYTHON_STATIC_LIB"]
|
2024-02-02 11:51:46 +01:00
|
|
|
prefix = args.prefix
|
|
|
|
|
if prefix is None:
|
|
|
|
|
prefix = config.data["prefix"]
|
2024-07-09 12:30:00 +02:00
|
|
|
|
|
|
|
|
# <PROJECT_DIR> will be replaced by Project'Project_Dir after
|
|
|
|
|
# call to gprinstall. This ensure the installed project will
|
|
|
|
|
# keep some location relative to the project file and thus be
|
|
|
|
|
# moved around. (gprinstall by default replace Project'Project_Dir
|
|
|
|
|
# by the absolute path in which the project is originally installed
|
2024-02-02 11:51:46 +01:00
|
|
|
rel_target = os.path.join(
|
2024-07-09 12:30:00 +02:00
|
|
|
"<PROJECT_DIR>",
|
|
|
|
|
"..",
|
|
|
|
|
"..",
|
|
|
|
|
"lib",
|
|
|
|
|
"gnatcoll_python.static",
|
|
|
|
|
os.path.basename(python_la),
|
2021-05-26 10:56:34 +01:00
|
|
|
)
|
2024-02-02 11:51:46 +01:00
|
|
|
abs_target = os.path.join(
|
|
|
|
|
prefix, "lib", "gnatcoll_python.static", os.path.basename(python_la)
|
2024-02-02 11:50:11 +01:00
|
|
|
)
|
2020-11-18 15:52:37 +00:00
|
|
|
|
2024-02-02 11:51:46 +01:00
|
|
|
shutil.copy(config.json_cache, config.json_cache + ".backup")
|
|
|
|
|
try:
|
|
|
|
|
# Temporary change the configuration to set a relative path to the
|
|
|
|
|
# static Python library.
|
|
|
|
|
config.set_data(
|
|
|
|
|
"GNATCOLL_PYTHON_STATIC_LIB", rel_target, sub="gprbuild"
|
|
|
|
|
)
|
|
|
|
|
config.save_data()
|
|
|
|
|
|
|
|
|
|
# Perform the installation
|
|
|
|
|
super(GNATCollPython, self).install(args)
|
|
|
|
|
|
2024-07-09 12:30:00 +02:00
|
|
|
# Hack the installed project to replace PROJECT_DIR reference by Project'Project_Dir
|
|
|
|
|
with open(
|
|
|
|
|
os.path.join(prefix, "share", "gpr", "gnatcoll_python.gpr")
|
|
|
|
|
) as fd:
|
|
|
|
|
content = fd.read()
|
|
|
|
|
content = content.replace('"<PROJECT_DIR>', "Project'Project_Dir & \"")
|
|
|
|
|
with open(
|
|
|
|
|
os.path.join(prefix, "share", "gpr", "gnatcoll_python.gpr"), "w"
|
|
|
|
|
) as fd:
|
|
|
|
|
fd.write(content)
|
|
|
|
|
|
2024-02-02 11:51:46 +01:00
|
|
|
# Copy over the libpython*.a
|
|
|
|
|
logging.info("Copy static libpython into target lib")
|
|
|
|
|
if not os.path.isdir(os.path.dirname(abs_target)):
|
|
|
|
|
os.mkdir(abs_target)
|
|
|
|
|
result = shutil.copy(python_la, abs_target)
|
|
|
|
|
logging.info(f"Python static lib in {result}")
|
|
|
|
|
finally:
|
|
|
|
|
# Restore the configuration cache
|
|
|
|
|
shutil.copy(config.json_cache + ".backup", config.json_cache)
|
|
|
|
|
os.unlink(config.json_cache + ".backup")
|
|
|
|
|
|
2020-02-07 10:16:01 +01:00
|
|
|
|
2024-02-02 11:50:11 +01:00
|
|
|
if __name__ == "__main__":
|
2020-02-07 10:16:01 +01:00
|
|
|
app = GNATCollPython()
|
|
|
|
|
sys.exit(app.run())
|