/* * Copyright 2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gradle.nativeplatform.toolchain.internal.msvcpp; import net.rubygrapefruit.platform.MissingRegistryEntryException; import net.rubygrapefruit.platform.WindowsRegistry; import org.apache.commons.lang.StringUtils; import org.gradle.internal.FileUtils; import org.gradle.internal.os.OperatingSystem; import org.gradle.util.TreeVisitor; import org.gradle.util.VersionNumber; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.util.HashMap; import java.util.List; import java.util.Map; public class DefaultWindowsSdkLocator implements WindowsSdkLocator { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultWindowsSdkLocator.class); private static final String REGISTRY_BASEPATHS[] = { "SOFTWARE\\", "SOFTWARE\\Wow6432Node\\" }; private static final String REGISTRY_ROOTPATH_SDK = "Microsoft\\Microsoft SDKs\\Windows"; private static final String REGISTRY_ROOTPATH_KIT = "Microsoft\\Windows Kits\\Installed Roots"; private static final String REGISTRY_FOLDER = "InstallationFolder"; private static final String REGISTRY_VERSION = "ProductVersion"; private static final String REGISTRY_NAME = "ProductName"; private static final String REGISTRY_KIT_8 = "KitsRoot"; private static final String REGISTRY_KIT_81 = "KitsRoot81"; private static final String VERSION_KIT_8 = "8.0"; private static final String VERSION_KIT_81 = "8.1"; private static final String VERSION_USER = "user"; private static final String NAME_USER = "User-provided Windows SDK"; private static final String NAME_KIT = "Windows Kit"; private static final String RESOURCE_PATHS[] = { "bin/x86/", "bin/" }; private static final String KERNEL32_PATHS[] = { "lib/winv6.3/um/x86/", "lib/win8/um/x86/", "lib/" }; private static final String RESOURCE_FILENAME = "rc.exe"; private static final String KERNEL32_FILENAME = "kernel32.lib"; private final Map<File, WindowsSdk> foundSdks = new HashMap<File, WindowsSdk>(); private final OperatingSystem os; private final WindowsRegistry windowsRegistry; private WindowsSdk pathSdk; private boolean initialised; public DefaultWindowsSdkLocator(OperatingSystem os, WindowsRegistry windowsRegistry) { this.os = os; this.windowsRegistry = windowsRegistry; } @Override public SearchResult locateWindowsSdks(File candidate) { if (!initialised) { locateSdksInRegistry(); locateKitsInRegistry(); locateSdkInPath(); initialised = true; } if (candidate != null) { return locateUserSpecifiedSdk(candidate); } return locateDefaultSdk(); } private void locateSdksInRegistry() { for (String baseKey : REGISTRY_BASEPATHS) { locateSdksInRegistry(baseKey); } } private void locateSdksInRegistry(String baseKey) { try { List<String> subkeys = windowsRegistry.getSubkeys(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, baseKey + REGISTRY_ROOTPATH_SDK); for (String subkey : subkeys) { try { String basePath = baseKey + REGISTRY_ROOTPATH_SDK + "\\" + subkey; File sdkDir = FileUtils.canonicalize(new File(windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, basePath, REGISTRY_FOLDER))); String version = formatVersion(windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, basePath, REGISTRY_VERSION)); String name = windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, basePath, REGISTRY_NAME); if (isWindowsSdk(sdkDir)) { LOGGER.debug("Found Windows SDK {} at {}", version, sdkDir); addSdk(sdkDir, version, name); } else { LOGGER.debug("Ignoring candidate Windows SDK directory {} as it does not look like a Windows SDK installation.", sdkDir); } } catch (MissingRegistryEntryException e) { // Ignore the subkey if it doesn't have a folder and version } } } catch (MissingRegistryEntryException e) { // No SDK information available in the registry } } private void locateKitsInRegistry() { for (String baseKey : REGISTRY_BASEPATHS) { locateKitsInRegistry(baseKey); } } private void locateKitsInRegistry(String baseKey) { String[] versions = { VERSION_KIT_8, VERSION_KIT_81 }; String[] keys = { REGISTRY_KIT_8, REGISTRY_KIT_81 }; for (int i = 0; i != keys.length; ++i) { try { File kitDir = FileUtils.canonicalize(new File(windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, baseKey + REGISTRY_ROOTPATH_KIT, keys[i]))); if (isWindowsSdk(kitDir)) { LOGGER.debug("Found Windows Kit {} at {}", versions[i], kitDir); addSdk(kitDir, versions[i], NAME_KIT + " " + versions[i]); } else { LOGGER.debug("Ignoring candidate Windows Kit directory {} as it does not look like a Windows Kit installation.", kitDir); } } catch (MissingRegistryEntryException e) { // Ignore the version if the string cannot be read } } } private void locateSdkInPath() { File resourceCompiler = os.findInPath(RESOURCE_FILENAME); if (resourceCompiler == null) { LOGGER.debug("Could not find Windows resource compiler in system path."); return; } File sdkDir = FileUtils.canonicalize(resourceCompiler.getParentFile().getParentFile()); if (!isWindowsSdk(sdkDir)) { sdkDir = sdkDir.getParentFile(); if (!isWindowsSdk(sdkDir)) { LOGGER.debug("Ignoring candidate Windows SDK for {} as it does not look like a Windows SDK installation.", resourceCompiler); } } LOGGER.debug("Found Windows SDK {} using system path", sdkDir); if (!foundSdks.containsKey(sdkDir)) { addSdk(sdkDir, "path", "Path-resolved Windows SDK"); } pathSdk = foundSdks.get(sdkDir); } private SearchResult locateUserSpecifiedSdk(File candidate) { File sdkDir = FileUtils.canonicalize(candidate); if (!isWindowsSdk(sdkDir)) { return new SdkNotFound(String.format("The specified installation directory '%s' does not appear to contain a Windows SDK installation.", candidate)); } if (!foundSdks.containsKey(sdkDir)) { addSdk(sdkDir, VERSION_USER, NAME_USER); } return new SdkFound(foundSdks.get(sdkDir)); } private SearchResult locateDefaultSdk() { if (pathSdk != null) { return new SdkFound(pathSdk); } WindowsSdk candidate = null; for (WindowsSdk windowsSdk : foundSdks.values()) { if (candidate == null || windowsSdk.getVersion().compareTo(candidate.getVersion()) > 0) { candidate = windowsSdk; } } return candidate == null ? new SdkNotFound("Could not locate a Windows SDK installation, using the Windows registry and system path.") : new SdkFound(candidate); } private void addSdk(File path, String version, String name) { foundSdks.put(path, new WindowsSdk(path, VersionNumber.parse(version), name)); } private static boolean isWindowsSdk(File candidate) { boolean hasResourceCompiler = false; boolean hasKernel32Lib = false; for (String path : RESOURCE_PATHS) { if (new File(candidate, path + RESOURCE_FILENAME).isFile()) { hasResourceCompiler = true; break; } } for (String path : KERNEL32_PATHS) { if (new File(candidate, path + KERNEL32_FILENAME).isFile()) { hasKernel32Lib = true; break; } } return hasResourceCompiler && hasKernel32Lib; } private static String formatVersion(String version) { int index = StringUtils.ordinalIndexOf(version, ".", 2); if (index != -1) { version = version.substring(0, index); } return version; } private static class SdkFound implements SearchResult { private final WindowsSdk sdk; public SdkFound(WindowsSdk sdk) { this.sdk = sdk; } @Override public WindowsSdk getSdk() { return sdk; } @Override public boolean isAvailable() { return true; } @Override public void explain(TreeVisitor<? super String> visitor) { } } private static class SdkNotFound implements SearchResult { private final String message; private SdkNotFound(String message) { this.message = message; } @Override public WindowsSdk getSdk() { return null; } @Override public boolean isAvailable() { return false; } @Override public void explain(TreeVisitor<? super String> visitor) { visitor.node(message); } } }