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