• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

RobotWebTools / rclnodejs / 24119117358

08 Apr 2026 05:16AM UTC coverage: 85.368% (-0.04%) from 85.411%
24119117358

Pull #1478

github

web-flow
Merge 42ed23bba into 01847d535
Pull Request #1478: Raise Node.js Minimum to 20.20.2

1568 of 1998 branches covered (78.48%)

Branch coverage included in aggregate %.

15 of 18 new or added lines in 2 files covered. (83.33%)

1 existing line in 1 file now uncovered.

3187 of 3572 relevant lines covered (89.22%)

479.52 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.0
/lib/native_loader.js
1
// Copyright (c) 2025, The Robot Web Tools Contributors
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14

15
'use strict';
16

17
const fs = require('fs');
28✔
18
const path = require('path');
28✔
19
const { execSync } = require('child_process');
28✔
20
const { NativeError } = require('./errors.js');
28✔
21
const bindings = require('bindings');
28✔
22
const debug = require('debug')('rclnodejs');
28✔
23
const {
24
  detectPrebuildRuntime,
25
  getTaggedPrebuildFilename,
26
} = require('./prebuilds');
28✔
27
const { detectUbuntuCodename } = require('./utils');
28✔
28

29
let nativeModule = null;
28✔
30

31
// Simplified loader: only use prebuilt binaries with exact Ubuntu/ROS2/arch match
32
// Note: Prebuilt binaries are only supported on Linux (Ubuntu) platform
33
function customFallbackLoader() {
34
  // Prebuilt binaries are only for Linux platform
35
  if (process.platform !== 'linux') {
32✔
36
    debug('Prebuilt binaries are only supported on Linux platform');
1✔
37
    return null;
1✔
38
  }
39

40
  const rosDistro = process.env.ROS_DISTRO;
31✔
41
  const arch = process.arch;
31✔
42
  const runtime = detectPrebuildRuntime();
31✔
43
  const ubuntuCodename = detectUbuntuCodename();
31✔
44

45
  // Require all three components for exact match
46
  if (!rosDistro || !ubuntuCodename || !arch) {
31✔
47
    debug(
1✔
48
      `Missing environment info - ROS: ${rosDistro}, Ubuntu: ${ubuntuCodename}, Arch: ${arch}`
49
    );
50
    return null;
1✔
51
  }
52

53
  const prebuildDir = path.join(
30✔
54
    __dirname,
55
    '..',
56
    'prebuilds',
57
    `${process.platform}-${arch}`
58
  );
59

60
  if (!fs.existsSync(prebuildDir)) {
30✔
61
    debug('No prebuilds directory found');
28✔
62
    return null;
28✔
63
  }
64

65
  try {
2✔
66
    const candidate = getTaggedPrebuildFilename({
2✔
67
      rosDistro,
68
      ubuntuCodename,
69
      arch,
70
      runtime,
71
    });
72
    const candidatePath = path.join(prebuildDir, candidate);
2✔
73

74
    if (fs.existsSync(candidatePath)) {
2!
75
      debug(`Found ${runtime} prebuilt binary: ${candidate}`);
2✔
76
      return require(candidatePath);
2✔
77
    }
78

NEW
79
    debug(
×
80
      `No matching ${runtime} prebuilt binary found for ${rosDistro}-${ubuntuCodename}-${arch}`
81
    );
UNCOV
82
    return null;
×
83
  } catch (e) {
84
    debug('Error in simplified prebuilt loader:', e.message);
2✔
85
  }
86

87
  return null;
2✔
88
}
89

90
// Simplified prebuilt binary loader: exact match or build from source
91
function loadNativeAddon() {
92
  if (nativeModule) {
28!
93
    return nativeModule;
×
94
  }
95

96
  // Environment variable to force building from source
97
  if (process.env.RCLNODEJS_FORCE_BUILD === '1') {
28✔
98
    debug('Forcing build from source (RCLNODEJS_FORCE_BUILD=1)');
1✔
99

100
    // Trigger actual compilation
101
    try {
1✔
102
      debug('Running forced node-gyp rebuild...');
1✔
103
      execSync('npm run rebuild', {
1✔
104
        stdio: 'inherit',
105
        cwd: path.join(__dirname, '..'),
106
        timeout: 300000, // 5 minute timeout
107
      });
108

109
      // Load the newly built binary
110
      nativeModule = bindings('rclnodejs');
1✔
111
      debug('Successfully force compiled and loaded from source');
1✔
112
      return nativeModule;
1✔
113
    } catch (compileError) {
114
      debug('Forced compilation failed:', compileError.message);
×
115
      throw new NativeError(
×
116
        `Failed to force build rclnodejs from source: ${compileError.message}`,
117
        'Forced compilation',
118
        { cause: compileError }
119
      );
120
    }
121
  }
122

123
  const rosDistro = process.env.ROS_DISTRO;
27✔
124
  const runtime = detectPrebuildRuntime();
27✔
125
  const ubuntuCodename = detectUbuntuCodename();
27✔
126

127
  debug(
27✔
128
    `Platform: ${process.platform}, Arch: ${process.arch}, Runtime: ${runtime}, Ubuntu: ${ubuntuCodename || 'unknown'}, ROS: ${rosDistro || 'unknown'}`
54!
129
  );
130

131
  // Prebuilt binaries are only supported on Linux (Ubuntu)
132
  if (process.platform === 'linux') {
27!
133
    // Try exact match prebuilt binary first
134
    try {
27✔
135
      const prebuiltModule = customFallbackLoader();
27✔
136
      if (prebuiltModule) {
27!
137
        nativeModule = prebuiltModule;
×
138
        return nativeModule;
×
139
      }
140
    } catch (e) {
141
      debug('Exact match prebuilt loading failed:', e.message);
×
142
    }
143

144
    debug(
27✔
145
      'No exact match prebuilt binary found, falling back to build from source'
146
    );
147
  } else {
148
    debug(
×
149
      `Platform ${process.platform} does not support prebuilt binaries, will try existing build or compile from source`
150
    );
151
  }
152

153
  try {
27✔
154
    // Try to find existing built binary first (works on all platforms)
155
    // The 'bindings' module will search standard locations like:
156
    // - build/Release/rclnodejs.node
157
    // - build/Debug/rclnodejs.node
158
    // - compiled/{node_version}/{platform}/{arch}/rclnodejs.node
159
    // etc.
160
    nativeModule = bindings('rclnodejs');
27✔
161
    debug('Found and loaded existing built binary');
27✔
162
    return nativeModule;
27✔
163
  } catch {
164
    debug('No existing built binary found, triggering compilation...');
×
165

166
    // Trigger actual compilation
167
    try {
×
168
      debug('Running node-gyp rebuild...');
×
169
      execSync('npm run rebuild', {
×
170
        stdio: 'inherit',
171
        cwd: path.join(__dirname, '..'),
172
        timeout: 300000, // 5 minute timeout
173
      });
174

175
      // Try to load the newly built binary
176
      nativeModule = bindings('rclnodejs');
×
177
      debug('Successfully compiled and loaded from source');
×
178
      return nativeModule;
×
179
    } catch (compileError) {
180
      debug('Compilation failed:', compileError.message);
×
181
      throw new NativeError(
×
182
        `Failed to build rclnodejs from source: ${compileError.message}`,
183
        'Compilation',
184
        { cause: compileError }
185
      );
186
    }
187
  }
188
}
189

190
const addon = loadNativeAddon();
28✔
191

192
// Export internal functions for testing purposes
193
if (process.env.NODE_ENV === 'test') {
28✔
194
  addon.TestHelpers = {
27✔
195
    customFallbackLoader,
196
    loadNativeAddon,
197
  };
198
}
199

200
module.exports = addon;
28✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc