1 /* 2 * Copyright (c) 2019, 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.*; 30 import java.text.MessageFormat; 31 import java.util.*; 32 import java.util.function.Supplier; 33 import java.util.stream.Collectors; 34 import java.util.stream.Stream; 35 36 /** 37 * WiX tool. 38 */ 39 public enum WixTool { 40 Candle, Light; 41 42 static final class ToolInfo { 43 ToolInfo(Path path, String version) { 44 this.path = path; 45 this.version = new DottedVersion(version); 46 } 47 48 final Path path; 49 final DottedVersion version; 50 } 51 52 static Map<WixTool, ToolInfo> toolset() throws ConfigException { 53 Map<WixTool, ToolInfo> toolset = new HashMap<>(); 54 for (var tool : values()) { 55 toolset.put(tool, tool.find()); 56 } 57 return toolset; 58 } 59 60 ToolInfo find() throws ConfigException { 61 final Path toolFileName = IOUtils.addSuffix( 62 Path.of(name().toLowerCase()), ".exe"); 63 64 String[] version = new String[1]; 65 ConfigException reason = createToolValidator(toolFileName, version).get(); 66 if (version[0] != null) { 67 if (reason == null) { 68 // Found in PATH. 69 return new ToolInfo(toolFileName, version[0]); 70 } 71 72 // Found in PATH, but something went wrong. 73 throw reason; 74 } 75 76 for (var dir : findWixInstallDirs()) { 77 Path path = dir.resolve(toolFileName); 78 if (path.toFile().exists()) { 79 reason = createToolValidator(path, version).get(); 80 if (reason != null) { 81 throw reason; 82 } 83 return new ToolInfo(path, version[0]); 84 } 85 } 86 87 throw reason; 88 } 89 90 private static Supplier<ConfigException> createToolValidator(Path toolPath, 91 String[] versionCtnr) { 92 return new ToolValidator(toolPath) 93 .setCommandLine("/?") 94 .setMinimalVersion(MINIMAL_VERSION) 95 .setToolNotFoundErrorHandler( 96 (name, ex) -> new ConfigException( 97 I18N.getString("error.no-wix-tools"), 98 I18N.getString("error.no-wix-tools.advice"))) 99 .setToolOldVersionErrorHandler( 100 (name, version) -> new ConfigException( 101 MessageFormat.format(I18N.getString( 102 "message.wrong-tool-version"), name, 103 version, MINIMAL_VERSION), 104 I18N.getString("error.no-wix-tools.advice"))) 105 .setVersionParser(output -> { 106 versionCtnr[0] = ""; 107 String firstLineOfOutput = output.findFirst().orElse(""); 108 int separatorIdx = firstLineOfOutput.lastIndexOf(' '); 109 if (separatorIdx == -1) { 110 return null; 111 } 112 versionCtnr[0] = firstLineOfOutput.substring(separatorIdx + 1); 113 return versionCtnr[0]; 114 })::validate; 115 } 116 117 private final static DottedVersion MINIMAL_VERSION = DottedVersion.lazy("3.0"); 118 119 static Path getSystemDir(String envVar, String knownDir) { 120 return Optional 121 .ofNullable(getEnvVariableAsPath(envVar)) 122 .orElseGet(() -> Optional 123 .ofNullable(getEnvVariableAsPath("SystemDrive")) 124 .orElseGet(() -> Path.of("C:")).resolve(knownDir)); 125 } 126 127 private static Path getEnvVariableAsPath(String envVar) { 128 String path = System.getenv(envVar); 129 if (path != null) { 130 try { 131 return Path.of(path); 132 } catch (InvalidPathException ex) { 133 Log.error(MessageFormat.format(I18N.getString( 134 "error.invalid-envvar"), envVar)); 135 } 136 } 137 return null; 138 } 139 140 private static List<Path> findWixInstallDirs() { 141 PathMatcher wixInstallDirMatcher = FileSystems.getDefault().getPathMatcher( 142 "glob:WiX Toolset v*"); 143 144 Path programFiles = getSystemDir("ProgramFiles", "\\Program Files"); 145 Path programFilesX86 = getSystemDir("ProgramFiles(x86)", 146 "\\Program Files (x86)"); 147 148 // Returns list of WiX install directories ordered by WiX version number. 149 // Newer versions go first. 150 return Stream.of(programFiles, programFilesX86).map(path -> { 151 List<Path> result; 152 try (var paths = Files.walk(path, 1)) { 153 result = paths.collect(Collectors.toList()); 154 } catch (IOException ex) { 155 Log.verbose(ex); 156 result = Collections.emptyList(); 157 } 158 return result; 159 }).flatMap(List::stream) 160 .filter(path -> wixInstallDirMatcher.matches(path.getFileName())) 161 .sorted(Comparator.comparing(Path::getFileName).reversed()) 162 .map(path -> path.resolve("bin")) 163 .collect(Collectors.toList()); 164 } 165 }