• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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