-
Notifications
You must be signed in to change notification settings - Fork 142
TUTORIAL 02 Creating CMake.js based native addons with QT Creator
There are many tutorials about creating and debugging native node addons. They are about creating some files by hand, edit them in your favorite text editor, build an addon from them by using commandline tools like node-gyp, and fight against its bugs in gdb commandline. But come on, it's 21st Century, can't we use our favorite C++ IDE to do it?! This tutorial shows you how to use Qt Creator (my favourite C++ IDE) for creating, building, testing and debugging native node addons. (There will be tutorials for CLion, KDevelop and maybe for Visual Studio in the future.)
![](pictures/tutorial02/01 - QT Creator.png)
Before doing anything, please install cmake-js:
npm install -g cmake-js
Then install Node.js/io.js header files and libraries:
cmake-js install
We're gonna create the PI calculator addon from the first tutorial.
First step is to create a new project based on template of Non-Qt Project/Plain C++ Project (CMake Build).
![](pictures/tutorial02/02 - Create Project.png)
Give it a nice name, and put it into an empty folder.
![](pictures/tutorial02/03 - Name and dir.png)
We're creating the debug build first, so when QT Creator asks for Build directory, enter <project folder>/debug-build/
.
![](pictures/tutorial02/04 - Build location.png)
Then we'll land on Run CMake window, pick a generator for your desire.
![](pictures/tutorial02/05 - Run CMake Window.png)
The Arguments are provided by cmake-js. Launch a terminal, go to you project folder, then write:
cmake-js print-configure --debug
This will print cmake configure command, something like this:
cmake /home/gabor/sandbox/qt-creator-node-addon -GNinja -DCMAKE_LIBRARY_OUTPUT_DIRECTORY="/home/gabor/sandbox/qt-creator-node-addon/build/Debug" -DCMAKE_BUILD_TYPE="Debug" -DCMAKE_JS_INC="/home/gabor/.cmake-js/iojs-x64/v1.6.4/src;/home/gabor/.cmake-js/iojs-x64/v1.6.4/deps/v8/include;/home/gabor/.cmake-js/iojs-x64/v1.6.4/deps/uv/include" -DCMAKE_C_COMPILER="clang" -DCMAKE_CXX_COMPILER="clang++"
Since QT Creator will tell for CMake where is your project reside and what generator have you chosen, so those argument won't be required. Mark the printed command from after the -G... argument, and copy it to the clipboard, then paste it into QT Creator Run CMake window's Arguments text box. From the above I'm pasting:
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY="/home/gabor/sandbox/qt-creator-node-addon/build/Debug" -DCMAKE_BUILD_TYPE="Debug" -DCMAKE_JS_INC="/home/gabor/.cmake-js/iojs-x64/v1.6.4/src;/home/gabor/.cmake-js/iojs-x64/v1.6.4/deps/v8/include;/home/gabor/.cmake-js/iojs-x64/v1.6.4/deps/uv/include" -DCMAKE_C_COMPILER="clang" -DCMAKE_CXX_COMPILER="clang++"
Notice: On Windows you have to add -DCMAKE_RUNTIME_OUTPUT_DIRECTORY=your-project-dir-here/build/Debug
to arguments list because Qt Creator is using a Make generator instead of Viusal Studio.
Then click to Run CMake button, then to Finish button.
![](pictures/tutorial02/06 - CMake Ran.png)
Congratulations, the project has been created and configured to build the debug version. (We'll add release configuration later.)
![](pictures/tutorial02/07 - Da Project.png)
Make an src directory in you project's root folder and put there *.cc and *.h files from the NAN addon example module.
Then replace contents of the CMakeLists.txt file with the following (see Tutorial 01 for details):
cmake_minimum_required(VERSION 2.8)
# Name of the project (will be the name of the plugin)
project (addon)
# Essential include files to build a node addon,
# you should add this line in every CMake.js based project.
include_directories(${CMAKE_JS_INC})
# Declare the source files location
file(GLOB SOURCE_FILES "src/*.cc" "src/*.h")
# This line will tell CMake that we're building a shared library
# from the above source files
# named after the project's name
add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES})
# This line will give our library file a .node extension
# without any "lib" prefix
set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node")
# Essential library files to link to a node addon,
# you should add this line in every CMake.js based project.
target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB})
Choose Main Menu / Build / Run CMake then Run CMake window appears agan, click to Run CMake button, then to Finish.
![](pictures/tutorial02/08 - CMake Ran.png)
Notice: when CMakeLists.txt gets modified, you have to run CMake to get Qt Creator notified about the changes you made.
If everything done correctly source files appears in the Qt Creator project.
![](pictures/tutorial02/09 - src folder appears.png)
Notice: If you try to build the project, you will get an error because NAN header is not found.
Choose MainMenu / New File or Project / General / Text File.
![](pictures/tutorial02/10 - New file.png)
Enter package.json
as its name, ensure Path is point to your project's root directory, then click to Next, Finish. Write the following into your package.json
file (see Tutorial 01 for details):
{
"name": "cmake-js-tut-02-module",
"version": "1.0.0",
"description": "Native module calculating estimate of PI",
"dependencies": {
"bindings": "^1.2.1",
"cmake-js": "*",
"nan": "^1.7.0"
},
"scripts": {
"install": "cmake-js compile"
}
}
Notice: since this json file is not part of the CMake project, it is only accessible by selecting File System from left panel's view menu. While you are here, you can delete main.cpp
file that generated by Qt Creator, it's not required.
![](pictures/tutorial02/11 - package.json.png)
It's time to install dependencies. Launch a terminal, go to your project's folder, then enter:
npm install
Notice: since there is an install script configured in package.json, it will execute right after the dependencies installed, so there will be a release build of you module in the <project-directory>/build/Release
folder once the command executed successfully.
Create a new text file called index.js
into the project's root folder, and write the following into it (see Tutorial 01 for details):
module.exports = require("bindings")("addon");
![](pictures/tutorial02/12 - index.js.png)
Create a test application that runs the addon. Create a tests
directory into the project's root, and create a text file named runAddon.js
with the following content:
var addon = require("../");
var calculations = 10000000;
function printResult(type, pi, ms) {
console.log(type, 'method:')
console.log('\tπ ≈ ' + pi
+ ' (' + Math.abs(pi - Math.PI) + ' away from actual)')
console.log('\tTook ' + ms + 'ms');
console.log()
}
function runSync () {
var start = Date.now();
// Estimate() will execute in the current thread,
// the next line won't return until it is finished
var result = addon.calculateSync(calculations);
printResult('Sync', result, Date.now() - start)
}
function runAsync () {
// how many batches should we split the work in to?
var batches = 16;
var ended = 0;
var total = 0;
var start = Date.now();
function done (err, result) {
if (err) {
return;
}
total += result;
// have all the batches finished executing?
if (++ended == batches) {
printResult('Async', total / batches, Date.now() - start)
}
}
// for each batch of work, request an async Estimate() for
// a portion of the total number of calculations
for (var i = 0; i < batches; i++) {
addon.calculateAsync(calculations / batches, done);
}
}
runSync();
runAsync();
![](pictures/tutorial02/13 - runAddon.js.png)
Ok, you can now run the above test application that runs you addon's code. Launc a terminal, go to project's root, then enter:
node tests/runAddon.js
![](pictures/tutorial02/14 - OK.png)
It runs despite you haven't built anything yet in Qt Creator, because if you remember, npm install
did a full rebuild for the CMake project.
Let's tune up Qt Creator's build process. The native addon is loaded by the bindings module. It looks for *.addon files by the following order:
- build/addon.node
- build/Debug/addon.node
- build/Release/addon.node
- out/Debug/addon.node
- Debug/addon.node
- out/Release/addon.node
- Release/addon.node
- build/default/addon.node
- compiled/x.x.x/darwin/xyy/addon.node
Qt Creator's clean command by default only calls the given generator's clean command, so if you are working with a Release build, then it cleans only the files reside in the build/Release/
folder. So if you did a Debug build before, then switch to Release, then do a clean, it won't delete the build/Debug/addon.node
, so you will run the Debug mode build, even you are working with the Release configuration.
So we will fix this. Go to Projects menu, click to Add Clean Step, choose Custom Process Step.
![](pictures/tutorial02/15 - Add Clean Step.png)
Enter:
- Command:
cmake-js
- Arguments:
clean
- Working directory:
%{sourceDir}
![](pictures/tutorial02/16 - Clean Steps Done.png)
So, when you do a clean operation in Qt Creator, the entire build folder gets removed. Let's try it by choosing Main Menu / Build / Clean All! This time Qt Creator will invoke cmake-js' clean command that removes all compiled module files for sure, not just that belongs to the actual build configuration.
![](pictures/tutorial02/17 - Cleaned.png)
After modifying clean steps, by some reason, Qt Creator forgets CMake arguments, so please click to Run CMake int the Projects menu, enter Arguments from result of cmake-js print-configure --debug
, then click to Run CMake button, and to Finish (like you did in the Creating the project chapter).
Since you created the debug configuration first, the release one that needs to be created. Open Projects menu, and rename the actual configuration to Debug, by clicking on Rename... button on top of the form.
![](pictures/tutorial02/18 - Renamed to Debug.png)
Click to Add then choose Clone Selected. Enter new configuration name: Release.
New build configuration has been created. Change its Buld directory setting to <project-name>/release-build
. Then you will land on Run CMake window again, but this time its arguments needs to be changed to do release build. So pick a Generator, then launch a terminal, go to you project folder, then write:
cmake-js print-configure
It will prints CMake arguments to do a release build (because there is no --debug
switch). Copy its printed arguments to Run CMake window's Arguments value like you did in Creating the project chapter. Now click to Run CMake button, then to Finish.
![](pictures/tutorial02/19 - Release CMake.png)
Cool, you have a Release configuration. You can switch between Debug and Release configurations by clicking to the monitor like icon in the menu, right below the Help button.
It's time to run the stuff. Got to Projects menu, choose your kit's Run configuration settings. Click to Add / Custom Executable.
Enter:
- Executable:
node
- Arguments:
tests/runAddon.js
- Working directory:
%{sourceDir}
![](pictures/tutorial02/20 - Run config.png)
Then you can remove the defult Run configuration.
Now you can Run the project (CRTL+R, or green arrow button in the menu). Its result will be shown in the Application Output window.
![](pictures/tutorial02/21 - Ran.png)
It's easy as goblin pie. Switch to Debug configuration, then do a Clean All operation.
Open any of the source files, and put a breakpoint anywhere.
![](pictures/tutorial02/22 - BP.png)
Then start debugging (F5), and you breakpoint gets hit, you can debug your native module like a boss.
![](pictures/tutorial02/23 - BP Hit.png)
Notice: debug symbols of your module are available of course, but node and v8 symbols are only accessible for the debugger if you are using a node debug build. In most cases you just don't need this level of details to find issues of your own code.
Your module is completed. It can be pushed to github or released to the npm. Cunsumers can isntall it by using regular npm install command (see Tutorial 01 for details). They have to install CMake to get your module compiled first, but it's a minor PITA compared to Python 2.x dependency that node-gyp based modules require, isn't it?