https://wiki.gnuradio.org/api.php?action=feedcontributions&user=Tom+N5EG&feedformat=atomGNU Radio - User contributions [en]2024-03-29T12:29:03ZUser contributionsMediaWiki 1.39.5https://wiki.gnuradio.org/index.php?title=InstallingGR&diff=10312InstallingGR2022-01-16T14:38:45Z<p>Tom N5EG: </p>
<hr />
<div>= From Binaries =<br />
<br />
The recommended way to install GNU Radio on most platforms is using already available binary packages (see [[#Ubuntu_PPA_Installation|Ubuntu PPA Installation]]). For some platforms there are no binaries provided by available package managers or the GNU Radio project. In these cases please contact the maintainer of the package manager or the GNU Radio project to find a sensible way to provide binaries for your platform.<br />
<br />
In addition to using binaries, GNU Radio can be installed:<br />
<br />
# '''[[#From_Source|From source]]''' (for those who want full control)<br />
# '''[[#Using_PyBOMBS|Using PyBOMBS]]''' (for those who want it built from source and/or installed to a specific directory using a script)<br />
# '''[[CondaInstall|Using conda]]''' (for those who want binaries installed to managed environments)<br />
<br />
== Linux ==<br />
<br />
Most distributions contain a package named <code>gnuradio</code> or similar in their standard repositories. For most use cases it is enough to install this package and start developing.<br />
<br />
The development of GNU Radio can be fast-paced, and binaries provided by your distribution may be outdated. '''Do check if the version you're installing is up to date! Sometimes old versions are not updated in the packaging systems.''' If you find a bug in a older GNU Radio version, please check if the bug still exists in the newer version of GNU Radio before filing a new issue.<br />
<br />
If the version shipped in your distribution is outdated please contact the corresponding maintainer to update it in the packaging system.<br />
<br />
Here are examples of how to install GNU Radio in various Linux distributions. <b>Click the link under &quot;Distribution&quot; for how to install dependencies.</b><br />
<br />
{|class="wikitable" style="margin: auto; width: 90%;"<br />
!scope="col"|Distribution<br />
!scope="col"|Command<br />
|-<br />
| [[UbuntuInstall|Debian/Ubuntu and derivates]]<br />
| <pre># apt install gnuradio</pre><br />
|-<br />
| [[FedoraInstall|Fedora]]<br />
| <pre># dnf install gnuradio</pre><br />
|-<br />
| RHEL/CentOS<br />
| <pre># yum install gnuradio</pre><br />
|-<br />
| [[ArchInstall|Archlinux]]<br />
| <pre># pacman -S gnuradio</pre><br />
|-<br />
| [[GentooInstall|Gentoo Linux]]<br />
| <pre># emerge net-wireless/gnuradio</pre><br />
|-<br />
| [[SuseInstall|Suse Linux]]<br />
| <br />
|-<br />
|}<br />
<br />
On other distributions, simply use the appropriate package management command to install the <code>gnuradio</code> package and then please add it to this list. If you need newer versions or have a different platform please contact the package maintainer of your distribution or raise your issue on the mailing list.<br />
<br />
=== Ubuntu PPA Installation ===<br />
For Ubuntu, the latest builds (both released and pulled from master branch) are maintained as PPAs on [https://launchpad.net/~gnuradio launchpad]. Be sure to uninstall any previously installed versions of gnuradio first.<br />
<br />
To access the current released version (3.10), add the gnuradio/gnuradio-releases ppa (removing other gnuradio ppas if already configured)<br />
<br />
<pre>$ sudo add-apt-repository ppa:gnuradio/gnuradio-releases</pre><br />
<br />
To access the 3.9 released version, add the gnuradio/gnuradio-releases-3.9 ppa (removing other gnuradio ppas if already configured)<br />
<br />
<pre>$ sudo add-apt-repository ppa:gnuradio/gnuradio-releases-3.9</pre><br />
<br />
To access the 3.8 released version, add the gnuradio/gnuradio-releases-3.8 ppa (removing other gnuradio ppas if already configured)<br />
<br />
<pre>$ sudo add-apt-repository ppa:gnuradio/gnuradio-releases-3.8</pre><br />
<br />
To access the 3.7 released version (legacy), add the gnuradio/gnuradio-releases-3.7 ppa (removing other gnuradio ppas if already configured)<br />
<br />
<pre>$ sudo add-apt-repository ppa:gnuradio/gnuradio-releases-3.7</pre><br />
<br />
Then, update the apt sources, and install gnuradio<br />
<pre>$ sudo apt-get update</pre><br />
<pre>$ sudo apt install gnuradio</pre><br />
<br />
'''Attention:''' Do '''NOT''' try to install further packages like `gr-osmosdr` via Ubuntu's package management (i.e. using "apt"). Ubuntu will try to install a potentially incompatible version and your system will be in an undefined state.<br />
<br />
<br />
'''Note on 3.10 packaging dependency'''<br />
For some distributions you may need to install the python module 'packaging' using pip (which may also need to be installed).<br />
<br />
$ sudo apt install python3-pip<br />
$ pip install packaging<br />
<br />
==== Modtool on Ubuntu ====<br />
NOTE: On released builds for Ubuntu 18 (bionic), there is an issue using gr_modtool after GNU Radio has been installed from the PPA. This is due to byte-compiled code that remains in the modtool templates after installation. To workaround this issue:<br />
<br />
<pre>$ cd /usr/share/gnuradio/modtool/templates/gr-newmod<br />
$ sudo py3clean .</pre><br />
<br />
This issue does not appear for Ubuntu 19 and later packages<br />
<br />
=== Fedora COPR Installation ===<br />
<br />
**NOTE: FEDORA COPR not currently maintained**<br />
<br />
Packages are available for Fedora 29,30,31 hosted under COPR:<br />
<br />
https://copr.fedorainfracloud.org/coprs/gnuradio/<br />
<br />
1. Add the repository:<br />
<br />
-- For the latest released version:<br />
<pre>$ sudo dnf copr enable gnuradio/gnuradio-releases </pre><br />
-- For the latest pull from git master:<br />
<pre>$ sudo dnf copr enable gnuradio/gnuradio-master </pre><br />
<br />
2. Install GNU Radio<br />
<pre>$ sudo dnf install gnuradio </pre><br />
<br />
=== Raspberry Pi ===<br />
<br />
There is a pre-built 32 bit SDR flavored Raspberry Pi OS distro, [https://github.com/luigifcruz/pisdr-image PiSDR] that includes GnuRadio and other SDR utilities. There are also instructions for [[InstallingGRFromSource_on_Raspberry_Pi]].<br />
<br />
== Windows ==<br />
<br />
Binary installers are now available for GNU Radio 3.7 and 3.8, download them [http://www.gcndevelopment.com/gnuradio/index.htm here].<br /><br />
Conda packages are an alternative for installing binaries for GNU Radio 3.8+. See the [[CondaInstall|conda install guide]] or [https://github.com/ryanvolz/radioconda radioconda].<br /><br />
If you need to install GNU Radio from source refer to the [[WindowsInstall|Windows install guide]].<br />
<br />
Note: We do not officially support Windows. We do our best to provide installation instructions and work out build bugs on Windows when they are reported and patches received. As new versions of GNU Radio, its dependencies, and Windows itself come out, however, keeping all of them working together is beyond the scope of what the project currently supports. User updates to the above wiki installation instructions are very welcome.<br />
<br />
== Mac OS X ==<br />
<br />
Refer to the [[MacInstall|Mac OS X install guide page]].<br />
<br />
= From Source =<br />
<br />
Binary installation should be sufficient for most users, and certainly for anyone who is new to GNU Radio. However, if you have special requirements, want the latest version, or the binary packages are not working for you, you may want to install GNU Radio from source.<br />
<br />
=== Notes ===<br />
<br />
* By default GNU Radio will be installed in the <code>/usr/local</code> directory. See notes about [[InstallingGR#Common_cmake_flags|-DCMAKE_INSTALL_PREFIX]] to install it elsewhere.<br />
* Running and developing out-of-tree modules does not require GNU Radio to be installed from source. <br />
* <b>If you want to use GNU Radio with a USRP, you FIRST must clone and install UHD</b>. See the [https://kb.ettus.com/Building_and_Installing_the_USRP_Open-Source_Toolchain_(UHD_and_GNU_Radio)_on_Linux UHD Installation Page] for more info, <b>OR</b> follow the instructions in [[InstallingGRFromSource_on_Raspberry_Pi#Load_prerequisites|Install UHD from source]].<br />
* To install on a Raspberry Pi, see [[InstallingGRFromSource on Raspberry Pi]].<br />
* To build from source from within a conda environment, see [[CondaInstall#Building GNU Radio from source within a conda environment|the conda install guide]].<br />
<br />
=== For GNU Radio 3.9 and Master Branch ===<br />
<br />
This section describes how to install GNU Radio 3.9 and the Master branch from source code.<br />
<br />
==== Installing Dependencies ====<br />
<br />
Refer to [[InstallingGR#Linux|this page for your specific Linux distro]] to find how to install dependencies. For example, on Ubuntu 20.04 [[UbuntuInstall#Focal_Fossa_.2820.04.29_through_Impish_Indri_.2821.10.29|use this command]].<br />
<br />
==== Installing Volk ====<br />
<br />
Since Volk is no longer considered as a submodule of GNU Radio (GNU Radio commit #80c04479da962d048d41165081b026aafdaa0316 ),<br> you <b>MUST FIRST</b> install Volk, and then install GNU Radio. <br />
<br />
The basic idea is the same, but instead of building Volk along with GNU Radio, you need to clone and build it separately. For this example, we will start in the home directory. You can, of course, use any directory you wish and the results will be the same.<br />
<br />
* <code>cd</code><br />
* <code>git clone --recursive https://github.com/gnuradio/volk.git</code><br />
* <code>cd volk</code><br />
* <code>mkdir build</code><br />
* <code>cd build</code><br />
<b>Note:</b> In the following command, you can add <code>-DCMAKE_INSTALL_PREFIX=XXX</code> to install Volk into the PREFIX <code>XXX</code>; if not specified, then the PREFIX is <code>/usr/local</code>. See other CMake options in [[#Common_cmake_flags|Common cmake flags]].<br><br />
* <code>cmake -DCMAKE_BUILD_TYPE=Release -DPYTHON_EXECUTABLE=/usr/bin/python3 ../</code><br />
* <code>make</code><br />
* <code>make test</code><br />
* <code>sudo make install</code><br />
<br />
If you're running Linux, then always remember to do the following command after installing any library:<br />
* <code>sudo ldconfig</code><br />
<br />
==== Installing GNU Radio ====<br />
<br />
* <code>cd</code><br />
* <code>git clone https://github.com/gnuradio/gnuradio.git</code><br />
* <code>cd gnuradio</code><br />
<b>Note:</b> If you want to build the <code>maint-3.9</code> branch rather than the default <code>master</code> branch, enter:<br />
<code>git checkout maint-3.9</code> and then<br><br />
* <code>mkdir build</code><br />
* <code>cd build</code><br />
<b>Note:</b> In the following command, you can add <code>-DCMAKE_INSTALL_PREFIX=XXX</code> to install GNU Radio into the PREFIX <code>XXX</code>; if not specified, then the PREFIX is <code>/usr/local</code>. See other CMake options in [[#Common_cmake_flags|Common cmake flags]].<br><br />
* <code>cmake -DCMAKE_BUILD_TYPE=Release -DPYTHON_EXECUTABLE=/usr/bin/python3 ../</code><br />
* <code>make -j3</code> (e.g. if you want to use 3 CPU cores during the build. To use 8 do -j8, to use 1, leave out the -j flag.)<br />
<b>Note:</b> In the following command, it is very possible that not all tests pass. Generally any error is a sign of a missing dependency such as the Python interface to ZMQ or NumPy or SciPy, none of which are required for building GNU Radio but are required for testing.<br />
* <code>make test</code><br />
* <code>sudo make install</code><br />
If you're running Linux, then always remember to do the following command after installing any library:<br />
* <code>sudo ldconfig</code><br />
If you encounter "Cannot import gnuradio" error, then go to [[ModuleNotFoundError#B._Finding_the_Python_library|Finding the Python library]] to set your PYTHONPATH and LD_LIBRARY_PATH.<br><b>After setting these environment variables, you need to do</b> <code>sudo ldconfig</code> <b>again</b> for the Linux dynamic library loader to find the just-installed GNU Radio libraries.<br><br />
If you have installed in a custom path with <code>-DCMAKE_INSTALL_PREFIX=XXX</code>, you will need to add that path to $PATH in order to find gnuradio-companion.<br />
<br />
=== For GNU Radio 3.8 or Earlier ===<br />
<br />
==== Installing Dependencies ====<br />
<br />
Refer to [[InstallingGR#Linux|this page for your specific Linux distro]] to find how to install dependencies. For example, on Ubuntu 20.04 [[UbuntuInstall#Focal_Fossa_.2820.04.29_through_Impish_Indri_.2821.10.29|use this command]].<br />
<br />
==== Installing GNU Radio ====<br />
For this example, we will start in the home directory; you can, of course, use any directory you wish and the results will be the same.<br />
<br />
* <code>cd</code><br />
* <code>git clone https://github.com/gnuradio/gnuradio.git</code><br />
* <code>cd gnuradio</code><br />
<br />
<b>Note:</b> In the following command, change <code>maint-3.8</code> to some other branch or tag if you want to build a different version of GNU Radio; see [https://github.com/gnuradio/gnuradio/tags tags] for tagged releases including pre-releases ("rc"). For [https://github.com/gnuradio/gnuradio/branches branches], it's generally wise to stick with "master" (the default after cloning), and, currently: <code>maint-3.7</code> or <code>maint-3.8</code>. Here we checkout the <code>maint-3.8</code> branch, which contains the latest 3.8 release plus any fixes or augmentations to it that will be in the next 3.8 release.<br />
<br />
* <code>git checkout maint-3.8</code><br />
* <code>git submodule update --init --recursive</code><br />
* <code>mkdir build</code><br />
* <code>cd build</code><br />
<br />
<b>Note:</b> In the following command, you can add <code>-DCMAKE_INSTALL_PREFIX=XXX</code> to install GNU Radio into the PREFIX <code>XXX</code>; if not specified, then the PREFIX is <code>/usr/local</code>. See other CMake options in [[#Common_cmake_flags|Common cmake flags]].<br><br />
<br />
* <code>cmake -DCMAKE_BUILD_TYPE=Release -DPYTHON_EXECUTABLE=/usr/bin/python3 ../</code><br />
* <code>make -j3</code> (e.g. if you want to use 3 CPU cores during the build. To use 8 do -j8, to use 1 leave out the -j flag.)<br />
* <code>sudo make install</code><br />
<br />
If you're running Linux, then always remember to do the following command after installing any library:<br />
* <code>sudo ldconfig</code><br />
<br />
Go to [[ModuleNotFoundError#B._Finding_the_Python_library|Finding the Python library]] to set your PYTHONPATH and LD_LIBRARY_PATH.<br><b>After setting these environment variables, you need to do</b> <code>sudo ldconfig</code> <b>again</b> for the Linux dynamic library loader to find the just-installed GNU Radio libraries.<br><br />
If you have installed in a custom path with <code>-DCMAKE_INSTALL_PREFIX=XXX</code>, you will need to add that path to $PATH in order to find gnuradio-companion.<br />
<br />
==== For Ubuntu 18.04 ====<br />
<br />
An easy way to install GNU Radio 3.8 on many Ubuntu systems is to use the following commands (note that this skips the setup for UHD hardware):<br />
<br />
* <code>sudo apt install git cmake g++ libboost-all-dev libgmp-dev swig python3-numpy python3-mako python3-sphinx python3-lxml doxygen libfftw3-dev libsdl1.2-dev libgsl-dev libqwt-qt5-dev libqt5opengl5-dev python3-pyqt5 liblog4cpp5-dev libzmq3-dev python3-yaml python3-click python3-click-plugins python3-zmq python3-scipy python3-pip python3-gi-cairo</code><br />
* <code>pip3 install git+https://github.com/pyqtgraph/pyqtgraph@develop</code><br />
* <code>pip3 install numpy scipy</code><br />
* <code>echo 'export PYTHONPATH=/usr/local/lib/python3/dist-packages:usr/local/lib/python2.7/site-packages:$PYTHONPATH' >> ~/.bashrc</code><br />
* <code>echo 'export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH' >> ~/.bashrc</code><br />
* <code>echo 'export PYTHONPATH=/usr/local/lib/python3/dist-packages:usr/local/lib/python2.7/site-packages:$PYTHONPATH' >> ~/.profile</code><br />
* <code>echo 'export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH' >> ~/.profile</code><br />
* <code>cd ~/</code><br />
* <code>git clone --recursive https://github.com/gnuradio/gnuradio</code><br />
* <code>cd gnuradio</code><br />
* <code>git checkout maint-3.8</code><br />
* <code>mkdir build</code><br />
* <code>cd build</code><br />
* <code>git pull --recurse-submodules=on</code><br />
* <code>git submodule update --init</code><br />
* <code>cmake -DENABLE_GR_UHD=OFF ..</code><br />
* <code>make -j $(nproc --all)</code><br />
* <code>sudo make install</code><br />
* <code>sudo ldconfig</code><br />
<br />
Once this is done, reboot your computer and GNU Radio should be all set for you.<br />
<br />
== Common cmake flags ==<br />
<br />
* <code>-DENABLE_GR_XXX=ON</code> This enables (or disables for =OFF) the GNU Radio component named XXX. You might not need all of them, and this way, you can compile quicker.<br />
* <code>-DCMAKE_INSTALL_PREFIX=XXX</code> Install your stuff to XXX.<br />
* <code>-DCMAKE_BUILD_TYPE=Debug</code> This causes gcc to add debug symbols to all binaries. Useful for debugging (otherwise, it decreases efficiency!)<br />
* <code>-DPYTHON_EXECUTABLE=/usr/bin/python{2,3}</code> This selects the Python version and executable to be used during build time and will determine which Python libraries will be used for building the Python bindings.<br />
<p>For a list of additional cmake flags, as well as minimum versions of dependencies, see [https://www.gnuradio.org/doc/doxygen/build_guide.html]</p><br />
<br />
= Using PyBOMBS =<br />
<br />
PyBOMBS is good at building GNU Radio, UHD, and various Out of Tree (OOT) modules from source and then installing into a specified user directory rather than in the system files. PyBOMBS detects the user's Operating System and loads all of the prerequisites in the first stage of the build.<br />
<br />
The PyBOMBS documentation is in the PyBOMBS [https://github.com/gnuradio/pybombs#pybombs README].<br />
<br />
= VMs and Live Images =<br />
Over the years a number of Live Images and VMs have been created. There are currently no official versions but here are some current options:<br />
*[https://github.com/bastibl/instant-gnuradio Instant GNU Radio] A customizable, programmatically generated VM and live environment for GNU Radio. <br />
*[[UbuntuVM]] An Ubuntu 20.04 virtual machine image with GNU Radio 3.8.2.0, Fosphor, GQRX, and several other useful pieces of software. (Created using Instant GNU Radio)<br />
<br />
<br />
= OK, it's installed, what now? =<br />
<br />
If the installation worked without any trouble, you're ready to use GNU Radio! If you have no idea how to do that, the best place to start is with the [[Tutorials]].<br />
<br />
Optionally, you may run <code>volk_profile</code> on your terminal to help libvolk to determine the optimal kernels (may speed up GNU Radio). <br />
<br />
[[Category:Installation]]<br />
[[Category:Guide]]</div>Tom N5EGhttps://wiki.gnuradio.org/index.php?title=GNU_Radio_3.9_OOT_Module_Porting_Guide&diff=8703GNU Radio 3.9 OOT Module Porting Guide2021-06-18T16:44:13Z<p>Tom N5EG: /* Porting Guide */</p>
<hr />
<div>[[Category:3.9]]<br />
The major changes in the (in-progress) GNU Radio 3.9 release that will impact OOTs are:<br />
<br />
* C++ modernization (C++11/14?)<br />
* Replacement of SWIG with Pybind11<br />
* Cleanup of filter and fft APIs<br />
<br />
<br />
= Porting Guide = <br />
<br />
Porting from 3.8 to 3.9 can be achieved most simply by creating a new OOT module (with the same name as the 3.8 OOT but in a different directory), then performing some manual steps<br />
<br />
1. Use the 3.9 gr_modtool to generate a module with the same name (in another directory)<br />
<br />
2. Copy the python folder from 3.9 OOT into your 3.8 OOT<br />
<br />
3. (in 3.8 OOT) Add the bindings directory to the python directory CMakeLists<br />
./python/CMakeLists.txt → add the line: <br />
add_subdirectory(bindings)<br />
<br />
4. (in 3.8 OOT) Call gr_modtool bind for each block in your OOT<br />
NOTE: If you are doing more than just make function in your public header, e.g. setters/getters or other callback functions, be sure to have pygccxml set up<br />
<br />
5. c++ blocks that have python QA will need the import statements updated in that QA.<br />
<br />
6. python/bindings/python_bindings.cc needs to be updated for all C++ blocks (in two places!)<br />
<br />
7. Replace occurrences of boost::shared_ptr<blockname> with std::shared_ptr<blockname><br />
<br />
8. Though not necessary, boost::bind instances for message port handlers can be replaced with lambda functions for performance and simplicity as well as consistency with the in-tree modules<br />
<br />
* The instructions below are primarily intended for C++ projects. Python projects may be slightly different.<br />
<br />
9. Merge 3.8 /lib files (.cc, .h, etc) with the 3.9 lib file prototypes constructed by gr_modtool into 3.9 /lib.<br />
<br />
10. Copy the 3.8 /python/binding and /docstring files into 3.9 <br />
<br />
11. Merge the 3.8 /grc yml files with the 3.9 prototypes into 3.9/grc<br />
<br />
12. Merge the 3.9 /include files with the 3.9 prototypes into 3.9/include.<br />
<br />
13. Edit 3.9 /lib/CMakeLists.txt to add all the targets needed as specified in the 3.8 /lib/CMakeLists.txt file.<br />
<br />
14. bind will compare date/time stamps of the files. If the target bindings are out of date, manually rebind each 3.9 block with gr_modtool bind block.<br />
<br />
Porting from 3.7 to 3.9 should follow the 3.8 Porting Guide, but basically create a new OOT using 3.9 and add blocks from there, then copy in code.<br />
<br />
== Versioning Your shared object files ==<br />
You may want to edit the /lib/CMakeList.txt file in order to set a version. The default VERSION_PATCH is set<br />
to git, you may want to edit it to 0 for your first version. Then when you need to push out a modified version remember<br />
to edit the version numbers before building. If you leave the VERSION_PATCH at git the install directory may eventually become<br />
littered with old libgnuradio-yourproject.so.git-commit-number files and soft links.<br />
<br />
= Details = <br />
<br />
== C++ Modernization ==<br />
<br />
Boost shared pointers have been replaced with std:: shared pointers and memory management. At the top level of each block, the instantiation will need to change, e.g. <br />
<br />
In include/blockname_xx.h:<br />
<br />
<nowiki>typedef std::shared_ptr<blockname_xx> sptr;</nowiki><br />
<br />
Inbound message ports receive an update too. We move from <nowiki>boost::function</nowiki> to <nowiki>std::function</nowiki>. This affects how message handlers are registered. The preferred style is to use lambdas which is already compatible with GNU Radio 3.8:<br />
<br />
<nowiki>set_msg_handler(pmt::mp("message"), [this](pmt::pmt_t msg) { this->handle_msg(msg); });</nowiki><br />
<br />
== Pybind11 Python Bindings ==<br />
<br />
As of the GNU Radio 3.9 release, python bindings are handled using pybind11, which is inherently different than they were in previous releases<br />
<br />
=== Dependencies ===<br />
<br />
* pybind11 &gt; 2.4.3 [https://pybind11.readthedocs.io/ https://pybind11.readthedocs.io/]<br />
** pip does not provide the proper cmake (https://github.com/pybind/pybind11/issues/1379)<br />
** gnuradio 3.9 was built using pybind11 version 2.5.0. Your OOT should be built against that same version. (Version 2.4.3 will not work).<br />
** As of May 2021 Ubuntu 20.04 package manager references version 2.5.0:<br />
sudo apt install pybind11-dev<br />
<br />
** For some distributions (e.g. Ubuntu <20.04), this will need to be installed from source as 2.5.0 is not the supplied version with package managers<br />
<nowiki><br />
curl -Lo pybind11.tar.gz https://github.com/pybind/pybind11/archive/v2.5.0.tar.gz <br />
mkdir pybind11 && tar xzf pybind11.tar.gz -C pybind11 --strip-components=1 && cd pybind11<br />
mkdir build && cd build <br />
cmake .. -DCMAKE_BUILD_TYPE=Release -DPYBIND11_TEST=OFF <br />
make<br />
sudo make install </nowiki><br />
<br />
* pygccxml [https://pygccxml.readthedocs.io/en/develop/install.html https://pygccxml.readthedocs.io/en/develop/install.html]<br />
** This is an optional dependency and basic functionality for OOT generation can be performed without pygccxml<br />
** It is required for automatically generating bindings for most of the GR source tree<br />
<br />
=== Components ===<br />
<br />
Python bindings are contained in the <code>python/.../bindings</code> directory<br />
<br />
<source lang="sh">./python<br />
└── module_name<br />
├── bindings<br />
│ ├── blockname1_python.cc<br />
│ ├── blockname2_python.cc<br />
│ ├── CMakeLists.txt<br />
| ├── python_bindings.cc<br />
│ ├── docstrings<br />
│ │ ├── blockname1_pydoc_template.h<br />
│ │ ├── blockname1_pydoc_template.h<br />
</source><br />
The bindings for each block exist in blockname_python.cc under the <code>python/bindings</code> directory. Additionally, a template header file for each block that is used as a placeholder for the scraped docstrings lives in the <code>docstrings/</code> dir<br />
<br />
==== blockname_python.cc ====<br />
<br />
This is the class function enum variable bindings for everything that needs to be exposed through the Python API<br />
<br />
==== python_bindings.cc ====<br />
The structure of this file is<br />
<br />
<source lang="cpp">// Headers for binding functions<br />
/**************************************/<br />
/* The following comment block is used for<br />
/* gr_modtool to insert function prototypes<br />
/* Please do not delete<br />
/**************************************/<br />
// BINDING_FUNCTION_PROTOTYPES(<br />
<br />
void bind_blockname1(py::module&);<br />
<br />
// ) END BINDING_FUNCTION_PROTOTYPES<br />
<br />
<br />
PYBIND11_MODULE(module_name__python, m)<br />
{<br />
<br />
{<br />
// Initialize the numpy C API<br />
// (otherwise we will see segmentation faults)<br />
init_numpy();<br />
<br />
// Allow access to base block methods<br />
py::module::import("gnuradio.gr");<br />
/**************************************/<br />
/* The following comment block is used for<br />
/* gr_modtool to insert binding function calls<br />
/* Please do not delete<br />
/**************************************/<br />
// BINDING_FUNCTION_CALLS(<br />
bind_blockname1(m);<br />
// ) END BINDING_FUNCTION_CALLS<br />
}<br />
</source><br />
===== Comment Block =====<br />
<br />
Each block binding file contains an automatically generated and maintained comment block that informs CMake when the bindings are out of sync with the header file they refer to, and what to do about it<br />
<br />
<source lang="cpp">/***********************************************************************************/<br />
/* This file is automatically generated using bindtool and can be manually edited */<br />
/* The following lines can be configured to regenerate this file during cmake */<br />
/* If manual edits are made, the following tags should be modified accordingly. */<br />
/* BINDTOOL_GEN_AUTOMATIC(0) */<br />
/* BINDTOOL_USE_PYGCCXML(0) */<br />
/* BINDTOOL_HEADER_FILE(basic_block.h) */<br />
/* BINDTOOL_HEADER_FILE_HASH(549c06530e2afdf6f2c989017cb5f36e) */<br />
/***********************************************************************************/<br />
</source><br />
<code>BINDTOOL_GEN_AUTOMATIC</code>: Many times for complex in-tree blocks, the automated tools are not entirely sufficient to generate all of the bindings in an automated fashion. In this case, the flag should be set to 0, and the bindings need to be updated manually. If the flag is set to 1, CMake will override the binding file ''in the source tree'' when it detects out of sync bindings. This should only be done in simple cases.<br />
<br />
<code>BINDTOOL_USE_PYGCCXML</code>: Currently there are limitations on the amount of code generation that can be accomplished without the <code>pygccxml</code> dependency. If a block needs pygccxml for the bindings to be properly generated automatically, this should be set to <code>1</code><br />
<br />
<code>BINDTOOL_HEADER_FILE</code>: The header file that bindings are based on, filename only<br />
<br />
<code>BINDTOOL_HEADER_FILE_HASH</code>: The MD5 hash of the header file that the bindings were built on<br />
<br />
=== Workflow ===<br />
<br />
==== Out-of-Tree modules ====<br />
<br />
The steps for creating an out of tree module with pybind11 bindings are as follows:<br />
<br />
# Use <code>gr_modtool</code> to create an out of tree module and add blocks<br />
<br />
<source lang="sh">gr_modtool newmod foo<br />
gr_modtool add bar<br />
</source><br />
<ol start="2"><br />
<li>Update the parameters or functions in the public include file and rebind with <code>gr_modtool bind bar</code></li></ol><br />
<br />
'''NOTE''': without pygccxml, only the make function is currently accounted for, similar to <code>gr_modtool makeyaml</code><br />
<br />
If the public API changes, just call <code>gr_modtool bind [blockname]</code> to regenerate the bindings<br />
<br />
When the public header file for a block is changed, CMake will fail as it checks the hash of the header file compared to the hash stored in the bindings file until the bindings are updated<br />
<br />
<ol start="3"><br />
<li>Build and install</li></ol><br />
<br />
=== Docstrings ===<br />
<br />
If Doxygen is enabled in GNU Radio and/or the OOT, Docstrings are scraped from the header files, and placed in auto-generated <code>[blockname]_pydoc.h</code> files in the build directory on compile. Generated templates (via the binding steps described above) are placed in the <code>python/bindings/docstrings</code> directory and are used as placeholders for the scraped strings<br />
<br />
Upon compilation, docstrings are scraped from the module and stored in a dictionary (using <code>update_pydoc.py scrape</code>) and then the values are substituted in the template file (using <code>update_pydoc.py sub</code>)<br />
<br />
<br />
=== OOT Migration ===<br />
<br />
The easiest way to migrate an OOT to 3.9 is to use <code>gr_modtool</code> to create a new OOT, use <code>gr_modtool add</code> to create the blocks, and copy code from the previous OOT.<br />
<br />
Steps to do this without regenerating a new module are TBD<br />
<br />
=== Caveats ===<br />
<br />
Pybind11 bound methods do not implicitly convert int to enum, so blocks that take enum as input, must have either "raw" or "enum" in the grc yml definition of the block. "Raw" will allow the value to be changed by another variable in the flowgraph.<br />
<br />
Block inheritance must be specified completely in the python bindings in order to use the inherited methods. For instance, if a block inherits from sync_block, both block and basic_block must be included in the inheritance specification of the class:<br />
<br />
<nowiki> py::class_<atsc_interleaver,<br />
gr::sync_block,<br />
gr::block,<br />
gr::basic_block,<br />
std::shared_ptr<atsc_interleaver>>(<br />
m, "atsc_interleaver", D(atsc_interleaver)) </nowiki><br />
<br />
<br />
If your OOT module uses types from or its classes derive from another gr module, it is necessary in <code>python_bindings.cc</code> to specify these modules. <br />
<br />
For instance, since all OOT modules require the base block types, there is a line <code>py::module::import("gnuradio.gr");</code>.<br />
<br />
If you wanted to utilize <code>digital::constellation</code> objects in your OOT, it would be necessary to add <code>py::module::import("gnuradio.digital");</code> so that pybind knows to use the bindings already compiled into gnuradio.digital for the constellation objects<br />
<br />
If your OOT module uses other classes as parameter, you must either setup a python binding for this class or use an existing one. <br />
An example, how to setup a binding for the QWidget class of QT5 used in gr-qtgui, can be found here[https://github.com/gnuradio/gnuradio/blob/master/gr-qtgui/python/qtgui/bindings/QWidget_python.cc]<br />
<br />
If you want to use this binding in your OOT module, you have to import it. This has to be done in the python_bindings.cc inside the '''PYBIND11_MODULE''' macro.<br />
In case of the QWidget usage this would be <code>py::module::import("gnuradio.qtgui.qtgui_python");</code><br />
<br />
==== Using default values ====<br />
<br />
For standard types like int,float, etc. you can set default values as usual.<br />
<code>gr_modtool bind blockname</code> will setup the correct bindings in blockname_python.cc.<br />
<br />
But if you use something like <code> QWidget* parent = NULL </code> gr_modtool generates code like<br />
<code>py::arg("parent") = __null, </code><br />
<br />
But at this point pybind does not know the type of parent, so you have to modify the binding code and cast the type.<br />
<code>py::arg("parent") = (QWidget *) __null, </code><br />
<br />
Another way to come across this issue is to modify your header file from<br />
<code> QWidget* parent = NULL </code> to<br />
<code> QWidget* parent = nullptr </code><br />
<br />
pybind generates <br />
<code>py::arg("parent") = nullptr, </code><br />
<br />
which will be handled correctly<br />
=== Troubleshooting ===<br />
<br />
==== Unable to find pydoc.h ====<br />
<nowiki>fatal error: blockname_pydoc.h: No such file or directory<br />
28 | #include <blockname_pydoc.h><br />
| ^~~~~~~~~~~~~~~~~~~~~~~~~<br />
compilation terminated.</nowiki><br />
<br />
blockname_pydoc.h is generated during compilation based on the template in the docstring directory. When the block is first created in blocktool, this template does not exist. Run <code>gr_modtool bind</code> to generate the appropriate template used as a placeholder for the scraped docstrings<br />
<br />
Also, the scraping of docstrings only takes place at CMake time, so it may be necessary to do a <code>make clean</code> to re-trigger the scraping<br />
<br />
You can also try <br />
<nowiki>rm python/bindings/docstring_status</nowiki><br />
which will reset the docstring scraping target in cmake and re-copy the docstring templates<br />
<br />
==== TypeError: 'modulename_python.blockname' object is not subscriptable ====<br />
<br />
This is caused by an incomplete inheritance chain specified in the binding declaration of the block.<br />
<br />
Instead of<br />
<br />
py::class_<blockname,<br />
std::shared_ptr<blockname>>(m, "blockname", D(blockname))<br />
<br />
Try something like (taking into account your block type)<br />
<br />
py::class_<blockname,<br />
gr::sync_block,<br />
gr::block,<br />
gr::basic_block,<br />
std::shared_ptr<blockname>>(m, "blockname", D(blockname))<br />
<br />
==== Module is Empty ====<br />
<br />
This is usually caused by linker errors that prevent the binding module from being loaded. When you see something like:<br />
<br />
>>> import foo<br />
>>> dir(foo)<br />
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__']<br />
<br />
But you expect foo to have other C++ blocks, try loading the pybind module separately<br />
<br />
cd build/python/bindings<br />
python3<br />
>>> import foo_python<br />
[linker error should be evident here in mangled symbol name]</div>Tom N5EGhttps://wiki.gnuradio.org/index.php?title=GNU_Radio_3.9_OOT_Module_Porting_Guide&diff=8702GNU Radio 3.9 OOT Module Porting Guide2021-06-18T16:43:54Z<p>Tom N5EG: /* Versioning Your shared object files */</p>
<hr />
<div>[[Category:3.9]]<br />
The major changes in the (in-progress) GNU Radio 3.9 release that will impact OOTs are:<br />
<br />
* C++ modernization (C++11/14?)<br />
* Replacement of SWIG with Pybind11<br />
* Cleanup of filter and fft APIs<br />
<br />
<br />
= Porting Guide = <br />
<br />
Porting from 3.8 to 3.9 can be achieved most simply by creating a new OOT module (with the same name as the 3.8 OOT but in a different directory), then performing some manual steps<br />
<br />
1. Use the 3.9 gr_modtool to generate a module with the same name (in another directory)<br />
<br />
2. Copy the python folder from 3.9 OOT into your 3.8 OOT<br />
<br />
3. (in 3.8 OOT) Add the bindings directory to the python directory CMakeLists<br />
./python/CMakeLists.txt → add the line: <br />
add_subdirectory(bindings)<br />
<br />
4. (in 3.8 OOT) Call gr_modtool bind for each block in your OOT<br />
NOTE: If you are doing more than just make function in your public header, e.g. setters/getters or other callback functions, be sure to have pygccxml set up<br />
<br />
5. c++ blocks that have python QA will need the import statements updated in that QA.<br />
<br />
6. python/bindings/python_bindings.cc needs to be updated for all C++ blocks (in two places!)<br />
<br />
7. Replace occurrences of boost::shared_ptr<blockname> with std::shared_ptr<blockname><br />
<br />
8. Though not necessary, boost::bind instances for message port handlers can be replaced with lambda functions for performance and simplicity as well as consistency with the in-tree modules<br />
<br />
* The instructions below are primarily intended for C++ projects. Python projects may be slightly different.<br />
<br />
9. Merge 3.8 /lib files (.cc, .h, etc) with the 3.9 lib file prototypes constructed by gr_modtool into 3.9 /lib.<br />
<br />
10. Copy the 3.8 /python/binding and /docstring files into 3.9 <br />
<br />
11. Merge the 3.8 /grc yml files with the 3.9 prototypes into 3.9/grc<br />
<br />
12. Merge the 3.9 /include files with the 3.9 prototypes into 3.9/include.<br />
<br />
13. Edit 3.9 /lib/CMakeLists.txt to add all the targets needed as specified in the 3.8 /lib/CMakeLists.txt file.<br />
<br />
14. bind will compare date/time stamps of the files. If the target bindings are out of date, manually rebind each 3.9 block with gr_modtool bind block.<br />
<br />
<br />
== Versioning Your shared object files ==<br />
You may want to edit the /lib/CMakeList.txt file in order to set a version. The default VERSION_PATCH is set<br />
to git, you may want to edit it to 0 for your first version. Then when you need to push out a modified version remember<br />
to edit the version numbers before building. If you leave the VERSION_PATCH at git the install directory may eventually become<br />
littered with old libgnuradio-yourproject.so.git-commit-number files and soft links.<br />
<br />
= Details = <br />
<br />
== C++ Modernization ==<br />
<br />
Boost shared pointers have been replaced with std:: shared pointers and memory management. At the top level of each block, the instantiation will need to change, e.g. <br />
<br />
In include/blockname_xx.h:<br />
<br />
<nowiki>typedef std::shared_ptr<blockname_xx> sptr;</nowiki><br />
<br />
Inbound message ports receive an update too. We move from <nowiki>boost::function</nowiki> to <nowiki>std::function</nowiki>. This affects how message handlers are registered. The preferred style is to use lambdas which is already compatible with GNU Radio 3.8:<br />
<br />
<nowiki>set_msg_handler(pmt::mp("message"), [this](pmt::pmt_t msg) { this->handle_msg(msg); });</nowiki><br />
<br />
== Pybind11 Python Bindings ==<br />
<br />
As of the GNU Radio 3.9 release, python bindings are handled using pybind11, which is inherently different than they were in previous releases<br />
<br />
=== Dependencies ===<br />
<br />
* pybind11 &gt; 2.4.3 [https://pybind11.readthedocs.io/ https://pybind11.readthedocs.io/]<br />
** pip does not provide the proper cmake (https://github.com/pybind/pybind11/issues/1379)<br />
** gnuradio 3.9 was built using pybind11 version 2.5.0. Your OOT should be built against that same version. (Version 2.4.3 will not work).<br />
** As of May 2021 Ubuntu 20.04 package manager references version 2.5.0:<br />
sudo apt install pybind11-dev<br />
<br />
** For some distributions (e.g. Ubuntu <20.04), this will need to be installed from source as 2.5.0 is not the supplied version with package managers<br />
<nowiki><br />
curl -Lo pybind11.tar.gz https://github.com/pybind/pybind11/archive/v2.5.0.tar.gz <br />
mkdir pybind11 && tar xzf pybind11.tar.gz -C pybind11 --strip-components=1 && cd pybind11<br />
mkdir build && cd build <br />
cmake .. -DCMAKE_BUILD_TYPE=Release -DPYBIND11_TEST=OFF <br />
make<br />
sudo make install </nowiki><br />
<br />
* pygccxml [https://pygccxml.readthedocs.io/en/develop/install.html https://pygccxml.readthedocs.io/en/develop/install.html]<br />
** This is an optional dependency and basic functionality for OOT generation can be performed without pygccxml<br />
** It is required for automatically generating bindings for most of the GR source tree<br />
<br />
=== Components ===<br />
<br />
Python bindings are contained in the <code>python/.../bindings</code> directory<br />
<br />
<source lang="sh">./python<br />
└── module_name<br />
├── bindings<br />
│ ├── blockname1_python.cc<br />
│ ├── blockname2_python.cc<br />
│ ├── CMakeLists.txt<br />
| ├── python_bindings.cc<br />
│ ├── docstrings<br />
│ │ ├── blockname1_pydoc_template.h<br />
│ │ ├── blockname1_pydoc_template.h<br />
</source><br />
The bindings for each block exist in blockname_python.cc under the <code>python/bindings</code> directory. Additionally, a template header file for each block that is used as a placeholder for the scraped docstrings lives in the <code>docstrings/</code> dir<br />
<br />
==== blockname_python.cc ====<br />
<br />
This is the class function enum variable bindings for everything that needs to be exposed through the Python API<br />
<br />
==== python_bindings.cc ====<br />
The structure of this file is<br />
<br />
<source lang="cpp">// Headers for binding functions<br />
/**************************************/<br />
/* The following comment block is used for<br />
/* gr_modtool to insert function prototypes<br />
/* Please do not delete<br />
/**************************************/<br />
// BINDING_FUNCTION_PROTOTYPES(<br />
<br />
void bind_blockname1(py::module&);<br />
<br />
// ) END BINDING_FUNCTION_PROTOTYPES<br />
<br />
<br />
PYBIND11_MODULE(module_name__python, m)<br />
{<br />
<br />
{<br />
// Initialize the numpy C API<br />
// (otherwise we will see segmentation faults)<br />
init_numpy();<br />
<br />
// Allow access to base block methods<br />
py::module::import("gnuradio.gr");<br />
/**************************************/<br />
/* The following comment block is used for<br />
/* gr_modtool to insert binding function calls<br />
/* Please do not delete<br />
/**************************************/<br />
// BINDING_FUNCTION_CALLS(<br />
bind_blockname1(m);<br />
// ) END BINDING_FUNCTION_CALLS<br />
}<br />
</source><br />
===== Comment Block =====<br />
<br />
Each block binding file contains an automatically generated and maintained comment block that informs CMake when the bindings are out of sync with the header file they refer to, and what to do about it<br />
<br />
<source lang="cpp">/***********************************************************************************/<br />
/* This file is automatically generated using bindtool and can be manually edited */<br />
/* The following lines can be configured to regenerate this file during cmake */<br />
/* If manual edits are made, the following tags should be modified accordingly. */<br />
/* BINDTOOL_GEN_AUTOMATIC(0) */<br />
/* BINDTOOL_USE_PYGCCXML(0) */<br />
/* BINDTOOL_HEADER_FILE(basic_block.h) */<br />
/* BINDTOOL_HEADER_FILE_HASH(549c06530e2afdf6f2c989017cb5f36e) */<br />
/***********************************************************************************/<br />
</source><br />
<code>BINDTOOL_GEN_AUTOMATIC</code>: Many times for complex in-tree blocks, the automated tools are not entirely sufficient to generate all of the bindings in an automated fashion. In this case, the flag should be set to 0, and the bindings need to be updated manually. If the flag is set to 1, CMake will override the binding file ''in the source tree'' when it detects out of sync bindings. This should only be done in simple cases.<br />
<br />
<code>BINDTOOL_USE_PYGCCXML</code>: Currently there are limitations on the amount of code generation that can be accomplished without the <code>pygccxml</code> dependency. If a block needs pygccxml for the bindings to be properly generated automatically, this should be set to <code>1</code><br />
<br />
<code>BINDTOOL_HEADER_FILE</code>: The header file that bindings are based on, filename only<br />
<br />
<code>BINDTOOL_HEADER_FILE_HASH</code>: The MD5 hash of the header file that the bindings were built on<br />
<br />
=== Workflow ===<br />
<br />
==== Out-of-Tree modules ====<br />
<br />
The steps for creating an out of tree module with pybind11 bindings are as follows:<br />
<br />
# Use <code>gr_modtool</code> to create an out of tree module and add blocks<br />
<br />
<source lang="sh">gr_modtool newmod foo<br />
gr_modtool add bar<br />
</source><br />
<ol start="2"><br />
<li>Update the parameters or functions in the public include file and rebind with <code>gr_modtool bind bar</code></li></ol><br />
<br />
'''NOTE''': without pygccxml, only the make function is currently accounted for, similar to <code>gr_modtool makeyaml</code><br />
<br />
If the public API changes, just call <code>gr_modtool bind [blockname]</code> to regenerate the bindings<br />
<br />
When the public header file for a block is changed, CMake will fail as it checks the hash of the header file compared to the hash stored in the bindings file until the bindings are updated<br />
<br />
<ol start="3"><br />
<li>Build and install</li></ol><br />
<br />
=== Docstrings ===<br />
<br />
If Doxygen is enabled in GNU Radio and/or the OOT, Docstrings are scraped from the header files, and placed in auto-generated <code>[blockname]_pydoc.h</code> files in the build directory on compile. Generated templates (via the binding steps described above) are placed in the <code>python/bindings/docstrings</code> directory and are used as placeholders for the scraped strings<br />
<br />
Upon compilation, docstrings are scraped from the module and stored in a dictionary (using <code>update_pydoc.py scrape</code>) and then the values are substituted in the template file (using <code>update_pydoc.py sub</code>)<br />
<br />
<br />
=== OOT Migration ===<br />
<br />
The easiest way to migrate an OOT to 3.9 is to use <code>gr_modtool</code> to create a new OOT, use <code>gr_modtool add</code> to create the blocks, and copy code from the previous OOT.<br />
<br />
Steps to do this without regenerating a new module are TBD<br />
<br />
=== Caveats ===<br />
<br />
Pybind11 bound methods do not implicitly convert int to enum, so blocks that take enum as input, must have either "raw" or "enum" in the grc yml definition of the block. "Raw" will allow the value to be changed by another variable in the flowgraph.<br />
<br />
Block inheritance must be specified completely in the python bindings in order to use the inherited methods. For instance, if a block inherits from sync_block, both block and basic_block must be included in the inheritance specification of the class:<br />
<br />
<nowiki> py::class_<atsc_interleaver,<br />
gr::sync_block,<br />
gr::block,<br />
gr::basic_block,<br />
std::shared_ptr<atsc_interleaver>>(<br />
m, "atsc_interleaver", D(atsc_interleaver)) </nowiki><br />
<br />
<br />
If your OOT module uses types from or its classes derive from another gr module, it is necessary in <code>python_bindings.cc</code> to specify these modules. <br />
<br />
For instance, since all OOT modules require the base block types, there is a line <code>py::module::import("gnuradio.gr");</code>.<br />
<br />
If you wanted to utilize <code>digital::constellation</code> objects in your OOT, it would be necessary to add <code>py::module::import("gnuradio.digital");</code> so that pybind knows to use the bindings already compiled into gnuradio.digital for the constellation objects<br />
<br />
If your OOT module uses other classes as parameter, you must either setup a python binding for this class or use an existing one. <br />
An example, how to setup a binding for the QWidget class of QT5 used in gr-qtgui, can be found here[https://github.com/gnuradio/gnuradio/blob/master/gr-qtgui/python/qtgui/bindings/QWidget_python.cc]<br />
<br />
If you want to use this binding in your OOT module, you have to import it. This has to be done in the python_bindings.cc inside the '''PYBIND11_MODULE''' macro.<br />
In case of the QWidget usage this would be <code>py::module::import("gnuradio.qtgui.qtgui_python");</code><br />
<br />
==== Using default values ====<br />
<br />
For standard types like int,float, etc. you can set default values as usual.<br />
<code>gr_modtool bind blockname</code> will setup the correct bindings in blockname_python.cc.<br />
<br />
But if you use something like <code> QWidget* parent = NULL </code> gr_modtool generates code like<br />
<code>py::arg("parent") = __null, </code><br />
<br />
But at this point pybind does not know the type of parent, so you have to modify the binding code and cast the type.<br />
<code>py::arg("parent") = (QWidget *) __null, </code><br />
<br />
Another way to come across this issue is to modify your header file from<br />
<code> QWidget* parent = NULL </code> to<br />
<code> QWidget* parent = nullptr </code><br />
<br />
pybind generates <br />
<code>py::arg("parent") = nullptr, </code><br />
<br />
which will be handled correctly<br />
=== Troubleshooting ===<br />
<br />
==== Unable to find pydoc.h ====<br />
<nowiki>fatal error: blockname_pydoc.h: No such file or directory<br />
28 | #include <blockname_pydoc.h><br />
| ^~~~~~~~~~~~~~~~~~~~~~~~~<br />
compilation terminated.</nowiki><br />
<br />
blockname_pydoc.h is generated during compilation based on the template in the docstring directory. When the block is first created in blocktool, this template does not exist. Run <code>gr_modtool bind</code> to generate the appropriate template used as a placeholder for the scraped docstrings<br />
<br />
Also, the scraping of docstrings only takes place at CMake time, so it may be necessary to do a <code>make clean</code> to re-trigger the scraping<br />
<br />
You can also try <br />
<nowiki>rm python/bindings/docstring_status</nowiki><br />
which will reset the docstring scraping target in cmake and re-copy the docstring templates<br />
<br />
==== TypeError: 'modulename_python.blockname' object is not subscriptable ====<br />
<br />
This is caused by an incomplete inheritance chain specified in the binding declaration of the block.<br />
<br />
Instead of<br />
<br />
py::class_<blockname,<br />
std::shared_ptr<blockname>>(m, "blockname", D(blockname))<br />
<br />
Try something like (taking into account your block type)<br />
<br />
py::class_<blockname,<br />
gr::sync_block,<br />
gr::block,<br />
gr::basic_block,<br />
std::shared_ptr<blockname>>(m, "blockname", D(blockname))<br />
<br />
==== Module is Empty ====<br />
<br />
This is usually caused by linker errors that prevent the binding module from being loaded. When you see something like:<br />
<br />
>>> import foo<br />
>>> dir(foo)<br />
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__']<br />
<br />
But you expect foo to have other C++ blocks, try loading the pybind module separately<br />
<br />
cd build/python/bindings<br />
python3<br />
>>> import foo_python<br />
[linker error should be evident here in mangled symbol name]</div>Tom N5EGhttps://wiki.gnuradio.org/index.php?title=GNU_Radio_3.9_OOT_Module_Porting_Guide&diff=8699GNU Radio 3.9 OOT Module Porting Guide2021-06-15T18:31:17Z<p>Tom N5EG: /* Porting Guide */</p>
<hr />
<div>[[Category:3.9]]<br />
The major changes in the (in-progress) GNU Radio 3.9 release that will impact OOTs are:<br />
<br />
* C++ modernization (C++11/14?)<br />
* Replacement of SWIG with Pybind11<br />
* Cleanup of filter and fft APIs<br />
<br />
<br />
= Porting Guide = <br />
<br />
Porting from 3.8 to 3.9 can be achieved most simply by creating a new OOT module (with the same name as the 3.8 OOT but in a different directory), then performing some manual steps<br />
<br />
1. Use the 3.9 gr_modtool to generate a module with the same name (in another directory)<br />
<br />
2. Copy the python folder from 3.9 OOT into your 3.8 OOT<br />
<br />
3. (in 3.8 OOT) Add the bindings directory to the python directory CMakeLists<br />
./python/CMakeLists.txt → add the line: <br />
add_subdirectory(bindings)<br />
<br />
4. (in 3.8 OOT) Call gr_modtool bind for each block in your OOT<br />
NOTE: If you are doing more than just make function in your public header, e.g. setters/getters or other callback functions, be sure to have pygccxml set up<br />
<br />
5. c++ blocks that have python QA will need the import statements updated in that QA.<br />
<br />
6. python/bindings/python_bindings.cc needs to be updated for all C++ blocks (in two places!)<br />
<br />
7. Replace occurrences of boost::shared_ptr<blockname> with std::shared_ptr<blockname><br />
<br />
8. Though not necessary, boost::bind instances for message port handlers can be replaced with lambda functions for performance and simplicity as well as consistency with the in-tree modules<br />
<br />
* The instructions below are primarily intended for C++ projects. Python projects may be slightly different.<br />
<br />
9. Merge 3.8 /lib files (.cc, .h, etc) with the 3.9 lib file prototypes constructed by gr_modtool into 3.9 /lib.<br />
<br />
10. Copy the 3.8 /python/binding and /docstring files into 3.9 <br />
<br />
11. Merge the 3.8 /grc yml files with the 3.9 prototypes into 3.9/grc<br />
<br />
12. Merge the 3.9 /include files with the 3.9 prototypes into 3.9/include.<br />
<br />
13. Edit 3.9 /lib/CMakeLists.txt to add all the targets needed as specified in the 3.8 /lib/CMakeLists.txt file.<br />
<br />
14. bind will compare date/time stamps of the files. If the target bindings are out of date, manually rebind each 3.9 block with gr_modtool bind block.<br />
<br />
<br />
== Versioning Your shared object files ==<br />
You may want to edit the /lib/CMakeList.txt file in order to set a version. The default VERSION_PATCH is set<br />
to git, you may want to edit it to 0 for your first version. Then when you need to push out a modified version remember<br />
to edit the version numbers before building. If you leave the VERSION_PATCH at git the install directory may eventually become<br />
littered with old libgnuradio-yourproject.so.git-commit-number files and soft links.<br />
<br />
<br />
Porting from 3.7 to 3.9 should follow the 3.8 Porting Guide, but basically create a new OOT using 3.9 and add blocks from there, then copy in code.<br />
<br />
= Details = <br />
<br />
== C++ Modernization ==<br />
<br />
Boost shared pointers have been replaced with std:: shared pointers and memory management. At the top level of each block, the instantiation will need to change, e.g. <br />
<br />
In include/blockname_xx.h:<br />
<br />
<nowiki>typedef std::shared_ptr<blockname_xx> sptr;</nowiki><br />
<br />
Inbound message ports receive an update too. We move from <nowiki>boost::function</nowiki> to <nowiki>std::function</nowiki>. This affects how message handlers are registered. The preferred style is to use lambdas which is already compatible with GNU Radio 3.8:<br />
<br />
<nowiki>set_msg_handler(pmt::mp("message"), [this](pmt::pmt_t msg) { this->handle_msg(msg); });</nowiki><br />
<br />
== Pybind11 Python Bindings ==<br />
<br />
As of the GNU Radio 3.9 release, python bindings are handled using pybind11, which is inherently different than they were in previous releases<br />
<br />
=== Dependencies ===<br />
<br />
* pybind11 &gt; 2.4.3 [https://pybind11.readthedocs.io/ https://pybind11.readthedocs.io/]<br />
** pip does not provide the proper cmake (https://github.com/pybind/pybind11/issues/1379)<br />
** gnuradio 3.9 was built using pybind11 version 2.5.0. Your OOT should be built against that same version. (Version 2.4.3 will not work).<br />
** As of May 2021 Ubuntu 20.04 package manager references version 2.5.0:<br />
sudo apt install pybind11-dev<br />
<br />
** For some distributions (e.g. Ubuntu <20.04), this will need to be installed from source as 2.5.0 is not the supplied version with package managers<br />
<nowiki><br />
curl -Lo pybind11.tar.gz https://github.com/pybind/pybind11/archive/v2.5.0.tar.gz <br />
mkdir pybind11 && tar xzf pybind11.tar.gz -C pybind11 --strip-components=1 && cd pybind11<br />
mkdir build && cd build <br />
cmake .. -DCMAKE_BUILD_TYPE=Release -DPYBIND11_TEST=OFF <br />
make<br />
sudo make install </nowiki><br />
<br />
* pygccxml [https://pygccxml.readthedocs.io/en/develop/install.html https://pygccxml.readthedocs.io/en/develop/install.html]<br />
** This is an optional dependency and basic functionality for OOT generation can be performed without pygccxml<br />
** It is required for automatically generating bindings for most of the GR source tree<br />
<br />
=== Components ===<br />
<br />
Python bindings are contained in the <code>python/.../bindings</code> directory<br />
<br />
<source lang="sh">./python<br />
└── module_name<br />
├── bindings<br />
│ ├── blockname1_python.cc<br />
│ ├── blockname2_python.cc<br />
│ ├── CMakeLists.txt<br />
| ├── python_bindings.cc<br />
│ ├── docstrings<br />
│ │ ├── blockname1_pydoc_template.h<br />
│ │ ├── blockname1_pydoc_template.h<br />
</source><br />
The bindings for each block exist in blockname_python.cc under the <code>python/bindings</code> directory. Additionally, a template header file for each block that is used as a placeholder for the scraped docstrings lives in the <code>docstrings/</code> dir<br />
<br />
==== blockname_python.cc ====<br />
<br />
This is the class function enum variable bindings for everything that needs to be exposed through the Python API<br />
<br />
==== python_bindings.cc ====<br />
The structure of this file is<br />
<br />
<source lang="cpp">// Headers for binding functions<br />
/**************************************/<br />
/* The following comment block is used for<br />
/* gr_modtool to insert function prototypes<br />
/* Please do not delete<br />
/**************************************/<br />
// BINDING_FUNCTION_PROTOTYPES(<br />
<br />
void bind_blockname1(py::module&);<br />
<br />
// ) END BINDING_FUNCTION_PROTOTYPES<br />
<br />
<br />
PYBIND11_MODULE(module_name__python, m)<br />
{<br />
<br />
{<br />
// Initialize the numpy C API<br />
// (otherwise we will see segmentation faults)<br />
init_numpy();<br />
<br />
// Allow access to base block methods<br />
py::module::import("gnuradio.gr");<br />
/**************************************/<br />
/* The following comment block is used for<br />
/* gr_modtool to insert binding function calls<br />
/* Please do not delete<br />
/**************************************/<br />
// BINDING_FUNCTION_CALLS(<br />
bind_blockname1(m);<br />
// ) END BINDING_FUNCTION_CALLS<br />
}<br />
</source><br />
===== Comment Block =====<br />
<br />
Each block binding file contains an automatically generated and maintained comment block that informs CMake when the bindings are out of sync with the header file they refer to, and what to do about it<br />
<br />
<source lang="cpp">/***********************************************************************************/<br />
/* This file is automatically generated using bindtool and can be manually edited */<br />
/* The following lines can be configured to regenerate this file during cmake */<br />
/* If manual edits are made, the following tags should be modified accordingly. */<br />
/* BINDTOOL_GEN_AUTOMATIC(0) */<br />
/* BINDTOOL_USE_PYGCCXML(0) */<br />
/* BINDTOOL_HEADER_FILE(basic_block.h) */<br />
/* BINDTOOL_HEADER_FILE_HASH(549c06530e2afdf6f2c989017cb5f36e) */<br />
/***********************************************************************************/<br />
</source><br />
<code>BINDTOOL_GEN_AUTOMATIC</code>: Many times for complex in-tree blocks, the automated tools are not entirely sufficient to generate all of the bindings in an automated fashion. In this case, the flag should be set to 0, and the bindings need to be updated manually. If the flag is set to 1, CMake will override the binding file ''in the source tree'' when it detects out of sync bindings. This should only be done in simple cases.<br />
<br />
<code>BINDTOOL_USE_PYGCCXML</code>: Currently there are limitations on the amount of code generation that can be accomplished without the <code>pygccxml</code> dependency. If a block needs pygccxml for the bindings to be properly generated automatically, this should be set to <code>1</code><br />
<br />
<code>BINDTOOL_HEADER_FILE</code>: The header file that bindings are based on, filename only<br />
<br />
<code>BINDTOOL_HEADER_FILE_HASH</code>: The MD5 hash of the header file that the bindings were built on<br />
<br />
=== Workflow ===<br />
<br />
==== Out-of-Tree modules ====<br />
<br />
The steps for creating an out of tree module with pybind11 bindings are as follows:<br />
<br />
# Use <code>gr_modtool</code> to create an out of tree module and add blocks<br />
<br />
<source lang="sh">gr_modtool newmod foo<br />
gr_modtool add bar<br />
</source><br />
<ol start="2"><br />
<li>Update the parameters or functions in the public include file and rebind with <code>gr_modtool bind bar</code></li></ol><br />
<br />
'''NOTE''': without pygccxml, only the make function is currently accounted for, similar to <code>gr_modtool makeyaml</code><br />
<br />
If the public API changes, just call <code>gr_modtool bind [blockname]</code> to regenerate the bindings<br />
<br />
When the public header file for a block is changed, CMake will fail as it checks the hash of the header file compared to the hash stored in the bindings file until the bindings are updated<br />
<br />
<ol start="3"><br />
<li>Build and install</li></ol><br />
<br />
=== Docstrings ===<br />
<br />
If Doxygen is enabled in GNU Radio and/or the OOT, Docstrings are scraped from the header files, and placed in auto-generated <code>[blockname]_pydoc.h</code> files in the build directory on compile. Generated templates (via the binding steps described above) are placed in the <code>python/bindings/docstrings</code> directory and are used as placeholders for the scraped strings<br />
<br />
Upon compilation, docstrings are scraped from the module and stored in a dictionary (using <code>update_pydoc.py scrape</code>) and then the values are substituted in the template file (using <code>update_pydoc.py sub</code>)<br />
<br />
<br />
=== OOT Migration ===<br />
<br />
The easiest way to migrate an OOT to 3.9 is to use <code>gr_modtool</code> to create a new OOT, use <code>gr_modtool add</code> to create the blocks, and copy code from the previous OOT.<br />
<br />
Steps to do this without regenerating a new module are TBD<br />
<br />
=== Caveats ===<br />
<br />
Pybind11 bound methods do not implicitly convert int to enum, so blocks that take enum as input, must have either "raw" or "enum" in the grc yml definition of the block. "Raw" will allow the value to be changed by another variable in the flowgraph.<br />
<br />
Block inheritance must be specified completely in the python bindings in order to use the inherited methods. For instance, if a block inherits from sync_block, both block and basic_block must be included in the inheritance specification of the class:<br />
<br />
<nowiki> py::class_<atsc_interleaver,<br />
gr::sync_block,<br />
gr::block,<br />
gr::basic_block,<br />
std::shared_ptr<atsc_interleaver>>(<br />
m, "atsc_interleaver", D(atsc_interleaver)) </nowiki><br />
<br />
<br />
If your OOT module uses types from or its classes derive from another gr module, it is necessary in <code>python_bindings.cc</code> to specify these modules. <br />
<br />
For instance, since all OOT modules require the base block types, there is a line <code>py::module::import("gnuradio.gr");</code>.<br />
<br />
If you wanted to utilize <code>digital::constellation</code> objects in your OOT, it would be necessary to add <code>py::module::import("gnuradio.digital");</code> so that pybind knows to use the bindings already compiled into gnuradio.digital for the constellation objects<br />
<br />
If your OOT module uses other classes as parameter, you must either setup a python binding for this class or use an existing one. <br />
An example, how to setup a binding for the QWidget class of QT5 used in gr-qtgui, can be found here[https://github.com/gnuradio/gnuradio/blob/master/gr-qtgui/python/qtgui/bindings/QWidget_python.cc]<br />
<br />
If you want to use this binding in your OOT module, you have to import it. This has to be done in the python_bindings.cc inside the '''PYBIND11_MODULE''' macro.<br />
In case of the QWidget usage this would be <code>py::module::import("gnuradio.qtgui.qtgui_python");</code><br />
<br />
==== Using default values ====<br />
<br />
For standard types like int,float, etc. you can set default values as usual.<br />
<code>gr_modtool bind blockname</code> will setup the correct bindings in blockname_python.cc.<br />
<br />
But if you use something like <code> QWidget* parent = NULL </code> gr_modtool generates code like<br />
<code>py::arg("parent") = __null, </code><br />
<br />
But at this point pybind does not know the type of parent, so you have to modify the binding code and cast the type.<br />
<code>py::arg("parent") = (QWidget *) __null, </code><br />
<br />
Another way to come across this issue is to modify your header file from<br />
<code> QWidget* parent = NULL </code> to<br />
<code> QWidget* parent = nullptr </code><br />
<br />
pybind generates <br />
<code>py::arg("parent") = nullptr, </code><br />
<br />
which will be handled correctly<br />
=== Troubleshooting ===<br />
<br />
==== Unable to find pydoc.h ====<br />
<nowiki>fatal error: blockname_pydoc.h: No such file or directory<br />
28 | #include <blockname_pydoc.h><br />
| ^~~~~~~~~~~~~~~~~~~~~~~~~<br />
compilation terminated.</nowiki><br />
<br />
blockname_pydoc.h is generated during compilation based on the template in the docstring directory. When the block is first created in blocktool, this template does not exist. Run <code>gr_modtool bind</code> to generate the appropriate template used as a placeholder for the scraped docstrings<br />
<br />
Also, the scraping of docstrings only takes place at CMake time, so it may be necessary to do a <code>make clean</code> to re-trigger the scraping<br />
<br />
You can also try <br />
<nowiki>rm python/bindings/docstring_status</nowiki><br />
which will reset the docstring scraping target in cmake and re-copy the docstring templates<br />
<br />
==== TypeError: 'modulename_python.blockname' object is not subscriptable ====<br />
<br />
This is caused by an incomplete inheritance chain specified in the binding declaration of the block.<br />
<br />
Instead of<br />
<br />
py::class_<blockname,<br />
std::shared_ptr<blockname>>(m, "blockname", D(blockname))<br />
<br />
Try something like (taking into account your block type)<br />
<br />
py::class_<blockname,<br />
gr::sync_block,<br />
gr::block,<br />
gr::basic_block,<br />
std::shared_ptr<blockname>>(m, "blockname", D(blockname))<br />
<br />
==== Module is Empty ====<br />
<br />
This is usually caused by linker errors that prevent the binding module from being loaded. When you see something like:<br />
<br />
>>> import foo<br />
>>> dir(foo)<br />
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__']<br />
<br />
But you expect foo to have other C++ blocks, try loading the pybind module separately<br />
<br />
cd build/python/bindings<br />
python3<br />
>>> import foo_python<br />
[linker error should be evident here in mangled symbol name]</div>Tom N5EGhttps://wiki.gnuradio.org/index.php?title=GNU_Radio_3.9_OOT_Module_Porting_Guide&diff=8698GNU Radio 3.9 OOT Module Porting Guide2021-06-15T18:28:22Z<p>Tom N5EG: /* Porting Guide */</p>
<hr />
<div>[[Category:3.9]]<br />
The major changes in the (in-progress) GNU Radio 3.9 release that will impact OOTs are:<br />
<br />
* C++ modernization (C++11/14?)<br />
* Replacement of SWIG with Pybind11<br />
* Cleanup of filter and fft APIs<br />
<br />
<br />
= Porting Guide = <br />
<br />
Porting from 3.8 to 3.9 can be achieved most simply by creating a new OOT module (with the same name as the 3.8 OOT but in a different directory), then performing some manual steps<br />
<br />
1. Use the 3.9 gr_modtool to generate a module with the same name (in another directory)<br />
<br />
2. Copy the python folder from 3.9 OOT into your 3.8 OOT<br />
<br />
3. (in 3.8 OOT) Add the bindings directory to the python directory CMakeLists<br />
./python/CMakeLists.txt → add the line: <br />
add_subdirectory(bindings)<br />
<br />
4. (in 3.8 OOT) Call gr_modtool bind for each block in your OOT<br />
NOTE: If you are doing more than just make function in your public header, e.g. setters/getters or other callback functions, be sure to have pygccxml set up<br />
<br />
5. c++ blocks that have python QA will need the import statements updated in that QA.<br />
<br />
6. python/bindings/python_bindings.cc needs to be updated for all C++ blocks (in two places!)<br />
<br />
7. Replace occurrences of boost::shared_ptr<blockname> with std::shared_ptr<blockname><br />
<br />
8. Though not necessary, boost::bind instances for message port handlers can be replaced with lambda functions for performance and simplicity as well as consistency with the in-tree modules<br />
<br />
* The instructions below are primarily intended for C++ projects. Python projects may be slightly different.<br />
<br />
9. Merge 3.8 /lib files (.cc, .h, etc) with the 3.9 lib file prototypes constructed by gr_modtool into 3.9 /lib.<br />
<br />
10. Copy the 3.8 /python/binding and /docstring files into 3.9 <br />
<br />
11. Merge the 3.8 /grc yml files with the 3.9 prototypes into 3.9/grc<br />
<br />
12. Merge the 3.9 /include files with the 3.9 prototypes into 3.9/include.<br />
<br />
13. Edit 3.9 /lib/CMakeLists.txt to add all the targets needed as specified in the 3.8 /lib/CMakeLists.txt file.<br />
<br />
14. bind will compare date/time stamps of the files. If the target bindings are out of date, manually rebind each 3.9 block with gr_modtool bind block.<br />
<br />
'''NOTE''': you may want to edit the /lib/CMakeList.txt file in order to set a version. The default VERSION_PATCH is set<br />
to git, you may want to edit it to 0 for your first version. Then if you need to push out a modified version, remember<br />
to edit these version numbers before building. If you leave the VERSION_PATCH at git the install directory may eventually become<br />
littered with old libgnuradio-yourproject.so.git-commit-number files and soft links.<br />
<br />
Porting from 3.7 to 3.9 should follow the 3.8 Porting Guide, but basically create a new OOT using 3.9 and add blocks from there, then copy in code.<br />
<br />
= Details = <br />
<br />
== C++ Modernization ==<br />
<br />
Boost shared pointers have been replaced with std:: shared pointers and memory management. At the top level of each block, the instantiation will need to change, e.g. <br />
<br />
In include/blockname_xx.h:<br />
<br />
<nowiki>typedef std::shared_ptr<blockname_xx> sptr;</nowiki><br />
<br />
Inbound message ports receive an update too. We move from <nowiki>boost::function</nowiki> to <nowiki>std::function</nowiki>. This affects how message handlers are registered. The preferred style is to use lambdas which is already compatible with GNU Radio 3.8:<br />
<br />
<nowiki>set_msg_handler(pmt::mp("message"), [this](pmt::pmt_t msg) { this->handle_msg(msg); });</nowiki><br />
<br />
== Pybind11 Python Bindings ==<br />
<br />
As of the GNU Radio 3.9 release, python bindings are handled using pybind11, which is inherently different than they were in previous releases<br />
<br />
=== Dependencies ===<br />
<br />
* pybind11 &gt; 2.4.3 [https://pybind11.readthedocs.io/ https://pybind11.readthedocs.io/]<br />
** pip does not provide the proper cmake (https://github.com/pybind/pybind11/issues/1379)<br />
** gnuradio 3.9 was built using pybind11 version 2.5.0. Your OOT should be built against that same version. (Version 2.4.3 will not work).<br />
** As of May 2021 Ubuntu 20.04 package manager references version 2.5.0:<br />
sudo apt install pybind11-dev<br />
<br />
** For some distributions (e.g. Ubuntu <20.04), this will need to be installed from source as 2.5.0 is not the supplied version with package managers<br />
<nowiki><br />
curl -Lo pybind11.tar.gz https://github.com/pybind/pybind11/archive/v2.5.0.tar.gz <br />
mkdir pybind11 && tar xzf pybind11.tar.gz -C pybind11 --strip-components=1 && cd pybind11<br />
mkdir build && cd build <br />
cmake .. -DCMAKE_BUILD_TYPE=Release -DPYBIND11_TEST=OFF <br />
make<br />
sudo make install </nowiki><br />
<br />
* pygccxml [https://pygccxml.readthedocs.io/en/develop/install.html https://pygccxml.readthedocs.io/en/develop/install.html]<br />
** This is an optional dependency and basic functionality for OOT generation can be performed without pygccxml<br />
** It is required for automatically generating bindings for most of the GR source tree<br />
<br />
=== Components ===<br />
<br />
Python bindings are contained in the <code>python/.../bindings</code> directory<br />
<br />
<source lang="sh">./python<br />
└── module_name<br />
├── bindings<br />
│ ├── blockname1_python.cc<br />
│ ├── blockname2_python.cc<br />
│ ├── CMakeLists.txt<br />
| ├── python_bindings.cc<br />
│ ├── docstrings<br />
│ │ ├── blockname1_pydoc_template.h<br />
│ │ ├── blockname1_pydoc_template.h<br />
</source><br />
The bindings for each block exist in blockname_python.cc under the <code>python/bindings</code> directory. Additionally, a template header file for each block that is used as a placeholder for the scraped docstrings lives in the <code>docstrings/</code> dir<br />
<br />
==== blockname_python.cc ====<br />
<br />
This is the class function enum variable bindings for everything that needs to be exposed through the Python API<br />
<br />
==== python_bindings.cc ====<br />
The structure of this file is<br />
<br />
<source lang="cpp">// Headers for binding functions<br />
/**************************************/<br />
/* The following comment block is used for<br />
/* gr_modtool to insert function prototypes<br />
/* Please do not delete<br />
/**************************************/<br />
// BINDING_FUNCTION_PROTOTYPES(<br />
<br />
void bind_blockname1(py::module&);<br />
<br />
// ) END BINDING_FUNCTION_PROTOTYPES<br />
<br />
<br />
PYBIND11_MODULE(module_name__python, m)<br />
{<br />
<br />
{<br />
// Initialize the numpy C API<br />
// (otherwise we will see segmentation faults)<br />
init_numpy();<br />
<br />
// Allow access to base block methods<br />
py::module::import("gnuradio.gr");<br />
/**************************************/<br />
/* The following comment block is used for<br />
/* gr_modtool to insert binding function calls<br />
/* Please do not delete<br />
/**************************************/<br />
// BINDING_FUNCTION_CALLS(<br />
bind_blockname1(m);<br />
// ) END BINDING_FUNCTION_CALLS<br />
}<br />
</source><br />
===== Comment Block =====<br />
<br />
Each block binding file contains an automatically generated and maintained comment block that informs CMake when the bindings are out of sync with the header file they refer to, and what to do about it<br />
<br />
<source lang="cpp">/***********************************************************************************/<br />
/* This file is automatically generated using bindtool and can be manually edited */<br />
/* The following lines can be configured to regenerate this file during cmake */<br />
/* If manual edits are made, the following tags should be modified accordingly. */<br />
/* BINDTOOL_GEN_AUTOMATIC(0) */<br />
/* BINDTOOL_USE_PYGCCXML(0) */<br />
/* BINDTOOL_HEADER_FILE(basic_block.h) */<br />
/* BINDTOOL_HEADER_FILE_HASH(549c06530e2afdf6f2c989017cb5f36e) */<br />
/***********************************************************************************/<br />
</source><br />
<code>BINDTOOL_GEN_AUTOMATIC</code>: Many times for complex in-tree blocks, the automated tools are not entirely sufficient to generate all of the bindings in an automated fashion. In this case, the flag should be set to 0, and the bindings need to be updated manually. If the flag is set to 1, CMake will override the binding file ''in the source tree'' when it detects out of sync bindings. This should only be done in simple cases.<br />
<br />
<code>BINDTOOL_USE_PYGCCXML</code>: Currently there are limitations on the amount of code generation that can be accomplished without the <code>pygccxml</code> dependency. If a block needs pygccxml for the bindings to be properly generated automatically, this should be set to <code>1</code><br />
<br />
<code>BINDTOOL_HEADER_FILE</code>: The header file that bindings are based on, filename only<br />
<br />
<code>BINDTOOL_HEADER_FILE_HASH</code>: The MD5 hash of the header file that the bindings were built on<br />
<br />
=== Workflow ===<br />
<br />
==== Out-of-Tree modules ====<br />
<br />
The steps for creating an out of tree module with pybind11 bindings are as follows:<br />
<br />
# Use <code>gr_modtool</code> to create an out of tree module and add blocks<br />
<br />
<source lang="sh">gr_modtool newmod foo<br />
gr_modtool add bar<br />
</source><br />
<ol start="2"><br />
<li>Update the parameters or functions in the public include file and rebind with <code>gr_modtool bind bar</code></li></ol><br />
<br />
'''NOTE''': without pygccxml, only the make function is currently accounted for, similar to <code>gr_modtool makeyaml</code><br />
<br />
If the public API changes, just call <code>gr_modtool bind [blockname]</code> to regenerate the bindings<br />
<br />
When the public header file for a block is changed, CMake will fail as it checks the hash of the header file compared to the hash stored in the bindings file until the bindings are updated<br />
<br />
<ol start="3"><br />
<li>Build and install</li></ol><br />
<br />
=== Docstrings ===<br />
<br />
If Doxygen is enabled in GNU Radio and/or the OOT, Docstrings are scraped from the header files, and placed in auto-generated <code>[blockname]_pydoc.h</code> files in the build directory on compile. Generated templates (via the binding steps described above) are placed in the <code>python/bindings/docstrings</code> directory and are used as placeholders for the scraped strings<br />
<br />
Upon compilation, docstrings are scraped from the module and stored in a dictionary (using <code>update_pydoc.py scrape</code>) and then the values are substituted in the template file (using <code>update_pydoc.py sub</code>)<br />
<br />
<br />
=== OOT Migration ===<br />
<br />
The easiest way to migrate an OOT to 3.9 is to use <code>gr_modtool</code> to create a new OOT, use <code>gr_modtool add</code> to create the blocks, and copy code from the previous OOT.<br />
<br />
Steps to do this without regenerating a new module are TBD<br />
<br />
=== Caveats ===<br />
<br />
Pybind11 bound methods do not implicitly convert int to enum, so blocks that take enum as input, must have either "raw" or "enum" in the grc yml definition of the block. "Raw" will allow the value to be changed by another variable in the flowgraph.<br />
<br />
Block inheritance must be specified completely in the python bindings in order to use the inherited methods. For instance, if a block inherits from sync_block, both block and basic_block must be included in the inheritance specification of the class:<br />
<br />
<nowiki> py::class_<atsc_interleaver,<br />
gr::sync_block,<br />
gr::block,<br />
gr::basic_block,<br />
std::shared_ptr<atsc_interleaver>>(<br />
m, "atsc_interleaver", D(atsc_interleaver)) </nowiki><br />
<br />
<br />
If your OOT module uses types from or its classes derive from another gr module, it is necessary in <code>python_bindings.cc</code> to specify these modules. <br />
<br />
For instance, since all OOT modules require the base block types, there is a line <code>py::module::import("gnuradio.gr");</code>.<br />
<br />
If you wanted to utilize <code>digital::constellation</code> objects in your OOT, it would be necessary to add <code>py::module::import("gnuradio.digital");</code> so that pybind knows to use the bindings already compiled into gnuradio.digital for the constellation objects<br />
<br />
If your OOT module uses other classes as parameter, you must either setup a python binding for this class or use an existing one. <br />
An example, how to setup a binding for the QWidget class of QT5 used in gr-qtgui, can be found here[https://github.com/gnuradio/gnuradio/blob/master/gr-qtgui/python/qtgui/bindings/QWidget_python.cc]<br />
<br />
If you want to use this binding in your OOT module, you have to import it. This has to be done in the python_bindings.cc inside the '''PYBIND11_MODULE''' macro.<br />
In case of the QWidget usage this would be <code>py::module::import("gnuradio.qtgui.qtgui_python");</code><br />
<br />
==== Using default values ====<br />
<br />
For standard types like int,float, etc. you can set default values as usual.<br />
<code>gr_modtool bind blockname</code> will setup the correct bindings in blockname_python.cc.<br />
<br />
But if you use something like <code> QWidget* parent = NULL </code> gr_modtool generates code like<br />
<code>py::arg("parent") = __null, </code><br />
<br />
But at this point pybind does not know the type of parent, so you have to modify the binding code and cast the type.<br />
<code>py::arg("parent") = (QWidget *) __null, </code><br />
<br />
Another way to come across this issue is to modify your header file from<br />
<code> QWidget* parent = NULL </code> to<br />
<code> QWidget* parent = nullptr </code><br />
<br />
pybind generates <br />
<code>py::arg("parent") = nullptr, </code><br />
<br />
which will be handled correctly<br />
=== Troubleshooting ===<br />
<br />
==== Unable to find pydoc.h ====<br />
<nowiki>fatal error: blockname_pydoc.h: No such file or directory<br />
28 | #include <blockname_pydoc.h><br />
| ^~~~~~~~~~~~~~~~~~~~~~~~~<br />
compilation terminated.</nowiki><br />
<br />
blockname_pydoc.h is generated during compilation based on the template in the docstring directory. When the block is first created in blocktool, this template does not exist. Run <code>gr_modtool bind</code> to generate the appropriate template used as a placeholder for the scraped docstrings<br />
<br />
Also, the scraping of docstrings only takes place at CMake time, so it may be necessary to do a <code>make clean</code> to re-trigger the scraping<br />
<br />
You can also try <br />
<nowiki>rm python/bindings/docstring_status</nowiki><br />
which will reset the docstring scraping target in cmake and re-copy the docstring templates<br />
<br />
==== TypeError: 'modulename_python.blockname' object is not subscriptable ====<br />
<br />
This is caused by an incomplete inheritance chain specified in the binding declaration of the block.<br />
<br />
Instead of<br />
<br />
py::class_<blockname,<br />
std::shared_ptr<blockname>>(m, "blockname", D(blockname))<br />
<br />
Try something like (taking into account your block type)<br />
<br />
py::class_<blockname,<br />
gr::sync_block,<br />
gr::block,<br />
gr::basic_block,<br />
std::shared_ptr<blockname>>(m, "blockname", D(blockname))<br />
<br />
==== Module is Empty ====<br />
<br />
This is usually caused by linker errors that prevent the binding module from being loaded. When you see something like:<br />
<br />
>>> import foo<br />
>>> dir(foo)<br />
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__']<br />
<br />
But you expect foo to have other C++ blocks, try loading the pybind module separately<br />
<br />
cd build/python/bindings<br />
python3<br />
>>> import foo_python<br />
[linker error should be evident here in mangled symbol name]</div>Tom N5EGhttps://wiki.gnuradio.org/index.php?title=GNU_Radio_3.9_OOT_Module_Porting_Guide&diff=8697GNU Radio 3.9 OOT Module Porting Guide2021-06-15T18:27:44Z<p>Tom N5EG: /* Porting Guide */</p>
<hr />
<div>[[Category:3.9]]<br />
The major changes in the (in-progress) GNU Radio 3.9 release that will impact OOTs are:<br />
<br />
* C++ modernization (C++11/14?)<br />
* Replacement of SWIG with Pybind11<br />
* Cleanup of filter and fft APIs<br />
<br />
<br />
= Porting Guide = <br />
<br />
Porting from 3.8 to 3.9 can be achieved most simply by creating a new OOT module (with the same name as the 3.8 OOT but in a different directory), then performing some manual steps<br />
<br />
1. Use the 3.9 gr_modtool to generate a module with the same name (in another directory)<br />
<br />
2. Copy the python folder from 3.9 OOT into your 3.8 OOT<br />
<br />
3. (in 3.8 OOT) Add the bindings directory to the python directory CMakeLists<br />
./python/CMakeLists.txt → add the line: <br />
add_subdirectory(bindings)<br />
<br />
4. (in 3.8 OOT) Call gr_modtool bind for each block in your OOT<br />
NOTE: If you are doing more than just make function in your public header, e.g. setters/getters or other callback functions, be sure to have pygccxml set up<br />
<br />
5. c++ blocks that have python QA will need the import statements updated in that QA.<br />
<br />
6. python/bindings/python_bindings.cc needs to be updated for all C++ blocks (in two places!)<br />
<br />
7. Replace occurrences of boost::shared_ptr<blockname> with std::shared_ptr<blockname><br />
<br />
8. Though not necessary, boost::bind instances for message port handlers can be replaced with lambda functions for performance and simplicity as well as consistency with the in-tree modules<br />
<br />
* The instructions below are primarily intended for C++ projects. Python projects may be slightly different.<br />
<br />
9. Merge 3.8 /lib files (.cc, .h, etc) with the 3.9 lib file prototypes constructed by gr_modtool into 3.9 /lib.<br />
<br />
10. Copy the 3.8 /python/binding and /docstring files into 3.9 <br />
<br />
11. Merge the 3.8 /grc yml files with the 3.9 prototypes into 3.9/grc<br />
<br />
12. Merge the 3.9 /include files with the 3.9 prototypes into 3.9/include.<br />
<br />
13. Edit 3.9 /lib/CMakeLists.txt to add all the targets needed as specified in the 3.8 /lib/CMakeLists.txt file.<br />
<br />
14. bind will compare date/time stamps of the files. If the target bindings are out of date, manually rebind each 3.9 block with gr_modtool bind block.<br />
<br />
NOTE: you may want to edit the /lib/CMakeList.txt file in order to set a version. The default VERSION_PATCH is set<br />
to git, you may want to edit it to 0 for your first version. Then if you need to push out a modified version, remember<br />
to edit these version numbers before building. If you leave the VERSION_PATCH at git the install directory may eventually become<br />
littered with old libgnuradio-yourproject.so.git-commit-number files and soft links.<br />
<br />
Porting from 3.7 to 3.9 should follow the 3.8 Porting Guide, but basically create a new OOT using 3.9 and add blocks from there, then copy in code.<br />
<br />
= Details = <br />
<br />
== C++ Modernization ==<br />
<br />
Boost shared pointers have been replaced with std:: shared pointers and memory management. At the top level of each block, the instantiation will need to change, e.g. <br />
<br />
In include/blockname_xx.h:<br />
<br />
<nowiki>typedef std::shared_ptr<blockname_xx> sptr;</nowiki><br />
<br />
Inbound message ports receive an update too. We move from <nowiki>boost::function</nowiki> to <nowiki>std::function</nowiki>. This affects how message handlers are registered. The preferred style is to use lambdas which is already compatible with GNU Radio 3.8:<br />
<br />
<nowiki>set_msg_handler(pmt::mp("message"), [this](pmt::pmt_t msg) { this->handle_msg(msg); });</nowiki><br />
<br />
== Pybind11 Python Bindings ==<br />
<br />
As of the GNU Radio 3.9 release, python bindings are handled using pybind11, which is inherently different than they were in previous releases<br />
<br />
=== Dependencies ===<br />
<br />
* pybind11 &gt; 2.4.3 [https://pybind11.readthedocs.io/ https://pybind11.readthedocs.io/]<br />
** pip does not provide the proper cmake (https://github.com/pybind/pybind11/issues/1379)<br />
** gnuradio 3.9 was built using pybind11 version 2.5.0. Your OOT should be built against that same version. (Version 2.4.3 will not work).<br />
** As of May 2021 Ubuntu 20.04 package manager references version 2.5.0:<br />
sudo apt install pybind11-dev<br />
<br />
** For some distributions (e.g. Ubuntu <20.04), this will need to be installed from source as 2.5.0 is not the supplied version with package managers<br />
<nowiki><br />
curl -Lo pybind11.tar.gz https://github.com/pybind/pybind11/archive/v2.5.0.tar.gz <br />
mkdir pybind11 && tar xzf pybind11.tar.gz -C pybind11 --strip-components=1 && cd pybind11<br />
mkdir build && cd build <br />
cmake .. -DCMAKE_BUILD_TYPE=Release -DPYBIND11_TEST=OFF <br />
make<br />
sudo make install </nowiki><br />
<br />
* pygccxml [https://pygccxml.readthedocs.io/en/develop/install.html https://pygccxml.readthedocs.io/en/develop/install.html]<br />
** This is an optional dependency and basic functionality for OOT generation can be performed without pygccxml<br />
** It is required for automatically generating bindings for most of the GR source tree<br />
<br />
=== Components ===<br />
<br />
Python bindings are contained in the <code>python/.../bindings</code> directory<br />
<br />
<source lang="sh">./python<br />
└── module_name<br />
├── bindings<br />
│ ├── blockname1_python.cc<br />
│ ├── blockname2_python.cc<br />
│ ├── CMakeLists.txt<br />
| ├── python_bindings.cc<br />
│ ├── docstrings<br />
│ │ ├── blockname1_pydoc_template.h<br />
│ │ ├── blockname1_pydoc_template.h<br />
</source><br />
The bindings for each block exist in blockname_python.cc under the <code>python/bindings</code> directory. Additionally, a template header file for each block that is used as a placeholder for the scraped docstrings lives in the <code>docstrings/</code> dir<br />
<br />
==== blockname_python.cc ====<br />
<br />
This is the class function enum variable bindings for everything that needs to be exposed through the Python API<br />
<br />
==== python_bindings.cc ====<br />
The structure of this file is<br />
<br />
<source lang="cpp">// Headers for binding functions<br />
/**************************************/<br />
/* The following comment block is used for<br />
/* gr_modtool to insert function prototypes<br />
/* Please do not delete<br />
/**************************************/<br />
// BINDING_FUNCTION_PROTOTYPES(<br />
<br />
void bind_blockname1(py::module&);<br />
<br />
// ) END BINDING_FUNCTION_PROTOTYPES<br />
<br />
<br />
PYBIND11_MODULE(module_name__python, m)<br />
{<br />
<br />
{<br />
// Initialize the numpy C API<br />
// (otherwise we will see segmentation faults)<br />
init_numpy();<br />
<br />
// Allow access to base block methods<br />
py::module::import("gnuradio.gr");<br />
/**************************************/<br />
/* The following comment block is used for<br />
/* gr_modtool to insert binding function calls<br />
/* Please do not delete<br />
/**************************************/<br />
// BINDING_FUNCTION_CALLS(<br />
bind_blockname1(m);<br />
// ) END BINDING_FUNCTION_CALLS<br />
}<br />
</source><br />
===== Comment Block =====<br />
<br />
Each block binding file contains an automatically generated and maintained comment block that informs CMake when the bindings are out of sync with the header file they refer to, and what to do about it<br />
<br />
<source lang="cpp">/***********************************************************************************/<br />
/* This file is automatically generated using bindtool and can be manually edited */<br />
/* The following lines can be configured to regenerate this file during cmake */<br />
/* If manual edits are made, the following tags should be modified accordingly. */<br />
/* BINDTOOL_GEN_AUTOMATIC(0) */<br />
/* BINDTOOL_USE_PYGCCXML(0) */<br />
/* BINDTOOL_HEADER_FILE(basic_block.h) */<br />
/* BINDTOOL_HEADER_FILE_HASH(549c06530e2afdf6f2c989017cb5f36e) */<br />
/***********************************************************************************/<br />
</source><br />
<code>BINDTOOL_GEN_AUTOMATIC</code>: Many times for complex in-tree blocks, the automated tools are not entirely sufficient to generate all of the bindings in an automated fashion. In this case, the flag should be set to 0, and the bindings need to be updated manually. If the flag is set to 1, CMake will override the binding file ''in the source tree'' when it detects out of sync bindings. This should only be done in simple cases.<br />
<br />
<code>BINDTOOL_USE_PYGCCXML</code>: Currently there are limitations on the amount of code generation that can be accomplished without the <code>pygccxml</code> dependency. If a block needs pygccxml for the bindings to be properly generated automatically, this should be set to <code>1</code><br />
<br />
<code>BINDTOOL_HEADER_FILE</code>: The header file that bindings are based on, filename only<br />
<br />
<code>BINDTOOL_HEADER_FILE_HASH</code>: The MD5 hash of the header file that the bindings were built on<br />
<br />
=== Workflow ===<br />
<br />
==== Out-of-Tree modules ====<br />
<br />
The steps for creating an out of tree module with pybind11 bindings are as follows:<br />
<br />
# Use <code>gr_modtool</code> to create an out of tree module and add blocks<br />
<br />
<source lang="sh">gr_modtool newmod foo<br />
gr_modtool add bar<br />
</source><br />
<ol start="2"><br />
<li>Update the parameters or functions in the public include file and rebind with <code>gr_modtool bind bar</code></li></ol><br />
<br />
'''NOTE''': without pygccxml, only the make function is currently accounted for, similar to <code>gr_modtool makeyaml</code><br />
<br />
If the public API changes, just call <code>gr_modtool bind [blockname]</code> to regenerate the bindings<br />
<br />
When the public header file for a block is changed, CMake will fail as it checks the hash of the header file compared to the hash stored in the bindings file until the bindings are updated<br />
<br />
<ol start="3"><br />
<li>Build and install</li></ol><br />
<br />
=== Docstrings ===<br />
<br />
If Doxygen is enabled in GNU Radio and/or the OOT, Docstrings are scraped from the header files, and placed in auto-generated <code>[blockname]_pydoc.h</code> files in the build directory on compile. Generated templates (via the binding steps described above) are placed in the <code>python/bindings/docstrings</code> directory and are used as placeholders for the scraped strings<br />
<br />
Upon compilation, docstrings are scraped from the module and stored in a dictionary (using <code>update_pydoc.py scrape</code>) and then the values are substituted in the template file (using <code>update_pydoc.py sub</code>)<br />
<br />
<br />
=== OOT Migration ===<br />
<br />
The easiest way to migrate an OOT to 3.9 is to use <code>gr_modtool</code> to create a new OOT, use <code>gr_modtool add</code> to create the blocks, and copy code from the previous OOT.<br />
<br />
Steps to do this without regenerating a new module are TBD<br />
<br />
=== Caveats ===<br />
<br />
Pybind11 bound methods do not implicitly convert int to enum, so blocks that take enum as input, must have either "raw" or "enum" in the grc yml definition of the block. "Raw" will allow the value to be changed by another variable in the flowgraph.<br />
<br />
Block inheritance must be specified completely in the python bindings in order to use the inherited methods. For instance, if a block inherits from sync_block, both block and basic_block must be included in the inheritance specification of the class:<br />
<br />
<nowiki> py::class_<atsc_interleaver,<br />
gr::sync_block,<br />
gr::block,<br />
gr::basic_block,<br />
std::shared_ptr<atsc_interleaver>>(<br />
m, "atsc_interleaver", D(atsc_interleaver)) </nowiki><br />
<br />
<br />
If your OOT module uses types from or its classes derive from another gr module, it is necessary in <code>python_bindings.cc</code> to specify these modules. <br />
<br />
For instance, since all OOT modules require the base block types, there is a line <code>py::module::import("gnuradio.gr");</code>.<br />
<br />
If you wanted to utilize <code>digital::constellation</code> objects in your OOT, it would be necessary to add <code>py::module::import("gnuradio.digital");</code> so that pybind knows to use the bindings already compiled into gnuradio.digital for the constellation objects<br />
<br />
If your OOT module uses other classes as parameter, you must either setup a python binding for this class or use an existing one. <br />
An example, how to setup a binding for the QWidget class of QT5 used in gr-qtgui, can be found here[https://github.com/gnuradio/gnuradio/blob/master/gr-qtgui/python/qtgui/bindings/QWidget_python.cc]<br />
<br />
If you want to use this binding in your OOT module, you have to import it. This has to be done in the python_bindings.cc inside the '''PYBIND11_MODULE''' macro.<br />
In case of the QWidget usage this would be <code>py::module::import("gnuradio.qtgui.qtgui_python");</code><br />
<br />
==== Using default values ====<br />
<br />
For standard types like int,float, etc. you can set default values as usual.<br />
<code>gr_modtool bind blockname</code> will setup the correct bindings in blockname_python.cc.<br />
<br />
But if you use something like <code> QWidget* parent = NULL </code> gr_modtool generates code like<br />
<code>py::arg("parent") = __null, </code><br />
<br />
But at this point pybind does not know the type of parent, so you have to modify the binding code and cast the type.<br />
<code>py::arg("parent") = (QWidget *) __null, </code><br />
<br />
Another way to come across this issue is to modify your header file from<br />
<code> QWidget* parent = NULL </code> to<br />
<code> QWidget* parent = nullptr </code><br />
<br />
pybind generates <br />
<code>py::arg("parent") = nullptr, </code><br />
<br />
which will be handled correctly<br />
=== Troubleshooting ===<br />
<br />
==== Unable to find pydoc.h ====<br />
<nowiki>fatal error: blockname_pydoc.h: No such file or directory<br />
28 | #include <blockname_pydoc.h><br />
| ^~~~~~~~~~~~~~~~~~~~~~~~~<br />
compilation terminated.</nowiki><br />
<br />
blockname_pydoc.h is generated during compilation based on the template in the docstring directory. When the block is first created in blocktool, this template does not exist. Run <code>gr_modtool bind</code> to generate the appropriate template used as a placeholder for the scraped docstrings<br />
<br />
Also, the scraping of docstrings only takes place at CMake time, so it may be necessary to do a <code>make clean</code> to re-trigger the scraping<br />
<br />
You can also try <br />
<nowiki>rm python/bindings/docstring_status</nowiki><br />
which will reset the docstring scraping target in cmake and re-copy the docstring templates<br />
<br />
==== TypeError: 'modulename_python.blockname' object is not subscriptable ====<br />
<br />
This is caused by an incomplete inheritance chain specified in the binding declaration of the block.<br />
<br />
Instead of<br />
<br />
py::class_<blockname,<br />
std::shared_ptr<blockname>>(m, "blockname", D(blockname))<br />
<br />
Try something like (taking into account your block type)<br />
<br />
py::class_<blockname,<br />
gr::sync_block,<br />
gr::block,<br />
gr::basic_block,<br />
std::shared_ptr<blockname>>(m, "blockname", D(blockname))<br />
<br />
==== Module is Empty ====<br />
<br />
This is usually caused by linker errors that prevent the binding module from being loaded. When you see something like:<br />
<br />
>>> import foo<br />
>>> dir(foo)<br />
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__']<br />
<br />
But you expect foo to have other C++ blocks, try loading the pybind module separately<br />
<br />
cd build/python/bindings<br />
python3<br />
>>> import foo_python<br />
[linker error should be evident here in mangled symbol name]</div>Tom N5EGhttps://wiki.gnuradio.org/index.php?title=GNU_Radio_3.9_OOT_Module_Porting_Guide&diff=8598GNU Radio 3.9 OOT Module Porting Guide2021-05-22T16:55:19Z<p>Tom N5EG: /* Pybind11 Python Bindings */</p>
<hr />
<div>[[Category:3.9]]<br />
The major changes in the (in-progress) GNU Radio 3.9 release that will impact OOTs are:<br />
<br />
* C++ modernization (C++11/14?)<br />
* Replacement of SWIG with Pybind11<br />
* Cleanup of filter and fft APIs<br />
<br />
<br />
= Porting Guide = <br />
<br />
Porting from 3.8 to 3.9 can be achieved most simply by creating a new OOT module (with the same name as the 3.8 OOT but in a different directory), then performing some manual steps<br />
<br />
1. Use the 3.9 gr_modtool to generate a module with the same name (in another directory)<br />
<br />
2. Copy the python folder from 3.9 OOT into your 3.8 OOT<br />
<br />
3. (in 3.8 OOT) Add the bindings directory to the python directory CMakeLists<br />
./python/CMakeLists.txt → add the line: <br />
add_subdirectory(bindings)<br />
<br />
4. (in 3.8 OOT) Call gr_modtool bind for each block in your OOT<br />
NOTE: If you are doing more than just make function in your public header, e.g. setters/getters or other callback functions, be sure to have pygccxml set up<br />
<br />
5. c++ blocks that have python QA will need the import statements updated in that QA.<br />
<br />
6. python/bindings/python_bindings.cc needs to be updated for all C++ blocks (in two places!)<br />
<br />
7. Replace occurrences of boost::shared_ptr<blockname> with std::shared_ptr<blockname><br />
<br />
8. Though not necessary, boost::bind instances for message port handlers can be replaced with lambda functions for performance and simplicity as well as consistency with the in-tree modules<br />
<br />
* The instructions below are primarily intended for C++ projects. Python projects may be slightly different.<br />
<br />
9. Merge 3.8 /lib files (.cc, .h, etc) with the 3.9 lib file prototypes constructed by gr_modtool into 3.9 /lib.<br />
<br />
10. Copy the 3.8 /python/binding and /docstring files into 3.9 <br />
<br />
11. Merge the 3.8 /grc yml files with the 3.9 prototypes into 3.9/grc<br />
<br />
12. Merge the 3.9 /include files with the 3.9 prototypes into 3.9/include.<br />
<br />
13. Edit 3.9 /lib/CMakeLists.txt to add all the targets needed as specified in the 3.8 /lib/CMakeLists.txt file.<br />
<br />
14. bind will compare date/time stamps of the files. If the target bindings are out of date, manually rebind each 3.9 block with gr_modtool bind block.<br />
<br />
<br />
<br />
Porting from 3.7 to 3.9 should follow the 3.8 Porting Guide, but basically create a new OOT using 3.9 and add blocks from there, then copy in code.<br />
<br />
= Details = <br />
<br />
== C++ Modernization ==<br />
<br />
Boost shared pointers have been replaced with std:: shared pointers and memory management. At the top level of each block, the instantiation will need to change, e.g. <br />
<br />
In include/blockname_xx.h:<br />
<br />
<nowiki>typedef std::shared_ptr<blockname_xx> sptr;</nowiki><br />
<br />
Inbound message ports receive an update too. We move from <nowiki>boost::function</nowiki> to <nowiki>std::function</nowiki>. This affects how message handlers are registered. The preferred style is to use lambdas which is already compatible with GNU Radio 3.8:<br />
<br />
<nowiki>set_msg_handler(pmt::mp("message"), [this](pmt::pmt_t msg) { this->handle_msg(msg); });</nowiki><br />
<br />
== Pybind11 Python Bindings ==<br />
<br />
As of the GNU Radio 3.9 release, python bindings are handled using pybind11, which is inherently different than they were in previous releases<br />
<br />
=== Dependencies ===<br />
<br />
* pybind11 &gt; 2.4.3 [https://pybind11.readthedocs.io/ https://pybind11.readthedocs.io/]<br />
** pip does not provide the proper cmake (https://github.com/pybind/pybind11/issues/1379)<br />
** gnuradio 3.9 was built using pybind11 version 2.5.0. Your OOT should be built against that same version. (Version 2.4.3 will not work).<br />
** As of May 2021 Ubuntu 20.04 package manager references version 2.5.0:<br />
sudo apt install pybind11-dev<br />
<br />
** For some distributions (e.g. Ubuntu <20.04), this will need to be installed from source as 2.5.0 is not the supplied version with package managers<br />
<nowiki><br />
curl -Lo pybind11.tar.gz https://github.com/pybind/pybind11/archive/v2.5.0.tar.gz <br />
mkdir pybind11 && tar xzf pybind11.tar.gz -C pybind11 --strip-components=1 && cd pybind11<br />
mkdir build && cd build <br />
cmake .. -DCMAKE_BUILD_TYPE=Release -DPYBIND11_TEST=OFF <br />
make<br />
sudo make install </nowiki><br />
<br />
* pygccxml [https://pygccxml.readthedocs.io/en/develop/install.html https://pygccxml.readthedocs.io/en/develop/install.html]<br />
** This is an optional dependency and basic functionality for OOT generation can be performed without pygccxml<br />
** It is required for automatically generating bindings for most of the GR source tree<br />
<br />
=== Components ===<br />
<br />
Python bindings are contained in the <code>python/.../bindings</code> directory<br />
<br />
<source lang="sh">./python<br />
└── module_name<br />
├── bindings<br />
│ ├── blockname1_python.cc<br />
│ ├── blockname2_python.cc<br />
│ ├── CMakeLists.txt<br />
| ├── python_bindings.cc<br />
│ ├── docstrings<br />
│ │ ├── blockname1_pydoc_template.h<br />
│ │ ├── blockname1_pydoc_template.h<br />
</source><br />
The bindings for each block exist in blockname_python.cc under the <code>python/bindings</code> directory. Additionally, a template header file for each block that is used as a placeholder for the scraped docstrings lives in the <code>docstrings/</code> dir<br />
<br />
==== blockname_python.cc ====<br />
<br />
This is the class function enum variable bindings for everything that needs to be exposed through the Python API<br />
<br />
==== python_bindings.cc ====<br />
The structure of this file is<br />
<br />
<source lang="cpp">// Headers for binding functions<br />
/**************************************/<br />
/* The following comment block is used for<br />
/* gr_modtool to insert function prototypes<br />
/* Please do not delete<br />
/**************************************/<br />
// BINDING_FUNCTION_PROTOTYPES(<br />
<br />
void bind_blockname1(py::module&);<br />
<br />
// ) END BINDING_FUNCTION_PROTOTYPES<br />
<br />
<br />
PYBIND11_MODULE(module_name__python, m)<br />
{<br />
<br />
{<br />
// Initialize the numpy C API<br />
// (otherwise we will see segmentation faults)<br />
init_numpy();<br />
<br />
// Allow access to base block methods<br />
py::module::import("gnuradio.gr");<br />
/**************************************/<br />
/* The following comment block is used for<br />
/* gr_modtool to insert binding function calls<br />
/* Please do not delete<br />
/**************************************/<br />
// BINDING_FUNCTION_CALLS(<br />
bind_blockname1(m);<br />
// ) END BINDING_FUNCTION_CALLS<br />
}<br />
</source><br />
===== Comment Block =====<br />
<br />
Each block binding file contains an automatically generated and maintained comment block that informs CMake when the bindings are out of sync with the header file they refer to, and what to do about it<br />
<br />
<source lang="cpp">/***********************************************************************************/<br />
/* This file is automatically generated using bindtool and can be manually edited */<br />
/* The following lines can be configured to regenerate this file during cmake */<br />
/* If manual edits are made, the following tags should be modified accordingly. */<br />
/* BINDTOOL_GEN_AUTOMATIC(0) */<br />
/* BINDTOOL_USE_PYGCCXML(0) */<br />
/* BINDTOOL_HEADER_FILE(basic_block.h) */<br />
/* BINDTOOL_HEADER_FILE_HASH(549c06530e2afdf6f2c989017cb5f36e) */<br />
/***********************************************************************************/<br />
</source><br />
<code>BINDTOOL_GEN_AUTOMATIC</code>: Many times for complex in-tree blocks, the automated tools are not entirely sufficient to generate all of the bindings in an automated fashion. In this case, the flag should be set to 0, and the bindings need to be updated manually. If the flag is set to 1, CMake will override the binding file ''in the source tree'' when it detects out of sync bindings. This should only be done in simple cases.<br />
<br />
<code>BINDTOOL_USE_PYGCCXML</code>: Currently there are limitations on the amount of code generation that can be accomplished without the <code>pygccxml</code> dependency. If a block needs pygccxml for the bindings to be properly generated automatically, this should be set to <code>1</code><br />
<br />
<code>BINDTOOL_HEADER_FILE</code>: The header file that bindings are based on, filename only<br />
<br />
<code>BINDTOOL_HEADER_FILE_HASH</code>: The MD5 hash of the header file that the bindings were built on<br />
<br />
=== Workflow ===<br />
<br />
==== Out-of-Tree modules ====<br />
<br />
The steps for creating an out of tree module with pybind11 bindings are as follows:<br />
<br />
# Use <code>gr_modtool</code> to create an out of tree module and add blocks<br />
<br />
<source lang="sh">gr_modtool newmod foo<br />
gr_modtool add bar<br />
</source><br />
<ol start="2"><br />
<li>Update the parameters or functions in the public include file and rebind with <code>gr_modtool bind bar</code></li></ol><br />
<br />
'''NOTE''': without pygccxml, only the make function is currently accounted for, similar to <code>gr_modtool makeyaml</code><br />
<br />
If the public API changes, just call <code>gr_modtool bind [blockname]</code> to regenerate the bindings<br />
<br />
When the public header file for a block is changed, CMake will fail as it checks the hash of the header file compared to the hash stored in the bindings file until the bindings are updated<br />
<br />
<ol start="3"><br />
<li>Build and install</li></ol><br />
<br />
=== Docstrings ===<br />
<br />
If Doxygen is enabled in GNU Radio and/or the OOT, Docstrings are scraped from the header files, and placed in auto-generated <code>[blockname]_pydoc.h</code> files in the build directory on compile. Generated templates (via the binding steps described above) are placed in the <code>python/bindings/docstrings</code> directory and are used as placeholders for the scraped strings<br />
<br />
Upon compilation, docstrings are scraped from the module and stored in a dictionary (using <code>update_pydoc.py scrape</code>) and then the values are substituted in the template file (using <code>update_pydoc.py sub</code>)<br />
<br />
<br />
=== OOT Migration ===<br />
<br />
The easiest way to migrate an OOT to 3.9 is to use <code>gr_modtool</code> to create a new OOT, use <code>gr_modtool add</code> to create the blocks, and copy code from the previous OOT.<br />
<br />
Steps to do this without regenerating a new module are TBD<br />
<br />
=== Caveats ===<br />
<br />
Pybind11 bound methods do not implicitly convert int to enum, so blocks that take enum as input, must have either "raw" or "enum" in the grc yml definition of the block. "Raw" will allow the value to be changed by another variable in the flowgraph.<br />
<br />
Block inheritance must be specified completely in the python bindings in order to use the inherited methods. For instance, if a block inherits from sync_block, both block and basic_block must be included in the inheritance specification of the class:<br />
<br />
<nowiki> py::class_<atsc_interleaver,<br />
gr::sync_block,<br />
gr::block,<br />
gr::basic_block,<br />
std::shared_ptr<atsc_interleaver>>(<br />
m, "atsc_interleaver", D(atsc_interleaver)) </nowiki><br />
<br />
<br />
If your OOT module uses types from or its classes derive from another gr module, it is necessary in <code>python_bindings.cc</code> to specify these modules. <br />
<br />
For instance, since all OOT modules require the base block types, there is a line <code>py::module::import("gnuradio.gr");</code>.<br />
<br />
If you wanted to utilize <code>digital::constellation</code> objects in your OOT, it would be necessary to add <code>py::module::import("gnuradio.digital");</code> so that pybind knows to use the bindings already compiled into gnuradio.digital for the constellation objects<br />
<br />
If your OOT module uses other classes as parameter, you must either setup a python binding for this class or use an existing one. <br />
An example, how to setup a binding for the QWidget class of QT5 used in gr-qtgui, can be found here[https://github.com/gnuradio/gnuradio/blob/master/gr-qtgui/python/qtgui/bindings/QWidget_python.cc]<br />
<br />
If you want to use this binding in your OOT module, you have to import it. This has to be done in the python_bindings.cc inside the '''PYBIND11_MODULE''' macro.<br />
In case of the QWidget usage this would be <code>py::module::import("gnuradio.qtgui.qtgui_python");</code><br />
<br />
==== Using default values ====<br />
<br />
For standard types like int,float, etc. you can set default values as usual.<br />
<code>gr_modtool bind blockname</code> will setup the correct bindings in blockname_python.cc.<br />
<br />
But if you use something like <code> QWidget* parent = NULL </code> gr_modtool generates code like<br />
<code>py::arg("parent") = __null, </code><br />
<br />
But at this point pybind does not know the type of parent, so you have to modify the binding code and cast the type.<br />
<code>py::arg("parent") = (QWidget *) __null, </code><br />
<br />
Another way to come across this issue is to modify your header file from<br />
<code> QWidget* parent = NULL </code> to<br />
<code> QWidget* parent = nullptr </code><br />
<br />
pybind generates <br />
<code>py::arg("parent") = nullptr, </code><br />
<br />
which will be handled correctly<br />
=== Troubleshooting ===<br />
<br />
==== Unable to find pydoc.h ====<br />
<nowiki>fatal error: blockname_pydoc.h: No such file or directory<br />
28 | #include <blockname_pydoc.h><br />
| ^~~~~~~~~~~~~~~~~~~~~~~~~<br />
compilation terminated.</nowiki><br />
<br />
blockname_pydoc.h is generated during compilation based on the template in the docstring directory. When the block is first created in blocktool, this template does not exist. Run <code>gr_modtool bind</code> to generate the appropriate template used as a placeholder for the scraped docstrings<br />
<br />
Also, the scraping of docstrings only takes place at CMake time, so it may be necessary to do a <code>make clean</code> to re-trigger the scraping<br />
<br />
You can also try <br />
<nowiki>rm python/bindings/docstring_status</nowiki><br />
which will reset the docstring scraping target in cmake and re-copy the docstring templates<br />
<br />
==== TypeError: 'modulename_python.blockname' object is not subscriptable ====<br />
<br />
This is caused by an incomplete inheritance chain specified in the binding declaration of the block.<br />
<br />
Instead of<br />
<br />
py::class_<blockname,<br />
std::shared_ptr<blockname>>(m, "blockname", D(blockname))<br />
<br />
Try something like (taking into account your block type)<br />
<br />
py::class_<blockname,<br />
gr::sync_block,<br />
gr::block,<br />
gr::basic_block,<br />
std::shared_ptr<blockname>>(m, "blockname", D(blockname))<br />
<br />
==== Module is Empty ====<br />
<br />
This is usually caused by linker errors that prevent the binding module from being loaded. When you see something like:<br />
<br />
>>> import foo<br />
>>> dir(foo)<br />
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__']<br />
<br />
But you expect foo to have other C++ blocks, try loading the pybind module separately<br />
<br />
cd build/python/bindings<br />
python3<br />
>>> import foo_python<br />
[linker error should be evident here in mangled symbol name]</div>Tom N5EGhttps://wiki.gnuradio.org/index.php?title=GNU_Radio_3.9_OOT_Module_Porting_Guide&diff=8551GNU Radio 3.9 OOT Module Porting Guide2021-05-13T15:31:13Z<p>Tom N5EG: /* Pybind11 Python Bindings */</p>
<hr />
<div>[[Category:3.9]]<br />
The major changes in the (in-progress) GNU Radio 3.9 release that will impact OOTs are:<br />
<br />
* C++ modernization (C++11/14?)<br />
* Replacement of SWIG with Pybind11<br />
* Cleanup of filter and fft APIs<br />
<br />
<br />
= Porting Guide = <br />
<br />
Porting from 3.8 to 3.9 can be achieved most simply by creating a new OOT module (with the same name as the 3.8 OOT but in a different directory), then performing some manual steps<br />
<br />
1. Use the 3.9 gr_modtool to generate a module with the same name (in another directory)<br />
<br />
2. Copy the python folder from 3.9 OOT into your 3.8 OOT<br />
<br />
3. (in 3.8 OOT) Add the bindings directory to the python directory CMakeLists<br />
./python/CMakeLists.txt → add the line: <br />
add_subdirectory(bindings)<br />
<br />
4. (in 3.8 OOT) Call gr_modtool bind for each block in your OOT<br />
NOTE: If you are doing more than just make function in your public header, e.g. setters/getters or other callback functions, be sure to have pygccxml set up<br />
<br />
5. c++ blocks that have python QA will need the import statements updated in that QA.<br />
<br />
6. python/bindings/python_bindings.cc needs to be updated for all C++ blocks (in two places!)<br />
<br />
7. Replace occurrences of boost::shared_ptr<blockname> with std::shared_ptr<blockname><br />
<br />
8. Though not necessary, boost::bind instances for message port handlers can be replaced with lambda functions for performance and simplicity as well as consistency with the in-tree modules<br />
<br />
* The instructions below are primarily intended for C++ projects. Python projects may be slightly different.<br />
<br />
9. Merge 3.8 /lib files (.cc, .h, etc) with the 3.9 lib file prototypes constructed by gr_modtool into 3.9 /lib.<br />
<br />
10. Copy the 3.8 /python/binding and /docstring files into 3.9 <br />
<br />
11. Merge the 3.8 /grc yml files with the 3.9 prototypes into 3.9/grc<br />
<br />
12. Merge the 3.9 /include files with the 3.9 prototypes into 3.9/include.<br />
<br />
13. Edit 3.9 /lib/CMakeLists.txt to add all the targets needed as specified in the 3.8 /lib/CMakeLists.txt file.<br />
<br />
14. bind will compare date/time stamps of the files. If the target bindings are out of date, manually rebind each 3.9 block with gr_modtool bind block.<br />
<br />
<br />
<br />
Porting from 3.7 to 3.9 should follow the 3.8 Porting Guide, but basically create a new OOT using 3.9 and add blocks from there, then copy in code.<br />
<br />
= Details = <br />
<br />
== C++ Modernization ==<br />
<br />
Boost shared pointers have been replaced with std:: shared pointers and memory management. At the top level of each block, the instantiation will need to change, e.g. <br />
<br />
In include/blockname_xx.h:<br />
<br />
<nowiki>typedef std::shared_ptr<blockname_xx> sptr;</nowiki><br />
<br />
Inbound message ports receive an update too. We move from <nowiki>boost::function</nowiki> to <nowiki>std::function</nowiki>. This affects how message handlers are registered. The preferred style is to use lambdas which is already compatible with GNU Radio 3.8:<br />
<br />
<nowiki>set_msg_handler(pmt::mp("message"), [this](pmt::pmt_t msg) { this->handle_msg(msg); });</nowiki><br />
<br />
== Pybind11 Python Bindings ==<br />
<br />
As of the GNU Radio 3.9 release, python bindings are handled using pybind11, which is inherently different than they were in previous releases<br />
<br />
=== Dependencies ===<br />
<br />
* pybind11 &gt; 2.4.3 [https://pybind11.readthedocs.io/ https://pybind11.readthedocs.io/]<br />
** pip does not provide the proper cmake (https://github.com/pybind/pybind11/issues/1379)<br />
** gnuradio 3.9 was built using pybind11 version 2.5.0. Your OOT should be built against that same version. (Version 2.4.3 will not work).<br />
** As of May 2021 Ubuntu 20.04 package manager references version 2.5.0, apt install pybind11-dev<br />
<br />
** For some distributions (e.g. Ubuntu <20.04), this will need to be installed from source as 2.5.0 is not the supplied version with package managers<br />
<nowiki><br />
curl -Lo pybind11.tar.gz https://github.com/pybind/pybind11/archive/v2.5.0.tar.gz <br />
mkdir pybind11 && tar xzf pybind11.tar.gz -C pybind11 --strip-components=1 && cd pybind11<br />
mkdir build && cd build <br />
cmake .. -DCMAKE_BUILD_TYPE=Release -DPYBIND11_TEST=OFF <br />
make<br />
sudo make install </nowiki><br />
<br />
* pygccxml [https://pygccxml.readthedocs.io/en/develop/install.html https://pygccxml.readthedocs.io/en/develop/install.html]<br />
** This is an optional dependency and basic functionality for OOT generation can be performed without pygccxml<br />
** It is required for automatically generating bindings for most of the GR source tree<br />
<br />
=== Components ===<br />
<br />
Python bindings are contained in the <code>python/.../bindings</code> directory<br />
<br />
<source lang="sh">./python<br />
└── module_name<br />
├── bindings<br />
│ ├── blockname1_python.cc<br />
│ ├── blockname2_python.cc<br />
│ ├── CMakeLists.txt<br />
| ├── python_bindings.cc<br />
│ ├── docstrings<br />
│ │ ├── blockname1_pydoc_template.h<br />
│ │ ├── blockname1_pydoc_template.h<br />
</source><br />
The bindings for each block exist in blockname_python.cc under the <code>python/bindings</code> directory. Additionally, a template header file for each block that is used as a placeholder for the scraped docstrings lives in the <code>docstrings/</code> dir<br />
<br />
==== blockname_python.cc ====<br />
<br />
This is the class function enum variable bindings for everything that needs to be exposed through the Python API<br />
<br />
==== python_bindings.cc ====<br />
The structure of this file is<br />
<br />
<source lang="cpp">// Headers for binding functions<br />
/**************************************/<br />
/* The following comment block is used for<br />
/* gr_modtool to insert function prototypes<br />
/* Please do not delete<br />
/**************************************/<br />
// BINDING_FUNCTION_PROTOTYPES(<br />
<br />
void bind_blockname1(py::module&);<br />
<br />
// ) END BINDING_FUNCTION_PROTOTYPES<br />
<br />
<br />
PYBIND11_MODULE(module_name__python, m)<br />
{<br />
<br />
{<br />
// Initialize the numpy C API<br />
// (otherwise we will see segmentation faults)<br />
init_numpy();<br />
<br />
// Allow access to base block methods<br />
py::module::import("gnuradio.gr");<br />
/**************************************/<br />
/* The following comment block is used for<br />
/* gr_modtool to insert binding function calls<br />
/* Please do not delete<br />
/**************************************/<br />
// BINDING_FUNCTION_CALLS(<br />
bind_blockname1(m);<br />
// ) END BINDING_FUNCTION_CALLS<br />
}<br />
</source><br />
===== Comment Block =====<br />
<br />
Each block binding file contains an automatically generated and maintained comment block that informs CMake when the bindings are out of sync with the header file they refer to, and what to do about it<br />
<br />
<source lang="cpp">/***********************************************************************************/<br />
/* This file is automatically generated using bindtool and can be manually edited */<br />
/* The following lines can be configured to regenerate this file during cmake */<br />
/* If manual edits are made, the following tags should be modified accordingly. */<br />
/* BINDTOOL_GEN_AUTOMATIC(0) */<br />
/* BINDTOOL_USE_PYGCCXML(0) */<br />
/* BINDTOOL_HEADER_FILE(basic_block.h) */<br />
/* BINDTOOL_HEADER_FILE_HASH(549c06530e2afdf6f2c989017cb5f36e) */<br />
/***********************************************************************************/<br />
</source><br />
<code>BINDTOOL_GEN_AUTOMATIC</code>: Many times for complex in-tree blocks, the automated tools are not entirely sufficient to generate all of the bindings in an automated fashion. In this case, the flag should be set to 0, and the bindings need to be updated manually. If the flag is set to 1, CMake will override the binding file ''in the source tree'' when it detects out of sync bindings. This should only be done in simple cases.<br />
<br />
<code>BINDTOOL_USE_PYGCCXML</code>: Currently there are limitations on the amount of code generation that can be accomplished without the <code>pygccxml</code> dependency. If a block needs pygccxml for the bindings to be properly generated automatically, this should be set to <code>1</code><br />
<br />
<code>BINDTOOL_HEADER_FILE</code>: The header file that bindings are based on, filename only<br />
<br />
<code>BINDTOOL_HEADER_FILE_HASH</code>: The MD5 hash of the header file that the bindings were built on<br />
<br />
=== Workflow ===<br />
<br />
==== Out-of-Tree modules ====<br />
<br />
The steps for creating an out of tree module with pybind11 bindings are as follows:<br />
<br />
# Use <code>gr_modtool</code> to create an out of tree module and add blocks<br />
<br />
<source lang="sh">gr_modtool newmod foo<br />
gr_modtool add bar<br />
</source><br />
<ol start="2"><br />
<li>Update the parameters or functions in the public include file and rebind with <code>gr_modtool bind bar</code></li></ol><br />
<br />
'''NOTE''': without pygccxml, only the make function is currently accounted for, similar to <code>gr_modtool makeyaml</code><br />
<br />
If the public API changes, just call <code>gr_modtool bind [blockname]</code> to regenerate the bindings<br />
<br />
When the public header file for a block is changed, CMake will fail as it checks the hash of the header file compared to the hash stored in the bindings file until the bindings are updated<br />
<br />
<ol start="3"><br />
<li>Build and install</li></ol><br />
<br />
=== Docstrings ===<br />
<br />
If Doxygen is enabled in GNU Radio and/or the OOT, Docstrings are scraped from the header files, and placed in auto-generated <code>[blockname]_pydoc.h</code> files in the build directory on compile. Generated templates (via the binding steps described above) are placed in the <code>python/bindings/docstrings</code> directory and are used as placeholders for the scraped strings<br />
<br />
Upon compilation, docstrings are scraped from the module and stored in a dictionary (using <code>update_pydoc.py scrape</code>) and then the values are substituted in the template file (using <code>update_pydoc.py sub</code>)<br />
<br />
<br />
=== OOT Migration ===<br />
<br />
The easiest way to migrate an OOT to 3.9 is to use <code>gr_modtool</code> to create a new OOT, use <code>gr_modtool add</code> to create the blocks, and copy code from the previous OOT.<br />
<br />
Steps to do this without regenerating a new module are TBD<br />
<br />
=== Caveats ===<br />
<br />
Pybind11 bound methods do not implicitly convert int to enum, so blocks that take enum as input, must have either "raw" or "enum" in the grc yml definition of the block. "Raw" will allow the value to be changed by another variable in the flowgraph.<br />
<br />
Block inheritance must be specified completely in the python bindings in order to use the inherited methods. For instance, if a block inherits from sync_block, both block and basic_block must be included in the inheritance specification of the class:<br />
<br />
<nowiki> py::class_<atsc_interleaver,<br />
gr::sync_block,<br />
gr::block,<br />
gr::basic_block,<br />
std::shared_ptr<atsc_interleaver>>(<br />
m, "atsc_interleaver", D(atsc_interleaver)) </nowiki><br />
<br />
<br />
If your OOT module uses types from or its classes derive from another gr module, it is necessary in <code>python_bindings.cc</code> to specify these modules. <br />
<br />
For instance, since all OOT modules require the base block types, there is a line <code>py::module::import("gnuradio.gr");</code>.<br />
<br />
If you wanted to utilize <code>digital::constellation</code> objects in your OOT, it would be necessary to add <code>py::module::import("gnuradio.digital");</code> so that pybind knows to use the bindings already compiled into gnuradio.digital for the constellation objects<br />
<br />
If your OOT module uses other classes as parameter, you must either setup a python binding for this class or use an existing one. <br />
An example, how to setup a binding for the QWidget class of QT5 used in gr-qtgui, can be found here[https://github.com/gnuradio/gnuradio/blob/master/gr-qtgui/python/qtgui/bindings/QWidget_python.cc]<br />
<br />
If you want to use this binding in your OOT module, you have to import it. This has to be done in the python_bindings.cc inside the '''PYBIND11_MODULE''' macro.<br />
In case of the QWidget usage this would be <code>py::module::import("gnuradio.qtgui.qtgui_python");</code><br />
<br />
==== Using default values ====<br />
<br />
For standard types like int,float, etc. you can set default values as usual.<br />
<code>gr_modtool bind blockname</code> will setup the correct bindings in blockname_python.cc.<br />
<br />
But if you use something like <code> QWidget* parent = NULL </code> gr_modtool generates code like<br />
<code>py::arg("parent") = __null, </code><br />
<br />
But at this point pybind does not know the type of parent, so you have to modify the binding code and cast the type.<br />
<code>py::arg("parent") = (QWidget *) __null, </code><br />
<br />
Another way to come across this issue is to modify your header file from<br />
<code> QWidget* parent = NULL </code> to<br />
<code> QWidget* parent = nullptr </code><br />
<br />
pybind generates <br />
<code>py::arg("parent") = nullptr, </code><br />
<br />
which will be handled correctly<br />
=== Troubleshooting ===<br />
<br />
==== Unable to find pydoc.h ====<br />
<nowiki>fatal error: blockname_pydoc.h: No such file or directory<br />
28 | #include <blockname_pydoc.h><br />
| ^~~~~~~~~~~~~~~~~~~~~~~~~<br />
compilation terminated.</nowiki><br />
<br />
blockname_pydoc.h is generated during compilation based on the template in the docstring directory. When the block is first created in blocktool, this template does not exist. Run <code>gr_modtool bind</code> to generate the appropriate template used as a placeholder for the scraped docstrings<br />
<br />
Also, the scraping of docstrings only takes place at CMake time, so it may be necessary to do a <code>make clean</code> to re-trigger the scraping<br />
<br />
You can also try <br />
<nowiki>rm python/bindings/docstring_status</nowiki><br />
which will reset the docstring scraping target in cmake and re-copy the docstring templates<br />
<br />
==== TypeError: 'modulename_python.blockname' object is not subscriptable ====<br />
<br />
This is caused by an incomplete inheritance chain specified in the binding declaration of the block.<br />
<br />
Instead of<br />
<br />
py::class_<blockname,<br />
std::shared_ptr<blockname>>(m, "blockname", D(blockname))<br />
<br />
Try something like (taking into account your block type)<br />
<br />
py::class_<blockname,<br />
gr::sync_block,<br />
gr::block,<br />
gr::basic_block,<br />
std::shared_ptr<blockname>>(m, "blockname", D(blockname))<br />
<br />
==== Module is Empty ====<br />
<br />
This is usually caused by linker errors that prevent the binding module from being loaded. When you see something like:<br />
<br />
>>> import foo<br />
>>> dir(foo)<br />
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__']<br />
<br />
But you expect foo to have other C++ blocks, try loading the pybind module separately<br />
<br />
cd build/python/bindings<br />
python3<br />
>>> import foo_python<br />
[linker error should be evident here in mangled symbol name]</div>Tom N5EGhttps://wiki.gnuradio.org/index.php?title=GNU_Radio_3.9_OOT_Module_Porting_Guide&diff=8550GNU Radio 3.9 OOT Module Porting Guide2021-05-13T15:28:36Z<p>Tom N5EG: /* Porting Guide */</p>
<hr />
<div>[[Category:3.9]]<br />
The major changes in the (in-progress) GNU Radio 3.9 release that will impact OOTs are:<br />
<br />
* C++ modernization (C++11/14?)<br />
* Replacement of SWIG with Pybind11<br />
* Cleanup of filter and fft APIs<br />
<br />
<br />
= Porting Guide = <br />
<br />
Porting from 3.8 to 3.9 can be achieved most simply by creating a new OOT module (with the same name as the 3.8 OOT but in a different directory), then performing some manual steps<br />
<br />
1. Use the 3.9 gr_modtool to generate a module with the same name (in another directory)<br />
<br />
2. Copy the python folder from 3.9 OOT into your 3.8 OOT<br />
<br />
3. (in 3.8 OOT) Add the bindings directory to the python directory CMakeLists<br />
./python/CMakeLists.txt → add the line: <br />
add_subdirectory(bindings)<br />
<br />
4. (in 3.8 OOT) Call gr_modtool bind for each block in your OOT<br />
NOTE: If you are doing more than just make function in your public header, e.g. setters/getters or other callback functions, be sure to have pygccxml set up<br />
<br />
5. c++ blocks that have python QA will need the import statements updated in that QA.<br />
<br />
6. python/bindings/python_bindings.cc needs to be updated for all C++ blocks (in two places!)<br />
<br />
7. Replace occurrences of boost::shared_ptr<blockname> with std::shared_ptr<blockname><br />
<br />
8. Though not necessary, boost::bind instances for message port handlers can be replaced with lambda functions for performance and simplicity as well as consistency with the in-tree modules<br />
<br />
* The instructions below are primarily intended for C++ projects. Python projects may be slightly different.<br />
<br />
9. Merge 3.8 /lib files (.cc, .h, etc) with the 3.9 lib file prototypes constructed by gr_modtool into 3.9 /lib.<br />
<br />
10. Copy the 3.8 /python/binding and /docstring files into 3.9 <br />
<br />
11. Merge the 3.8 /grc yml files with the 3.9 prototypes into 3.9/grc<br />
<br />
12. Merge the 3.9 /include files with the 3.9 prototypes into 3.9/include.<br />
<br />
13. Edit 3.9 /lib/CMakeLists.txt to add all the targets needed as specified in the 3.8 /lib/CMakeLists.txt file.<br />
<br />
14. bind will compare date/time stamps of the files. If the target bindings are out of date, manually rebind each 3.9 block with gr_modtool bind block.<br />
<br />
<br />
<br />
Porting from 3.7 to 3.9 should follow the 3.8 Porting Guide, but basically create a new OOT using 3.9 and add blocks from there, then copy in code.<br />
<br />
= Details = <br />
<br />
== C++ Modernization ==<br />
<br />
Boost shared pointers have been replaced with std:: shared pointers and memory management. At the top level of each block, the instantiation will need to change, e.g. <br />
<br />
In include/blockname_xx.h:<br />
<br />
<nowiki>typedef std::shared_ptr<blockname_xx> sptr;</nowiki><br />
<br />
Inbound message ports receive an update too. We move from <nowiki>boost::function</nowiki> to <nowiki>std::function</nowiki>. This affects how message handlers are registered. The preferred style is to use lambdas which is already compatible with GNU Radio 3.8:<br />
<br />
<nowiki>set_msg_handler(pmt::mp("message"), [this](pmt::pmt_t msg) { this->handle_msg(msg); });</nowiki><br />
<br />
== Pybind11 Python Bindings ==<br />
<br />
As of the GNU Radio 3.9 release, python bindings are handled using pybind11, which is inherently different than they were in previous releases<br />
<br />
=== Dependencies ===<br />
<br />
* pybind11 &gt; 2.4.3 [https://pybind11.readthedocs.io/ https://pybind11.readthedocs.io/]<br />
** pip does not provide the proper cmake (https://github.com/pybind/pybind11/issues/1379)<br />
** gnuradio 3.9 was built using pybind11 version 2.5.0. Your OOT should be built against that same version. (Version 2.4.3 will not work).<br />
** As of May 2021 Ubuntu 20.04 package manager references version 2.5.0, apt install pybind11-dev<br />
<br />
** For some distributions (e.g. Ubuntu <20.04), this will need to be installed from source as 2.5.0 is not the supplied version with package managers<br />
<nowiki><br />
curl -Lo pybind11.tar.gz https://github.com/pybind/pybind11/archive/v2.5.0.tar.gz <br />
mkdir pybind11 && tar xzf pybind11.tar.gz -C pybind11 --strip-components=1 && cd pybind11<br />
mkdir build && cd build <br />
cmake .. -DCMAKE_BUILD_TYPE=Release -DPYBIND11_TEST=OFF <br />
make && make install </nowiki><br />
<br />
* pygccxml [https://pygccxml.readthedocs.io/en/develop/install.html https://pygccxml.readthedocs.io/en/develop/install.html]<br />
** This is an optional dependency and basic functionality for OOT generation can be performed without pygccxml<br />
** It is required for automatically generating bindings for most of the GR source tree<br />
<br />
=== Components ===<br />
<br />
Python bindings are contained in the <code>python/.../bindings</code> directory<br />
<br />
<source lang="sh">./python<br />
└── module_name<br />
├── bindings<br />
│ ├── blockname1_python.cc<br />
│ ├── blockname2_python.cc<br />
│ ├── CMakeLists.txt<br />
| ├── python_bindings.cc<br />
│ ├── docstrings<br />
│ │ ├── blockname1_pydoc_template.h<br />
│ │ ├── blockname1_pydoc_template.h<br />
</source><br />
The bindings for each block exist in blockname_python.cc under the <code>python/bindings</code> directory. Additionally, a template header file for each block that is used as a placeholder for the scraped docstrings lives in the <code>docstrings/</code> dir<br />
<br />
==== blockname_python.cc ====<br />
<br />
This is the class function enum variable bindings for everything that needs to be exposed through the Python API<br />
<br />
==== python_bindings.cc ====<br />
The structure of this file is<br />
<br />
<source lang="cpp">// Headers for binding functions<br />
/**************************************/<br />
/* The following comment block is used for<br />
/* gr_modtool to insert function prototypes<br />
/* Please do not delete<br />
/**************************************/<br />
// BINDING_FUNCTION_PROTOTYPES(<br />
<br />
void bind_blockname1(py::module&);<br />
<br />
// ) END BINDING_FUNCTION_PROTOTYPES<br />
<br />
<br />
PYBIND11_MODULE(module_name__python, m)<br />
{<br />
<br />
{<br />
// Initialize the numpy C API<br />
// (otherwise we will see segmentation faults)<br />
init_numpy();<br />
<br />
// Allow access to base block methods<br />
py::module::import("gnuradio.gr");<br />
/**************************************/<br />
/* The following comment block is used for<br />
/* gr_modtool to insert binding function calls<br />
/* Please do not delete<br />
/**************************************/<br />
// BINDING_FUNCTION_CALLS(<br />
bind_blockname1(m);<br />
// ) END BINDING_FUNCTION_CALLS<br />
}<br />
</source><br />
===== Comment Block =====<br />
<br />
Each block binding file contains an automatically generated and maintained comment block that informs CMake when the bindings are out of sync with the header file they refer to, and what to do about it<br />
<br />
<source lang="cpp">/***********************************************************************************/<br />
/* This file is automatically generated using bindtool and can be manually edited */<br />
/* The following lines can be configured to regenerate this file during cmake */<br />
/* If manual edits are made, the following tags should be modified accordingly. */<br />
/* BINDTOOL_GEN_AUTOMATIC(0) */<br />
/* BINDTOOL_USE_PYGCCXML(0) */<br />
/* BINDTOOL_HEADER_FILE(basic_block.h) */<br />
/* BINDTOOL_HEADER_FILE_HASH(549c06530e2afdf6f2c989017cb5f36e) */<br />
/***********************************************************************************/<br />
</source><br />
<code>BINDTOOL_GEN_AUTOMATIC</code>: Many times for complex in-tree blocks, the automated tools are not entirely sufficient to generate all of the bindings in an automated fashion. In this case, the flag should be set to 0, and the bindings need to be updated manually. If the flag is set to 1, CMake will override the binding file ''in the source tree'' when it detects out of sync bindings. This should only be done in simple cases.<br />
<br />
<code>BINDTOOL_USE_PYGCCXML</code>: Currently there are limitations on the amount of code generation that can be accomplished without the <code>pygccxml</code> dependency. If a block needs pygccxml for the bindings to be properly generated automatically, this should be set to <code>1</code><br />
<br />
<code>BINDTOOL_HEADER_FILE</code>: The header file that bindings are based on, filename only<br />
<br />
<code>BINDTOOL_HEADER_FILE_HASH</code>: The MD5 hash of the header file that the bindings were built on<br />
<br />
=== Workflow ===<br />
<br />
==== Out-of-Tree modules ====<br />
<br />
The steps for creating an out of tree module with pybind11 bindings are as follows:<br />
<br />
# Use <code>gr_modtool</code> to create an out of tree module and add blocks<br />
<br />
<source lang="sh">gr_modtool newmod foo<br />
gr_modtool add bar<br />
</source><br />
<ol start="2"><br />
<li>Update the parameters or functions in the public include file and rebind with <code>gr_modtool bind bar</code></li></ol><br />
<br />
'''NOTE''': without pygccxml, only the make function is currently accounted for, similar to <code>gr_modtool makeyaml</code><br />
<br />
If the public API changes, just call <code>gr_modtool bind [blockname]</code> to regenerate the bindings<br />
<br />
When the public header file for a block is changed, CMake will fail as it checks the hash of the header file compared to the hash stored in the bindings file until the bindings are updated<br />
<br />
<ol start="3"><br />
<li>Build and install</li></ol><br />
<br />
=== Docstrings ===<br />
<br />
If Doxygen is enabled in GNU Radio and/or the OOT, Docstrings are scraped from the header files, and placed in auto-generated <code>[blockname]_pydoc.h</code> files in the build directory on compile. Generated templates (via the binding steps described above) are placed in the <code>python/bindings/docstrings</code> directory and are used as placeholders for the scraped strings<br />
<br />
Upon compilation, docstrings are scraped from the module and stored in a dictionary (using <code>update_pydoc.py scrape</code>) and then the values are substituted in the template file (using <code>update_pydoc.py sub</code>)<br />
<br />
<br />
=== OOT Migration ===<br />
<br />
The easiest way to migrate an OOT to 3.9 is to use <code>gr_modtool</code> to create a new OOT, use <code>gr_modtool add</code> to create the blocks, and copy code from the previous OOT.<br />
<br />
Steps to do this without regenerating a new module are TBD<br />
<br />
=== Caveats ===<br />
<br />
Pybind11 bound methods do not implicitly convert int to enum, so blocks that take enum as input, must have either "raw" or "enum" in the grc yml definition of the block. "Raw" will allow the value to be changed by another variable in the flowgraph.<br />
<br />
Block inheritance must be specified completely in the python bindings in order to use the inherited methods. For instance, if a block inherits from sync_block, both block and basic_block must be included in the inheritance specification of the class:<br />
<br />
<nowiki> py::class_<atsc_interleaver,<br />
gr::sync_block,<br />
gr::block,<br />
gr::basic_block,<br />
std::shared_ptr<atsc_interleaver>>(<br />
m, "atsc_interleaver", D(atsc_interleaver)) </nowiki><br />
<br />
<br />
If your OOT module uses types from or its classes derive from another gr module, it is necessary in <code>python_bindings.cc</code> to specify these modules. <br />
<br />
For instance, since all OOT modules require the base block types, there is a line <code>py::module::import("gnuradio.gr");</code>.<br />
<br />
If you wanted to utilize <code>digital::constellation</code> objects in your OOT, it would be necessary to add <code>py::module::import("gnuradio.digital");</code> so that pybind knows to use the bindings already compiled into gnuradio.digital for the constellation objects<br />
<br />
If your OOT module uses other classes as parameter, you must either setup a python binding for this class or use an existing one. <br />
An example, how to setup a binding for the QWidget class of QT5 used in gr-qtgui, can be found here[https://github.com/gnuradio/gnuradio/blob/master/gr-qtgui/python/qtgui/bindings/QWidget_python.cc]<br />
<br />
If you want to use this binding in your OOT module, you have to import it. This has to be done in the python_bindings.cc inside the '''PYBIND11_MODULE''' macro.<br />
In case of the QWidget usage this would be <code>py::module::import("gnuradio.qtgui.qtgui_python");</code><br />
<br />
==== Using default values ====<br />
<br />
For standard types like int,float, etc. you can set default values as usual.<br />
<code>gr_modtool bind blockname</code> will setup the correct bindings in blockname_python.cc.<br />
<br />
But if you use something like <code> QWidget* parent = NULL </code> gr_modtool generates code like<br />
<code>py::arg("parent") = __null, </code><br />
<br />
But at this point pybind does not know the type of parent, so you have to modify the binding code and cast the type.<br />
<code>py::arg("parent") = (QWidget *) __null, </code><br />
<br />
Another way to come across this issue is to modify your header file from<br />
<code> QWidget* parent = NULL </code> to<br />
<code> QWidget* parent = nullptr </code><br />
<br />
pybind generates <br />
<code>py::arg("parent") = nullptr, </code><br />
<br />
which will be handled correctly<br />
=== Troubleshooting ===<br />
<br />
==== Unable to find pydoc.h ====<br />
<nowiki>fatal error: blockname_pydoc.h: No such file or directory<br />
28 | #include <blockname_pydoc.h><br />
| ^~~~~~~~~~~~~~~~~~~~~~~~~<br />
compilation terminated.</nowiki><br />
<br />
blockname_pydoc.h is generated during compilation based on the template in the docstring directory. When the block is first created in blocktool, this template does not exist. Run <code>gr_modtool bind</code> to generate the appropriate template used as a placeholder for the scraped docstrings<br />
<br />
Also, the scraping of docstrings only takes place at CMake time, so it may be necessary to do a <code>make clean</code> to re-trigger the scraping<br />
<br />
You can also try <br />
<nowiki>rm python/bindings/docstring_status</nowiki><br />
which will reset the docstring scraping target in cmake and re-copy the docstring templates<br />
<br />
==== TypeError: 'modulename_python.blockname' object is not subscriptable ====<br />
<br />
This is caused by an incomplete inheritance chain specified in the binding declaration of the block.<br />
<br />
Instead of<br />
<br />
py::class_<blockname,<br />
std::shared_ptr<blockname>>(m, "blockname", D(blockname))<br />
<br />
Try something like (taking into account your block type)<br />
<br />
py::class_<blockname,<br />
gr::sync_block,<br />
gr::block,<br />
gr::basic_block,<br />
std::shared_ptr<blockname>>(m, "blockname", D(blockname))<br />
<br />
==== Module is Empty ====<br />
<br />
This is usually caused by linker errors that prevent the binding module from being loaded. When you see something like:<br />
<br />
>>> import foo<br />
>>> dir(foo)<br />
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__']<br />
<br />
But you expect foo to have other C++ blocks, try loading the pybind module separately<br />
<br />
cd build/python/bindings<br />
python3<br />
>>> import foo_python<br />
[linker error should be evident here in mangled symbol name]</div>Tom N5EGhttps://wiki.gnuradio.org/index.php?title=GNU_Radio_3.9_OOT_Module_Porting_Guide&diff=8549GNU Radio 3.9 OOT Module Porting Guide2021-05-13T15:07:20Z<p>Tom N5EG: /* Pybind11 Python Bindings */</p>
<hr />
<div>[[Category:3.9]]<br />
The major changes in the (in-progress) GNU Radio 3.9 release that will impact OOTs are:<br />
<br />
* C++ modernization (C++11/14?)<br />
* Replacement of SWIG with Pybind11<br />
* Cleanup of filter and fft APIs<br />
<br />
<br />
= Porting Guide = <br />
<br />
Porting from 3.8 to 3.9 can be achieved most simply by creating a new OOT module (with the same name as the 3.8 OOT but in a different directory), then performing some manual steps<br />
<br />
1. Use the 3.9 gr_modtool to generate a module with the same name (in another directory)<br />
<br />
2. Copy the python folder from 3.9 OOT into your 3.8 OOT<br />
<br />
3. (in 3.8 OOT) Add the bindings directory to the python directory CMakeLists<br />
./python/CMakeLists.txt → add the line: <br />
add_subdirectory(bindings)<br />
<br />
4. (in 3.8 OOT) Call gr_modtool bind for each block in your OOT<br />
NOTE: If you are doing more than just make function in your public header, e.g. setters/getters or other callback functions, be sure to have pygccxml set up<br />
<br />
5. c++ blocks that have python QA will need the import statements updated in that QA.<br />
<br />
6. python/bindings/python_bindings.cc needs to be updated for all C++ blocks (in two places!)<br />
<br />
7. Replace occurrences of boost::shared_ptr<blockname> with std::shared_ptr<blockname><br />
<br />
8. Though not necessary, boost::bind instances for message port handlers can be replaced with lambda functions for performance and simplicity as well as consistency with the in-tree modules<br />
<br />
<br />
Porting from 3.7 to 3.9 should follow the 3.8 Porting Guide, but basically create a new OOT using 3.9 and add blocks from there, then copy in code.<br />
<br />
= Details = <br />
<br />
== C++ Modernization ==<br />
<br />
Boost shared pointers have been replaced with std:: shared pointers and memory management. At the top level of each block, the instantiation will need to change, e.g. <br />
<br />
In include/blockname_xx.h:<br />
<br />
<nowiki>typedef std::shared_ptr<blockname_xx> sptr;</nowiki><br />
<br />
Inbound message ports receive an update too. We move from <nowiki>boost::function</nowiki> to <nowiki>std::function</nowiki>. This affects how message handlers are registered. The preferred style is to use lambdas which is already compatible with GNU Radio 3.8:<br />
<br />
<nowiki>set_msg_handler(pmt::mp("message"), [this](pmt::pmt_t msg) { this->handle_msg(msg); });</nowiki><br />
<br />
== Pybind11 Python Bindings ==<br />
<br />
As of the GNU Radio 3.9 release, python bindings are handled using pybind11, which is inherently different than they were in previous releases<br />
<br />
=== Dependencies ===<br />
<br />
* pybind11 &gt; 2.4.3 [https://pybind11.readthedocs.io/ https://pybind11.readthedocs.io/]<br />
** pip does not provide the proper cmake (https://github.com/pybind/pybind11/issues/1379)<br />
** gnuradio 3.9 was built using pybind11 version 2.5.0. Your OOT should be built against that same version. (Version 2.4.3 will not work).<br />
** As of May 2021 Ubuntu 20.04 package manager references version 2.5.0, apt install pybind11-dev<br />
<br />
** For some distributions (e.g. Ubuntu <20.04), this will need to be installed from source as 2.5.0 is not the supplied version with package managers<br />
<nowiki><br />
curl -Lo pybind11.tar.gz https://github.com/pybind/pybind11/archive/v2.5.0.tar.gz <br />
mkdir pybind11 && tar xzf pybind11.tar.gz -C pybind11 --strip-components=1 && cd pybind11<br />
mkdir build && cd build <br />
cmake .. -DCMAKE_BUILD_TYPE=Release -DPYBIND11_TEST=OFF <br />
make && make install </nowiki><br />
<br />
* pygccxml [https://pygccxml.readthedocs.io/en/develop/install.html https://pygccxml.readthedocs.io/en/develop/install.html]<br />
** This is an optional dependency and basic functionality for OOT generation can be performed without pygccxml<br />
** It is required for automatically generating bindings for most of the GR source tree<br />
<br />
=== Components ===<br />
<br />
Python bindings are contained in the <code>python/.../bindings</code> directory<br />
<br />
<source lang="sh">./python<br />
└── module_name<br />
├── bindings<br />
│ ├── blockname1_python.cc<br />
│ ├── blockname2_python.cc<br />
│ ├── CMakeLists.txt<br />
| ├── python_bindings.cc<br />
│ ├── docstrings<br />
│ │ ├── blockname1_pydoc_template.h<br />
│ │ ├── blockname1_pydoc_template.h<br />
</source><br />
The bindings for each block exist in blockname_python.cc under the <code>python/bindings</code> directory. Additionally, a template header file for each block that is used as a placeholder for the scraped docstrings lives in the <code>docstrings/</code> dir<br />
<br />
==== blockname_python.cc ====<br />
<br />
This is the class function enum variable bindings for everything that needs to be exposed through the Python API<br />
<br />
==== python_bindings.cc ====<br />
The structure of this file is<br />
<br />
<source lang="cpp">// Headers for binding functions<br />
/**************************************/<br />
/* The following comment block is used for<br />
/* gr_modtool to insert function prototypes<br />
/* Please do not delete<br />
/**************************************/<br />
// BINDING_FUNCTION_PROTOTYPES(<br />
<br />
void bind_blockname1(py::module&);<br />
<br />
// ) END BINDING_FUNCTION_PROTOTYPES<br />
<br />
<br />
PYBIND11_MODULE(module_name__python, m)<br />
{<br />
<br />
{<br />
// Initialize the numpy C API<br />
// (otherwise we will see segmentation faults)<br />
init_numpy();<br />
<br />
// Allow access to base block methods<br />
py::module::import("gnuradio.gr");<br />
/**************************************/<br />
/* The following comment block is used for<br />
/* gr_modtool to insert binding function calls<br />
/* Please do not delete<br />
/**************************************/<br />
// BINDING_FUNCTION_CALLS(<br />
bind_blockname1(m);<br />
// ) END BINDING_FUNCTION_CALLS<br />
}<br />
</source><br />
===== Comment Block =====<br />
<br />
Each block binding file contains an automatically generated and maintained comment block that informs CMake when the bindings are out of sync with the header file they refer to, and what to do about it<br />
<br />
<source lang="cpp">/***********************************************************************************/<br />
/* This file is automatically generated using bindtool and can be manually edited */<br />
/* The following lines can be configured to regenerate this file during cmake */<br />
/* If manual edits are made, the following tags should be modified accordingly. */<br />
/* BINDTOOL_GEN_AUTOMATIC(0) */<br />
/* BINDTOOL_USE_PYGCCXML(0) */<br />
/* BINDTOOL_HEADER_FILE(basic_block.h) */<br />
/* BINDTOOL_HEADER_FILE_HASH(549c06530e2afdf6f2c989017cb5f36e) */<br />
/***********************************************************************************/<br />
</source><br />
<code>BINDTOOL_GEN_AUTOMATIC</code>: Many times for complex in-tree blocks, the automated tools are not entirely sufficient to generate all of the bindings in an automated fashion. In this case, the flag should be set to 0, and the bindings need to be updated manually. If the flag is set to 1, CMake will override the binding file ''in the source tree'' when it detects out of sync bindings. This should only be done in simple cases.<br />
<br />
<code>BINDTOOL_USE_PYGCCXML</code>: Currently there are limitations on the amount of code generation that can be accomplished without the <code>pygccxml</code> dependency. If a block needs pygccxml for the bindings to be properly generated automatically, this should be set to <code>1</code><br />
<br />
<code>BINDTOOL_HEADER_FILE</code>: The header file that bindings are based on, filename only<br />
<br />
<code>BINDTOOL_HEADER_FILE_HASH</code>: The MD5 hash of the header file that the bindings were built on<br />
<br />
=== Workflow ===<br />
<br />
==== Out-of-Tree modules ====<br />
<br />
The steps for creating an out of tree module with pybind11 bindings are as follows:<br />
<br />
# Use <code>gr_modtool</code> to create an out of tree module and add blocks<br />
<br />
<source lang="sh">gr_modtool newmod foo<br />
gr_modtool add bar<br />
</source><br />
<ol start="2"><br />
<li>Update the parameters or functions in the public include file and rebind with <code>gr_modtool bind bar</code></li></ol><br />
<br />
'''NOTE''': without pygccxml, only the make function is currently accounted for, similar to <code>gr_modtool makeyaml</code><br />
<br />
If the public API changes, just call <code>gr_modtool bind [blockname]</code> to regenerate the bindings<br />
<br />
When the public header file for a block is changed, CMake will fail as it checks the hash of the header file compared to the hash stored in the bindings file until the bindings are updated<br />
<br />
<ol start="3"><br />
<li>Build and install</li></ol><br />
<br />
=== Docstrings ===<br />
<br />
If Doxygen is enabled in GNU Radio and/or the OOT, Docstrings are scraped from the header files, and placed in auto-generated <code>[blockname]_pydoc.h</code> files in the build directory on compile. Generated templates (via the binding steps described above) are placed in the <code>python/bindings/docstrings</code> directory and are used as placeholders for the scraped strings<br />
<br />
Upon compilation, docstrings are scraped from the module and stored in a dictionary (using <code>update_pydoc.py scrape</code>) and then the values are substituted in the template file (using <code>update_pydoc.py sub</code>)<br />
<br />
<br />
=== OOT Migration ===<br />
<br />
The easiest way to migrate an OOT to 3.9 is to use <code>gr_modtool</code> to create a new OOT, use <code>gr_modtool add</code> to create the blocks, and copy code from the previous OOT.<br />
<br />
Steps to do this without regenerating a new module are TBD<br />
<br />
=== Caveats ===<br />
<br />
Pybind11 bound methods do not implicitly convert int to enum, so blocks that take enum as input, must have either "raw" or "enum" in the grc yml definition of the block. "Raw" will allow the value to be changed by another variable in the flowgraph.<br />
<br />
Block inheritance must be specified completely in the python bindings in order to use the inherited methods. For instance, if a block inherits from sync_block, both block and basic_block must be included in the inheritance specification of the class:<br />
<br />
<nowiki> py::class_<atsc_interleaver,<br />
gr::sync_block,<br />
gr::block,<br />
gr::basic_block,<br />
std::shared_ptr<atsc_interleaver>>(<br />
m, "atsc_interleaver", D(atsc_interleaver)) </nowiki><br />
<br />
<br />
If your OOT module uses types from or its classes derive from another gr module, it is necessary in <code>python_bindings.cc</code> to specify these modules. <br />
<br />
For instance, since all OOT modules require the base block types, there is a line <code>py::module::import("gnuradio.gr");</code>.<br />
<br />
If you wanted to utilize <code>digital::constellation</code> objects in your OOT, it would be necessary to add <code>py::module::import("gnuradio.digital");</code> so that pybind knows to use the bindings already compiled into gnuradio.digital for the constellation objects<br />
<br />
If your OOT module uses other classes as parameter, you must either setup a python binding for this class or use an existing one. <br />
An example, how to setup a binding for the QWidget class of QT5 used in gr-qtgui, can be found here[https://github.com/gnuradio/gnuradio/blob/master/gr-qtgui/python/qtgui/bindings/QWidget_python.cc]<br />
<br />
If you want to use this binding in your OOT module, you have to import it. This has to be done in the python_bindings.cc inside the '''PYBIND11_MODULE''' macro.<br />
In case of the QWidget usage this would be <code>py::module::import("gnuradio.qtgui.qtgui_python");</code><br />
<br />
==== Using default values ====<br />
<br />
For standard types like int,float, etc. you can set default values as usual.<br />
<code>gr_modtool bind blockname</code> will setup the correct bindings in blockname_python.cc.<br />
<br />
But if you use something like <code> QWidget* parent = NULL </code> gr_modtool generates code like<br />
<code>py::arg("parent") = __null, </code><br />
<br />
But at this point pybind does not know the type of parent, so you have to modify the binding code and cast the type.<br />
<code>py::arg("parent") = (QWidget *) __null, </code><br />
<br />
Another way to come across this issue is to modify your header file from<br />
<code> QWidget* parent = NULL </code> to<br />
<code> QWidget* parent = nullptr </code><br />
<br />
pybind generates <br />
<code>py::arg("parent") = nullptr, </code><br />
<br />
which will be handled correctly<br />
=== Troubleshooting ===<br />
<br />
==== Unable to find pydoc.h ====<br />
<nowiki>fatal error: blockname_pydoc.h: No such file or directory<br />
28 | #include <blockname_pydoc.h><br />
| ^~~~~~~~~~~~~~~~~~~~~~~~~<br />
compilation terminated.</nowiki><br />
<br />
blockname_pydoc.h is generated during compilation based on the template in the docstring directory. When the block is first created in blocktool, this template does not exist. Run <code>gr_modtool bind</code> to generate the appropriate template used as a placeholder for the scraped docstrings<br />
<br />
Also, the scraping of docstrings only takes place at CMake time, so it may be necessary to do a <code>make clean</code> to re-trigger the scraping<br />
<br />
You can also try <br />
<nowiki>rm python/bindings/docstring_status</nowiki><br />
which will reset the docstring scraping target in cmake and re-copy the docstring templates<br />
<br />
==== TypeError: 'modulename_python.blockname' object is not subscriptable ====<br />
<br />
This is caused by an incomplete inheritance chain specified in the binding declaration of the block.<br />
<br />
Instead of<br />
<br />
py::class_<blockname,<br />
std::shared_ptr<blockname>>(m, "blockname", D(blockname))<br />
<br />
Try something like (taking into account your block type)<br />
<br />
py::class_<blockname,<br />
gr::sync_block,<br />
gr::block,<br />
gr::basic_block,<br />
std::shared_ptr<blockname>>(m, "blockname", D(blockname))<br />
<br />
==== Module is Empty ====<br />
<br />
This is usually caused by linker errors that prevent the binding module from being loaded. When you see something like:<br />
<br />
>>> import foo<br />
>>> dir(foo)<br />
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__']<br />
<br />
But you expect foo to have other C++ blocks, try loading the pybind module separately<br />
<br />
cd build/python/bindings<br />
python3<br />
>>> import foo_python<br />
[linker error should be evident here in mangled symbol name]</div>Tom N5EGhttps://wiki.gnuradio.org/index.php?title=GNU_Radio_3.9_OOT_Module_Porting_Guide&diff=8547GNU Radio 3.9 OOT Module Porting Guide2021-05-13T12:02:46Z<p>Tom N5EG: /* Dependencies */</p>
<hr />
<div>[[Category:3.9]]<br />
The major changes in the (in-progress) GNU Radio 3.9 release that will impact OOTs are:<br />
<br />
* C++ modernization (C++11/14?)<br />
* Replacement of SWIG with Pybind11<br />
* Cleanup of filter and fft APIs<br />
<br />
<br />
= Porting Guide = <br />
<br />
Porting from 3.8 to 3.9 can be achieved most simply by creating a new OOT module (with the same name as the 3.8 OOT but in a different directory), then performing some manual steps<br />
<br />
1. Use the 3.9 gr_modtool to generate a module with the same name (in another directory)<br />
<br />
2. Copy the python folder from 3.9 OOT into your 3.8 OOT<br />
<br />
3. (in 3.8 OOT) Add the bindings directory to the python directory CMakeLists<br />
./python/CMakeLists.txt → add the line: <br />
add_subdirectory(bindings)<br />
<br />
4. (in 3.8 OOT) Call gr_modtool bind for each block in your OOT<br />
NOTE: If you are doing more than just make function in your public header, e.g. setters/getters or other callback functions, be sure to have pygccxml set up<br />
<br />
5. c++ blocks that have python QA will need the import statements updated in that QA.<br />
<br />
6. python/bindings/python_bindings.cc needs to be updated for all C++ blocks (in two places!)<br />
<br />
7. Replace occurrences of boost::shared_ptr<blockname> with std::shared_ptr<blockname><br />
<br />
8. Though not necessary, boost::bind instances for message port handlers can be replaced with lambda functions for performance and simplicity as well as consistency with the in-tree modules<br />
<br />
<br />
Porting from 3.7 to 3.9 should follow the 3.8 Porting Guide, but basically create a new OOT using 3.9 and add blocks from there, then copy in code.<br />
<br />
= Details = <br />
<br />
== C++ Modernization ==<br />
<br />
Boost shared pointers have been replaced with std:: shared pointers and memory management. At the top level of each block, the instantiation will need to change, e.g. <br />
<br />
In include/blockname_xx.h:<br />
<br />
<nowiki>typedef std::shared_ptr<blockname_xx> sptr;</nowiki><br />
<br />
Inbound message ports receive an update too. We move from <nowiki>boost::function</nowiki> to <nowiki>std::function</nowiki>. This affects how message handlers are registered. The preferred style is to use lambdas which is already compatible with GNU Radio 3.8:<br />
<br />
<nowiki>set_msg_handler(pmt::mp("message"), [this](pmt::pmt_t msg) { this->handle_msg(msg); });</nowiki><br />
<br />
== Pybind11 Python Bindings ==<br />
<br />
As of the GNU Radio 3.9 release, python bindings are handled using pybind11, which is inherently different than they were in previous releases<br />
<br />
=== Dependencies ===<br />
<br />
* pybind11 &gt; 2.4.3 [https://pybind11.readthedocs.io/ https://pybind11.readthedocs.io/]<br />
** pip does not provide the proper cmake (https://github.com/pybind/pybind11/issues/1379)<br />
** gnuradio 3.9 was built using pybind11 version 2.5.0. Your OOT should be built against that same version. (Version 2.4.3 will not work).<br />
** As of May 2021 Ubuntu 20.04 package manager references version 2.5.0, apt install pybind11-dev<br />
<br />
** For some distributions (e.g. Ubuntu <20.04), this will need to be installed from source as 2.4.3 is not the supplied version with package managers<br />
<nowiki><br />
curl -Lo pybind11.tar.gz https://github.com/pybind/pybind11/archive/v2.4.3.tar.gz <br />
mkdir pybind11 && tar xzf pybind11.tar.gz -C pybind11 --strip-components=1 && cd pybind11<br />
mkdir build && cd build <br />
cmake .. -DCMAKE_BUILD_TYPE=Release -DPYBIND11_TEST=OFF <br />
make && make install </nowiki><br />
<br />
* pygccxml [https://pygccxml.readthedocs.io/en/develop/install.html https://pygccxml.readthedocs.io/en/develop/install.html]<br />
** This is an optional dependency and basic functionality for OOT generation can be performed without pygccxml<br />
** It is required for automatically generating bindings for most of the GR source tree<br />
<br />
=== Components ===<br />
<br />
Python bindings are contained in the <code>python/.../bindings</code> directory<br />
<br />
<source lang="sh">./python<br />
└── module_name<br />
├── bindings<br />
│ ├── blockname1_python.cc<br />
│ ├── blockname2_python.cc<br />
│ ├── CMakeLists.txt<br />
| ├── python_bindings.cc<br />
│ ├── docstrings<br />
│ │ ├── blockname1_pydoc_template.h<br />
│ │ ├── blockname1_pydoc_template.h<br />
</source><br />
The bindings for each block exist in blockname_python.cc under the <code>python/bindings</code> directory. Additionally, a template header file for each block that is used as a placeholder for the scraped docstrings lives in the <code>docstrings/</code> dir<br />
<br />
==== blockname_python.cc ====<br />
<br />
This is the class function enum variable bindings for everything that needs to be exposed through the Python API<br />
<br />
==== python_bindings.cc ====<br />
The structure of this file is<br />
<br />
<source lang="cpp">// Headers for binding functions<br />
/**************************************/<br />
/* The following comment block is used for<br />
/* gr_modtool to insert function prototypes<br />
/* Please do not delete<br />
/**************************************/<br />
// BINDING_FUNCTION_PROTOTYPES(<br />
<br />
void bind_blockname1(py::module&);<br />
<br />
// ) END BINDING_FUNCTION_PROTOTYPES<br />
<br />
<br />
PYBIND11_MODULE(module_name__python, m)<br />
{<br />
<br />
{<br />
// Initialize the numpy C API<br />
// (otherwise we will see segmentation faults)<br />
init_numpy();<br />
<br />
// Allow access to base block methods<br />
py::module::import("gnuradio.gr");<br />
/**************************************/<br />
/* The following comment block is used for<br />
/* gr_modtool to insert binding function calls<br />
/* Please do not delete<br />
/**************************************/<br />
// BINDING_FUNCTION_CALLS(<br />
bind_blockname1(m);<br />
// ) END BINDING_FUNCTION_CALLS<br />
}<br />
</source><br />
===== Comment Block =====<br />
<br />
Each block binding file contains an automatically generated and maintained comment block that informs CMake when the bindings are out of sync with the header file they refer to, and what to do about it<br />
<br />
<source lang="cpp">/***********************************************************************************/<br />
/* This file is automatically generated using bindtool and can be manually edited */<br />
/* The following lines can be configured to regenerate this file during cmake */<br />
/* If manual edits are made, the following tags should be modified accordingly. */<br />
/* BINDTOOL_GEN_AUTOMATIC(0) */<br />
/* BINDTOOL_USE_PYGCCXML(0) */<br />
/* BINDTOOL_HEADER_FILE(basic_block.h) */<br />
/* BINDTOOL_HEADER_FILE_HASH(549c06530e2afdf6f2c989017cb5f36e) */<br />
/***********************************************************************************/<br />
</source><br />
<code>BINDTOOL_GEN_AUTOMATIC</code>: Many times for complex in-tree blocks, the automated tools are not entirely sufficient to generate all of the bindings in an automated fashion. In this case, the flag should be set to 0, and the bindings need to be updated manually. If the flag is set to 1, CMake will override the binding file ''in the source tree'' when it detects out of sync bindings. This should only be done in simple cases.<br />
<br />
<code>BINDTOOL_USE_PYGCCXML</code>: Currently there are limitations on the amount of code generation that can be accomplished without the <code>pygccxml</code> dependency. If a block needs pygccxml for the bindings to be properly generated automatically, this should be set to <code>1</code><br />
<br />
<code>BINDTOOL_HEADER_FILE</code>: The header file that bindings are based on, filename only<br />
<br />
<code>BINDTOOL_HEADER_FILE_HASH</code>: The MD5 hash of the header file that the bindings were built on<br />
<br />
=== Workflow ===<br />
<br />
==== Out-of-Tree modules ====<br />
<br />
The steps for creating an out of tree module with pybind11 bindings are as follows:<br />
<br />
# Use <code>gr_modtool</code> to create an out of tree module and add blocks<br />
<br />
<source lang="sh">gr_modtool newmod foo<br />
gr_modtool add bar<br />
</source><br />
<ol start="2"><br />
<li>Update the parameters or functions in the public include file and rebind with <code>gr_modtool bind bar</code></li></ol><br />
<br />
'''NOTE''': without pygccxml, only the make function is currently accounted for, similar to <code>gr_modtool makeyaml</code><br />
<br />
If the public API changes, just call <code>gr_modtool bind [blockname]</code> to regenerate the bindings<br />
<br />
When the public header file for a block is changed, CMake will fail as it checks the hash of the header file compared to the hash stored in the bindings file until the bindings are updated<br />
<br />
<ol start="3"><br />
<li>Build and install</li></ol><br />
<br />
=== Docstrings ===<br />
<br />
If Doxygen is enabled in GNU Radio and/or the OOT, Docstrings are scraped from the header files, and placed in auto-generated <code>[blockname]_pydoc.h</code> files in the build directory on compile. Generated templates (via the binding steps described above) are placed in the <code>python/bindings/docstrings</code> directory and are used as placeholders for the scraped strings<br />
<br />
Upon compilation, docstrings are scraped from the module and stored in a dictionary (using <code>update_pydoc.py scrape</code>) and then the values are substituted in the template file (using <code>update_pydoc.py sub</code>)<br />
<br />
<br />
=== OOT Migration ===<br />
<br />
The easiest way to migrate an OOT to 3.9 is to use <code>gr_modtool</code> to create a new OOT, use <code>gr_modtool add</code> to create the blocks, and copy code from the previous OOT.<br />
<br />
Steps to do this without regenerating a new module are TBD<br />
<br />
=== Caveats ===<br />
<br />
Pybind11 bound methods do not implicitly convert int to enum, so blocks that take enum as input, must have either "raw" or "enum" in the grc yml definition of the block. "Raw" will allow the value to be changed by another variable in the flowgraph.<br />
<br />
Block inheritance must be specified completely in the python bindings in order to use the inherited methods. For instance, if a block inherits from sync_block, both block and basic_block must be included in the inheritance specification of the class:<br />
<br />
<nowiki> py::class_<atsc_interleaver,<br />
gr::sync_block,<br />
gr::block,<br />
gr::basic_block,<br />
std::shared_ptr<atsc_interleaver>>(<br />
m, "atsc_interleaver", D(atsc_interleaver)) </nowiki><br />
<br />
<br />
If your OOT module uses types from or its classes derive from another gr module, it is necessary in <code>python_bindings.cc</code> to specify these modules. <br />
<br />
For instance, since all OOT modules require the base block types, there is a line <code>py::module::import("gnuradio.gr");</code>.<br />
<br />
If you wanted to utilize <code>digital::constellation</code> objects in your OOT, it would be necessary to add <code>py::module::import("gnuradio.digital");</code> so that pybind knows to use the bindings already compiled into gnuradio.digital for the constellation objects<br />
<br />
If your OOT module uses other classes as parameter, you must either setup a python binding for this class or use an existing one. <br />
An example, how to setup a binding for the QWidget class of QT5 used in gr-qtgui, can be found here[https://github.com/gnuradio/gnuradio/blob/master/gr-qtgui/python/qtgui/bindings/QWidget_python.cc]<br />
<br />
If you want to use this binding in your OOT module, you have to import it. This has to be done in the python_bindings.cc inside the '''PYBIND11_MODULE''' macro.<br />
In case of the QWidget usage this would be <code>py::module::import("gnuradio.qtgui.qtgui_python");</code><br />
<br />
==== Using default values ====<br />
<br />
For standard types like int,float, etc. you can set default values as usual.<br />
<code>gr_modtool bind blockname</code> will setup the correct bindings in blockname_python.cc.<br />
<br />
But if you use something like <code> QWidget* parent = NULL </code> gr_modtool generates code like<br />
<code>py::arg("parent") = __null, </code><br />
<br />
But at this point pybind does not know the type of parent, so you have to modify the binding code and cast the type.<br />
<code>py::arg("parent") = (QWidget *) __null, </code><br />
<br />
Another way to come across this issue is to modify your header file from<br />
<code> QWidget* parent = NULL </code> to<br />
<code> QWidget* parent = nullptr </code><br />
<br />
pybind generates <br />
<code>py::arg("parent") = nullptr, </code><br />
<br />
which will be handled correctly<br />
=== Troubleshooting ===<br />
<br />
==== Unable to find pydoc.h ====<br />
<nowiki>fatal error: blockname_pydoc.h: No such file or directory<br />
28 | #include <blockname_pydoc.h><br />
| ^~~~~~~~~~~~~~~~~~~~~~~~~<br />
compilation terminated.</nowiki><br />
<br />
blockname_pydoc.h is generated during compilation based on the template in the docstring directory. When the block is first created in blocktool, this template does not exist. Run <code>gr_modtool bind</code> to generate the appropriate template used as a placeholder for the scraped docstrings<br />
<br />
Also, the scraping of docstrings only takes place at CMake time, so it may be necessary to do a <code>make clean</code> to re-trigger the scraping<br />
<br />
You can also try <br />
<nowiki>rm python/bindings/docstring_status</nowiki><br />
which will reset the docstring scraping target in cmake and re-copy the docstring templates<br />
<br />
==== TypeError: 'modulename_python.blockname' object is not subscriptable ====<br />
<br />
This is caused by an incomplete inheritance chain specified in the binding declaration of the block.<br />
<br />
Instead of<br />
<br />
py::class_<blockname,<br />
std::shared_ptr<blockname>>(m, "blockname", D(blockname))<br />
<br />
Try something like (taking into account your block type)<br />
<br />
py::class_<blockname,<br />
gr::sync_block,<br />
gr::block,<br />
gr::basic_block,<br />
std::shared_ptr<blockname>>(m, "blockname", D(blockname))<br />
<br />
==== Module is Empty ====<br />
<br />
This is usually caused by linker errors that prevent the binding module from being loaded. When you see something like:<br />
<br />
>>> import foo<br />
>>> dir(foo)<br />
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__']<br />
<br />
But you expect foo to have other C++ blocks, try loading the pybind module separately<br />
<br />
cd build/python/bindings<br />
python3<br />
>>> import foo_python<br />
[linker error should be evident here in mangled symbol name]</div>Tom N5EG