Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion components/Sidebar/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const PrefetchLink = props => <a {...props} rel="prefetch" />;
*/
export default ({ metadata }) => (
<SideBar
pathname={`/learn${metadata.path}`}
pathname={`/learn${metadata.path.replace('/index', '')}`}
groups={sidebar}
onSelect={redirect}
as={PrefetchLink}
Expand Down
125 changes: 125 additions & 0 deletions pages/napi/build-tools/cmake-js.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
---
authors: gabrielschulhof, NickNaso, jschlight, mhdawson, KevinEady, avivkeller
---

# CMake.js

[CMake.js](https://github.com/cmake-js/cmake-js) is good build tool alternative to [node-gyp](/learn/napi/build-tools/node-gyp.md). CMake.js is based on the [CMake](https://cmake.org) tool which must be installed.

### Pros

- Uses the CMake tool which is widely-adopted in the open source community.
- Ideal for existing C/C++ libraries already based on CMake.

### Cons

- Not widely adopted in the Node community.

## Installation

CMake.js requires that CMake is already installed. Installers are available on the [CMake website](https://cmake.org).

> macOS developers may find it more convenient to install CMake using [Homebrew](https://brew.sh). With Homebrew installed, CMake can be installed using a `brew install cmake` command.

You can verify your CMake installation with the command:

```bash
cmake --version
```

As a Node native module developer, you may find it convenient to install CMake.js as a global command line tool:

```bash
npm install cmake-js -g
```

You can verify your CMake.js installation with the command:

```bash
cmake-js --version
```

## package.json

Your `package.json` file needs to have a couple of entries for your native module to work with CMake.js.

Since your native module needs to be compiled using CMake.js on installation, the `scripts` property of your `package.json` file needs an `install` entry to make this happen:

```json
"scripts": {
"install": "cmake-js compile"
}
```

It is unlikely that the users of your native module will have CMake.js installed as a global command line tool. Therefore, your project needs to declare a development dependency on CMake.js. This can be accomplished by entering this command:

```bash
npm install cmake-js --save-dev
```

An alternative is to manually add the development dependency to your `package.json` file:

```json
"devDependencies": {
"cmake-js": "^6.0.0"
}
```

An example of this approach is [available here](https://github.com/nodejs/node-addon-examples/blob/main/src/8-tooling/build_with_cmake/node-addon-api/package.json).

## CMakeLists.txt

Native modules built on CMake.js have a `CMakeLists.txt` that describes how the module is to be built. The file serves the same purpose as the `binding.gyp` for projects that use `node-gyp`.

In addition to the entries required for any CMake build, additional entries are required when building native modules.

### CMake.js

Here are the lines required for all native modules built using CMake.js:

```cpp
project(napi-cmake-build-example)
include_directories(${CMAKE_JS_INC})
file(GLOB SOURCE_FILES "hello.cc")
add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${CMAKE_JS_SRC})
set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node")
target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB})
```

### NAPI_VERSION

When building a native module based on Node-API, it is important to declare the minimum Node-API version against which your module is designed to work. For CMake.js, this is accomplished by adding a line like this to the `CMakeLists.txt` file:

```cpp
# define NAPI_VERSION
add_definitions(-DNAPI_VERSION=3)
```

> In the absence of other requirements, Node-API version 3 is a good choice as this is the Node-API version active when Node-API left experimental status.

### node-addon-api

Additional configuration values are required for Node-API modules based on `node-addon-api`.

`node-addon-api` requires C++11. These configuration lines at the top of the `CMakeLists.txt` file specify this requirement:

```cpp
cmake_minimum_required(VERSION 3.9)
cmake_policy(SET CMP0042 NEW)
set (CMAKE_CXX_STANDARD 11)
```

Modules based on `node-addon-api` include additional header files that are not part of Node itself. These lines instruct CMake.js where to find these files:

```cpp
# Include Node-API wrappers
execute_process(COMMAND node -p "require('node-addon-api').include"
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE NODE_ADDON_API_DIR
)
string(REPLACE "\n" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})
string(REPLACE "\"" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})
target_include_directories(${PROJECT_NAME} PRIVATE ${NODE_ADDON_API_DIR})
```

An example of this approach is [available here](https://github.com/nodejs/node-addon-examples/blob/main/src/8-tooling/build_with_cmake/node-addon-api/CMakeLists.txt).
22 changes: 22 additions & 0 deletions pages/napi/build-tools/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
authors: avivkeller
---

# Build Tools

Building a native Node.js addon requires compiling C/C++ source code into a binary `.node` file that Node.js can load at runtime. This section covers the build tools available to you and the trade-offs between them.

## Compiling on installation vs. distributing pre-built binaries

There are two broad strategies for shipping a native addon:

**Compile on the user's machine** - When a user runs `npm install`, the build tool compiles your C/C++ source on their system. This is simple to set up but requires every user to have a working C/C++ toolchain installed.

**Distribute pre-built binaries** - You compile binaries for each supported platform and architecture ahead of time and upload them somewhere users can download. Users who download a matching binary skip the compilation step entirely; others fall back to compiling locally.

## Build tools covered in this section

- [node-gyp](/learn/napi/build-tools/node-gyp.md) - the default build tool bundled with npm; uses Google's GYP format and is nearly universally supported in the Node ecosystem
- [CMake.js](/learn/napi/build-tools/cmake-js.md) - a CMake-based alternative, well-suited for projects that already use CMake
- [node-pre-gyp](/learn/napi/build-tools/node-pre-gyp.md) - a layer on top of node-gyp for distributing pre-built binaries via Amazon S3
- [prebuild](/learn/napi/build-tools/prebuild.md) - an alternative pre-build tool that publishes binaries as GitHub Releases
30 changes: 30 additions & 0 deletions pages/napi/build-tools/node-gyp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
authors: gabrielschulhof, NickNaso, jschlight, mhdawson, KevinEady, avivkeller
---

# node-gyp

[node-gyp](https://github.com/nodejs/node-gyp) is the standard build tool for native Node.js addons and is used by the vast majority of packages in the npm ecosystem. It is actively maintained by the Node.js team. Most of the examples on this site use node-gyp to build binaries.

node-gyp is based on Google's [GYP](https://gyp.gsrc.io/) build tool. GYP provides a single cross-platform configuration format for C/C++ builds. Although Google archived the upstream GYP repository, node-gyp continues to receive active development and maintenance independently.

> node-gyp requires **Python 3.6 or later**. Python 2 is not supported. The full list of requirements for each platform can be found in the [node-gyp installation docs](https://github.com/nodejs/node-gyp#installation).

node-gyp is included with npm; when npm sees `"gypfile": true` in `package.json`, it invokes node-gyp automatically during `npm install`. You can also install and use it directly:

```bash
npm install -g node-gyp
```

For developers who find node-gyp too constraining, [CMake.js](/learn/napi/build-tools/cmake-js.md) is a good alternative.

### Pros

- Included with npm - no separate global install required for consumers.
- Nearly universally used in the Node.js ecosystem, with broad documentation and community knowledge.
- Supports Windows, macOS, and Linux from a single `binding.gyp` configuration file.

### Cons

- The underlying GYP format is no longer actively developed by Google.
- Some developers find GYP's configuration syntax verbose or difficult to debug.
157 changes: 157 additions & 0 deletions pages/napi/build-tools/node-pre-gyp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
---
authors: gabrielschulhof, NickNaso, jschlight, mhdawson, KevinEady, avivkeller
---

# node-pre-gyp

One of the limitations of native addons is that they must be compiled for each target platform and architecture. Without pre-built binaries, every user who installs your package must have a working C/C++ toolchain on their machine.

[node-pre-gyp](https://github.com/mapbox/node-pre-gyp) solves this by letting you build binaries ahead of time, upload them to a remote location, and have users download the right binary at install time - falling back to compiling from source only if a matching binary is not available.

> Note that Node-API support was added to node-pre-gyp in version 0.8.0.

> [prebuild](/learn/napi/build-tools/prebuild.md) is an alternative tool that addresses the same problem.

This page describes the changes required to a Node-API addon to support node-pre-gyp.

## Amazon S3

By default, node-pre-gyp uploads binaries to [Amazon S3](https://aws.amazon.com/s3/).

> The [node-pre-gyp-github](https://github.com/bchr02/node-pre-gyp-github) module adds support for publishing to GitHub Releases instead.

### Amazon S3 Requirements

Before uploading you need:

1. An Amazon Web Services account.
2. An IAM user or role with permission to upload to S3.
3. An [S3 bucket](https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingBucket.html) to host the binaries.

### AWS Credentials

Never store credentials in your repository. node-pre-gyp supports two common approaches for providing credentials during development:

1. A `~/.node_pre_gyprc` file:

```json
{
"accessKeyId": "xxx",
"secretAccessKey": "xxx"
}
```

2. Environment variables:

```bash
export node_pre_gyp_accessKeyId=xxx
export node_pre_gyp_secretAccessKey=xxx
```

For CI environments, prefer IAM roles or short-lived credentials rather than long-lived access keys. See the [node-pre-gyp credentials documentation](https://github.com/mapbox/node-pre-gyp#3-configure-aws-credentials) for additional options.

## package.json

### The `dependencies` and `devDependencies` properties

The package is now published under the `@mapbox` scope. Use `aws-sdk` as a dev dependency for the upload step.

```json
"dependencies": {
"@mapbox/node-pre-gyp": "^1.0.0"
},
"devDependencies": {
"aws-sdk": "^2.0.0"
}
```

### The `scripts` property

The `install` script should invoke node-pre-gyp with `--fallback-to-build` so that users who don't have a pre-built binary available can still compile locally:

```json
"scripts": {
"install": "node-pre-gyp install --fallback-to-build"
}
```

### The `binary` property

The `binary` property tells node-pre-gyp which Node-API versions your addon supports and where to find/upload binaries:

```json
"binary": {
"module_name": "your_module",
"module_path": "./lib/binding/napi-v{napi_build_version}",
"remote_path": "./{module_name}/v{version}/{configuration}/",
"package_name": "{platform}-{arch}-napi-v{napi_build_version}.tar.gz",
"host": "https://your_bucket.s3.us-west-1.amazonaws.com",
"napi_versions": [3]
}
```

Set `module_name` to a valid C identifier. The `napi_versions` array lists which Node-API versions to build for; `3` is a reasonable minimum for most addons.

See the [node-pre-gyp docs](https://github.com/mapbox/node-pre-gyp#1-add-new-entries-to-your-packagejson) for a complete reference, including [Node-API considerations](https://github.com/mapbox/node-pre-gyp#n-api-considerations).

## binding.gyp

### New target

Add a post-build target to copy the compiled binary to the path specified by `module_path`:

```json
{
"target_name": "action_after_build",
"type": "none",
"dependencies": ["<(module_name)"],
"copies": [
{
"files": ["<(PRODUCT_DIR)/<(module_name).node"],
"destination": "<(module_path)"
}
]
}
```

### NAPI_VERSION

Include the Node-API version in the first target's `defines` so the header files configure themselves correctly:

```json
"defines": [
"NAPI_VERSION=<(napi_build_version)"
]
```

## JavaScript updates

JavaScript code that loads the native binary must dynamically resolve the path to the correct `.node` file:

```cjs
const binary = require('@mapbox/node-pre-gyp');
const path = require('path');
const bindingPath = binary.find(
path.resolve(path.join(__dirname, './package.json'))
);
const binding = require(bindingPath);
```

## Build

Once everything is in place, build from source:

```bash
npm install --build-from-source
```

## Package and publish

```bash
./node_modules/.bin/node-pre-gyp package
./node_modules/.bin/node-pre-gyp publish
```

## CI and automated builds

Use [GitHub Actions](https://docs.github.com/en/actions) to build, test, and publish binaries for multiple platforms and architectures. A typical workflow matrix covers `ubuntu-latest`, `macos-latest`, and `windows-latest`, plus any architecture variants you need (e.g. `x64`, `arm64`). See the node-pre-gyp repository for [example workflow configurations](https://github.com/mapbox/node-pre-gyp).
Loading
Loading