From c1f7923d25785f6a3d86305ca84b53039e48df15 Mon Sep 17 00:00:00 2001 From: lijunru Date: Sat, 19 Jul 2025 20:21:29 +0800 Subject: [PATCH] Supports file-level hybird Issue: https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/ICNFKF Signed-off-by: lijunru --- .../src/common/arkTSConfigGenerator.ts | 59 +++++++++- ets2panda/bindings/src/common/types.ts | 7 +- ets2panda/bindings/src/common/utils.ts | 12 +- .../bindings/src/lsp/generateArkTSConfig.ts | 19 +-- .../bindings/src/lsp/generateBuildConfig.ts | 67 ++++++----- ets2panda/bindings/src/lsp/lsp_helper.ts | 110 +++++++----------- 6 files changed, 167 insertions(+), 107 deletions(-) diff --git a/ets2panda/bindings/src/common/arkTSConfigGenerator.ts b/ets2panda/bindings/src/common/arkTSConfigGenerator.ts index 5b2a1ee5f9..7cfdaed9f8 100644 --- a/ets2panda/bindings/src/common/arkTSConfigGenerator.ts +++ b/ets2panda/bindings/src/common/arkTSConfigGenerator.ts @@ -16,7 +16,7 @@ import * as path from 'path'; import * as fs from 'fs'; -import { changeFileExtension, ensurePathExists } from './utils'; +import { changeFileExtension, ensurePathExists, getFileLanguageVersion } from './utils'; import { BuildConfig, ModuleInfo } from './types'; import { LANGUAGE_VERSION, PANDA_SDK_PATH_FROM_SDK, SYSTEM_SDK_PATH_FROM_SDK } from './preDefine'; @@ -120,6 +120,20 @@ export class ArkTSConfigGenerator { }); } + private getAlias(fullPath: string, entryRoot: string): string { + const normalizedFull = path.normalize(fullPath); + const normalizedEntry = path.normalize(entryRoot); + const entryDir = normalizedEntry.endsWith(path.sep) ? normalizedEntry : normalizedEntry + path.sep; + if (!normalizedFull.startsWith(entryDir)) { + throw new Error(`Path ${fullPath} is not under entry root ${entryRoot}`); + } + const entryName = path.basename(normalizedEntry); + const relativePath = normalizedFull.substring(entryDir.length); + const formatPath = path.join(entryName, relativePath).replace(/\\/g, '/'); + const alias = formatPath; + return changeFileExtension(alias, ''); + } + private getPathSection(moduleInfo: ModuleInfo): Record { if (Object.keys(this.pathSection).length !== 0) { return this.pathSection; @@ -132,9 +146,26 @@ export class ArkTSConfigGenerator { Object.values(moduleInfo.staticDepModuleInfos).forEach((depModuleName: string) => { let depModuleInfo = this.moduleInfos[depModuleName]; - this.pathSection[depModuleInfo.packageName] = [path.resolve(depModuleInfo.moduleRootPath)]; + if (depModuleInfo.language === LANGUAGE_VERSION.ARKTS_1_2) { + this.pathSection[depModuleInfo.packageName] = [path.resolve(depModuleInfo.moduleRootPath)]; + } else if (depModuleInfo.language === LANGUAGE_VERSION.ARKTS_HYBRID) { + depModuleInfo.compileFiles.forEach((file) => { + const firstLine = fs.readFileSync(file, 'utf-8').split('\n')[0]; + if (firstLine.includes('use static')) { + this.pathSection[this.getAlias(file, depModuleInfo.moduleRootPath)] = [path.resolve(file)]; + } + }); + } }); + if (moduleInfo.language === LANGUAGE_VERSION.ARKTS_HYBRID) { + moduleInfo.compileFiles.forEach((file) => { + if (getFileLanguageVersion(file) === LANGUAGE_VERSION.ARKTS_1_2) { + this.pathSection[this.getAlias(file, moduleInfo.moduleRootPath)] = [path.resolve(file)]; + } + }); + } + return this.pathSection; } @@ -169,6 +200,30 @@ export class ArkTSConfigGenerator { } }); }); + + if (moduleInfo.language === LANGUAGE_VERSION.ARKTS_HYBRID) { + if (!moduleInfo.declFilesPath || !fs.existsSync(moduleInfo.declFilesPath)) { + console.error( + `Module ${moduleInfo.packageName} is hybrid, but decl file not found on path ${moduleInfo.declFilesPath}` + ); + return; + } + let declFilesObject = JSON.parse(fs.readFileSync(moduleInfo.declFilesPath, 'utf-8')); + Object.keys(declFilesObject.files).forEach((file: string) => { + let ohmurl: string = this.getOhmurl(file, moduleInfo); + dependencySection[ohmurl] = { + language: 'js', + path: declFilesObject.files[file].declPath, + ohmUrl: declFilesObject.files[file].ohmUrl + }; + + let absFilePath: string = path.resolve(moduleInfo.moduleRootPath, file); + let entryFileWithoutExtension: string = changeFileExtension(moduleInfo.entryFile, ''); + if (absFilePath === entryFileWithoutExtension) { + dependencySection[moduleInfo.packageName] = dependencySection[ohmurl]; + } + }); + } } public writeArkTSConfigFile(moduleInfo: ModuleInfo): void { diff --git a/ets2panda/bindings/src/common/types.ts b/ets2panda/bindings/src/common/types.ts index 29edf9d82a..bf770a74b9 100644 --- a/ets2panda/bindings/src/common/types.ts +++ b/ets2panda/bindings/src/common/types.ts @@ -109,7 +109,11 @@ export class Context extends ArktsObject { throwError(`Config not initialized`); } return new Context( - global.es2panda._CreateContextFromStringWithHistory(global.config, passString(source), passString(global.filePath)) + global.es2panda._CreateContextFromStringWithHistory( + global.config, + passString(source), + passString(global.filePath) + ) ); } @@ -164,7 +168,6 @@ export interface PathConfig { } export interface DeclgenConfig { - enableDeclgenEts2Ts: boolean; declgenV1OutPath?: string; declgenBridgeCodePath?: string; } diff --git a/ets2panda/bindings/src/common/utils.ts b/ets2panda/bindings/src/common/utils.ts index 800f92b064..8ad712c656 100644 --- a/ets2panda/bindings/src/common/utils.ts +++ b/ets2panda/bindings/src/common/utils.ts @@ -16,7 +16,7 @@ import * as fs from 'fs'; import * as path from 'path'; import * as os from 'os'; -import { DECL_ETS_SUFFIX } from './preDefine'; +import { DECL_ETS_SUFFIX, LANGUAGE_VERSION } from './preDefine'; export function throwError(error: string): never { throw new Error(error); @@ -67,3 +67,13 @@ export function getModuleNameAndPath(filePath: string, projectPath: string): [st } return [moduleName, moduleRootPath]; } + +export function getFileLanguageVersion(fileSource: string): string { + const firstLine = fileSource.split('\n')[0]; + const hasUseStatic = firstLine.includes('use static'); + if (hasUseStatic) { + return LANGUAGE_VERSION.ARKTS_1_2; + } else { + return LANGUAGE_VERSION.ARKTS_1_1; + } +} diff --git a/ets2panda/bindings/src/lsp/generateArkTSConfig.ts b/ets2panda/bindings/src/lsp/generateArkTSConfig.ts index fae5a16029..0e87e96088 100644 --- a/ets2panda/bindings/src/lsp/generateArkTSConfig.ts +++ b/ets2panda/bindings/src/lsp/generateArkTSConfig.ts @@ -26,9 +26,14 @@ function collectDepModuleInfos(moduleInfo: ModuleInfo, allBuildConfig: Record { let depModule = allBuildConfig[moduleName]; - depModule.language === LANGUAGE_VERSION.ARKTS_1_2 - ? staticDepModules.push(depModule.packageName) - : dynamicDepModules.push(depModule.packageName); + if (depModule.language === LANGUAGE_VERSION.ARKTS_1_2) { + staticDepModules.push(depModule.packageName); + } else if (depModule.language === LANGUAGE_VERSION.ARKTS_1_1) { + dynamicDepModules.push(depModule.packageName); + } else { + staticDepModules.push(depModule.packageName); + dynamicDepModules.push(depModule.packageName); + } }); } moduleInfo.dynamicDepModuleInfos = dynamicDepModules; @@ -69,10 +74,10 @@ export function generateModuleInfo(allBuildConfig: Record, export function generateArkTsConfigs(allBuildConfig: Record): Record { let moduleInfos: Record = collectModuleInfos(allBuildConfig); - Object.keys(moduleInfos).forEach((filePath: string) => { - let packageName = moduleInfos[filePath].packageName; - let generator = ArkTSConfigGenerator.getGenerator(allBuildConfig[packageName], moduleInfos); - generator.writeArkTSConfigFile(moduleInfos[filePath]); + Object.keys(moduleInfos).forEach((packageName: string) => { + let buildConfig = allBuildConfig[packageName]; + let generator = ArkTSConfigGenerator.getGenerator(buildConfig, moduleInfos); + generator.writeArkTSConfigFile(moduleInfos[packageName]); }); let fileToModuleInfo: Record = {}; Object.values(moduleInfos).forEach((moduleInfo: ModuleInfo) => { diff --git a/ets2panda/bindings/src/lsp/generateBuildConfig.ts b/ets2panda/bindings/src/lsp/generateBuildConfig.ts index 7eace71cb1..664a14175f 100644 --- a/ets2panda/bindings/src/lsp/generateBuildConfig.ts +++ b/ets2panda/bindings/src/lsp/generateBuildConfig.ts @@ -17,13 +17,14 @@ import * as fs from 'fs'; import * as path from 'path'; import * as JSON5 from 'json5'; import { BuildConfig, PathConfig } from '../common/types'; -import { DEFAULT_CACHE_DIR, EXTERNAL_API_PATH_FROM_SDK } from '../common/preDefine'; +import { DEFAULT_CACHE_DIR, EXTERNAL_API_PATH_FROM_SDK, LANGUAGE_VERSION } from '../common/preDefine'; +import { getFileLanguageVersion } from '../common/utils'; export interface ModuleDescriptor { - arktsversion: string; name: string; moduleType: string; srcPath: string; + arktsversion?: string; aceModuleJsonPath?: string; } @@ -172,6 +173,28 @@ function addPluginPathConfigs(buildConfig: BuildConfig, module: ModuleDescriptor buildConfig.aceModuleJsonPath = module.aceModuleJsonPath; } +function getModuleLanguageVersion(compileFiles: Set): string { + let found1_1 = false; + let found1_2 = false; + + for (const file of compileFiles) { + const sourceFile = fs.readFileSync(file, 'utf8'); + const languageVersion = getFileLanguageVersion(sourceFile); + + if (languageVersion === LANGUAGE_VERSION.ARKTS_1_2) { + found1_2 = true; + } else if (languageVersion === LANGUAGE_VERSION.ARKTS_1_1) { + found1_1 = true; + } + + if (found1_1 && found1_2) { + return LANGUAGE_VERSION.ARKTS_HYBRID; + } + } + + return found1_2 ? LANGUAGE_VERSION.ARKTS_1_2 : found1_1 ? LANGUAGE_VERSION.ARKTS_1_1 : ''; +} + export function generateBuildConfigs( pathConfig: PathConfig, modules?: ModuleDescriptor[] @@ -185,8 +208,6 @@ export function generateBuildConfigs( const definedModules = modules; - const enableDeclgen: Map = new Map(modules.map((module) => [module.name, false])); - for (const module of definedModules) { const modulePath = module.srcPath; const compileFiles = new Set(getEtsFiles(modulePath)); @@ -196,19 +217,15 @@ export function generateBuildConfigs( const dependencies = getModuleDependencies(modulePath); for (const depPath of dependencies) { getEtsFiles(depPath).forEach((file) => compileFiles.add(file)); - const depModule = definedModules.find((m) => m.srcPath === depPath); - if (module.arktsversion === '1.1' && depModule?.arktsversion === '1.2') { - enableDeclgen.set(depModule.name, true); - } } - + let languageVersion = getModuleLanguageVersion(compileFiles); allBuildConfigs[module.name] = { plugins: pluginMap, compileFiles: Array.from(compileFiles), packageName: module.name, moduleType: module.moduleType, moduleRootPath: modulePath, - language: module.arktsversion, + language: languageVersion, buildSdkPath: pathConfig.buildSdkPath, projectPath: pathConfig.projectPath, declgenOutDir: pathConfig.declgenOutDir, @@ -217,30 +234,24 @@ export function generateBuildConfigs( : path.resolve(pathConfig.buildSdkPath, EXTERNAL_API_PATH_FROM_SDK), cacheDir: pathConfig.cacheDir !== undefined ? pathConfig.cacheDir : path.join(pathConfig.projectPath, DEFAULT_CACHE_DIR), - enableDeclgenEts2Ts: false, declFilesPath: - module.arktsversion === '1.1' - ? path.join(pathConfig.declgenOutDir, 'static', module.name, 'decl-fileInfo.json') + languageVersion !== LANGUAGE_VERSION.ARKTS_1_2 + ? path.join(pathConfig.declgenOutDir, module.name, 'declgen', 'dynamic', 'decl-fileInfo.json') + : undefined, + declgenV1OutPath: + languageVersion !== LANGUAGE_VERSION.ARKTS_1_1 + ? path.join(pathConfig.declgenOutDir, module.name, 'declgen', 'static') + : undefined, + declgenBridgeCodePath: + languageVersion !== LANGUAGE_VERSION.ARKTS_1_1 + ? path.join(pathConfig.declgenOutDir, module.name, 'declgen', 'static', 'declgenBridgeCode') : undefined, dependencies: dependencies.map((dep) => { - const depModule = definedModules.find((m) => m.srcPath === dep); - return depModule!.name; + const depModule = modules.find((m) => m.srcPath === dep); + return depModule ? depModule.name : ''; }) }; addPluginPathConfigs(allBuildConfigs[module.name], module); } - Object.entries(allBuildConfigs).forEach(([key, config]) => { - if (enableDeclgen.get(key) === true) { - config.enableDeclgenEts2Ts = true; - config.declgenV1OutPath = path.join(pathConfig.declgenOutDir, 'dynamic', key, 'declgenV1'); - config.declgenBridgeCodePath = path.join(pathConfig.declgenOutDir, 'dynamic', key, 'declgenBridgeCode'); - if (!fs.existsSync(config.declgenV1OutPath)) { - fs.mkdirSync(config.declgenV1OutPath, { recursive: true }); - } - if (!fs.existsSync(config.declgenBridgeCodePath)) { - fs.mkdirSync(config.declgenBridgeCodePath, { recursive: true }); - } - } - }); return allBuildConfigs; } diff --git a/ets2panda/bindings/src/lsp/lsp_helper.ts b/ets2panda/bindings/src/lsp/lsp_helper.ts index 3b8999ce90..360cc6fa31 100644 --- a/ets2panda/bindings/src/lsp/lsp_helper.ts +++ b/ets2panda/bindings/src/lsp/lsp_helper.ts @@ -72,9 +72,9 @@ import { KInt, KNativePointer, KPointer } from '../common/InteropTypes'; import { passPointerArray } from '../common/private'; import { NativePtrDecoder } from '../common/Platform'; import { Worker as ThreadWorker } from 'worker_threads'; -import { ensurePathExists } from '../common/utils'; +import { ensurePathExists, getFileLanguageVersion } from '../common/utils'; import * as child_process from 'child_process'; -import { DECL_ETS_SUFFIX, DEFAULT_CACHE_DIR, TS_SUFFIX } from '../common/preDefine'; +import { DECL_ETS_SUFFIX, DEFAULT_CACHE_DIR, LANGUAGE_VERSION, TS_SUFFIX } from '../common/preDefine'; import * as crypto from 'crypto'; import * as os from 'os'; import { changeDeclgenFileExtension, getModuleNameAndPath } from '../common/utils'; @@ -198,101 +198,64 @@ export class Lsp { } generateDeclFile(): void { - for (const [moduleName, buildConfig] of Object.entries(this.buildConfigs)) { - if (!buildConfig.enableDeclgenEts2Ts) { + for (const buildConfig of Object.values(this.buildConfigs)) { + if (buildConfig.language === LANGUAGE_VERSION.ARKTS_1_1) { continue; } - if (!buildConfig.declgenOutDir || buildConfig.declgenOutDir === '') { - return; - } buildConfig.compileFiles.forEach((compilefilePath: string) => { if (!this.moduleInfos.hasOwnProperty(compilefilePath)) { return; } - const [cfg, ctx] = this.createContext(compilefilePath); - try { - // declgen file - let moduleInfo = this.moduleInfos[compilefilePath]; - let modulePath: string = path.relative(buildConfig.moduleRootPath, compilefilePath); - let declOut: string = ''; - let declBridgeOut: string = ''; - if (!moduleInfo.declgenV1OutPath) { - declOut = path.join(buildConfig.declgenOutDir, moduleName); - } - if (!moduleInfo.declgenBridgeCodePath) { - declBridgeOut = path.join(buildConfig.declgenOutDir, moduleName); + const fileSource = this.getFileSource(compilefilePath); + if (getFileLanguageVersion(fileSource) === LANGUAGE_VERSION.ARKTS_1_2) { + const [cfg, ctx] = this.createContext(compilefilePath); + try { + // declgen file + let moduleInfo = this.moduleInfos[compilefilePath]; + let modulePath: string = path.relative(buildConfig.moduleRootPath, compilefilePath); + let declEtsOutputPath: string = changeDeclgenFileExtension( + path.join(moduleInfo.declgenV1OutPath!, modulePath), + DECL_ETS_SUFFIX + ); + let etsOutputPath: string = changeDeclgenFileExtension( + path.join(moduleInfo.declgenBridgeCodePath!, modulePath), + TS_SUFFIX + ); + ensurePathExists(declEtsOutputPath); + ensurePathExists(etsOutputPath); + global.es2pandaPublic._GenerateTsDeclarationsFromContext(ctx, declEtsOutputPath, etsOutputPath, 1, 0); + } finally { + this.destroyContext(cfg, ctx); } - let declEtsOutputPath: string = changeDeclgenFileExtension( - path.join(moduleInfo.declgenV1OutPath ?? declOut, modulePath), - DECL_ETS_SUFFIX - ); - let etsOutputPath: string = changeDeclgenFileExtension( - path.join(moduleInfo.declgenBridgeCodePath ?? declBridgeOut, modulePath), - TS_SUFFIX - ); - ensurePathExists(declEtsOutputPath); - ensurePathExists(etsOutputPath); - global.es2pandaPublic._GenerateTsDeclarationsFromContext(ctx, declEtsOutputPath, etsOutputPath, 1, 0); - } finally { - this.destroyContext(cfg, ctx); } }); } } - modifyDeclFile(modifyFilePath: string, arktsConfigFile?: string): void { + modifyDeclFile(modifyFilePath: string): void { // source file let sourceFilePath = path.resolve(modifyFilePath.valueOf()); let moduleInfo: ModuleInfo; if (this.moduleInfos.hasOwnProperty(sourceFilePath)) { moduleInfo = this.moduleInfos[sourceFilePath]; } else { - const [newModuleName, newModuleRootPath] = getModuleNameAndPath(modifyFilePath, this.pathConfig.projectPath); - if (newModuleName && newModuleName !== '' && newModuleRootPath && newModuleRootPath !== '') { - moduleInfo = { - packageName: newModuleName, - moduleRootPath: newModuleRootPath, - moduleType: '', - entryFile: '', - arktsConfigFile: arktsConfigFile ?? '', - compileFiles: [], - declgenV1OutPath: '', - declgenBridgeCodePath: '', - staticDepModuleInfos: [], - dynamicDepModuleInfos: [], - language: '' - }; - } else { - return; - } + return; } const moduleName = moduleInfo.packageName; const moduleRootPath = moduleInfo.moduleRootPath; if (!this.buildConfigs.hasOwnProperty(moduleName)) { return; } - const buildConfig = this.buildConfigs[moduleName]; - if (!buildConfig.enableDeclgenEts2Ts) { - return; - } const [cfg, ctx] = this.createContext(sourceFilePath); try { // declgen file - let declOut: string = ''; - let declBridgeOut: string = ''; - if (!moduleInfo.declgenV1OutPath) { - declOut = path.join(buildConfig.declgenOutDir, moduleName); - } - if (!moduleInfo.declgenBridgeCodePath) { - declBridgeOut = path.join(buildConfig.declgenOutDir, moduleName); - } let filePathFromModuleRoot: string = path.relative(moduleRootPath, modifyFilePath); let declEtsOutputPath: string = changeDeclgenFileExtension( - path.join(moduleInfo.declgenV1OutPath ?? declOut, filePathFromModuleRoot), + path.join(moduleInfo.declgenV1OutPath!, filePathFromModuleRoot), DECL_ETS_SUFFIX ); let etsOutputPath: string = changeDeclgenFileExtension( - path.join(moduleInfo.declgenBridgeCodePath ?? declBridgeOut, filePathFromModuleRoot), + path.join(moduleInfo.declgenBridgeCodePath!, filePathFromModuleRoot), TS_SUFFIX ); ensurePathExists(declEtsOutputPath); @@ -902,7 +865,14 @@ export class Lsp { } private collectCompileJobs(jobs: Record, isValid: boolean = false): void { - let entryFileList: string[] = Object.keys(this.moduleInfos); + let entryFileList: string[] = Object.keys(this.moduleInfos).filter((file) => { + if (this.moduleInfos[file].language === LANGUAGE_VERSION.ARKTS_1_2) { + return true; + } else if (this.moduleInfos[file].language === LANGUAGE_VERSION.ARKTS_HYBRID) { + const fileSource = this.getFileSource(file); + return getFileLanguageVersion(fileSource) === LANGUAGE_VERSION.ARKTS_1_2; + } + }); this.getFileDependencies(entryFileList, this.fileDependencies); const data = fs.readFileSync(this.fileDependencies, 'utf-8'); let fileDepsInfo: FileDepsInfo = JSON.parse(data) as FileDepsInfo; @@ -1022,6 +992,10 @@ export class Lsp { } }); + if (files.length === 0) { + return; + } + let ets2pandaCmd: string[] = [ '_', '--extension', @@ -1189,7 +1163,9 @@ export class Lsp { this.collectCompileJobs(jobs); this.initGlobalContext(jobs); this.initCompileQueues(jobs, queues); - + if (Object.keys(jobs).length === 0 && queues.length === 0) { + return; + } const processingJobs = new Set(); const workers: ThreadWorker[] = []; await this.invokeWorkers(jobs, queues, processingJobs, workers, numWorkers); -- Gitee