@bobfrankston/msger 0.1.128 → 0.1.130
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/cruft/clean.d.ts +3 -0
- package/cruft/clean.d.ts.map +1 -0
- package/cruft/clean.js +71 -0
- package/cruft/clean.js.map +1 -0
- package/{msgernative-linux-x64 → cruft/msgernative-linux-x64} +0 -0
- package/docs/README.md +26 -0
- package/msger-native/bin/msgernative +0 -0
- package/msger-native/bin/msgernative-arm64 +0 -0
- package/msger-native/bin/msgernative.exe +0 -0
- package/msger-native/builder/README.md +69 -0
- package/msger-native/builder/build-config.json +14 -0
- package/msger-native/builder/build-under-wsl.ts +210 -0
- package/msger-native/builder/builder.ts +394 -0
- package/msger-native/builder/clean.ts +86 -0
- package/msger-native/builder/postinstall.ts +107 -0
- package/msger-native/package.json +1 -4
- package/msger-native/pibuild/README.md +42 -0
- package/msger-native/src/main.rs +5 -4
- package/package.json +5 -10
- /package/{msger-native → cruft}/apply-zoom-fix.js +0 -0
- /package/{msger-native → cruft}/build-arm64.ts +0 -0
- /package/{msger-native → cruft}/build-pi.ts +0 -0
- /package/{msger-native → cruft}/build-pi.ts.backup +0 -0
- /package/{msger-native → cruft}/build-pi.ts.old +0 -0
- /package/{msger-native → cruft}/build-wsl.ts +0 -0
- /package/{msger-native → cruft}/build.js +0 -0
- /package/{msger-native → cruft}/build.ts +0 -0
- /package/{msgernative-win32-x64.exe → cruft/msgernative-win32-x64.exe} +0 -0
- /package/{postinstall.js → cruft/postinstall.js} +0 -0
- /package/{test-size.d.ts → cruft/test-size.d.ts} +0 -0
- /package/{CHANGELOG.md → docs/CHANGELOG.md} +0 -0
- /package/{CLOSE-API.md → docs/CLOSE-API.md} +0 -0
- /package/{DEVELOPERS.md → docs/DEVELOPERS.md} +0 -0
- /package/{FULLSCREEN-FEATURE.md → docs/FULLSCREEN-FEATURE.md} +0 -0
- /package/{IMPLEMENTATION-SUMMARY.md → docs/IMPLEMENTATION-SUMMARY.md} +0 -0
- /package/{KNOWN-BUGS.md → docs/KNOWN-BUGS.md} +0 -0
- /package/{MSGER-API-SUMMARY.md → docs/MSGER-API-SUMMARY.md} +0 -0
- /package/{MSGER-API.md → docs/MSGER-API.md} +0 -0
- /package/{PI-RENDERING-NOTES.md → docs/PI-RENDERING-NOTES.md} +0 -0
- /package/{RELEASE-NOTES.md → docs/RELEASE-NOTES.md} +0 -0
- /package/{TODO.md → docs/TODO.md} +0 -0
- /package/{SESSION-2025-11-06.md → docs/sessions/SESSION-2025-11-06.md} +0 -0
- /package/{SESSION-NOTES.md → docs/sessions/SESSION-NOTES.md} +0 -0
- /package/{SESSION-RESUME-NOTES.md → docs/sessions/SESSION-RESUME-NOTES.md} +0 -0
- /package/msger-native/{build-on-pi.sh → pibuild/build-on-pi.sh} +0 -0
- /package/msger-native/{build-pi.ps1 → pibuild/build-pi.ps1} +0 -0
- /package/msger-native/{pi-diagnostics.ts → pibuild/pi-diagnostics.ts} +0 -0
- /package/msger-native/{setup-arm64-toolchain.sh → pibuild/setup-arm64-toolchain.sh} +0 -0
package/cruft/clean.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clean.d.ts","sourceRoot":"","sources":["clean.js"],"names":[],"mappings":""}
|
package/cruft/clean.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Cross-platform clean script
|
|
4
|
+
* Removes build artifacts
|
|
5
|
+
*/
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import { execSync } from 'child_process';
|
|
9
|
+
const rootDir = process.cwd();
|
|
10
|
+
console.log('🧹 Cleaning build artifacts...\n');
|
|
11
|
+
// Files to delete in root directory
|
|
12
|
+
const rootPatterns = [
|
|
13
|
+
'*.js',
|
|
14
|
+
'*.d.ts',
|
|
15
|
+
'*.d.ts.map',
|
|
16
|
+
'*.js.map'
|
|
17
|
+
];
|
|
18
|
+
// Directories to exclude
|
|
19
|
+
const excludeDirs = ['node_modules', '.git', 'msger-native'];
|
|
20
|
+
function deleteFile(filePath) {
|
|
21
|
+
try {
|
|
22
|
+
fs.unlinkSync(filePath);
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function matchesPattern(filename, pattern) {
|
|
30
|
+
const regex = new RegExp('^' + pattern.replace(/\*/g, '.*').replace(/\./g, '\\.') + '$');
|
|
31
|
+
return regex.test(filename);
|
|
32
|
+
}
|
|
33
|
+
// Clean root directory
|
|
34
|
+
let deletedCount = 0;
|
|
35
|
+
const files = fs.readdirSync(rootDir);
|
|
36
|
+
for (const file of files) {
|
|
37
|
+
const filePath = path.join(rootDir, file);
|
|
38
|
+
const stat = fs.statSync(filePath);
|
|
39
|
+
if (stat.isDirectory())
|
|
40
|
+
continue;
|
|
41
|
+
for (const pattern of rootPatterns) {
|
|
42
|
+
if (matchesPattern(file, pattern)) {
|
|
43
|
+
if (deleteFile(filePath)) {
|
|
44
|
+
console.log(` ✓ Deleted: ${file}`);
|
|
45
|
+
deletedCount++;
|
|
46
|
+
}
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
console.log(`\n📁 Cleaned ${deletedCount} files from root directory`);
|
|
52
|
+
// Clean Rust build artifacts
|
|
53
|
+
console.log('\n🦀 Cleaning Rust build artifacts...');
|
|
54
|
+
const nativeDir = path.join(rootDir, 'msger-native');
|
|
55
|
+
if (fs.existsSync(nativeDir)) {
|
|
56
|
+
try {
|
|
57
|
+
execSync('cargo clean', {
|
|
58
|
+
cwd: nativeDir,
|
|
59
|
+
stdio: 'inherit'
|
|
60
|
+
});
|
|
61
|
+
console.log(' ✓ Rust artifacts cleaned');
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
console.error(' ✗ Failed to run cargo clean');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
console.log(' ⚠️ msger-native directory not found');
|
|
69
|
+
}
|
|
70
|
+
console.log('\n✨ Clean complete!\n');
|
|
71
|
+
//# sourceMappingURL=clean.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clean.js","sourceRoot":"","sources":["clean.ts"],"names":[],"mappings":";AACA;;;GAGG;AAEH,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;AAE9B,OAAO,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAC;AAEhD,oCAAoC;AACpC,MAAM,YAAY,GAAG;IACjB,MAAM;IACN,QAAQ;IACR,YAAY;IACZ,UAAU;CACb,CAAC;AAEF,yBAAyB;AACzB,MAAM,WAAW,GAAG,CAAC,cAAc,EAAE,MAAM,EAAE,cAAc,CAAC,CAAC;AAE7D,SAAS,UAAU,CAAC,QAAgB;IAChC,IAAI,CAAC;QACD,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QACxB,OAAO,IAAI,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,KAAK,CAAC;IACjB,CAAC;AACL,CAAC;AAED,SAAS,cAAc,CAAC,QAAgB,EAAE,OAAe;IACrD,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC;IACzF,OAAO,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAChC,CAAC;AAED,uBAAuB;AACvB,IAAI,YAAY,GAAG,CAAC,CAAC;AACrB,MAAM,KAAK,GAAG,EAAE,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;AAEtC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;IACvB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAEnC,IAAI,IAAI,CAAC,WAAW,EAAE;QAAE,SAAS;IAEjC,KAAK,MAAM,OAAO,IAAI,YAAY,EAAE,CAAC;QACjC,IAAI,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,CAAC;YAChC,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACvB,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,EAAE,CAAC,CAAC;gBACpC,YAAY,EAAE,CAAC;YACnB,CAAC;YACD,MAAM;QACV,CAAC;IACL,CAAC;AACL,CAAC;AAED,OAAO,CAAC,GAAG,CAAC,gBAAgB,YAAY,4BAA4B,CAAC,CAAC;AAEtE,6BAA6B;AAC7B,OAAO,CAAC,GAAG,CAAC,uCAAuC,CAAC,CAAC;AACrD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;AAErD,IAAI,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;IAC3B,IAAI,CAAC;QACD,QAAQ,CAAC,aAAa,EAAE;YACpB,GAAG,EAAE,SAAS;YACd,KAAK,EAAE,SAAS;SACnB,CAAC,CAAC;QACH,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;IAC9C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;IACnD,CAAC;AACL,CAAC;KAAM,CAAC;IACJ,OAAO,CAAC,GAAG,CAAC,wCAAwC,CAAC,CAAC;AAC1D,CAAC;AAED,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC"}
|
|
Binary file
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# msger Documentation
|
|
2
|
+
|
|
3
|
+
## User Documentation
|
|
4
|
+
- [README.md](../README.md) - Main project README (in root)
|
|
5
|
+
- [MSGER-API.md](MSGER-API.md) - JavaScript API for message box content
|
|
6
|
+
- [MSGER-API-SUMMARY.md](MSGER-API-SUMMARY.md) - Quick API reference
|
|
7
|
+
- [CLOSE-API.md](CLOSE-API.md) - Programmatic close API
|
|
8
|
+
|
|
9
|
+
## Release Information
|
|
10
|
+
- [CHANGELOG.md](CHANGELOG.md) - Version history and changes
|
|
11
|
+
- [RELEASE-NOTES.md](RELEASE-NOTES.md) - User-facing release notes
|
|
12
|
+
- [KNOWN-BUGS.md](KNOWN-BUGS.md) - Known issues and limitations
|
|
13
|
+
|
|
14
|
+
## Features
|
|
15
|
+
- [DETACH-FEATURE.md](DETACH-FEATURE.md) - Detached window mode
|
|
16
|
+
- [FULLSCREEN-FEATURE.md](FULLSCREEN-FEATURE.md) - Fullscreen mode
|
|
17
|
+
- [MOUSE-ZOOM-FEATURE.md](MOUSE-ZOOM-FEATURE.md) - Mouse wheel zoom
|
|
18
|
+
|
|
19
|
+
## Developer Documentation
|
|
20
|
+
- [DEVELOPERS.md](DEVELOPERS.md) - Developer guide
|
|
21
|
+
- [IMPLEMENTATION-SUMMARY.md](IMPLEMENTATION-SUMMARY.md) - Implementation details
|
|
22
|
+
- [PI-RENDERING-NOTES.md](PI-RENDERING-NOTES.md) - Raspberry Pi specific notes
|
|
23
|
+
- [TODO.md](TODO.md) - Future plans and tasks
|
|
24
|
+
|
|
25
|
+
## Session Notes
|
|
26
|
+
Development session notes are in [sessions/](sessions/)
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# msger-native Builder
|
|
2
|
+
|
|
3
|
+
Unified build system for msger-native that uses JSON configuration to control which platforms to build.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
cd msger-native
|
|
9
|
+
npm run build
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Configuration
|
|
13
|
+
|
|
14
|
+
Edit `builder/build-config.json` to control which platforms are built:
|
|
15
|
+
|
|
16
|
+
```json
|
|
17
|
+
{
|
|
18
|
+
"platforms": {
|
|
19
|
+
"windows": true, // Build Windows x64 binary
|
|
20
|
+
"wsl": false, // Build Linux x64 binary (via WSL)
|
|
21
|
+
"pi": false, // Build Raspberry Pi ARM64 binary (remote build on Pi)
|
|
22
|
+
"arm64": false // Build Linux ARM64 binary (cross-compile via WSL)
|
|
23
|
+
},
|
|
24
|
+
"options": {
|
|
25
|
+
"release": true, // Use release mode (optimized)
|
|
26
|
+
"verbose": false, // Show detailed cargo output
|
|
27
|
+
"piHost": "pi4c", // Raspberry Pi hostname for SSH
|
|
28
|
+
"piProjectPath": "/home/pi/dev/msger/msger-native" // Project path on Pi
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Build Targets
|
|
34
|
+
|
|
35
|
+
| Platform | Output | Method | Requirements |
|
|
36
|
+
|----------|--------|--------|--------------|
|
|
37
|
+
| `windows` | `bin/msgernative.exe` | Native build on Windows | Windows, Visual Studio Build Tools |
|
|
38
|
+
| `wsl` | `bin/msgernative` | Native build in WSL | WSL with Rust toolchain |
|
|
39
|
+
| `pi` | `bin/msgernative-linux-aarch64` | **Remote build on Pi** (slower, guaranteed compatible) | SSH access to Raspberry Pi |
|
|
40
|
+
| `arm64` | `bin/msgernative-linux-aarch64` | **Cross-compile on WSL** (faster, requires setup) | WSL with ARM64 cross-compilation toolchain |
|
|
41
|
+
|
|
42
|
+
**Pi vs ARM64:**
|
|
43
|
+
- **`pi`**: Builds on the actual Raspberry Pi via SSH - slower but guaranteed to work
|
|
44
|
+
- **`arm64`**: Cross-compiles locally using WSL - faster but requires cross-toolchain setup
|
|
45
|
+
|
|
46
|
+
## Features
|
|
47
|
+
|
|
48
|
+
- **JSON-driven configuration**: Simply edit JSON file to enable/disable platforms
|
|
49
|
+
- **Build summary**: Shows which builds succeeded/failed
|
|
50
|
+
- **Size reporting**: Displays binary size for each platform
|
|
51
|
+
- **TypeScript**: Uses Node's native TypeScript support (no compilation needed)
|
|
52
|
+
- **Conservative defaults**: Only builds Windows by default
|
|
53
|
+
|
|
54
|
+
## Legacy Scripts
|
|
55
|
+
|
|
56
|
+
The old individual build scripts are still available:
|
|
57
|
+
- `npm run build:legacy` - Original Windows build script
|
|
58
|
+
- `npm run build:wsl` - WSL Linux build
|
|
59
|
+
- `npm run build:pi` - Raspberry Pi build
|
|
60
|
+
- `npm run build:arm64` - ARM64 cross-compile
|
|
61
|
+
|
|
62
|
+
## Future Enhancements
|
|
63
|
+
|
|
64
|
+
Potential additions:
|
|
65
|
+
- Custom Cargo features/flags configuration
|
|
66
|
+
- Parallel builds
|
|
67
|
+
- Post-build verification/testing
|
|
68
|
+
- Binary signing/notarization
|
|
69
|
+
- Deployment automation
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Build script to run natively inside WSL
|
|
4
|
+
* Handles both x64 and ARM64 builds
|
|
5
|
+
* Reads configuration from build-config.json
|
|
6
|
+
*
|
|
7
|
+
* Usage from WSL:
|
|
8
|
+
* node builder/build-under-wsl.ts
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { execSync } from 'child_process';
|
|
12
|
+
import fs from 'fs';
|
|
13
|
+
import path from 'path';
|
|
14
|
+
import { fileURLToPath } from 'url';
|
|
15
|
+
|
|
16
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
17
|
+
const __dirname = path.dirname(__filename);
|
|
18
|
+
|
|
19
|
+
interface BuildConfig {
|
|
20
|
+
platforms: {
|
|
21
|
+
windows?: boolean;
|
|
22
|
+
wsl?: boolean;
|
|
23
|
+
pi?: boolean;
|
|
24
|
+
arm64?: boolean;
|
|
25
|
+
};
|
|
26
|
+
options?: {
|
|
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();
|
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Unified build system for msger-native
|
|
4
|
+
* Reads build-config.json to determine which platforms to build
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { execSync } from 'child_process';
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
11
|
+
import { platform } from 'os';
|
|
12
|
+
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
const __dirname = path.dirname(__filename);
|
|
15
|
+
|
|
16
|
+
interface BuildConfig {
|
|
17
|
+
platforms: {
|
|
18
|
+
windows?: boolean;
|
|
19
|
+
wsl?: boolean;
|
|
20
|
+
pi?: boolean;
|
|
21
|
+
arm64?: boolean;
|
|
22
|
+
};
|
|
23
|
+
options?: {
|
|
24
|
+
release?: boolean;
|
|
25
|
+
verbose?: boolean;
|
|
26
|
+
piHost?: string;
|
|
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
|
+
function needsRebuild(binaryPath: string, sourceDirs: string[]): boolean {
|
|
56
|
+
if (!fs.existsSync(binaryPath)) {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const binaryTime = fs.statSync(binaryPath).mtime.getTime();
|
|
61
|
+
|
|
62
|
+
function checkDir(dirPath: string): boolean {
|
|
63
|
+
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
64
|
+
for (const entry of entries) {
|
|
65
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
66
|
+
if (entry.isFile()) {
|
|
67
|
+
const fileTime = fs.statSync(fullPath).mtime.getTime();
|
|
68
|
+
if (fileTime > binaryTime) {
|
|
69
|
+
console.log(` 📝 Source file changed: ${entry.name}`);
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
} else if (entry.isDirectory()) {
|
|
73
|
+
if (checkDir(fullPath)) {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
for (const sourcePath of sourceDirs) {
|
|
82
|
+
if (!fs.existsSync(sourcePath)) continue;
|
|
83
|
+
|
|
84
|
+
const stats = fs.statSync(sourcePath);
|
|
85
|
+
if (stats.isFile()) {
|
|
86
|
+
const fileTime = stats.mtime.getTime();
|
|
87
|
+
if (fileTime > binaryTime) {
|
|
88
|
+
console.log(` 📝 Source file changed: ${sourcePath}`);
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
} else if (stats.isDirectory()) {
|
|
92
|
+
if (checkDir(sourcePath)) {
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Load build configuration from JSON file
|
|
103
|
+
*/
|
|
104
|
+
function loadConfig(): BuildConfig {
|
|
105
|
+
const configPath = path.join(__dirname, 'build-config.json');
|
|
106
|
+
|
|
107
|
+
if (!fs.existsSync(configPath)) {
|
|
108
|
+
console.error('❌ build-config.json not found');
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
const content = fs.readFileSync(configPath, 'utf-8');
|
|
114
|
+
return JSON.parse(content);
|
|
115
|
+
} catch (error: any) {
|
|
116
|
+
console.error(`❌ Failed to parse build-config.json: ${error.message}`);
|
|
117
|
+
process.exit(1);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function timestamp(): string {
|
|
122
|
+
const now = new Date();
|
|
123
|
+
return now.toTimeString().split(' ')[0]; // HH:MM:SS
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Build Windows binary
|
|
128
|
+
*/
|
|
129
|
+
function buildWindows(nativeDir: string, binDir: string, verbose: boolean): boolean {
|
|
130
|
+
console.log(`\n[${timestamp()}] 📦 Building Windows x64 binary...`);
|
|
131
|
+
|
|
132
|
+
const binaryPath = path.join(binDir, 'msgernative.exe');
|
|
133
|
+
const sourceDirs = [
|
|
134
|
+
path.join(nativeDir, 'src'),
|
|
135
|
+
path.join(nativeDir, 'Cargo.toml'),
|
|
136
|
+
path.join(nativeDir, 'build.rs')
|
|
137
|
+
];
|
|
138
|
+
|
|
139
|
+
if (!needsRebuild(binaryPath, sourceDirs)) {
|
|
140
|
+
console.log(` [${timestamp()}] ✅ Binary is up to date, skipping build`);
|
|
141
|
+
const stats = fs.statSync(binaryPath);
|
|
142
|
+
const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
|
|
143
|
+
console.log(` [${timestamp()}] 📊 Binary size: ${sizeMB} MB`);
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
console.log(` [${timestamp()}] 🔧 Fixing PATH to use VS linker instead of Git link.exe`);
|
|
148
|
+
const cleanPath = getCleanPathForWindows();
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
execSync('cargo build --release', {
|
|
152
|
+
cwd: nativeDir,
|
|
153
|
+
stdio: verbose ? 'inherit' : 'pipe',
|
|
154
|
+
env: { ...process.env, PATH: cleanPath }
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// Copy to bin directory
|
|
158
|
+
if (!fs.existsSync(binDir)) {
|
|
159
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const sourcePath = path.join(nativeDir, 'target', 'release', 'msgernative.exe');
|
|
163
|
+
fs.copyFileSync(sourcePath, binaryPath);
|
|
164
|
+
|
|
165
|
+
const stats = fs.statSync(binaryPath);
|
|
166
|
+
const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
|
|
167
|
+
console.log(` [${timestamp()}] ✅ Windows x64 binary completed`);
|
|
168
|
+
console.log(` [${timestamp()}] 📊 Binary size: ${sizeMB} MB`);
|
|
169
|
+
return true;
|
|
170
|
+
} catch (error: any) {
|
|
171
|
+
console.error(` [${timestamp()}] ❌ Windows build failed: ${error.message}`);
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Build WSL Linux binary
|
|
178
|
+
*/
|
|
179
|
+
function buildWSL(nativeDir: string, binDir: string, verbose: boolean): boolean {
|
|
180
|
+
console.log(`\n[${timestamp()}] 📦 Building WSL Linux x64 binary...`);
|
|
181
|
+
|
|
182
|
+
const binaryPath = path.join(binDir, 'msgernative');
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
execSync('wsl bash -c "cd /mnt/y/dev/utils/msger/msger-native && cargo build --release --target x86_64-unknown-linux-gnu"', {
|
|
186
|
+
stdio: verbose ? 'inherit' : 'pipe'
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// Copy to bin directory
|
|
190
|
+
if (!fs.existsSync(binDir)) {
|
|
191
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const sourcePath = path.join(nativeDir, 'target', 'x86_64-unknown-linux-gnu', 'release', 'msgernative');
|
|
195
|
+
fs.copyFileSync(sourcePath, binaryPath);
|
|
196
|
+
fs.chmodSync(binaryPath, 0o755);
|
|
197
|
+
|
|
198
|
+
const stats = fs.statSync(binaryPath);
|
|
199
|
+
const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
|
|
200
|
+
console.log(` [${timestamp()}] ✅ WSL Linux x64 binary completed`);
|
|
201
|
+
console.log(` [${timestamp()}] 📊 Binary size: ${sizeMB} MB`);
|
|
202
|
+
return true;
|
|
203
|
+
} catch (error: any) {
|
|
204
|
+
console.error(` [${timestamp()}] ❌ WSL build failed: ${error.message}`);
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Build Raspberry Pi ARM64 binary
|
|
211
|
+
*/
|
|
212
|
+
function buildPi(nativeDir: string, binDir: string, verbose: boolean, piHost: string, piProjectPath: string): boolean {
|
|
213
|
+
console.log(`\n[${timestamp()}] 📦 Building Raspberry Pi ARM64 binary...`);
|
|
214
|
+
console.log(` Target: ${piHost}:${piProjectPath}`);
|
|
215
|
+
|
|
216
|
+
const binaryPath = path.join(binDir, 'msgernative-linux-aarch64');
|
|
217
|
+
|
|
218
|
+
try {
|
|
219
|
+
// Build remotely on Pi
|
|
220
|
+
console.log(` [${timestamp()}] 🔗 Building on remote Raspberry Pi...`);
|
|
221
|
+
execSync(`ssh ${piHost} "cd ${piProjectPath} && cargo build --release"`, {
|
|
222
|
+
stdio: verbose ? 'inherit' : 'pipe'
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
// Copy from Pi
|
|
226
|
+
console.log(` [${timestamp()}] 📥 Copying binary from Pi...`);
|
|
227
|
+
if (!fs.existsSync(binDir)) {
|
|
228
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
execSync(`scp ${piHost}:${piProjectPath}/target/release/msgernative "${binaryPath}"`, {
|
|
232
|
+
stdio: verbose ? 'inherit' : 'pipe'
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
fs.chmodSync(binaryPath, 0o755);
|
|
236
|
+
|
|
237
|
+
const stats = fs.statSync(binaryPath);
|
|
238
|
+
const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
|
|
239
|
+
console.log(` [${timestamp()}] ✅ Raspberry Pi ARM64 binary completed`);
|
|
240
|
+
console.log(` [${timestamp()}] 📊 Binary size: ${sizeMB} MB`);
|
|
241
|
+
return true;
|
|
242
|
+
} catch (error: any) {
|
|
243
|
+
console.error(` [${timestamp()}] ❌ Pi build failed: ${error.message}`);
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Check if ARM64 toolchain is installed
|
|
250
|
+
*/
|
|
251
|
+
function checkARM64Toolchain(): boolean {
|
|
252
|
+
try {
|
|
253
|
+
// Check if Rust target is installed
|
|
254
|
+
const targets = execSync('wsl bash -c "rustup target list --installed"', { encoding: 'utf-8' });
|
|
255
|
+
if (!targets.includes('aarch64-unknown-linux-gnu')) {
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Check if cross-compiler is installed
|
|
260
|
+
const which = execSync('wsl bash -c "which aarch64-linux-gnu-gcc"', { encoding: 'utf-8' });
|
|
261
|
+
return which.trim().length > 0;
|
|
262
|
+
} catch {
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Install ARM64 toolchain
|
|
269
|
+
*/
|
|
270
|
+
function installARM64Toolchain(verbose: boolean): boolean {
|
|
271
|
+
console.log(` [${timestamp()}] 📦 ARM64 toolchain not found, installing...`);
|
|
272
|
+
|
|
273
|
+
try {
|
|
274
|
+
execSync('wsl bash msger-native/pibuild/setup-arm64-toolchain.sh', {
|
|
275
|
+
stdio: verbose ? 'inherit' : 'pipe'
|
|
276
|
+
});
|
|
277
|
+
console.log(` [${timestamp()}] ✅ ARM64 toolchain installed successfully`);
|
|
278
|
+
return true;
|
|
279
|
+
} catch (error: any) {
|
|
280
|
+
console.error(` [${timestamp()}] ❌ Failed to install ARM64 toolchain: ${error.message}`);
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Build generic ARM64 binary via cross-compilation
|
|
287
|
+
*/
|
|
288
|
+
function buildARM64(nativeDir: string, binDir: string, verbose: boolean): boolean {
|
|
289
|
+
console.log(`\n[${timestamp()}] 📦 Building Linux ARM64 binary (cross-compile)...`);
|
|
290
|
+
|
|
291
|
+
// Check and install toolchain if needed
|
|
292
|
+
if (!checkARM64Toolchain()) {
|
|
293
|
+
console.log(` [${timestamp()}] ⚙️ Checking ARM64 toolchain...`);
|
|
294
|
+
if (!installARM64Toolchain(verbose)) {
|
|
295
|
+
return false;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const binaryPath = path.join(binDir, 'msgernative-linux-aarch64');
|
|
300
|
+
|
|
301
|
+
try {
|
|
302
|
+
execSync('wsl bash -c "cd /mnt/y/dev/utils/msger/msger-native && cargo build --release --target aarch64-unknown-linux-gnu"', {
|
|
303
|
+
stdio: verbose ? 'inherit' : 'pipe'
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
// Copy to bin directory
|
|
307
|
+
if (!fs.existsSync(binDir)) {
|
|
308
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const sourcePath = path.join(nativeDir, 'target', 'aarch64-unknown-linux-gnu', 'release', 'msgernative');
|
|
312
|
+
fs.copyFileSync(sourcePath, binaryPath);
|
|
313
|
+
fs.chmodSync(binaryPath, 0o755);
|
|
314
|
+
|
|
315
|
+
const stats = fs.statSync(binaryPath);
|
|
316
|
+
const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
|
|
317
|
+
console.log(` [${timestamp()}] ✅ Linux ARM64 binary completed`);
|
|
318
|
+
console.log(` [${timestamp()}] 📊 Binary size: ${sizeMB} MB`);
|
|
319
|
+
return true;
|
|
320
|
+
} catch (error: any) {
|
|
321
|
+
console.error(` [${timestamp()}] ❌ ARM64 build failed: ${error.message}`);
|
|
322
|
+
return false;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Main build orchestrator
|
|
328
|
+
*/
|
|
329
|
+
function main() {
|
|
330
|
+
console.log('🚀 msger-native unified builder\n');
|
|
331
|
+
|
|
332
|
+
const config = loadConfig();
|
|
333
|
+
const verbose = config.options?.verbose ?? false;
|
|
334
|
+
const release = config.options?.release ?? true;
|
|
335
|
+
|
|
336
|
+
const nativeDir = path.join(__dirname, '..');
|
|
337
|
+
const binDir = path.join(nativeDir, 'bin');
|
|
338
|
+
|
|
339
|
+
const enabledPlatforms = Object.entries(config.platforms)
|
|
340
|
+
.filter(([_, enabled]) => enabled)
|
|
341
|
+
.map(([name]) => name);
|
|
342
|
+
|
|
343
|
+
console.log(`📋 Build configuration:`);
|
|
344
|
+
console.log(` Mode: ${release ? 'release' : 'debug'}`);
|
|
345
|
+
console.log(` Platforms: ${enabledPlatforms.join(', ') || 'none'}\n`);
|
|
346
|
+
|
|
347
|
+
if (enabledPlatforms.length === 0) {
|
|
348
|
+
console.log('⚠️ No platforms enabled in build-config.json');
|
|
349
|
+
process.exit(0);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const results: { platform: string; success: boolean }[] = [];
|
|
353
|
+
|
|
354
|
+
// Build each enabled platform
|
|
355
|
+
if (config.platforms.windows) {
|
|
356
|
+
results.push({ platform: 'windows', success: buildWindows(nativeDir, binDir, verbose) });
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (config.platforms.wsl) {
|
|
360
|
+
results.push({ platform: 'wsl', success: buildWSL(nativeDir, binDir, verbose) });
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (config.platforms.pi) {
|
|
364
|
+
const piHost = config.options?.piHost ?? 'pi4c';
|
|
365
|
+
const piProjectPath = config.options?.piProjectPath ?? '/home/pi/dev/msger/msger-native';
|
|
366
|
+
results.push({ platform: 'pi', success: buildPi(nativeDir, binDir, verbose, piHost, piProjectPath) });
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if (config.platforms.arm64) {
|
|
370
|
+
results.push({ platform: 'arm64', success: buildARM64(nativeDir, binDir, verbose) });
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Summary
|
|
374
|
+
console.log('\n' + '='.repeat(50));
|
|
375
|
+
console.log('📊 Build Summary:');
|
|
376
|
+
console.log('='.repeat(50));
|
|
377
|
+
|
|
378
|
+
for (const result of results) {
|
|
379
|
+
const status = result.success ? '✅' : '❌';
|
|
380
|
+
console.log(`${status} ${result.platform}`);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const successCount = results.filter(r => r.success).length;
|
|
384
|
+
const totalCount = results.length;
|
|
385
|
+
|
|
386
|
+
console.log('='.repeat(50));
|
|
387
|
+
console.log(`\n🎉 Completed: ${successCount}/${totalCount} builds successful\n`);
|
|
388
|
+
|
|
389
|
+
if (successCount < totalCount) {
|
|
390
|
+
process.exit(1);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
main();
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Cross-platform clean script
|
|
4
|
+
* Removes build artifacts
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import { execSync } from 'child_process';
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
11
|
+
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = path.dirname(__filename);
|
|
14
|
+
|
|
15
|
+
// Navigate to project root (two levels up from builder/)
|
|
16
|
+
const rootDir = path.join(__dirname, '../..');
|
|
17
|
+
|
|
18
|
+
console.log('🧹 Cleaning build artifacts...\n');
|
|
19
|
+
|
|
20
|
+
// Files to delete in root directory
|
|
21
|
+
const rootPatterns = [
|
|
22
|
+
'*.js',
|
|
23
|
+
'*.d.ts',
|
|
24
|
+
'*.d.ts.map',
|
|
25
|
+
'*.js.map'
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
// Directories to exclude
|
|
29
|
+
const excludeDirs = ['node_modules', '.git', 'msger-native'];
|
|
30
|
+
|
|
31
|
+
function deleteFile(filePath: string): boolean {
|
|
32
|
+
try {
|
|
33
|
+
fs.unlinkSync(filePath);
|
|
34
|
+
return true;
|
|
35
|
+
} catch {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function matchesPattern(filename: string, pattern: string): boolean {
|
|
41
|
+
const regex = new RegExp('^' + pattern.replace(/\*/g, '.*').replace(/\./g, '\\.') + '$');
|
|
42
|
+
return regex.test(filename);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Clean root directory
|
|
46
|
+
let deletedCount = 0;
|
|
47
|
+
const files = fs.readdirSync(rootDir);
|
|
48
|
+
|
|
49
|
+
for (const file of files) {
|
|
50
|
+
const filePath = path.join(rootDir, file);
|
|
51
|
+
const stat = fs.statSync(filePath);
|
|
52
|
+
|
|
53
|
+
if (stat.isDirectory()) continue;
|
|
54
|
+
|
|
55
|
+
for (const pattern of rootPatterns) {
|
|
56
|
+
if (matchesPattern(file, pattern)) {
|
|
57
|
+
if (deleteFile(filePath)) {
|
|
58
|
+
console.log(` ✓ Deleted: ${file}`);
|
|
59
|
+
deletedCount++;
|
|
60
|
+
}
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
console.log(`\n📁 Cleaned ${deletedCount} files from root directory`);
|
|
67
|
+
|
|
68
|
+
// Clean Rust build artifacts
|
|
69
|
+
console.log('\n🦀 Cleaning Rust build artifacts...');
|
|
70
|
+
const nativeDir = path.join(rootDir, 'msger-native');
|
|
71
|
+
|
|
72
|
+
if (fs.existsSync(nativeDir)) {
|
|
73
|
+
try {
|
|
74
|
+
execSync('cargo clean', {
|
|
75
|
+
cwd: nativeDir,
|
|
76
|
+
stdio: 'inherit'
|
|
77
|
+
});
|
|
78
|
+
console.log(' ✓ Rust artifacts cleaned');
|
|
79
|
+
} catch (error) {
|
|
80
|
+
console.error(' ✗ Failed to run cargo clean');
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
console.log(' ⚠️ msger-native directory not found');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
console.log('\n✨ Clean complete!\n');
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Post-install script to set executable permissions on Linux binaries
|
|
4
|
+
* and check for required system dependencies
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
import { execSync } from 'child_process';
|
|
11
|
+
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = path.dirname(__filename);
|
|
14
|
+
|
|
15
|
+
function detectDistro(): string {
|
|
16
|
+
if (!fs.existsSync('/etc/os-release')) {
|
|
17
|
+
return 'unknown';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const osRelease = fs.readFileSync('/etc/os-release', 'utf8').toLowerCase();
|
|
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: string): void {
|
|
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: string): void {
|
|
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 {
|
|
68
|
+
// ldd command failed or not installed - skip check
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function setExecutePermissions(binaryPath: string, binaryName: string): void {
|
|
73
|
+
try {
|
|
74
|
+
fs.chmodSync(binaryPath, 0o755);
|
|
75
|
+
console.log(`✅ Set execute permissions on ${binaryName} binary`);
|
|
76
|
+
} catch (error: any) {
|
|
77
|
+
console.warn(`⚠️ Could not set execute permission on ${binaryPath}: ${error.message}`);
|
|
78
|
+
console.warn(` You may need to run: sudo chmod +x ${binaryPath}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function main(): void {
|
|
83
|
+
// Windows doesn't need executable permissions
|
|
84
|
+
if (process.platform === 'win32') {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Determine binary name based on architecture
|
|
89
|
+
const arch = process.arch;
|
|
90
|
+
const binaryName = arch === 'arm64' ? 'msgernative-linux-aarch64' : 'msgernative';
|
|
91
|
+
const binaryPath = path.join(__dirname, '..', 'bin', binaryName);
|
|
92
|
+
|
|
93
|
+
// Binary doesn't exist - warn user
|
|
94
|
+
if (!fs.existsSync(binaryPath)) {
|
|
95
|
+
console.warn(`⚠️ Binary not found at ${binaryPath}`);
|
|
96
|
+
console.warn(` The msger package may not support your platform (${process.platform} ${arch})`);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Set executable permissions
|
|
101
|
+
setExecutePermissions(binaryPath, binaryName);
|
|
102
|
+
|
|
103
|
+
// Check for required system libraries
|
|
104
|
+
checkSystemDependencies(binaryPath);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
main();
|
|
@@ -4,10 +4,7 @@
|
|
|
4
4
|
"description": "Rust binary for msger - native message box implementation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"build": "node
|
|
8
|
-
"build:wsl": "node build-wsl.ts",
|
|
9
|
-
"build:arm64": "node build-arm64.ts",
|
|
10
|
-
"build:pi": "node build-pi.ts"
|
|
7
|
+
"build": "node builder/builder.ts"
|
|
11
8
|
},
|
|
12
9
|
"keywords": [
|
|
13
10
|
"message-box",
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Raspberry Pi Build Utilities
|
|
2
|
+
|
|
3
|
+
Utilities for building msger-native on Raspberry Pi (ARM64/aarch64).
|
|
4
|
+
|
|
5
|
+
## Files
|
|
6
|
+
|
|
7
|
+
### build-on-pi.sh
|
|
8
|
+
Shell script to run on the Raspberry Pi itself to build the native binary.
|
|
9
|
+
|
|
10
|
+
### build-pi.ps1
|
|
11
|
+
PowerShell script to build remotely on Pi via SSH (called from Windows).
|
|
12
|
+
|
|
13
|
+
### pi-diagnostics.ts
|
|
14
|
+
TypeScript diagnostic script to check Pi build environment and dependencies.
|
|
15
|
+
|
|
16
|
+
### setup-arm64-toolchain.sh
|
|
17
|
+
Sets up ARM64 cross-compilation toolchain on WSL for local ARM64 builds.
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
The main builder (`../builder/builder.ts`) handles Pi builds automatically when enabled in `build-config.json`. These utilities are for manual builds or troubleshooting.
|
|
22
|
+
|
|
23
|
+
### Remote build on Pi (from Windows):
|
|
24
|
+
```powershell
|
|
25
|
+
.\build-pi.ps1
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Direct build on Pi:
|
|
29
|
+
```bash
|
|
30
|
+
ssh pi4c
|
|
31
|
+
cd /home/pi/dev/msger/msger-native
|
|
32
|
+
./pibuild/build-on-pi.sh
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Setup ARM64 cross-compilation:
|
|
36
|
+
```bash
|
|
37
|
+
wsl bash pibuild/setup-arm64-toolchain.sh
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Output
|
|
41
|
+
|
|
42
|
+
Pi builds produce: `bin/msgernative-linux-aarch64`
|
package/msger-native/src/main.rs
CHANGED
|
@@ -170,15 +170,16 @@ fn default_title() -> String {
|
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
fn get_package_version() -> Option<String> {
|
|
173
|
-
// Read
|
|
173
|
+
// Read version from parent package.json (not msger-native/package.json)
|
|
174
174
|
let exe_path = std::env::current_exe().ok()?;
|
|
175
175
|
let exe_dir = exe_path.parent()?;
|
|
176
176
|
|
|
177
177
|
// Try multiple possible locations for package.json
|
|
178
|
+
// NOTE: Check ../../package.json FIRST to skip msger-native/package.json
|
|
178
179
|
let possible_paths = vec![
|
|
179
|
-
exe_dir.join("
|
|
180
|
-
exe_dir.join("
|
|
181
|
-
exe_dir.join("
|
|
180
|
+
exe_dir.join("../../package.json"), // Development: bin/../../package.json (parent of msger-native)
|
|
181
|
+
exe_dir.join("../../../package.json"), // Installed globally: lib/node_modules/@bobfrankston/msger/package.json
|
|
182
|
+
exe_dir.join("../../../../package.json"), // Deeply nested node_modules
|
|
182
183
|
];
|
|
183
184
|
|
|
184
185
|
for path in possible_paths {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/msger",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.130",
|
|
4
4
|
"description": "Fast, lightweight, cross-platform message box - Rust-powered alternative to msgview",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.js",
|
|
@@ -16,19 +16,14 @@
|
|
|
16
16
|
"scripts": {
|
|
17
17
|
"build": "npm run build:ts && npm run build:native",
|
|
18
18
|
"build:native": "cd msger-native && npm run build",
|
|
19
|
-
"build:native:wsl": "cd msger-native && npm run build:wsl",
|
|
20
|
-
"build:native:arm64": "cd msger-native && npm run build:arm64",
|
|
21
|
-
"build:native:pi": "cd msger-native && npm run build:pi",
|
|
22
|
-
"build:native:all": "npm run build:native && npm run build:native:wsl && (npm run build:native:pi || true)",
|
|
23
|
-
"setup:arm64": "wsl bash msger-native/setup-arm64-toolchain.sh",
|
|
24
19
|
"build:ts": "tsc",
|
|
25
20
|
"watch": "tsc -w",
|
|
26
|
-
"clean": "
|
|
21
|
+
"clean": "node msger-native/builder/clean.ts",
|
|
27
22
|
"test": "node test.js",
|
|
28
|
-
"postinstall": "node postinstall.
|
|
29
|
-
"prepublishOnly": "npm run build:ts && npm run build:native
|
|
23
|
+
"postinstall": "node msger-native/builder/postinstall.ts",
|
|
24
|
+
"prepublishOnly": "npm run build:ts && npm run build:native",
|
|
30
25
|
"prerelease:local": "git add -A && (git diff-index --quiet HEAD || git commit -m \"Pre-release commit\")",
|
|
31
|
-
"preversion": "npm run build:ts && npm run build:native
|
|
26
|
+
"preversion": "npm run build:ts && npm run build:native && git add -A",
|
|
32
27
|
"postversion": "git push && git push --tags",
|
|
33
28
|
"release": "npm run prerelease:local && npm version patch && npm publish",
|
|
34
29
|
"installer": "npm run release && npm cache clean --force && npm install -g @bobfrankston/msger@latest && wsl npm cache clean --force && wsl npm install -g @bobfrankston/msger@latest"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|