2022-05-10 17:38:53 +03:00
|
|
|
function RegisterExtractorPack(id)
|
2022-08-31 10:03:12 +03:00
|
|
|
function Exify(path)
|
|
|
|
if OperatingSystem == 'windows' then return path .. '.exe' else return path end
|
|
|
|
end
|
|
|
|
|
|
|
|
local extractor = Exify(GetPlatformToolsDirectory() .. 'Semmle.Extraction.CSharp.Driver')
|
2021-09-09 20:21:08 +03:00
|
|
|
|
2023-09-14 15:49:57 +03:00
|
|
|
local function isDotnet(name)
|
|
|
|
return name == 'dotnet' or name == 'dotnet.exe'
|
|
|
|
end
|
|
|
|
|
|
|
|
local function isDotnetPath(path)
|
|
|
|
return path:match('dotnet[.]exe$') or path:match('dotnet$')
|
|
|
|
end
|
|
|
|
|
|
|
|
local function isPossibleDotnetSubcommand(arg)
|
|
|
|
-- dotnet options start with either - or / (both are legal)
|
|
|
|
-- It is possible to run dotnet with dotnet, e.g., `dotnet dotnet build`
|
|
|
|
-- but we shouldn't consider `dotnet` to be a subcommand.
|
|
|
|
local firstCharacter = string.sub(arg, 1, 1)
|
|
|
|
return not (firstCharacter == '-') and
|
|
|
|
not (firstCharacter == '/') and
|
|
|
|
not isDotnetPath(arg)
|
|
|
|
end
|
|
|
|
|
2024-02-29 13:49:06 +03:00
|
|
|
local function isPathToExecutable(path)
|
|
|
|
return path:match('%.exe$') or path:match('%.dll')
|
|
|
|
end
|
|
|
|
|
2021-09-09 20:21:08 +03:00
|
|
|
function DotnetMatcherBuild(compilerName, compilerPath, compilerArguments,
|
|
|
|
_languageId)
|
2023-09-14 15:49:57 +03:00
|
|
|
if not isDotnet(compilerName) then
|
2021-09-09 20:21:08 +03:00
|
|
|
return nil
|
|
|
|
end
|
|
|
|
|
|
|
|
-- The dotnet CLI has the following usage instructions:
|
2023-09-14 15:49:57 +03:00
|
|
|
-- dotnet [sdk-options] [command] [command-options] [arguments] OR
|
|
|
|
-- dotnet [runtime-options] [path-to-application] [arguments]
|
2021-09-09 20:21:08 +03:00
|
|
|
-- we are interested in dotnet build, which has the following usage instructions:
|
|
|
|
-- dotnet [options] build [<PROJECT | SOLUTION>...]
|
|
|
|
-- For now, parse the command line as follows:
|
|
|
|
-- Everything that starts with `-` (or `/`) will be ignored.
|
2023-09-14 15:49:57 +03:00
|
|
|
-- The first non-option argument is treated as the command (except if it is dotnet itself).
|
|
|
|
-- if that's `build` or similar, we append `-p:UseSharedCompilation=false`
|
|
|
|
-- and `-p:EmitCompilerGeneratedFiles=true` to the command line,
|
2021-09-09 20:21:08 +03:00
|
|
|
-- otherwise we do nothing.
|
|
|
|
local match = false
|
2023-06-08 09:51:21 +03:00
|
|
|
local testMatch = false
|
2022-10-04 10:54:48 +03:00
|
|
|
local dotnetRunNeedsSeparator = false;
|
|
|
|
local dotnetRunInjectionIndex = nil;
|
2023-07-21 21:05:22 +03:00
|
|
|
-- A flag indicating whether we are in a position where we expect a sub-command such as `build`.
|
|
|
|
-- Once we have found one, we set this to `false` to not accidentally pick up on things that
|
|
|
|
-- look like sub-command names later on in the argument vector.
|
|
|
|
local inSubCommandPosition = true;
|
2021-09-09 20:21:08 +03:00
|
|
|
local argv = compilerArguments.argv
|
|
|
|
if OperatingSystem == 'windows' then
|
|
|
|
-- let's hope that this split matches the escaping rules `dotnet` applies to command line arguments
|
|
|
|
-- or, at least, that it is close enough
|
|
|
|
argv =
|
2022-08-08 11:43:29 +03:00
|
|
|
NativeArgumentsToArgv(compilerArguments.nativeArgumentPointer)
|
2021-09-09 20:21:08 +03:00
|
|
|
end
|
|
|
|
for i, arg in ipairs(argv) do
|
2024-02-29 13:49:06 +03:00
|
|
|
-- if dotnet is being used to execute any application except dotnet itself, we should
|
|
|
|
-- not inject any flags.
|
|
|
|
if not match and isPathToExecutable(arg) and not isDotnetPath(arg) then
|
|
|
|
Log(1, 'Execute a .NET application usage detected')
|
|
|
|
Log(1, 'Dotnet path-to-application detected: %s', arg)
|
|
|
|
break
|
|
|
|
end
|
2023-09-14 15:49:57 +03:00
|
|
|
if isPossibleDotnetSubcommand(arg) then
|
2024-02-29 13:49:06 +03:00
|
|
|
if not match and inSubCommandPosition then
|
|
|
|
Log(1, 'Execute a .NET SDK command usage detected')
|
2022-10-04 10:54:48 +03:00
|
|
|
Log(1, 'Dotnet subcommand detected: %s', arg)
|
|
|
|
end
|
2023-07-21 21:05:22 +03:00
|
|
|
-- only respond to strings that look like sub-command names if we have not yet
|
|
|
|
-- encountered something that looks like a sub-command
|
|
|
|
if inSubCommandPosition then
|
|
|
|
if arg == 'build' or arg == 'msbuild' or arg == 'publish' or arg == 'pack' then
|
|
|
|
match = true
|
|
|
|
break
|
|
|
|
end
|
|
|
|
if arg == 'run' then
|
|
|
|
-- for `dotnet run`, we need to make sure that `-p:UseSharedCompilation=false` is
|
|
|
|
-- not passed in as an argument to the program that is run
|
|
|
|
match = true
|
|
|
|
dotnetRunNeedsSeparator = true
|
|
|
|
dotnetRunInjectionIndex = i + 1
|
|
|
|
end
|
|
|
|
if arg == 'test' then
|
|
|
|
match = true
|
|
|
|
testMatch = true
|
|
|
|
end
|
2023-06-06 18:22:52 +03:00
|
|
|
end
|
2023-07-21 21:05:22 +03:00
|
|
|
|
|
|
|
-- we have found a sub-command, ignore all strings that look like sub-command names from now on
|
|
|
|
inSubCommandPosition = false
|
2022-09-19 10:30:16 +03:00
|
|
|
end
|
2023-11-07 16:52:37 +03:00
|
|
|
-- for `dotnet test`, we should not append `-p:UseSharedCompilation=false` to the command line
|
|
|
|
-- if an `exe` or `dll` is passed as an argument as the call is forwarded to vstest.
|
2024-02-29 13:49:06 +03:00
|
|
|
if testMatch and isPathToExecutable(arg) then
|
2023-11-07 16:52:37 +03:00
|
|
|
match = false
|
|
|
|
break
|
|
|
|
end
|
2022-10-04 10:54:48 +03:00
|
|
|
-- if we see a separator to `dotnet run`, inject just prior to the existing separator
|
2022-09-19 10:30:16 +03:00
|
|
|
if arg == '--' then
|
2022-10-04 10:54:48 +03:00
|
|
|
dotnetRunNeedsSeparator = false
|
|
|
|
dotnetRunInjectionIndex = i
|
2022-09-19 10:30:16 +03:00
|
|
|
break
|
2021-09-09 20:21:08 +03:00
|
|
|
end
|
2022-10-04 10:54:48 +03:00
|
|
|
-- if we see an option to `dotnet run` (e.g., `--project`), inject just prior
|
|
|
|
-- to the last option
|
2023-09-14 15:49:57 +03:00
|
|
|
if string.sub(arg, 1, 1) == '-' then
|
2022-10-04 10:54:48 +03:00
|
|
|
dotnetRunNeedsSeparator = false
|
|
|
|
dotnetRunInjectionIndex = i
|
|
|
|
end
|
2021-09-09 20:21:08 +03:00
|
|
|
end
|
2023-06-07 15:49:04 +03:00
|
|
|
if match then
|
2023-05-25 16:52:56 +03:00
|
|
|
local injections = { '-p:UseSharedCompilation=false', '-p:EmitCompilerGeneratedFiles=true' }
|
2022-10-04 10:54:48 +03:00
|
|
|
if dotnetRunNeedsSeparator then
|
2022-09-19 10:30:16 +03:00
|
|
|
table.insert(injections, '--')
|
|
|
|
end
|
2022-10-04 10:54:48 +03:00
|
|
|
if dotnetRunInjectionIndex == nil then
|
2022-09-19 10:30:16 +03:00
|
|
|
-- Simple case; just append at the end
|
|
|
|
return {
|
|
|
|
order = ORDER_REPLACE,
|
|
|
|
invocation = BuildExtractorInvocation(id, compilerPath, compilerPath, compilerArguments, nil,
|
|
|
|
injections)
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Complex case; splice injections into the middle of the command line
|
|
|
|
for i, injectionArg in ipairs(injections) do
|
2022-10-04 10:54:48 +03:00
|
|
|
table.insert(argv, dotnetRunInjectionIndex + i - 1, injectionArg)
|
2022-09-19 10:30:16 +03:00
|
|
|
end
|
|
|
|
|
|
|
|
if OperatingSystem == 'windows' then
|
|
|
|
return {
|
|
|
|
order = ORDER_REPLACE,
|
|
|
|
invocation = {
|
|
|
|
path = AbsolutifyExtractorPath(id, compilerPath),
|
|
|
|
arguments = {
|
2023-09-06 12:35:45 +03:00
|
|
|
commandLineString = ArgvToCommandLineString(argv)
|
2022-09-19 10:30:16 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return {
|
|
|
|
order = ORDER_REPLACE,
|
|
|
|
invocation = {
|
|
|
|
path = AbsolutifyExtractorPath(id, compilerPath),
|
|
|
|
arguments = {
|
|
|
|
argv = argv
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
end
|
2021-09-09 20:21:08 +03:00
|
|
|
end
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
|
2022-08-31 10:03:12 +03:00
|
|
|
function MsBuildMatcher(compilerName, compilerPath, compilerArguments, _languageId)
|
|
|
|
if MatchCompilerName('^' .. Exify('msbuild') .. '$', compilerName, compilerPath,
|
|
|
|
compilerArguments) or
|
|
|
|
MatchCompilerName('^' .. Exify('xbuild') .. '$', compilerName, compilerPath,
|
|
|
|
compilerArguments) then
|
|
|
|
return {
|
|
|
|
order = ORDER_REPLACE,
|
|
|
|
invocation = BuildExtractorInvocation(id, compilerPath,
|
|
|
|
compilerPath,
|
|
|
|
compilerArguments,
|
|
|
|
nil, {
|
2022-08-31 10:05:04 +03:00
|
|
|
'/p:UseSharedCompilation=false',
|
2023-05-25 16:52:56 +03:00
|
|
|
'/p:MvcBuildViews=true',
|
|
|
|
'/p:EmitCompilerGeneratedFiles=true',
|
2022-08-31 10:03:12 +03:00
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-05-09 17:12:32 +03:00
|
|
|
local windowsMatchers = {
|
2021-09-09 20:21:08 +03:00
|
|
|
DotnetMatcherBuild,
|
2022-08-31 10:03:12 +03:00
|
|
|
MsBuildMatcher,
|
2022-08-08 11:43:29 +03:00
|
|
|
CreatePatternMatcher({ '^csc.*%.exe$' }, MatchCompilerName, extractor, {
|
2022-08-31 10:03:12 +03:00
|
|
|
prepend = { '--compiler', '"${compiler}"' },
|
2022-05-17 12:25:06 +03:00
|
|
|
order = ORDER_BEFORE
|
|
|
|
}),
|
2022-08-08 11:43:29 +03:00
|
|
|
CreatePatternMatcher({ '^fakes.*%.exe$', 'moles.*%.exe' },
|
|
|
|
MatchCompilerName, nil, { trace = false }),
|
|
|
|
function(compilerName, compilerPath, compilerArguments, _languageId)
|
|
|
|
-- handle cases like `dotnet.exe exec csc.dll <args>`
|
|
|
|
if compilerName ~= 'dotnet.exe' then
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
|
|
|
|
local seenCompilerCall = false
|
|
|
|
local argv = NativeArgumentsToArgv(compilerArguments.nativeArgumentPointer)
|
2022-08-19 14:08:04 +03:00
|
|
|
local extractorArgs = { '--compiler' }
|
2022-08-08 11:43:29 +03:00
|
|
|
for _, arg in ipairs(argv) do
|
|
|
|
if arg:match('csc%.dll$') then
|
|
|
|
seenCompilerCall = true
|
|
|
|
end
|
|
|
|
if seenCompilerCall then
|
2023-09-06 12:35:45 +03:00
|
|
|
table.insert(extractorArgs, arg)
|
2022-08-08 11:43:29 +03:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if seenCompilerCall then
|
|
|
|
return {
|
2023-05-25 16:52:56 +03:00
|
|
|
order = ORDER_AFTER,
|
2022-08-08 11:43:29 +03:00
|
|
|
invocation = {
|
|
|
|
path = AbsolutifyExtractorPath(id, extractor),
|
|
|
|
arguments = {
|
2023-09-06 12:35:45 +03:00
|
|
|
commandLineString = ArgvToCommandLineString(extractorArgs)
|
2022-08-08 11:43:29 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
end
|
|
|
|
return nil
|
|
|
|
end
|
2022-05-09 17:12:32 +03:00
|
|
|
}
|
|
|
|
local posixMatchers = {
|
2021-09-09 20:21:08 +03:00
|
|
|
DotnetMatcherBuild,
|
2022-08-08 11:43:29 +03:00
|
|
|
CreatePatternMatcher({ '^mcs%.exe$', '^csc%.exe$' }, MatchCompilerName,
|
|
|
|
extractor, {
|
2022-08-19 14:08:04 +03:00
|
|
|
prepend = { '--compiler', '"${compiler}"' },
|
2022-05-17 12:25:06 +03:00
|
|
|
order = ORDER_BEFORE
|
2022-08-31 10:03:12 +03:00
|
|
|
}),
|
|
|
|
MsBuildMatcher,
|
|
|
|
function(compilerName, compilerPath, compilerArguments, _languageId)
|
2022-08-08 11:43:29 +03:00
|
|
|
-- handle cases like `dotnet exec csc.dll <args>` and `mono(-sgen64) csc.exe <args>`
|
|
|
|
if compilerName ~= 'dotnet' and not compilerName:match('^mono') then
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
|
|
|
|
local seenCompilerCall = false
|
|
|
|
local argv = compilerArguments.argv
|
2022-08-19 14:08:04 +03:00
|
|
|
local extractorArgs = { '--compiler' }
|
2022-08-08 11:43:29 +03:00
|
|
|
for _, arg in ipairs(argv) do
|
|
|
|
if arg:match('csc%.dll$') or arg:match('csc%.exe$') or arg:match('mcs%.exe$') then
|
|
|
|
seenCompilerCall = true
|
|
|
|
end
|
|
|
|
if seenCompilerCall then
|
|
|
|
table.insert(extractorArgs, arg)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if seenCompilerCall then
|
|
|
|
return {
|
2023-05-25 16:52:56 +03:00
|
|
|
order = ORDER_AFTER,
|
2022-08-08 11:43:29 +03:00
|
|
|
invocation = {
|
|
|
|
path = AbsolutifyExtractorPath(id, extractor),
|
|
|
|
arguments = {
|
|
|
|
argv = extractorArgs
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
end
|
|
|
|
return nil
|
2022-05-09 17:12:32 +03:00
|
|
|
end
|
|
|
|
}
|
|
|
|
if OperatingSystem == 'windows' then
|
|
|
|
return windowsMatchers
|
|
|
|
else
|
|
|
|
return posixMatchers
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Return a list of minimum supported versions of the configuration file format
|
|
|
|
-- return one entry per supported major version.
|
2022-08-08 11:43:29 +03:00
|
|
|
function GetCompatibleVersions() return { '1.0.0' } end
|