Skip to content

Commit ff904ea

Browse files
authored
fix(linux): Improve dependency checks (#195)
* Add some package management logic * Improve wording and skip install if not an apt-based distro * Use dependency package tools for esp32 setup * Change device alias back to lin * Use dependency detection for system packages on esp8266 * Fix imports after the files have been renamed * Fix format and lint errors * Use === for comparison * Fix the pylib dependency check * Rebase and clean up merge conflicts * Ensure that Linux setup and update works with correct filenames
1 parent 4dbbe16 commit ff904ea

File tree

8 files changed

+131
-27
lines changed

8 files changed

+131
-27
lines changed

src/toolbox/prompt/devices.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ export const DEVICE_ALIAS: Record<Device | 'esp', Device> = Object.freeze({
88
windows_nt: 'windows',
99
windows: 'windows',
1010
win: 'windows',
11-
linux: 'linux',
12-
lin: 'linux',
11+
linux: 'lin',
12+
lin: 'lin',
1313
esp32: 'esp32',
1414
wasm: 'wasm',
1515
pico: 'pico',

src/toolbox/setup/esp32/linux.ts

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,31 @@
11
import type { GluegunPrint } from 'gluegun'
2-
import { execWithSudo } from '../../system/exec'
2+
import type { Dependency } from '../../system/types'
3+
import { findMissingDependencies, installPackages } from '../../system/packages'
34

4-
// apt-get install git wget flex bison gperf python-is-python3 python3-pip python3-serial python-setuptools cmake ninja-build ccache libffi-dev libssl-dev dfu-util
55
export async function installDeps(
66
spinner: ReturnType<GluegunPrint['spin']>,
77
): Promise<void> {
8-
await execWithSudo(
9-
'apt-get install --yes git wget flex bison gperf python-is-python3 python3-pip python3-serial python3-setuptools cmake ninja-build ccache libffi-dev libssl-dev dfu-util',
10-
{ stdout: process.stdout },
11-
)
8+
const dependencies: Dependency[] = [
9+
{ name: 'bison', packageName: 'bison', type: 'binary' },
10+
{ name: 'ccache', packageName: 'ccache', type: 'binary' },
11+
{ name: 'cmake', packageName: 'cmake', type: 'binary' },
12+
{ name: 'dfu-util', packageName: 'dfu-util', type: 'binary' },
13+
{ name: 'flex', packageName: 'flex', type: 'binary' },
14+
{ name: 'git', packageName: 'git', type: 'binary' },
15+
{ name: 'gperf', packageName: 'gperf', type: 'binary' },
16+
{ name: 'libffi', packageName: 'libffi-dev', type: 'library' },
17+
{ name: 'libssl', packageName: 'libssl-dev', type: 'library' },
18+
{ name: 'ninja', packageName: 'ninja-build', type: 'binary' },
19+
{ name: 'pip', packageName: 'python-pip', type: 'binary' },
20+
{ name: 'pyserial-miniterm', packageName: 'python3-serial', type: 'binary' },
21+
{ name: 'python', packageName: 'python-is-python3', type: 'binary' },
22+
{ name: 'setuptools', packageName: 'python3-setuptools', type: 'pylib' },
23+
{ name: 'wget', packageName: 'wget', type: 'binary' },
24+
]
25+
26+
const missingDependencies = await findMissingDependencies(dependencies)
27+
if (missingDependencies.length !== 0) {
28+
await installPackages(missingDependencies)
29+
}
1230
spinner.succeed()
1331
}

src/toolbox/setup/esp8266/linux.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
import type { GluegunPrint } from 'gluegun'
2-
import { execWithSudo } from '../../system/exec'
2+
import type { Dependency } from '../../system/types'
3+
import { findMissingDependencies, installPackages } from '../../system/packages'
34

45
export async function installDeps(
56
spinner: ReturnType<GluegunPrint['spin']>,
67
): Promise<void> {
78
spinner.start('Installing python deps with apt-get')
8-
await execWithSudo(
9-
'apt-get install --yes python-is-python3 python3-pip python3-serial',
10-
{ stdout: process.stdout },
11-
)
9+
const dependencies: Dependency[] = [
10+
{ name: 'pip', packageName: 'python-pip', type: 'binary' },
11+
{ name: 'pyserial-miniterm', packageName: 'python3-serial', type: 'binary' },
12+
{ name: 'python', packageName: 'python-is-python3', type: 'binary' },
13+
]
14+
15+
const missingDependencies = await findMissingDependencies(dependencies)
16+
if (missingDependencies.length !== 0) {
17+
await installPackages(missingDependencies)
18+
}
1219
spinner.succeed()
1320
}
Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import {
1010
} from './constants'
1111
import upsert from '../patching/upsert'
1212
import { execWithSudo } from '../system/exec'
13+
import { findMissingDependencies, installPackages } from '../system/packages'
14+
import type { Dependency } from '../system/types'
1315
import type { PlatformSetupArgs } from './types'
1416
import { fetchRelease, downloadReleaseTools } from './moddable'
1517

@@ -48,24 +50,29 @@ export default async function ({
4850
const spinner = print.spin()
4951
spinner.start('Beginning setup...')
5052

51-
// 0. clone moddable repo into ./local/share directory if it does not exist yet
5253
filesystem.dir(INSTALL_DIR)
5354

54-
await upsert(EXPORTS_FILE_PATH, `# Generated by xs-dev CLI`)
55+
// 0. check for the required build tools and libraries
56+
const dependencies: Dependency[] = [
57+
{ name: 'bison', packageName: 'bison', type: 'binary' },
58+
{ name: 'flex', packageName: 'flex', type: 'binary' },
59+
{ name: 'gcc', packageName: 'gcc', type: 'binary' },
60+
{ name: 'git', packageName: 'git', type: 'binary' },
61+
{ name: 'gperf', packageName: 'gperf', type: 'binary' },
62+
{ name: 'make', packageName: 'make', type: 'binary' },
63+
{ name: 'wget', packageName: 'wget', type: 'binary' },
64+
{ name: 'ncurses', packageName: 'libncurses-dev', type: 'library' },
65+
{ name: 'gtk+-3.0', packageName: 'libgtk-3-dev', type: 'library' }
66+
]
67+
68+
spinner.start('Checking for missing dependencies...')
69+
const missingDependencies = await findMissingDependencies(dependencies)
5570

5671
// 1. Install or update the packages required to compile:
57-
spinner.start('Installing dependencies...')
58-
await execWithSudo(
59-
'apt-get install --yes gcc git wget make libncurses-dev flex bison gperf',
60-
{ stdout: process.stdout },
61-
)
62-
spinner.succeed()
63-
64-
// 2. Install the development version of the GTK+ 3 library
65-
spinner.start('Installing GTK+ 3...')
66-
await execWithSudo('apt-get --yes install libgtk-3-dev', {
67-
stdout: process.stdout,
68-
})
72+
spinner.start('Attempting to install dependencies...')
73+
if (missingDependencies.length !== 0) {
74+
await installPackages(missingDependencies)
75+
}
6976
spinner.succeed()
7077

7178
// 3. Download the Moddable repository, or use the git command line tool as follows:
@@ -140,6 +147,7 @@ export default async function ({
140147
process.env.MODDABLE = INSTALL_PATH
141148
process.env.PATH = `${String(process.env.PATH)}:${BIN_PATH}`
142149

150+
await upsert(EXPORTS_FILE_PATH, `# Generated by xs-dev CLI`)
143151
await upsert(EXPORTS_FILE_PATH, `export MODDABLE=${process.env.MODDABLE}`)
144152
await upsert(EXPORTS_FILE_PATH, `export PATH="${BIN_PATH}:$PATH"`)
145153

src/toolbox/system/exec.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,16 @@ export async function execWithSudo(
3737
await system.exec(`sudo --askpass --preserve-env ${command}`, options)
3838
}
3939

40+
/**
41+
* Use Policykit pkexec to run the command as an admin user
42+
*/
43+
export async function pkexec(
44+
command: string,
45+
options: Record<string, unknown> = {},
46+
): Promise <void> {
47+
await system.exec(`pkexec ${command}`, options)
48+
}
49+
4050
/**
4151
* Utility for updating in-memory process.env after running a command
4252
*/

src/toolbox/system/packages.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import type { Dependency } from './types'
2+
import { pkexec } from '../system/exec'
3+
import { print, system } from 'gluegun'
4+
5+
/**
6+
* Check if the list of dependencies are installed on the system.
7+
**/
8+
export async function findMissingDependencies(dependencies: Dependency[]): Promise<Dependency[]> {
9+
const missingDependencies: Dependency[] = []
10+
11+
for (const dep of dependencies) {
12+
if (dep.type === 'binary') {
13+
if (system.which(dep.name) === null) {
14+
missingDependencies.push(dep)
15+
}
16+
}
17+
if (dep.type === 'library') {
18+
try {
19+
await system.run(`pkg-config --exists ${dep.name}`)
20+
} catch (error) {
21+
missingDependencies.push(dep)
22+
}
23+
}
24+
if (dep.type === 'pylib') {
25+
try {
26+
await system.run(`pip3 show ${dep.name}`)
27+
} catch (error) {
28+
missingDependencies.push(dep)
29+
}
30+
}
31+
}
32+
33+
return missingDependencies
34+
}
35+
36+
/**
37+
* Attempt to install packages on the linux platform.
38+
**/
39+
export async function installPackages(packages: Dependency[]): Promise<void> {
40+
const packageManager = system.which('apt')
41+
42+
if (packageManager !== null && packageManager !== undefined) {
43+
await pkexec(
44+
`${packageManager} install --yes ${packages.map((p) => p.packageName).join(' ')}`,
45+
{ stdout: process.stdout },
46+
)
47+
} else {
48+
print.warning(
49+
'xs-dev attempted to install dependencies, but does not yet support your package manager',
50+
)
51+
print.warning(
52+
`Please install these dependencies before running this command again: ${packages.map((p) => p.packageName).join(', ')}`,
53+
)
54+
process.exit(1)
55+
}
56+
}

src/toolbox/system/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export interface Dependency {
2+
name: string
3+
packageName: string
4+
type: 'binary' | 'library' | 'pylib'
5+
}

0 commit comments

Comments
 (0)