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

RobotWebTools / rclnodejs / 18426571657

11 Oct 2025 07:51AM UTC coverage: 83.174% (-1.1%) from 84.306%
18426571657

Pull #1287

github

web-flow
Merge 72774434c into d4d2d7c21
Pull Request #1287: Leverage prebuildify to provide prebuilt addon for npm package

803 of 1060 branches covered (75.75%)

Branch coverage included in aggregate %.

61 of 103 new or added lines in 24 files covered. (59.22%)

1980 of 2286 relevant lines covered (86.61%)

432.38 hits per line

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

48.62
/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');
26✔
18
const path = require('path');
26✔
19
const debug = require('debug')('rclnodejs');
26✔
20

21
let nativeModule = null;
26✔
22

23
// Simplified loader: only use prebuilt binaries with exact Ubuntu/ROS2/arch match
24
// Note: Prebuilt binaries are only supported on Linux (Ubuntu) platform
25
function customFallbackLoader() {
26
  // Prebuilt binaries are only for Linux platform
27
  if (process.platform !== 'linux') {
26!
NEW
28
    debug('Prebuilt binaries are only supported on Linux platform');
×
NEW
29
    return null;
×
30
  }
31

32
  const rosDistro = process.env.ROS_DISTRO;
26✔
33
  const arch = process.arch;
26✔
34
  let ubuntuCodename = null;
26✔
35

36
  // Detect Ubuntu codename
37
  try {
26✔
38
    const osRelease = fs.readFileSync('/etc/os-release', 'utf8');
26✔
39
    const match = osRelease.match(/^VERSION_CODENAME=(.*)$/m);
26✔
40
    if (match) {
26!
41
      ubuntuCodename = match[1].trim();
26✔
42
    }
43
  } catch (e) {
NEW
44
    debug('Could not detect Ubuntu codename:', e.message);
×
45
  }
46

47
  // Require all three components for exact match
48
  if (!rosDistro || !ubuntuCodename || !arch) {
26!
NEW
49
    debug(
×
50
      `Missing environment info - ROS: ${rosDistro}, Ubuntu: ${ubuntuCodename}, Arch: ${arch}`
51
    );
NEW
52
    return null;
×
53
  }
54

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

62
  if (!fs.existsSync(prebuildDir)) {
26!
63
    debug('No prebuilds directory found');
26✔
64
    return null;
26✔
65
  }
66

NEW
67
  try {
×
68
    // Look for exact match binary: {ros_distro}-{ubuntu_codename}-{arch}-rclnodejs.node
NEW
69
    const exactMatchFilename = `${rosDistro}-${ubuntuCodename}-${arch}-rclnodejs.node`;
×
NEW
70
    const exactMatchPath = path.join(prebuildDir, exactMatchFilename);
×
71

NEW
72
    if (fs.existsSync(exactMatchPath)) {
×
NEW
73
      debug(`Found exact match binary: ${exactMatchFilename}`);
×
NEW
74
      return require(exactMatchPath);
×
75
    }
76

NEW
77
    debug(`No exact match found for: ${exactMatchFilename}`);
×
NEW
78
    return null;
×
79
  } catch (e) {
NEW
80
    debug('Error in simplified prebuilt loader:', e.message);
×
81
  }
82

NEW
83
  return null;
×
84
}
85

86
// Simplified prebuilt binary loader: exact match or build from source
87
function loadNativeAddon() {
88
  if (nativeModule) {
26!
NEW
89
    return nativeModule;
×
90
  }
91

92
  // Environment variable to force building from source
93
  if (process.env.RCLNODEJS_FORCE_BUILD === '1') {
26!
NEW
94
    debug('Forcing build from source (RCLNODEJS_FORCE_BUILD=1)');
×
95

96
    // Trigger actual compilation
NEW
97
    const { execSync } = require('child_process');
×
NEW
98
    try {
×
NEW
99
      debug('Running forced node-gyp rebuild...');
×
NEW
100
      execSync('npm run rebuild', {
×
101
        stdio: 'inherit',
102
        cwd: __dirname + '/..',
103
        timeout: 300000, // 5 minute timeout
104
      });
105

106
      // Load the newly built binary
NEW
107
      nativeModule = require('bindings')('rclnodejs');
×
NEW
108
      debug('Successfully force compiled and loaded from source');
×
NEW
109
      return nativeModule;
×
110
    } catch (compileError) {
NEW
111
      debug('Forced compilation failed:', compileError.message);
×
NEW
112
      throw new Error(
×
113
        `Failed to force build rclnodejs from source: ${compileError.message}`
114
      );
115
    }
116
  }
117

118
  const rosDistro = process.env.ROS_DISTRO;
26✔
119
  const ubuntuCodename = detectUbuntuCodename();
26✔
120

121
  debug(
26✔
122
    `Platform: ${process.platform}, Arch: ${process.arch}, Ubuntu: ${ubuntuCodename || 'unknown'}, ROS: ${rosDistro || 'unknown'}`
52!
123
  );
124

125
  // Prebuilt binaries are only supported on Linux (Ubuntu)
126
  if (process.platform === 'linux') {
26!
127
    // Try exact match prebuilt binary first
128
    try {
26✔
129
      const prebuiltModule = customFallbackLoader();
26✔
130
      if (prebuiltModule) {
26!
NEW
131
        nativeModule = prebuiltModule;
×
NEW
132
        return nativeModule;
×
133
      }
134
    } catch (e) {
NEW
135
      debug('Exact match prebuilt loading failed:', e.message);
×
136
    }
137

138
    debug(
26✔
139
      'No exact match prebuilt binary found, falling back to build from source'
140
    );
141
  } else {
NEW
142
    debug(
×
143
      `Platform ${process.platform} does not support prebuilt binaries, will try existing build or compile from source`
144
    );
145
  }
146

147
  try {
26✔
148
    // Try to find existing built binary first (works on all platforms)
149
    // The 'bindings' module will search standard locations like:
150
    // - build/Release/rclnodejs.node
151
    // - build/Debug/rclnodejs.node
152
    // - compiled/{node_version}/{platform}/{arch}/rclnodejs.node
153
    // etc.
154
    nativeModule = require('bindings')('rclnodejs');
26✔
155
    debug('Found and loaded existing built binary');
26✔
156
    return nativeModule;
26✔
157
  } catch {
NEW
158
    debug('No existing built binary found, triggering compilation...');
×
159

160
    // Trigger actual compilation
NEW
161
    const { execSync } = require('child_process');
×
NEW
162
    try {
×
NEW
163
      debug('Running node-gyp rebuild...');
×
NEW
164
      execSync('npm run rebuild', {
×
165
        stdio: 'inherit',
166
        cwd: __dirname + '/..',
167
        timeout: 300000, // 5 minute timeout
168
      });
169

170
      // Try to load the newly built binary
NEW
171
      nativeModule = require('bindings')('rclnodejs');
×
NEW
172
      debug('Successfully compiled and loaded from source');
×
NEW
173
      return nativeModule;
×
174
    } catch (compileError) {
NEW
175
      debug('Compilation failed:', compileError.message);
×
NEW
176
      throw new Error(
×
177
        `Failed to build rclnodejs from source: ${compileError.message}`
178
      );
179
    }
180
  }
181
}
182

183
function detectUbuntuCodename() {
184
  if (process.platform !== 'linux') {
26!
NEW
185
    return null;
×
186
  }
187

188
  try {
26✔
189
    const osRelease = fs.readFileSync('/etc/os-release', 'utf8');
26✔
190
    const match = osRelease.match(/^VERSION_CODENAME=(.*)$/m);
26✔
191
    return match ? match[1].trim() : null;
26!
192
  } catch {
NEW
193
    return null;
×
194
  }
195
}
196

197
module.exports = loadNativeAddon();
26✔
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