1/* 2* Copyright (c) 2022 Shenzhen Kaihong Digital Industry Development 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*/ 15const path = require('path'); 16const fs = require('fs'); 17const { Logger } = require('./logger'); 18const { Tool } = require('./tool'); 19const { exit } = require('process'); 20 21function saveMockData(projectPath, analyzeResult) { 22 let ss = JSON.stringify({ 23 projectPath: projectPath, 24 analyzeResult: analyzeResult 25 }, null, 4); 26 fs.writeFileSync(path.join(Tool.CURRENT_TOOL_PATH, 'mock.json'), ss); 27 Logger.err('save mock exit'); 28} 29 30function preProcessResult(analyzeResult) {//把所有路径搞成绝对路径 31 for (let r of analyzeResult) { 32 changePathToAbs(r); 33 } 34} 35 36function changePathToAbs(r) { 37 if (!r.target.startsWith('/')) { 38 if (!path.isAbsolute(r.target)) { 39 r.target = path.join(r.workDir, r.target); 40 } 41 } 42 for (let i = 0; i < r.inputs.length; i++) { 43 if (r.inputs[i].startsWith('"') && r.inputs[i].endsWith('"')) { 44 r.inputs[i] = r.inputs[i].substring(1, r.inputs[i].length - 1); 45 } 46 if (!r.inputs[i].startsWith('/')) { 47 if (!path.isAbsolute(r.inputs[i])) { 48 r.inputs[i] = path.join(r.workDir, r.inputs[i]); 49 } 50 } 51 } 52 for (let i = 0; i < r.includes.length; i++) { 53 if (!r.includes[i].startsWith('/')) { 54 if (!path.isAbsolute(r.includes[i])) { 55 r.includes[i] = path.join(r.workDir, r.includes[i]); 56 } 57 } 58 } 59} 60 61function checkoutLibName(name) {//比如/home/libabc.so,返回['dynamic',abc] 62 let pn = path.parse(name); 63 let tname = pn.base; 64 if (tname.endsWith('.a')) { 65 tname = tname.substring(0, tname.length - 2); 66 if (tname.startsWith('lib')) { 67 tname = tname.substring(3); 68 } 69 return ['static', tname]; 70 } 71 else if (tname.endsWith('.so') || tname.indexOf('.so.') > 0) { 72 tname = tname.substring(0, tname.indexOf('.so')); 73 if (tname.startsWith('lib')) { 74 tname = tname.substring(3); 75 } 76 return ['dynamic', tname]; 77 } 78 else { 79 return ['executable', tname]; 80 } 81} 82 83class GenerateGn { 84 constructor() { 85 } 86 87 static mockGenerate() { 88 let ss = fs.readFileSync(path.join(Tool.CURRENT_TOOL_PATH, 'mock.json'), { encoding: 'utf8' }); 89 let ret = JSON.parse(ss); 90 GenerateGn.generate(ret.projectPath, ret.analyzeResult); 91 Logger.err('generate mock exit'); 92 } 93 94 static generate(projectPath, analyzeResult) { 95 if (Tool.MOCK_TYPE === Tool.MOCK_ENUM.MOCK_RECORD) { 96 saveMockData(projectPath, analyzeResult);//保存mock数据 97 } 98 99 preProcessResult(analyzeResult); 100 101 let genList = {}; 102 for (let ret of analyzeResult) { 103 if (ret) { 104 if (ret.workDir in genList) {//根据目录分类genList,每个目录生成一个BUILD.gn 105 genList[ret.workDir].push(ret); 106 } 107 else { 108 genList[ret.workDir] = [ret]; 109 } 110 } 111 } 112 let num = 0; 113 for (let gnPath in genList) {//genList的key即为需要生成gn的目录 114 Logger.info('%d-------------------generate dir %s'.format(num, gnPath)); 115 GenerateGn.generateGn(gnPath, genList, projectPath); 116 num++; 117 } 118 GenerateGn.generateTargetGroup(projectPath); 119 Logger.info('-------------------generate gn ok'); 120 } 121 static COLLECT_TARGET = { 122 static: [], 123 dynamic: [], 124 executable: [] 125 }; 126 static generateTargetGroup(projectPath) { 127 let gnName = path.join(projectPath, 'BUILD.gn'); 128 let gnStr = 'import("//build/ohos.gni")\n\n'; 129 if (fs.existsSync(gnName)) { 130 gnStr = fs.readFileSync(gnName, { encoding: 'utf8' }); 131 } 132 133 let staticTargets = []; 134 for (let t of GenerateGn.COLLECT_TARGET.static) { 135 staticTargets.push(Tool.swapPath(t.path) + ':' + t.name); 136 } 137 let dynamicTargets = []; 138 for (let t of GenerateGn.COLLECT_TARGET.dynamic) { 139 dynamicTargets.push(Tool.swapPath(t.path) + ':' + t.name); 140 } 141 let executableTargets = []; 142 for (let t of GenerateGn.COLLECT_TARGET.executable) { 143 executableTargets.push(Tool.swapPath(t.path) + ':' + t.name); 144 } 145 146 gnStr += ` 147group("all_targets") { 148 deps = [`; 149 if (staticTargets.length > 0) { 150 gnStr += ` 151 #静态库 152 "%s", 153`.format(staticTargets.join('",\n "')); 154 } 155 if (dynamicTargets.length > 0) { 156 gnStr += ` 157 #动态库 158 "%s", 159`.format(dynamicTargets.join('",\n "')); 160 } 161 if (executableTargets.length > 0) { 162 gnStr += ` 163 #可执行程序 164 "%s", 165`.format(executableTargets.join('",\n "')); 166 } 167 gnStr += ` 168 ] 169} 170`; 171 while (gnStr.indexOf(Tool.OHOS_PROJECT_PATH) >= 0) { 172 gnStr = gnStr.replace(Tool.OHOS_PROJECT_PATH, '/'); 173 } 174 fs.writeFileSync(gnName, gnStr, { encoding: 'utf8' }); 175 } 176 static genTargetStr(targetName) { 177 let result; // 声明一个变量来存储返回结果 178 switch (targetName[0]) { 179 case 'static': 180 result = 'ohos_static_library("' + targetName[1] + '")'; 181 break; 182 case 'dynamic': 183 result = 'ohos_shared_library("' + targetName[1] + '")'; 184 break; 185 case 'executable': 186 result = 'ohos_executable("' + targetName[1] + '")'; 187 break; 188 default: 189 // 为未处理的情况提供一个默认返回值 190 result = 'unknown_target_type'; // 或者其他适当的默认值 191 } 192 return result; // 确保函数返回一个值 193 } 194 static genCollectDetail(gen, genList) { 195 let collectDetails = { 196 cflags: new Set(), 197 cflagsCc: new Set(), 198 sources: new Set(), 199 includeDirs: new Set(), 200 defines: new Set(), 201 deps: new Set(), 202 }; 203 for (let absDepTarget of gen.inputs) { 204 GenerateGn.collectFromGList(absDepTarget, genList, collectDetails); 205 } 206 return collectDetails; 207 } 208 static genTargetStr2(collectDetails, targetStr, targetName) { 209 if (collectDetails.cflags.size > 0 || collectDetails.cflagsCc.size > 0) {//放到config里面才生效 210 let configDetail = ''; 211 let removeConfigs = ''; 212 if (collectDetails.cflags.size > 0) { 213 configDetail += GenerateGn.genDetail('cflags', collectDetails.cflags); 214 } 215 if (collectDetails.cflagsCc.size > 0) { 216 configDetail += GenerateGn.genDetail('cflags_cc', collectDetails.cflagsCc); 217 } 218 if (collectDetails.cflags.has('-frtti')) { 219 removeConfigs += ` "//build/config/compiler:no_rtti",\n`; 220 } 221 if (collectDetails.cflags.has('-fexceptions')) { 222 removeConfigs += ` "//build/config/compiler:no_exceptions",\n`; 223 } 224 targetStr = `config("%s_config") {%s} 225 226%s 227remove_configs = [ 228%s ] 229configs = [ ":%s_config" ] 230`.format(targetName[1], configDetail, targetStr, removeConfigs, targetName[1]); 231 } 232 if (collectDetails.sources.size > 0) { 233 targetStr += GenerateGn.genDetail('sources', collectDetails.sources); 234 } 235 if (collectDetails.includeDirs.size > 0) { 236 targetStr += GenerateGn.genDetail('include_dirs', collectDetails.includeDirs); 237 } 238 if (collectDetails.defines.size > 0) { 239 targetStr += GenerateGn.genDetail('defines', collectDetails.defines); 240 } 241 if (collectDetails.deps.size > 0) { 242 targetStr += GenerateGn.genDetail('deps', collectDetails.deps); 243 } 244 245 targetStr += ` 246part_name = "%s" 247subsystem_name = "%s" 248} 249 250`.format(Tool.OHOS_PART_NAME, Tool.OHOS_SUBSYSTEM_NAME); 251 return targetStr; 252 } 253 static generateGn(gnPath, genList, projectPath) { 254 if (!gnPath.startsWith(projectPath)) { 255 Logger.err('target path not in project path\ntarget:%s\nproject:%s'.format(gnPath, projectPath)); 256 } 257 let gnStr = 'import("//build/ohos.gni")\n\n'; 258 let targetCount = 0; 259 let gens = genList[gnPath]; 260 for (let gen of gens) {//枚举在gnPath下的目标 261 if (gen.isLink) {//不是链接,不生成目标 262 let targetName = checkoutLibName(gen.target); 263 let targetStr = GenerateGn.genTargetStr(targetName) + '\n{'; 264 265 GenerateGn.COLLECT_TARGET[targetName[0]].push({ 266 path: gnPath, 267 name: targetName[1] 268 }); 269 let collectDetails = GenerateGn.genCollectDetail(gen, genList); 270 targetStr = GenerateGn.genTargetStr2(collectDetails, targetStr, targetName); 271 272 gnStr += targetStr; 273 targetCount++; 274 } 275 } 276 277 if (targetCount > 0) { 278 let gnName = path.join(gnPath, 'BUILD.gn'); 279 Logger.info('输出:' + gnName + '\n' + gnStr); 280 fs.writeFileSync(gnName, gnStr, { encoding: 'utf8' }); 281 } else { 282 Logger.info(' no target'); 283 } 284 } 285 286 static genDetail(name, detail) { 287 let ss = ''; 288 for (let s of detail) { 289 s = Tool.swapPath(s); 290 if (ss.length > 0) { 291 ss += '",\n "'; 292 } 293 ss += s; 294 } 295 let ret = ` 296 %s = [ 297 "%s" 298 ] 299`.format(name, ss); 300 301 while (ret.indexOf(Tool.OHOS_PROJECT_PATH) >= 0) { 302 ret = ret.replace(Tool.OHOS_PROJECT_PATH, '/'); 303 } 304 return ret; 305 } 306 307 static searchLib(name, genList) { 308 for (let gnPath in genList) { 309 let gens = genList[gnPath]; 310 for (let gen of gens) { 311 if (gen.target === name) { 312 let tt = checkoutLibName(gen.target); 313 return gen.workDir + ':' + tt[1]; 314 } 315 } 316 } 317 return null; 318 } 319 320 static collectGen2(gen, collectDetails) { 321 let collectFileStat = 0; 322 for (let a of gen.cflags) { 323 let badd = true; 324 switch (collectFileStat) { 325 case 0: 326 if (a === '-Xclang') { 327 collectFileStat = 1; 328 badd = false; 329 } 330 break; 331 case 1: 332 if (a === '-include' || a === '-include-pch') { 333 collectFileStat = 2; 334 badd = false; 335 } 336 else { 337 collectDetails.cflags.add('-Xclang'); 338 collectFileStat = 0; 339 } 340 break; 341 case 2: 342 if (a === '-Xclang') { 343 collectFileStat = 3; 344 badd = false; 345 } 346 else { 347 collectFileStat = 0; 348 } 349 break; 350 case 3://预编译文件加入进来一起编译 351 GenerateGn.collectFromGList(a, genList, collectDetails); 352 collectFileStat = 0; 353 badd = false; 354 break; 355 } 356 357 if (badd) { 358 collectDetails.cflags.add(a); 359 } 360 } 361 } 362 static collectGen(gen, genList, collectDetails) { 363 for (let i of gen.inputs) { 364 if (!(i.endsWith('.c') || i.endsWith('.cpp') || i.endsWith('.cxx') || i.endsWith('.cc'))) { 365 GenerateGn.collectFromGList(i, genList, collectDetails); 366 } else { 367 collectDetails.sources.add(i); 368 for (let a of gen.includes) { 369 collectDetails.includeDirs.add(a); 370 } 371 for (let a of gen.defines) { 372 collectDetails.defines.add(a); 373 } 374 GenerateGn.collectGen2(gen, collectDetails); 375 console.log(gen); 376 for (let a of gen.cflagsCc) { 377 collectDetails.cflagsCc.add(a); 378 } 379 } 380 } 381 } 382 static collectFromGList(name, genList, collectDetails) {//获取依赖 383 if (name.endsWith('.a') || name.endsWith('.so') || name.indexOf('.so.') > 0) { 384 let dep = GenerateGn.searchLib(name, genList); 385 if (dep) {//找到依赖,设置依赖 386 collectDetails.deps.add(dep); 387 } 388 else {//没找到,让用户判断,依赖识别 389 collectDetails.deps.add(name); 390 } 391 return; 392 } 393 if (name.endsWith('.c')) { 394 collectDetails.sources.add(name); 395 } 396 for (let gnPath in genList) { 397 let gens = genList[gnPath]; 398 for (let gen of gens) { 399 if (name.endsWith(gen.target)) { 400 GenerateGn.collectGen(gen, genList, collectDetails); 401 } 402 } 403 } 404 } 405} 406 407module.exports = { 408 GenerateGn 409}; 410