1 /* 2 * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.incubator.jpackage.internal; 27 28 import java.io.IOException; 29 import java.nio.file.FileSystems; 30 import java.nio.file.Files; 31 import java.nio.file.InvalidPathException; 32 import java.nio.file.Path; 33 import java.nio.file.PathMatcher; 34 import java.text.MessageFormat; 35 import java.util.Collections; 36 import java.util.Comparator; 37 import java.util.HashMap; 38 import java.util.List; 39 import java.util.Map; 40 import java.util.Optional; 41 import java.util.function.Supplier; 42 import java.util.stream.Collectors; 43 import java.util.stream.Stream; 44 45 /** 46 * WiX tool. 47 */ 48 public enum WixTool { 49 Candle, Light; 50 51 static final class ToolInfo { 52 ToolInfo(Path path, String version) { 53 this.path = path; 54 this.version = new DottedVersion(version); 55 } 56 57 final Path path; 58 final DottedVersion version; 59 } 60 61 static Map<WixTool, ToolInfo> toolset() throws ConfigException { 62 Map<WixTool, ToolInfo> toolset = new HashMap<>(); 63 for (var tool : values()) { 64 toolset.put(tool, tool.find()); 65 } 66 return toolset; 67 } 68 69 ToolInfo find() throws ConfigException { 70 final Path toolFileName = IOUtils.addSuffix( 71 Path.of(name().toLowerCase()), ".exe"); 72 73 String[] version = new String[1]; 74 ConfigException reason = createToolValidator(toolFileName, version).get(); 75 if (version[0] != null) { 76 if (reason == null) { 77 // Found in PATH. 78 return new ToolInfo(toolFileName, version[0]); 79 } 80 81 // Found in PATH, but something went wrong. 82 throw reason; 83 } 84 85 for (var dir : findWixInstallDirs()) { 86 Path path = dir.resolve(toolFileName); 87 if (Files.exists(path)) { 88 reason = createToolValidator(path, version).get(); 89 if (reason != null) { 90 throw reason; 91 } 92 return new ToolInfo(path, version[0]); 93 } 94 } 95 96 throw reason; 97 } 98 99 private static Supplier<ConfigException> createToolValidator(Path toolPath, 100 String[] versionCtnr) { 101 return new ToolValidator(toolPath) 102 .setCommandLine("/?") 103 .setMinimalVersion(MINIMAL_VERSION) 104 .setToolNotFoundErrorHandler( 105 (name, ex) -> new ConfigException( 106 I18N.getString("error.no-wix-tools"), 107 I18N.getString("error.no-wix-tools.advice"))) 108 .setToolOldVersionErrorHandler( 109 (name, version) -> new ConfigException( 110 MessageFormat.format(I18N.getString( 111 "message.wrong-tool-version"), name, 112 version, MINIMAL_VERSION), 113 I18N.getString("error.no-wix-tools.advice"))) 114 .setVersionParser(output -> { 115 versionCtnr[0] = ""; 116 String firstLineOfOutput = output.findFirst().orElse(""); 117 int separatorIdx = firstLineOfOutput.lastIndexOf(' '); 118 if (separatorIdx == -1) { 119 return null; 120 } 121 versionCtnr[0] = firstLineOfOutput.substring(separatorIdx + 1); 122 return versionCtnr[0]; 123 })::validate; 124 } 125 126 private final static DottedVersion MINIMAL_VERSION = DottedVersion.lazy("3.0"); 127 128 static Path getSystemDir(String envVar, String knownDir) { 129 return Optional 130 .ofNullable(getEnvVariableAsPath(envVar)) 131 .orElseGet(() -> Optional 132 .ofNullable(getEnvVariableAsPath("SystemDrive")) 133 .orElseGet(() -> Path.of("C:")).resolve(knownDir)); 134 } 135 136 private static Path getEnvVariableAsPath(String envVar) { 137 String path = System.getenv(envVar); 138 if (path != null) { 139 try { 140 return Path.of(path); 141 } catch (InvalidPathException ex) { 142 Log.error(MessageFormat.format(I18N.getString( 143 "error.invalid-envvar"), envVar)); 144 } 145 } 146 return null; 147 } 148 149 private static List<Path> findWixInstallDirs() { 150 PathMatcher wixInstallDirMatcher = FileSystems.getDefault().getPathMatcher( 151 "glob:WiX Toolset v*"); 152 153 Path programFiles = getSystemDir("ProgramFiles", "\\Program Files"); 154 Path programFilesX86 = getSystemDir("ProgramFiles(x86)", 155 "\\Program Files (x86)"); 156 157 // Returns list of WiX install directories ordered by WiX version number. 158 // Newer versions go first. 159 return Stream.of(programFiles, programFilesX86).map(path -> { 160 List<Path> result; 161 try (var paths = Files.walk(path, 1)) { 162 result = paths.collect(Collectors.toList()); 163 } catch (IOException ex) { 164 Log.verbose(ex); 165 result = Collections.emptyList(); 166 } 167 return result; 168 }).flatMap(List::stream) 169 .filter(path -> wixInstallDirMatcher.matches(path.getFileName())) 170 .sorted(Comparator.comparing(Path::getFileName).reversed()) 171 .map(path -> path.resolve("bin")) 172 .collect(Collectors.toList()); 173 } 174 }