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