1# - Find the Windows SDK aka Platform SDK
2#
3# Relevant Wikipedia article: http://en.wikipedia.org/wiki/Microsoft_Windows_SDK
4#
5# Pass "COMPONENTS tools" to ignore Visual Studio version checks: in case
6# you just want the tool binaries to run, rather than the libraries and headers
7# for compiling.
8#
9# Variables:
10#  WINDOWSSDK_FOUND - if any version of the windows or platform SDK was found that is usable with the current version of visual studio
11#  WINDOWSSDK_LATEST_DIR
12#  WINDOWSSDK_LATEST_NAME
13#  WINDOWSSDK_FOUND_PREFERENCE - if we found an entry indicating a "preferred" SDK listed for this visual studio version
14#  WINDOWSSDK_PREFERRED_DIR
15#  WINDOWSSDK_PREFERRED_NAME
16#
17#  WINDOWSSDK_DIRS - contains no duplicates, ordered most recent first.
18#  WINDOWSSDK_PREFERRED_FIRST_DIRS - contains no duplicates, ordered with preferred first, followed by the rest in descending recency
19#
20# Functions:
21#  windowssdk_name_lookup(<directory> <output variable>) - Find the name corresponding with the SDK directory you pass in, or
22#     NOTFOUND if not recognized. Your directory must be one of WINDOWSSDK_DIRS for this to work.
23#
24#  windowssdk_build_lookup(<directory> <output variable>) - Find the build version number corresponding with the SDK directory you pass in, or
25#     NOTFOUND if not recognized. Your directory must be one of WINDOWSSDK_DIRS for this to work.
26#
27#  get_windowssdk_from_component(<file or dir> <output variable>) - Given a library or include dir,
28#     find the Windows SDK root dir corresponding to it, or NOTFOUND if unrecognized.
29#
30#  get_windowssdk_library_dirs(<directory> <output variable>) - Find the architecture-appropriate
31#     library directories corresponding to the SDK directory you pass in (or NOTFOUND if none)
32#
33#  get_windowssdk_library_dirs_multiple(<output variable> <directory> ...) - Find the architecture-appropriate
34#     library directories corresponding to the SDK directories you pass in, in order, skipping those not found. NOTFOUND if none at all.
35#     Good for passing WINDOWSSDK_DIRS or WINDOWSSDK_DIRS to if you really just want a file and don't care where from.
36#
37#  get_windowssdk_include_dirs(<directory> <output variable>) - Find the
38#     include directories corresponding to the SDK directory you pass in (or NOTFOUND if none)
39#
40#  get_windowssdk_include_dirs_multiple(<output variable> <directory> ...) - Find the
41#     include directories corresponding to the SDK directories you pass in, in order, skipping those not found. NOTFOUND if none at all.
42#     Good for passing WINDOWSSDK_DIRS or WINDOWSSDK_DIRS to if you really just want a file and don't care where from.
43#
44# Requires these CMake modules:
45#  FindPackageHandleStandardArgs (known included with CMake >=2.6.2)
46#
47# Original Author:
48# 2012 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
49# http://academic.cleardefinition.com
50# Iowa State University HCI Graduate Program/VRAC
51#
52# Copyright Iowa State University 2012.
53# Distributed under the Boost Software License, Version 1.0.
54# (See accompanying file LICENSE_1_0.txt or copy at
55# http://www.boost.org/LICENSE_1_0.txt)
56
57set(_preferred_sdk_dirs) # pre-output
58set(_win_sdk_dirs) # pre-output
59set(_win_sdk_versanddirs) # pre-output
60set(_win_sdk_buildsanddirs) # pre-output
61set(_winsdk_vistaonly) # search parameters
62set(_winsdk_kits) # search parameters
63
64
65set(_WINDOWSSDK_ANNOUNCE OFF)
66if(NOT WINDOWSSDK_FOUND AND (NOT WindowsSDK_FIND_QUIETLY))
67	set(_WINDOWSSDK_ANNOUNCE ON)
68endif()
69macro(_winsdk_announce)
70	if(_WINSDK_ANNOUNCE)
71		message(STATUS ${ARGN})
72	endif()
73endmacro()
74
75set(_winsdk_win10vers
76	10.0.10586.0 # TH2 aka Win10 1511
77	10.0.10240.0 # Win10 RTM
78	10.0.10150.0 # just ucrt
79	10.0.10056.0
80)
81
82if(WindowsSDK_FIND_COMPONENTS MATCHES "tools")
83	set(_WINDOWSSDK_IGNOREMSVC ON)
84	_winsdk_announce("Checking for tools from Windows/Platform SDKs...")
85else()
86	set(_WINDOWSSDK_IGNOREMSVC OFF)
87	_winsdk_announce("Checking for Windows/Platform SDKs...")
88endif()
89
90# Appends to the three main pre-output lists used only if the path exists
91# and is not already in the list.
92function(_winsdk_conditional_append _vername _build _path)
93	if(("${_path}" MATCHES "registry") OR (NOT EXISTS "${_path}"))
94		# Path invalid - do not add
95		return()
96	endif()
97	list(FIND _win_sdk_dirs "${_path}" _win_sdk_idx)
98	if(_win_sdk_idx GREATER -1)
99		# Path already in list - do not add
100		return()
101	endif()
102	_winsdk_announce( " - ${_vername}, Build ${_build} @ ${_path}")
103	# Not yet in the list, so we'll add it
104	list(APPEND _win_sdk_dirs "${_path}")
105	set(_win_sdk_dirs "${_win_sdk_dirs}" CACHE INTERNAL "" FORCE)
106	list(APPEND
107		_win_sdk_versanddirs
108		"${_vername}"
109		"${_path}")
110	set(_win_sdk_versanddirs "${_win_sdk_versanddirs}" CACHE INTERNAL "" FORCE)
111	list(APPEND
112		_win_sdk_buildsanddirs
113		"${_build}"
114		"${_path}")
115	set(_win_sdk_buildsanddirs "${_win_sdk_buildsanddirs}" CACHE INTERNAL "" FORCE)
116endfunction()
117
118# Appends to the "preferred SDK" lists only if the path exists
119function(_winsdk_conditional_append_preferred _info _path)
120	if(("${_path}" MATCHES "registry") OR (NOT EXISTS "${_path}"))
121		# Path invalid - do not add
122		return()
123	endif()
124
125	get_filename_component(_path "${_path}" ABSOLUTE)
126
127	list(FIND _win_sdk_preferred_sdk_dirs "${_path}" _win_sdk_idx)
128	if(_win_sdk_idx GREATER -1)
129		# Path already in list - do not add
130		return()
131	endif()
132	_winsdk_announce( " - Found \"preferred\" SDK ${_info} @ ${_path}")
133	# Not yet in the list, so we'll add it
134	list(APPEND _win_sdk_preferred_sdk_dirs "${_path}")
135	set(_win_sdk_preferred_sdk_dirs "${_win_sdk_dirs}" CACHE INTERNAL "" FORCE)
136
137	# Just in case we somehow missed it:
138	_winsdk_conditional_append("${_info}" "" "${_path}")
139endfunction()
140
141# Given a version like v7.0A, looks for an SDK in the registry under "Microsoft SDKs".
142# If the given version might be in both HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows
143# and HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots aka "Windows Kits",
144# use this macro first, since these registry keys usually have more information.
145#
146# Pass a "default" build number as an extra argument in case we can't find it.
147function(_winsdk_check_microsoft_sdks_registry _winsdkver)
148	set(SDKKEY "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\${_winsdkver}")
149	get_filename_component(_sdkdir
150		"[${SDKKEY};InstallationFolder]"
151		ABSOLUTE)
152
153	set(_sdkname "Windows SDK ${_winsdkver}")
154
155	# Default build number passed as extra argument
156	set(_build ${ARGN})
157	# See if the registry holds a Microsoft-mutilated, err, designated, product name
158	# (just using get_filename_component to execute the registry lookup)
159	get_filename_component(_sdkproductname
160		"[${SDKKEY};ProductName]"
161		NAME)
162	if(NOT "${_sdkproductname}" MATCHES "registry")
163		# Got a product name
164		set(_sdkname "${_sdkname} (${_sdkproductname})")
165	endif()
166
167	# try for a version to augment our name
168	# (just using get_filename_component to execute the registry lookup)
169	get_filename_component(_sdkver
170		"[${SDKKEY};ProductVersion]"
171		NAME)
172	if(NOT "${_sdkver}" MATCHES "registry" AND NOT MATCHES)
173		# Got a version
174		if(NOT "${_sdkver}" MATCHES "\\.\\.")
175			# and it's not an invalid one with two dots in it:
176			# use to override the default build
177			set(_build ${_sdkver})
178			if(NOT "${_sdkname}" MATCHES "${_sdkver}")
179				# Got a version that's not already in the name, let's use it to improve our name.
180				set(_sdkname "${_sdkname} (${_sdkver})")
181			endif()
182		endif()
183	endif()
184	_winsdk_conditional_append("${_sdkname}" "${_build}" "${_sdkdir}")
185endfunction()
186
187# Given a name for identification purposes, the build number, and a key (technically a "value name")
188# corresponding to a Windows SDK packaged as a "Windows Kit", look for it
189# in HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots
190# Note that the key or "value name" tends to be something weird like KitsRoot81 -
191# no easy way to predict, just have to observe them in the wild.
192# Doesn't hurt to also try _winsdk_check_microsoft_sdks_registry for these:
193# sometimes you get keys in both parts of the registry (in the wow64 portion especially),
194# and the non-"Windows Kits" location is often more descriptive.
195function(_winsdk_check_windows_kits_registry _winkit_name _winkit_build _winkit_key)
196	get_filename_component(_sdkdir
197		"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots;${_winkit_key}]"
198		ABSOLUTE)
199	_winsdk_conditional_append("${_winkit_name}" "${_winkit_build}" "${_sdkdir}")
200endfunction()
201
202# Given a name for identification purposes and the build number
203# corresponding to a Windows 10 SDK packaged as a "Windows Kit", look for it
204# in HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots
205# Doesn't hurt to also try _winsdk_check_microsoft_sdks_registry for these:
206# sometimes you get keys in both parts of the registry (in the wow64 portion especially),
207# and the non-"Windows Kits" location is often more descriptive.
208function(_winsdk_check_win10_kits _winkit_build)
209	get_filename_component(_sdkdir
210		"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots;KitsRoot10]"
211		ABSOLUTE)
212	if(("${_sdkdir}" MATCHES "registry") OR (NOT EXISTS "${_sdkdir}"))
213		return() # not found
214	endif()
215	if(EXISTS "${_sdkdir}/Include/${_winkit_build}/um")
216		_winsdk_conditional_append("Windows Kits 10 (Build ${_winkit_build})" "${_winkit_build}" "${_sdkdir}")
217	endif()
218endfunction()
219
220# Given a name for indentification purposes, the build number, and the associated package GUID,
221# look in the registry under both HKLM and HKCU in \\SOFTWARE\\Microsoft\\MicrosoftSDK\\InstalledSDKs\\
222# for that guid and the SDK it points to.
223function(_winsdk_check_platformsdk_registry _platformsdkname _build _platformsdkguid)
224	foreach(_winsdk_hive HKEY_LOCAL_MACHINE HKEY_CURRENT_USER)
225		get_filename_component(_sdkdir
226			"[${_winsdk_hive}\\SOFTWARE\\Microsoft\\MicrosoftSDK\\InstalledSDKs\\${_platformsdkguid};Install Dir]"
227			ABSOLUTE)
228		_winsdk_conditional_append("${_platformsdkname} (${_build})" "${_build}" "${_sdkdir}")
229	endforeach()
230endfunction()
231
232###
233# Detect toolchain information: to know whether it's OK to use Vista+ only SDKs
234###
235set(_winsdk_vistaonly_ok OFF)
236if(MSVC AND NOT _WINDOWSSDK_IGNOREMSVC)
237	# VC 10 and older has broad target support
238	if(MSVC_VERSION LESS 1700)
239		# VC 11 by default targets Vista and later only, so we can add a few more SDKs that (might?) only work on vista+
240	elseif("${CMAKE_VS_PLATFORM_TOOLSET}" MATCHES "_xp")
241		# This is the XP-compatible v110+ toolset
242	elseif("${CMAKE_VS_PLATFORM_TOOLSET}" STREQUAL "v100" OR "${CMAKE_VS_PLATFORM_TOOLSET}" STREQUAL "v90")
243		# This is the VS2010/VS2008 toolset
244	else()
245		# OK, we're VC11 or newer and not using a backlevel or XP-compatible toolset.
246		# These versions have no XP (and possibly Vista pre-SP1) support
247		set(_winsdk_vistaonly_ok ON)
248		if(_WINDOWSSDK_ANNOUNCE AND NOT _WINDOWSSDK_VISTAONLY_PESTERED)
249			set(_WINDOWSSDK_VISTAONLY_PESTERED ON CACHE INTERNAL "" FORCE)
250			message(STATUS "FindWindowsSDK: Detected Visual Studio 2012 or newer, not using the _xp toolset variant: including SDK versions that drop XP support in search!")
251		endif()
252	endif()
253endif()
254if(_WINDOWSSDK_IGNOREMSVC)
255	set(_winsdk_vistaonly_ok ON)
256endif()
257
258###
259# MSVC version checks - keeps messy conditionals in one place
260# (messy because of _WINDOWSSDK_IGNOREMSVC)
261###
262set(_winsdk_msvc_greater_1200 OFF)
263if(_WINDOWSSDK_IGNOREMSVC OR (MSVC AND (MSVC_VERSION GREATER 1200)))
264	set(_winsdk_msvc_greater_1200 ON)
265endif()
266# Newer than VS .NET/VS Toolkit 2003
267set(_winsdk_msvc_greater_1310 OFF)
268if(_WINDOWSSDK_IGNOREMSVC OR (MSVC AND (MSVC_VERSION GREATER 1310)))
269	set(_winsdk_msvc_greater_1310 ON)
270endif()
271
272# VS2005/2008
273set(_winsdk_msvc_less_1600 OFF)
274if(_WINDOWSSDK_IGNOREMSVC OR (MSVC AND (MSVC_VERSION LESS 1600)))
275	set(_winsdk_msvc_less_1600 ON)
276endif()
277
278# VS2013+
279set(_winsdk_msvc_not_less_1800 OFF)
280if(_WINDOWSSDK_IGNOREMSVC OR (MSVC AND (NOT MSVC_VERSION LESS 1800)))
281	set(_winsdk_msvc_not_less_1800 ON)
282endif()
283
284###
285# START body of find module
286###
287if(_winsdk_msvc_greater_1310) # Newer than VS .NET/VS Toolkit 2003
288	###
289	# Look for "preferred" SDKs
290	###
291
292	# Environment variable for SDK dir
293	if(EXISTS "$ENV{WindowsSDKDir}" AND (NOT "$ENV{WindowsSDKDir}" STREQUAL ""))
294		_winsdk_conditional_append_preferred("WindowsSDKDir environment variable" "$ENV{WindowsSDKDir}")
295	endif()
296
297	if(_winsdk_msvc_less_1600)
298		# Per-user current Windows SDK for VS2005/2008
299		get_filename_component(_sdkdir
300			"[HKEY_CURRENT_USER\\Software\\Microsoft\\Microsoft SDKs\\Windows;CurrentInstallFolder]"
301			ABSOLUTE)
302		_winsdk_conditional_append_preferred("Per-user current Windows SDK" "${_sdkdir}")
303
304		# System-wide current Windows SDK for VS2005/2008
305		get_filename_component(_sdkdir
306			"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows;CurrentInstallFolder]"
307			ABSOLUTE)
308		_winsdk_conditional_append_preferred("System-wide current Windows SDK" "${_sdkdir}")
309	endif()
310
311	###
312	# Begin the massive list of SDK searching!
313	###
314	if(_winsdk_vistaonly_ok AND _winsdk_msvc_not_less_1800)
315		# These require at least Visual Studio 2013 (VC12)
316
317		_winsdk_check_microsoft_sdks_registry(v10.0A)
318
319		# Windows Software Development Kit (SDK) for Windows 10
320		# Several different versions living in the same directory - if nothing else we can assume RTM (10240)
321		_winsdk_check_microsoft_sdks_registry(v10.0 10.0.10240.0)
322		foreach(_win10build ${_winsdk_win10vers})
323			_winsdk_check_win10_kits(${_win10build})
324		endforeach()
325	endif() # vista-only and 2013+
326
327	# Included in Visual Studio 2013
328	# Includes the v120_xp toolset
329	_winsdk_check_microsoft_sdks_registry(v8.1A 8.1.51636)
330
331	if(_winsdk_vistaonly_ok AND _winsdk_msvc_not_less_1800)
332		# Windows Software Development Kit (SDK) for Windows 8.1
333		# http://msdn.microsoft.com/en-gb/windows/desktop/bg162891
334		_winsdk_check_microsoft_sdks_registry(v8.1 8.1.25984.0)
335		_winsdk_check_windows_kits_registry("Windows Kits 8.1" 8.1.25984.0 KitsRoot81)
336	endif() # vista-only and 2013+
337
338	if(_winsdk_vistaonly_ok)
339		# Included in Visual Studio 2012
340		_winsdk_check_microsoft_sdks_registry(v8.0A 8.0.50727)
341
342		# Microsoft Windows SDK for Windows 8 and .NET Framework 4.5
343		# This is the first version to also include the DirectX SDK
344		# http://msdn.microsoft.com/en-US/windows/desktop/hh852363.aspx
345		_winsdk_check_microsoft_sdks_registry(v8.0 6.2.9200.16384)
346		_winsdk_check_windows_kits_registry("Windows Kits 8.0" 6.2.9200.16384 KitsRoot)
347	endif() # vista-only
348
349	# Included with VS 2012 Update 1 or later
350	# Introduces v110_xp toolset
351	_winsdk_check_microsoft_sdks_registry(v7.1A 7.1.51106)
352	if(_winsdk_vistaonly_ok)
353		# Microsoft Windows SDK for Windows 7 and .NET Framework 4
354		# http://www.microsoft.com/downloads/en/details.aspx?FamilyID=6b6c21d2-2006-4afa-9702-529fa782d63b
355		_winsdk_check_microsoft_sdks_registry(v7.1 7.1.7600.0.30514)
356	endif() # vista-only
357
358	# Included with VS 2010
359	_winsdk_check_microsoft_sdks_registry(v7.0A 6.1.7600.16385)
360
361	# Windows SDK for Windows 7 and .NET Framework 3.5 SP1
362	# Works with VC9
363	# http://www.microsoft.com/en-us/download/details.aspx?id=18950
364	_winsdk_check_microsoft_sdks_registry(v7.0 6.1.7600.16385)
365
366	# Two versions call themselves "v6.1":
367	# Older:
368	# Windows Vista Update & .NET 3.0 SDK
369	# http://www.microsoft.com/en-us/download/details.aspx?id=14477
370
371	# Newer:
372	# Windows Server 2008 & .NET 3.5 SDK
373	# may have broken VS9SP1? they recommend v7.0 instead, or a KB...
374	# http://www.microsoft.com/en-us/download/details.aspx?id=24826
375	_winsdk_check_microsoft_sdks_registry(v6.1 6.1.6000.16384.10)
376
377	# Included in VS 2008
378	_winsdk_check_microsoft_sdks_registry(v6.0A 6.1.6723.1)
379
380	# Microsoft Windows Software Development Kit for Windows Vista and .NET Framework 3.0 Runtime Components
381	# http://blogs.msdn.com/b/stanley/archive/2006/11/08/microsoft-windows-software-development-kit-for-windows-vista-and-net-framework-3-0-runtime-components.aspx
382	_winsdk_check_microsoft_sdks_registry(v6.0 6.0.6000.16384)
383endif()
384
385# Let's not forget the Platform SDKs, which sometimes are useful!
386if(_winsdk_msvc_greater_1200)
387	_winsdk_check_platformsdk_registry("Microsoft Platform SDK for Windows Server 2003 R2" "5.2.3790.2075.51" "D2FF9F89-8AA2-4373-8A31-C838BF4DBBE1")
388	_winsdk_check_platformsdk_registry("Microsoft Platform SDK for Windows Server 2003 SP1" "5.2.3790.1830.15" "8F9E5EF3-A9A5-491B-A889-C58EFFECE8B3")
389endif()
390###
391# Finally, look for "preferred" SDKs
392###
393if(_winsdk_msvc_greater_1310) # Newer than VS .NET/VS Toolkit 2003
394
395
396	# Environment variable for SDK dir
397	if(EXISTS "$ENV{WindowsSDKDir}" AND (NOT "$ENV{WindowsSDKDir}" STREQUAL ""))
398		_winsdk_conditional_append_preferred("WindowsSDKDir environment variable" "$ENV{WindowsSDKDir}")
399	endif()
400
401	if(_winsdk_msvc_less_1600)
402		# Per-user current Windows SDK for VS2005/2008
403		get_filename_component(_sdkdir
404			"[HKEY_CURRENT_USER\\Software\\Microsoft\\Microsoft SDKs\\Windows;CurrentInstallFolder]"
405			ABSOLUTE)
406		_winsdk_conditional_append_preferred("Per-user current Windows SDK" "${_sdkdir}")
407
408		# System-wide current Windows SDK for VS2005/2008
409		get_filename_component(_sdkdir
410			"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows;CurrentInstallFolder]"
411			ABSOLUTE)
412		_winsdk_conditional_append_preferred("System-wide current Windows SDK" "${_sdkdir}")
413	endif()
414endif()
415
416
417function(windowssdk_name_lookup _dir _outvar)
418	list(FIND _win_sdk_versanddirs "${_dir}" _diridx)
419	math(EXPR _idx "${_diridx} - 1")
420	if(${_idx} GREATER -1)
421		list(GET _win_sdk_versanddirs ${_idx} _ret)
422	else()
423		set(_ret "NOTFOUND")
424	endif()
425	set(${_outvar} "${_ret}" PARENT_SCOPE)
426endfunction()
427
428function(windowssdk_build_lookup _dir _outvar)
429	list(FIND _win_sdk_buildsanddirs "${_dir}" _diridx)
430	math(EXPR _idx "${_diridx} - 1")
431	if(${_idx} GREATER -1)
432		list(GET _win_sdk_buildsanddirs ${_idx} _ret)
433	else()
434		set(_ret "NOTFOUND")
435	endif()
436	set(${_outvar} "${_ret}" PARENT_SCOPE)
437endfunction()
438
439# If we found something...
440if(_win_sdk_dirs)
441	list(GET _win_sdk_dirs 0 WINDOWSSDK_LATEST_DIR)
442	windowssdk_name_lookup("${WINDOWSSDK_LATEST_DIR}"
443		WINDOWSSDK_LATEST_NAME)
444	set(WINDOWSSDK_DIRS ${_win_sdk_dirs})
445
446	# Fallback, in case no preference found.
447	set(WINDOWSSDK_PREFERRED_DIR "${WINDOWSSDK_LATEST_DIR}")
448	set(WINDOWSSDK_PREFERRED_NAME "${WINDOWSSDK_LATEST_NAME}")
449	set(WINDOWSSDK_PREFERRED_FIRST_DIRS ${WINDOWSSDK_DIRS})
450	set(WINDOWSSDK_FOUND_PREFERENCE OFF)
451endif()
452
453# If we found indications of a user preference...
454if(_win_sdk_preferred_sdk_dirs)
455	list(GET _win_sdk_preferred_sdk_dirs 0 WINDOWSSDK_PREFERRED_DIR)
456	windowssdk_name_lookup("${WINDOWSSDK_PREFERRED_DIR}"
457		WINDOWSSDK_PREFERRED_NAME)
458	set(WINDOWSSDK_PREFERRED_FIRST_DIRS
459		${_win_sdk_preferred_sdk_dirs}
460		${_win_sdk_dirs})
461	list(REMOVE_DUPLICATES WINDOWSSDK_PREFERRED_FIRST_DIRS)
462	set(WINDOWSSDK_FOUND_PREFERENCE ON)
463endif()
464
465include(FindPackageHandleStandardArgs)
466find_package_handle_standard_args(WindowsSDK
467	"No compatible version of the Windows SDK or Platform SDK found."
468	WINDOWSSDK_DIRS)
469
470if(WINDOWSSDK_FOUND)
471	# Internal: Architecture-appropriate library directory names.
472	if("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "ARM")
473		if(CMAKE_SIZEOF_VOID_P MATCHES "8")
474			# Only supported in Win10 SDK and up.
475			set(_winsdk_arch8 arm64) # what the WDK for Win8+ calls this architecture
476		else()
477			set(_winsdk_archbare /arm) # what the architecture used to be called in oldest SDKs
478			set(_winsdk_arch arm) # what the architecture used to be called
479			set(_winsdk_arch8 arm) # what the WDK for Win8+ calls this architecture
480		endif()
481	else()
482		if(CMAKE_SIZEOF_VOID_P MATCHES "8")
483			set(_winsdk_archbare /x64) # what the architecture used to be called in oldest SDKs
484			set(_winsdk_arch amd64) # what the architecture used to be called
485			set(_winsdk_arch8 x64) # what the WDK for Win8+ calls this architecture
486		else()
487			set(_winsdk_archbare ) # what the architecture used to be called in oldest SDKs
488			set(_winsdk_arch i386) # what the architecture used to be called
489			set(_winsdk_arch8 x86) # what the WDK for Win8+ calls this architecture
490		endif()
491	endif()
492
493	function(get_windowssdk_from_component _component _var)
494		get_filename_component(_component "${_component}" ABSOLUTE)
495		file(TO_CMAKE_PATH "${_component}" _component)
496		foreach(_sdkdir ${WINDOWSSDK_DIRS})
497			get_filename_component(_sdkdir "${_sdkdir}" ABSOLUTE)
498			string(LENGTH "${_sdkdir}" _sdklen)
499			file(RELATIVE_PATH _rel "${_sdkdir}" "${_component}")
500			# If we don't have any "parent directory" items...
501			if(NOT "${_rel}" MATCHES "[.][.]")
502				set(${_var} "${_sdkdir}" PARENT_SCOPE)
503				return()
504			endif()
505		endforeach()
506		# Fail.
507		set(${_var} "NOTFOUND" PARENT_SCOPE)
508	endfunction()
509	function(get_windowssdk_library_dirs _winsdk_dir _var)
510		set(_result)
511		set(_suffixes
512			"lib${_winsdk_archbare}" # SDKs like 7.1A
513			"lib/${_winsdk_arch}" # just because some SDKs have x86 dir and root dir
514			"lib/w2k/${_winsdk_arch}" # Win2k min requirement
515			"lib/wxp/${_winsdk_arch}" # WinXP min requirement
516			"lib/wnet/${_winsdk_arch}" # Win Server 2003 min requirement
517			"lib/wlh/${_winsdk_arch}"
518			"lib/wlh/um/${_winsdk_arch8}" # Win Vista ("Long Horn") min requirement
519			"lib/win7/${_winsdk_arch}"
520			"lib/win7/um/${_winsdk_arch8}" # Win 7 min requirement
521		)
522		foreach(_ver
523			wlh # Win Vista ("Long Horn") min requirement
524			win7 # Win 7 min requirement
525			win8 # Win 8 min requirement
526			winv6.3 # Win 8.1 min requirement
527		)
528
529			list(APPEND _suffixes
530				"lib/${_ver}/${_winsdk_arch}"
531				"lib/${_ver}/um/${_winsdk_arch8}"
532				"lib/${_ver}/km/${_winsdk_arch8}"
533			)
534		endforeach()
535
536		# Look for WDF libraries in Win10+ SDK
537		foreach(_mode umdf kmdf)
538			file(GLOB _wdfdirs RELATIVE "${_winsdk_dir}" "${_winsdk_dir}/lib/wdf/${_mode}/${_winsdk_arch8}/*")
539			if(_wdfdirs)
540				list(APPEND _suffixes ${_wdfdirs})
541			endif()
542		endforeach()
543
544		# Look in each Win10+ SDK version for the components
545		foreach(_win10ver ${_winsdk_win10vers})
546			foreach(_component um km ucrt mmos)
547				list(APPEND _suffixes "lib/${_win10ver}/${_component}/${_winsdk_arch8}")
548			endforeach()
549		endforeach()
550
551		foreach(_suffix ${_suffixes})
552			# Check to see if a library actually exists here.
553			file(GLOB _libs "${_winsdk_dir}/${_suffix}/*.lib")
554			if(_libs)
555				list(APPEND _result "${_winsdk_dir}/${_suffix}")
556			endif()
557		endforeach()
558		if(NOT _result)
559			set(_result NOTFOUND)
560		else()
561			list(REMOVE_DUPLICATES _result)
562		endif()
563		set(${_var} ${_result} PARENT_SCOPE)
564	endfunction()
565	function(get_windowssdk_include_dirs _winsdk_dir _var)
566		set(_result)
567
568		set(_subdirs shared um winrt km wdf mmos ucrt)
569		set(_suffixes Include)
570
571		foreach(_dir ${_subdirs})
572			list(APPEND _suffixes "Include/${_dir}")
573		endforeach()
574
575		foreach(_ver ${_winsdk_win10vers})
576			foreach(_dir ${_subdirs})
577				list(APPEND _suffixes "Include/${_ver}/${_dir}")
578			endforeach()
579		endforeach()
580
581		foreach(_suffix ${_suffixes})
582			# Check to see if a header file actually exists here.
583			file(GLOB _headers "${_winsdk_dir}/${_suffix}/*.h")
584			if(_headers)
585				list(APPEND _result "${_winsdk_dir}/${_suffix}")
586			endif()
587		endforeach()
588		if(NOT _result)
589			set(_result NOTFOUND)
590		else()
591			list(REMOVE_DUPLICATES _result)
592		endif()
593		set(${_var} ${_result} PARENT_SCOPE)
594	endfunction()
595	function(get_windowssdk_library_dirs_multiple _var)
596		set(_result)
597		foreach(_sdkdir ${ARGN})
598			get_windowssdk_library_dirs("${_sdkdir}" _current_sdk_libdirs)
599			if(_current_sdk_libdirs)
600				list(APPEND _result ${_current_sdk_libdirs})
601			endif()
602		endforeach()
603		if(NOT _result)
604			set(_result NOTFOUND)
605		else()
606			list(REMOVE_DUPLICATES _result)
607		endif()
608		set(${_var} ${_result} PARENT_SCOPE)
609	endfunction()
610	function(get_windowssdk_include_dirs_multiple _var)
611		set(_result)
612		foreach(_sdkdir ${ARGN})
613			get_windowssdk_include_dirs("${_sdkdir}" _current_sdk_incdirs)
614			if(_current_sdk_libdirs)
615				list(APPEND _result ${_current_sdk_incdirs})
616			endif()
617		endforeach()
618		if(NOT _result)
619			set(_result NOTFOUND)
620		else()
621			list(REMOVE_DUPLICATES _result)
622		endif()
623		set(${_var} ${_result} PARENT_SCOPE)
624	endfunction()
625endif()
626