• 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 { Logger } = require("./logger");
16const fs = require("fs");
17const path = require("path");
18const { Tool } = require("./tool");
19
20class AnalyzeCommand {
21    constructor() {
22
23    }
24
25    static isCmd(cmd, name) {
26        let cmdName = cmd.split(" ")[0];
27        return cmdName.endsWith(name) || cmdName.endsWith(name + ".exe");
28    }
29
30    static COMPILE_CMDS = {
31        "clang": 0,
32        "ar": 1,
33        "clang++": 2
34    };
35
36    static COLLECT_COMMANDS = [];
37    static storeCommands() {
38        fs.writeFileSync(path.join(Tool.CURRENT_TOOL_PATH, "cmds.txt"),
39            AnalyzeCommand.COLLECT_COMMANDS.join("\n"), { encoding: "utf8" });
40    }
41    static getCompileCmdId(cmd) {
42        let cmdName = cmd.split(" ")[0];
43        for (let c in AnalyzeCommand.COMPILE_CMDS) {
44            if (cmdName.endsWith(c) || cmdName.endsWith(c+".exe")) {
45                return AnalyzeCommand.COMPILE_CMDS[c];//返回命令ID
46            }
47        }
48        return -1;
49    }
50    static analyze(cmd) {
51        let cmds;
52        if (cmd.indexOf("&&") >= 0) {
53            cmds = cmd.split("&&");
54        }
55        else {
56            cmds = [cmd];
57        }
58        let result = [];
59        Tool.backupd(0);
60        for (let c of cmds) {
61            let ret = AnalyzeCommand.analyzeOneCmd(c);
62            if (ret) {
63                result.push(...ret);
64            }
65        }
66        Tool.recoverd(0);
67        return result;
68    }
69    static exAnalyzeCmake(cmd) {
70        let ss = cmd.split(" ");
71        if (ss.indexOf("-P") > 0) {//需要 cmake执行脚本,在这里直接执行 or 移到BUILD.gn里面执行
72            const childProcess = require("child_process");
73            childProcess.execSync(cmd);
74            AnalyzeCommand.COLLECT_COMMANDS.push(cmd);
75            return false;
76        }
77        let cmakeLinkScriptOffset = ss.indexOf("cmake_link_script");
78        if (cmakeLinkScriptOffset >= 0) {//需要 这里可能要做一些错误判断
79            let cmakeLinkScriptFile = ss[cmakeLinkScriptOffset + 1];
80            let cmakeLinkScriptData = fs.readFileSync(path.join(process.cwd(), cmakeLinkScriptFile),
81                { encoding: "utf8" });
82            let cmds = cmakeLinkScriptData.split("\n");//link.txt中可能有多条命令链接
83            let rets = []
84            for (let c of cmds) {
85                let r = AnalyzeCommand.analyzeOneCmd(c);
86                if (r) {
87                    rets.push(...r);
88                }
89            }
90            if (rets.length > 0) {
91                return rets;
92            }
93            else {
94                return false;
95            }
96        }
97        return false;
98    }
99    static analyzeOneCmd(cmd) {
100        while (cmd.startsWith("\n") || cmd.startsWith(" ")) {
101            cmd = cmd.substring(1);
102        }
103        if (cmd.length <= 0) {
104            return false;
105        }
106        if (cmd.match("^make(\\[\\d+\\]:)|: (Entering)|(Leaving) directory")) {//跳过进出目录的log
107            //需要 改变工作目录
108            return false;
109        }
110        if (cmd.startsWith("cd ")) {
111            let t = AnalyzeCommand.splitString(cmd);
112            Tool.pushd(t[1]);//改变工作目录
113            return false;
114        }
115        if (AnalyzeCommand.isCmd(cmd, "ccache")) {//去掉ccache头
116            cmd = cmd.substring(cmd.indexOf("ccache") + "ccache".length);
117            return AnalyzeCommand.analyzeOneCmd(cmd);
118        }
119        if (AnalyzeCommand.isCmd(cmd, "cmake")) {//跳过cmake的log,需要解析link命令行
120            return AnalyzeCommand.exAnalyzeCmake(cmd);
121        }
122        if (AnalyzeCommand.isCmd(cmd, "make") ||
123            AnalyzeCommand.isCmd(cmd, "ranlib")) {//跳过这些命令
124            return false;
125        }
126        if (AnalyzeCommand.getCompileCmdId(cmd) >= 0) {//解析编译命令行
127            AnalyzeCommand.COLLECT_COMMANDS.push(cmd);
128            return [AnalyzeCommand.analyzeCompileCommand(cmd)];
129        }
130        if (AnalyzeCommand.isCmd(cmd, "python3")) {
131            // 需要即时执行(可能会生成依赖源文件),如果不执行,后续编译命令可能会报错,找不到源文件
132            Logger.info(cmd);
133            const childProcess = require("child_process");
134            childProcess.execSync(cmd);
135            return false;
136        }
137        Logger.err("未解析的命令行:" + cmd);
138        return false;
139    }
140
141    static resultTemplete() {//解析命令行之后的结果模板
142        return {
143            type: 0,//0 compile command,1 other command
144            workDir: process.cwd(),
145            command: "",
146            inputs: [],
147            target: "",
148            isLink: false,//是否编译,.a/.o/可执行程序,需要生成目标
149            includes: [],
150            defines: [
151                "_XOPEN_SOURCE=600",//ohos的编译环境缺失宏
152                "FE_TONEAREST=0x00000000",
153                "FE_UPWARD=0x00400000",
154                "FE_DOWNWARD=0x00800000",
155                "FE_TOWARDZERO=0x00c00000",
156            ],
157            cflags: [
158                "-Wno-implicit-function-declaration",
159                "-Wno-unused-function",
160                "-Wno-comments",//允许注释后面有个\
161                "-Wno-string-conversion",//允许char*当做bool使用
162                "-Wno-header-hygiene",//不检测命名空间污染
163                "-frtti",//支持typeid(xxx)
164                "-fexceptions",//支持try catch
165            ],//c和c++选项
166            cflagsCc: [],//c++选项
167            cflagsC: [],//c选项
168        }
169    }
170
171    static splitString(s) {//按空格分割字符串
172        let ret = [];
173        let startp = -1;
174        for (let p = 0; p < s.length; p++) {
175            if (startp >= 0) {
176                if (s[p] == ' ') {
177                    ret.push(s.substring(startp, p));
178                    startp = -1;
179                }
180            }
181            else if (s[p] != ' ') {
182                startp = p;
183            }
184        }
185        if (startp >= 0) {
186            ret.push(s.substring(startp));
187        }
188        return ret;
189    }
190
191    static mockTarget(t) {
192        const childProcess = require("child_process");
193        childProcess.execSync("echo a >" + t.target);
194    }
195    static clangCheck1(e) {
196        if (e.startsWith("--sysroot=") ||
197            e.startsWith("-pthread") ||
198            e.startsWith("-Qunused-arguments") ||
199            e.startsWith("-ffunction-sections") ||
200            e.startsWith("-fdata-sections") ||
201            e.startsWith("-fvisibility=hidden") ||
202            e.startsWith("-fvisibility-inlines-hidden") ||
203            e.startsWith("-O3") ||
204            e.startsWith("-fPIC") ||
205            e.startsWith("-pedantic") ||
206            e.startsWith("-fwrapv") ||
207            e.startsWith("-lm") ||
208            e.startsWith("-lpthread") ||
209            e.startsWith("-shared") ||
210            e.startsWith("-lz") ||
211            e.startsWith("-MD") ||
212            e == "-w") {//-----直接忽略的编译参数(和链接参数)
213            return true;
214        }
215        return false;
216    }
217    static clangCheck2(local, e) {
218        if (e.startsWith("-MT") || e.startsWith("-MF")) {
219            if (e.length == 3) {
220                local.p++;
221            }
222            return true;
223        }
224        return false;
225    }
226    static clangCheck3(local, e) {
227        if (e.startsWith("-D")) {//需要记录到defines里面的参数
228            //需要 是否-D开头的,全部记录到defines里面
229            if (e.length == 2) {//-D xxx
230                local.ret.defines.push(local.eles[local.p++]);
231            }
232            else {//-Dxxx
233                local.ret.defines.push(e.substring(2));
234            }
235            return true;
236        }
237        return false;
238    }
239    static clangCheck4(local, e) {
240        if (e.startsWith("-I")) {//需要记录到includes的参数
241            if (e.length == 2) {//-I xxx
242                local.ret.includes.push(local.eles[local.p++]);
243            }
244            else {//-Ixxx
245                local.ret.includes.push(e.substring(2));
246            }
247            return true;
248        }
249        return false;
250    }
251    static clangCheck5(local, e) {
252        if (e.startsWith("--target=") ||
253            e == "-D__clang__" ||
254            e.startsWith("-march=") ||
255            e.startsWith("-mfloat-abi=") ||
256            e.startsWith("-mfpu=") ||
257            e.startsWith("-fsigned-char") ||
258            e.startsWith("-fdiagnostics-show-option")) {//需要记录到flags里面的参数
259            local.ret.cflags.push(e);
260            return true;
261        }
262        return false;
263    }
264    static clangCheck6(local, e) {
265        if (e == "-o") {
266            if (e.length == 2) {//-o xxx
267                local.ret.target = local.eles[local.p++];
268            }
269            else {//-oxxx
270                local.ret.target = e.substring(2);
271            }
272            if (local.ret.target.endsWith(".a") ||
273                local.ret.target.endsWith(".so") ||
274                (!e.endsWith(".c") && !e.endsWith(".o"))) {
275                local.ret.isLink = true;
276            }
277            return true;
278        }
279        return false;
280    }
281
282    static clangCheck7(local, e) {
283        if (e.endsWith(".c") ||
284            e.endsWith(".o") ||
285            e.endsWith('.o"') ||
286            e.endsWith(".a") ||
287            e.endsWith(".S") ||
288            e.endsWith(".so")) {
289                local.ret.inputs.push(e);
290            return true;
291        }
292        return false;
293    }
294
295    static analyzeCcClang(cmd) {
296        let local = {
297            ret: AnalyzeCommand.resultTemplete(),
298            eles: AnalyzeCommand.splitString(cmd),
299            p: 0,
300        }
301        while (local.p < local.eles.length) {
302            let e = local.eles[local.p++];
303            if (e.endsWith("clang") || e.endsWith("clang.exe")) {
304                local.ret.command = e;
305            }
306            else if (AnalyzeCommand.clangCheck1(e)) { }
307            else if (AnalyzeCommand.clangCheck2(local, e)) { }
308            else if (e.startsWith("-Wl,--dynamic-linker,") || e.startsWith("-rdynamic")) {//-----直接忽略的链接参数
309                local.ret.isLink = true;
310            }
311            else if (AnalyzeCommand.clangCheck3(local, e)) { }
312            else if (AnalyzeCommand.clangCheck4(local, e)) { }
313            else if (AnalyzeCommand.clangCheck5(local, e)) { }
314            else if (e.startsWith("-std=")) {
315                local.ret.cflagsCc.push(e);
316            }
317            else if (e.startsWith("-W")) {//需要 -W开头的怎么处理,-W -Wall -Werror=return-type -Wno-unnamed-type-template-args
318                if (e.startsWith("-Wno-")) {
319                    local.ret.cflags.push(e);
320                }
321            }
322            else if (AnalyzeCommand.clangCheck6(local, e)) { }
323            else if (e == "-c") {//编译
324                local.ret.isLink = false;
325            }
326            else if (AnalyzeCommand.clangCheck7(local, e)) { }
327            else {
328                Logger.err(cmd + "\nclang未解析参数 " + e);
329                process.exit();
330            }
331        }
332        Logger.info("----clang-----" + local.ret.workDir + "\n\t" + local.ret.isLink + "," + local.ret.target)
333        return local.ret;
334    }
335
336    static analyzeCcAr(cmd) {
337        let ret = AnalyzeCommand.resultTemplete();
338        let eles = AnalyzeCommand.splitString(cmd);
339        ret.isLink = true;
340        let p = 0;
341        while (p < eles.length) {
342            let e = eles[p++];
343            if (e.endsWith("ar")) {
344                ret.command = e;
345            }
346            else if (e.endsWith(".a")) {
347                ret.target = e;
348            }
349            else if (e.endsWith(".o")) {
350                ret.inputs.push(e);
351            }
352            else if (e == "qc") {
353            }
354            else {
355                Logger.err(cmd + "\nar未解析参数 " + e);
356                process.exit();
357            }
358        }
359        Logger.info("---ar----" + ret.workDir + "\n\t" + ret.isLink + "," + ret.target);
360        return ret;
361    }
362
363    static clangxxCheck1(e) {
364        let ss = ["--sysroot=",
365            "-pthread",
366            "-Qunused-arguments",
367            "-ffunction-sections",
368            "-fdata-sections",
369            "-fvisibility=hidden",
370            "-fvisibility-inlines-hidden",
371            "-funwind-tables",
372            "-fwrapv",
373            "-O3",
374            "-fPIC",
375            "-shared",
376            "-ldl",
377            "-lm",
378            "-lpthread",
379            "-lrt",
380            "-fPIE",
381            "-g",
382            "-ftemplate-depth=1024",
383            "-pedantic-errors"
384        ];
385        for (let s of ss) {
386            if (e.startsWith(s)) {
387                return true;
388            }
389        }
390        if (e == "-w") {//-----直接忽略的编译参数(和链接参数)
391            return true;
392        }
393        return false;
394    }
395    static clangxxCheck2(local, e) {
396        if (e.startsWith("-isystem")) {//需要 不清楚这个有什么用
397            if (e == "-isystem") {//-isystem xxxx
398                local.ret.includes.push(local.eles[local.p++]);
399            }
400            else {//-Ixxx
401                local.ret.includes.push(e.substring(2));
402            }
403            return true;
404        }
405        return false;
406    }
407    static clangxxCheck3(local, e) {
408        if (e.startsWith("-D")) {//需要记录到defines里面的参数
409            //需要 是否-D开头的,全部记录到defines里面
410            if (e.length == 2) {//-D xxx
411                local.ret.defines.push(local.eles[local.p++]);
412            }
413            else {//-Dxxx
414                local.ret.defines.push(e.substring(2));
415            }
416            return true;
417        }
418        return false;
419    }
420    static clangxxCheck4(local, e) {
421        if (e.startsWith("-I")) {//需要记录到includes的参数
422            if (e.length == 2) {//-I xxx
423                local.ret.includes.push(local.eles[local.p++]);
424            }
425            else {//-Ixxx
426                local.ret.includes.push(e.substring(2));
427            }
428            return true;
429        }
430        return false;
431    }
432    static clangxxCheck5(local, e) {
433        if (e.startsWith("--target=") ||
434            e.startsWith("-march=") ||
435            e.startsWith("-mfloat-abi=") ||
436            e.startsWith("-mfpu=") ||
437            e.startsWith("-fsigned-char") ||
438            e.startsWith("-ffast-math") ||
439            e.startsWith("-fdiagnostics-show-option")) {//需要记录到flags里面的参数
440            local.ret.cflags.push(e);
441            return true;
442        }
443        return false;
444    }
445    static clangxxCheck6(local, e) {
446        if (e.startsWith("-Xclang")) {//透传参数
447            let v = local.eles[local.p++];
448            if (v != "-emit-pch") {//需要丢弃这个选项
449                local.ret.cflags.push(e);
450                local.ret.cflags.push(v);
451            }
452            return true;
453        }
454        return false;
455    }
456    static clangxxCheck7(local, e) {
457        if (e.startsWith("-W")) {//需要 -W开头的怎么处理,-W -Wall -Werror=return-type -Wno-unnamed-type-template-args
458            if (e.startsWith("-Wno-")) {
459                local.ret.cflags.push(e);
460            }
461            return true;
462        }
463        return false;
464    }
465    static clangxxCheck8(local, e) {
466        if (e == "-o") {
467            if (e.length == 2) {//-o xxx
468                local.ret.target = local.eles[local.p++];
469            }
470            else {//-oxxx
471                local.ret.target = e.substring(2);
472            }
473            if (local.ret.target.endsWith(".a") ||
474                local.ret.target.endsWith(".so") ||
475                (!e.endsWith(".c") && !e.endsWith(".o"))) {
476                local.ret.isLink = true;
477            }
478            return true;
479        }
480        return false;
481    }
482    static clangxxCheck9(local, e) {
483        if (e.endsWith(".cpp") ||
484            e.endsWith(".cxx") ||
485            e.endsWith(".cc") ||
486            e.endsWith(".o") ||
487            e.endsWith(".z") ||
488            e.endsWith(".so") ||
489            e.indexOf(".so.") > 0 ||
490            e.endsWith(".a")) {
491            local.ret.inputs.push(e);
492            return true;
493        }
494        return false;
495    }
496    static analyzeCcClangxx(cmd) {
497        let local = {
498            ret: AnalyzeCommand.resultTemplete(),
499            eles: AnalyzeCommand.splitString(cmd),
500            p: 0,
501        }
502        while (local.p < local.eles.length) {
503            let e = local.eles[local.p++];
504            if (e.endsWith("clang++")) {
505                local.ret.command = e;
506            }
507            else if (AnalyzeCommand.clangxxCheck1(e)) { }
508            else if (e.startsWith("-fno-rtti")) {
509                local.ret.cflags.splice(local.ret.cflags.indexOf("-frtti"), 1);
510            }
511            else if (e.startsWith("-fno-exceptions")) {
512                local.ret.cflags.splice(local.ret.cflags.indexOf("-fexceptions"), 1);
513            }
514            else if (AnalyzeCommand.clangxxCheck2(local, e)) { }
515            else if (AnalyzeCommand.clangxxCheck3(local, e)) { }
516            else if (AnalyzeCommand.clangxxCheck4(local, e)) { }
517            else if (AnalyzeCommand.clangxxCheck5(local, e)) { }
518            else if (AnalyzeCommand.clangxxCheck6(local, e)) { }
519            else if (e.startsWith("-std=")) {
520                local.ret.cflagsCc.push(e);
521            }
522            else if (AnalyzeCommand.clangxxCheck7(local, e)) { }
523            else if (AnalyzeCommand.clangxxCheck8(local, e)) { }
524            else if (e == "-c") {//编译
525                local.ret.isLink = false;
526            }
527            else if (AnalyzeCommand.clangxxCheck9(local, e)) { }
528            else {
529                Logger.err(cmd + "\nclang++未解析参数 " + e);
530                process.exit();
531            }
532        }
533        Logger.info("---clang++----" + local.ret.workDir + "\n\t" + local.ret.isLink + "," + local.ret.target)
534        return local.ret;
535    }
536
537    static analyzeCompileCommand(cmd) {
538        //整理命令行
539        while (cmd.indexOf("\\\n") >= 0) {//去除\换行
540            cmd = cmd.replace("\\\n", "");
541        }
542        while (cmd.indexOf("\t") >= 0) {//所有tab换成空格
543            cmd = cmd.replace("\t", " ");
544        }
545        while (cmd.endsWith("\n") || cmd.endsWith(" ")) {
546            cmd = cmd.substring(0, cmd.length - 1);
547        }
548        let ret = null;
549        switch (AnalyzeCommand.getCompileCmdId(cmd)) {
550            case AnalyzeCommand.COMPILE_CMDS["clang"]:
551                ret = AnalyzeCommand.analyzeCcClang(cmd);
552                break;
553            case AnalyzeCommand.COMPILE_CMDS["ar"]:
554                ret = AnalyzeCommand.analyzeCcAr(cmd);
555                break;
556            case AnalyzeCommand.COMPILE_CMDS["clang++"]:
557                ret = AnalyzeCommand.analyzeCcClangxx(cmd);
558                break;
559        }
560        if (ret) {
561            AnalyzeCommand.mockTarget(ret);//解析出的目标,touch一个出来,否则会出现不同Makefile中依赖无法找到的问题
562            return ret;
563        }
564        Logger.err("解析编译命令行失败:" + cmd);
565        return false;
566    }
567}
568module.exports = {
569    AnalyzeCommand
570}