• 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        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