mirror of https://github.com/acidanthera/audk.git
BaseTools: Add BaseTools plugins to support CI
https://bugzilla.tianocore.org/show_bug.cgi?id=2315 Add the following plugins that are required to support EDK II Continuous Integration (CI) builds. These plugins are added to BaseTools because that support EDK II BaseTools features. * BuildToolsReportGenerator * LinuxGcc5ToolChain * WindowsResourceCompiler * WindowsVsToolChain Cc: Bob Feng <bob.c.feng@intel.com> Cc: Liming Gao <liming.gao@intel.com> Signed-off-by: Michael D Kinney <michael.d.kinney@intel.com> Reviewed-by: Liming Gao <liming.gao@intel.com>
This commit is contained in:
parent
f7978bb258
commit
de4ce46d6e
|
@ -0,0 +1,69 @@
|
|||
##
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
##
|
||||
import os
|
||||
import logging
|
||||
import json
|
||||
|
||||
try:
|
||||
from edk2toolext.environment.plugintypes.uefi_build_plugin import IUefiBuildPlugin
|
||||
|
||||
class BuildToolsReportGenerator(IUefiBuildPlugin):
|
||||
def do_report(self, thebuilder):
|
||||
try:
|
||||
from edk2toolext.environment import version_aggregator
|
||||
except ImportError:
|
||||
logging.critical("Loading BuildToolsReportGenerator failed, please update your Edk2-PyTool-Extensions")
|
||||
return 0
|
||||
|
||||
OutputReport = os.path.join(thebuilder.env.GetValue("BUILD_OUTPUT_BASE"), "BUILD_TOOLS_REPORT")
|
||||
OutputReport = os.path.normpath(OutputReport)
|
||||
if not os.path.isdir(os.path.dirname(OutputReport)):
|
||||
os.makedirs(os.path.dirname(OutputReport))
|
||||
|
||||
Report = BuildToolsReport()
|
||||
Report.MakeReport(version_aggregator.GetVersionAggregator().GetAggregatedVersionInformation(), OutputReport=OutputReport)
|
||||
|
||||
def do_pre_build(self, thebuilder):
|
||||
self.do_report(thebuilder)
|
||||
return 0
|
||||
|
||||
def do_post_build(self, thebuilder):
|
||||
self.do_report(thebuilder)
|
||||
return 0
|
||||
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
class BuildToolsReport(object):
|
||||
MY_FOLDER = os.path.dirname(os.path.realpath(__file__))
|
||||
VERSION = "1.00"
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def MakeReport(self, BuildTools, OutputReport="BuildToolsReport"):
|
||||
logging.info("Writing BuildToolsReports to {0}".format(OutputReport))
|
||||
versions_list = []
|
||||
for key, value in BuildTools.items():
|
||||
versions_list.append(value)
|
||||
versions_list = sorted(versions_list, key=lambda k: k['type'])
|
||||
json_dict = {"modules": versions_list,
|
||||
"PluginVersion": BuildToolsReport.VERSION}
|
||||
|
||||
htmlfile = open(OutputReport + ".html", "w")
|
||||
jsonfile = open(OutputReport + ".json", "w")
|
||||
template = open(os.path.join(BuildToolsReport.MY_FOLDER, "BuildToolsReport_Template.html"), "r")
|
||||
|
||||
for line in template.readlines():
|
||||
if "%TO_BE_FILLED_IN_BY_PYTHON_SCRIPT%" in line:
|
||||
line = line.replace("%TO_BE_FILLED_IN_BY_PYTHON_SCRIPT%", json.dumps(json_dict))
|
||||
htmlfile.write(line)
|
||||
|
||||
jsonfile.write(json.dumps(versions_list, indent=4))
|
||||
|
||||
jsonfile.close()
|
||||
template.close()
|
||||
htmlfile.close()
|
|
@ -0,0 +1,12 @@
|
|||
## @file
|
||||
# Build Plugin used to output html report of all versions collected
|
||||
# during the build
|
||||
#
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
##
|
||||
{
|
||||
"scope": "global",
|
||||
"name": "Build Tools Report Generator",
|
||||
"module": "BuildToolsReportGenerator"
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible">
|
||||
<title>Build Tools Report</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" type="text/css" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/css/bootstrap.min.css" />
|
||||
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.15/css/dataTables.bootstrap.min.css" />
|
||||
<style>
|
||||
div.attribution {
|
||||
border: 1px solid #ddd;
|
||||
background-color: #bbb;
|
||||
padding-left: 20px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<h1>Build Tools Report</h1>
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active"><a data-toggle="tab" href="#tabs-1">Tools</a></li>
|
||||
<li><a data-toggle="tab" href="#tabs-2">About</a></li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div id="tabs-1" class="tab-pane fade in active">
|
||||
<table id="modinfo" class="table table-striped table-bordered table-hover" cellspacing="0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Key</th>
|
||||
<th>Value</th>
|
||||
<th>Type</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div id="tabs-2" class="tab-pane">
|
||||
<div class="row">
|
||||
<div class="col-xs-7">
|
||||
<p></p>
|
||||
<p>
|
||||
Build Tools Report Template Version: <span id="ReportTemplateVersion">1.00</span><br />
|
||||
Build Tools Report Plugin Version: <span id='ReportToolVersion'></span><br />
|
||||
</p>
|
||||
<h3>License</h3>
|
||||
<hr />
|
||||
<div id="ToolLicenseContent">
|
||||
<p>
|
||||
<span class="copyright">Copyright (c) Microsoft Corporation.</span><br />
|
||||
<span class="license">
|
||||
SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="AttributionListWrapper" class="col-xs-5">
|
||||
<h3>External Licenses</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Javascript libraries -->
|
||||
<script type="text/javascript" charset="utf8" src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.2.1.min.js"></script>
|
||||
<script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/1.10.15/js/jquery.dataTables.min.js"></script>
|
||||
<script type="text/javascript" charset="utf8" src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/1.10.15/js/dataTables.bootstrap.min.js"></script>
|
||||
|
||||
<script>
|
||||
var EmbeddedJd = %TO_BE_FILLED_IN_BY_PYTHON_SCRIPT%;
|
||||
</script>
|
||||
<!-- Add javascript here -->
|
||||
<script>
|
||||
var MODULE_TABLE_OFFSET = 350; //Space needed for other stuff besides the Table
|
||||
$(document).ready(function () {
|
||||
$('span#ReportToolVersion').text(EmbeddedJd.PluginVersion);
|
||||
//To support tabs and correct column width we need this change
|
||||
$('a[data-toggle="tab"][href="#tabs-1"]').on('shown.bs.tab', function (e) {
|
||||
$.fn.dataTable.tables({ visible: true, api: true }).columns.adjust();
|
||||
});
|
||||
//table for modules
|
||||
var mTable = $('table#modinfo').dataTable({
|
||||
"aaData": EmbeddedJd.modules,
|
||||
"paginate": false,
|
||||
"autoWidth": false,
|
||||
"scrollY": ($(window).height() - MODULE_TABLE_OFFSET) + "px",
|
||||
"aaSorting": [[2, "asc"]],
|
||||
"aoColumnDefs": [
|
||||
{
|
||||
"mData": "name",
|
||||
"aTargets": [0]
|
||||
},
|
||||
|
||||
{
|
||||
"mData": "version",
|
||||
"aTargets": [1]
|
||||
},
|
||||
{
|
||||
"mData": "type",
|
||||
"aTargets": [2],
|
||||
}
|
||||
] //end of column def
|
||||
}); //end of modules table
|
||||
|
||||
//
|
||||
// Create Attribution List for all external libraries used
|
||||
//
|
||||
[
|
||||
{ Title: "JQuery", Copyright: "Copyright 2017 The jQuery Foundation", Version: $.fn.jquery, LicenseType: "MIT", LicenseLink: "https://jquery.org/license/" },
|
||||
{ Title: "DataTables", Copyright: "DataTables designed and created by SpryMedia Ltd Copyright 2007-2017", Version: $.fn.dataTable.version, LicenseType: "MIT", LicenseLink: "https://datatables.net/license/mit" },
|
||||
{ Title: "BootStrap", Copyright: "Code and documentation copyright 2011-2017 the Bootstrap Authors and Twitter, Inc.", Version: "3.3.7", LicenseType: "MIT", LicenseLink: "https://github.com/twbs/bootstrap/blob/master/LICENSE" }
|
||||
].forEach(function (element) {
|
||||
$("<div class='attribution'><h4>" + element.Title + "</h4><p>Version: <span class='version'>" + element.Version + "</span><br /><span class='copyright'>" +
|
||||
element.Copyright + "</span><br />License: <a class='license' href='" + element.LicenseLink + "'>" + element.LicenseType + "</a></p></div>").appendTo("div#AttributionListWrapper");
|
||||
});
|
||||
});
|
||||
$(window).resize(function() {
|
||||
$.fn.dataTable.tables({ visible: true, api: true }).columns.adjust();
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,85 @@
|
|||
# @file LinuxGcc5ToolChain.py
|
||||
# Plugin to configures paths for GCC5 ARM/AARCH64 Toolchain
|
||||
##
|
||||
# This plugin works in conjuncture with the tools_def
|
||||
#
|
||||
# Copyright (c) Microsoft Corporation
|
||||
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
##
|
||||
import os
|
||||
import logging
|
||||
from edk2toolext.environment.plugintypes.uefi_build_plugin import IUefiBuildPlugin
|
||||
from edk2toolext.environment import shell_environment
|
||||
|
||||
|
||||
class LinuxGcc5ToolChain(IUefiBuildPlugin):
|
||||
|
||||
def do_post_build(self, thebuilder):
|
||||
return 0
|
||||
|
||||
def do_pre_build(self, thebuilder):
|
||||
self.Logger = logging.getLogger("LinuxGcc5ToolChain")
|
||||
|
||||
#
|
||||
# GCC5 - The ARM and AARCH64 compilers need their paths set if available
|
||||
if thebuilder.env.GetValue("TOOL_CHAIN_TAG") == "GCC5":
|
||||
|
||||
# Start with AARACH64 compiler
|
||||
ret = self._check_aarch64()
|
||||
if ret != 0:
|
||||
self.Logger.critical("Failed in check aarch64")
|
||||
return ret
|
||||
|
||||
# Check arm compiler
|
||||
ret = self._check_arm()
|
||||
if ret != 0:
|
||||
self.Logger.critical("Failed in check arm")
|
||||
return ret
|
||||
|
||||
return 0
|
||||
|
||||
def _check_arm(self):
|
||||
# check to see if full path already configured
|
||||
if shell_environment.GetEnvironment().get_shell_var("GCC5_ARM_PREFIX") is not None:
|
||||
self.Logger.info("GCC5_ARM_PREFIX is already set.")
|
||||
|
||||
else:
|
||||
# now check for install dir. If set then set the Prefix
|
||||
install_path = shell_environment.GetEnvironment().get_shell_var("GCC5_ARM_INSTALL")
|
||||
if install_path is None:
|
||||
return 0
|
||||
|
||||
# make GCC5_ARM_PREFIX to align with tools_def.txt
|
||||
prefix = os.path.join(install_path, "bin", "arm-linux-gnueabihf-")
|
||||
shell_environment.GetEnvironment().set_shell_var("GCC5_ARM_PREFIX", prefix)
|
||||
|
||||
# now confirm it exists
|
||||
if not os.path.exists(shell_environment.GetEnvironment().get_shell_var("GCC5_ARM_PREFIX") + "gcc"):
|
||||
self.Logger.error("Path for GCC5_ARM_PREFIX toolchain is invalid")
|
||||
return -2
|
||||
|
||||
return 0
|
||||
|
||||
def _check_aarch64(self):
|
||||
# check to see if full path already configured
|
||||
if shell_environment.GetEnvironment().get_shell_var("GCC5_AARCH64_PREFIX") is not None:
|
||||
self.Logger.info("GCC5_AARCH64_PREFIX is already set.")
|
||||
|
||||
else:
|
||||
# now check for install dir. If set then set the Prefix
|
||||
install_path = shell_environment.GetEnvironment(
|
||||
).get_shell_var("GCC5_AARCH64_INSTALL")
|
||||
if install_path is None:
|
||||
return 0
|
||||
|
||||
# make GCC5_AARCH64_PREFIX to align with tools_def.txt
|
||||
prefix = os.path.join(install_path, "bin", "aarch64-linux-gnu-")
|
||||
shell_environment.GetEnvironment().set_shell_var("GCC5_AARCH64_PREFIX", prefix)
|
||||
|
||||
# now confirm it exists
|
||||
if not os.path.exists(shell_environment.GetEnvironment().get_shell_var("GCC5_AARCH64_PREFIX") + "gcc"):
|
||||
self.Logger.error(
|
||||
"Path for GCC5_AARCH64_PREFIX toolchain is invalid")
|
||||
return -2
|
||||
|
||||
return 0
|
|
@ -0,0 +1,12 @@
|
|||
## @file
|
||||
# Build Plugin used to set the path
|
||||
# for the GCC5 ARM/AARCH64 downloaded compilers
|
||||
#
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
##
|
||||
{
|
||||
"scope": "global-nix",
|
||||
"name": "Linux GCC5 Tool Chain Support",
|
||||
"module": "LinuxGcc5ToolChain"
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
## @file WinRcPath.py
|
||||
# Plugin to find Windows SDK Resource Compiler rc.exe
|
||||
##
|
||||
# This plugin works in conjuncture with the tools_def to support rc.exe
|
||||
#
|
||||
# Copyright (c) Microsoft Corporation
|
||||
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
##
|
||||
import os
|
||||
from edk2toolext.environment.plugintypes.uefi_build_plugin import IUefiBuildPlugin
|
||||
import edk2toollib.windows.locate_tools as locate_tools
|
||||
from edk2toolext.environment import shell_environment
|
||||
from edk2toolext.environment import version_aggregator
|
||||
|
||||
class WinRcPath(IUefiBuildPlugin):
|
||||
|
||||
def do_post_build(self, thebuilder):
|
||||
return 0
|
||||
|
||||
def do_pre_build(self, thebuilder):
|
||||
#get the locate tools module
|
||||
path = locate_tools.FindToolInWinSdk("rc.exe")
|
||||
if path is None:
|
||||
thebuilder.logging.warning("Failed to find rc.exe")
|
||||
else:
|
||||
p = os.path.abspath(os.path.dirname(path))
|
||||
shell_environment.GetEnvironment().set_shell_var("WINSDK_PATH_FOR_RC_EXE", p)
|
||||
version_aggregator.GetVersionAggregator().ReportVersion("WINSDK_PATH_FOR_RC_EXE", p, version_aggregator.VersionTypes.INFO)
|
||||
return 0
|
|
@ -0,0 +1,13 @@
|
|||
## @file
|
||||
# Build Plugin used to set the path to rc.exe on windows.
|
||||
# The plugin is able to use python to locate the tool as to avoid
|
||||
# hard-coding the path
|
||||
#
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
##
|
||||
{
|
||||
"scope": "global-win",
|
||||
"name": "Windows RC Path Support",
|
||||
"module": "WinRcPath"
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
## @file WindowsVsToolChain.py
|
||||
# Plugin to configures paths for the VS2017 and VS2019 tool chain
|
||||
##
|
||||
# This plugin works in conjuncture with the tools_def
|
||||
#
|
||||
# Copyright (c) Microsoft Corporation
|
||||
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
##
|
||||
import os
|
||||
import logging
|
||||
from edk2toolext.environment.plugintypes.uefi_build_plugin import IUefiBuildPlugin
|
||||
import edk2toollib.windows.locate_tools as locate_tools
|
||||
from edk2toollib.windows.locate_tools import FindWithVsWhere
|
||||
from edk2toolext.environment import shell_environment
|
||||
from edk2toolext.environment import version_aggregator
|
||||
|
||||
class WindowsVsToolChain(IUefiBuildPlugin):
|
||||
|
||||
def do_post_build(self, thebuilder):
|
||||
return 0
|
||||
|
||||
def do_pre_build(self, thebuilder):
|
||||
self.Logger = logging.getLogger("WindowsVsToolChain")
|
||||
|
||||
#
|
||||
# VS2017 - Follow VS2017 where there is potential for many versions of the tools.
|
||||
# If a specific version is required then the user must set both env variables:
|
||||
## VS150INSTALLPATH: base install path on system to VC install dir. Here you will find the VC folder, etc
|
||||
## VS150TOOLVER: version number for the VC compiler tools
|
||||
## VS2017_PREFIX: path to MSVC compiler folder with trailing slash (can be used instead of two vars above)
|
||||
if thebuilder.env.GetValue("TOOL_CHAIN_TAG") == "VS2017":
|
||||
|
||||
# check to see if full path already configured
|
||||
if shell_environment.GetEnvironment().get_shell_var("VS2017_PREFIX") != None:
|
||||
self.Logger.info("VS2017_PREFIX is already set.")
|
||||
|
||||
else:
|
||||
install_path = self._get_vs_install_path("VS2017".lower(), "VS150INSTALLPATH")
|
||||
vc_ver = self._get_vc_version(install_path, "VS150TOOLVER")
|
||||
|
||||
if install_path is None or vc_ver is None:
|
||||
self.Logger.error("Failed to configure environment for VS2017")
|
||||
return -1
|
||||
|
||||
version_aggregator.GetVersionAggregator().ReportVersion(
|
||||
"Visual Studio Install Path", install_path, version_aggregator.VersionTypes.INFO)
|
||||
version_aggregator.GetVersionAggregator().ReportVersion(
|
||||
"VC Version", vc_ver, version_aggregator.VersionTypes.TOOL)
|
||||
|
||||
#make VS2017_PREFIX to align with tools_def.txt
|
||||
prefix = os.path.join(install_path, "VC", "Tools", "MSVC", vc_ver)
|
||||
prefix = prefix + os.path.sep
|
||||
shell_environment.GetEnvironment().set_shell_var("VS2017_PREFIX", prefix)
|
||||
|
||||
# now confirm it exists
|
||||
if not os.path.exists(shell_environment.GetEnvironment().get_shell_var("VS2017_PREFIX")):
|
||||
self.Logger.error("Path for VS2017 toolchain is invalid")
|
||||
return -2
|
||||
|
||||
#
|
||||
# VS2019 - Follow VS2019 where there is potential for many versions of the tools.
|
||||
# If a specific version is required then the user must set both env variables:
|
||||
## VS160INSTALLPATH: base install path on system to VC install dir. Here you will find the VC folder, etc
|
||||
## VS160TOOLVER: version number for the VC compiler tools
|
||||
## VS2019_PREFIX: path to MSVC compiler folder with trailing slash (can be used instead of two vars above)
|
||||
elif thebuilder.env.GetValue("TOOL_CHAIN_TAG") == "VS2019":
|
||||
|
||||
# check to see if full path already configured
|
||||
if shell_environment.GetEnvironment().get_shell_var("VS2019_PREFIX") != None:
|
||||
self.Logger.info("VS2019_PREFIX is already set.")
|
||||
|
||||
else:
|
||||
install_path = self._get_vs_install_path("VS2019".lower(), "VS160INSTALLPATH")
|
||||
vc_ver = self._get_vc_version(install_path, "VS160TOOLVER")
|
||||
|
||||
if install_path is None or vc_ver is None:
|
||||
self.Logger.error("Failed to configure environment for VS2019")
|
||||
return -1
|
||||
|
||||
version_aggregator.GetVersionAggregator().ReportVersion(
|
||||
"Visual Studio Install Path", install_path, version_aggregator.VersionTypes.INFO)
|
||||
version_aggregator.GetVersionAggregator().ReportVersion(
|
||||
"VC Version", vc_ver, version_aggregator.VersionTypes.TOOL)
|
||||
|
||||
#make VS2019_PREFIX to align with tools_def.txt
|
||||
prefix = os.path.join(install_path, "VC", "Tools", "MSVC", vc_ver)
|
||||
prefix = prefix + os.path.sep
|
||||
shell_environment.GetEnvironment().set_shell_var("VS2019_PREFIX", prefix)
|
||||
|
||||
# now confirm it exists
|
||||
if not os.path.exists(shell_environment.GetEnvironment().get_shell_var("VS2019_PREFIX")):
|
||||
self.Logger.error("Path for VS2019 toolchain is invalid")
|
||||
return -2
|
||||
|
||||
return 0
|
||||
|
||||
def _get_vs_install_path(self, vs_version, varname):
|
||||
# check if already specified
|
||||
path = shell_environment.GetEnvironment().get_shell_var(varname)
|
||||
if(path is None):
|
||||
# Not specified...find latest
|
||||
(rc, path) = FindWithVsWhere(vs_version=vs_version)
|
||||
if rc == 0 and path is not None and os.path.exists(path):
|
||||
self.Logger.debug("Found VS instance for %s", vs_version)
|
||||
else:
|
||||
self.Logger.error("Failed to find VS instance with VsWhere (%d)" % rc)
|
||||
return path
|
||||
|
||||
def _get_vc_version(self, path, varname):
|
||||
# check if already specified
|
||||
vc_ver = shell_environment.GetEnvironment().get_shell_var(varname)
|
||||
if (path is None):
|
||||
self.Logger.critical("Failed to find Visual Studio tools. Might need to check for VS install")
|
||||
return vc_ver
|
||||
if(vc_ver is None):
|
||||
# Not specified...find latest
|
||||
p2 = os.path.join(path, "VC", "Tools", "MSVC")
|
||||
if not os.path.isdir(p2):
|
||||
self.Logger.critical(
|
||||
"Failed to find VC tools. Might need to check for VS install")
|
||||
return vc_ver
|
||||
vc_ver = os.listdir(p2)[-1].strip() # get last in list
|
||||
self.Logger.debug("Found VC Tool version is %s" % vc_ver)
|
||||
return vc_ver
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
## @file
|
||||
# Build Plugin used to set the path to the visual studio tools chain
|
||||
#
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
##
|
||||
{
|
||||
"scope": "global-win",
|
||||
"name": "Windows Visual Studio Tool Chain Support",
|
||||
"module": "WindowsVsToolChain"
|
||||
}
|
Loading…
Reference in New Issue