From ba5c9717e15d92e535710e85d639bf81b46ec233 Mon Sep 17 00:00:00 2001 From: fxy060608 Date: Wed, 23 Nov 2022 14:24:41 +0800 Subject: [PATCH] wip(uts): compiler --- packages/shims-node.d.ts | 1 + .../uni-uts-v1/__tests__/manifest.spec.ts | 40 +++-- packages/uni-uts-v1/src/index.ts | 157 ++++++++++++++---- packages/uni-uts-v1/src/kotlin.ts | 7 + packages/uni-uts-v1/src/manifest/index.ts | 45 ++--- packages/uni-uts-v1/src/manifest/manifest.ts | 39 ++--- packages/uni-uts-v1/src/manifest/utils.ts | 2 +- packages/uni-uts-v1/src/swift.ts | 8 + packages/uni-uts-v1/src/utils.ts | 4 + 9 files changed, 204 insertions(+), 99 deletions(-) diff --git a/packages/shims-node.d.ts b/packages/shims-node.d.ts index ddf7daae23..0bcb161ac3 100644 --- a/packages/shims-node.d.ts +++ b/packages/shims-node.d.ts @@ -37,6 +37,7 @@ declare namespace NodeJS { UNI_APP_CHANGED_PAGES: string VUE_APP_DARK_MODE: 'true' | 'false' HX_USE_BASE_TYPE?: 'standard' | 'custom' + HX_DEPENDENCIES_DIR?: string __VUE_PROD_DEVTOOLS__?: 'true' __VUE_DEVTOOLS_HOST__: string diff --git a/packages/uni-uts-v1/__tests__/manifest.spec.ts b/packages/uni-uts-v1/__tests__/manifest.spec.ts index 6020afb395..d544ac0a2b 100644 --- a/packages/uni-uts-v1/__tests__/manifest.spec.ts +++ b/packages/uni-uts-v1/__tests__/manifest.spec.ts @@ -2,10 +2,9 @@ import { resolve } from 'path' import { checkManifest, resolveManifestJson, - resolvePluginAndroidFiles, - resolvePluginIOSFiles, + resolvePluginFiles, } from '../src/manifest/manifest' -import { checkKotlinCompile } from '../src/manifest/index' +import { checkKotlinCompile, checkSwiftCompile } from '../src/manifest/index' const pluginModuleDir = resolve(__dirname, 'examples/uts/uni_modules/test-uts') const pluginDir = resolve(__dirname, 'examples/uts/utssdk/test-uts') @@ -36,13 +35,15 @@ const pluginOptions = { describe('manifest', () => { test('resolve android files', async () => { - expect(await resolvePluginAndroidFiles(pluginDir, false)).toEqual([ + expect(await resolvePluginFiles('app-android', pluginDir, false)).toEqual([ 'index.uts', 'package.json', 'common/utils.uts', 'common/test/test.uts', ]) - expect(await resolvePluginAndroidFiles(pluginModuleDir, true)).toEqual([ + expect( + await resolvePluginFiles('app-android', pluginModuleDir, true) + ).toEqual([ 'package.json', 'utssdk/common/utils.uts', 'utssdk/app-android/index.uts', @@ -50,13 +51,13 @@ describe('manifest', () => { ]) }) test('resolve ios files', async () => { - expect(await resolvePluginIOSFiles(pluginDir, false)).toEqual([ + expect(await resolvePluginFiles('app-ios', pluginDir, false)).toEqual([ 'index.uts', 'package.json', 'common/utils.uts', 'common/test/test.uts', ]) - expect(await resolvePluginIOSFiles(pluginModuleDir, true)).toEqual([ + expect(await resolvePluginFiles('app-ios', pluginModuleDir, true)).toEqual([ 'package.json', 'utssdk/common/utils.uts', 'utssdk/app-ios/index.uts', @@ -99,17 +100,24 @@ describe('manifest', () => { ).toBe(filename) }) test('gen android manifest', async () => { - expect( - await ( - await checkKotlinCompile('standard', pluginModuleOptions) - ).expired - ).toBe(true) - - const res = await checkKotlinCompile('standard', pluginOptions) + const res = await checkKotlinCompile('standard', pluginModuleOptions) expect(res.expired).toBe(false) - expect(res.cacheFile).toContain('classes.dex') + expect(res.files.length).toBe(4) + expect(res.tips).toBeTruthy() + + const res1 = await checkKotlinCompile('standard', pluginOptions) + expect(res1.expired).toBe(false) + expect(res1.files.length).toBe(4) + expect(res1.tips).toBe('') }) test('gen ios manifest', async () => { - // console.log(await checkCompile('standard', pluginModuleOptions)) + const res = await checkSwiftCompile('standard', pluginModuleOptions) + expect(res.expired).toBe(false) + expect(res.files.length).toBe(4) + expect(res.tips).toBe('') + const res1 = await checkSwiftCompile('standard', pluginOptions) + expect(res1.expired).toBe(false) + expect(res1.files.length).toBe(4) + expect(res1.tips).toBe('') }) }) diff --git a/packages/uni-uts-v1/src/index.ts b/packages/uni-uts-v1/src/index.ts index a638464dc5..ddc5dc149a 100644 --- a/packages/uni-uts-v1/src/index.ts +++ b/packages/uni-uts-v1/src/index.ts @@ -1,7 +1,9 @@ import { isArray } from '@vue/shared' +import { basename, join, relative } from 'path' +import { copySync, existsSync } from 'fs-extra' -import { runKotlinProd, runKotlinDev } from './kotlin' -import { runSwiftProd, runSwiftDev } from './swift' +import { runKotlinProd, runKotlinDev, resolveAndroidDepFiles } from './kotlin' +import { runSwiftProd, runSwiftDev, resolveIOSDepFiles } from './swift' import { genProxyCode, resolvePlatformIndex, resolveRootIndex } from './code' import { ERR_MSG_PLACEHOLDER, resolvePackage } from './utils' @@ -12,6 +14,7 @@ import { generateCodeFrameWithKotlinStacktrace, generateCodeFrameWithSwiftStacktrace, } from './legacy' +import { checkCompile, genManifestFile, initCheckOptionsEnv } from './manifest' export const sourcemap = { generateCodeFrameWithKotlinStacktrace, @@ -31,41 +34,59 @@ function compileErrMsg(id: string) { return `uts插件[${id}]编译失败,无法使用` } -export async function compile(module: string) { - const pkg = resolvePackage(module) +export async function compile(pluginDir: string) { + const pkg = resolvePackage(pluginDir) if (!pkg) { return } + const cacheDir = process.env.HX_DEPENDENCIES_DIR + const inputDir = process.env.UNI_INPUT_DIR + const outputDir = process.env.UNI_OUTPUT_DIR + const utsPlatform = process.env.UNI_UTS_PLATFORM + const pluginRelativeDir = relative(inputDir, pluginDir) + const env = initCheckOptionsEnv() const deps: string[] = [] - const code = await genProxyCode(module, pkg) + const code = await genProxyCode(pluginDir, pkg) let errMsg = '' if (process.env.NODE_ENV !== 'development') { // 生产模式 支持同时生成 android 和 ios 的 uts 插件 - if ( - process.env.UNI_UTS_PLATFORM === 'app-android' || - process.env.UNI_UTS_PLATFORM === 'app' - ) { + if (utsPlatform === 'app-android' || utsPlatform === 'app') { const filename = - resolvePlatformIndex('app-android', module, pkg) || - resolveRootIndex(module, pkg) + resolvePlatformIndex('app-android', pluginDir, pkg) || + resolveRootIndex(pluginDir, pkg) if (filename) { await getCompiler('kotlin').runProd(filename) + if (cacheDir) { + genManifestFile('app-android', { + pluginDir, + env, + cacheDir, + pluginRelativeDir, + is_uni_modules: pkg.is_uni_modules, + }) + } } } - if ( - process.env.UNI_UTS_PLATFORM === 'app-ios' || - process.env.UNI_UTS_PLATFORM === 'app' - ) { + if (utsPlatform === 'app-ios' || utsPlatform === 'app') { const filename = - resolvePlatformIndex('app-ios', module, pkg) || - resolveRootIndex(module, pkg) + resolvePlatformIndex('app-ios', pluginDir, pkg) || + resolveRootIndex(pluginDir, pkg) if (filename) { await getCompiler('swift').runProd(filename) + if (cacheDir) { + genManifestFile('app-ios', { + pluginDir, + env, + cacheDir, + pluginRelativeDir, + is_uni_modules: pkg.is_uni_modules, + }) + } } } } else { // iOS windows 平台,标准基座不编译 - if (process.env.UNI_UTS_PLATFORM === 'app-ios') { + if (utsPlatform === 'app-ios') { if (isWindows) { process.env.UNI_UTS_TIPS = `iOS手机在windows上真机运行时uts插件代码修改需提交云端打包自定义基座才能生效` return { @@ -81,25 +102,66 @@ export async function compile(module: string) { } } } - if ( - process.env.UNI_UTS_PLATFORM === 'app-android' || - process.env.UNI_UTS_PLATFORM === 'app-ios' - ) { + if (utsPlatform === 'app-android' || utsPlatform === 'app-ios') { // dev 模式 + if (cacheDir) { + // 检查缓存 + let start = Date.now() + const res = await checkCompile( + utsPlatform, + process.env.HX_USE_BASE_TYPE, + { + id: pkg.id, + env, + cacheDir, + outputDir, + pluginDir, + pluginRelativeDir, + is_uni_modules: pkg.is_uni_modules, + } + ) + console.log('uts插件[' + pkg.id + ']缓存检查耗时:', Date.now() - start) + if (!res.expired) { + if (utsPlatform === 'app-android') { + const cacheFile = resolveDexCacheFile(pluginRelativeDir, outputDir) + if (cacheFile) { + copySync( + cacheFile, + join(outputDir, pluginRelativeDir, basename(cacheFile)) + ) + } + } + if (res.tips) { + console.warn(res.tips) + } + + return { + code, + // 所有文件加入依赖 + deps: res.files.map((name) => join(pluginDir, name)), + } + } + } const filename = - resolvePlatformIndex(process.env.UNI_UTS_PLATFORM, module, pkg) || - resolveRootIndex(module, pkg) - const compilerType = - process.env.UNI_UTS_PLATFORM === 'app-android' ? 'kotlin' : 'swift' + resolvePlatformIndex(utsPlatform, pluginDir, pkg) || + resolveRootIndex(pluginDir, pkg) + const compilerType = utsPlatform === 'app-android' ? 'kotlin' : 'swift' if (filename) { deps.push(filename) + if (utsPlatform === 'app-android') { + deps.push(...resolveAndroidDepFiles(filename)) + } else { + deps.push(...resolveIOSDepFiles(filename)) + } + const res = await getCompiler(compilerType).runDev(filename) if (res) { if (isArray(res.deps) && res.deps.length) { // 添加其他文件的依赖 deps.push(...res.deps) } + let isSuccess = false if (res.type === 'swift') { if (res.code) { errMsg = compileErrMsg(pkg.id) @@ -110,14 +172,31 @@ export async function compile(module: string) { sourceMapFile: resolveUtsPluginSourceMapFile( 'swift', filename, - process.env.UNI_INPUT_DIR, - process.env.UNI_OUTPUT_DIR + inputDir, + outputDir ), - sourceRoot: process.env.UNI_INPUT_DIR, + sourceRoot: inputDir, })) ) + } else { + isSuccess = true } + } else if (res.type === 'kotlin') { + if (res.changed.length) { + isSuccess = true + } + } + // 生成缓存文件 + if (cacheDir && isSuccess) { + genManifestFile(utsPlatform, { + pluginDir, + env, + cacheDir, + pluginRelativeDir, + is_uni_modules: pkg.is_uni_modules, + }) } + const files: string[] = [] if (process.env.UNI_APP_UTS_CHANGED_FILES) { try { @@ -126,6 +205,17 @@ export async function compile(module: string) { } if (res.changed && res.changed.length) { files.push(...res.changed) + // 需要缓存 dex 文件 + if (cacheDir && res.type === 'kotlin') { + res.changed.forEach((file) => { + if (file.endsWith('classes.dex')) { + copySync( + join(outputDir, file), + resolveDexCacheFilename(pluginRelativeDir, outputDir) + ) + } + }) + } } else { if (res.type === 'kotlin') { errMsg = compileErrMsg(pkg.id) @@ -158,3 +248,12 @@ function getCompiler(type: 'kotlin' | 'swift') { runDev: runKotlinDev, } } + +function resolveDexCacheFilename(pluginRelativeDir: string, outputDir: string) { + return join(outputDir, '../.uts/dex', pluginRelativeDir, 'classes.dex') +} + +function resolveDexCacheFile(pluginRelativeDir: string, outputDir: string) { + const file = resolveDexCacheFilename(pluginRelativeDir, outputDir) + return (existsSync(file) && file) || '' +} diff --git a/packages/uni-uts-v1/src/kotlin.ts b/packages/uni-uts-v1/src/kotlin.ts index 4c2576ec7d..a84cfe44cb 100644 --- a/packages/uni-uts-v1/src/kotlin.ts +++ b/packages/uni-uts-v1/src/kotlin.ts @@ -254,6 +254,13 @@ function resolveAndroidManifestPackage(filename: string) { } } +const deps = ['AndroidManifest.xml', 'config.json'] + +export function resolveAndroidDepFiles(filename: string) { + const dir = resolveAndroidDir(filename) + return deps.map((dep) => path.resolve(dir, dep)) +} + function resolveConfigJsonFile(filename: string) { const configJsonFile = path.resolve( resolveAndroidDir(filename), diff --git a/packages/uni-uts-v1/src/manifest/index.ts b/packages/uni-uts-v1/src/manifest/index.ts index 37a530bdc4..482217b7d8 100644 --- a/packages/uni-uts-v1/src/manifest/index.ts +++ b/packages/uni-uts-v1/src/manifest/index.ts @@ -1,11 +1,9 @@ -import { existsSync } from 'fs' -import { join } from 'path' import { checkManifest, hasCustomResources, isCustomResources, resolveManifestJson, - resolvePluginAndroidFiles, + resolvePluginFiles, } from './manifest' import { APP_PLATFORM, @@ -15,9 +13,10 @@ import { customResourceTips, } from './utils' +export { genManifestFile } from './manifest' + interface PlatformOptions { customRes: string[] - cacheFile: false | string } const ANDROID_CUSTOM_RES = [ @@ -49,25 +48,22 @@ export function checkSwiftCompile( return checkCompile('app-ios', playground, options) } -function checkCompile( +export function checkCompile( platform: APP_PLATFORM, playground: typeof process.env.HX_USE_BASE_TYPE, options: CheckOptions ) { const platformOptions: PlatformOptions = { customRes: platform === 'app-android' ? ANDROID_CUSTOM_RES : IOS_CUSTOM_RES, - cacheFile: - platform === 'app-android' - ? resolveDexCacheFile(options.pluginRelativeDir, options.outputDir) - : false, } if (playground === 'standard') { - return checkWithPlayground('standard', options, platformOptions) + return checkWithPlayground(platform, 'standard', options, platformOptions) } - return checkWithPlayground('custom', options, platformOptions) + return checkWithPlayground(platform, 'custom', options, platformOptions) } async function checkWithPlayground( + platform: APP_PLATFORM, type: typeof process.env.HX_USE_BASE_TYPE, { id, @@ -77,27 +73,19 @@ async function checkWithPlayground( pluginRelativeDir, is_uni_modules, }: CheckOptions, - { customRes, cacheFile }: PlatformOptions + { customRes }: PlatformOptions ): Promise { // 第一步:获取所有文件列表 - const files = await resolvePluginAndroidFiles(pluginDir, is_uni_modules) + const files = await resolvePluginFiles(platform, pluginDir, is_uni_modules) let tips = '' // 标准基座检查是否包含原生资源/配置 if (type === 'standard' && hasCustomResources(files, customRes)) { tips = customResourceTips(id) } - // 第二步:检查 dex 文件是否存在 - if (cacheFile !== false && !cacheFile) { - return { expired: true, tips, cacheFile: '' } - } - // 第三步:获取当前插件缓存文件信息 - const manifest = resolveManifestJson( - 'app-android', - pluginRelativeDir, - cacheDir - ) + // 第二步:获取当前插件缓存文件信息 + const manifest = resolveManifestJson(platform, pluginRelativeDir, cacheDir) if (!manifest) { - return { expired: true, tips, cacheFile: '' } + return { expired: true, tips, files } } // 第四步:检查文件变更 const res = await checkManifest(manifest, { env, files, pluginDir }) @@ -110,11 +98,12 @@ async function checkWithPlayground( return { expired: res !== true, tips, - cacheFile, + files, } } -function resolveDexCacheFile(pluginRelativeDir: string, outputDir: string) { - const file = join(outputDir, '../.uts/dex', pluginRelativeDir, 'classes.dex') - return (existsSync(file) && file) || '' +export function initCheckOptionsEnv(): CheckOptions['env'] { + return { + compilerVersion: require('../../package.json').version, + } } diff --git a/packages/uni-uts-v1/src/manifest/manifest.ts b/packages/uni-uts-v1/src/manifest/manifest.ts index 943aae2bdb..5b5dc2c4cf 100644 --- a/packages/uni-uts-v1/src/manifest/manifest.ts +++ b/packages/uni-uts-v1/src/manifest/manifest.ts @@ -50,6 +50,15 @@ interface GenManifestJsonOptions { is_uni_modules: boolean } +export interface GenManifestFileOptions { + cacheDir: string + pluginRelativeDir: string + is_uni_modules: boolean + env: Record + pluginDir: string + files?: string[] +} + export async function genManifestFile( platform: APP_PLATFORM, { @@ -59,14 +68,7 @@ export async function genManifestFile( cacheDir, pluginRelativeDir, is_uni_modules, - }: { - cacheDir: string - pluginRelativeDir: string - is_uni_modules: boolean - env: Record - pluginDir: string - files?: string[] - } + }: GenManifestFileOptions ) { outputFileSync( resolveManifestFilename(platform, pluginRelativeDir, cacheDir), @@ -89,11 +91,7 @@ export async function genManifestJson( { pluginDir, files, env, is_uni_modules }: GenManifestJsonOptions ): Promise { if (!files) { - if (platform === 'app-android') { - files = await resolvePluginAndroidFiles(pluginDir, is_uni_modules) - } else if (platform === 'app-ios') { - files = await resolvePluginIOSFiles(pluginDir, is_uni_modules) - } + files = await resolvePluginFiles(platform, pluginDir, is_uni_modules) } if (!files) { files = [] @@ -137,23 +135,14 @@ async function resolvePluginCommonFiles( }) } -export async function resolvePluginAndroidFiles( - pluginDir: string, - is_uni_modules: boolean -) { - return Promise.all([ - resolvePluginCommonFiles(pluginDir, is_uni_modules), - resolvePluginPlatformFiles('app-android', pluginDir, is_uni_modules), - ]).then((files) => files.flat()) -} - -export async function resolvePluginIOSFiles( +export async function resolvePluginFiles( + platform: APP_PLATFORM, pluginDir: string, is_uni_modules: boolean ) { return Promise.all([ resolvePluginCommonFiles(pluginDir, is_uni_modules), - resolvePluginPlatformFiles('app-ios', pluginDir, is_uni_modules), + resolvePluginPlatformFiles(platform, pluginDir, is_uni_modules), ]).then((files) => files.flat()) } diff --git a/packages/uni-uts-v1/src/manifest/utils.ts b/packages/uni-uts-v1/src/manifest/utils.ts index 8a7360c116..2c847d7fd3 100644 --- a/packages/uni-uts-v1/src/manifest/utils.ts +++ b/packages/uni-uts-v1/src/manifest/utils.ts @@ -13,7 +13,7 @@ export interface CheckOptions { export interface CheckResult { expired: boolean tips?: string - cacheFile: string | false + files: string[] } export function customResourceTips(id: string) { diff --git a/packages/uni-uts-v1/src/swift.ts b/packages/uni-uts-v1/src/swift.ts index 5ced907071..3acd971eba 100644 --- a/packages/uni-uts-v1/src/swift.ts +++ b/packages/uni-uts-v1/src/swift.ts @@ -5,6 +5,7 @@ import { getCompilerServer, getUtsCompiler, moveRootIndexSourceMap, + resolveIOSDir, resolvePackage, resolveUTSPlatformFile, resolveUTSSourceMapPath, @@ -162,6 +163,13 @@ export async function compile( return result } +const deps = ['Info.plist', 'config.json'] + +export function resolveIOSDepFiles(filename: string) { + const dir = resolveIOSDir(filename) + return deps.map((dep) => path.resolve(dir, dep)) +} + interface SwiftCompilerServer { compile(options: { projectPath: string diff --git a/packages/uni-uts-v1/src/utils.ts b/packages/uni-uts-v1/src/utils.ts index 2a73585da3..3474367e07 100644 --- a/packages/uni-uts-v1/src/utils.ts +++ b/packages/uni-uts-v1/src/utils.ts @@ -127,6 +127,10 @@ export function resolveAndroidDir(filename: string) { return resolveUTSPlatformDir(filename, 'app-android') } +export function resolveIOSDir(filename: string) { + return resolveUTSPlatformDir(filename, 'app-ios') +} + function resolveUTSPlatformDir( filename: string, platform: typeof process.env.UNI_UTS_PLATFORM -- GitLab