diff --git a/src/vscode_plugin/src/extension.ts b/src/vscode_plugin/src/extension.ts index 8efd6bfb8aa94df79f5c792bb19d899194df1c5b..c48d7f27079d053149ccfbcc1fc6f6a8e517aa19 100644 --- a/src/vscode_plugin/src/extension.ts +++ b/src/vscode_plugin/src/extension.ts @@ -18,6 +18,9 @@ import * as vscode from 'vscode'; import * as path from 'path'; import * as ts from 'typescript'; +import * as fs from 'fs'; +import * as os from 'os'; +import { downloadSdk, extractTarGz, extractZip, crossCompile } from './ohcrosscompile'; import { parseHeaderFile } from './parsec'; import { DtscppRootInfo, GenInfo } from './datatype'; import { parseTsFile } from './parsets'; @@ -53,19 +56,285 @@ export function activate(context: vscode.ExtensionContext) { console.log('Congratulations, your extension "helloworld-sample" is now active!'); const ohcrosscompile = vscode.commands.registerCommand('extension.ohcrosscompile', async (uri) => { - // The code you place here will be executed every time your command is executed - if (uri && uri.fsPath) { - const stat = await vscode.workspace.fs.stat(uri); - if (stat.type === vscode.FileType.Directory) { - vscode.window.showInformationMessage(SELECTED_DIR + uri.fsPath); - } else { - vscode.window.showWarningMessage(SELECTE_DIR); - } + const platform = os.platform(); + // vscode.window.showInformationMessage(`Platform: ${platform}`); + + // 创建选择框,选择要编译的三方库目录 + const thirdPartyPick = vscode.window.createQuickPick(); + thirdPartyPick.title = "OpenHarmony cross compile"; + thirdPartyPick.step = 1; + thirdPartyPick.totalSteps = 5; + thirdPartyPick.placeholder = "Please select the directory of the third-party library you want to compile. "; + thirdPartyPick.items = [{ label: "Browse local files" }]; + thirdPartyPick.onDidAccept(async () => { + const thirdPartyUri = await vscode.window.showOpenDialog({ + canSelectMany: false, //只允许选择一个文件夹 + canSelectFolders: true, //能选择文件夹 + canSelectFiles: false //不能选择文件 + }); + if (thirdPartyUri && thirdPartyUri[0]) { //检查是否选择了文件 + // 获取文件路径 + let thirdPartyPath = ""; + thirdPartyPath = thirdPartyUri[0].path; + if (platform === "win32") { //Windows系统下也可以使用正斜杠的路径,如 /D:/Learning/native,但要去除最前面的/ + thirdPartyPath = thirdPartyPath.slice(1); + } + console.log(`thirdPartyPath: ${thirdPartyPath}`); + + // 创建选项框,选择是编译工具是make还是cmake + const toolPick = vscode.window.createQuickPick(); + toolPick.title = "OpenHarmony cross compile"; + toolPick.step = 2; + toolPick.totalSteps = 5; + toolPick.placeholder = "Please select the way you want to compile: "; + toolPick.items = [{ label: "cmake" }, { label: "make" }]; + toolPick.onDidAccept(async () => { + // 获取编译工具选择的结果 + const compileTool = toolPick.selectedItems[0].label; + // vscode.window.showInformationMessage("You've selected " + compileTool); + + // 创建选项框,选择交叉编译的目标系统架构,是arm64-v8a还是armeabi-v7a + const archPick = vscode.window.createQuickPick(); + archPick.title = "OpenHarmony cross compile"; + archPick.step = 3; + archPick.totalSteps = 5; + archPick.placeholder = "Please select the target system architecture for compilation: "; + archPick.items = [ + { + label: "arm64-v8a", + description: "To compile 64-bit third-party library." + }, + { + label: "armeabi-v7a", + description: "To compile 32-bit third-party library." + } + ]; + archPick.onDidAccept(async () => { + // 获取系统架构选择的结果 + const ohArchitecture = archPick.selectedItems[0].label; + console.log(ohArchitecture); + + // 创建选项框,选择是使用本地sdk还是从网上下载指定版本的sdk + const sourcePick = vscode.window.createQuickPick(); + sourcePick.title = "OpenHarmony cross compile"; + sourcePick.step = 4; + sourcePick.totalSteps = 5; + sourcePick.placeholder = "Please select the SDK you want to use: "; + sourcePick.items = [ + { + label: "Local", + description: "Select the 'native' folder in local OpenHarmony SDK files." + }, + { + label: "Download", + description: "Download a specified version of OpenHarmony SDK from internet." + } + ]; + sourcePick.onDidAccept(async () => { + // 获取sdk来源选择的结果 + const sdkSource = sourcePick.selectedItems[0].label; + console.log(sdkSource); + + let nativePath = ""; + if (sdkSource === "Local") { + const folderUri = await vscode.window.showOpenDialog({ + canSelectMany: false, //只允许选择一个文件夹 + canSelectFolders: true, //能选择文件夹 + canSelectFiles: false //不能选择文件 + }); + if (folderUri && folderUri[0]) { //检查是否选择了文件 + // 获取文件路径 + let folderPath = ""; + let pathNames: string[]; + folderPath = folderUri[0].path; + if (platform === "win32") { + folderPath = folderPath.slice(1); + } + pathNames = folderPath.split('/'); + // 检查所选文件夹是否为native文件夹 + if (pathNames[pathNames.length - 1] !== "native") { + vscode.window.showErrorMessage('Can\'t detect the native folder!'); + } else { + nativePath = folderPath; + + // 选择生成编译配置文件的目录 + const generatePick = vscode.window.createQuickPick(); + generatePick.title = "OpenHarmony cross compile"; + generatePick.step = 5; + generatePick.totalSteps = 5; + generatePick.placeholder = "Please select a folder to store the generated compilation configuration files: "; + generatePick.items = [{ label: "Browse local files" }]; + generatePick.onDidAccept(async () => { + const generateUri = await vscode.window.showOpenDialog({ + canSelectMany: false, + canSelectFolders: true, + canSelectFiles: false + }); + if (generateUri && generateUri[0]) { + let generatePath = ""; + generatePath = generateUri[0].path; + if (platform === "win32") { + generatePath = generatePath.slice(1); + } + console.log(`generatePath: ${generatePath}`); + + crossCompile(platform, undefined, thirdPartyPath, compileTool, ohArchitecture, nativePath, generatePath); + + } else { + vscode.window.showErrorMessage('You haven\'t selected a folder to store the generated compilation configuration files! '); + } + }); + generatePick.show(); + } + } else { + vscode.window.showErrorMessage('No file selected'); + } + + } else if (sdkSource === "Download") { + // 选择下载sdk的版本,并确定对应下载链接 + const versionPick = vscode.window.createQuickPick(); + versionPick.title = "OpenHarmony cross compile"; + versionPick.placeholder = "Please specify the SDK version: "; + versionPick.items = [ + { + label: "API Version 9", + description: "Ohos_sdk_public 3.2.11.9 (API Version 9 Release)", + detail: "Select a folder to install this SDK. It is compatible with OpenHarmony 3.2 Release." + }, + { + label: "API Version 10", + description: "Ohos_sdk_public 4.0.10.13 (API Version 10 Release)", + detail: "Select a folder to install this SDK. It is compatible with OpenHarmony 4.0 Release." + }, + { + label: "API Version 11", + description: "Ohos_sdk_public 4.1.7.5 (API Version 11 Release)", + detail: "Select a folder to install this SDK. It is compatible with OpenHarmony 4.1 Release." + }, + { + label: "API Version 12", + description: "Ohos_sdk_public 5.0.0.71 (API Version 12 Release)", + detail: "Select a folder to install this SDK. It is compatible with OpenHarmony 5.0.0 Release." + }, + ]; + + versionPick.onDidAccept(async () => { + const apiVersion = versionPick.selectedItems[0].label; + let downloadLink = ""; + switch (apiVersion) { + case "API Version 9": + downloadLink = "https://repo.huaweicloud.com/openharmony/os/3.2-Release/ohos-sdk-windows_linux-public.tar.gz"; + break; + case "API Version 10": + downloadLink = "https://repo.huaweicloud.com/openharmony/os/4.0-Release/ohos-sdk-windows_linux-public.tar.gz"; + break; + case "API Version 11": + downloadLink = "https://repo.huaweicloud.com/openharmony/os/4.1-Release/ohos-sdk-windows_linux-public.tar.gz"; + break; + case "API Version 12": + downloadLink = "https://repo.huaweicloud.com/openharmony/os/5.0.0-Release/ohos-sdk-windows_linux-public.tar.gz"; + break; + } + console.log(downloadLink); + const terminal = vscode.window.createTerminal({name: "OpenHarmony cross compile"}); + + // 选择sdk下载路径 + const folderUri = await vscode.window.showOpenDialog({ + canSelectMany: false, + canSelectFolders: true, + canSelectFiles: false + }); + if (folderUri && folderUri[0]) { + // 获取下载文件夹路径,拼装下载文件路径 + let folderPath = ""; + folderPath = folderUri[0].path; + if (platform === "win32") { + folderPath = folderPath.slice(1); + } + const fileName = "ohos-sdk-windows_linux-public.tar.gz"; + let filePath = path.join(folderPath, fileName); + console.log(filePath); + + // 下载并解压sdk中的native + await vscode.window.withProgress({ + location: vscode.ProgressLocation.Notification, + title: "Downloading and installing SDK", + cancellable: false + }, async (progress) => { + // 下载sdk + progress.report({ increment: 0, message: "Start downloading..." }); + + await downloadSdk(downloadLink, filePath, progress); + vscode.window.showInformationMessage(`SDK downloaded to: ${filePath}`); + + // 解压sdk中的native,并拼装nativePath + progress.report({ increment: 10, message: "Download complete. Extracting..." }); + await extractTarGz(filePath, folderPath); + nativePath = folderPath; + if (apiVersion !== "API Version 12") { //api12版本路径中没有ohos-sdk;9-11版本则有 + nativePath = path.join(nativePath, "ohos-sdk"); + } + if (platform === "win32") { + nativePath = path.join(nativePath, "windows"); //windows系统下的nativePath路径 + } else { + nativePath = path.join(nativePath, "linux"); //linux系统下的nativePath路径 + } + for (const file of await fs.promises.readdir(nativePath)) { + if (file.startsWith("native")) { + filePath = path.join(nativePath, file); //获取native压缩包的文件路径 + } + } + await extractZip(platform, terminal, filePath, nativePath); + nativePath = path.join(nativePath, "native"); + vscode.window.showInformationMessage(`SDK (${apiVersion}) installed to: ${folderPath}`); + progress.report({ increment: 100, message: "SDK installation complete." }) + }); + + // 选择生成编译配置文件的目录 + const generatePick = vscode.window.createQuickPick(); + generatePick.title = "OpenHarmony cross compile"; + generatePick.step = 5; + generatePick.totalSteps = 5; + generatePick.placeholder = "Please select a folder to store the compiled binary files: "; + generatePick.items = [{ label: "Browse local files" }]; + generatePick.onDidAccept(async () => { + const generateUri = await vscode.window.showOpenDialog({ + canSelectMany: false, + canSelectFolders: true, + canSelectFiles: false + }); + if (generateUri && generateUri[0]) { + let generatePath = ""; + generatePath = generateUri[0].path; + if (platform === "win32") { + generatePath = generatePath.slice(1); + } + console.log(`generatePath: ${generatePath}`); + + terminal.show(); + crossCompile(platform, terminal, thirdPartyPath, compileTool, ohArchitecture, nativePath, generatePath); + } else { + vscode.window.showErrorMessage('You haven\'t selected a folder to store the compiled binary files! '); + } + }); + generatePick.show(); + } else { + vscode.window.showErrorMessage('You haven\'t selected a folder to install SDK! '); + } + }); + versionPick.show(); + } + }); + sourcePick.show(); + }); + archPick.show(); + }); + toolPick.show(); } else { - vscode.window.showWarningMessage(NO_RES_SELECTED); + vscode.window.showErrorMessage('You haven\'t selected the third-party library folder to compile! '); } - // Display a message box to the user - vscode.window.showInformationMessage('ohcrosscompile!'); + }); + thirdPartyPick.show(); }); // The command has been defined in the package.json file diff --git a/src/vscode_plugin/src/ohcrosscompile.ts b/src/vscode_plugin/src/ohcrosscompile.ts new file mode 100644 index 0000000000000000000000000000000000000000..c8177212e7e86599b4cc67c07a2176c26826f771 --- /dev/null +++ b/src/vscode_plugin/src/ohcrosscompile.ts @@ -0,0 +1,231 @@ +/* +* Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +import * as vscode from 'vscode'; +import * as fs from 'fs'; +import * as https from 'https'; +import * as zlib from 'zlib'; +import * as tar from 'tar'; +import * as unzipper from 'unzipper'; +import * as path from 'path'; + +// 涓嬭浇url鎵鎸囩ず鐨剆dk鏂囦欢锛屽埌destination鎵鎸囩ず鐨勬枃浠朵腑 +export function downloadSdk(url: string, destination: string, progress: vscode.Progress<{ increment: number, message?: string }>): Promise { + return new Promise((resolve, reject) => { + const file = fs.createWriteStream(destination); //鍒涘缓鍐欏叆鏂囦欢娴 + https.get(url, (response) => { + if (response.statusCode === 200) { + const totalSize = parseInt(String(response.headers['content-length'])); + console.log(`totalSize: ${totalSize}`); + let downloadedSize = 0; + response.on('data', (chunk) => { //璁剧疆response鐨刣ata浜嬩欢锛屽綋姣忔帴鏀朵竴涓暟鎹潡鏃讹紝璁$畻涓嬭浇杩涘害骞舵姤鍛 + downloadedSize += chunk.length; + const percentage = (downloadedSize / totalSize) * 100; + + // increment鏄竴涓疮鍔犻噺锛屽簲姣忔绱姞褰撳墠鏁版嵁鍧楀ぇ灏忓崰鎬诲ぇ灏忕殑姣斾緥 + progress.report({ increment: ((chunk.length / totalSize) * 100 * 0.8), message: `Downloading SDK ... ${percentage.toFixed(2)}%` }); + }); + response.pipe(file); + file.on('finish', () => { + file.close(); + resolve(); + }); + } else { + vscode.window.showErrorMessage(`Connection failed! Statuscode: ${response.statusCode}`); + reject(new Error(`Failed to get '${url}' (${response.statusCode})`)); + } + }).on('error', (err) => { + fs.unlink(destination, () => reject(err)); + }); + }); +} + +// 鎻愬彇filePath鎵鎸囩ず鐨.tar.gz鏂囦欢锛屽埌destination鎵鎸囩ず鐨勬枃浠跺す涓 +export function extractTarGz(filePath: string, destination: string): Promise { + return new Promise((resolve, reject) => { + fs.createReadStream(filePath) + .pipe(zlib.createGunzip()) // 瑙e帇 .gz + .pipe(tar.extract({ // 瑙e帇 .tar 鍐呭 + cwd: destination // 瑙e帇鍒版寚瀹氭枃浠跺す + })) + .on('finish', () => resolve()) + .on('error', (err) => reject(err)); + }); +} + +// 鎻愬彇filePath鎵鎸囩ず鐨.zip鏂囦欢锛屽埌destination鎵鎸囩ず鐨勬枃浠跺す涓 +// export function extractZip(filePath: string, destination: string): Promise { +// return new Promise((resolve, reject) => { +// fs.createReadStream(filePath) +// .pipe(unzipper.Extract({ path: destination })) // 瑙e帇 .zip +// .on('close', () => { +// resolve(); +// }) +// .on('error', (err) => reject(err)); +// }); +// } + +// 鍒╃敤缁堢鍛戒护锛屾彁鍙杅ilePath鎵鎸囩ず鐨.zip鏂囦欢锛屽埌destination鎵鎸囩ず鐨勬枃浠跺す涓 +export function extractZip(platform: string, terminal: vscode.Terminal, filePath: string, destination: string): Promise { + return new Promise((resolve, reject) => { + if (platform === "win32") { //Windows + terminal.sendText(`Expand-Archive -Path \"${filePath}\" -DestinationPath \"${destination}\"`); + terminal.processId?.then( + () => { + resolve(); + }, + (err) => { + vscode.window.showErrorMessage(`Error extracting file: ${err}`); + reject(err); + } + ); + } else { //Linux + terminal.sendText(`unzip ${filePath} -d ${destination}`); + terminal.processId?.then( + () => { + resolve(); + }, + (err) => { + vscode.window.showErrorMessage(`Error extracting file: ${err}`); + reject(err); + } + ); + } + }); + + +} + +// windows绯荤粺涓嬪涓夋柟搴撹繘琛屼氦鍙夌紪璇 +function crossCompile_win32(terminal: vscode.Terminal | undefined, thirdPartyPath: string, compileTool: string, ohArchitecture: string, nativePath: string, generatePath: string): Promise { + return new Promise((resolve, reject) => { + + vscode.window.showInformationMessage("Starting compilation on Windows."); + if (terminal === undefined) { //鑻ヤ娇鐢ㄦ湰鍦扮殑sdk锛屼笉杩涜瑙e帇鎿嶄綔锛屽垯terminal涓簎ndefined锛屽湪缂栬瘧鍓嶈繘琛屽垱寤 + terminal = terminal = vscode.window.createTerminal({ + name: "OpenHarmony cross compile", + cwd: thirdPartyPath + }); + } else { //鑻ヤ娇鐢ㄤ笅杞界殑sdk锛岃В鍘嬪畬瑕佸垏鎹㈠埌涓夋柟搴撶洰褰曪紝鍐嶈繘琛屽悗缁紪璇戞搷浣 + const driveLetter = thirdPartyPath.split('/')[0]; //鑾峰彇涓夋柟搴撶洰褰曟墍鍦ㄧ殑椹卞姩鍣ㄧ洏绗︼紝濡俤: + terminal.sendText(`if ($?) {${driveLetter}}`); + terminal.sendText(`if ($?) {cd ${thirdPartyPath}}`); + } + + if (compileTool === "cmake") { + const command1 = "mkdir ohCrossCompile_build"; + const command2 = "cd ohCrossCompile_build"; + const command3 = `${nativePath}/build-tools/cmake/bin/cmake.exe -G "MinGW Makefiles" -DCMAKE_SH="CMAKE_SH-NOTFOUND" -DCMAKE_TOOLCHAIN_FILE=${nativePath}/build/cmake/ohos.toolchain.cmake -DCMAKE_INSTALL_PREFIX=${generatePath} -DOHOS_ARCH=${ohArchitecture} .. -L`; + console.log(command3); + const command4 = "mingw32-make"; + const command5 = "mingw32-make install"; + terminal.sendText(`if ($?) {${command1}} ; if ($?) {${command2}} ; if ($?) {${command3}} ; if ($?) {${command4}} ; if ($?) {${command5}}`); + terminal.processId.then( + () => { + resolve(); + }, + (err) => { + vscode.window.showErrorMessage(`Error occured while compiling. Error: ${err}`); + reject(err); + } + ); + } else if (compileTool === "make") { + let target: string; + if(ohArchitecture === "arm64-v8a") { //64浣嶇郴缁 + target = "aarch64-linux-ohos"; + } else { //32浣嶇郴缁 + target = "arm-linux-ohos"; + } + + const command1 = `mingw32-make CC=\"${nativePath}/llvm/bin/clang.exe --target=${target}\" AR=${nativePath}/llvm/bin/llvm-ar.exe RANDLIB=${nativePath}/llvm/bin/llvm-ranlib.exe`; + const command2 = `mingw32-make install PREFIX=${generatePath}`; + // terminal.sendText(`if ($?) {${command1}} ; if ($?) {${command2}}`); + terminal.sendText(`${command1} ; ${command2}`); + terminal.processId.then( + () => { + resolve(); + }, + (err) => { + vscode.window.showErrorMessage(`Error occured while compiling. Error: ${err}`); + reject(err); + } + ); + } + }); +} + +// linux绯荤粺涓嬪涓夋柟搴撹繘琛屼氦鍙夌紪璇 +function crossCompile_linux(terminal: vscode.Terminal | undefined, thirdPartyPath: string, compileTool: string, ohArchitecture: string, nativePath: string, generatePath: string): Promise { + return new Promise((resolve, reject) => { + vscode.window.showInformationMessage("Starting compilation on Linux."); + // vscode.window.showInformationMessage("Please enter your password in the newly opened terminal to continue."); + if (terminal === undefined) { //鑻ヤ娇鐢ㄦ湰鍦扮殑sdk锛屼笉杩涜瑙e帇鎿嶄綔锛屽垯terminal涓簎ndefined锛屽湪缂栬瘧鍓嶈繘琛屽垱寤 + terminal = terminal = vscode.window.createTerminal({ + name: "OpenHarmony cross compile", + cwd: thirdPartyPath + }); + } else { //鑻ヤ娇鐢ㄤ笅杞界殑sdk锛岃В鍘嬪畬瑕佸垏鎹㈠埌涓夋柟搴撶洰褰曪紝鍐嶈繘琛屽悗缁紪璇戞搷浣 + terminal.sendText(`cd ${thirdPartyPath}`); + } + + if (compileTool === "cmake") { + const command1 = "mkdir ohCrossCompile_build"; + const command2 = "cd ohCrossCompile_build"; + // const command3 = `sudo ${nativePath}/build-tools/cmake/bin/cmake -DCMAKE_TOOLCHAIN_FILE=/${nativePath}/build/cmake/ohos.toolchain.cmake -DCMAKE_INSTALL_PREFIX=${generatePath} -DOHOS_ARCH=${ohArchitecture} .. -L`; + const command3 = `${nativePath}/build-tools/cmake/bin/cmake -DCMAKE_TOOLCHAIN_FILE=/${nativePath}/build/cmake/ohos.toolchain.cmake -DCMAKE_INSTALL_PREFIX=${generatePath} -DOHOS_ARCH=${ohArchitecture} .. -L`; + // const command4 = "sudo make"; + const command4 = "make"; + const command5 = "make install"; + terminal.sendText(`${command1} && ${command2} && ${command3} && ${command4} && ${command5}`); + terminal.processId?.then( + () => { + resolve(); + }, + (err) => { + vscode.window.showErrorMessage(`Error occured while compiling. Error: ${err}`); + reject(err); + } + ); + } else if (compileTool === "make") { + let target: string; + if(ohArchitecture === "arm64-v8a") { //64浣嶇郴缁 + target = "aarch64-linux-ohos"; + } else { //32浣嶇郴缁 + target = "arm-linux-ohos"; + } + const command1 = `sudo make CC=\"${nativePath}/llvm/bin/clang --target=${target}\" AR=${nativePath}/llvm/bin/llvm-ar RANDLIB=${nativePath}/llvm/bin/llvm-ranlib`; + const command2 = `make install PREFIX=${generatePath}`; + // terminal.sendText(`${command1} && ${command2}`); + terminal.sendText(`${command1} ; ${command2}`); + terminal.processId?.then( + () => { + resolve(); + }, + (err) => { + vscode.window.showErrorMessage(`Error occured while compiling. Error: ${err}`); + reject(err); + } + ); + } + }); +} + + +export function crossCompile(platform: string, terminal: vscode.Terminal | undefined, thirdPartyPath: string, compileTool: string, ohArchitecture: string, nativePath: string, generatePath: string) { + if (platform === "win32") { + crossCompile_win32(terminal, thirdPartyPath, compileTool, ohArchitecture, nativePath, generatePath); + } else { + crossCompile_linux(terminal, thirdPartyPath, compileTool, ohArchitecture, nativePath, generatePath); + } +} \ No newline at end of file