summaryrefslogtreecommitdiff
path: root/SCons/Tool/MSCommon/README.rst
blob: 36e58aad462877d0dad93ccf6558ec04d4452fb1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
.. sectnum::

README - SCons.Tool.MSCommon
############################

.. contents:: **Table of Contents**
   :depth: 2
   :local:


Design Notes
============

* Public, user-callable functions and exception types are available via
  the ``SCons.Tool.MSCommon`` namespace.

* Some existing code has been moved from ``MSCommon/vc.py`` to the appropriate
  ``MSCommon/MSVC/<modulename>``.

* No functions from the MSVC module or its child modules are intended to be invoked directly.
  All functions of interest are made available via the ``SCons.Tool.MSCommon`` namespace.
  It is anticipated that more code may be moved in the future as new features are added.
  By exposing the public API through ``SCons.Tool.MSCommon`` there should not be a problem
  with code movement.

* Additional helper functions primarily used for the test suite were added to
  ``MSCommon/vc.py`` and are available via the ``SCons.Tool.MSCommon`` namespace.


Known Issues
============

The following issues are known to exist:

* Using ``MSVC_USE_SCRIPT`` and ``MSVC_USE_SCRIPT_ARGS`` to call older Microsoft SDK
  ``SetEnv.cmd`` batch files may result in build failures.  Some of these batch files
  require delayed expansion to be enabled which is not usually the Windows default.
  One solution would be to launch the MSVC batch file command in a new command interpreter
  instance with delayed expansion enabled via command-line options.

* The code to suppress the "No versions of the MSVC compiler were found" warning for
  the default environment was moved from ``MSCommon/vc.py`` to ``MSCommon/MSVC/SetupEnvDefault.py``.
  There very few, if any, existing unit tests. Now that the code is isolated in its own
  module with a limited API, unit tests may be easier to implement.


Experimental Features
=====================

msvc_query_version_toolset(version=None, prefer_newest=True)
------------------------------------------------------------

The experimental function ``msvc_query_version_toolset`` was added to ``MSCommon/vc.py``
and is available via the ``SCons.Tool.MSCommon`` namespace. This function takes a version
specification or a toolset version specification and a product preference as arguments and
returns the msvc version and the msvc toolset version for the corresponding version specification.

This is a proxy for using the toolset version for selection until that functionality can be added.

Example usage:
::
    for version in [
        '14.3',
        '14.2',
        '14.1',
        '14.0',
        '14.32',
        '14.31',
        '14.29',
        '14.16',
        '14.00',
        '14.28.29333', # only 14.2
        '14.20.29333', # fictitious for testing
    ]:

        for prefer_newest in (True, False):
            try:
                msvc_version, msvc_toolset_version = msvc_query_version_toolset(version, prefer_newest=prefer_newest)
                failed = False
            except MSVCToolsetVersionNotFound:
                failed = True
            if failed:
                msg = 'FAILED'
                newline = '\n'
            else:
                env = Environment(MSVC_VERSION=msvc_version, MSVC_TOOLSET_VERSION=msvc_toolset_version)
                msg = 'passed'
                newline = ''
            print('{}Query: {} version={}, prefer_newest={}'.format(newline, msg, version, prefer_newest))

Example output fragment
::
    Build: _build003 {'MSVC_VERSION': '14.3', 'MSVC_TOOLSET_VERSION': '14.29.30133'}
    Where: C:\Software\MSVS-2022-143-Com\VC\Tools\MSVC\14.29.30133\bin\HostX64\x64\cl.exe
    Where: C:\Software\MSVS-2022-143-Com\Common7\Tools\guidgen.exe
    Query: passed version=14.2, prefer_newest=True

    Build: _build004 {'MSVC_VERSION': '14.2', 'MSVC_TOOLSET_VERSION': '14.29.30133'}
    Where: C:\Software\MSVS-2019-142-Com\VC\Tools\MSVC\14.29.30133\bin\HostX64\x64\cl.exe
    Where: C:\Software\MSVS-2019-142-Com\Common7\Tools\guidgen.exe
    Query: passed version=14.2, prefer_newest=False


Undocumented Features
=====================

set SCONS_CACHE_MSVC_FORCE_DEFAULTS=1
-------------------------------------

The Windows system environment variable ``SCONS_CACHE_MSVC_FORCE_DEFAULTS`` was added.  This variable is only
evaluated when the msvc cache is enabled and accepts the values ``1``, ``true``, and ``True``.

When enabled, the default msvc toolset version and the default sdk version, if not otherwise specified, are
added to the batch file argument list.  This is intended to make the cache more resilient to Visual Studio
updates that may change the default toolset version and/or the default SDK version.

Example usage:
::

    @echo Enabling scons cache ...
    @set "SCONS_CACHE_MSVC_CONFIG=mycachefile.json"
    @set "SCONS_CACHE_MSVC_FORCE_DEFAULTS=True"


End-User Diagnostic Tools
=========================

Due to the proliferation of user-defined msvc batch file arguments, the likelihood of end-user build
failures has increased.

Some of the options that may be employed in diagnosing end-user msvc build failures are listed below.

msvc_set_scripterror_policy('Warning') and MSVC_SCRIPTERROR_POLICY='Warning'
----------------------------------------------------------------------------

Enabling warnings to be produced for detected msvc batch file errors may provide additional context
for build failures. Refer to the documentation for details.

Change the default policy:
::
    from SCons.Tool.MSCommon import msvc_set_scripterror_policy

    msvc_set_scripterror_policy('Warning')

Specify the policy per-environment:
::

    env = Environment(MSVC_VERSION='14.3', MSVC_SPECTRE_LIBS=True, MSVC_SCRIPTERROR_POLICY='Warning')


set SCONS_MSCOMMON_DEBUG=mydebugfile.txt
----------------------------------------

The traditional method of diagnosing end-user issues is to enable the internal msvc debug logging.


set SCONS_CACHE_MSVC_CONFIG=mycachefile.json
--------------------------------------------

On occasion, enabling the cache file can prove to be a useful diagnostic tool.  If nothing else,
issues with the msvc environment may be readily apparent.


vswhere.exe
-----------

On occasion, the raw vswhere output may prove useful especially if there are suspected issues with
detection of installed msvc instances.

Windows command-line sample invocations:
::
    @rem 64-Bit Windows
    "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -all -sort -prerelease -products * -legacy -format json >MYVSWHEREOUTPUT.json

    @rem 32-Bit Windows:
    "%ProgramFiles%\Microsoft Visual Studio\Installer\vswhere.exe" -all -sort -prerelease -products * -legacy -format json >MYVSWHEREOUTPUT.json


Visual Studio Implementation Notes
==================================

Batch File Arguments
--------------------

Supported MSVC batch file arguments by product:

======= === === ======= =======
Product UWP SDK Toolset Spectre
======= === === ======= =======
VS2022   X   X     X       X
------- --- --- ------- -------
VS2019   X   X     X       X
------- --- --- ------- -------
VS2017   X   X     X       X
------- --- --- ------- -------
VS2015   X   X
======= === === ======= =======

Supported MSVC batch file arguments in SCons:

======== ====================================== ===================================================
Argument Construction Variable                  Script Argument Equivalent
======== ====================================== ===================================================
UWP      ``MSVC_UWP_APP=True``                  ``MSVC_SCRIPT_ARGS='store'``
-------- -------------------------------------- ---------------------------------------------------
SDK      ``MSVC_SDK_VERSION='10.0.20348.0'``    ``MSVC_SCRIPT_ARGS='10.0.20348.0'``
-------- -------------------------------------- ---------------------------------------------------
Toolset  ``MSVC_TOOLSET_VERSION='14.31.31103'`` ``MSVC_SCRIPT_ARGS='-vcvars_ver=14.31.31103'``
-------- -------------------------------------- ---------------------------------------------------
Spectre  ``MSVC_SPECTRE_LIBS=True``             ``MSVC_SCRIPT_ARGS='-vcvars_spectre_libs=spectre'``
======== ====================================== ===================================================

**MSVC_SCRIPT_ARGS contents are not validated.  Utilizing script arguments that have construction
variable equivalents is discouraged and may lead to difficult to diagnose build errors.**

Additional constraints:

* ``MSVC_SDK_VERSION='8.1'`` and ``MSVC_UWP_APP=True`` is supported only for the v140
  build tools (i.e., ``MSVC_VERSION='14.0'`` or ``MSVC_TOOLSET_VERSION='14.0'``).

* ``MSVC_SPECTRE_LIBS=True`` and ``MSVC_UWP_APP=True`` is not supported (i.e., there
  are no spectre mitigations libraries for UWP builds).

Default Toolset Version
-----------------------

Side-by-side toolset versions were introduced in Visual Studio 2017.
The examples shown below are for Visual Studio 2022.

The msvc default toolset version is dependent on the installation options
selected.  This means that the default toolset version may be different for
each machine given the same Visual Studio product.

The msvc default toolset is not necessarily the latest toolset installed.
This has implications when a toolset version is specified using only one minor
digit (e.g., ``MSVC_TOOLSET_VERSION='14.3'`` or ``MSVC_SCRIPT_ARGS='-vcvars_ver=14.3'``).

Explicitly defining ``MSVC_TOOLSET_VERSION=None`` will return the same toolset
that the msvc batch files would return.  When using ``MSVC_SCRIPT_ARGS``, the
toolset specification should be omitted entirely.

Local installation and summary test results:
::
    VS2022\VC\Auxiliary\Build\Microsoft.VCToolsVersion.v143.default.txt
        14.31.31103

    VS2022\VC\Auxiliary\Build\Microsoft.VCToolsVersion.default.txt
        14.32.31326

Toolset version summary:
::
    14.31.31103   Environment()
    14.31.31103   Environment(MSVC_TOOLSET_VERSION=None)

    14.32.31326*  Environment(MSVC_TOOLSET_VERSION='14.3')
    14.32.31326*  Environment(MSVC_SCRIPT_ARGS=['-vcvars_ver=14.3'])

    14.31.31103   Environment(MSVC_TOOLSET_VERSION='14.31')
    14.31.31103   Environment(MSVC_SCRIPT_ARGS=['-vcvars_ver=14.31'])

    14.32.31326   Environment(MSVC_TOOLSET_VERSION='14.32')
    14.32.31326   Environment(MSVC_SCRIPT_ARGS=['-vcvars_ver=14.32'])

VS2022\\Common7\\Tools\\vsdevcmd\\ext\\vcvars.bat usage fragment:
::
    @echo     -vcvars_ver=version : Version of VC++ Toolset to select
    @echo            ** [Default]   : If -vcvars_ver=version is NOT specified, the toolset specified by
    @echo                             [VSInstallDir]\VC\Auxiliary\Build\Microsoft.VCToolsVersion.v143.default.txt will be used.
    @echo            ** 14.0        : VS 2015 (v140) VC++ Toolset (installation of the v140 toolset is a prerequisite)
    @echo            ** 14.xx       : VS 2017 or VS 2019 VC++ Toolset, if that version is installed on the system under
    @echo                             [VSInstallDir]\VC\MSVC\Tools\[version].  Where '14.xx' specifies a partial
    @echo                             [version]. The latest [version] directory that matches the specified value will
    @echo                             be used.
    @echo            ** 14.xx.yyyyy : VS 2017 or VS 2019 VC++ Toolset, if that version is installed on the system under
    @echo                             [VSInstallDir]\VC\MSVC\Tools\[version]. Where '14.xx.yyyyy' specifies an
    @echo                             exact [version] directory to be used.
    @echo            ** 14.xx.VV.vv : VS 2019 C++ side-by-side toolset package identity alias, if the SxS toolset has been installed on the system.
    @echo                             Where '14.xx.VV.vv' corresponds to a SxS toolset
    @echo                                 VV = VS Update Major Version (e.g. "16" for VS 2019 v16.9)
    @echo                                 vv = VS Update Minor version (e.g. "9" for VS 2019 v16.9)
    @echo                             Please see [VSInstallDir]\VC\Auxiliary\Build\[version]\Microsoft.VCToolsVersion.[version].txt for mapping of
    @echo                             SxS toolset to [VSInstallDir]\VC\MSVC\Tools\ directory.

VS2022 batch file fragment to determine the default toolset version:
::
    @REM Add MSVC
    set "__VCVARS_DEFAULT_CONFIG_FILE=%VCINSTALLDIR%Auxiliary\Build\Microsoft.VCToolsVersion.default.txt"

    @REM We will "fallback" to Microsoft.VCToolsVersion.default.txt (latest) if Microsoft.VCToolsVersion.v143.default.txt does not exist.
    if EXIST "%VCINSTALLDIR%Auxiliary\Build\Microsoft.VCToolsVersion.v143.default.txt" (
        if "%VSCMD_DEBUG%" GEQ "2" @echo [DEBUG:ext\%~nx0] Microsoft.VCToolsVersion.v143.default.txt was found.
        set "__VCVARS_DEFAULT_CONFIG_FILE=%VCINSTALLDIR%Auxiliary\Build\Microsoft.VCToolsVersion.v143.default.txt"

    ) else (
        if "%VSCMD_DEBUG%" GEQ "1" @echo [DEBUG:ext\%~nx0] Microsoft.VCToolsVersion.v143.default.txt was not found. Defaulting to 'Microsoft.VCToolsVersion.default.txt'.
    )

Empirical evidence suggests that the default toolset version is different from the latest
toolset version when the toolset version immediately preceding the latest version is
installed.  For example, the ``14.31`` toolset version is installed when the ``14.32``
toolset version is the latest.


Visual Studio Version Notes
============================

SDK Versions
------------

==== ============
SDK  Format
==== ============
10.0 10.0.XXXXX.Y
---- ------------
8.1  8.1
==== ============

BuildTools Versions
-------------------

========== ===== ===== ========
BuildTools VCVER CLVER MSVCRT
========== ===== ===== ========
v143       14.3   19.3 140/ucrt
---------- ----- ----- --------
v142       14.2   19.2 140/ucrt
---------- ----- ----- --------
v141       14.1   19.1 140/ucrt
---------- ----- ----- --------
v140       14.0   19.0 140/ucrt
---------- ----- ----- --------
v120       12.0   18.0 120
---------- ----- ----- --------
v110       11.0   17.0 110
---------- ----- ----- --------
v100       10.0   16.0 100
---------- ----- ----- --------
v90         9.0   15.0  90
---------- ----- ----- --------
v80         8.0   14.0  80
---------- ----- ----- --------
v71         7.1   13.1  71
---------- ----- ----- --------
v70         7.0   13.0  70
---------- ----- ----- --------
v60         6.0   12.0  60
========== ===== ===== ========

Product Versions
----------------

======== ===== ========= ============
Product  VSVER SDK       BuildTools
======== ===== ========= ============
2022     17.0  10.0, 8.1 v143 .. v140
-------- ----- --------- ------------
2019     16.0  10.0, 8.1 v142 .. v140
-------- ----- --------- ------------
2017     15.0  10.0, 8.1 v141 .. v140
-------- ----- --------- ------------
2015     14.0  10.0, 8.1 v140
-------- ----- --------- ------------
2013     12.0            v120
-------- ----- --------- ------------
2012     11.0            v110
-------- ----- --------- ------------
2010     10.0            v100
-------- ----- --------- ------------
2008      9.0            v90
-------- ----- --------- ------------
2005      8.0            v80
-------- ----- --------- ------------
2003.NET  7.1            v71
-------- ----- --------- ------------
2002.NET  7.0            v70
-------- ----- --------- ------------
6.0       6.0            v60
======== ===== ========= ============


SCons Implementation Notes
==========================

Compiler Detection Logic
------------------------

**WARNING: the compiler detection logic documentation below is likely out-of-date.**

In the future, the compiler detection logic documentation will be updated and integrated
into the current document format as appropriate.

::

    This is the flow of the compiler detection logic:

    External to MSCommon:

      The Tool init modules, in their exists() routines, call -> msvc_exists(env)

    At the moment, those modules are:
      SCons/Tool/midl.py
      SCons/Tool/mslib.py
      SCons/Tool/mslink.py
      SCons/Tool/msvc.py
      SCons/Tool/msvs.py

    env may contain a version request in MSVC_VERSION, but this is not used
    in the detection that follows from msvc_exists(), only in the later
    batch that starts with a call to msvc_setup_env().

    Internal to MSCommon/vc.py:

    + MSCommon/vc.py:msvc_exists:
    | vcs = cached_get_installed_vcs(env)
    | returns True if vcs > 0
    |
    +-> MSCommon/vc.py:cached_get_installed_vcs:
      | checks global if we've run previously, if so return it
      | populate the global from -> get_installed_vcs(env)
      |
      +-> MSCommon/vc.py:get_installed_vcs:
        | loop through "known" versions of msvc, granularity is maj.min
        |   check for product dir -> find_vc_pdir(env, ver)
        |
        +-> MSCommon/vc.py:find_vc_pdir:
          | From the msvc-version to pdir mapping dict, get reg key base and value
          | If value is none -> find_vc_pdir_vswhere(ver, env)
          |
          +-> MSCommon/vc.py:find_vc_pdir_vswhere:
            | From the vc-version to VS-version mapping table get string
            | Figure out where vswhere is -> msvc_find_vswhere()
            | Use subprocess to call vswhere, return first line of match
            /
          | else get product directory from registry (<= 14.0)
          /
        | if we found one -> _check_cl_exists_in_vc_dir(env, pdir, ver)
        |
        +-> MSCommon/vc.py:_check_cl_exists_in_vc_dir:
          | Figure out host/target pair
          | if version > 14.0 get specific version by looking in
          |    pdir + Auxiliary/Build/Microsoft/VCToolsVersion/default.txt
          |    look for pdir + Tools/MSVC/{specver}/bin/host/target/cl.exe
          | if 14.0 or less, "do older stuff"

    All of this just got us a yes-no answer on whether /some/ msvc version
    exists, but does populate __INSTALLED_VCS_RUN with all of the top-level
    versions as noted for get_installed_vcs

    Externally:

      Once a module's exists() has been called (or, in the case of
      clang/clangxx, after the compiler has been detected by other means -
      those still expect the rest of the msvc chain but not cl.exe)
      the module's generate() function calls -> msvc_setup_env_once(env)

    Internally:

    + MSCommon/vc.py:msvc_setup_env_once:
    | checks for environment flag MSVC_SETUP_RUN
    | if not, -> msvc_setup_env(env) and set flag
    |
    +-+ MSCommon/vc.py:msvc_setup_env:
      | set ver from -> get_default_version(env)
      |
      +-+ MSCommon/vc.py:get_default_version:
        | if no version specified in env.MSVC_VERSION:
        |   return first entry from -> cached_get_installed_vcs(env)
        | else return requested version
        /
      | get script from MSVC_USE_SCRIPT if set to a filename
      | -> script_env(script)
      |
      +-+ MSCommon/vc.py:script_env:
        | return (possibly cached) script variables matching script arg
        /
      | else -> msvc_find_valid_batch_script(env, version)
      |
      +-+ MSCommon/vc.py:msvc_find_valid_batch_script:
        | Build a list of plausible target values, and loop through
        |   look for host + target -> find_batch_file(env, ver, host, target)
        |
        +-+ MSCommon/vc.py:find_batch_file:
          | call -> find_vc_pdir (see above)
          | use the return to construct a version-biased batfile path, check
          /
        | if not found, try sdk scripts (unknown if this is still useful)


    Problems:
    - For VS >= 2017, VS and VS are not 1:1, there can be many VC for one VS
    - For vswhere-ready versions, detection does not proceed beyond the
      product level ("2019") into individual "features" (individual msvc)
    - As documented for MSVC_VERSION, compilers can only be requested if versions
      are from the set in _VCVER, so 14.1 but not 14.16 or 14.16.27023
    - Information found in the first pass (msvs_exists) isn't really
      available anywhere except the cached version list, since we just
      return true/false.
    - Since msvc_exists chain of calls does not look at version, we
      can proceed to compiler setup if *any* msvc was found, even if the
      one requested wasn't found.