Skip to content

Commit 3fec192

Browse files
committed
Support MSVC external include handling and environment variables
1 parent b977587 commit 3fec192

File tree

4 files changed

+156
-0
lines changed

4 files changed

+156
-0
lines changed

Extension/src/LanguageServer/client.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3214,6 +3214,38 @@ export class DefaultClient implements Client {
32143214
const compilerPathAndArgs: util.CompilerPathAndArgs =
32153215
util.extractCompilerPathAndArgs(!!settings.legacyCompilerArgsBehavior, c.compilerPath, c.compilerArgs);
32163216
modifiedConfig.compilerPath = compilerPathAndArgs.compilerPath;
3217+
3218+
// Handle external includes
3219+
const externalIncludePaths: string[] = [];
3220+
if (c.compilerArgs) {
3221+
const extracted = util.extractIncludePathsFromArgs(c.compilerArgs);
3222+
externalIncludePaths.push(...extracted.externalIncludePaths);
3223+
3224+
// Resolve external include variables
3225+
for (const varName of extracted.externalIncludeVars) {
3226+
const resolved = this.AdditionalEnvironment ? this.AdditionalEnvironment[varName] : process.env[varName];
3227+
if (util.isString(resolved)) {
3228+
externalIncludePaths.push(...resolved.split(path.delimiter).filter(p => !!p));
3229+
} else if (util.isArrayOfString(resolved)) {
3230+
externalIncludePaths.push(...resolved);
3231+
}
3232+
}
3233+
}
3234+
3235+
// Handle EXTERNAL_INCLUDE environment variable
3236+
const externalIncludeEnv = this.AdditionalEnvironment ? this.AdditionalEnvironment["EXTERNAL_INCLUDE"] : process.env["EXTERNAL_INCLUDE"];
3237+
if (util.isString(externalIncludeEnv)) {
3238+
externalIncludePaths.push(...externalIncludeEnv.split(path.delimiter).filter(p => !!p));
3239+
} else if (util.isArrayOfString(externalIncludeEnv)) {
3240+
externalIncludePaths.push(...externalIncludeEnv);
3241+
}
3242+
3243+
if (externalIncludePaths.length > 0) {
3244+
const currentIncludePath = modifiedConfig.includePath || [];
3245+
const uniqueNewIncludes = externalIncludePaths.filter(p => !currentIncludePath.includes(p));
3246+
modifiedConfig.includePath = currentIncludePath.concat(uniqueNewIncludes);
3247+
}
3248+
32173249
if (settings.legacyCompilerArgsBehavior) {
32183250
modifiedConfig.compilerArgsLegacy = compilerPathAndArgs.allCompilerArgs;
32193251
modifiedConfig.compilerArgs = undefined;

Extension/src/Utility/msvcFlags.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/* --------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All Rights Reserved.
3+
* See 'LICENSE' in the project root for license information.
4+
* ------------------------------------------------------------------------------------------ */
5+
6+
export interface IncludePathsFromArgs {
7+
includePaths: string[];
8+
externalIncludePaths: string[];
9+
externalIncludeVars: string[];
10+
}
11+
12+
/**
13+
* Parses compiler arguments for include-related flags (MSVC/clang-cl style).
14+
* Supports /I, /imsvc, /external:I, /external:var.
15+
*/
16+
export function extractIncludePathsFromArgs(args: string[]): IncludePathsFromArgs {
17+
const result: IncludePathsFromArgs = {
18+
includePaths: [],
19+
externalIncludePaths: [],
20+
externalIncludeVars: []
21+
};
22+
23+
for (let i = 0; i < args.length; i++) {
24+
const arg = args[i];
25+
if (arg.startsWith("/I") || arg.startsWith("-I")) {
26+
let path = arg.substring(2);
27+
if (path === "" && i + 1 < args.length) {
28+
const nextArg = args[i + 1];
29+
if (!nextArg.startsWith("/") && !nextArg.startsWith("-")) {
30+
path = args[++i];
31+
}
32+
}
33+
if (path !== "") {
34+
result.includePaths.push(path);
35+
}
36+
} else if (arg.startsWith("/imsvc") || arg.startsWith("-imsvc")) {
37+
let path = arg.substring(6);
38+
if (path === "" && i + 1 < args.length) {
39+
const nextArg = args[i + 1];
40+
if (!nextArg.startsWith("/") && !nextArg.startsWith("-")) {
41+
path = args[++i];
42+
}
43+
}
44+
if (path !== "") {
45+
// imsvc is treated as a system include, so we put it in externalIncludePaths
46+
result.externalIncludePaths.push(path);
47+
}
48+
} else if (arg.startsWith("/external:I") || arg.startsWith("-external:I")) {
49+
let path = arg.substring(11);
50+
if (path === "" && i + 1 < args.length) {
51+
const nextArg = args[i + 1];
52+
if (!nextArg.startsWith("/") && !nextArg.startsWith("-")) {
53+
path = args[++i];
54+
}
55+
}
56+
if (path !== "") {
57+
result.externalIncludePaths.push(path);
58+
}
59+
} else if (arg.startsWith("/external:var:") || arg.startsWith("-external:var:")) {
60+
61+
const varName = arg.substring(14);
62+
if (varName !== "") {
63+
result.externalIncludeVars.push(varName);
64+
}
65+
}
66+
}
67+
68+
return result;
69+
}

Extension/src/common.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1850,3 +1850,7 @@ export function getVSCodeLanguageModel(): any | undefined {
18501850
}
18511851
return vscodelm;
18521852
}
1853+
1854+
export * from './Utility/msvcFlags';
1855+
1856+
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/* --------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All Rights Reserved.
3+
* See 'LICENSE' in the project root for license information.
4+
* ------------------------------------------------------------------------------------------ */
5+
6+
import { deepEqual } from 'assert';
7+
import { describe, it } from 'mocha';
8+
import { extractIncludePathsFromArgs } from '../../src/Utility/msvcFlags';
9+
10+
11+
describe('MSVC Argument Parsing', () => {
12+
it('extracts /I paths', () => {
13+
const args = ['/I', 'C:\\path1', '/IC:\\path2', '-I', 'C:\\path3', '-IC:\\path4'];
14+
const result = extractIncludePathsFromArgs(args);
15+
deepEqual(result.includePaths, ['C:\\path1', 'C:\\path2', 'C:\\path3', 'C:\\path4']);
16+
});
17+
18+
it('extracts /imsvc paths', () => {
19+
const args = ['/imsvc', 'C:\\sys1', '/imsvcC:\\sys2', '-imsvc', 'C:\\sys3', '-imsvcC:\\sys4'];
20+
const result = extractIncludePathsFromArgs(args);
21+
deepEqual(result.externalIncludePaths, ['C:\\sys1', 'C:\\sys2', 'C:\\sys3', 'C:\\sys4']);
22+
});
23+
24+
it('extracts /external:I paths', () => {
25+
const args = ['/external:I', 'C:\\ext1', '/external:IC:\\ext2', '-external:I', 'C:\\ext3', '-external:IC:\\ext4'];
26+
const result = extractIncludePathsFromArgs(args);
27+
deepEqual(result.externalIncludePaths, ['C:\\ext1', 'C:\\ext2', 'C:\\ext3', 'C:\\ext4']);
28+
});
29+
30+
it('extracts /external:var variables', () => {
31+
const args = ['/external:var:MYVAR', '-external:var:OTHERVAR'];
32+
const result = extractIncludePathsFromArgs(args);
33+
deepEqual(result.externalIncludeVars, ['MYVAR', 'OTHERVAR']);
34+
});
35+
36+
it('handles mixed flags', () => {
37+
const args = ['/I', 'p1', '/external:I', 'p2', '/external:var:v1', '/imsvc', 'p3'];
38+
const result = extractIncludePathsFromArgs(args);
39+
deepEqual(result.includePaths, ['p1']);
40+
deepEqual(result.externalIncludePaths, ['p2', 'p3']);
41+
deepEqual(result.externalIncludeVars, ['v1']);
42+
});
43+
44+
it('handles empty/incomplete flags', () => {
45+
const args = ['/I', '/external:I', '/external:var:'];
46+
const result = extractIncludePathsFromArgs(args);
47+
deepEqual(result.includePaths, []);
48+
deepEqual(result.externalIncludePaths, []);
49+
deepEqual(result.externalIncludeVars, []);
50+
});
51+
});

0 commit comments

Comments
 (0)