Package: cmaketools
Setuptools extensions for CMake: Seamless integration of Cmake build system to setuptoolsThis Python package provides an extension to setuptools to integrate CMake into setuptools workflow. CMake build tool is tasked to build/install a complete Python distribution package with binary extensions and necessary data files. Then, setuptools follows to package up the bundled files for binary distribution (bdist_wheel/bdist_egg/etc.) or the CMake source directory for source distribution (sdist).Featuressetup() Wrapper:cmaketools.setup()wrapssetuptools.setup()to provide one-stopsetup()call for both CMake and setupstools.Source Distributable:cmaketoolslet you create apip-installable source distribution via Setuptools'sdistcommand. This enables the use oftoxto perform multi-environment testing.Automatic Source Content Detection: By taking advantage of the source directory structure imposed by CMake project,setup.py's critical keywords:package_dir,packages,package_data,ext_modules, andcmdclass.Source File Protection: Neither CMake nor Python setuptools will modify any content of the source directory under any command. It will not be cluttered by__pycache__or other runtime artifacts.Git Submodule Aware: If a project contains git submodules, the submodules will be automatically cloned duringpipinstallation, and the pinned commit of each submodule will be checked out before build.Support for CMake Command-Line Options: The most ofthe CMake command line optionsare made available as thebuild_extcommand options. For example,python setup.py build_ext -GNinjawill build the CMake project with Ninja build system.Integaration of Native Code Tests: CMake ships with a test driver program, calledctest. It could be called to run the CMake build tests from Python viacmaketools.cmakeutil.ctest().Usage ExamplesYou can experimentcmaketoolswith different Python/native interfaces availeble from following GitHub templates:CPython: https://github.com/python-cmaketools/cpython-examplePybind11: https://github.com/python-cmaketools/pybind-exampleBoost-Python: https://github.com/python-cmaketools/boost-python-examplePackage Authoring GuideSource Directory StructureThe structure of the source directory and placements ofCMakeLists.txtmust adhere to the requirements below forcmaketoolsto detect the package structure correctly. Here are some key tips in structuring the source directory:Source Directory(src) corresponds to the root package (orLib\site-packagesin Python directory). It could be named arbitrarily so long as it is assigned tosrc_dirattribute ofCMakeBuilder.Package DirectorySource subdirectories with__init__.pyfile are included inpackagessetupargument.Pure Python ModulesPlace all.pymodule files where they belong within the package structure.Binary Extension ModuleTo define a binary module, create a directory under a package directory it belongs to. In the example,src/mypkg/example_moduleis one such directory then we expectmypkg.example_modulebinary module.Make sure the directory name matches the module name defined in C/C++ source file.Each binary module directory should containCMakeLists.txtfile which defines the library target. For example, theCMakeLists.txtfile in module directory shall callpybind11_add_moduleto include apybind11-based module to the build project. This is a requirement for the auto-configuration ofext_modulessetupargument.Additional FilesAny "owned" additional data files needed to build the binary modules or to be used by the package shall be placed somewhere insrcas it is the directory packaged bysdist.3rd-Pary FilesIf downloadable or git-clonable, place them outside ofsrcto keepsdistpackage small. Script CMake to install them to their final in-package location to keep your package platform agnostic. This can be done viagit submodulesor CMakefile(DOWNLOAD <url> <file> ...)command, then build it if necessary and install the files relative toCMAKE_INSTALL_PREFIX. Only if they must be distributed insdistpackage, place them insidesrc.CMakeLists.txtAuthoring TipsFirst, to learn how to author CMake scripts, visitOfficial CMake Tutorial.The CMake integration relies on CMake's ability to traverse directory hierarchies, i.e., to encapsulate the build process of each directory via itsCMakeLists.txtscript and traverse directries. Some script snippets are repetitive and reusable as described below.Here are general tips:In general,CMakeLists.txtis expected in the source directory and its (sub)directories (possibly excluding resource/asset directories). ParentCMakeLists.txtmust calladd_subdirectory()for each of its subdirectories.Base Source Directoryshall define aSRC_DIRvariable byset(SRC_DIR${CMAKE_CURRENT_SOURCE_DIR})so relative paths of subdirectories can be evaluated later.Package Directorieswith pure Python modules must containinstall(FILES <file>...)command to copy all.pyfiles to the install target folder (typicallydist/<package_name>):file(RELATIVE_PATHDST_DIR${SRC_DIR}${CMAKE_CURRENT_SOURCE_DIR})file(GLOBPYFILESLIST_DIRECTORIESfalseRELATIVE${CMAKE_CURRENT_SOURCE_DIR}"*.py")install(FILES${PYFILES}DESTINATION${DST_DIR}COMPONENT"PY")NoteCOMPONENT "PY"designation ininstall. This letssetuptools'sbuild_pyto run CMake to install these files (andpackage_datafiles).External Module Directoriesrunsadd_library(<name> SHARED | MODULE ...)command either directly or indirectly. Here, it is imperative to setnameof the library target to match its directory name. Then the target is copied to the final destination withinstall(TARGETS <target>...)command.# match target name to folder nameget_filename_component(TARGET${CMAKE_CURRENT_SOURCE_DIR}NAME)# build commandsadd_library(${TARGET}...)# typically Python3_add_library or pybind11_add_moduleset_target_properties(${TARGET}PROPERTIESPREFIX"${PYTHON_MODULE_PREFIX}")set_target_properties(${TARGET}PROPERTIESSUFFIX"${PYTHON_MODULE_EXTENSION}")# ... more build commands to follow# install commandsget_filename_component(CURRENT_SRC_DIR${CMAKE_CURRENT_SOURCE_DIR}DIRECTORY)if(${SRC_DIR}STREQUAL${CURRENT_SRC_DIR})set(DST_DIR".")# if parent is the base source folderelse()file(RELATIVE_PATHDST_DIR${SRC_DIR}${CURRENT_SRC_DIR})endif()install(TARGETS${TARGET}DESTINATION${DST_DIR}COMPONENT"EXT")Here we register the install asEXTcomponent sobuild_extwill only copy external modules to their final locations.Own Package Data Filesare handled in a similar fashion as the pure Python modules withinstall(FILES <file>...)command asPYcomponent.# to install a package data file 'data.txt'file(RELATIVE_PATHDST_DIR${SRC_DIR}${CMAKE_CURRENT_SOURCE_DIR})install(FILES"data.txt"DESTINATION${DST_DIR}COMPONENT"PY")3rd-Party Package Data Filesare a bit trickier. The most intuitive way perhaps is to call theinstallcommand from the source folder, which matches the folder where the 3rd-party file is placed in the package. For example, suppose this skeletal directory model:# After 'cmake --install build'project-root/
├──build/|└──lib/|└──3rd-party-tool/|└──libtool.dll# <=original├──dist/|└──mypkg/|└──lib/|└──libtool.dll# <=distro-ready file (the install destination)├──lib/|└──3rd-party-tool/# lib source files in here to be built└──src/└──mypkg/└──lib/└──CMakeLists.txt# <=issue install command in this fileThe source files of a 3rd-party library is included to the project via git submodule inlib/3rd-party-tool/and when built let's assume its DLL (assuming Windows) file will be found atbuild/lib/3rd-party-tool/libtool.dll. We want this DLL file to be placed inlibfolder of the Python package, which means CMake must install (copy)libtool.dlltodist/mypkg/lib/libtool.dll. The install command shall be issued bysrc/mypkg/lib/CMakeLists.txteven ifsrc/mypkg/lib/would otherwise be empty.# to install a package data fileSET(DLL_NAME"libtool.dll")SET(DLL_PATH"${CMAKE_BINARY_DIR}/lib/3rd-party-tool/${DLL_NAME}")file(RELATIVE_PATHDST_DIR${SRC_DIR}${CMAKE_CURRENT_SOURCE_DIR})install(FILES${DLL_PATH}DESTINATION${DST_DIR}COMPONENT"PY")Note: Typically you can construct CMake variable via libarary's CMake variables rather than hard-coding theDLL_PATHas done above.Project RootCMakeLists.txtdefines general configurations (such as finding dependent libraries and setting up tests) of the build project. There are a couple things could be configured here to improve the CMake/Setuptools co-operation.Set default install path to bedistso CMake by default installs to the samedistdirectory location as setuptools:if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)set(CMAKE_INSTALL_PREFIX"${CMAKE_SOURCE_DIR}/dist"CACHEPATH"default install path"FORCE)endif()setup()Argumentscmaketools.setup()call wrapssetuptools.setup()so to initializeCMakeBuilderand auto-generatesetuptools.setup()arguments. As such, it accepts most of setuptools.setup() arguments with additional arguments to configure CMake build. Also, it may overwrite user-provided setuptools arguments in order to integrate CMake.List of New Arguments for CMakeKeywordTypeDescriptioncmake_pathstrpath to cmake command (default auto-detected)src_dirstrSource directory (default "src")ext_module_dirsstr[]List of source directories defining external modulesext_module_hintstrRegex pattern to auto-detect external module directoriestest_dirstrUnit test directory (default "tests")test_submodulesstr[]List of git submodules only used for testinghas_package_databoolSet False if project has no package_data (default True)skip_configureboolSet True to configure cmake externally (default False)configstrDefault CMake build type (default "Release")generatorstrDefault CMake--GargumentplatformstrDefault CMake--platformargumenttoolsetstrDefault CMake--toolsetargumentparallelint > 0Default CMake--parallelargumentconfigure_optsstr[]List of other default option arguments for CMake configurebuild_optsstr[]List of other default option arguments for CMake buildinstall_optsstr[]List of other default option arguments for CMake installOverriden setuptools argumentscmdclass(partial override, overridesegg_info,build_py,build_ext,sdist, andinstall_datacommands)data_filesext_modulespackage_dirpackage_datapackagesbuild_extCommand Options forcmaketools-basedsetup.pyThebuild_extcommand options are completely changed to accomodate CMake command-line options. Here is the output ofpython setup.py --help build_extCommoncommands:(see'--help-commands'formore)setup.pybuildwillbuildthepackageunderneath'build/'setup.pyinstallwillinstallthepackage

Globaloptions:
--verbose(-v)runverbosely(default)--quiet(-q)runquietly(turnsverbosityoff)--dry-run(-n)don't actually do anything--help (-h) show detailed help message--no-user-cfg ignore pydistutils.cfg in your home directoryOptions for 'build_ext' command:--cmake-path Name/path of the CMake executable to use, overridingdefault auto-detection.--build-lib (-b) directory for compiled extension modules--inplace (-i) ignore build-lib and put compiled extensions into thesource directory alongside your pure Python modules--force (-f) forcibly build everything (delete existingCMakeCache.txt)--cache (-C) Pre-load a CMake script to populate the cache.--define (-D) Create or update a CMake CACHE entry (separated by';')--undef(-U)RemovematchingentriesfromCMakeCACHE.
--generator(-G)Specifyabuildsystemgenerator.
--toolset(-T)Toolsetspecificationforthegenerator,ifsupported.
--platform(-A)Specifyplatformnameifsupportedbygenerator.
--Wno-devSuppressdeveloperwarnings.
--WdevEnabledeveloperwarnings.
--WerrorMakespecifiedwarningsintoerrors:devor
deprecated.
--Wno-errorMakespecifiedwarningsnoterrors.
--WdeprecatedEnabledeprecatedfunctionalitywarnings.
--Wno-deprecatedSuppressdeprecatedfunctionalitywarnings.
--log-levelSetthelogleveltooneof:ERROR,WARNING,NOTICE,
STATUS,VERBOSE,DEBUG,TRACE
--log-contextEnablethemessage()commandoutputtingcontext
attachedtoeachmessage.
--debug-trycompileDonotdeletethetry_compile()buildtree.Only
usefulononetry_compile()atatime.
--debug-outputPutcmakeinadebugmode.
--debug-findPutcmakefindcommandsinadebugmode.
--tracePutcmakeintracemode.
--trace-expandPutcmakeintracemodewithvariablesexpanded.
--trace-formatPutcmakeintracemodeandsetsthetraceoutput
format.
--trace-sourcePutcmakeintracemode,butoutputonlylinesofa
specifiedfile.
--trace-redirectPutcmakeintracemodeandredirecttraceoutputtoa
fileinsteadofstderr.
--warn-uninitializedSpecifyabuildsystemgenerator.
--warn-unused-varsWarnaboutunusedvariables.
--no-warn-unused-cliDon’twarnaboutcommandlineoptions.
--check-system-varsFindproblemswithvariableusageinsystemfiles.
--parallel(-j)Themaximumnumberofconcurrentprocessestousewhen
building.
--configFormulti-configurationtools,choosethis
configuration.
--clean-firstBuildtargetcleanfirst,thenbuild.
--verbose(-v)Enableverboseoutput-ifsupported-includingthe
buildcommandstobeexecuted.
--stripStripbeforeinstalling.
--help-generatorlistavailablecompilers

usage:setup.py[global_opts]cmd1[cmd1_opts][cmd2[cmd2_opts]...]or:setup.py--help[cmd1cmd2...]or:setup.py--help-commands
or:setup.pycmd--help