mapnik/scons/scons-local-4.8.1/SCons/Tool/MSCommon/MSVC/SetupEnvDefault.py
2024-09-09 10:56:17 +01:00

236 lines
9.1 KiB
Python
Vendored

# MIT License
#
# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""
Determine if and/or when an error/warning should be issued when there
are no versions of msvc installed. If there is at least one version of
msvc installed, these routines do (almost) nothing.
Notes:
* When msvc is the default compiler because there are no compilers
installed, a build may fail due to the cl.exe command not being
recognized. Currently, there is no easy way to detect during
msvc initialization if the default environment will be used later
to build a program and/or library. There is no error/warning
as there are legitimate SCons uses that do not require a c compiler.
* An error is indicated by returning a non-empty tool list from the
function register_iserror.
"""
import re
from .. common import (
debug,
)
from . import Dispatcher
Dispatcher.register_modulename(__name__)
class _Data:
separator = r';'
need_init = True
@classmethod
def reset(cls) -> None:
debug('msvc default:init')
cls.n_setup = 0 # number of calls to msvc_setup_env_once
cls.default_ismsvc = False # is msvc the default compiler
cls.default_tools_re_list = [] # list of default tools regular expressions
cls.msvc_tools_init = set() # tools registered via msvc_exists
cls.msvc_tools = None # tools registered via msvc_setup_env_once
cls.msvc_installed = False # is msvc installed (vcs_installed > 0)
cls.msvc_nodefault = False # is there a default version of msvc
cls.need_init = True # reset initialization indicator
def _initialize(env, msvc_exists_func) -> None:
if _Data.need_init:
_Data.reset()
_Data.need_init = False
_Data.msvc_installed = msvc_exists_func(env)
debug('msvc default:msvc_installed=%s', _Data.msvc_installed)
def register_tool(env, tool, msvc_exists_func):
if _Data.need_init:
_initialize(env, msvc_exists_func)
if _Data.msvc_installed:
return None
if not tool:
return None
if _Data.n_setup == 0:
if tool not in _Data.msvc_tools_init:
_Data.msvc_tools_init.add(tool)
debug('msvc default:tool=%s, msvc_tools_init=%s', tool, _Data.msvc_tools_init)
return None
if tool not in _Data.msvc_tools:
_Data.msvc_tools.add(tool)
debug('msvc default:tool=%s, msvc_tools=%s', tool, _Data.msvc_tools)
def register_setup(env, msvc_exists_func) -> None:
if _Data.need_init:
_initialize(env, msvc_exists_func)
_Data.n_setup += 1
if not _Data.msvc_installed:
_Data.msvc_tools = set(_Data.msvc_tools_init)
if _Data.n_setup == 1:
tool_list = env.get('TOOLS', None)
if tool_list and tool_list[0] == 'default':
if len(tool_list) > 1 and tool_list[1] in _Data.msvc_tools:
# msvc tools are the default compiler
_Data.default_ismsvc = True
_Data.msvc_nodefault = False
debug(
'msvc default:n_setup=%d, msvc_installed=%s, default_ismsvc=%s',
_Data.n_setup, _Data.msvc_installed, _Data.default_ismsvc
)
def set_nodefault() -> None:
# default msvc version, msvc not installed
_Data.msvc_nodefault = True
debug('msvc default:msvc_nodefault=%s', _Data.msvc_nodefault)
def register_iserror(env, tool, msvc_exists_func):
register_tool(env, tool, msvc_exists_func)
if _Data.msvc_installed:
# msvc installed
return None
if not _Data.msvc_nodefault:
# msvc version specified
return None
tool_list = env.get('TOOLS', None)
if not tool_list:
# tool list is empty
return None
debug(
'msvc default:n_setup=%s, default_ismsvc=%s, msvc_tools=%s, tool_list=%s',
_Data.n_setup, _Data.default_ismsvc, _Data.msvc_tools, tool_list
)
if not _Data.default_ismsvc:
# Summary:
# * msvc is not installed
# * msvc version not specified (default)
# * msvc is not the default compiler
# construct tools set
tools_set = set(tool_list)
else:
if _Data.n_setup == 1:
# first setup and msvc is default compiler:
# build default tools regex for current tool state
tools = _Data.separator.join(tool_list)
tools_nchar = len(tools)
debug('msvc default:add regex:nchar=%d, tools=%s', tools_nchar, tools)
re_default_tools = re.compile(re.escape(tools))
_Data.default_tools_re_list.insert(0, (tools_nchar, re_default_tools))
# early exit: no error for default environment when msvc is not installed
return None
# Summary:
# * msvc is not installed
# * msvc version not specified (default)
# * environment tools list is not empty
# * default tools regex list constructed
# * msvc tools set constructed
#
# Algorithm using tools string and sets:
# * convert environment tools list to a string
# * iteratively remove default tools sequences via regex
# substition list built from longest sequence (first)
# to shortest sequence (last)
# * build environment tools set with remaining tools
# * compute intersection of environment tools and msvc tools sets
# * if the intersection is:
# empty - no error: default tools and/or no additional msvc tools
# not empty - error: user specified one or more msvc tool(s)
#
# This will not produce an error or warning when there are no
# msvc installed instances nor any other recognized compilers
# and the default environment is needed for a build. The msvc
# compiler is forcibly added to the environment tools list when
# there are no compilers installed on win32. In this case, cl.exe
# will not be found on the path resulting in a failed build.
# construct tools string
tools = _Data.separator.join(tool_list)
tools_nchar = len(tools)
debug('msvc default:check tools:nchar=%d, tools=%s', tools_nchar, tools)
# iteratively remove default tool sequences (longest to shortest)
if not _Data.default_tools_re_list:
debug('default_tools_re_list=%s', _Data.default_tools_re_list)
else:
re_nchar_min, re_tools_min = _Data.default_tools_re_list[-1]
if tools_nchar >= re_nchar_min and re_tools_min.search(tools):
# minimum characters satisfied and minimum pattern exists
for re_nchar, re_default_tool in _Data.default_tools_re_list:
if tools_nchar < re_nchar:
# not enough characters for pattern
continue
tools = re_default_tool.sub('', tools).strip(_Data.separator)
tools_nchar = len(tools)
debug('msvc default:check tools:nchar=%d, tools=%s', tools_nchar, tools)
if tools_nchar < re_nchar_min or not re_tools_min.search(tools):
# less than minimum characters or minimum pattern does not exist
break
# construct non-default list(s) tools set
tools_set = {msvc_tool for msvc_tool in tools.split(_Data.separator) if msvc_tool}
debug('msvc default:tools=%s', tools_set)
if not tools_set:
return None
# compute intersection of remaining tools set and msvc tools set
tools_found = _Data.msvc_tools.intersection(tools_set)
debug('msvc default:tools_exist=%s', tools_found)
if not tools_found:
return None
# construct in same order as tools list
tools_found_list = []
seen_tool = set()
for tool in tool_list:
if tool not in seen_tool:
seen_tool.add(tool)
if tool in tools_found:
tools_found_list.append(tool)
# return tool list in order presented
return tools_found_list
def reset() -> None:
debug('')
_Data.reset()