@bobfrankston/msger 0.1.188 → 0.1.190
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/msger-native/builder/build-under-wsl.ts +26 -210
- package/msger-native/builder/builder.ts +24 -554
- package/msger-native/builder/postinstall.js +21 -108
- package/package.json +63 -63
package/README.md
CHANGED
|
@@ -668,10 +668,11 @@ This package is the **Rust/wry-based** implementation. There's also **[@bobfrank
|
|
|
668
668
|
- Need reliable cross-platform rendering
|
|
669
669
|
- Prefer Electron ecosystem
|
|
670
670
|
|
|
671
|
-
###
|
|
672
|
-
Both packages will share a common JavaScript API (`window.msgapi`) for file system access, window manipulation, and bidirectional communication. See [TODO.md](TODO.md) for details.
|
|
671
|
+
### window.msgapi
|
|
673
672
|
|
|
674
|
-
|
|
673
|
+
msger injects `window.msgapi` into loaded pages via `msger-api.js`, providing window control, UDP networking, and HTTP fetch. File system and shell operations are not yet implemented in msger.
|
|
674
|
+
|
|
675
|
+
See **[msgapidefs README](../msgapidefs/README.md)** for the full `window.msgapi` API reference, implementation status, and security notes. APIs are experimental and subject to change.
|
|
675
676
|
|
|
676
677
|
---
|
|
677
678
|
|
|
@@ -1,210 +1,26 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
release?: boolean;
|
|
28
|
-
verbose?: boolean;
|
|
29
|
-
piHost?: string;
|
|
30
|
-
piProjectPath?: string;
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function timestamp(): string {
|
|
35
|
-
const now = new Date();
|
|
36
|
-
return now.toTimeString().split(' ')[0];
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
interface BuildTarget {
|
|
40
|
-
name: string;
|
|
41
|
-
rustTarget: string;
|
|
42
|
-
outputName: string;
|
|
43
|
-
description: string;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const targets: Record<string, BuildTarget> = {
|
|
47
|
-
x64: {
|
|
48
|
-
name: 'x64',
|
|
49
|
-
rustTarget: 'x86_64-unknown-linux-gnu',
|
|
50
|
-
outputName: 'msgernative',
|
|
51
|
-
description: 'Linux x64'
|
|
52
|
-
},
|
|
53
|
-
arm64: {
|
|
54
|
-
name: 'arm64',
|
|
55
|
-
rustTarget: 'aarch64-unknown-linux-gnu',
|
|
56
|
-
outputName: 'msgernative-linux-aarch64',
|
|
57
|
-
description: 'Linux ARM64'
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
function checkToolchain(target: BuildTarget): boolean {
|
|
62
|
-
try {
|
|
63
|
-
const installed = execSync('rustup target list --installed', { encoding: 'utf-8' });
|
|
64
|
-
return installed.includes(target.rustTarget);
|
|
65
|
-
} catch {
|
|
66
|
-
return false;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function installToolchain(target: BuildTarget): boolean {
|
|
71
|
-
console.log(` [${timestamp()}] 📦 Installing ${target.description} toolchain...`);
|
|
72
|
-
|
|
73
|
-
try {
|
|
74
|
-
// Add Rust target
|
|
75
|
-
execSync(`rustup target add ${target.rustTarget}`, { stdio: 'inherit' });
|
|
76
|
-
|
|
77
|
-
// For ARM64, also install cross-compiler
|
|
78
|
-
if (target.name === 'arm64') {
|
|
79
|
-
console.log(` [${timestamp()}] 📦 Installing ARM64 cross-compiler...`);
|
|
80
|
-
execSync('sudo apt update && sudo apt install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu libc6-dev-arm64-cross', {
|
|
81
|
-
stdio: 'inherit'
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
console.log(` [${timestamp()}] ✅ Toolchain installed`);
|
|
86
|
-
return true;
|
|
87
|
-
} catch (error: any) {
|
|
88
|
-
console.error(` [${timestamp()}] ❌ Failed to install toolchain: ${error.message}`);
|
|
89
|
-
return false;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function build(target: BuildTarget, verbose: boolean): boolean {
|
|
94
|
-
console.log(`\n[${timestamp()}] 📦 Building ${target.description} binary...`);
|
|
95
|
-
|
|
96
|
-
// Check toolchain
|
|
97
|
-
if (!checkToolchain(target)) {
|
|
98
|
-
console.log(` [${timestamp()}] ⚙️ ${target.description} toolchain not found`);
|
|
99
|
-
if (!installToolchain(target)) {
|
|
100
|
-
return false;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const nativeDir = path.join(__dirname, '..');
|
|
105
|
-
const binDir = path.join(nativeDir, 'bin');
|
|
106
|
-
const binaryPath = path.join(binDir, target.outputName);
|
|
107
|
-
|
|
108
|
-
try {
|
|
109
|
-
console.log(` [${timestamp()}] 🔨 Compiling with cargo...`);
|
|
110
|
-
execSync(`cargo build --release --target ${target.rustTarget}`, {
|
|
111
|
-
cwd: nativeDir,
|
|
112
|
-
stdio: verbose ? 'inherit' : 'pipe'
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
// Copy to bin directory
|
|
116
|
-
if (!fs.existsSync(binDir)) {
|
|
117
|
-
fs.mkdirSync(binDir, { recursive: true });
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const sourcePath = path.join(nativeDir, 'target', target.rustTarget, 'release', 'msgernative');
|
|
121
|
-
fs.copyFileSync(sourcePath, binaryPath);
|
|
122
|
-
fs.chmodSync(binaryPath, 0o755);
|
|
123
|
-
|
|
124
|
-
const stats = fs.statSync(binaryPath);
|
|
125
|
-
const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
|
|
126
|
-
console.log(` [${timestamp()}] ✅ ${target.description} binary completed`);
|
|
127
|
-
console.log(` [${timestamp()}] 📊 Binary size: ${sizeMB} MB`);
|
|
128
|
-
console.log(` [${timestamp()}] 📁 Output: ${binaryPath}`);
|
|
129
|
-
return true;
|
|
130
|
-
} catch (error: any) {
|
|
131
|
-
console.error(` [${timestamp()}] ❌ Build failed: ${error.message}`);
|
|
132
|
-
return false;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function loadConfig(): BuildConfig {
|
|
137
|
-
const configPath = path.join(__dirname, 'build-config.json');
|
|
138
|
-
|
|
139
|
-
if (!fs.existsSync(configPath)) {
|
|
140
|
-
console.error('❌ build-config.json not found');
|
|
141
|
-
process.exit(1);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
try {
|
|
145
|
-
const content = fs.readFileSync(configPath, 'utf-8');
|
|
146
|
-
return JSON.parse(content);
|
|
147
|
-
} catch (error: any) {
|
|
148
|
-
console.error(`❌ Failed to parse build-config.json: ${error.message}`);
|
|
149
|
-
process.exit(1);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function main() {
|
|
154
|
-
console.log('🚀 WSL Native Builder\n');
|
|
155
|
-
|
|
156
|
-
const config = loadConfig();
|
|
157
|
-
const verboseFlag = config.options?.verbose ?? false;
|
|
158
|
-
|
|
159
|
-
// Determine which targets to build based on config
|
|
160
|
-
const requestedTargets: string[] = [];
|
|
161
|
-
if (config.platforms.wsl) requestedTargets.push('x64');
|
|
162
|
-
if (config.platforms.arm64) requestedTargets.push('arm64');
|
|
163
|
-
|
|
164
|
-
if (requestedTargets.length === 0) {
|
|
165
|
-
console.log('⚠️ No WSL platforms enabled in build-config.json');
|
|
166
|
-
console.log(' Enable "wsl" for x64 or "arm64" for ARM64 builds');
|
|
167
|
-
process.exit(0);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
console.log(`📋 Configuration:`);
|
|
171
|
-
console.log(` Mode: ${config.options?.release ? 'release' : 'debug'}`);
|
|
172
|
-
console.log(` Targets: ${requestedTargets.join(', ')}`);
|
|
173
|
-
console.log(` Verbose: ${verboseFlag}\n`);
|
|
174
|
-
|
|
175
|
-
const results: { target: string; success: boolean }[] = [];
|
|
176
|
-
|
|
177
|
-
for (const targetName of requestedTargets) {
|
|
178
|
-
const target = targets[targetName];
|
|
179
|
-
if (!target) {
|
|
180
|
-
console.error(`❌ Unknown target: ${targetName}`);
|
|
181
|
-
console.error(` Available targets: x64, arm64, all`);
|
|
182
|
-
process.exit(1);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const success = build(target, verboseFlag);
|
|
186
|
-
results.push({ target: targetName, success });
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// Summary
|
|
190
|
-
console.log('\n' + '='.repeat(50));
|
|
191
|
-
console.log('📊 Build Summary:');
|
|
192
|
-
console.log('='.repeat(50));
|
|
193
|
-
|
|
194
|
-
for (const result of results) {
|
|
195
|
-
const status = result.success ? '✅' : '❌';
|
|
196
|
-
console.log(`${status} ${result.target}`);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const successCount = results.filter(r => r.success).length;
|
|
200
|
-
const totalCount = results.length;
|
|
201
|
-
|
|
202
|
-
console.log('='.repeat(50));
|
|
203
|
-
console.log(`\n🎉 Completed: ${successCount}/${totalCount} builds successful\n`);
|
|
204
|
-
|
|
205
|
-
if (successCount < totalCount) {
|
|
206
|
-
process.exit(1);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
main();
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* msger-native WSL builder — delegates to @bobfrankston/rust-builder
|
|
4
|
+
* Run this directly inside WSL: node builder/build-under-wsl.ts
|
|
5
|
+
*/
|
|
6
|
+
import { loadConfig, build } from "@bobfrankston/rust-builder";
|
|
7
|
+
import path from "path";
|
|
8
|
+
import { fileURLToPath } from "url";
|
|
9
|
+
|
|
10
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
const nativeDir = path.join(__dirname, "..");
|
|
12
|
+
const binDir = path.join(nativeDir, "bin");
|
|
13
|
+
|
|
14
|
+
const config = loadConfig(
|
|
15
|
+
path.join(__dirname, "build-config.json"),
|
|
16
|
+
"msgernative",
|
|
17
|
+
nativeDir,
|
|
18
|
+
binDir
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
// Only build WSL targets when running inside WSL
|
|
22
|
+
config.platforms.windows = false;
|
|
23
|
+
config.platforms.pi = false;
|
|
24
|
+
|
|
25
|
+
const success = build(config);
|
|
26
|
+
if (!success) process.exit(1);
|
|
@@ -1,556 +1,26 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
piProjectPath?: string;
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function isWSL(): boolean {
|
|
32
|
-
try {
|
|
33
|
-
const release = fs.readFileSync('/proc/version', 'utf8');
|
|
34
|
-
return release.toLowerCase().includes('microsoft');
|
|
35
|
-
} catch {
|
|
36
|
-
return false;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function isWindows(): boolean {
|
|
41
|
-
return platform() === 'win32' && !isWSL();
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function getCleanPathForWindows(): string {
|
|
45
|
-
// Remove Git's /usr/bin from PATH to avoid link.exe conflict with VS linker
|
|
46
|
-
const currentPath = process.env.PATH || '';
|
|
47
|
-
const pathParts = currentPath.split(';');
|
|
48
|
-
const filteredParts = pathParts.filter(part => {
|
|
49
|
-
const lower = part.toLowerCase();
|
|
50
|
-
return !lower.includes('git\\usr\\bin') && !lower.includes('git/usr/bin');
|
|
51
|
-
});
|
|
52
|
-
return filteredParts.join(';');
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Execute a command in WSL using spawn to avoid quoting issues
|
|
57
|
-
*/
|
|
58
|
-
function wslExec(command: string, args: string[], options?: { stdio?: 'inherit' | 'pipe'; encoding?: BufferEncoding }): { success: boolean; output: string; error: string } {
|
|
59
|
-
const result = spawnSync('wsl', [command, ...args], {
|
|
60
|
-
stdio: options?.stdio || 'pipe',
|
|
61
|
-
encoding: options?.encoding || 'utf-8'
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
return {
|
|
65
|
-
success: result.status === 0,
|
|
66
|
-
output: result.stdout?.toString() || '',
|
|
67
|
-
error: result.stderr?.toString() || ''
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Convert Windows path to WSL path
|
|
73
|
-
*/
|
|
74
|
-
function toWSLPath(winPath: string): string {
|
|
75
|
-
// Convert backslashes to forward slashes for wslpath
|
|
76
|
-
const normalizedPath = winPath.replace(/\\/g, '/');
|
|
77
|
-
const result = spawnSync('wsl', ['wslpath', normalizedPath], { encoding: 'utf-8' });
|
|
78
|
-
if (result.status !== 0) {
|
|
79
|
-
const error = result.stderr?.toString() || 'unknown error';
|
|
80
|
-
throw new Error(`Failed to convert path: ${winPath} - ${error}`);
|
|
81
|
-
}
|
|
82
|
-
return result.stdout.trim();
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function needsRebuild(binaryPath: string, sourceDirs: string[]): boolean {
|
|
86
|
-
if (!fs.existsSync(binaryPath)) {
|
|
87
|
-
return true;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const binaryTime = fs.statSync(binaryPath).mtime.getTime();
|
|
91
|
-
|
|
92
|
-
function checkDir(dirPath: string): boolean {
|
|
93
|
-
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
94
|
-
for (const entry of entries) {
|
|
95
|
-
const fullPath = path.join(dirPath, entry.name);
|
|
96
|
-
if (entry.isFile()) {
|
|
97
|
-
const fileTime = fs.statSync(fullPath).mtime.getTime();
|
|
98
|
-
if (fileTime > binaryTime) {
|
|
99
|
-
console.log(` 📝 Source file changed: ${entry.name}`);
|
|
100
|
-
return true;
|
|
101
|
-
}
|
|
102
|
-
} else if (entry.isDirectory()) {
|
|
103
|
-
if (checkDir(fullPath)) {
|
|
104
|
-
return true;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
return false;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
for (const sourcePath of sourceDirs) {
|
|
112
|
-
if (!fs.existsSync(sourcePath)) continue;
|
|
113
|
-
|
|
114
|
-
const stats = fs.statSync(sourcePath);
|
|
115
|
-
if (stats.isFile()) {
|
|
116
|
-
const fileTime = stats.mtime.getTime();
|
|
117
|
-
if (fileTime > binaryTime) {
|
|
118
|
-
console.log(` 📝 Source file changed: ${sourcePath}`);
|
|
119
|
-
return true;
|
|
120
|
-
}
|
|
121
|
-
} else if (stats.isDirectory()) {
|
|
122
|
-
if (checkDir(sourcePath)) {
|
|
123
|
-
return true;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return false;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Load build configuration from JSON file
|
|
133
|
-
*/
|
|
134
|
-
function loadConfig(): BuildConfig {
|
|
135
|
-
const configPath = path.join(__dirname, 'build-config.json');
|
|
136
|
-
|
|
137
|
-
if (!fs.existsSync(configPath)) {
|
|
138
|
-
console.error('❌ build-config.json not found');
|
|
139
|
-
process.exit(1);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
try {
|
|
143
|
-
const content = fs.readFileSync(configPath, 'utf-8');
|
|
144
|
-
return JSON.parse(content);
|
|
145
|
-
} catch (error: any) {
|
|
146
|
-
console.error(`❌ Failed to parse build-config.json: ${error.message}`);
|
|
147
|
-
process.exit(1);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function timestamp(): string {
|
|
152
|
-
const now = new Date();
|
|
153
|
-
return now.toTimeString().split(' ')[0]; // HH:MM:SS
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Build Windows binary
|
|
158
|
-
*/
|
|
159
|
-
function buildWindows(nativeDir: string, binDir: string, verbose: boolean): boolean {
|
|
160
|
-
console.log(`\n[${timestamp()}] 📦 Building Windows x64 binary...`);
|
|
161
|
-
|
|
162
|
-
const binaryPath = path.join(binDir, 'msgernative.exe');
|
|
163
|
-
const sourceDirs = [
|
|
164
|
-
path.join(nativeDir, 'src'),
|
|
165
|
-
path.join(nativeDir, 'Cargo.toml'),
|
|
166
|
-
path.join(nativeDir, 'build.rs')
|
|
167
|
-
];
|
|
168
|
-
|
|
169
|
-
if (!needsRebuild(binaryPath, sourceDirs)) {
|
|
170
|
-
console.log(` [${timestamp()}] ✅ Binary is up to date, skipping build`);
|
|
171
|
-
const stats = fs.statSync(binaryPath);
|
|
172
|
-
const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
|
|
173
|
-
console.log(` [${timestamp()}] 📊 Binary size: ${sizeMB} MB`);
|
|
174
|
-
return true;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
console.log(` [${timestamp()}] 🔧 Fixing PATH to use VS linker instead of Git link.exe`);
|
|
178
|
-
const cleanPath = getCleanPathForWindows();
|
|
179
|
-
|
|
180
|
-
try {
|
|
181
|
-
execSync('cargo build --release', {
|
|
182
|
-
cwd: nativeDir,
|
|
183
|
-
stdio: verbose ? 'inherit' : 'pipe',
|
|
184
|
-
env: { ...process.env, PATH: cleanPath }
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
// Copy to bin directory
|
|
188
|
-
if (!fs.existsSync(binDir)) {
|
|
189
|
-
fs.mkdirSync(binDir, { recursive: true });
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const sourcePath = path.join(nativeDir, 'target', 'release', 'msgernative.exe');
|
|
193
|
-
fs.copyFileSync(sourcePath, binaryPath);
|
|
194
|
-
|
|
195
|
-
// Copy msger icons (.ico and .png) to bin directory
|
|
196
|
-
const winIcoSource = path.join(nativeDir, '..', 'msger.ico');
|
|
197
|
-
const winIcoDest = path.join(binDir, 'msger.ico');
|
|
198
|
-
const winPngSource = path.join(nativeDir, '..', 'msger.png');
|
|
199
|
-
const winPngDest = path.join(binDir, 'msger.png');
|
|
200
|
-
|
|
201
|
-
if (fs.existsSync(winIcoSource)) {
|
|
202
|
-
fs.copyFileSync(winIcoSource, winIcoDest);
|
|
203
|
-
console.log(` [${timestamp()}] 🎨 Icon (ICO) copied to bin directory`);
|
|
204
|
-
}
|
|
205
|
-
if (fs.existsSync(winPngSource)) {
|
|
206
|
-
fs.copyFileSync(winPngSource, winPngDest);
|
|
207
|
-
console.log(` [${timestamp()}] 🎨 Icon (PNG) copied to bin directory`);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
const stats = fs.statSync(binaryPath);
|
|
211
|
-
const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
|
|
212
|
-
console.log(` [${timestamp()}] ✅ Windows x64 binary completed`);
|
|
213
|
-
console.log(` [${timestamp()}] 📊 Binary size: ${sizeMB} MB`);
|
|
214
|
-
return true;
|
|
215
|
-
} catch (error: any) {
|
|
216
|
-
console.error(` [${timestamp()}] ❌ Windows build failed: ${error.message}`);
|
|
217
|
-
return false;
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* Build WSL Linux binary
|
|
223
|
-
*/
|
|
224
|
-
function buildWSL(nativeDir: string, binDir: string, verbose: boolean): boolean {
|
|
225
|
-
console.log(`\n[${timestamp()}] 📦 Building WSL Linux x64 binary...`);
|
|
226
|
-
|
|
227
|
-
const binaryPath = path.join(binDir, 'msgernative');
|
|
228
|
-
const wslNativeDir = toWSLPath(nativeDir);
|
|
229
|
-
|
|
230
|
-
try {
|
|
231
|
-
// Use rustup's cargo, not system cargo
|
|
232
|
-
const cmd = `export PATH="$HOME/.cargo/bin:$PATH" && cd "${wslNativeDir}" && cargo build --release --target x86_64-unknown-linux-gnu`;
|
|
233
|
-
const result = wslExec('bash', ['-c', cmd], {
|
|
234
|
-
stdio: verbose ? 'inherit' : 'pipe'
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
if (!result.success) {
|
|
238
|
-
throw new Error(result.error || 'Build failed');
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// Copy to bin directory
|
|
242
|
-
if (!fs.existsSync(binDir)) {
|
|
243
|
-
fs.mkdirSync(binDir, { recursive: true });
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
const sourcePath = path.join(nativeDir, 'target', 'x86_64-unknown-linux-gnu', 'release', 'msgernative');
|
|
247
|
-
fs.copyFileSync(sourcePath, binaryPath);
|
|
248
|
-
fs.chmodSync(binaryPath, 0o755);
|
|
249
|
-
|
|
250
|
-
// Copy msger.png icon to bin directory (Linux uses PNG)
|
|
251
|
-
const wslIconSource = path.join(nativeDir, '..', 'msger.png');
|
|
252
|
-
const wslIconDest = path.join(binDir, 'msger.png');
|
|
253
|
-
if (fs.existsSync(wslIconSource)) {
|
|
254
|
-
fs.copyFileSync(wslIconSource, wslIconDest);
|
|
255
|
-
console.log(` [${timestamp()}] 🎨 Icon (PNG) copied to bin directory`);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
const stats = fs.statSync(binaryPath);
|
|
259
|
-
const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
|
|
260
|
-
console.log(` [${timestamp()}] ✅ WSL Linux x64 binary completed`);
|
|
261
|
-
console.log(` [${timestamp()}] 📊 Binary size: ${sizeMB} MB`);
|
|
262
|
-
return true;
|
|
263
|
-
} catch (error: any) {
|
|
264
|
-
console.error(` [${timestamp()}] ❌ WSL build failed: ${error.message}`);
|
|
265
|
-
return false;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Check if Rust is installed on Pi
|
|
271
|
-
*/
|
|
272
|
-
function checkPiRust(piHost: string): boolean {
|
|
273
|
-
try {
|
|
274
|
-
// Source cargo env before checking (cargo won't be in PATH until sourced)
|
|
275
|
-
const result = execSync(`ssh ${piHost} "source ~/.cargo/env 2>/dev/null && command -v cargo"`, {
|
|
276
|
-
encoding: 'utf-8',
|
|
277
|
-
stdio: 'pipe'
|
|
278
|
-
});
|
|
279
|
-
return result.trim().length > 0;
|
|
280
|
-
} catch {
|
|
281
|
-
return false;
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* Install Rust on Pi
|
|
287
|
-
*/
|
|
288
|
-
function installPiRust(piHost: string, verbose: boolean): boolean {
|
|
289
|
-
console.log(` [${timestamp()}] 📦 Rust not found on Pi, installing...`);
|
|
290
|
-
|
|
291
|
-
try {
|
|
292
|
-
// Install Rust using rustup
|
|
293
|
-
execSync(`ssh ${piHost} "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y"`, {
|
|
294
|
-
stdio: verbose ? 'inherit' : 'pipe'
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
console.log(` [${timestamp()}] ✅ Rust installed on Pi`);
|
|
298
|
-
return true;
|
|
299
|
-
} catch (error: any) {
|
|
300
|
-
console.error(` [${timestamp()}] ❌ Failed to install Rust on Pi: ${error.message}`);
|
|
301
|
-
return false;
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
/**
|
|
306
|
-
* Build Raspberry Pi ARM64 binary
|
|
307
|
-
*/
|
|
308
|
-
function buildPi(nativeDir: string, binDir: string, verbose: boolean, piHost: string, piProjectPath: string): boolean {
|
|
309
|
-
console.log(`\n[${timestamp()}] 📦 Building Raspberry Pi ARM64 binary...`);
|
|
310
|
-
console.log(` Target: ${piHost}:${piProjectPath}`);
|
|
311
|
-
|
|
312
|
-
const binaryPath = path.join(binDir, 'msgernative-linux-aarch64');
|
|
313
|
-
const sourceDirs = [
|
|
314
|
-
path.join(nativeDir, 'src'),
|
|
315
|
-
path.join(nativeDir, 'Cargo.toml'),
|
|
316
|
-
path.join(nativeDir, 'build.rs')
|
|
317
|
-
];
|
|
318
|
-
|
|
319
|
-
try {
|
|
320
|
-
// Check if rebuild is needed
|
|
321
|
-
if (!needsRebuild(binaryPath, sourceDirs)) {
|
|
322
|
-
console.log(` [${timestamp()}] ✅ Binary is up to date, skipping build`);
|
|
323
|
-
const stats = fs.statSync(binaryPath);
|
|
324
|
-
const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
|
|
325
|
-
console.log(` [${timestamp()}] 📊 Binary size: ${sizeMB} MB`);
|
|
326
|
-
return true;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// Check if Rust is installed on Pi, install if missing
|
|
330
|
-
if (!checkPiRust(piHost)) {
|
|
331
|
-
if (!installPiRust(piHost, verbose)) {
|
|
332
|
-
return false;
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// Copy source files to Pi
|
|
337
|
-
console.log(` [${timestamp()}] 📤 Copying source files to Pi...`);
|
|
338
|
-
execSync(`ssh ${piHost} "mkdir -p ${piProjectPath}"`, {
|
|
339
|
-
stdio: verbose ? 'inherit' : 'pipe'
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
// Copy only necessary files for compilation: src/, Cargo.toml, build.rs
|
|
343
|
-
const filesToCopy = ['src', 'Cargo.toml', 'Cargo.lock', 'build.rs'];
|
|
344
|
-
for (const file of filesToCopy) {
|
|
345
|
-
const sourcePath = path.join(nativeDir, file);
|
|
346
|
-
if (fs.existsSync(sourcePath)) {
|
|
347
|
-
console.log(` [${timestamp()}] → ${file}`);
|
|
348
|
-
execSync(`scp -r "${sourcePath}" ${piHost}:${piProjectPath}/`, {
|
|
349
|
-
stdio: verbose ? 'inherit' : 'pipe'
|
|
350
|
-
});
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
// Build remotely on Pi (source cargo environment in case just installed)
|
|
355
|
-
console.log(` [${timestamp()}] 🔗 Building on remote Raspberry Pi...`);
|
|
356
|
-
execSync(`ssh ${piHost} "source ~/.cargo/env && cd ${piProjectPath} && cargo build --release"`, {
|
|
357
|
-
stdio: verbose ? 'inherit' : 'pipe'
|
|
358
|
-
});
|
|
359
|
-
|
|
360
|
-
// Copy back only the compiled binary
|
|
361
|
-
console.log(` [${timestamp()}] 📥 Copying binary from Pi...`);
|
|
362
|
-
if (!fs.existsSync(binDir)) {
|
|
363
|
-
fs.mkdirSync(binDir, { recursive: true });
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
execSync(`scp ${piHost}:${piProjectPath}/target/release/msgernative "${binaryPath}"`, {
|
|
367
|
-
stdio: verbose ? 'inherit' : 'pipe'
|
|
368
|
-
});
|
|
369
|
-
|
|
370
|
-
fs.chmodSync(binaryPath, 0o755);
|
|
371
|
-
|
|
372
|
-
const stats = fs.statSync(binaryPath);
|
|
373
|
-
const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
|
|
374
|
-
console.log(` [${timestamp()}] ✅ Raspberry Pi ARM64 binary completed`);
|
|
375
|
-
console.log(` [${timestamp()}] 📊 Binary size: ${sizeMB} MB`);
|
|
376
|
-
return true;
|
|
377
|
-
} catch (error: any) {
|
|
378
|
-
console.error(` [${timestamp()}] ❌ Pi build failed: ${error.message}`);
|
|
379
|
-
return false;
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
/**
|
|
384
|
-
* Check if ARM64 toolchain is installed
|
|
385
|
-
*/
|
|
386
|
-
function checkARM64Toolchain(): boolean {
|
|
387
|
-
// Check with PATH set to include cargo bin
|
|
388
|
-
// Also verify ARM64 libraries are present (linker needs these)
|
|
389
|
-
const cmd = 'export PATH="$HOME/.cargo/bin:$PATH" && which rustup && rustup target list --installed | grep -q aarch64-unknown-linux-gnu && which aarch64-linux-gnu-gcc && dpkg -l | grep -q "libwebkit2gtk-4.1.*arm64"';
|
|
390
|
-
const result = wslExec('bash', ['-c', cmd]);
|
|
391
|
-
return result.success;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
/**
|
|
395
|
-
* Install ARM64 toolchain
|
|
396
|
-
*/
|
|
397
|
-
function installARM64Toolchain(verbose: boolean): boolean {
|
|
398
|
-
console.log(` [${timestamp()}] 📦 ARM64 toolchain not found, installing...`);
|
|
399
|
-
|
|
400
|
-
// Get project root (parent of msger-native) and convert to WSL path
|
|
401
|
-
const projectRoot = path.resolve(__dirname, '..', '..');
|
|
402
|
-
const wslPath = toWSLPath(projectRoot);
|
|
403
|
-
|
|
404
|
-
// Check if rustup is installed, if not install Rust first
|
|
405
|
-
const rustupCheck = wslExec('which', ['rustup']);
|
|
406
|
-
if (!rustupCheck.success) {
|
|
407
|
-
console.log(` [${timestamp()}] 📦 Installing Rust in WSL...`);
|
|
408
|
-
const rustInstallScript = `${wslPath}/msger-native/pibuild/install-rust-wsl.sh`;
|
|
409
|
-
const rustInstall = wslExec('bash', [rustInstallScript], { stdio: verbose ? 'inherit' : 'pipe' });
|
|
410
|
-
|
|
411
|
-
if (!rustInstall.success) {
|
|
412
|
-
console.error(` [${timestamp()}] ❌ Failed to install Rust in WSL`);
|
|
413
|
-
if (!verbose && rustInstall.error) {
|
|
414
|
-
console.error(` [${timestamp()}] ${rustInstall.error}`);
|
|
415
|
-
}
|
|
416
|
-
return false;
|
|
417
|
-
}
|
|
418
|
-
console.log(` [${timestamp()}] ✅ Rust installed in WSL`);
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
// Now install ARM64 cross-compilation toolchain
|
|
422
|
-
const scriptPath = `${wslPath}/msger-native/pibuild/setup-arm64-toolchain.sh`;
|
|
423
|
-
const result = wslExec('bash', [scriptPath], { stdio: verbose ? 'inherit' : 'pipe' });
|
|
424
|
-
|
|
425
|
-
if (!result.success) {
|
|
426
|
-
console.error(` [${timestamp()}] ❌ Failed to install ARM64 toolchain`);
|
|
427
|
-
if (!verbose && result.error) {
|
|
428
|
-
console.error(` [${timestamp()}] ${result.error}`);
|
|
429
|
-
}
|
|
430
|
-
return false;
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
console.log(` [${timestamp()}] ✅ ARM64 toolchain installed successfully`);
|
|
434
|
-
return true;
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
/**
|
|
438
|
-
* Build generic ARM64 binary via cross-compilation
|
|
439
|
-
*/
|
|
440
|
-
function buildARM64(nativeDir: string, binDir: string, verbose: boolean): boolean {
|
|
441
|
-
console.log(`\n[${timestamp()}] 📦 Building Linux ARM64 binary (cross-compile)...`);
|
|
442
|
-
|
|
443
|
-
// Check and install toolchain if needed
|
|
444
|
-
if (!checkARM64Toolchain()) {
|
|
445
|
-
console.log(` [${timestamp()}] ⚙️ Checking ARM64 toolchain...`);
|
|
446
|
-
if (!installARM64Toolchain(verbose)) {
|
|
447
|
-
return false;
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
const binaryPath = path.join(binDir, 'msgernative-linux-aarch64');
|
|
452
|
-
const wslNativeDir = toWSLPath(nativeDir);
|
|
453
|
-
|
|
454
|
-
console.log(` [${timestamp()}] 🔨 Compiling ARM64 binary (this may take 2-5 minutes)...`);
|
|
455
|
-
|
|
456
|
-
try {
|
|
457
|
-
// Use rustup's cargo, not system cargo
|
|
458
|
-
// Set PKG_CONFIG_ALLOW_CROSS to enable cross-compilation
|
|
459
|
-
const cmd = `export PATH="$HOME/.cargo/bin:$PATH" && export PKG_CONFIG_ALLOW_CROSS=1 && cd "${wslNativeDir}" && cargo build --release --target aarch64-unknown-linux-gnu`;
|
|
460
|
-
const result = wslExec('bash', ['-c', cmd], {
|
|
461
|
-
stdio: verbose ? 'inherit' : 'pipe'
|
|
462
|
-
});
|
|
463
|
-
|
|
464
|
-
if (!result.success) {
|
|
465
|
-
throw new Error(result.error || 'Build failed');
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
// Copy to bin directory
|
|
469
|
-
if (!fs.existsSync(binDir)) {
|
|
470
|
-
fs.mkdirSync(binDir, { recursive: true });
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
const sourcePath = path.join(nativeDir, 'target', 'aarch64-unknown-linux-gnu', 'release', 'msgernative');
|
|
474
|
-
fs.copyFileSync(sourcePath, binaryPath);
|
|
475
|
-
fs.chmodSync(binaryPath, 0o755);
|
|
476
|
-
|
|
477
|
-
const stats = fs.statSync(binaryPath);
|
|
478
|
-
const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
|
|
479
|
-
console.log(` [${timestamp()}] ✅ Linux ARM64 binary completed`);
|
|
480
|
-
console.log(` [${timestamp()}] 📊 Binary size: ${sizeMB} MB`);
|
|
481
|
-
return true;
|
|
482
|
-
} catch (error: any) {
|
|
483
|
-
console.error(` [${timestamp()}] ❌ ARM64 build failed: ${error.message}`);
|
|
484
|
-
return false;
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
/**
|
|
489
|
-
* Main build orchestrator
|
|
490
|
-
*/
|
|
491
|
-
function main() {
|
|
492
|
-
console.log('🚀 msger-native unified builder\n');
|
|
493
|
-
|
|
494
|
-
const config = loadConfig();
|
|
495
|
-
const verbose = config.options?.verbose ?? false;
|
|
496
|
-
const release = config.options?.release ?? true;
|
|
497
|
-
|
|
498
|
-
const nativeDir = path.join(__dirname, '..');
|
|
499
|
-
const binDir = path.join(nativeDir, 'bin');
|
|
500
|
-
|
|
501
|
-
const enabledPlatforms = Object.entries(config.platforms)
|
|
502
|
-
.filter(([_, enabled]) => enabled)
|
|
503
|
-
.map(([name]) => name);
|
|
504
|
-
|
|
505
|
-
console.log(`📋 Build configuration:`);
|
|
506
|
-
console.log(` Mode: ${release ? 'release' : 'debug'}`);
|
|
507
|
-
console.log(` Platforms: ${enabledPlatforms.join(', ') || 'none'}\n`);
|
|
508
|
-
|
|
509
|
-
if (enabledPlatforms.length === 0) {
|
|
510
|
-
console.log('⚠️ No platforms enabled in build-config.json');
|
|
511
|
-
process.exit(0);
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
const results: { platform: string; success: boolean }[] = [];
|
|
515
|
-
|
|
516
|
-
// Build each enabled platform
|
|
517
|
-
if (config.platforms.windows) {
|
|
518
|
-
results.push({ platform: 'windows', success: buildWindows(nativeDir, binDir, verbose) });
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
if (config.platforms.wsl) {
|
|
522
|
-
results.push({ platform: 'wsl', success: buildWSL(nativeDir, binDir, verbose) });
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
if (config.platforms.pi) {
|
|
526
|
-
const piHost = config.options?.piHost ?? 'pi4c';
|
|
527
|
-
const piProjectPath = config.options?.piProjectPath ?? '/home/pi/dev/msger/msger-native';
|
|
528
|
-
results.push({ platform: 'pi', success: buildPi(nativeDir, binDir, verbose, piHost, piProjectPath) });
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
if (config.platforms.arm64) {
|
|
532
|
-
results.push({ platform: 'arm64', success: buildARM64(nativeDir, binDir, verbose) });
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
// Summary
|
|
536
|
-
console.log('\n' + '='.repeat(50));
|
|
537
|
-
console.log('📊 Build Summary:');
|
|
538
|
-
console.log('='.repeat(50));
|
|
539
|
-
|
|
540
|
-
for (const result of results) {
|
|
541
|
-
const status = result.success ? '✅' : '❌';
|
|
542
|
-
console.log(`${status} ${result.platform}`);
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
const successCount = results.filter(r => r.success).length;
|
|
546
|
-
const totalCount = results.length;
|
|
547
|
-
|
|
548
|
-
console.log('='.repeat(50));
|
|
549
|
-
console.log(`\n🎉 Completed: ${successCount}/${totalCount} builds successful\n`);
|
|
550
|
-
|
|
551
|
-
if (successCount < totalCount) {
|
|
552
|
-
process.exit(1);
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
main();
|
|
3
|
+
* msger-native builder — delegates to @bobfrankston/rust-builder
|
|
4
|
+
*/
|
|
5
|
+
import { loadConfig, build } from "@bobfrankston/rust-builder";
|
|
6
|
+
import path from "path";
|
|
7
|
+
import { fileURLToPath } from "url";
|
|
8
|
+
|
|
9
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
const nativeDir = path.join(__dirname, "..");
|
|
11
|
+
const binDir = path.join(nativeDir, "bin");
|
|
12
|
+
|
|
13
|
+
const config = loadConfig(
|
|
14
|
+
path.join(__dirname, "build-config.json"),
|
|
15
|
+
"msgernative",
|
|
16
|
+
nativeDir,
|
|
17
|
+
binDir
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
// msger-specific: copy icons
|
|
21
|
+
config.options = config.options || {};
|
|
22
|
+
config.options.icons = ["msger.ico", "msger.png"];
|
|
23
|
+
config.options.iconDir = path.join(nativeDir, "..");
|
|
24
|
+
|
|
25
|
+
const success = build(config);
|
|
26
|
+
if (!success) process.exit(1);
|
|
@@ -1,108 +1,21 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
*
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (osRelease.includes('ubuntu') || osRelease.includes('debian')) {
|
|
24
|
-
return 'debian';
|
|
25
|
-
}
|
|
26
|
-
if (osRelease.includes('fedora') || osRelease.includes('rhel') || osRelease.includes('centos')) {
|
|
27
|
-
return 'fedora';
|
|
28
|
-
}
|
|
29
|
-
if (osRelease.includes('arch')) {
|
|
30
|
-
return 'arch';
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return 'unknown';
|
|
34
|
-
} catch {
|
|
35
|
-
return 'unknown';
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function showDependencyInstallCommand(distro) {
|
|
40
|
-
console.warn('\n⚠️ Missing system dependencies detected!');
|
|
41
|
-
console.warn(' Install required libraries with:\n');
|
|
42
|
-
|
|
43
|
-
switch (distro) {
|
|
44
|
-
case 'debian':
|
|
45
|
-
console.warn(' sudo apt update');
|
|
46
|
-
console.warn(' sudo apt install libwebkit2gtk-4.1-0 libgtk-3-0\n');
|
|
47
|
-
break;
|
|
48
|
-
case 'fedora':
|
|
49
|
-
console.warn(' sudo dnf install webkit2gtk4.1 gtk3\n');
|
|
50
|
-
break;
|
|
51
|
-
case 'arch':
|
|
52
|
-
console.warn(' sudo pacman -S webkit2gtk gtk3\n');
|
|
53
|
-
break;
|
|
54
|
-
default:
|
|
55
|
-
console.warn(' Install webkit2gtk and gtk3 for your distribution\n');
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function checkSystemDependencies(binaryPath) {
|
|
60
|
-
try {
|
|
61
|
-
const lddOutput = execSync(`ldd "${binaryPath}" 2>&1`, { encoding: 'utf8' });
|
|
62
|
-
|
|
63
|
-
if (lddOutput.includes('not found')) {
|
|
64
|
-
const distro = detectDistro();
|
|
65
|
-
showDependencyInstallCommand(distro);
|
|
66
|
-
}
|
|
67
|
-
} catch (error) {
|
|
68
|
-
console.warn(`⚠️ Could not check system dependencies (ldd failed): ${error.message}`);
|
|
69
|
-
console.warn(' You may need to install webkit2gtk and gtk3 manually.');
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function setExecutePermissions(binaryPath, binaryName) {
|
|
74
|
-
try {
|
|
75
|
-
fs.chmodSync(binaryPath, 0o755);
|
|
76
|
-
console.log(`✅ Set execute permissions on ${binaryName} binary`);
|
|
77
|
-
} catch (error) {
|
|
78
|
-
console.warn(`⚠️ Could not set execute permission on ${binaryPath}: ${error.message}`);
|
|
79
|
-
console.warn(` You may need to run: sudo chmod +x ${binaryPath}`);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function main() {
|
|
84
|
-
// Windows doesn't need executable permissions
|
|
85
|
-
if (process.platform === 'win32') {
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Determine binary name based on architecture
|
|
90
|
-
const arch = process.arch;
|
|
91
|
-
const binaryName = arch === 'arm64' ? 'msgernative-linux-aarch64' : 'msgernative';
|
|
92
|
-
const binaryPath = path.join(__dirname, '..', 'bin', binaryName);
|
|
93
|
-
|
|
94
|
-
// Binary doesn't exist - warn user
|
|
95
|
-
if (!fs.existsSync(binaryPath)) {
|
|
96
|
-
console.warn(`⚠️ Binary not found at ${binaryPath}`);
|
|
97
|
-
console.warn(` The msger package may not support your platform (${process.platform} ${arch})`);
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Set executable permissions
|
|
102
|
-
setExecutePermissions(binaryPath, binaryName);
|
|
103
|
-
|
|
104
|
-
// Check for required system libraries
|
|
105
|
-
checkSystemDependencies(binaryPath);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
main();
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* msger-native postinstall — delegates to @bobfrankston/rust-builder
|
|
4
|
+
*/
|
|
5
|
+
import { runPostinstall } from "@bobfrankston/rust-builder/postinstall";
|
|
6
|
+
import path from "path";
|
|
7
|
+
import { fileURLToPath } from "url";
|
|
8
|
+
|
|
9
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
|
|
11
|
+
runPostinstall({
|
|
12
|
+
binaryName: "msgernative",
|
|
13
|
+
binDir: path.join(__dirname, "..", "bin"),
|
|
14
|
+
binaries: {
|
|
15
|
+
win32: "msgernative.exe",
|
|
16
|
+
darwin: "msgernative",
|
|
17
|
+
darwinArm64: "msgernative-arm64",
|
|
18
|
+
linux: "msgernative",
|
|
19
|
+
linuxArm64: "msgernative-linux-aarch64",
|
|
20
|
+
},
|
|
21
|
+
});
|
package/package.json
CHANGED
|
@@ -1,65 +1,65 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
2
|
+
"name": "@bobfrankston/msger",
|
|
3
|
+
"version": "0.1.190",
|
|
4
|
+
"description": "Fast, lightweight, cross-platform message box - Rust-powered alternative to msgview",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"msger": "cli.js"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": "./index.js",
|
|
13
|
+
"types": "./index.d.ts"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "npm run build:ts && npm run build:native",
|
|
18
|
+
"build:native": "cd msger-native && npm run build",
|
|
19
|
+
"build:ts": "tsc",
|
|
20
|
+
"watch": "tsc -w",
|
|
21
|
+
"clean": "node msger-native/builder/clean.ts",
|
|
22
|
+
"test": "node test.js",
|
|
23
|
+
"postinstall": "node msger-native/builder/postinstall.js",
|
|
24
|
+
"prepublishOnly": "npm run build:ts && npm run build:native",
|
|
25
|
+
"prerelease:local": "git add -A && (git diff-index --quiet HEAD || git commit -m \"Pre-release commit\")",
|
|
26
|
+
"preversion": "npm run build:ts && npm run build:native && git add -A",
|
|
27
|
+
"release": "npm run prerelease:local && npm version patch && npm publish --quiet",
|
|
28
|
+
"installer": "npm run release && node utils/install-latest.ts",
|
|
29
|
+
"postversion": "git push && git push --tags"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"message-box",
|
|
33
|
+
"dialog",
|
|
34
|
+
"webview",
|
|
35
|
+
"rust",
|
|
36
|
+
"native",
|
|
37
|
+
"cross-platform",
|
|
38
|
+
"typescript"
|
|
39
|
+
],
|
|
40
|
+
"author": "Bob Frankston",
|
|
41
|
+
"license": "ISC",
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "git+https://github.com/BobFrankston/msger.git"
|
|
45
|
+
},
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/node": "^25.2.1"
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"@bobfrankston/msgcommon": "^0.1.23",
|
|
54
|
+
"ansi-to-html": "^0.7.2",
|
|
55
|
+
"json5": "^2.2.3"
|
|
56
|
+
},
|
|
57
|
+
"files": [
|
|
58
|
+
"*.js",
|
|
59
|
+
"*.d.ts",
|
|
60
|
+
"*.d.ts.map",
|
|
61
|
+
"msger-native/bin/",
|
|
62
|
+
"msger-native/builder/",
|
|
63
|
+
"README.md"
|
|
64
|
+
]
|
|
65
65
|
}
|