This document explains how to create, configure and share a NAP application or module.
All project management utilities reside in the tools
directory under the NAP root. Convenience shortcuts to regenerate the solution and package the application also sit within each app. We'll go over the basic tasks here and then cover some more advanced topics in the Custom CMake section for those who want to take things further.
We will use the unix shell syntax in this document, for convenience and readability. If you're on Windows and using the prompt, simply replace ./*.sh
with *.bat
. Any exception to this rule will be clearly documented.
This document assumes you are working from a pre-compiled distributable NAP package. However, some people prefer working with NAP directly from source. Fortunately most instructions in this document are the same for both contexts, except some paths. Additional information is provided when this is the case.
A NAP application is a pre-compiled program, made using NAP Framework, that runs stand-alone on a client machine.
Follow these steps to create a new application titled MyFirstApp
.
The application will be located in apps/MyFirstApp
. This directory contains your application source-code, assets, makefiles (Linux) or visual studio solution (Windows) and build instructions.
Within each application folder you'll find the app.json
file which defines various project specific settings, such as: the name of your app , which modules to include and which content to load:
Note that when working with NAP from source your new application is added to the overall solution in solution_info.json
, which includes your app as a target for the build system. When you delete your application you must (manually) remove the application and application module from solution_info.json
, otherwise the build system will fail to generate the solution.
The most important module is napMyFirstApp
. This is your application module, located in the module
directory of your app. This directory contains the resources and components that are specific to your application. This module is created and added by the build system when the app is created. You can omit the creation of the application module by appending --no-module
to tools/create_app.sh
.
You can add external modules to your application by modifying the RequiredModules
property in app.json
. The module name should match the module directory name in /system_modules
or /modules
. For example: add napaudio to add audio functionality or napmidi to add mini functionality to your application.
/system_modules
directory contains modules distributed with NAP./modules
directory contains user modules.If you want your application module to have access to the resources and components of an external module you must modify the RequiredModules
property in module/module.json
instead of app.json
.
Run ./regenerate.sh
inside your application folder to update the solution. Always run this script after making changes to app.json
or module.json
Run ./package.sh
to compile and install a release build of your application into a distributable package. The self-contained package contains everything that your app needs to run stand-alone when extracted (except system libraries), including:
The package is archived to a .zip
on Windows and tar.xz
on Linux. The package does not include system libraries such as the Visual C++ redistributable
. Options for creating installers for projects may be explored for a future NAP release.
Packaging an application with default settings:
By default, projects are compressed and contain Napkin. Projects can be left uncompressed by adding the option --no-zip
. Napkin can be left out via --no-napkin
. Excluding Napkin can save a considerable amount of space if you don't intend on using it. Other minor options and shorthand versions of the options above can be viewed by running package --help
.
A user module contains more generic (not app related) resources and components that can be added to your NAP application.
A list of publicly available user modules can be found at modules.nap-framework.tech. These modules are created, maintained and shared by NAP users, independent of NAP Framework.
Download the module as .zip
archive from (for example) Github and install it into the nap modules
directory:
Clone the repository and set up the module in the nap modules
directory:
Follow the steps below to create a new module named MyFirstModule
:
The module will be created in modules/napMyFirstModule
. This directory contains your module source-code, assets and build instructions. This module is not app specific and can be added to every NAP application.
Add napMyFirstModule
to the list of RequiredModules of your app or module to include it in your project.
Within each module folder you'll find the module.json
file. This file defines various module specific settings, such as which modules it depends upon and which third-party library search paths to use.
You can add dependencies to your module by modifying the RequiredModules
property in module.json
. The module name should match the module directory name in /system_modules
or /modules
. For example: add napaudio to add audio functionality or napmidi to add mini functionality to your application.
Run ./regenerate.sh
inside your application folder to update the solution if your application uses the configured module](). Always run this script after making changes to app.json
or module.json
A target that links to a NAP module, that depends on a third-party dynamic library, must know where to find it. Modify the LibrarySearchPaths
property in module.json
to add library search paths to any target (executable or module) that includes your module:
This tells the build system to add the (above-mentioned) paths as rpaths to any target that includes the napfont system module, based on the build configuration and NAP root directory.
Note that the Windows
element of LibrarySearchPaths
is currently not used. It will however, in a future version of NAP, replace the deprecated WindowsDllSearchPaths. It is therefore recommended to already provide it.
This section only applies to Windows. If your module links to a dynamic (third party) library NAP must be made aware of where it can find it. Otherwise, Napkin won't be able to open the project because it cannot load the third-party library requested by your module.
You can tell the system where to attempt to find the library by modifying the WindowsDllSearchPaths
property in module.json
. Take as an example the module.json
of napfont in system_modules
:
This entry tells NAP to look for the freetype.dll
(on which napfont depends) in the above-mentioned directories, both relative to the NAP root. Depending on the current build configuration the system will load either the Debug or Release version.
Note that in a future version of NAP the WindowsDllSearchPaths
property will be deprecated and replaced by the Windows
element of LibrarySearchPaths
This entry defines paths to search for module data in, use these locations to store for example hardcoded shader files of your module.
Use the Module::findAsset function to retrieve paths e.g.: mYourModuleService->getModule()->findAsset("someshader.frag")
You can share your module with others on modules.nap-framework.tech. A typical module has the following content, which will be included when the module is shared:
Content | Directory | Required |
---|---|---|
source code | src | yes |
thirdparty dependencies | thirdparty | no |
demo application | demo | no |
Run tools/prepare_module_to_share
with as the input argument the module you want to share, for example:
This prepares a copy of the MyFirstModule
module that is optimized for sharing. The copy of the module is moved next to the nap root. Add the –help flag for more information about the available options.
Upload the contents of your module to a service of your liking, for example Github or Gitlab.
Share your module on modules.nap-framework.tech by following the registration procedure in the readme.
We've focused on providing a streamlined build environment for people to start making applications and modules, together with trying out some demos. But sometimes that's not enough: you need to add your own build logic. For that purpose we've provided additional hooks that you can use to add custom CMake to your project and include third-party library dependencies.
You can add additional CMake logic at the application level by providing an app_extra.cmake
file in the root of the application directory. If app_extra.cmake
exists its content will be included into the application template when the solution is regenerated.
Within app_extra.cmake
${PROJECT_NAME}
can be used as in any standard CMake project and ${NAP_ROOT}
points to the root of the NAP installation. Below is an example of a simple app_extra.cmake
with an added include path:
In this example target_include_directories is used with ${PROJECT_NAME}
referring to the project target and CMAKE_CURRENT_LIST_DIR
being used to refer to the application root directory. It's important to remember that app_extra.cmake
is included within NAP's existing CMake project and as such doesn't replace the existing template. As a result this limits what functions make sense within this supplementary file.
In rare cases it might be desirable to execute extra logic before the target is defined. A hook for that has been provided via app_extra_pre_target.cmake
.
Note that the CMakeLists.txt
file within the application root should not be modified. It is automatically generated by the build system and will be overwritten when the solution is regenerated.
When an application is packaged the entire data
directory from the app is included, alongside the core libraries, the modules and their third party dependencies. If you need to include anything extra into the packaged project do so using CMake's install command. Below is an example app_extra.json
that install an extra file example.txt
from the application root into the packaged application.
You can add additional CMake logic at the user module level by providing an module_extra.cmake
file in the root of the module directory, similar to app_extra.cmake. If module_extra.cmake
exists its content will be included into the module template when the solution is regenerated.
This section explains how to add a third-party dependency to your module using the module_extra.cmake file. The same steps apply when adding a third-party dependency directly to your application, substituting module_extra.cmake
with app_extra.cmake
. We're going to use CMake modules for this. Although other options are available, this is the preferred method.
Note that these instructions are by no means complete - they only scratch the surface of what's possible. Actual and better implementations can be found on modules.nap-framework.tech, where many of the shared modules have a third-party dependency.
Let's add an imaginary (pre-built) dynamic library called libfoo
to napMyFirstModule
. All third-party dependencies are stored in the thirdparty
directory under the project (module or app) root, in this case:
The first step for including libfoo
will be to create (or import) a find package file. This file defines and exposes all the pieces of information you need to include the libfoo
in your project. Many third party libraries will come with one ready for you to use, but we're going to create a simple one from scratch. For brevity and simplicity I limited this example to Windows, but it can easily be extended to work for other systems.
Create a cmake_find_modules
directory inside the thirdparty
directory:
Within the directory create a file called Findfoo.cmake
and add the following content:
Now it's time to add libfoo
to napMyFirstModule
. Create a file called module_extra.cmake
in the root of the module directory (if it doesn't exist already) and add the following content:
So what did we do here? find_package locates and imports the libfoo
library into our project. We then include it with target_include_directories and link to it with target_link_libraries. Finally, to ensure the program runs on Windows, we copy the DLL to the build output directory using a custom build command.
Note that on Unix based systems you must install the libfoo
library into the lib
directory when your application is packaged. To do that append the following to module_extra.cmake
:
On Windows the previously defined add_custom_command
already takes care of that. Note that the packaged application won't run if you forget to install the libfoo
library during packaging.
One thing to keep an eye out for on macOS is the install name of the third party libraries that you're attempting to integrate.
The install name of a shared library can be viewed in macOS using the command otool -D
. See this example, showing mpg123:
If that install name were allowed to remain this way any library linked against libmpg123.0.dylib would look for it at the path shown above. What we want to end up with instead is having our library found in its relative position within the NAP framework directory.
To achieve that let's update the library's install name, prefixing it with @rpath
:
We can then check the results by running otool
again:
Anything using our library with the updated install name (in this case mpg123) would then need to be rebuilt to pull in the changed path.
One risk with RPATH management and third party libraries is in the case where you're integrating a library that you also have installed on your system via (for example) Homebrew or MacPorts. In this case it's possible to unknowingly be building and running against the system-level third party library. There are a number of ways to detect this, one of which is while running your project using lsof
in a terminal and grepping the output to ensure that the correct instance of your library is being used. If this issue goes unresolved you're likely to run into problems when the project or module is used on another system.
NAP uses a path mapping system based on simple JSON files to manage the directory relationships between the project binaries, Napkin and modules in the different arrangements of working against a Framework Release, from a packaged app, or even against source. The path mapping for the platform and context you're using will be deployed by the build system to cache/path_mapping.json
relative to both the project directory and the project binary output.
In the very large majority of cases NAP will manage these paths for you and you won't need to pay attention to this system. There is however a provision for custom project-specific path mappings which get pulled in from the directory config/custom_path_mappings
within the project directory, containing content similar to tools/platform/path_mappings
in the Framework Release. Only the platforms and contexts you want to modify should be defined. NAP will locate these custom mappings and deploy them as expected.
Custom path mappings are fairly beta at this stage. If you end up finding a need for them, we would be interested in hearing from you