• 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");
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