1/* 2 * Copyright (c) 2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import * as fs from 'fs'; 17import * as path from 'path'; 18import * as os from 'os'; 19import * as childProcess from 'child_process'; 20import * as process from 'process'; 21import { logger } from './compile_info'; 22import { projectConfig } from '../main'; 23import { 24 FAIL, 25 FILESINFO_TXT, 26 MODULES_CACHE, 27 NPMENTRIES_TXT, 28 NODE_MODULES, 29 PACKAGES, 30 PATCH_SYMBOL_TABLE, 31 EXTNAME_PROTO_BIN 32} from './pre_define'; 33import { 34 EntryInfo, 35 ModuleInfo, 36 initAbcEnv 37} from './gen_abc_plugin'; 38import { 39 mkdirsSync, 40 toUnixPath, 41 validateFilePathLength 42} from './utils'; 43 44const red: string = '\u001b[31m'; 45const reset: string = '\u001b[39m'; 46 47function generateCompileFilesInfo(moduleInfos: Array<ModuleInfo>) { 48 const tempModuleInfos: ModuleInfo[] = Array<ModuleInfo>(); 49 moduleInfos.forEach((item) => { 50 let check: boolean = tempModuleInfos.every((newItem) => { 51 return item.tempFilePath !== newItem.tempFilePath; 52 }); 53 if (check) { 54 tempModuleInfos.push(item); 55 } 56 }); 57 moduleInfos = tempModuleInfos; 58 59 const filesInfoPath: string = path.join(process.env.cachePath, FILESINFO_TXT); 60 validateFilePathLength(filesInfoPath, logger); 61 let filesInfo: string = ''; 62 moduleInfos.forEach(info => { 63 const moduleType: string = info.isCommonJs ? 'commonjs' : 'esm'; 64 const sourceFile: string = info.filePath.replace(projectConfig.projectRootPath + path.sep, ''); 65 filesInfo += `${info.tempFilePath};${info.recordName};${moduleType};${toUnixPath(sourceFile)};${info.packageName}\n`; 66 }); 67 fs.writeFileSync(filesInfoPath, filesInfo, 'utf-8'); 68} 69 70export function generateNpmEntriesInfo(entryInfos: Map<string, EntryInfo>) { 71 const npmEntriesInfoPath: string = path.join(process.env.cachePath, NPMENTRIES_TXT); 72 validateFilePathLength(npmEntriesInfoPath, logger); 73 let entriesInfo: string = ''; 74 for (const value of entryInfos.values()) { 75 const buildPath: string = 76 value.buildPath.replace(toUnixPath(projectConfig.nodeModulesPath), '').replace(new RegExp(NODE_MODULES, 'g'), PACKAGES); 77 const entryFile: string = toUnixPath(path.join(buildPath, value.entry)); 78 const entry: string = entryFile.substring(0, entryFile.lastIndexOf('.')).replace(new RegExp(NODE_MODULES, 'g'), PACKAGES); 79 entriesInfo += 80 `${toUnixPath(path.join(PACKAGES, buildPath))}:${toUnixPath(path.join(PACKAGES, entry))}\n`; 81 } 82 fs.writeFileSync(npmEntriesInfoPath, entriesInfo, 'utf-8'); 83} 84 85function generateAbcCacheFilesInfo(moduleInfos: Array<ModuleInfo>, npmEntriesInfoPath: string, cacheFilePath: string): void { 86 let abcCacheFilesInfo: string = ''; 87 88 // generate source file cache 89 moduleInfos.forEach((info) => { 90 let tempFilePathWithoutExt: string = info.tempFilePath.substring(0, info.tempFilePath.lastIndexOf('.')); 91 let abcCacheFilePath: string = tempFilePathWithoutExt + EXTNAME_PROTO_BIN; 92 abcCacheFilesInfo += `${info.tempFilePath};${abcCacheFilePath}\n`; 93 }); 94 95 // generate npm entries cache 96 let npmEntriesCacheFileWithoutExt: string = npmEntriesInfoPath.substring(0, npmEntriesInfoPath.lastIndexOf('.')); 97 let npmEntriesCacheFilePath: string = npmEntriesCacheFileWithoutExt + EXTNAME_PROTO_BIN; 98 abcCacheFilesInfo += `${npmEntriesInfoPath};${npmEntriesCacheFilePath}\n`; 99 100 fs.writeFileSync(cacheFilePath, abcCacheFilesInfo, 'utf-8'); 101} 102 103export function generateMergedAbc(moduleInfos: Array<ModuleInfo>, entryInfos: Map<string, EntryInfo>, outputABCPath: string) { 104 generateCompileFilesInfo(moduleInfos); 105 generateNpmEntriesInfo(entryInfos); 106 107 const filesInfoPath: string = path.join(process.env.cachePath, FILESINFO_TXT); 108 const npmEntriesInfoPath: string = path.join(process.env.cachePath, NPMENTRIES_TXT); 109 const cacheFilePath: string = path.join(process.env.cachePath, MODULES_CACHE); 110 validateFilePathLength(cacheFilePath, logger); 111 generateAbcCacheFilesInfo(moduleInfos, npmEntriesInfoPath, cacheFilePath); 112 113 const fileThreads = os.cpus().length < 16 ? os.cpus().length : 16; 114 mkdirsSync(projectConfig.buildPath); 115 let genAbcCmd: string = 116 `${initAbcEnv().join(' ')} "@${filesInfoPath}" --npm-module-entry-list "${npmEntriesInfoPath}" --output "${outputABCPath}" --file-threads "${fileThreads}"`; 117 118 projectConfig.patch = projectConfig.patch || false; 119 projectConfig.enableMap = projectConfig.enableMap || false; 120 projectConfig.inOldSymbolTablePath = projectConfig.inOldSymbolTablePath || projectConfig.projectRootPath; 121 122 if (projectConfig.patch) { 123 let oldHapSymbolTable = path.join(projectConfig.inOldSymbolTablePath, PATCH_SYMBOL_TABLE); 124 genAbcCmd += ` --input-symbol-table "${oldHapSymbolTable}" --generate-patch`; 125 } 126 127 if (!projectConfig.enableMap) { 128 genAbcCmd += ` --cache-file "@${cacheFilePath}"`; 129 } else { 130 // when generating map, cache is forbiden to avoid uncomplete symbol table 131 let oldHapSymbolTable = path.join(projectConfig.inOldSymbolTablePath, PATCH_SYMBOL_TABLE); 132 genAbcCmd += ` --dump-symbol-table "${oldHapSymbolTable}"`; 133 } 134 135 logger.debug('gen abc cmd is: ', genAbcCmd); 136 try { 137 if (process.env.watchMode === 'true') { 138 childProcess.execSync(genAbcCmd); 139 } else { 140 const child = childProcess.exec(genAbcCmd); 141 child.on('exit', (code: any) => { 142 if (code === 1) { 143 logger.debug(red, "ArkTS:ERROR failed to execute es2abc", reset); 144 process.exit(FAIL); 145 } 146 }); 147 148 child.on('error', (err: any) => { 149 logger.debug(red, err.toString(), reset); 150 process.exit(FAIL); 151 }); 152 153 child.stderr.on('data', (data: any) => { 154 if (projectConfig.patch) { 155 let patchErr :string[] = 156 data.split(os.EOL).filter(line => line.includes("[Patch]") || line.includes("Error:")); 157 logger.error(red, patchErr.join(os.EOL), reset); 158 } else { 159 logger.error(red, data.toString(), reset); 160 } 161 }); 162 } 163 } catch (e) { 164 logger.debug(red, `ArkTS:ERROR failed to generate abc with filesInfo ${filesInfoPath}. Error message: ${e}`, reset); 165 process.env.abcCompileSuccess = 'false'; 166 if (process.env.watchMode !== 'true') { 167 process.exit(FAIL); 168 } 169 } 170} 171