Skip to content

Commit b51ab26

Browse files
committed
harmony run
1 parent d1b82fd commit b51ab26

File tree

17 files changed

+397
-104
lines changed

17 files changed

+397
-104
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
"proxy-agent": "^6.5.0",
5454
"send": "^1.1.0",
5555
"validate-npm-package-name": "^6.0.0",
56+
"which": "^5.0.0",
5657
"ws": "^8.18.0"
5758
},
5859
"devDependencies": {
@@ -61,6 +62,7 @@
6162
"@types/node": "^22.10.2",
6263
"@types/send": "^0.17.4",
6364
"@types/validate-npm-package-name": "^4.0.2",
65+
"@types/which": "^3.0.4",
6466
"@types/ws": "^8.5.13",
6567
"@wtto00/biome-config": "^0.0.1",
6668
"globstar": "^1.0.0",

pnpm-lock.yaml

Lines changed: 26 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/android/build.ts

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { resolve } from 'node:path'
22
import Android from '@wtto00/android-tools'
3-
import { type ResultPromise, execa } from 'execa'
3+
import { execa } from 'execa'
44
import ora from 'ora'
55
import type { BuildOptions } from '../build.js'
66
import { App } from '../utils/app.js'
@@ -11,17 +11,10 @@ import { cleanAndroid } from './clean.js'
1111
import { getGradleExePath } from './gradle.js'
1212
import { prepare } from './prepare.js'
1313

14-
let logcatProcess: ResultPromise<{
15-
stdout: 'inherit'
16-
stderr: 'ignore'
17-
buffer: false
18-
reject: false
19-
}>
14+
let logcatProcessController = new AbortController()
2015

2116
function killLogcat() {
22-
if (logcatProcess && !logcatProcess.killed) {
23-
logcatProcess.kill()
24-
}
17+
logcatProcessController.abort()
2518
}
2619

2720
export interface AndroidBuildOptions {
@@ -114,11 +107,13 @@ export async function buildAndroid(options: BuildOptions, runOptions?: AndroidBu
114107
}
115108

116109
if (!runOptions?.isBuild) {
117-
logcatProcess = execa({
110+
logcatProcessController = new AbortController()
111+
await execa({
118112
stdout: 'inherit',
119113
stderr: 'ignore',
120114
buffer: false,
121115
reject: false,
116+
cancelSignal: logcatProcessController.signal,
122117
})`${android.adbBin} -s ${deviceName} logcat console:D jsLog:D weex:E aaa:D *:S -v raw -v color -v time`
123118
}
124119
} catch (error) {

src/build.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,13 @@ import { App } from './utils/app.js'
44
import Log from './utils/log.js'
55
import { isInstalled } from './utils/package.js'
66

7+
export type AndroidBundle = 'apk' | 'aab' | 'wgt'
8+
9+
export type HarmonyBundle = 'hap' | 'har' | 'hsp' | 'app'
10+
711
export interface BuildOptions extends CommonOptions, AndroidCommonOptoins {
812
/** Android打包产物类型 */
9-
bundle?: 'apk' | 'aab' | 'wgt'
13+
bundle?: AndroidBundle | HarmonyBundle
1014
}
1115

1216
export async function build(platform: PLATFORM, options: BuildOptions) {

src/harmony/build.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,27 @@
1-
import type { RunOptions } from '../run.js'
1+
import type { BuildOptions, HarmonyBundle } from '../build.js'
2+
import Log from '../utils/log.js'
3+
import { clean } from './clean.js'
4+
import { prepare } from './prepare.js'
5+
import { hvigorwBuild, hvigorwSync } from './tools/hvigorw.js'
6+
import { ohpmInstall } from './tools/ohpm.js'
27

3-
export async function buildHarmony(_options: RunOptions) {}
8+
export interface HarmonyBuildOptions {
9+
isBuild?: boolean
10+
}
11+
12+
export async function buildHarmony(options: BuildOptions, runOptions?: HarmonyBuildOptions) {
13+
Log.info('执行 clean')
14+
await clean()
15+
16+
Log.info('执行 prepare')
17+
await prepare({ isBuild: runOptions?.isBuild })
18+
19+
Log.info('安装 Harmony 依赖')
20+
await ohpmInstall()
21+
22+
Log.info('打包初始化')
23+
await hvigorwSync()
24+
25+
Log.info('开始打包')
26+
await hvigorwBuild(options.bundle as HarmonyBundle)
27+
}

src/harmony/check.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,18 @@ export function checkConfig() {
1414

1515
if (!bundleName) {
1616
failed = true
17-
Log.warn('请在文件manifest.json中配置应用包名: app-harmony.distribute.bundleName')
17+
Log.error('请在文件manifest.json中配置应用包名: app-harmony.distribute.bundleName')
1818
}
1919

2020
// versionName
2121
if (!manifest.versionName) {
2222
failed = true
23-
Log.warn('请在文件manifest.json中配置应用版本号: versionName')
23+
Log.error('请在文件manifest.json中配置应用版本号: versionName')
2424
}
2525
// versionCode
26-
if (!manifest.versionCode) {
26+
if (!manifest.versionCode || Number.isNaN(Number(manifest.versionCode))) {
2727
failed = true
28-
Log.warn('请在文件manifest.json中配置应用版本码: versionCode')
28+
Log.error('请在文件manifest.json中配置应用版本码(数字类型): versionCode')
2929
}
3030

3131
if (!icons?.foreground) {
@@ -47,7 +47,7 @@ export function checkConfig() {
4747

4848
if (modules?.['uni-map']?.tencent && !modules['uni-map'].tencent.key) {
4949
failed = true
50-
Log.warn(
50+
Log.error(
5151
'您配置了腾讯地图,请在文件manifest.json中配置腾讯地图的key: app-harmony.distribute.modules.uni-map.tencent.key',
5252
)
5353
}

src/harmony/clean.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { join } from 'node:path'
2+
import { cleanFiles } from '../utils/file.js'
3+
import { HarmonyDir, TemplateDir } from '../utils/path.js'
4+
import { hvigorwClean } from './tools/hvigorw.js'
5+
6+
export async function clean() {
7+
await cleanFiles(join(TemplateDir, 'harmony'), HarmonyDir, ['oh_modules', '.hvigor', '.idea', 'build'])
8+
}
9+
10+
export async function cleanHvigorw() {
11+
await hvigorwClean()
12+
}
13+
14+
export async function reset() {
15+
await cleanFiles(join(TemplateDir, 'harmony'), HarmonyDir)
16+
}

src/harmony/prepare.ts

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,62 @@
1-
import { cp, rm } from 'node:fs/promises'
1+
import { cp, readdir, rm } from 'node:fs/promises'
22
import { join } from 'node:path'
33
import type { PackageJson } from 'pkg-types'
44
import { App } from '../utils/app.js'
5-
import { editJsonFile, exists } from '../utils/file.js'
5+
import { editJsonFile, exists, readJsonFile } from '../utils/file.js'
66
import Log from '../utils/log.js'
7+
import { getPackageDependencies } from '../utils/package.js'
78
import { HarmonyDir, TemplateDir } from '../utils/path.js'
89
import type { AppJson } from './templates/app.json5.js'
10+
import type { BuildProfile } from './templates/build-profile.js'
911
import type { ColorConfig } from './templates/element.json.js'
10-
import { assetsAppsPath, copyWww } from './www.js'
12+
13+
export const assetsAppsPath = join(HarmonyDir, 'entry/src/main/resources/resfile/apps/HBuilder/www')
14+
15+
export const devDistPath = join(App.projectRoot, 'dist/dev/app-harmony')
16+
17+
export const buildDistPath = join(App.projectRoot, 'dist/build/app-harmony')
18+
19+
export async function copyWww(options?: { isBuild?: boolean }) {
20+
const buildDistDir = options?.isBuild ? buildDistPath : devDistPath
21+
const files = await readdir(buildDistDir)
22+
for (const file of files) {
23+
const filePath = join(buildDistDir, file)
24+
if (file === 'uni_modules') {
25+
const hmFiles = await readdir(filePath)
26+
for (const hmFile of hmFiles) {
27+
const hmFilePath = join(filePath, hmFile)
28+
if (hmFile === 'build-profile.json5') {
29+
const modules = readJsonFile<BuildProfile>(hmFilePath, true).modules || []
30+
if (modules.length > 0) {
31+
await editJsonFile(join(HarmonyDir, hmFile), (data: BuildProfile) => {
32+
data.modules.push(...modules)
33+
})
34+
}
35+
} else if (hmFile === 'index.generated.ets') {
36+
await cp(hmFilePath, join(HarmonyDir, `entry/src/main/ets/uni_modules/${hmFile}`), { recursive: true })
37+
} else if (hmFile === 'oh-package.json5') {
38+
const dependencies = getPackageDependencies(readJsonFile<PackageJson>(hmFilePath, true) || {})
39+
if (Object.keys(dependencies).length > 0) {
40+
await editJsonFile(join(HarmonyDir, hmFile), (data: PackageJson) => {
41+
if (!data.dependencies) data.dependencies = dependencies
42+
else {
43+
for (const key in dependencies) {
44+
if (data.dependencies[key]) {
45+
if (data.dependencies[key] === dependencies[key]) continue
46+
Log.debug(`uni_modules 依赖 \`${key}\` 版本不一致,使用版本 ${dependencies[key]}`)
47+
}
48+
data.dependencies[key] = dependencies[key]
49+
}
50+
}
51+
})
52+
}
53+
}
54+
}
55+
} else {
56+
await cp(filePath, join(assetsAppsPath, file), { recursive: true })
57+
}
58+
}
59+
}
1160

1261
export async function prepare(options?: { isBuild?: boolean }) {
1362
Log.debug('前端打包资源嵌入 Harmony 资源中')

src/harmony/tools/hdc.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { readdir } from 'node:fs/promises'
2+
import { join } from 'node:path'
3+
import which from 'which'
4+
import { exists } from '../../utils/file.js'
5+
import { isDarwin, isWindows } from '../../utils/util.js'
6+
7+
export async function getHdc() {
8+
let hdcPath = await which('hdc', { nothrow: true })
9+
if (hdcPath && (await exists(hdcPath))) return hdcPath
10+
11+
const hdcFileName = isWindows() ? 'hdc.exe' : 'hdc'
12+
13+
if (process.env.HARMONY_HOME) {
14+
hdcPath = join(process.env.HARMONY_HOME, 'sdk', 'default', 'openharmony', 'toolchains', hdcFileName)
15+
if (await exists(hdcPath)) return hdcPath
16+
}
17+
18+
if (isWindows()) {
19+
if (process.env['DevEco Studio']) {
20+
hdcPath = join(process.env['DevEco Studio'], '..', 'sdk', 'default', 'openharmony', 'toolchains', hdcFileName)
21+
if (await exists(hdcPath)) return hdcPath
22+
}
23+
if (process.env.ProgramFiles) {
24+
hdcPath = join(
25+
process.env.ProgramFiles,
26+
'Huawei',
27+
'DevEco Studio',
28+
'sdk',
29+
'default',
30+
'openharmony',
31+
'toolchains',
32+
hdcFileName,
33+
)
34+
if (await exists(hdcPath)) return hdcPath
35+
}
36+
if (process.env.LOCALAPPDATA) {
37+
const sdkDir = join(process.env.LOCALAPPDATA, 'OpenHarmony', 'Sdk')
38+
if (await exists(sdkDir)) {
39+
const sdkVerions = (await readdir(sdkDir)).map((file) => Number(file)).filter((ver) => !Number.isNaN(ver))
40+
if (sdkVerions.length > 0) {
41+
const maxSdkVersion = Math.max(...sdkVerions)
42+
hdcPath = join(sdkDir, `${maxSdkVersion}`, 'toolchains', hdcFileName)
43+
if (await exists(hdcPath)) return hdcPath
44+
}
45+
}
46+
}
47+
} else if (isDarwin()) {
48+
hdcPath = `/Applications/DevEco-Studio.app/Contents/sdk/default/openharmony/toolchains/${hdcFileName}`
49+
if (await exists(hdcPath)) return hdcPath
50+
}
51+
52+
throw Error('未找到 hdc 可执行文件,请确定已安装 Harmony SDK')
53+
}

0 commit comments

Comments
 (0)