# GNU Radio Build and Runtime Dependencies

## Required Packages

|Key:                                     |
-------------------------------------------

|C: Conan based dep                       |
|CM: CMake based dep                      |
|P: python/pip based dep                  |
|S: system library, dont bundle or install|

| All platforms | |
|----------------|-----------------|
| qt/5.15.11 | C |
| libsndfile/1.2.2 | C |
| fftw/3.3.10 | C |
| gsl/2.7.1 | C |
| zeromq/4.3.5 | C |
| qwt/6.2.0 | C |
| gmp/6.3.0 | C |
| volk/1.3.268.0 | CM |
| UHD | CM |
| OpenGL | S |
| portaudio | CM |
| GTK | C/CM |
| Mako | P |
| Cairo | P |
| numpy | P |
| scipy | P |
| pybind11.2.11.1 | C |
| SoapySDR | CM |

|Linux Only | |
|------------|--|
| ALSA | S |
| OSS | S |
| libad9361 | S |

| Docs | |
|----------|----|
| Doxygen | C |

libunwind, sdl2, and libiio should be installed apt/yum/zypper/etc on Linux, brew on MacOS, and by downloading their binaries and extracting to the same place you'll be installing your CMake install based dependencies on Windows.

.. Note: Qt and QWT _must_ be built as shared binaries

### Installer deps

Wix (install by hand, ensure candle and light come bundled)
NSIS (install by hand)
DragNDrop - available by default on MacOS
DMG - available by default on MacOS
Flatpak - install on Linux - experimental (works AFAICT)

## Building Notes for deps

### Conan

The conan toolchain is currently very broken, do not use this.

Instead for detection, add the conan generator directory to CMAKE_PREFIX_PATH on the command line
Additionally, it is important to set `CMAKE_FIND_PACKAGE_PREFER_CONFIG` to ON on the CMake command line
to ensure all Conan modules are discovered before any system or CMake modules as Conan modules are not compatible
with those vendored by CMake.

### libusb

Required by UHD
Driven by CMake script
MSBuild on Windows
Autotools on MacOS

> &#9432; **Note**
    **THIS MUST BE THE SAME RUNTIME AS GNU Radio + Boost ARE BUILT WITH**
    This should be accomplished by running CMake from the appropriate VSDev prompt

### Boost

Boost installed by Spack or Conan requires `"-DCMAKE_CXX_FLAGS=-DBOOST_ALL_NO_LIB /EHsc"` specified in consuming project.
`-DBOOST_ALL_NO_LIB` is utilized in order to link against non-version mangled boost libs (those built with )
> &#9432; **Note**
    This is not required if boost is built with this option
    Currently required with Spack + Conan built Boost

`/EHsc` is specified to allow for Boost's exception handling semantics. Otherwise boost will fail to link

Need to be careful of boost dyn lib arguments

Do not let CMake's vendored FindBoost.cmake be the module to detect Boost, it will find the headers installed by conan but not the libs. This results in somewhat confusing linker errors as `${Boost_LIBRARIES}` is defined empty so no boost libs are included in the linker line.

Ensure both conan and cmake are building 64bit release configuration to build GNU Radio in same

Remember to specify Boost static libs argument (found in my CMake scripts for UHD)

When building the `cmake-deps` dependencies, the final CMake invocation should look something like:

``` bash
cmake .. -GNinja -DCMAKE_PREFIX_PATH=C:\dev\gnu-radio\deps\build\generators -DCMAKE_INSTALL_PREFIX=C:\dev\gnu-radio\deps\cmake-deps -DCMAKE_FIND_PACKAGE_PREFER_CONFIG=ON "-DCMAKE_CXX_FLAGS=-DBOOST_ALL_NO_LIB /EHsc"
```

### Python based deps

For each package that depends on one of these, you need to be sure
that the Python you "pip install" those py deps with is EXACTLY the same
python that GNU Radio is finding during its CMake run
Otherwise there will be conflicts between GNU Radio, it's installed deps, and its
bundled runtime python deps

### General Build notes for deps

* CMake based builds need to be in release configuration to link against Conan targets
Spack can be in either configuration, so just be mindful about your variants

* Need to be sure conan profile compiler version matches msvc compiler version picked up by cmake

## CMake Variables of consideration for the Windows Wix installer

_CPACK_WIX_PRODUCT_GUID_ - this is an identifier used by the installer + Windows to denote a a given installation. If this is the same value as an existing installation on the system the installer will error out indicating the software is already present. CPack will automatically generate this value, ensuring that each build of the installer has a unique GUID and can thus be re-installed.

It is recommended to not set ths variable and to allow CMake to generate it

_CPACK_WIX_UPGRADE_GUID_ - this is an identifier used by Wix + Windows to identify an installer as an "upgrade" rather than a new package altogether (ensures old version is replaced). The value in this proect was generated by `guidgen` and should not be modified, otherwise the installer will be unable to properly replace previous installations

_CPACK_WIX_SHORTCUT_GUID_ - This is not an official CPack variable but rather one created for the GNU Radio installer. It is used to allow the installer to identify previouslly created GRC start menu shortcuts and replace them on re-install.
This was generated by `guidgen` and should not be modified.

## Build notes and instructions for GNU Radio on Windows & MacOS

Thift support is fully broken on Windows. You can build without issue, but importing _anything_ related to GR will deadlock and hang.

Need to add numpy to include interface
-DPython_NumPy_INCLUDE_DIR=

The GTK bin directory should be set via the GTK_BIN_DIR variable on the CMake command line or in a CMake preset. If it is not set, GR will default to
the default GVS build location on Windows.

## Notes for the GTK

Install pipx + GVSBuild:
    ``` bash
    python -m pip install --user pipx
    python -m pipx ensurepath
    pipx install gvsbuild
    pipx ensurepath
    ```

Add `C:\gtk-build\gtk\(x64|x86|ARM)\release\lib` and the  to the LIB environment variable
Add `C:\gtk-build\gtk\(x64|x86\ARM)\release\include` to the INCLUDE environment variable
Note: these directories do not exist yet, but they do seem to make the build go smoother if set prior to building, as it allows subsequently built deps to find their dependent headers/libs

Run the GVS build command
    `gvsbuild build --enable-gi --py-wheel gtk3 pygobject`
Note: it is imperative to build gtk3 with GI and Python enabled from the start. The docs imply that everything can be rebuilt to enable the Python bindings after initial install of GTK, and this does produce the bindings,
but anything previously built will _not_ have introspection support, and while you'll be able to
`import gi` all subsequent commands will fail.

Then, in the same python environment you'll use to build GNU Radio, run:

``` bash
pip install --force-reinstall (Resolve-Path C:\gtk-build\build\x64\release\pygobject\dist\PyGObject*.whl)
pip install --force-reinstall (Resolve-Path C:\gtk-build\build\x64\release\pycairo\dist\pycairo*.whl)
```

Finally, add the path to the GTK built BIN to the PATH `C:\gtk-build\build\x64\release\bin`
Additionally, you'll need to inform GR's CMake system where the GTK bin is, by setting the `GTK_BIN_DIR` CMake variable in the cache, commandline or GUI.

To verify that the GTK was installed correctly, add the GTK bin directory to the path and initiate a Python session from the Python venv you're using to build GR and run the following:

```bash
>>> import os
>>> os.add_dll_directory("<path to gtk bin>")
>>> import gi
>>> assert gi.version_info >= (2, 28, 6)
>>> gi.require_version('Gtk', '3.0')
>>> gi.require_foreign('cairo', 'Context')
>>> gi.require_version('PangoCairo', '1.0')
```

..Note: It is imperative that the path to the GTK bin is added to the PATH AND is set via `os.add_dll_directory`, both steps are required. Typically on Windows adding DLLs to the PATH is sufficient, however due to the mechanisms the GTK relies on to load dlls, Python needs to be aware of the dll locations, but because these DLLs have additional dependencies (located within the bin of the GTK) which are loaded by the system not Python, the system needs to be able to find them, so both settings are required.

## PyQt6 Bindings

These must always be built from source to ensure they share the same shared libraries with GR's C++ Qt interface.

The developers of the SIP bindings (those used by GR) have created a great walkthrough on how to complete this: <https://www.riverbankcomputing.com/static/Docs/PyQt6/installation.html#building-and-installing-from-source>

In general, following this works cleanly, particularly on MacOS. I recommend using the `sip-build --no-make` approach so you can manually invoke make/jom with the proper `-j` flags to better utilize all system cores, the default builds in serial, which is very slow.

### MacOS

On MacOS, no more steps should need to be taken, so long as the QMake used to build the SIP bindings corresponds to the same Qt6 install used to build GR, everything should _just work_. That said, extreme care needs to be take to ensure this is the _exact_ Qt installation that will be used to build GR.

### Windows

On Windows, Qt6 _MUST_ be built in a shared configuration, static WILL NOT WORK. Both for this, and for building GR in general.

Further, you may see an error along the lines of: `sip/QtCore/qprocess.sip QPID is undefined`. The workaround is to take the typedef: `typedef void *Q_PID;` and define it in the beginning of that file. That will cause the file to be generated correctly, but will result in a redefinition error for Q_PID. Remove the line you just added and things should then just work.
Also be sure that you have not installed the `PyQt6.sip` module yet. That will need to be installed before GR is built, but not sooner.

Finally, this is not required, but strongly recommended, the SIP builder will generate NMake files on Windows, which do not have any inherent support for parallel builds or installs. The Qt project has a workaround in the form of a tool name JOM. Install Jom from QT, and instead of running `nmake` to build the SIP bindings, simply run JOM (after adding it to the PATH of course) and the project will build substantially faster.

## Building GR

### Windows

Starting from the directory above where GNU Radio's source is checked out:

1. Create a Python environment in which to build GR via the command `python -m venv gr-venv` and then activate this environment `gr-venv\Scripts\activate.bat`.
2. Install all required Python dependencies with `pip install -r gnuradio/release/requirements.txt`
3. Conan will have been installed by the previous step, run conan to install all packages supplied by conan: `conan install gnuradio/release --output-folder=deps --build=missing`. This will create a directory named `deps/build/generators` populated with CMake config modules.

    3.1 You must first setup conan before using it to install packages. Do this by running `conan profile detect --force`
4. Next we build the CMake based dependencies.

    4.1 `copy gnuradio/release/deps ./cmake-deps`
    4.2 `cd cmake-deps && mkdir build && cd build`
    4.3 `cmake .. -GNinja -DCMAKE_INSTALL_PREFIX=deps -DCMAKE_PREFIX_PATH=deps/build/generators -DCMAKE_FIND_PACKAGE_PREFER_CONFIG=ON "-DCMAKE_CXX_FLAGS=-DBOOST_ALL_NO_LIB /EHsc"`
    This command line can also be found above.

5. We then manually install libiio, sdl2, and libunwind and these projects are not so easily installable from source on Windows.

6. Next we need to install the GTK, this can be accomplised by following the instructions in that section. Once the GTK has been installed locally, follow the instructions for installing the GTK wheels while still in the Python environment we created in step 1.

7. We now need to build the Qt6 bindings from source. THIS IS REQUIRED. If you install the pyqt6 python package from pip, that will work to build GR. But you'll end up with a GR install that is fully incapable of running any mixed language Qt tasks (i.e. GR qtgui and Python Qt)

    To do this, follow the Qt python bindings instructions found earlier in this document, and then, while in the python environment we created in step 1, run `jom install`.

    ..Note: The bindings must be built in the same python environment as you plan on install them into.

8. We're not ready to build GR! To do this, navigate to gnuradio's source root and `mkdir build`.

    Then the most basic command is `cmake .. -GNinja -DGR_BUILD_INSTALLER=ON -DCMAKE_PREFIX_PATH=<full-path-to>deps/build/generators;<full-path-to>deps -DCMAKE_FIND_PACKAGE_PREFER_CONFIG=ON "-DCMAKE_CXX_FLAGS=-DBOOST_ALL_NO_LIB /EHsc"`

9. Once that completes successfully, from the root of the build tree, run `cpack -GWix` and you should have a complete Windows installer for GR!

### MacOS

1. Create a Python environment in which to build GR via the command `python -m venv gr-venv` and then activate this environment `source gr-venv\bin\activate.sh`.
2. Install all required Python dependencies with `pip install -r gnuradio/release/requirements.txt`
3. Now install the remaning dependencies with brew via `brew install x`
4. We're now ready to build GR, from the build directory: `cmake .. -GNinja -DGR_BUILD_INSTALLER=ON -DCMAKE_FIND_PACKAGE_PREFER_CONFIG=ON`
5. Then from the build directory `cpack -GDragNDrop` for GR and `cpack -Gproductbuild` for GR OOT Modules

## Troubleshooting

### Qt

If you observe issues along the lines of being unable to load Qt modules/dlls, this is likely because you have built Qt6 python bindings with a different Qt used to build GR's Qt bindings. Rebuild

If you are unable to load Qt's platform plugins, this is likely due to the path QT is using to search for those plugins, or they were not installed with GR. Qt expects these to be installed relative to the current executable (so Python for GRC) under `plugins/<platform>/*.[so|dll]`.

This path can be re-routed by setting the `QT_PLUGIN_PATH` env variable.

To debug other Qt issues when building or running GR, it is often useful to set the variable `QT_DEBUG_PLUGINS`.
