• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2* Copyright (c) 2024 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*/
15
16import * as vscode from 'vscode';
17import * as fs from 'fs';
18import * as https from 'https';
19import * as zlib from 'zlib';
20import * as tar from 'tar';
21import { Logger } from './common/log';
22
23const WINDOWS_START = vscode.l10n.t('Starting compilation on Windows.');
24const TERMINAL_TITLE = vscode.l10n.t('OpenHarmony Cross Compile');
25const LINUX_START = vscode.l10n.t('Starting compilation on Linux.');
26
27export function checkNative(platform: string, nativePath: string): boolean {
28    if (platform === "win32") {
29        const cmakePath = nativePath.concat("/build-tools/cmake/bin/cmake.exe");
30        const toolchainPath = nativePath.concat("/build/cmake/ohos.toolchain.cmake");
31        const clangPath = nativePath.concat("/llvm/bin/clang.exe");
32        const arPath = nativePath.concat("/llvm/bin/llvm-ar.exe");
33        const ranlibPath = nativePath.concat("/llvm/bin/llvm-ranlib.exe");
34        return fs.existsSync(cmakePath) && fs.existsSync(toolchainPath) && fs.existsSync(clangPath) && fs.existsSync(arPath) && fs.existsSync(ranlibPath);
35    } else {
36        const cmakePath = nativePath.concat("/build-tools/cmake/bin/cmake");
37        const toolchainPath = nativePath.concat("/build/cmake/ohos.toolchain.cmake");
38        const clangPath = nativePath.concat("/llvm/bin/clang");
39        const arPath = nativePath.concat("/llvm/bin/llvm-ar");
40        const ranlibPath = nativePath.concat("/llvm/bin/llvm-ranlib");
41        return fs.existsSync(cmakePath) && fs.existsSync(toolchainPath) && fs.existsSync(clangPath) && fs.existsSync(arPath) && fs.existsSync(ranlibPath);
42    }
43}
44
45// 下载url所指示的sdk文件,到destination所指示的文件中
46export function downloadSdk(url: string, destination: string, progress: vscode.Progress<{ increment: number, message?: string }>): Promise<void> {
47    return new Promise((resolve, reject) => {
48        // 创建写入文件流
49        const file = fs.createWriteStream(destination);
50        https.get(url, (response) => {
51            if (response.statusCode === 200) {
52                const totalSize = parseInt(String(response.headers['content-length']));
53                Logger.getInstance().debug(`totalSize: ${totalSize}`);
54                let downloadedSize = 0;
55                response.on('data', (chunk) => {
56                    // 设置response的data事件,当每接收一个数据块时,计算下载进度并报告
57                    downloadedSize += chunk.length;
58                    const percentage = (downloadedSize / totalSize) * 100;
59                    progress.report({ increment: ((chunk.length / totalSize) * 100 * 0.8), message: vscode.l10n.t('Downloading SDK ... {0}%', percentage.toFixed(2)) });
60                });
61                response.pipe(file);
62                file.on('finish', () => {
63                    file.close();
64                    resolve();
65                });
66            } else {
67                if (response.statusCode) {
68                    vscode.window.showErrorMessage(vscode.l10n.t('Connection failed! Statuscode: {0}', response.statusCode));
69                    reject(new Error(vscode.l10n.t('Failed to get \'{0}\' ({1})', url, response.statusCode)));
70                }
71
72            }
73        }).on('error', (err) => {
74            fs.unlink(destination, () => reject(err));
75        });
76    });
77}
78
79// 提取filePath所指示的.tar.gz文件,到destination所指示的文件夹中
80export function extractTarGz(filePath: string, destination: string): Promise<void> {
81    return new Promise((resolve, reject) => {
82        fs.createReadStream(filePath)
83            .pipe(zlib.createGunzip())
84            .pipe(tar.extract({
85                cwd: destination
86            }))
87            .on('finish', () => resolve())
88            .on('error', (err) => reject(err));
89    });
90}
91
92// 利用终端命令,提取filePath所指示的.zip文件,到destination所指示的文件夹中
93export function extractZip(platform: string, terminal: vscode.Terminal, filePath: string, destination: string): Promise<void> {
94    return new Promise((resolve, reject) => {
95        if (platform === "win32") {
96            terminal.sendText(`Expand-Archive -Path \"${filePath}\" -DestinationPath \"${destination}\" -Force`);
97            terminal.processId?.then(
98                () => {
99                    resolve();
100                },
101                (err) => {
102                    vscode.window.showErrorMessage(vscode.l10n.t('Error extracting file: {0}', err));
103                    reject(err);
104                }
105            );
106
107        } else {
108            terminal.sendText(`unzip ${filePath} -d ${destination}`);
109            terminal.processId?.then(
110                () => {
111                    resolve();
112                },
113                (err) => {
114                    vscode.window.showErrorMessage(vscode.l10n.t('Error extracting file: {0}', err));
115                    reject(err);
116                }
117            );
118        }
119    });
120}
121
122// windows系统下对三方库进行交叉编译
123function crossCompile_win32(terminal: vscode.Terminal | undefined, thirdPartyPath: string, configPath: string, compileTool: string, ohArchitecture: string[], nativePath: string, ohCrossCompilePath: string): Promise<void> {
124    return new Promise((resolve, reject) => {
125
126        vscode.window.showInformationMessage(WINDOWS_START);
127        if (terminal === undefined) {
128            // 若使用本地的sdk,不进行解压操作,则terminal为undefined,在编译前进行创建
129            terminal = terminal = vscode.window.createTerminal({
130                name: TERMINAL_TITLE,
131            });
132            terminal.show();
133        } else {
134            // 若使用下载的sdk,解压完要切换到三方库目录所在的驱动器盘符,以便进行后续编译操作
135            const driveLetter = thirdPartyPath.split('/')[0];
136            terminal.sendText(`if ($?) {${driveLetter}}`);
137        }
138
139        const configContent = JSON.parse(fs.readFileSync(configPath, 'utf8'));
140
141        // 若配置文件中actions为空,则根据settings设置actions
142        if (configContent.actions === undefined || configContent.actions.length === 0) {
143            let actions = new Array();
144            for (let arch of ohArchitecture) {
145                // 对每个目标系统架构,先组装出commands为空的action
146                let action = {
147                    compileTool: compileTool,
148                    ohArchitecture: arch,
149                    nativePath: nativePath,
150                    thirdPartyPath: thirdPartyPath,
151                    installPath: `${ohCrossCompilePath}/${arch}/installed`,
152                    cwd: "",
153                    commands: []
154                };
155                if (compileTool === "cmake") {
156                    action.cwd = `${ohCrossCompilePath}/${arch}`;
157                } else {
158                    action.cwd = `${thirdPartyPath}`;
159                }
160                actions.push(action);
161            }
162            configContent.actions = actions;
163        }
164
165        // 对配置文件中每个action,若其commands为空,则组装出默认命令
166        for (let action of configContent.actions) {
167            vscode.window.showInformationMessage(vscode.l10n.t('Compiled files of {0} system will be installed at {1}. ', action.ohArchitecture, action.installPath));
168            if (action.commands === undefined || action.commands.length === 0) {
169                let commands = new Array();
170                if (action.compileTool === "cmake") {
171                    commands.push({
172                        command: `cd ${action.cwd}`,
173                        arguments: []
174                    });
175                    commands.push({
176                        command: `${action.nativePath}/build-tools/cmake/bin/cmake.exe`,
177                        arguments: [
178                            "-G \"MinGW Makefiles\"",
179                            "-DCMAKE_SH=\"CMAKE_SH-NOTFOUND\"",
180                            `-DCMAKE_TOOLCHAIN_FILE=${action.nativePath}/build/cmake/ohos.toolchain.cmake`,
181                            `-DCMAKE_INSTALL_PREFIX=${action.installPath}`,
182                            `-DOHOS_ARCH=${action.ohArchitecture}`,
183                            "../..",
184                            "-L"
185                        ]
186                    });
187                    commands.push({
188                        command: "mingw32-make",
189                        arguments: []
190                    });
191                    commands.push({
192                        command: "mingw32-make install",
193                        arguments: []
194                    });
195                } else if (action.compileTool === "make") {
196                    let target: string;
197                    let ld: string;
198                    if (action.ohArchitecture === "arm64-v8a") {
199                        target = "aarch64-linux-ohos";
200                        ld = "ld64.lld.exe";
201                    } else {
202                        target = "arm-linux-ohos";
203                        ld = "ld.lld.exe";
204                    }
205                    commands.push({
206                        command: `cd ${action.cwd}`,
207                        arguments: []
208                    });
209                    if (ohArchitecture.length > 1) {
210                        commands.push({
211                            command: "mingw32-make clean",
212                            arguments: []
213                        });
214                    }
215                    commands.push({
216                        command: "mingw32-make",
217                        arguments: [
218                            `CC=\"${action.nativePath}/llvm/bin/clang.exe --target=${target}\"`,
219                            `LD=${action.nativePath}/llvm/bin/${ld}`,
220                            `AR=${action.nativePath}/llvm/bin/llvm-ar.exe`,
221                            `AS=${action.nativePath}/llvm/bin/llvm-as.exe`,
222                            `RANDLIB=${action.nativePath}/llvm/bin/llvm-ranlib.exe`,
223                            `STRIP=${action.nativePath}/llvm/bin/llvm-strip.exe`
224                        ]
225                    });
226                    commands.push({
227                        command: "mingw32-make install",
228                        arguments: [
229                            `PREFIX=${action.installPath}`
230                        ]
231                    });
232                }
233                action.commands = commands;
234            }
235        }
236        fs.writeFileSync(configPath, JSON.stringify(configContent, null, 4), 'utf8');
237
238        // 把所有actions的命令拼接在一起,送入终端执行
239        let finalCommand = "";
240        for (let action of configContent.actions) {
241            for (let item of action.commands) {
242                finalCommand = finalCommand.concat(item.command);
243                for (let i = 0; i <= item.arguments.length - 1; i++) {
244                    finalCommand = finalCommand.concat(` ${item.arguments[i]}`);
245                }
246                finalCommand = finalCommand.concat(" ; ");
247            }
248        }
249        terminal.sendText(finalCommand);
250        Logger.getInstance().debug(finalCommand);
251        terminal.processId.then(
252            () => {
253                resolve();
254            },
255            (err) => {
256                vscode.window.showErrorMessage(vscode.l10n.t('Error occured while compiling. Error: {0}', err));
257                reject(err);
258            }
259        );
260    });
261}
262
263// linux系统下对三方库进行交叉编译
264function crossCompile_linux(terminal: vscode.Terminal | undefined, thirdPartyPath: string, configPath: string, compileTool: string, ohArchitecture: string[], nativePath: string, ohCrossCompilePath: string): Promise<void> {
265    return new Promise((resolve, reject) => {
266        vscode.window.showInformationMessage(LINUX_START);
267        if (terminal === undefined) {
268            // 若使用本地的sdk,不进行解压操作,则terminal为undefined,在编译前进行创建
269            terminal = terminal = vscode.window.createTerminal({
270                name: TERMINAL_TITLE,
271            });
272            terminal.show();
273        }
274        const configContent = JSON.parse(fs.readFileSync(configPath, 'utf8'));
275
276        // 若配置文件中actions为空,则根据settings设置actions
277        if (configContent.actions === undefined || configContent.actions.length === 0) {
278            let actions = new Array();
279            for (let arch of ohArchitecture) {
280                // 对每个目标系统架构,先组装出commands为空的action
281                let action = {
282                    compileTool: compileTool,
283                    ohArchitecture: arch,
284                    nativePath: nativePath,
285                    thirdPartyPath: thirdPartyPath,
286                    installPath: `${ohCrossCompilePath}/${arch}/installed`,
287                    cwd: "",
288                    commands: []
289                };
290                if (compileTool === "cmake") {
291                    action.cwd = `${ohCrossCompilePath}/${arch}`;
292                } else {
293                    action.cwd = `${thirdPartyPath}`;
294                }
295                actions.push(action);
296            }
297            configContent.actions = actions;
298        }
299
300        // 对配置文件中每个action,若其commands为空,则组装出默认命令
301        for (let action of configContent.actions) {
302            vscode.window.showInformationMessage(vscode.l10n.t('Compiled files of {0} system will be installed at {1}. ', action.ohArchitecture, action.installPath));
303            if (action.commands === undefined || action.commands.length === 0) {
304                let commands = new Array();
305                if (action.compileTool === "cmake") {
306                    commands.push({
307                        command: `cd ${action.cwd}`,
308                        arguments: []
309                    });
310                    commands.push({
311                        command: `${action.nativePath}/build-tools/cmake/bin/cmake`,
312                        arguments: [
313                            `-DCMAKE_TOOLCHAIN_FILE=${action.nativePath}/build/cmake/ohos.toolchain.cmake`,
314                            `-DCMAKE_INSTALL_PREFIX=${action.installPath}`,
315                            `-DOHOS_ARCH=${action.ohArchitecture}`,
316                            "../..",
317                            "-L"
318                        ]
319                    });
320                    commands.push({
321                        command: "make",
322                        arguments: []
323                    });
324                    commands.push({
325                        command: "make install",
326                        arguments: []
327                    });
328                } else if (action.compileTool === "make") {
329                    let target: string;
330                    let ld: string;
331                    if (action.ohArchitecture === "arm64-v8a") {
332                        target = "aarch64-linux-ohos";
333                        ld = "ld64.lld";
334                    } else {
335                        target = "arm-linux-ohos";
336                        ld = "ld.lld";
337                    }
338                    commands.push({
339                        command: `cd ${action.cwd}`,
340                        arguments: []
341                    });
342                    if (ohArchitecture.length > 1) {
343                        commands.push({
344                            command: "make clean",
345                            arguments: []
346                        });
347                    }
348                    commands.push({
349                        command: "make",
350                        arguments: [
351                            `CC=\"${action.nativePath}/llvm/bin/clang --target=${target}\"`,
352                            `LD=${action.nativePath}/llvm/bin/${ld}`,
353                            `AR=${action.nativePath}/llvm/bin/llvm-ar`,
354                            `AS=${action.nativePath}/llvm/bin/llvm-as`,
355                            `RANDLIB=${action.nativePath}/llvm/bin/llvm-ranlib`,
356                            `STRIP=${action.nativePath}/llvm/bin/llvm-strip`
357                        ]
358                    });
359                    commands.push({
360                        command: "make install",
361                        arguments: [
362                            `PREFIX=${action.installPath}`
363                        ]
364                    });
365                } else if (action.compileTool === "configure") {
366                    let target: string;
367                    let ld: string;
368                    if (action.ohArchitecture === "arm64-v8a") {
369                        target = "aarch64-linux-ohos";
370                        ld = "ld64.lld";
371                    } else {
372                        target = "arm-linux-ohos";
373                        ld = "ld.lld";
374                    }
375                    commands.push({
376                        command: `cd ${action.cwd}`,
377                        arguments: []
378                    });
379                    if (ohArchitecture.length > 1) {
380                        commands.push({
381                            command: "make clean",
382                            arguments: []
383                        });
384                    }
385                    commands.push({
386                        command: "./configure",
387                        arguments: [
388                            `--host=${target}`,
389                            `--prefix=${action.installPath}`,
390
391                            //针对libcoap库的编译选项
392                            // `--disable-documentation`,
393                            // `--disable-dtls`,
394
395                            `CFLAGS=\"-pthread\"`,
396                            `LDFLAGS=\"-pthread\"`,
397                            `CC=\"${action.nativePath}/llvm/bin/clang --target=${target}\"`,
398                            `LD=${action.nativePath}/llvm/bin/${ld}`,
399                            `AR=${action.nativePath}/llvm/bin/llvm-ar`,
400                            `AS=${action.nativePath}/llvm/bin/llvm-as`,
401                            `RANDLIB=${action.nativePath}/llvm/bin/llvm-ranlib`,
402                            `STRIP=${action.nativePath}/llvm/bin/llvm-strip`
403                        ]
404                    });
405                    commands.push({
406                        command: "make",
407                        arguments: []
408                    });
409                    commands.push({
410                        command: "make install",
411                        arguments: []
412                    });
413                }
414                action.commands = commands;
415            }
416        }
417        fs.writeFileSync(configPath, JSON.stringify(configContent, null, 4), 'utf8');
418
419        // 把所有actions的命令拼接在一起,送入终端执行
420        let finalCommand = "";
421        for (let action of configContent.actions) {
422            for (let item of action.commands) {
423                finalCommand = finalCommand.concat(item.command);
424                for (let i = 0; i <= item.arguments.length - 1; i++) {
425                    finalCommand = finalCommand.concat(` ${item.arguments[i]}`);
426                }
427                finalCommand = finalCommand.concat(" ; ");
428            }
429        }
430        terminal.sendText(finalCommand);
431        Logger.getInstance().debug(finalCommand);
432        terminal.processId.then(
433            () => {
434                resolve();
435            },
436            (err) => {
437                vscode.window.showErrorMessage(vscode.l10n.t('Error occured while compiling. Error: {0}', err));
438                reject(err);
439            }
440        );
441    });
442}
443
444
445export function crossCompile(platform: string, terminal: vscode.Terminal | undefined, configPath: string, thirdPartyPath: string, compileTool: string, ohArchitecture: string[], nativePath: string, ohCrossCompilePath: string) {
446    if (platform === "win32") {
447        crossCompile_win32(terminal, thirdPartyPath, configPath, compileTool, ohArchitecture, nativePath, ohCrossCompilePath);
448    } else {
449        crossCompile_linux(terminal, thirdPartyPath, configPath, compileTool, ohArchitecture, nativePath, ohCrossCompilePath);
450    }
451}