Skip to content

Commit f995106

Browse files
Copilotsnehara99
andcommitted
Fix clang-cl vendor detection returning 'unknown vendor'
Co-authored-by: snehara99 <[email protected]>
1 parent 722a075 commit f995106

3 files changed

Lines changed: 137 additions & 5 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Bug Fixes:
1010
- Fix user-level tasks defined in `~/.config/Code/User/tasks.json` causing infinite spinner. [#4659](https://github.com/microsoft/vscode-cmake-tools/pull/4659)
1111
- Fix "Copy Value" in CMake debugger copying variable name instead of value. [#4551](https://github.com/microsoft/vscode-cmake-tools/issues/4551)
1212
- cmakeDriver: Fixes getCompilerVersion by using compilerPath instead of compilerName. [#4647](https://github.com/microsoft/vscode-cmake-tools/pull/4647) [@lygstate](https://github.com/lygstate)
13+
- Fix kit detection returning "unknown vendor" when using clang-cl compiler. [#4685](https://github.com/microsoft/vscode-cmake-tools/issues/4685)
1314

1415
## 1.22
1516

src/kits/kit.ts

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -239,9 +239,12 @@ export async function getKitDetect(kit: Kit): Promise<KitDetect> {
239239
if (!vs) {
240240
return kit;
241241
}
242+
// Determine if the compiler is clang-cl based on binary name
243+
const isClangCl = c_bin ? (path.basename(c_bin, '.exe') === 'clang-cl') : false;
244+
const clangVendor: CompilerVendorEnum = isClangCl ? 'ClangCl' : 'Clang';
242245
let version: CompilerVersion | null = null;
243246
if (c_bin) {
244-
version = await getCompilerVersion('Clang', c_bin);
247+
version = await getCompilerVersion(clangVendor, c_bin);
245248
}
246249
let targetArch = kit.preferredGenerator?.platform ?? kit.visualStudioArchitecture ?? 'i686';
247250
if (targetArch === 'win32') {
@@ -251,7 +254,7 @@ export async function getKitDetect(kit: Kit): Promise<KitDetect> {
251254
let versionCompiler = vs.installationVersion;
252255
let vendor: CompilerVendorEnum;
253256
if (version !== null) {
254-
vendor = 'Clang';
257+
vendor = clangVendor;
255258
versionCompiler = version.version;
256259
} else {
257260
vendor = `MSVC`;
@@ -271,6 +274,18 @@ export async function getKitDetect(kit: Kit): Promise<KitDetect> {
271274
} else if (kit.name.startsWith('Clang-cl')) {
272275
vendor = 'ClangCl';
273276
}
277+
// Fallback: detect vendor from compiler binary path if name pattern doesn't match
278+
if (vendor === undefined && c_bin) {
279+
const binBasename = path.basename(c_bin, '.exe').toLowerCase();
280+
if (binBasename === 'clang-cl' || binBasename.startsWith('clang-cl-')) {
281+
vendor = 'ClangCl';
282+
} else if (binBasename === 'clang' || binBasename.startsWith('clang-')) {
283+
vendor = 'Clang';
284+
} else if (binBasename === 'gcc' || binBasename.startsWith('gcc-') ||
285+
binBasename.endsWith('-gcc') || binBasename.includes('-gcc-')) {
286+
vendor = 'GCC';
287+
}
288+
}
274289
if (vendor === undefined) {
275290
return kit;
276291
}
@@ -280,7 +295,11 @@ export async function getKitDetect(kit: Kit): Promise<KitDetect> {
280295
version = await getCompilerVersion(vendor, c_bin);
281296
}
282297
if (!version) {
283-
return kit;
298+
// Return at least the vendor information even when version detection fails
299+
return {
300+
...kit,
301+
vendor
302+
};
284303
}
285304
return {
286305
vendor,
@@ -863,7 +882,8 @@ async function scanDirForClangForMSVCKits(dir: PathWithTrust, vsInstalls: VSInst
863882
return null;
864883
}
865884

866-
const version = dir.isTrusted ? await getCompilerVersion('Clang', binPath) : null;
885+
const clangVendor: CompilerVendorEnum = isClangMsvcCli ? 'ClangCl' : 'Clang';
886+
const version = dir.isTrusted ? await getCompilerVersion(clangVendor, binPath) : null;
867887
if (dir.isTrusted && version === null) {
868888
return null;
869889
}
@@ -896,7 +916,9 @@ async function scanDirForClangForMSVCKits(dir: PathWithTrust, vsInstalls: VSInst
896916
const vsArch = (version?.target && version.target.triple.includes('i686-pc')) ? 'x86' : 'x64';
897917
const archForKitName = vsArch === 'x86' ? 'x86' : 'amd64';
898918
const clangArchPath = (vsArch === "x64") ? "x64\\" : "";
899-
const clangKitName: string = `Clang ${version?.version} ${clang_cli} - ${archForKitName} for MSVC ${vs.installationVersion} (${install_name})`;
919+
// Use 'Clang-cl' prefix for clang-cl.exe, 'Clang' for clang.exe to match vendor detection pattern
920+
const clangNamePrefix = isClangMsvcCli ? 'Clang-cl' : 'Clang';
921+
const clangKitName: string = `${clangNamePrefix} ${version?.version} ${clang_cli} - ${archForKitName} for MSVC ${vs.installationVersion} (${install_name})`;
900922
const clangExists = async () => {
901923
const exists = binPath.startsWith(`${vs.installationPath}\\VC\\Tools\\Llvm\\${clangArchPath}bin`) && await util.checkFileExists(util.lightNormalizePath(binPath));
902924
return exists;

test/unit-tests/kit-scan.test.ts

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,4 +234,113 @@ suite('Kits scan test', () => {
234234
]);
235235
}).timeout(10000);
236236
});
237+
238+
suite('getKitDetect vendor detection from binary path', () => {
239+
test('Detect ClangCl vendor from clang-cl binary path', async () => {
240+
const testKit: kit.Kit = {
241+
name: 'Custom Kit Name',
242+
compilers: {
243+
C: 'C:/path/to/clang-cl.exe',
244+
CXX: 'C:/path/to/clang-cl.exe'
245+
},
246+
isTrusted: false
247+
};
248+
const detect = await kit.getKitDetect(testKit);
249+
// When not trusted, getKitDetect returns the original kit with vendor from binary path detection
250+
// Since the binary doesn't exist and we can't get version, it should still detect vendor from path
251+
expect(detect.vendor).to.eq('ClangCl');
252+
});
253+
254+
test('Detect Clang vendor from clang binary path', async () => {
255+
const testKit: kit.Kit = {
256+
name: 'Custom Kit Name',
257+
compilers: {
258+
C: '/usr/bin/clang',
259+
CXX: '/usr/bin/clang++'
260+
},
261+
isTrusted: false
262+
};
263+
const detect = await kit.getKitDetect(testKit);
264+
expect(detect.vendor).to.eq('Clang');
265+
});
266+
267+
test('Detect GCC vendor from gcc binary path', async () => {
268+
const testKit: kit.Kit = {
269+
name: 'Custom Kit Name',
270+
compilers: {
271+
C: '/usr/bin/gcc',
272+
CXX: '/usr/bin/g++'
273+
},
274+
isTrusted: false
275+
};
276+
const detect = await kit.getKitDetect(testKit);
277+
expect(detect.vendor).to.eq('GCC');
278+
});
279+
280+
test('Detect GCC vendor from versioned gcc binary path', async () => {
281+
const testKit: kit.Kit = {
282+
name: 'Custom Kit Name',
283+
compilers: {
284+
C: '/usr/bin/gcc-11',
285+
CXX: '/usr/bin/g++-11'
286+
},
287+
isTrusted: false
288+
};
289+
const detect = await kit.getKitDetect(testKit);
290+
expect(detect.vendor).to.eq('GCC');
291+
});
292+
293+
test('Detect Clang vendor from versioned clang binary path', async () => {
294+
const testKit: kit.Kit = {
295+
name: 'Custom Kit Name',
296+
compilers: {
297+
C: '/usr/bin/clang-14',
298+
CXX: '/usr/bin/clang++-14'
299+
},
300+
isTrusted: false
301+
};
302+
const detect = await kit.getKitDetect(testKit);
303+
expect(detect.vendor).to.eq('Clang');
304+
});
305+
306+
test('Detect ClangCl vendor from versioned clang-cl binary path', async () => {
307+
const testKit: kit.Kit = {
308+
name: 'Custom Kit Name',
309+
compilers: {
310+
C: 'C:/LLVM/bin/clang-cl-14.exe',
311+
CXX: 'C:/LLVM/bin/clang-cl-14.exe'
312+
},
313+
isTrusted: false
314+
};
315+
const detect = await kit.getKitDetect(testKit);
316+
expect(detect.vendor).to.eq('ClangCl');
317+
});
318+
319+
test('Detect GCC vendor from cross-compiler gcc binary path', async () => {
320+
const testKit: kit.Kit = {
321+
name: 'Custom Kit Name',
322+
compilers: {
323+
C: '/opt/toolchain/arm-linux-gnueabihf-gcc',
324+
CXX: '/opt/toolchain/arm-linux-gnueabihf-g++'
325+
},
326+
isTrusted: false
327+
};
328+
const detect = await kit.getKitDetect(testKit);
329+
expect(detect.vendor).to.eq('GCC');
330+
});
331+
332+
test('Return original kit when vendor cannot be detected', async () => {
333+
const testKit: kit.Kit = {
334+
name: 'Unknown Kit',
335+
compilers: {
336+
C: '/usr/bin/unknown-compiler',
337+
CXX: '/usr/bin/unknown-compiler++'
338+
},
339+
isTrusted: false
340+
};
341+
const detect = await kit.getKitDetect(testKit);
342+
// Should return original kit when vendor cannot be detected
343+
expect(detect.vendor).to.be.undefined;
344+
});
345+
});
237346
});

0 commit comments

Comments
 (0)