QGIS/python/common/qgis_sip_project.py

128 lines
4.7 KiB
Python

# ***************************************************************************
# qgis_sip_project.py
# ---------------------
# Date : September 2021
# Copyright : (C) 2021 by Sandro Mani
# Email : manisandro at gmail dot com
# ***************************************************************************
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU General Public License as published by *
# * the Free Software Foundation; either version 2 of the License, or *
# * (at your option) any later version. *
# * *
# ***************************************************************************
from pathlib import Path
import shutil
from pyqtbuild import PyQtBindings, PyQtProject
from sipbuild import Option, Bindings
class QgisProject(PyQtProject):
"""
A SIP project for building one part of the QGIS bindings.
To be subclassed in a project.py file.
"""
bindings_factories: list[type[Bindings]] # To be set by subclass
def __init__(self):
"""Initialize the project."""
super().__init__()
self.sip_files_dir = "."
self.bindings_factories = type(self).bindings_factories
def get_options(self):
"""Return the list of configurable options."""
options = super().get_options()
options.append(
Option(
"include_dirs",
option_type=list,
help="additional directory to search for .sip files",
metavar="DIR",
)
)
options.append(
Option(
"disable_features",
option_type=list,
help="features to disable project-wide",
metavar="NAME",
)
)
return options
def apply_user_defaults(self, tool):
"""Set default values for user options that haven't been set yet."""
super().apply_user_defaults(tool)
if self.include_dirs is not None:
self.sip_include_dirs += self.include_dirs
def build(self):
super().build()
# Copy all files from the build directory to "out/", only overwriting
# them if they've changed. This preserves the timestamp for unchanged
# files, so ninja doesn't rebuild them.
build_dir = Path(self.build_dir)
out_dir = Path("out")
out_dir.mkdir(exist_ok=True)
changed = []
for source in build_dir.glob("**/*"):
if not source.is_file():
continue
rel = source.relative_to(build_dir)
dest = out_dir / rel
if dest.exists():
# Check if the files match, otherwise copy
source_data = source.read_bytes()
if source_data != dest.read_bytes():
print("File changed:", rel)
dest.write_bytes(source_data)
changed.append(dest)
else:
# The file doesn't exist, create all directories and copy.
dest.parent.mkdir(exist_ok=True, parents=True)
shutil.copy(source, dest)
changed.append(dest)
# HACK: If a header changes, we need to rebuild all .cpp files that
# include it as well. This is what deps files are for, and Ninja
# should use them to find this out itself, but there's likely some
# bug with restat=1. So to make sure we don't get broken builds, we
# touch every .cpp file manually.
if any(f.suffix == ".h" for f in changed):
for f in out_dir.glob("**/*.cpp"):
f.touch()
class QgisBindings(PyQtBindings):
"""Bindings building one part of QGIS"""
name: str # To be set by subclass
def __init__(self, project):
"""Initialize the bindings."""
name = type(self).name
super().__init__(project, name)
self.sip_file = f"{name}.sip"
self.exceptions = True
self.release_gil = True
def apply_user_defaults(self, tool):
"""Set default values for user options that haven't been set yet."""
if self.project.py_platform.startswith("win32"):
self.tags.append("WS_WIN")
elif self.project.py_platform == "darwin":
self.tags.append("WS_MACX")
else:
self.tags.append("WS_X11")
if self.project.disable_features is not None:
self.disabled_features = self.project.disable_features
super().apply_user_defaults(tool)