* Copyright (c) 2025 Huawei Device 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 path from 'path';
import { getDefaultShell, getProjectType } from './utils';
import { resolveKeelsCommand } from './cjmpSdk';
export interface BuildPrompt {
pickBuildType(items: readonly string[], options: vscode.QuickPickOptions): Promise<string | undefined>;
pickBuildMode(items: readonly string[], options: vscode.QuickPickOptions): Promise<string | undefined>;
}
export interface TerminalFactory {
create(options: vscode.TerminalOptions): vscode.Terminal;
}
export interface BuildDependencies {
prompt: BuildPrompt;
terminalFactory: TerminalFactory;
}
const defaultBuildDependencies: BuildDependencies = {
prompt: {
pickBuildType: async (items, options) => vscode.window.showQuickPick(items, options),
pickBuildMode: async (items, options) => vscode.window.showQuickPick(items, options),
},
terminalFactory: {
create: (options) => vscode.window.createTerminal(options),
},
};
export async function buildCjmpProject(
dependencies: BuildDependencies = defaultBuildDependencies,
): Promise<void> {
const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
if (workspaceRoot) {
console.log('The root directory of the workspace: ', workspaceRoot);
} else {
vscode.window.showErrorMessage('No folder or workspace is opened.');
return;
}
const projectType = getProjectType();
if (projectType === 'app') {
await buildAppProject(workspaceRoot, dependencies);
} else if (
projectType === 'library'
|| projectType === 'logic-module'
|| projectType === 'module'
) {
await buildPackageProject(workspaceRoot, dependencies);
} else {
return;
}
}
function resolveTargetPlatform(type: string): string | undefined {
const platformMap = new Map<string, string>([
['apk', 'android-arm64'],
['hap', 'ohos-arm64'],
['ios', 'ios-arm64'],
['ipa', 'ios-arm64'],
['ios-sim', 'ios-sim-arm64'],
['aar', 'android-arm64'],
['har', 'ohos-arm64'],
['framework', 'ios-arm64'],
['framework-sim', 'ios-sim-arm64'],
]);
return platformMap.get(type);
}
async function buildAppProject(workspaceRoot: string, dependencies: BuildDependencies) {
const modeOptions = ['debug', 'release'];
const types = process.platform === 'darwin' ? ['apk', 'hap', 'ios', 'ipa', 'ios-sim'] : ['apk', 'hap'];
const type = await dependencies.prompt.pickBuildType(types, {
placeHolder: 'Select the app type',
});
if (!type) {
vscode.window.showWarningMessage('Build canceled: No type selected.');
return;
}
const targetPlatform = resolveTargetPlatform(type);
if (!targetPlatform) {
vscode.window.showWarningMessage('Build canceled: No target platform is available for this artifact type.');
return;
}
const mode = await dependencies.prompt.pickBuildMode(modeOptions, {
placeHolder: 'Select the build mode',
});
if (!mode) {
vscode.window.showWarningMessage('Build canceled: No build mode selected.');
return;
}
await cjmpBuildWithSpawn(workspaceRoot, type, targetPlatform, mode, dependencies.terminalFactory);
}
async function buildPackageProject(workspaceRoot: string, dependencies: BuildDependencies) {
const modeOptions = ['debug', 'release'];
const types = process.platform === 'darwin' ? ['aar', 'har', 'framework', 'framework-sim'] : ['aar', 'har'];
const type = await dependencies.prompt.pickBuildType(types, {
placeHolder: 'Select the package type',
});
if (!type) {
vscode.window.showWarningMessage('Build canceled: No type selected.');
return;
}
const targetPlatform = resolveTargetPlatform(type);
if (!targetPlatform) {
vscode.window.showWarningMessage('Build canceled: No target platform is available for this artifact type.');
return;
}
const mode = await dependencies.prompt.pickBuildMode(modeOptions, {
placeHolder: 'Select the build mode',
});
if (!mode) {
vscode.window.showWarningMessage('Build canceled: No build mode selected.');
return;
}
await cjmpBuildWithSpawn(workspaceRoot, type, targetPlatform, mode, dependencies.terminalFactory);
}
const cjmpBuildWithSpawn = async (
localPath: string | undefined,
type: string,
target_platform: string,
mode: string,
terminalFactory: TerminalFactory,
): Promise<void> => {
return new Promise((resolve, reject) => {
try {
const keelsCommand = resolveKeelsCommand();
if (!keelsCommand) {
resolve();
return;
}
const isWindows = process.platform === 'win32';
const shellPath = isWindows ? 'cmd.exe' : getDefaultShell();
const buildCommand = `${keelsCommand.executablePath} build ${type} --platform ${target_platform} --${mode} -v`;
const terminal = terminalFactory.create({
name: 'CJMP Build Terminal',
shellPath: shellPath,
cwd: localPath,
env: keelsCommand.environment,
});
terminal.show();
terminal.sendText(`${buildCommand}`);
resolve();
} catch (error) {
vscode.window.showErrorMessage(`Error building ${type}. : ${error instanceof Error ? error.message : String(error)}`);
reject(error);
}
});
};