提交 ba5c9717 编写于 作者: fxy060608's avatar fxy060608

wip(uts): compiler

上级 b1daddf4
......@@ -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
......
......@@ -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('')
})
})
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) || ''
}
......@@ -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),
......
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<CheckResult> {
// 第一步:获取所有文件列表
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,
}
}
......@@ -50,6 +50,15 @@ interface GenManifestJsonOptions {
is_uni_modules: boolean
}
export interface GenManifestFileOptions {
cacheDir: string
pluginRelativeDir: string
is_uni_modules: boolean
env: Record<string, unknown>
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<string, unknown>
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<Manifest> {
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())
}
......
......@@ -13,7 +13,7 @@ export interface CheckOptions {
export interface CheckResult {
expired: boolean
tips?: string
cacheFile: string | false
files: string[]
}
export function customResourceTips(id: string) {
......
......@@ -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
......
......@@ -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
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册