Skip to content

Commit 536fd38

Browse files
Merge pull request OpenAssetIO#66 from elliotcmorris/work/1088-dist-info
[Build] CMake install Python dist-info
2 parents 9eb732d + 1a13d84 commit 536fd38

File tree

11 files changed

+222
-2
lines changed

11 files changed

+222
-2
lines changed

.github/workflows/test.yml

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,35 @@ jobs:
9898
- name: Test
9999
run: |
100100
python -m pip install -r tests/python/requirements.txt
101-
PYTHONPATH=$(pwd)/build/dist/hybridpython python -m pytest -v
101+
PYTHONPATH=$(pwd)/build/dist/hybridpython python -m pytest -v
102+
103+
disallowed_pip_install:
104+
name: Disallowed pip install
105+
runs-on: ubuntu-latest
106+
container:
107+
image: aswf/ci-vfxall:2022-clang14.3
108+
109+
steps:
110+
- uses: actions/checkout@v3
111+
112+
- name: Install Traitgen
113+
run: python -m pip install openassetio-traitgen==1.0.0a7
114+
115+
- name: Set Python Root Dir
116+
run: echo "Python_ROOT_DIR=$(python -c 'import sys; print(sys.prefix)')" >> $GITHUB_ENV
117+
118+
- name: Configure CMake build
119+
run: >
120+
cmake -S . -B build --install-prefix $Python_ROOT_DIR -DOPENASSETIO_MEDIACREATION_GENERATE_PYTHON=ON
121+
122+
- name: Install package
123+
run: cmake --install build
124+
125+
- name: Attempt to install using pip
126+
# The runner has pipefail set, so if either the pip install
127+
# succeeds (inverted via `!`) or the grep fails, then the step
128+
# will fail.
129+
run: >
130+
! python -m pip install --upgrade --force-reinstall openassetio-mediacreation 2>&1
131+
| grep "The package was installed by cmake"
132+
shell: bash

CMakeLists.txt

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ if (OPENASSETIO_MEDIACREATION_GENERATE_PYTHON)
2222
""
2323
CACHE STRING
2424
"Override default Python module install directory, relative to CMAKE_INSTALL_PREFIX")
25+
26+
set(OPENASSETIO_MEDIACREATION_ENABLE_PYTHON_INSTALL_DIST_INFO_desc
27+
"Create a dist-info metadata directory alongside Python installation to provide"
28+
" discoverability and prevent overwrite by package managers such as pip")
29+
option(OPENASSETIO_MEDIACREATION_ENABLE_PYTHON_INSTALL_DIST_INFO
30+
"${OPENASSETIO_MEDIACREATION_ENABLE_PYTHON_INSTALL_DIST_INFO_desc}" ON)
2531
endif ()
2632

2733
message(STATUS "Test enabled = ${OPENASSETIO_MEDIACREATION_ENABLE_TEST}")
@@ -130,7 +136,7 @@ write_basic_package_version_file(${_version_config_file}
130136
COMPATIBILITY SameMajorVersion)
131137

132138
configure_package_config_file(
133-
cmake/Config.cmake.in
139+
cmake/packaging/Config.cmake.in
134140
${_project_config_file}
135141
INSTALL_DESTINATION ${_config_install_dir}
136142
)
@@ -152,6 +158,36 @@ if (OPENASSETIO_MEDIACREATION_GENERATE_PYTHON)
152158
DESTINATION "${OPENASSETIO_MEDIACREATION_PYTHON_SITEDIR}"
153159
FILES_MATCHING PATTERN "*.py"
154160
)
161+
162+
#-------------------------------------------------------------------
163+
# Install dist-info into the Python environment, to prevent
164+
# accidental overwrite, e.g. pip.
165+
166+
if (OPENASSETIO_MEDIACREATION_ENABLE_PYTHON_INSTALL_DIST_INFO)
167+
file(READ pyproject.toml _pyproject_toml)
168+
string(REGEX MATCH [[version *= *"([^"]+)"]] _unused "${_pyproject_toml}")
169+
set(OPENASSETIO_MEDIACREATION_PYTHON_PKG_VERSION ${CMAKE_MATCH_1})
170+
if (NOT OPENASSETIO_MEDIACREATION_PYTHON_PKG_VERSION)
171+
message(FATAL_ERROR "Failed to parse version from pyproject.toml")
172+
endif ()
173+
set(_dist_info_dir_name
174+
openassetio_mediacreation-${OPENASSETIO_MEDIACREATION_PYTHON_PKG_VERSION}.dist-info)
175+
file(
176+
COPY
177+
"${PROJECT_SOURCE_DIR}/cmake/packaging/python.dist-info/INSTALLER"
178+
"${PROJECT_SOURCE_DIR}/cmake/packaging/python.dist-info/REQUESTED"
179+
"${PROJECT_SOURCE_DIR}/cmake/packaging/python.dist-info/top_level.txt"
180+
DESTINATION "${_dist_info_dir_name}"
181+
)
182+
configure_file(
183+
"${PROJECT_SOURCE_DIR}/cmake/packaging/python.dist-info/METADATA.in"
184+
"${_dist_info_dir_name}/METADATA"
185+
)
186+
install(
187+
DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${_dist_info_dir_name}"
188+
DESTINATION "${OPENASSETIO_MEDIACREATION_PYTHON_SITEDIR}"
189+
)
190+
endif ()
155191
endif()
156192

157193
#-----------------------------------------------------------------------

RELEASE_NOTES.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,15 @@ v1.0.0-alpha.x
1010
compatibility with `openassetio` `v1.0.0a14`.
1111
[#60](https://github.com/OpenAssetIO/OpenAssetIO-MediaCreation/issues/60)
1212

13+
- Added some protection for accidental overwrites of a CMake installed
14+
`openassetio-mediacreation` Python package, by installing a
15+
`.dist-info` metadata directory alongside the package. `pip install`
16+
will then fail/warn against accidental overwrites/overrides. Added a
17+
CMake variable
18+
`OPENASSETIO_MEDIACREATION_ENABLE_PYTHON_INSTALL_DIST_INFO` to disable
19+
this feature.
20+
[#58](https://github.com/OpenAssetIO/OpenAssetIO-MediaCreation/issues/58)
21+
1322
v1.0.0-alpha.7
1423
--------------
1524

File renamed without changes.

cmake/packaging/README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Metadata files to be bundled with the installed package
2+
3+
This directory contains files used for package discovery in the install
4+
tree.
5+
6+
This includes the CMake config files, as well as a Python "dist-info"
7+
bundle.
8+
9+
The CMake config files are used to allow CMake's `find_package` to
10+
discover an installed OpenAssetIO-MediaCreation CMake package (see [CMake docs](https://cmake.org/cmake/help/latest/manual/cmake-packages.7.html)
11+
for more info).
12+
13+
The Python dist-info allows Python package managers (such as pip) to
14+
detect the presence of an `openassetio-mediacreation` package, and is
15+
configured such that a well-behaved package manager will error when
16+
trying to overwrite it. Specifically, the dist-info deliberately
17+
excludes a `RECORD` file (see [Python
18+
docs](https://packaging.python.org/en/latest/specifications/recording-installed-packages/#intentionally-preventing-changes-to-installed-packages)
19+
for more info).
20+
21+
Some of the config files are templates (as evidenced by the `.in`
22+
suffix), and will be rendered to their final form as part of a CMake
23+
build/install.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
cmake
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Metadata-Version: 2.1
2+
Name: openassetio-mediacreation
3+
Version: @OPENASSETIO_MEDIACREATION_PYTHON_PACKAGE_VERSION@

cmake/packaging/python.dist-info/REQUESTED

Whitespace-only changes.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
openassetio_mediacreation

tests/cpp/CMakeLists.txt

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,52 @@ OpenAssetIO::openassetio-core
2020
OpenAssetIO-MediaCreation::openassetio-mediacreation)
2121

2222
target_compile_features(test.cpp PRIVATE cxx_std_17)
23+
24+
#-----------------------------------------------------------------------
25+
# CMake Python packaging tests. (Dist-info)
26+
27+
if (OPENASSETIO_MEDIACREATION_ENABLE_PYTHON_INSTALL_DIST_INFO)
28+
29+
# Build the command to extend the PYTHONPATH such that the
30+
# site-packages directory in the install tree is included correctly.
31+
set(_set_pythonpath_command
32+
PYTHONPATH=${CMAKE_INSTALL_PREFIX}/${OPENASSETIO_MEDIACREATION_PYTHON_SITEDIR})
33+
34+
# Add pytest target to run the packaging tests. These are concerned
35+
# with python metadata information so run in a python context.
36+
add_custom_target(
37+
openassetio-mediacreation.tests.packaging
38+
COMMAND cmake -E echo -- "Running pytest check for CMake dist-info packaging"
39+
COMMAND ${_set_pythonpath_command} &&
40+
pytest -v --capture=tee-sys
41+
"${CMAKE_CURRENT_LIST_DIR}/test_cmake.py"
42+
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
43+
USES_TERMINAL
44+
)
45+
46+
# Create a test fixture that performs the install step.
47+
add_test(
48+
NAME openassetio-mediacreation.internal.tests.install
49+
COMMAND "${CMAKE_COMMAND}" --build "${PROJECT_BINARY_DIR}" --target install
50+
)
51+
set_tests_properties(openassetio-mediacreation.internal.tests.install
52+
PROPERTIES FIXTURES_SETUP test_install)
53+
54+
# Add the packaging test, and set the install as a required fixture.
55+
add_test(
56+
NAME openassetio-mediacreation.tests.packaging
57+
COMMAND ${CMAKE_COMMAND} --build "${PROJECT_BINARY_DIR}"
58+
--target openassetio-mediacreation.tests.packaging
59+
)
60+
set_tests_properties(openassetio-mediacreation.tests.packaging
61+
PROPERTIES FIXTURES_REQUIRED test_install)
62+
63+
# Set the project version as an environment variable accesible to
64+
# the packaging tests.
65+
set_tests_properties(
66+
openassetio-mediacreation.tests.packaging
67+
PROPERTIES
68+
ENVIRONMENT OPENASSETIO_MEDIACREATION_CMAKE_PACKAGE_VERSION=${PROJECT_VERSION}
69+
)
70+
71+
endif ()

0 commit comments

Comments
 (0)