Skip to content

Commit

Permalink
Support .pbc config files
Browse files Browse the repository at this point in the history
  • Loading branch information
tttapa committed May 26, 2024
1 parent 04c124d commit d890f34
Show file tree
Hide file tree
Showing 11 changed files with 217 additions and 15 deletions.
70 changes: 55 additions & 15 deletions src/py_build_cmake/config/load.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

from .. import __version__
from ..common import Config, ConfigError
from .cli_override import CLIOption, parse_cli
from .cli_override import CLIOption, parse_cli, parse_file
from .options.config_path import ConfPath
from .options.config_reference import ConfigReference
from .options.default import ConfigDefaulter
Expand Down Expand Up @@ -103,6 +103,20 @@ def try_load_toml(path: Path):
raise ConfigError(msg) from e


def try_load_pbc(path: Path):
try:
return parse_file(path.read_text("utf-8"))
except FileNotFoundError as e:
msg = f"Config file {str(path.absolute())!r} not found"
raise ConfigError(msg) from e
except OSError as e:
msg = f"Config file {str(path.absolute())!r} could not be loaded"
raise ConfigError(msg) from e
except LarkError as e:
msg = f"Config file {str(path.absolute())!r} is invalid"
raise ConfigError(msg) from e


def read_config(
pyproject_path: str | Path,
flag_overrides: dict[str, list[str]],
Expand All @@ -116,14 +130,8 @@ def read_config(
msg = "Missing [project] table"
raise ConfigError(msg)

# Load local override
localconfig_path = pyproject_folder / "py-build-cmake.local.toml"
if localconfig_path.exists():
flag_overrides.setdefault("local", []).insert(0, str(localconfig_path))
# Load override for cross-compilation
crossconfig_path = pyproject_folder / "py-build-cmake.cross.toml"
if crossconfig_path.exists():
flag_overrides.setdefault("cross", []).insert(0, str(crossconfig_path))
# Load local overrides
check_if_local_configs_exist(flag_overrides, pyproject_folder)

# File names mapping to the actual dict with the config
config_files: dict[str, dict[str, Any]] = {
Expand All @@ -142,10 +150,21 @@ def read_config(
fullpath = path
else:
fullpath = (Path(os.environ.get("PWD", ".")) / path).resolve()
config = try_load_toml(fullpath)
if config: # Treat empty file as no override
config_files[fullpath.as_posix()] = config
overrides[ConfPath((fullpath.as_posix(),))] = targetpath
if path.suffix == ".toml":
config = try_load_toml(fullpath)
if config: # Treat empty file as no override
config_files[fullpath.as_posix()] = config
overrides[ConfPath((fullpath.as_posix(),))] = targetpath
elif path.suffix == ".pbc":
options = try_load_pbc(fullpath)
for i, o in enumerate(options):
label = f"{fullpath.as_posix()}{i+1}"
override = add_cli_override(config_files, o, label, targetpath)
overrides.update(override)
else:
msg = f"Config file {str(path.absolute())!r} "
msg += "has an unsupported extension (.toml or .pbc)"
raise ConfigError(msg)

# Command-line overrides
for i, o in enumerate(cli_overrides):
Expand All @@ -154,10 +173,31 @@ def read_config(
return process_config(pyproject_path, config_files, overrides)


def check_if_local_configs_exist(flag_overrides, pyproject_folder):
localconfig_path = pyproject_folder / "py-build-cmake.local.pbc"
if localconfig_path.exists():
flag_overrides.setdefault("local", []).insert(0, str(localconfig_path))
localconfig_path = localconfig_path.with_suffix(".toml")
if localconfig_path.exists():
flag_overrides.setdefault("local", []).insert(0, str(localconfig_path))
# Load override for cross-compilation
crossconfig_path = pyproject_folder / "py-build-cmake.cross.pbc"
if crossconfig_path.exists():
flag_overrides.setdefault("cross", []).insert(0, str(crossconfig_path))
crossconfig_path = crossconfig_path.with_suffix(".toml")
if crossconfig_path.exists():
flag_overrides.setdefault("cross", []).insert(0, str(crossconfig_path))


def add_cli_override(
config_files: dict[str, dict[str, Any]], opt: CLIOption, label: str
config_files: dict[str, dict[str, Any]],
opt: CLIOption,
label: str,
targetpath: ConfPath | None = None,
):
overrides = {ConfPath.from_string(label): get_tool_pbc_path()}
if targetpath is None:
targetpath = get_tool_pbc_path()
overrides = {ConfPath((label,)): targetpath}
o: dict = config_files.setdefault(label, {})
for k in opt.key[:-1]:
o = o.setdefault(k, {})
Expand Down
2 changes: 2 additions & 0 deletions test-packages/local-options/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
!py-build-cmake.local.toml
!py-build-cmake.local.pbc
94 changes: 94 additions & 0 deletions test-packages/local-options/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
cmake_minimum_required(VERSION 3.17)
project(local-options)

if (NOT DEFINED CACHE{OPTION_A} OR NOT OPTION_A STREQUAL "foo-bar-local-toml")
message(SEND_ERROR "Failed - OPTION_A=\"${OPTION_A}\"")
else()
list(LENGTH OPTION_A OPTION_A_LEN)
if (NOT OPTION_A_LEN EQUAL 1)
message(SEND_ERROR "Failed - OPTION_A_LEN=${OPTION_A_LEN}")
endif()
get_property(OPTION_A_TYPE CACHE OPTION_A PROPERTY TYPE)
if (NOT OPTION_A_TYPE STREQUAL "UNINITIALIZED")
message(SEND_ERROR "Failed - OPTION_A_TYPE=${OPTION_A_TYPE}")
endif()
endif()

if (NOT DEFINED CACHE{OPTION_B} OR NOT OPTION_B STREQUAL "baz\;local")
message(SEND_ERROR "Failed - OPTION_B=\"${OPTION_B}\"")
else()
list(LENGTH OPTION_B OPTION_B_LEN)
if (NOT OPTION_B_LEN EQUAL 1)
message(SEND_ERROR "Failed - OPTION_B_LEN=${OPTION_B_LEN}")
endif()
get_property(OPTION_B_TYPE CACHE OPTION_B PROPERTY TYPE)
if (NOT OPTION_B_TYPE STREQUAL "UNINITIALIZED")
message(SEND_ERROR "Failed - OPTION_B_TYPE=${OPTION_B_TYPE}")
endif()
endif()

if (NOT DEFINED CACHE{OPTION_C} OR NOT OPTION_C STREQUAL "foo;bar;local-toml;local-pbc")
message(SEND_ERROR "Failed - OPTION_C=\"${OPTION_C}\"")
else()
list(LENGTH OPTION_C OPTION_C_LEN)
if (NOT OPTION_C_LEN EQUAL 4)
message(SEND_ERROR "Failed - OPTION_C_LEN=${OPTION_C_LEN}")
endif()
get_property(OPTION_C_TYPE CACHE OPTION_C PROPERTY TYPE)
if (NOT OPTION_C_TYPE STREQUAL "UNINITIALIZED")
message(SEND_ERROR "Failed - OPTION_C_TYPE=${OPTION_C_TYPE}")
endif()
endif()

if (NOT DEFINED CACHE{OPTION_D} OR NOT OPTION_D)
message(SEND_ERROR "Failed - OPTION_D=\"${OPTION_D}\"")
else()
list(LENGTH OPTION_D OPTION_D_LEN)
if (NOT OPTION_D_LEN EQUAL 1)
message(SEND_ERROR "Failed - OPTION_D_LEN=${OPTION_D_LEN}")
endif()
get_property(OPTION_D_TYPE CACHE OPTION_D PROPERTY TYPE)
if (NOT OPTION_D_TYPE STREQUAL "BOOL")
message(SEND_ERROR "Failed - OPTION_D_TYPE=${OPTION_D_TYPE}")
endif()
endif()

if (NOT DEFINED CACHE{OPTION_E} OR NOT OPTION_E STREQUAL "/usr/bin/local-toml")
message(SEND_ERROR "Failed - OPTION_E=\"${OPTION_E}\"")
else()
list(LENGTH OPTION_E OPTION_E_LEN)
if (NOT OPTION_E_LEN EQUAL 1)
message(SEND_ERROR "Failed - OPTION_E_LEN=${OPTION_E_LEN}")
endif()
get_property(OPTION_E_TYPE CACHE OPTION_E PROPERTY TYPE)
if (NOT OPTION_E_TYPE STREQUAL "FILEPATH")
message(SEND_ERROR "Failed - OPTION_E_TYPE=${OPTION_E_TYPE}")
endif()
endif()

if (LINUX)
set(OPTION_F_CMP "/some/linux/specific/path/local" "/usr/some/path")
elseif(WIN32)
set(OPTION_F_CMP "/some/windows/specific/path/local" "/usr/some/path")
elseif(APPLE)
set(OPTION_F_CMP "/some/macos/specific/path/local" "/usr/some/path")
else()
set(OPTION_F_CMP "/usr/some/path")
endif()
if (NOT DEFINED CACHE{OPTION_F} OR NOT OPTION_F STREQUAL OPTION_F_CMP)
message(SEND_ERROR "Failed - OPTION_F=\"${OPTION_F}\"")
else()
list(LENGTH OPTION_F OPTION_F_LEN)
list(LENGTH OPTION_F_CMP OPTION_F_CMP_LEN)
if (NOT OPTION_F_LEN EQUAL OPTION_F_CMP_LEN)
message(SEND_ERROR "Failed - OPTION_F_LEN=${OPTION_F_LEN}")
endif()
get_property(OPTION_F_TYPE CACHE OPTION_F PROPERTY TYPE)
if (NOT OPTION_F_TYPE STREQUAL "UNINITIALIZED")
message(SEND_ERROR "Failed - OPTION_F_TYPE=${OPTION_F_TYPE}")
endif()
endif()

file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/__init__.py" "# Empty")
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/__init__.py"
DESTINATION "${PY_BUILD_CMAKE_MODULE_NAME}")
Empty file.
3 changes: 3 additions & 0 deletions test-packages/local-options/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Local options

Checks loading of local override files.
6 changes: 6 additions & 0 deletions test-packages/local-options/py-build-cmake.local.pbc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
cmake.options.OPTION_C+=local-pbc
cmake.options.OPTION_D=True
cmake.options.OPTION_E-=/usr/bin/bash
linux.cmake.options.OPTION_F=/some/linux/specific/path/local
windows.cmake.options.OPTION_F=/some/windows/specific/path/local
mac.cmake.options.OPTION_F=/some/macos/specific/path/local
5 changes: 5 additions & 0 deletions test-packages/local-options/py-build-cmake.local.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[cmake.options]
OPTION_A = "foo-bar-local-toml"
OPTION_B = "baz;local"
OPTION_C = {"append" = "local-toml"}
OPTION_E = {"append" = "/usr/bin/local-toml"}
39 changes: 39 additions & 0 deletions test-packages/local-options/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
[project]
name = "local_options"
version = "0.2.0a15.dev0"
description = "Checks loading of local override files"
readme = "README.md"
requires-python = ">=3.7"
license = { "file" = "LICENSE" }
authors = [{ "name" = "Pieter P", "email" = "[email protected]" }]
keywords = ["example"]
classifiers = []
urls = { "Documentation" = "https://tttapa.github.io/" }
dependencies = []

[build-system]
requires = ["py-build-cmake~=0.2.0a15.dev0"]
build-backend = "py_build_cmake.build"

[tool.py-build-cmake.sdist]
include = ["CMakeLists.txt"]

[tool.py-build-cmake.module]
generated = "package"

[tool.py-build-cmake.cmake]
minimum_version = "3.17"
[tool.py-build-cmake.cmake.options]
OPTION_A = "foo-bar"
OPTION_B = "foo;bar"
OPTION_C = ["foo", "bar"]
OPTION_D = false
OPTION_E = {"value" = "/usr/bin/bash", "type" = "FILEPATH"}
OPTION_F = "/usr/some/path"

[tool.py-build-cmake.linux.cmake.options]
OPTION_F = {"prepend" = "/some/linux/specific/path"}
[tool.py-build-cmake.windows.cmake.options]
OPTION_F = {"prepend" = "/some/windows/specific/path"}
[tool.py-build-cmake.mac.cmake.options]
OPTION_F = {"prepend" = "/some/macos/specific/path"}
2 changes: 2 additions & 0 deletions test-packages/local-options/tests/test_placeholder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def test_placeholder():
import local_options # noqa: F401
5 changes: 5 additions & 0 deletions tests/expected_contents/local-options/sdist.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
cmake_options-{{version}}/CMakeLists.txt
cmake_options-{{version}}/LICENSE
cmake_options-{{version}}/PKG-INFO
cmake_options-{{version}}/README.md
cmake_options-{{version}}/pyproject.toml
6 changes: 6 additions & 0 deletions tests/expected_contents/local-options/whl.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
cmake_options-{{version}}.dist-info/LICENSE
cmake_options-{{version}}.dist-info/METADATA
cmake_options-{{version}}.dist-info/RECORD
cmake_options-{{version}}.dist-info/WHEEL
cmake_options-{{version}}.dist-info/entry_points.txt
cmake_options/__init__.py

0 comments on commit d890f34

Please sign in to comment.