How to use cpp_extension with an existing library built on top of libtorch?

username: Wesley_Neill
cooked: <p>I’ve successfully completed the <a href="https://pytorch.org/tutorials/advanced/cpp_extension.html" rel="noopener nofollow ugc">c++/CUDA extension tutorial</a>. I wrote and bound a different utility than what they used.</p>
<p>Then, I wrote and <a href="https://pytorch.org/cppdocs/installing.html" rel="noopener nofollow ugc">compiled a CMake project to create a static library</a> out of the same utility I used in the extensions tutorial.</p>
<p>Next, I want to know how I can take the library (.lib) I built and bind it to Python. I want to sort of blend the two tutorials I linked above.</p>
<h3>
<a name="what-i-tried-1" class="anchor" href="#what-i-tried-1"></a>What I tried:</h3>
<p>In my original project, where the library exists, I added a new directory called <code>pybind</code>:</p>
<pre><code class="lang-auto">├───external
│   └───libtorch
│       ├───bin
│       ├───cmake
│       ├───include
│
├───libray
│   │   CMakeLists.txt
│   │
│   ├───include    *** Public API of libray ***
│   │       CMakeLists.txt
│   │       raycast.cpp
│   │       raycast.h
│   │
│   └───src        *** Helpers, PRIVATE in cmake ***
│           raycast_cuda.cu
│           raycast_cuda.cuh
│           torch_checks.cpp
│           torch_checks.h
│
├───pybind
            extension.cpp
            setup.py
</code></pre>
<p>Inside that directory, I added the <code>extension.cpp</code> file that would include the header for the utilities I want to bind, and then bind the functions contained by those headers:</p>
<pre><code class="lang-auto">#include &lt;torch/extension.h&gt;
#include "raycast.h"

PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
  m.def("find_distance", &amp;find_distances, "Get minimum distance from ray origin to surface.");
  m.def("find_intersections", &amp;find_intersections, "Get ray's intersection with surface.");
}
</code></pre>
<p>along with the <code>setup.py</code> described in the first link in my question:</p>
<pre><code class="lang-auto">from setuptools import setup
from torch.utils import cpp_extension

setup(name='raycast',
      ext_modules=
      [
          Extension(
              name="raycast", 
              sources=['extension.cpp'],
              include_dirs=[
                  '../external/libtorch/include', 
                  "../external/libtorch/include/torch/csrc/api/include",
                  "../libray/include"],
              library_dirs=[
                  "../build/libray/include/Debug/", 
                  "../external/libtorch/lib"],
              libraries=["libray", "torch", "torch_cpu", "torch_cuda", "c10"]
      ],
      cmdclass={'build_ext': cpp_extension.BuildExtension})
</code></pre>
<p><strong>Note:</strong> I also added the private header files to the <code>ext_modules</code> list. This defeats the purpose of the question and also didn’t work.</p>
<h3>
<a name="what-happens-2" class="anchor" href="#what-happens-2"></a>What happens</h3>
<ol>
<li>When I run <code>python setup.py install</code>, I get an error saying there are 17 unresolved externals.</li>
</ol>
<pre><code class="lang-auto">extension.obj : error LNK2001: unresolved external symbol "class at::Tensor __cdecl find_distances(class at::Tensor,class at::Tensor,class at::Tensor,class at::Tensor)" (?find_distances@@YA?AVTensor@at@@V12@000@Z)
extension.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: __cdecl pybind11::detail::type_caster&lt;class at::Tensor,void&gt;::type_caster&lt;class at::Tensor,void&gt;(void)" (__imp_??0?$type_caster@VTensor@at@@X@detail@pybind11@@QEAA@XZ)
extension.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: __cdecl pybind11::detail::type_caster&lt;class at::Tensor,void&gt;::~type_caster&lt;class at::Tensor,void&gt;(void)" (__imp_??1?$type_caster@VTensor@at@@X@detail@pybind11@@QEAA@XZ)
extension.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: __cdecl pybind11::detail::type_caster&lt;class at::Tensor,void&gt;::operator class at::Tensor &amp;&amp;(void)&amp;&amp; " (__imp_??B?$type_caster@VTensor@at@@X@detail@pybind11@@QEHAA$$QEAVTensor@at@@XZ)
libray.lib(raycast.obj) : error LNK2001: unresolved external symbol __imp__invalid_parameter
libray.lib(raycast_cuda.obj) : error LNK2001: unresolved external symbol __imp__invalid_parameter
libray.lib(torch_checks.obj) : error LNK2001: unresolved external symbol __imp__invalid_parameter
libray.lib(raycast.obj) : error LNK2001: unresolved external symbol __imp__calloc_dbg
libray.lib(raycast_cuda.obj) : error LNK2001: unresolved external symbol __imp__calloc_dbg
libray.lib(torch_checks.obj) : error LNK2001: unresolved external symbol __imp__calloc_dbg
libray.lib(raycast.obj) : error LNK2001: unresolved external symbol __imp__CrtDbgReport
libray.lib(raycast_cuda.obj) : error LNK2001: unresolved external symbol __imp__CrtDbgReport
libray.lib(torch_checks.obj) : error LNK2001: unresolved external symbol __imp__CrtDbgReport
libray.lib(raycast_cuda.obj) : error LNK2001: unresolved external symbol cudaLaunchKernel
libray.lib(raycast_cuda.obj) : error LNK2001: unresolved external symbol __cudaPushCallConfiguration
libray.lib(raycast_cuda.obj) : error LNK2001: unresolved external symbol __cudaPopCallConfiguration
libray.lib(raycast_cuda.obj) : error LNK2001: unresolved external symbol __cudaRegisterFatBinary
libray.lib(raycast_cuda.obj) : error LNK2001: unresolved external symbol __cudaRegisterFatBinaryEnd
libray.lib(raycast_cuda.obj) : error LNK2001: unresolved external symbol __cudaUnregisterFatBinary
libray.lib(raycast_cuda.obj) : error LNK2001: unresolved external symbol __cudaRegisterVar
libray.lib(raycast_cuda.obj) : error LNK2001: unresolved external symbol __cudaRegisterFunction
build\lib.win-amd64-3.8\raycast.cp38-win_amd64.pyd : fatal error LNK1120: 17 unresolved externals
</code></pre>
<ol start="2">
<li>I get these errors as well, but many less than the other one.</li>
</ol>
<pre><code class="lang-auto">libray.lib(raycast.obj) : error LNK2038: mismatch detected for '_ITERATOR_DEBUG_LEVEL': value '2' doesn't match value '0' in extension.obj
libray.lib(raycast.obj) : error LNK2038: mismatch detected for 'RuntimeLibrary': value 'MDd_DynamicDebug' doesn't match value 'MD_DynamicRelease' in extension.obj
libray.lib(raycast_cuda.obj) : error LNK2038: mismatch detected for '_ITERATOR_DEBUG_LEVEL': value '2' doesn't match value '0' in extension.obj
libray.lib(raycast_cuda.obj) : error LNK2038: mismatch detected for 'RuntimeLibrary': value 'MDd_DynamicDebug' doesn't match value 'MD_DynamicRelease' in extension.obj
libray.lib(torch_checks.obj) : error LNK2038: mismatch detected for '_ITERATOR_DEBUG_LEVEL': value '2' doesn't match value '0' in extension.obj
libray.lib(torch_checks.obj) : error LNK2038: mismatch detected for 'RuntimeLibrary': value 'MDd_DynamicDebug' doesn't match value 'MD_DynamicRelease' in extension.obj
   Creating library build\temp.win-amd64-3.8\Release\raycast.cp38-win_amd64.lib and object build\temp.win-amd64-3.8\Release\raycast.cp38-win_amd64.exp
LINK : warning LNK4098: defaultlib 'MSVCRTD' conflicts with use of other libs; use /NODEFAULTLIB:library
</code></pre>
<p>I have looked up the meaning of LNK2038 and LNK2001 errors. I was able to get down from 158 to 17 LNK2001 errors by including more <code>.lib</code> files from <code>libtorch/lib/</code>. However, I still appear to be missing something.</p>
<h3>
<a name="my-questions-3" class="anchor" href="#my-questions-3"></a>My questions</h3>
<p>What am I missing, whether it be including paths or extra libraries to resolve these issues?</p>

username: Wesley_Neill
cooked: <p>In order to solve this problem, I ended up taking a look at the contents of the<br>
<code>setuptools.Extension</code> object created by calling <code>cpp_extension.CUDAExtension</code>.</p>
<pre><code class="lang-auto">from setuptools import setup, Extension
from torch.utils import cpp_extension

cuda_extension = cpp_extension.CUDAExtension('raycast', ['raycast.cpp', 'raycast_cuda.cu', 'torch_checks.cpp'])

for attribute in dir(cuda_extension):
    attr = getattr(cuda_extension, attribute)
    print("*********************************")
    print(attribute)
    print(attr)
    print("*********************************")
</code></pre>
<p>This ended up yielding the libraries and their locations that I was missing.</p>
<pre><code class="lang-auto">*********************************
include_dirs
['C:\\Users\\wesle\\anaconda3\\lib\\site-packages\\torch\\include', 'C:\\Users\\wesle\\anaconda3\\lib\\site-packages\\torch\\include\\torch\\csrc\\api\\include', 'C:\\Users\\wesle\\anaconda3\\lib\\site-packages\\torch\\include\\TH', 'C:\\Users\\wesle\\anaconda3\\lib\\site-packages\\torch\\include\\THC', 'C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v11.6\\include']
*********************************

*********************************
libraries
['c10', 'torch', 'torch_cpu', 'torch_python', 'cudart', 'c10_cuda', 'torch_cuda_cu', 'torch_cuda_cpp']
*********************************

*********************************
library_dirs
['C:\\Users\\wesle\\anaconda3\\lib\\site-packages\\torch\\lib', 'C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v11.6\\lib/x64'] 
*********************************
</code></pre>
<p>Strangely this still failed, as the library <code>torch_python.lib</code> could not be found. So, I searched for it in my conda env and added its location to the <code>library_dirs</code> list. And it worked!</p>
<p>There may be some redundancies in the search paths, as I included <code>libtorch</code> directories (stored in my project) and <code>torch</code> directories (from my conda env pytorch install). However, here is the final working <code>setup.py</code>:</p>
<pre><code class="lang-auto">from setuptools import setup, Extension
from torch.utils import cpp_extension

setup(name='raycast',
      ext_modules=
      [
          Extension(
              name="raycast", 
              sources=['extension.cpp'],
              
              include_dirs= 
                  cpp_extension.include_paths() + 
                  [
                        '../external/libtorch/include', 
                        "../external/libtorch/include/torch/csrc/api/include",
                        "../libray/include",
                        'C:\\Users\\wesle\\anaconda3\\lib\\site-packages\\torch\\include',
                        'C:\\Users\\wesle\\anaconda3\\lib\\site-packages\\torch\\include\\torch\\csrc\\api\\include', 
                        'C:\\Users\\wesle\\anaconda3\\lib\\site-packages\\torch\\include\\TH',
                        'C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v11.6\\include'
                  ],

              library_dirs=[
                  "../build/libray/include/Release/", 
                  "../external/libtorch/lib",
                  "C:\\Users\\wesle\\anaconda3\\Lib\\site-packages\\torch\\lib",
                  'C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v11.6\\lib/x64'],

              libraries=["libray", 'c10', 'torch', 'torch_cpu', 
                         'torch_python', 'cudart', 'c10_cuda', 
                         'torch_cuda_cu', 'torch_cuda_cpp']) 
      ],
      cmdclass={'build_ext': cpp_extension.BuildExtension})

</code></pre>