/*
* Copyright 2015 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 java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
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 net.rubygrapefruit.platform.MissingRegistryEntryException;
import net.rubygrapefruit.platform.WindowsRegistry;
public class DefaultUcrtLocator implements UcrtLocator {
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultUcrtLocator.class);
private static final String REGISTRY_BASEPATHS[] = {
"SOFTWARE\\",
"SOFTWARE\\Wow6432Node\\"
};
private static final String REGISTRY_ROOTPATH_KIT = "Microsoft\\Windows Kits\\Installed Roots";
private static final String REGISTRY_KIT_10 = "KitsRoot10";
private static final String VERSION_USER = "user";
private static final String NAME_USER = "User-provided UCRT";
private static final String NAME_KIT = "UCRT";
private final Map<File, Set<Ucrt>> foundUcrts = new HashMap<File, Set<Ucrt>>();
private final OperatingSystem os;
private final WindowsRegistry windowsRegistry;
private boolean initialised;
public DefaultUcrtLocator(OperatingSystem os, WindowsRegistry windowsRegistry) {
this.os = os;
this.windowsRegistry = windowsRegistry;
}
public SearchResult locateUcrts(File candidate) {
if (!initialised) {
locateUcrtsInRegistry();
initialised = true;
}
if (candidate != null) {
return locateUserSpecifiedUcrt(candidate);
}
return locateDefaultUcrt();
}
private void locateUcrtsInRegistry() {
for (String baseKey : REGISTRY_BASEPATHS) {
locateUcrtsInRegistry(baseKey);
}
}
private void locateUcrtsInRegistry(String baseKey) {
String[] keys = {
REGISTRY_KIT_10
};
for (String key : keys) {
try {
File ucrtDir = FileUtils.canonicalize(new File(windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, baseKey + REGISTRY_ROOTPATH_KIT, key)));
String[] versionDirs = getUcrtVersionDirs(ucrtDir);
if (versionDirs.length > 0) {
for (String versionDir : versionDirs) {
VersionNumber version = VersionNumber.withPatchNumber().parse(versionDir);
LOGGER.debug("Found ucrt {} at {}", version.toString(), ucrtDir);
putUcrt(new Ucrt(ucrtDir, NAME_KIT + " " + version.getMajor(), version));
}
} else {
LOGGER.debug("Ignoring candidate ucrt directory {} as it does not look like a ucrt installation.", ucrtDir);
}
} catch (MissingRegistryEntryException e) {
// Ignore the version if the string cannot be read
}
}
}
private SearchResult locateUserSpecifiedUcrt(File candidate) {
File ucrtDir = FileUtils.canonicalize(candidate);
String[] versionDirs = getUcrtVersionDirs(ucrtDir);
if (versionDirs.length > 0) {
for (String versionDir : versionDirs) {
VersionNumber version = VersionNumber.withPatchNumber().parse(versionDir);
LOGGER.debug("Found ucrt {} ({}) at {}", version.toString(), versionDir, ucrtDir);
if (!foundUcrts.containsKey(ucrtDir)) {
putUcrt(new Ucrt(ucrtDir, NAME_USER, version));
}
}
return new UcrtFound(getBestUcrt(candidate));
} else {
return new UcrtNotFound(String.format("The specified installation directory '%s' does not appear to contain a ucrt installation.", candidate));
}
}
private SearchResult locateDefaultUcrt() {
Ucrt selected = getBestUcrt();
return selected == null
? new UcrtNotFound("Could not locate a ucrt installation using the Windows registry.")
: new UcrtFound(selected);
}
private static String[] getUcrtVersionDirs(File candidate) {
final Pattern ucrtVersionPattern = Pattern.compile("[0-9]+(\\.[0-9]+)*");
FileFilter ucrtVersionFilter = new FileFilter() {
@Override
public boolean accept(File pathname) {
Matcher matcher = ucrtVersionPattern.matcher(pathname.getName());
if (pathname.isDirectory() && matcher.matches()) {
return true;
}
return false;
}
};
File includeDir = new File(candidate, "Include");
File libDir = new File(candidate, "Lib");
if (!includeDir.isDirectory() || !libDir.isDirectory()) {
return new String[0];
}
Map<String, File> includeDirs = new HashMap<String, File>();
for (File dir : includeDir.listFiles(ucrtVersionFilter)) {
includeDirs.put(dir.getName(), dir);
}
Map<String, File> libDirs = new HashMap<String, File>();
for (File dir : libDir.listFiles(ucrtVersionFilter)) {
libDirs.put(dir.getName(), dir);
}
Set<String> ucrtVersions = new HashSet<String>();
ucrtVersions.addAll(includeDirs.keySet());
ucrtVersions.addAll(libDirs.keySet());
List<String> result = new ArrayList<String>();
for (String version : ucrtVersions) {
File inc = includeDirs.get(version);
File lib = includeDirs.get(version);
if (inc != null && lib != null) {
File ucrtInc = new File(inc, "ucrt");
File ucrtLib = new File(lib, "ucrt");
if (ucrtInc.isDirectory() && ucrtLib.isDirectory()) {
result.add(version);
}
}
}
return result.toArray(new String[result.size()]);
}
private void putUcrt(Ucrt ucrt) {
Set<Ucrt> ucrts = foundUcrts.get(ucrt.getBaseDir());
if (ucrts == null) {
ucrts = new HashSet<Ucrt>();
foundUcrts.put(ucrt.getBaseDir(), ucrts);
}
ucrts.add(ucrt);
}
private Ucrt getBestUcrt(File baseDir) {
final SortedSet<Ucrt> candidates = new TreeSet<Ucrt>(new DescendingUcrtVersionComparator());
Set<Ucrt> ucrts = foundUcrts.get(baseDir);
if (ucrts != null) {
candidates.addAll(ucrts);
}
return candidates.isEmpty() ? null : candidates.iterator().next();
}
private Ucrt getBestUcrt() {
final SortedSet<Ucrt> candidates = new TreeSet<Ucrt>(new DescendingUcrtVersionComparator());
for (Set<Ucrt> ucrts : foundUcrts.values()) {
candidates.addAll(ucrts);
}
return candidates.isEmpty() ? null : candidates.iterator().next();
}
private static class UcrtFound implements SearchResult {
private final Ucrt ucrt;
public UcrtFound(Ucrt ucrt) {
this.ucrt = ucrt;
}
public Ucrt getUcrt() {
return ucrt;
}
public boolean isAvailable() {
return true;
}
public void explain(TreeVisitor<? super String> visitor) {
}
}
private static class UcrtNotFound implements SearchResult {
private final String message;
private UcrtNotFound(String message) {
this.message = message;
}
public Ucrt getUcrt() {
return null;
}
public boolean isAvailable() {
return false;
}
public void explain(TreeVisitor<? super String> visitor) {
visitor.node(message);
}
}
private static class DescendingUcrtVersionComparator implements Comparator<Ucrt> {
@Override
public int compare(Ucrt o1, Ucrt o2) {
return o2.getVersion().compareTo(o1.getVersion());
}
}
}