1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
|
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only
import * as vscode from 'vscode';
import * as path from 'path';
import * as child_process from 'child_process';
import * as constants from '@/constants';
import {
IsMacOS,
IsWindows,
OSExeSuffix,
exists,
locateQmakeExeFilePath,
QtInfo
} from 'qt-lib';
import { coreAPI } from '@/extension';
export function getConfig<T>(
key: string,
defaultValue: T,
folder?: vscode.WorkspaceFolder
): T {
return vscode.workspace
.getConfiguration(constants.EXTENSION_ID, folder)
.get<T>(key, defaultValue);
}
export function affectsConfig(
event: vscode.ConfigurationChangeEvent,
key: string,
folder?: vscode.WorkspaceFolder
): boolean {
return event.affectsConfiguration(`${constants.EXTENSION_ID}.${key}`, folder);
}
export async function delay(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
const DesignerExeName = IsMacOS ? 'Designer' : 'designer' + OSExeSuffix;
function getDesignerExePath(selectedQtBinPath: string) {
const macOSPath = path.join(
'Designer.app',
'Contents',
'MacOS',
DesignerExeName
);
return IsMacOS
? path.join(selectedQtBinPath, macOSPath)
: path.join(selectedQtBinPath, DesignerExeName);
}
export async function locateDesigner(selectedQtPath: string) {
let designerExePath = getDesignerExePath(path.join(selectedQtPath, 'bin'));
if (await exists(designerExePath)) {
return designerExePath;
}
const qmakeExePath = await locateQmakeExeFilePath(selectedQtPath);
if (!qmakeExePath) {
return '';
}
const designer = extractDesignerExePathFromQtPath(qmakeExePath);
if (await designer) {
return designer;
}
if (!IsWindows) {
designerExePath = '/usr/bin/designer';
if (await exists(designerExePath)) {
return designerExePath;
}
}
return '';
}
export async function locateDesignerFromQtPaths(qtPaths: string) {
const info = coreAPI?.getQtInfoFromPath(qtPaths);
if (!info) {
return '';
}
const designerExePath = await searchForDesignerInQtInfo(info);
if (designerExePath) {
return designerExePath;
}
return undefined;
}
async function extractDesignerExePathFromQtPath(qtPathExePath: string) {
const hostBinDir = await queryHostBinDirPath(qtPathExePath);
const designerExePath = getDesignerExePath(hostBinDir);
if (await exists(designerExePath)) {
return designerExePath;
}
return undefined;
}
async function searchForDesignerInQtInfo(info: QtInfo) {
const keysToCheck = [
'QT_HOST_BINS',
'QT_HOST_LIBEXECS',
'QT_INSTALL_LIBEXECS'
];
const paths = keysToCheck
.map((key) => info.get(key))
.filter((p) => {
return p !== undefined;
});
const addVcpkgPaths = (p: string[]) => {
const keys = ['QT_INSTALL_PREFIX', 'QT_HOST_PREFIX'];
for (const key of keys) {
const value = info.get(key);
if (value) {
const vcpkgPath = path.join(value, 'tools', 'qttools', 'bin');
p.push(vcpkgPath);
}
}
};
// It is a special case for vcpkg because on some platforms, Designer is
// installed in a different location
addVcpkgPaths(paths);
for (const p of paths) {
if (p) {
const designerExePath = getDesignerExePath(p);
if (await exists(designerExePath)) {
return designerExePath;
}
}
}
return undefined;
}
async function queryHostBinDirPath(qtpathsExePath: string): Promise<string> {
const childProcess = child_process.exec(
qtpathsExePath + ' -query QT_HOST_BINS'
);
const promiseFirstLineOfOutput = new Promise<string>((resolve, reject) => {
childProcess.stdout?.on('data', (data: string) => {
resolve(data.toString().trim());
});
childProcess.stderr?.on('data', (data: string) => {
reject(new Error(data.toString().trim()));
});
});
const promiseProcessClose = new Promise<string>((resolve, reject) => {
childProcess.on('close', () => {
resolve('');
});
childProcess.on('error', (err) => {
reject(err);
});
});
const hostBinDir = await Promise.race([
promiseFirstLineOfOutput,
promiseProcessClose
]);
return hostBinDir;
}
|