Skip to content

Commit 957433b

Browse files
authored
Merge pull request #14 from kunaltyagi/conan.integrate
Add minimal files required for a nice interface and conan integration
2 parents 410503c + 821a662 commit 957433b

4 files changed

Lines changed: 835 additions & 0 deletions

File tree

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
cmake_minimum_required(VERSION 3.10)
2+
project (${project.name}_pybind11)
3+
4+
% if not cmake.no_conan:
5+
include (conan.cmake)
6+
7+
conan_cmake_run (CONANFILE conanfile.txt
8+
BASIC_SETUP
9+
CMAKE_TARGETS
10+
BUILD missing
11+
% if conan.keep_rpaths:
12+
KEEP_RPATHS
13+
% endif
14+
% if conan.no_output_dirs:
15+
NO_OUTPUT_DIRS
16+
% endif
17+
% if conan.arch:
18+
ARCH ${conan.arch}
19+
% endif
20+
% if conan.build_type:
21+
BUILD_TYPE ${conan.build_type}
22+
% endif
23+
% if conan.profile:
24+
PROFILE ${conan.profile}
25+
% endif
26+
% if conan.profile_auto:
27+
PROFILE_AUTO ${conan.profile_auto}
28+
% endif
29+
)
30+
% endif
31+
32+
# `find_package` can be replaced with `add_subdirectory` by a power user
33+
# https://pybind11.readthedocs.io/en/stable/compiling.html#find-package-vs-add-subdirectory
34+
find_package(pybind11 REQUIRED)
35+
36+
set (CMAKE_CXX_STANDARD ${cmake.cpp_standard})
37+
38+
pybind11_add_module (${project.name}
39+
% if pybind11.lib_type:
40+
# for MODULE or SHARED
41+
${pybind11.lib_type}
42+
% endif
43+
% if pybind11.thin_lto:
44+
THIN_LTO
45+
% endif
46+
% if pybind11.optimise_for_size:
47+
OPT_SIZE
48+
% endif
49+
% for file in files:
50+
file
51+
% endfor
52+
)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[requires]
2+
pybind11[>2.2.2]
3+
4+
[generators]
5+
cmake
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
#! /usr/bin/env python
2+
3+
import argparse
4+
from functools import lru_cache
5+
import os
6+
7+
from mako.template import Template
8+
9+
10+
def get_args(parser):
11+
args, _ = parser.parse_known_args()
12+
return args
13+
14+
15+
def list_by_type(directory):
16+
from os.path import isfile, isdir, islink, join
17+
18+
all_files = os.listdir(directory)
19+
categorized = {
20+
"file": [f for f in all_files if isfile(join(directory, f))],
21+
"directory": [d for d in all_files if isdir(join(directory, d))],
22+
"link": [l for l in all_files if islink(join(directory, l))],
23+
}
24+
return categorized
25+
26+
27+
@lru_cache(maxsize=2)
28+
def guess_build_dir(guess=os.getcwd()):
29+
ls = list_by_type(guess)
30+
if "compile_commands.json" in ls["file"]:
31+
return guess
32+
second_guess = os.path.join(guess, "build")
33+
if second_guess in ls["directory"]:
34+
return second_guess
35+
return None
36+
37+
38+
def guess_project_dir():
39+
guess = os.getcwd()
40+
return guess
41+
42+
43+
class CategorizedArgs:
44+
def __init__(self):
45+
self._internal_categories = ["cmake", "conan", "project", "pybind11"]
46+
self._parse_group = {}
47+
self._parser = None
48+
self._generate()
49+
self.args = self._get_args()
50+
self.categorized_args = {
51+
category: self._get_args(category) for category in self._internal_categories
52+
}
53+
54+
def _cmake(self, parser=argparse.ArgumentParser()):
55+
parser.add_argument(
56+
"--cpp-standard",
57+
required=False,
58+
default="14",
59+
choices=["03", "11", "14", "17", "20"],
60+
help="C++ Standard used by your project",
61+
)
62+
parser.add_argument(
63+
"--no-conan",
64+
default=False,
65+
action="store_true",
66+
help="Use CMake's own package resolution instead of relying on conan",
67+
)
68+
return parser
69+
70+
def _conan(self, parser=argparse.ArgumentParser()):
71+
parser.add_argument("--keep-rpaths", required=False, action="store_true")
72+
parser.add_argument("--no-output-dirs", required=False, action="store_true")
73+
parser.add_argument("--arch", required=False)
74+
parser.add_argument("--build-type", required=False)
75+
parser.add_argument("--profile", required=False)
76+
parser.add_argument("--profile-auto", required=False)
77+
return parser
78+
79+
def _pybind11(self, parser=argparse.ArgumentParser()):
80+
parser.add_argument("--lib-type", required=False)
81+
parser.add_argument("--thin-lto", required=False, action="store_true")
82+
parser.add_argument("--optimise-for-size", required=False, action="store_true")
83+
return parser
84+
85+
def _project(self, parser=argparse.ArgumentParser()):
86+
parser.add_argument("--name", required=True)
87+
return parser
88+
89+
def _generate(self):
90+
parser = argparse.ArgumentParser(
91+
description="Generate bindings from C++",
92+
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
93+
)
94+
parser.add_argument(
95+
"--compile-commands",
96+
default=guess_build_dir(),
97+
required=guess_build_dir() is None,
98+
help="Path to compile_commands.json file or the directory containing it",
99+
)
100+
parser.add_argument(
101+
"--project-root",
102+
default=guess_project_dir(),
103+
help="Path to the project root to reduce name length of generated files",
104+
)
105+
parser.add_argument(
106+
"--out-dir",
107+
default=guess_build_dir(),
108+
required=guess_build_dir() is None,
109+
help="Output directory for intermediate json files",
110+
)
111+
parser.add_argument(
112+
"--select-files",
113+
nargs="*",
114+
metavar=("CHOSEN_FILE", "OTHER_CHOSEN_FILES"),
115+
default=None,
116+
help="Select only certain files from the compile database",
117+
)
118+
parser.add_argument(
119+
"--ignore-files",
120+
nargs="*",
121+
metavar=("IGNORED_FILE", "OTHER_IGNORED_FILES"),
122+
default=None,
123+
help="Ignore certain files from the compile database",
124+
)
125+
parser.add_argument(
126+
"--language",
127+
default="py",
128+
choices=["py"],
129+
help="Target language for the bindings",
130+
)
131+
parser.add_argument(
132+
"--use-latest-conan-cmake",
133+
default=False,
134+
action="store_true",
135+
help="Use the latest release of conan-cmake instead of the bundled release (0.15.0)",
136+
)
137+
138+
for category in self._internal_categories:
139+
gp = parser.add_argument_group(title=f"arguments for {category}")
140+
getattr(self, f"_{category}")(gp)
141+
# also create categorized parser
142+
self._parse_group[category] = getattr(self, f"_{category}")()
143+
self._parser = parser
144+
return self._parser
145+
146+
def _get_args(self, category=None):
147+
if category is None:
148+
args = get_args(self._parser)
149+
root, last_item = args.compile_commands.rsplit(os.path.sep, 1)
150+
if (last_item == "compile_commands.json") and os.path.isfile(
151+
args.compile_commands
152+
):
153+
args.compile_commands = root
154+
return args
155+
elif category in self._internal_categories:
156+
return get_args(self._parse_group[category])
157+
158+
159+
def main():
160+
cmake = Template(filename="CMakeLists.txt.in")
161+
cat = CategorizedArgs()
162+
all_args = cat.categorized_args
163+
data = cmake.render(files=[], **(all_args))
164+
print(data)
165+
166+
167+
if __name__ == "__main__":
168+
main()

0 commit comments

Comments
 (0)