2022-10-07 05:41:04 +03:00
import path from 'node:path'
import fs from 'node:fs'
import { command } from 'execa'
import archiver from 'archiver'
import prettyBytes from 'pretty-bytes'
2022-10-07 06:53:43 +03:00
import {
PYTHON _BRIDGE _SRC _PATH ,
TCP _SERVER _SRC _PATH ,
BINARIES _FOLDER _NAME ,
2023-04-30 19:36:03 +03:00
NODEJS _BRIDGE _DIST _PATH ,
2022-10-07 06:53:43 +03:00
PYTHON _BRIDGE _DIST _PATH ,
TCP _SERVER _DIST _PATH ,
2023-04-30 19:36:03 +03:00
NODEJS _BRIDGE _BIN _NAME ,
2022-10-07 06:53:43 +03:00
PYTHON _BRIDGE _BIN _NAME ,
2023-04-30 19:36:03 +03:00
TCP _SERVER _BIN _NAME ,
NODEJS _BRIDGE _ROOT _PATH
2022-10-07 06:53:43 +03:00
} from '@/constants'
2022-11-20 18:21:06 +03:00
import { OSTypes } from '@/types'
2022-10-07 05:41:04 +03:00
import { LogHelper } from '@/helpers/log-helper'
import { LoaderHelper } from '@/helpers/loader-helper'
2022-11-20 18:21:06 +03:00
import { SystemHelper } from '@/helpers/system-helper'
2022-10-07 05:41:04 +03:00
/ * *
* Build binaries for the given OS according to the given build target
* 1. Get the correct OS platform and CPU architecture
* 2. If Linux , install the required dependencies
* 3. Build the given build target
* 4. Pack the distribution entities into a ZIP file
* /
const BUILD _TARGETS = new Map ( )
2023-04-30 19:36:03 +03:00
BUILD _TARGETS . set ( 'nodejs-bridge' , {
name : 'Node.js bridge' ,
needsPythonEnv : false ,
distPath : NODEJS _BRIDGE _DIST _PATH ,
archiveName : ` ${ NODEJS _BRIDGE _BIN _NAME . split ( '.' ) [ 0 ] } .zip `
} )
2022-10-07 05:41:04 +03:00
BUILD _TARGETS . set ( 'python-bridge' , {
name : 'Python bridge' ,
2023-04-30 19:36:03 +03:00
needsPythonEnv : true ,
2022-10-07 05:41:04 +03:00
pipfilePath : path . join ( PYTHON _BRIDGE _SRC _PATH , 'Pipfile' ) ,
setupFilePath : path . join ( PYTHON _BRIDGE _SRC _PATH , 'setup.py' ) ,
2022-10-07 06:53:43 +03:00
distPath : PYTHON _BRIDGE _DIST _PATH ,
2022-10-07 07:07:48 +03:00
archiveName : ` ${ PYTHON _BRIDGE _BIN _NAME } - ${ BINARIES _FOLDER _NAME } .zip ` ,
dotVenvPath : path . join ( PYTHON _BRIDGE _SRC _PATH , '.venv' )
2022-10-07 05:41:04 +03:00
} )
BUILD _TARGETS . set ( 'tcp-server' , {
name : 'TCP server' ,
2023-04-30 19:36:03 +03:00
needsPythonEnv : true ,
2022-10-07 05:41:04 +03:00
pipfilePath : path . join ( TCP _SERVER _SRC _PATH , 'Pipfile' ) ,
setupFilePath : path . join ( TCP _SERVER _SRC _PATH , 'setup.py' ) ,
2022-10-07 06:53:43 +03:00
distPath : TCP _SERVER _DIST _PATH ,
2022-10-07 07:07:48 +03:00
archiveName : ` ${ TCP _SERVER _BIN _NAME } - ${ BINARIES _FOLDER _NAME } .zip ` ,
dotVenvPath : path . join ( TCP _SERVER _SRC _PATH , '.venv' )
2022-10-07 05:41:04 +03:00
} )
; ( async ( ) => {
LoaderHelper . start ( )
const { argv } = process
const givenBuildTarget = argv [ 2 ] . toLowerCase ( )
if ( ! BUILD _TARGETS . has ( givenBuildTarget ) ) {
LogHelper . error (
` Invalid build target: ${ givenBuildTarget } . Valid targets are: ${ Array . from (
BUILD _TARGETS . keys ( )
) . join ( ', ' ) } `
)
process . exit ( 1 )
}
const {
name : buildTarget ,
2023-04-30 19:36:03 +03:00
needsPythonEnv ,
2022-10-07 05:41:04 +03:00
pipfilePath ,
setupFilePath ,
distPath ,
2022-10-07 07:07:48 +03:00
archiveName ,
dotVenvPath
2022-10-07 05:41:04 +03:00
} = BUILD _TARGETS . get ( givenBuildTarget )
2023-04-30 19:36:03 +03:00
const buildPath = needsPythonEnv
? path . join ( distPath , BINARIES _FOLDER _NAME )
2023-05-01 17:48:31 +03:00
: path . join ( distPath , 'bin' )
2022-10-07 05:41:04 +03:00
2022-11-20 18:21:06 +03:00
const { type : osType } = SystemHelper . getInformation ( )
2022-10-07 05:41:04 +03:00
/ * *
* Install requirements
* /
try {
2023-04-30 19:36:03 +03:00
if ( needsPythonEnv && osType === OSTypes . Linux ) {
2022-10-07 05:41:04 +03:00
LogHelper . info ( 'Checking whether the "patchelf" utility can be found...' )
await command ( 'patchelf --version' , { shell : true } )
LogHelper . success ( 'The "patchelf" utility has been found' )
}
} catch ( e ) {
const installPatchelfCommand = 'sudo apt install patchelf'
LogHelper . error (
` The "patchelf" utility is not installed. Please run the following command: " ${ installPatchelfCommand } " or install it via a packages manager supported by your Linux distribution such as DNF, YUM, etc. Then try again `
)
process . exit ( 1 )
}
2023-04-30 19:36:03 +03:00
LogHelper . info ( ` Building the ${ buildTarget } ... ` )
if ( needsPythonEnv ) {
/ * *
* Build for binaries requiring a Python environment
* /
try {
// Required environment variables to set up
process . env . PIPENV _PIPFILE = pipfilePath
process . env . PIPENV _VENV _IN _PROJECT = true
await command (
` pipenv run python ${ setupFilePath } build --build-exe ${ buildPath } ` ,
{
shell : true ,
stdio : 'inherit'
}
)
LogHelper . success ( ` The ${ buildTarget } has been built ` )
} catch ( e ) {
LogHelper . error (
` An error occurred while building the ${ buildTarget } . Try to delete the ${ dotVenvPath } folder, run the setup command then build again: ${ e } `
)
process . exit ( 1 )
}
} else {
/ * *
* Build for binaries not requiring a Python environment
* /
try {
2023-05-06 16:38:49 +03:00
const distBinPath = path . join ( NODEJS _BRIDGE _DIST _PATH , 'bin' )
2023-05-08 16:24:26 +03:00
const distMainFilePath = path . join ( distBinPath , 'index.js' )
2023-04-30 19:36:03 +03:00
const distRenamedMainFilePath = path . join (
2023-05-06 16:38:49 +03:00
distBinPath ,
2023-04-30 19:36:03 +03:00
NODEJS _BRIDGE _BIN _NAME
)
2023-05-03 04:27:23 +03:00
await fs . promises . rm ( buildPath , { recursive : true , force : true } )
2023-05-06 16:38:49 +03:00
const inputMainFilePath = path . join (
NODEJS _BRIDGE _ROOT _PATH ,
'src' ,
'main.ts'
)
await command ( ` ncc build ${ inputMainFilePath } --out ${ distBinPath } ` , {
2022-10-07 05:41:04 +03:00
shell : true ,
stdio : 'inherit'
2023-04-30 19:36:03 +03:00
} )
2022-10-07 05:41:04 +03:00
2023-04-30 19:36:03 +03:00
await fs . promises . rename ( distMainFilePath , distRenamedMainFilePath )
LogHelper . success ( ` The ${ buildTarget } has been built ` )
} catch ( e ) {
LogHelper . error (
` An error occurred while building the ${ buildTarget } : ${ e } `
)
process . exit ( 1 )
}
2022-10-07 05:41:04 +03:00
}
/ * *
* Pack distribution entities into a ZIP archive
* /
const archivePath = path . join ( distPath , archiveName )
LogHelper . info ( ` Packing to ${ archivePath } ... ` )
const output = fs . createWriteStream ( archivePath )
const archive = archiver ( 'zip' )
output . on ( 'close' , ( ) => {
const size = prettyBytes ( archive . pointer ( ) )
LogHelper . info ( ` Total archive size: ${ size } ` )
LogHelper . success ( ` ${ buildTarget } has been packed to ${ archivePath } ` )
process . exit ( 0 )
} )
archive . on ( 'error' , ( err ) => {
LogHelper . error (
` An error occurred while packing the ${ buildTarget } : ${ err } `
)
} )
archive . pipe ( output )
2023-04-30 19:36:03 +03:00
if ( needsPythonEnv ) {
archive . directory ( buildPath , BINARIES _FOLDER _NAME )
} else {
2023-05-01 17:48:31 +03:00
archive . directory ( buildPath , 'bin' )
2023-04-30 19:36:03 +03:00
}
2022-10-07 05:41:04 +03:00
await archive . finalize ( )
} ) ( )