Skip to content

Commit d02bc0c

Browse files
committed
add installation support for pkg-config dependency detection
pkg-config is a buildsystem-agnostic alternative to `pybind11Config.cmake` that can be used from build systems other than cmake. Fixes #230
1 parent 59f03ee commit d02bc0c

11 files changed

+79
-3
lines changed

.pre-commit-config.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
#
1313
# See https://github.com/pre-commit/pre-commit
1414

15+
# third-party content
16+
exclude: ^tools/JoinPaths.cmake$
17+
1518
repos:
1619
# Standard hooks
1720
- repo: https://github.com/pre-commit/pre-commit-hooks

CMakeLists.txt

+13
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,9 @@ else()
198198
endif()
199199

200200
include("${CMAKE_CURRENT_SOURCE_DIR}/tools/pybind11Common.cmake")
201+
# https://github.com/jtojnar/cmake-snips/#concatenating-paths-when-building-pkg-config-files
202+
# TODO: cmake 3.20 adds the cmake_path() function, which obsoletes this snippet
203+
include("${CMAKE_CURRENT_SOURCE_DIR}/tools/JoinPaths.cmake")
201204

202205
# Relative directory setting
203206
if(USE_PYTHON_INCLUDE_DIR AND DEFINED Python_INCLUDE_DIRS)
@@ -262,6 +265,16 @@ if(PYBIND11_INSTALL)
262265
NAMESPACE "pybind11::"
263266
DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR})
264267

268+
# pkg-config support
269+
if(NOT prefix_for_pc_file)
270+
set(prefix_for_pc_file "${CMAKE_INSTALL_PREFIX}")
271+
endif()
272+
join_paths(includedir_for_pc_file "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}")
273+
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/tools/pybind11.pc.in"
274+
"${CMAKE_CURRENT_BINARY_DIR}/pybind11.pc" @ONLY)
275+
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/pybind11.pc"
276+
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig/")
277+
265278
# Uninstall target
266279
if(PYBIND11_MASTER_PROJECT)
267280
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake_uninstall.cmake.in"

pybind11/__init__.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@
66

77

88
from ._version import __version__, version_info
9-
from .commands import get_cmake_dir, get_include
9+
from .commands import get_cmake_dir, get_include, get_pkgconfig_dir
1010

1111
__all__ = (
1212
"version_info",
1313
"__version__",
1414
"get_include",
1515
"get_cmake_dir",
16+
"get_pkgconfig_dir",
1617
)

pybind11/__main__.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import sys
55
import sysconfig
66

7-
from .commands import get_cmake_dir, get_include
7+
from .commands import get_cmake_dir, get_include, get_pkgconfig_dir
88

99

1010
def print_includes() -> None:
@@ -36,13 +36,20 @@ def main() -> None:
3636
action="store_true",
3737
help="Print the CMake module directory, ideal for setting -Dpybind11_ROOT in CMake.",
3838
)
39+
parser.add_argument(
40+
"--pkgconfigdir",
41+
action="store_true",
42+
help="Print the pkgconfig directory, ideal for setting $PKG_CONFIG_PATH.",
43+
)
3944
args = parser.parse_args()
4045
if not sys.argv[1:]:
4146
parser.print_help()
4247
if args.includes:
4348
print_includes()
4449
if args.cmakedir:
4550
print(get_cmake_dir())
51+
if args.pkgconfigdir:
52+
print(get_pkgconfig_dir())
4653

4754

4855
if __name__ == "__main__":

pybind11/commands.py

+12
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,15 @@ def get_cmake_dir() -> str:
2323

2424
msg = "pybind11 not installed, installation required to access the CMake files"
2525
raise ImportError(msg)
26+
27+
28+
def get_pkgconfig_dir() -> str:
29+
"""
30+
Return the path to the pybind11 pkgconfig directory.
31+
"""
32+
pkgconfig_installed_path = os.path.join(DIR, "share", "pkgconfig")
33+
if os.path.exists(pkgconfig_installed_path):
34+
return pkgconfig_installed_path
35+
36+
msg = "pybind11 not installed, installation required to access the pkgconfig files"
37+
raise ImportError(msg)

setup.py

+1
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ def remove_output(*sources: str) -> Iterator[None]:
127127
"-DCMAKE_INSTALL_PREFIX=pybind11",
128128
"-DBUILD_TESTING=OFF",
129129
"-DPYBIND11_NOPYTHON=ON",
130+
"-Dprefix_for_pc_file=${pcfiledir}/../../",
130131
]
131132
if "CMAKE_ARGS" in os.environ:
132133
fcommand = [

tests/extra_python_package/test_files.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@
5959
"share/cmake/pybind11/pybind11Tools.cmake",
6060
}
6161

62+
pkgconfig_files = {
63+
"share/pkgconfig/pybind11.pc",
64+
}
65+
6266
py_files = {
6367
"__init__.py",
6468
"__main__.py",
@@ -69,7 +73,7 @@
6973
}
7074

7175
headers = main_headers | detail_headers | stl_headers
72-
src_files = headers | cmake_files
76+
src_files = headers | cmake_files | pkgconfig_files
7377
all_files = src_files | py_files
7478

7579

@@ -82,6 +86,7 @@
8286
"pybind11/share",
8387
"pybind11/share/cmake",
8488
"pybind11/share/cmake/pybind11",
89+
"pybind11/share/pkgconfig",
8590
"pyproject.toml",
8691
"setup.cfg",
8792
"setup.py",

tools/JoinPaths.cmake

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# This module provides function for joining paths
2+
# known from most languages
3+
#
4+
# SPDX-License-Identifier: (MIT OR CC0-1.0)
5+
# Copyright 2020 Jan Tojnar
6+
# https://github.com/jtojnar/cmake-snips
7+
#
8+
# Modelled after Python’s os.path.join
9+
# https://docs.python.org/3.7/library/os.path.html#os.path.join
10+
# Windows not supported
11+
function(join_paths joined_path first_path_segment)
12+
set(temp_path "${first_path_segment}")
13+
foreach(current_segment IN LISTS ARGN)
14+
if(NOT ("${current_segment}" STREQUAL ""))
15+
if(IS_ABSOLUTE "${current_segment}")
16+
set(temp_path "${current_segment}")
17+
else()
18+
set(temp_path "${temp_path}/${current_segment}")
19+
endif()
20+
endif()
21+
endforeach()
22+
set(${joined_path} "${temp_path}" PARENT_SCOPE)
23+
endfunction()

tools/pybind11.pc.in

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
prefix=@prefix_for_pc_file@
2+
includedir=@includedir_for_pc_file@
3+
4+
Name: @PROJECT_NAME@
5+
Description: Seamless operability between C++11 and Python
6+
Version: @PROJECT_VERSION@
7+
Cflags: -I${includedir}

tools/setup_global.py.in

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ main_headers = glob.glob("pybind11/include/pybind11/*.h")
2929
detail_headers = glob.glob("pybind11/include/pybind11/detail/*.h")
3030
stl_headers = glob.glob("pybind11/include/pybind11/stl/*.h")
3131
cmake_files = glob.glob("pybind11/share/cmake/pybind11/*.cmake")
32+
pkgconfig_files = glob.glob("pybind11/share/pkgconfig/*.pc")
3233
headers = main_headers + detail_headers + stl_headers
3334

3435
cmdclass = {"install_headers": InstallHeadersNested}
@@ -51,6 +52,7 @@ setup(
5152
headers=headers,
5253
data_files=[
5354
(base + "share/cmake/pybind11", cmake_files),
55+
(base + "share/pkgconfig", pkgconfig_files),
5456
(base + "include/pybind11", main_headers),
5557
(base + "include/pybind11/detail", detail_headers),
5658
(base + "include/pybind11/stl", stl_headers),

tools/setup_main.py.in

+2
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@ setup(
1717
"pybind11.include.pybind11.detail",
1818
"pybind11.include.pybind11.stl",
1919
"pybind11.share.cmake.pybind11",
20+
"pybind11.share.pkgconfig",
2021
],
2122
package_data={
2223
"pybind11": ["py.typed"],
2324
"pybind11.include.pybind11": ["*.h"],
2425
"pybind11.include.pybind11.detail": ["*.h"],
2526
"pybind11.include.pybind11.stl": ["*.h"],
2627
"pybind11.share.cmake.pybind11": ["*.cmake"],
28+
"pybind11.share.pkgconfig": ["*.pc"],
2729
},
2830
extras_require={
2931
"global": ["pybind11_global==$version"]

0 commit comments

Comments
 (0)