22import { WalletPluginPrivateKey } from '@wharfkit/wallet-plugin-privatekey'
33import { executeCommand , getDevKeys , getPlatform } from './utils'
44import { NonInteractiveConsoleUI } from '../../utils/wharfkit-ui'
5+ import * as os from 'os'
6+ import * as path from 'path'
7+ import * as fs from 'fs'
8+
9+ const LEAP_VERSION = 'v5.0.3'
10+ const LEAP_REPO = 'https://github.com/AntelopeIO/leap'
511
612export interface InstallationStatus {
713 installed : boolean
@@ -14,6 +20,13 @@ export interface InstallationStatus {
1420 }
1521}
1622
23+ /**
24+ * Get the directory where LEAP will be cloned and built
25+ */
26+ function getLeapBuildDir ( ) : string {
27+ return path . join ( os . homedir ( ) , '.wharfkit' , 'leap-build' )
28+ }
29+
1730/**
1831 * Check if LEAP is installed
1932 */
@@ -59,10 +72,162 @@ export async function checkLeapInstallation(): Promise<InstallationStatus> {
5972}
6073
6174/**
62- * Install LEAP on macOS using Homebrew
75+ * Ensure directory exists
76+ */
77+ async function ensureBuildDir ( dir : string ) : Promise < void > {
78+ if ( ! fs . existsSync ( dir ) ) {
79+ await fs . promises . mkdir ( dir , { recursive : true } )
80+ }
81+ }
82+
83+ /**
84+ * Clone and checkout LEAP repository
85+ */
86+ async function cloneLeapRepo ( buildDir : string ) : Promise < void > {
87+ const leapDir = path . join ( buildDir , 'leap' )
88+
89+ // Check if already cloned
90+ if ( fs . existsSync ( path . join ( leapDir , '.git' ) ) ) {
91+ console . log ( 'LEAP repository already cloned, updating...' )
92+ try {
93+ await executeCommand ( `cd ${ leapDir } && git fetch --all --tags` )
94+ await executeCommand ( `cd ${ leapDir } && git checkout ${ LEAP_VERSION } ` )
95+ await executeCommand ( `cd ${ leapDir } && git pull || true` )
96+ await executeCommand ( `cd ${ leapDir } && git submodule update --init --recursive` )
97+ } catch ( error : unknown ) {
98+ const message = error instanceof Error ? error . message : String ( error )
99+ throw new Error ( `Failed to update LEAP repository: ${ message } ` )
100+ }
101+ } else {
102+ console . log ( 'Cloning LEAP repository...' )
103+ try {
104+ await executeCommand ( `git clone --recursive ${ LEAP_REPO } ${ leapDir } ` )
105+ await executeCommand ( `cd ${ leapDir } && git fetch --all --tags` )
106+ await executeCommand ( `cd ${ leapDir } && git checkout ${ LEAP_VERSION } ` )
107+ await executeCommand ( `cd ${ leapDir } && git submodule update --init --recursive` )
108+ } catch ( error : unknown ) {
109+ const message = error instanceof Error ? error . message : String ( error )
110+ throw new Error ( `Failed to clone LEAP repository: ${ message } ` )
111+ }
112+ }
113+ }
114+
115+ /**
116+ * Build LEAP from source
117+ */
118+ async function buildLeap ( buildDir : string , numJobs ?: number ) : Promise < void > {
119+ const leapDir = path . join ( buildDir , 'leap' )
120+ const leapBuildDir = path . join ( leapDir , 'build' )
121+
122+ // Create build directory
123+ await ensureBuildDir ( leapBuildDir )
124+
125+ const jobs = numJobs || Math . max ( 1 , Math . floor ( os . cpus ( ) . length / 2 ) )
126+ console . log ( `Building LEAP with ${ jobs } parallel jobs (this may take a while)...` )
127+
128+ try {
129+ const { os : platform } = getPlatform ( )
130+
131+ if ( platform === 'darwin' ) {
132+ // macOS build - use llvm from Homebrew
133+ const llvmPrefix = await getLlvmPrefix ( )
134+ await executeCommand (
135+ `cd ${ leapBuildDir } && cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=${ llvmPrefix } ..`
136+ )
137+ } else {
138+ // Linux build
139+ await executeCommand (
140+ `cd ${ leapBuildDir } && cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=/usr/lib/llvm-11 ..`
141+ )
142+ }
143+
144+ await executeCommand ( `cd ${ leapBuildDir } && make -j ${ jobs } ` )
145+ } catch ( error : unknown ) {
146+ const message = error instanceof Error ? error . message : String ( error )
147+ throw new Error ( `Failed to build LEAP: ${ message } ` )
148+ }
149+ }
150+
151+ /**
152+ * Get LLVM prefix path on macOS
153+ */
154+ async function getLlvmPrefix ( ) : Promise < string > {
155+ try {
156+ const { stdout} = await executeCommand ( 'brew --prefix llvm@11' )
157+ return stdout . trim ( )
158+ } catch {
159+ // Try llvm without version
160+ try {
161+ const { stdout} = await executeCommand ( 'brew --prefix llvm' )
162+ return stdout . trim ( )
163+ } catch {
164+ return '/usr/local/opt/llvm'
165+ }
166+ }
167+ }
168+
169+ /**
170+ * Install built LEAP binaries
171+ */
172+ async function installBuiltLeap ( buildDir : string ) : Promise < void > {
173+ const leapDir = path . join ( buildDir , 'leap' )
174+ const leapBuildDir = path . join ( leapDir , 'build' )
175+ const binDir = path . join ( leapBuildDir , 'bin' )
176+
177+ console . log ( 'Installing LEAP binaries...' )
178+
179+ const binaries = [ 'nodeos' , 'cleos' , 'keosd' , 'leap-util' ]
180+ const targetDir = '/usr/local/bin'
181+
182+ try {
183+ const { os : platform } = getPlatform ( )
184+
185+ // First, try to copy binaries directly (works if user owns /usr/local/bin)
186+ try {
187+ for ( const binary of binaries ) {
188+ const src = path . join ( binDir , binary )
189+ const dest = path . join ( targetDir , binary )
190+ if ( fs . existsSync ( src ) ) {
191+ await fs . promises . copyFile ( src , dest )
192+ await fs . promises . chmod ( dest , 0o755 )
193+ }
194+ }
195+ console . log ( 'Binaries copied to /usr/local/bin' )
196+ return
197+ } catch {
198+ // Direct copy failed, try sudo methods
199+ console . log ( 'Direct copy failed, trying with elevated permissions...' )
200+ }
201+
202+ if ( platform === 'darwin' ) {
203+ // On macOS, use make install with sudo
204+ await executeCommand ( `cd ${ leapBuildDir } && sudo make install` )
205+ } else {
206+ // On Linux, install the .deb package if available, otherwise make install
207+ try {
208+ const { stdout} = await executeCommand (
209+ `ls ${ leapBuildDir } /leap*.deb 2>/dev/null | head -1`
210+ )
211+ if ( stdout . trim ( ) ) {
212+ await executeCommand ( `sudo apt-get install -y ${ stdout . trim ( ) } ` )
213+ } else {
214+ await executeCommand ( `cd ${ leapBuildDir } && sudo make install` )
215+ }
216+ } catch {
217+ await executeCommand ( `cd ${ leapBuildDir } && sudo make install` )
218+ }
219+ }
220+ } catch ( error : unknown ) {
221+ const message = error instanceof Error ? error . message : String ( error )
222+ throw new Error ( `Failed to install LEAP: ${ message } ` )
223+ }
224+ }
225+
226+ /**
227+ * Install LEAP on macOS by building from source
63228 */
64229async function installLeapMacOS ( ) : Promise < void > {
65- console . log ( 'Installing LEAP on macOS using Homebrew ...' )
230+ console . log ( 'Installing LEAP on macOS by building from source ...' )
66231
67232 // Check if Homebrew is installed
68233 try {
@@ -73,81 +238,137 @@ async function installLeapMacOS(): Promise<void> {
73238 )
74239 }
75240
76- // Tap AntelopeIO
77- console . log ( 'Adding AntelopeIO tap ...' )
241+ // Install build dependencies
242+ console . log ( 'Installing build dependencies ...' )
78243 try {
79- await executeCommand ( 'brew tap antelopeio/leap' )
80- } catch ( error : any ) {
81- throw new Error ( `Failed to add AntelopeIO tap: ${ error . message } ` )
244+ await executeCommand ( 'brew install cmake git llvm@11 gmp curl python3 numpy || true' )
245+ } catch ( error : unknown ) {
246+ const message = error instanceof Error ? error . message : String ( error )
247+ throw new Error ( `Failed to install dependencies: ${ message } ` )
82248 }
83249
84- // Install LEAP
85- console . log ( 'Installing LEAP (this may take a few minutes)...' )
86- try {
87- await executeCommand ( 'brew install leap' )
88- } catch ( error : any ) {
89- throw new Error ( `Failed to install LEAP: ${ error . message } ` )
90- }
250+ const buildDir = getLeapBuildDir ( )
251+ await ensureBuildDir ( buildDir )
252+
253+ // Clone repository
254+ await cloneLeapRepo ( buildDir )
255+
256+ // Build from source
257+ await buildLeap ( buildDir )
258+
259+ // Install
260+ await installBuiltLeap ( buildDir )
91261
92262 console . log ( 'LEAP installed successfully!' )
93263}
94264
95265/**
96- * Install LEAP on Linux using apt
266+ * Get Ubuntu version for determining LLVM version
97267 */
98- async function installLeapLinux ( ) : Promise < void > {
99- console . log ( 'Installing LEAP on Linux using apt...' )
100-
101- // Detect distribution
102- let distro = 'ubuntu'
103- let version = '22.04'
104-
105- try {
106- const { stdout} = await executeCommand ( 'lsb_release -is' )
107- distro = stdout . trim ( ) . toLowerCase ( )
108- } catch {
109- console . log ( 'Could not detect distribution, assuming Ubuntu' )
110- }
111-
268+ async function getUbuntuVersion ( ) : Promise < string > {
112269 try {
113270 const { stdout} = await executeCommand ( 'lsb_release -rs' )
114- version = stdout . trim ( )
271+ return stdout . trim ( )
115272 } catch {
116- console . log ( 'Could not detect version, assuming 22.04')
273+ return ' 22.04'
117274 }
275+ }
118276
119- // Add AntelopeIO repository
120- console . log ( 'Adding AntelopeIO repository...' )
121- try {
122- await executeCommand (
123- 'wget -O - https://apt.antelope.io/repos/antelope.gpg.key | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/antelope.gpg > /dev/null'
124- )
125- await executeCommand (
126- `echo "deb [arch=amd64] https://apt.antelope.io ${ distro } ${ version } " | sudo tee /etc/apt/sources.list.d/antelope.list`
127- )
128- } catch ( error : any ) {
129- throw new Error ( `Failed to add AntelopeIO repository: ${ error . message } ` )
130- }
277+ /**
278+ * Install LEAP on Linux by building from source
279+ */
280+ async function installLeapLinux ( ) : Promise < void > {
281+ console . log ( 'Installing LEAP on Linux by building from source...' )
282+
283+ const ubuntuVersion = await getUbuntuVersion ( )
284+ const majorVersion = parseInt ( ubuntuVersion . split ( '.' ) [ 0 ] , 10 )
131285
132286 // Update package list
133287 console . log ( 'Updating package list...' )
134288 try {
135289 await executeCommand ( 'sudo apt-get update' )
136- } catch ( error : any ) {
137- throw new Error ( `Failed to update package list: ${ error . message } ` )
290+ } catch ( error : unknown ) {
291+ const message = error instanceof Error ? error . message : String ( error )
292+ throw new Error ( `Failed to update package list: ${ message } ` )
138293 }
139294
140- // Install LEAP
141- console . log ( 'Installing LEAP (this may take a few minutes) ...' )
295+ // Install build dependencies
296+ console . log ( 'Installing build dependencies ...' )
142297 try {
143- await executeCommand ( 'sudo apt-get install -y leap' )
144- } catch ( error : any ) {
145- throw new Error ( `Failed to install LEAP: ${ error . message } ` )
298+ await executeCommand ( `sudo apt-get install -y \
299+ build-essential \
300+ cmake \
301+ git \
302+ libcurl4-openssl-dev \
303+ libgmp-dev \
304+ llvm-11-dev \
305+ python3-numpy \
306+ file \
307+ zlib1g-dev` )
308+
309+ // On Ubuntu 20.04, install gcc-10 for C++20 support
310+ if ( majorVersion === 20 ) {
311+ await executeCommand ( 'sudo apt-get install -y g++-10' )
312+ }
313+ } catch ( error : unknown ) {
314+ const message = error instanceof Error ? error . message : String ( error )
315+ throw new Error ( `Failed to install dependencies: ${ message } ` )
146316 }
147317
318+ const buildDir = getLeapBuildDir ( )
319+ await ensureBuildDir ( buildDir )
320+
321+ // Clone repository
322+ await cloneLeapRepo ( buildDir )
323+
324+ // Build from source (with Ubuntu 20.04 specific compiler flags)
325+ await buildLeapLinux ( buildDir , majorVersion )
326+
327+ // Install
328+ await installBuiltLeap ( buildDir )
329+
148330 console . log ( 'LEAP installed successfully!' )
149331}
150332
333+ /**
334+ * Build LEAP on Linux with version-specific settings
335+ */
336+ async function buildLeapLinux ( buildDir : string , ubuntuMajorVersion : number ) : Promise < void > {
337+ const leapDir = path . join ( buildDir , 'leap' )
338+ const leapBuildDir = path . join ( leapDir , 'build' )
339+
340+ // Create build directory
341+ await ensureBuildDir ( leapBuildDir )
342+
343+ const jobs = Math . max ( 1 , Math . floor ( os . cpus ( ) . length / 2 ) )
344+ console . log ( `Building LEAP with ${ jobs } parallel jobs (this may take a while)...` )
345+
346+ try {
347+ if ( ubuntuMajorVersion === 20 ) {
348+ // Ubuntu 20.04 needs gcc-10 specified
349+ await executeCommand (
350+ `cd ${ leapBuildDir } && cmake \
351+ -DCMAKE_C_COMPILER=gcc-10 \
352+ -DCMAKE_CXX_COMPILER=g++-10 \
353+ -DCMAKE_BUILD_TYPE=Release \
354+ -DCMAKE_PREFIX_PATH=/usr/lib/llvm-11 ..`
355+ )
356+ } else {
357+ // Ubuntu 22.04+ has gcc-11 by default
358+ await executeCommand (
359+ `cd ${ leapBuildDir } && cmake \
360+ -DCMAKE_BUILD_TYPE=Release \
361+ -DCMAKE_PREFIX_PATH=/usr/lib/llvm-11 ..`
362+ )
363+ }
364+
365+ await executeCommand ( `cd ${ leapBuildDir } && make -j ${ jobs } ` )
366+ } catch ( error : unknown ) {
367+ const message = error instanceof Error ? error . message : String ( error )
368+ throw new Error ( `Failed to build LEAP: ${ message } ` )
369+ }
370+ }
371+
151372/**
152373 * Install LEAP based on platform
153374 */
0 commit comments