From 4ab5ab00b021bbf463878939cb27ef3a7994b61e Mon Sep 17 00:00:00 2001 From: Fabio Niephaus Date: Wed, 1 Feb 2023 12:55:50 +0100 Subject: [PATCH 1/3] Set up build environment for Windows users. And provide better error messages in case Visual Studio is missing. --- .../com/oracle/svm/driver/NativeImage.java | 3 + .../driver/WindowsBuildEnvironmentUtil.java | 152 ++++++++++++++++++ 2 files changed, 155 insertions(+) create mode 100644 substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/WindowsBuildEnvironmentUtil.java diff --git a/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImage.java b/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImage.java index c18ce54fafd7..324c2d23df82 100644 --- a/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImage.java +++ b/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImage.java @@ -1428,6 +1428,9 @@ protected int buildImage(List javaArgs, LinkedHashSet cp, LinkedHa ProcessBuilder pb = new ProcessBuilder(); pb.command(command); pb.environment().put(ModuleSupport.ENV_VAR_USE_MODULE_SYSTEM, Boolean.toString(config.modulePathBuild)); + if (OS.getCurrent() == OS.WINDOWS) { + WindowsBuildEnvironmentUtil.propagateEnv(pb.environment()); + } sanitizeJVMEnvironment(pb.environment()); p = pb.inheritIO().start(); imageBuilderPid = p.pid(); diff --git a/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/WindowsBuildEnvironmentUtil.java b/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/WindowsBuildEnvironmentUtil.java new file mode 100644 index 000000000000..d5d6fdbba6a7 --- /dev/null +++ b/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/WindowsBuildEnvironmentUtil.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.driver; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.stream.Stream; + +/** + * This utility helps set up a build environment for Windows users automatically. + */ +class WindowsBuildEnvironmentUtil { + private static final String VSINSTALLDIR_ENV = "VSINSTALLDIR"; + private static final Path[] KNOWN_VS_LOCATIONS = { + Paths.get("C:", "Program Files", "Microsoft Visual Studio"), + // prefer x64 location over x86 + Paths.get("C:", "Program Files (x86)", "Microsoft Visual Studio")}; + private static final String[] KNOWN_VS_EDITIONS = { + // prefer Enterprise over Professional over Community + "Enterprise", "Professional", "Community"}; + private static final String VCVARSALL = "vcvarsall.bat"; + private static final Path VCVARSALL_SUBPATH = Paths.get("VC", "Auxiliary", "Build", VCVARSALL); + private static final String OUTPUT_SEPARATOR = "!NEXTCOMMAND!"; + + static void propagateEnv(Map environment) { + if (isCCompilerOnPath()) { + return; // nothing to do, build environment initialized by user + } + Path vcVarsAllLocation = null; + for (Path visualStudioLocation : KNOWN_VS_LOCATIONS) { + if (!Files.isDirectory(visualStudioLocation)) { + continue; + } + List installationCandidates; + try (Stream pathStream = Files.list(visualStudioLocation)) { + installationCandidates = pathStream + // only keep sub-directories matching 20\d\d + .filter(p -> p.toFile().getName().matches("20\\d\\d")) + // sort years + .sorted(Comparator.comparing(p -> p.toFile().getName())) + // reverse order to ensure latest year is first + .sorted(Comparator.reverseOrder()) + .toList(); + } catch (IOException e) { + throw fail("Failed to traverse known Visual Studio locations.", e); + } + for (Path installation : installationCandidates) { + for (String edition : KNOWN_VS_EDITIONS) { + Path possibleLocation = installation.resolve(edition).resolve(VCVARSALL_SUBPATH); + if (Files.isRegularFile(possibleLocation) && Files.isReadable(possibleLocation)) { + vcVarsAllLocation = possibleLocation; + break; + } + } + } + } + if (vcVarsAllLocation == null) { + throw fail(String.format("Failed to find '%s' in a Visual Studio installation.", VCVARSALL)); + } + Map originalEnv = new HashMap<>(); + int numSeenOutputSeparators = 0; + try { + // call `set`, then `vcvarsall.bat`, and then `set` again with separators in between + String commandSequence = String.format("cmd.exe /c set && echo %s && \"%s\" x64 && echo %s && set", + OUTPUT_SEPARATOR, vcVarsAllLocation, OUTPUT_SEPARATOR); + Process p = Runtime.getRuntime().exec(new String[]{"cmd.exe", "/c", commandSequence}); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()))) { + String line = null; + while ((line = reader.readLine()) != null) { + if (line.startsWith(OUTPUT_SEPARATOR)) { + numSeenOutputSeparators++; + } else if (numSeenOutputSeparators == 0) { + // collect environment variables from 1st `set` invocation + processLineWithKeyValue(line, (key, value) -> originalEnv.put(key, value)); + } else if (numSeenOutputSeparators == 2) { + // iterate through updated environment variables from 2nd `set` invocation + processLineWithKeyValue(line, (key, value) -> { + boolean isNewOrModifiedEnvVar = !originalEnv.getOrDefault(key, "").equals(value); + if (isNewOrModifiedEnvVar) { + environment.put(key, value); + } + }); + } + } + } + p.waitFor(); + } catch (IOException | InterruptedException e) { + throw fail("Failed to detect variables of Windows build environment.", e); + } + if (!environment.containsKey(VSINSTALLDIR_ENV)) { + throw fail("Failed to automatically set up Windows build environment."); + } + } + + private static boolean isCCompilerOnPath() { + try { + return Runtime.getRuntime().exec(new String[]{"cmd.exe", "/c", "where", "cl.exe"}).waitFor() == 0; + } catch (IOException | InterruptedException e) { + throw NativeImage.showError("Failed to check for 'cl.exe'.", e); + } + } + + private static void processLineWithKeyValue(String line, BiConsumer consumeKeyValue) { + String[] parts = line.split("="); + if (parts.length == 2) { + consumeKeyValue.accept(parts[0], parts[1]); + } + } + + private static Error fail(String reason) { + return fail(reason, null); + } + + private static Error fail(String reason, Throwable e) { + throw NativeImage.showError(reason + System.lineSeparator() + + "Please make sure that Visual Studio 2017 version 15.5 or later (C/C++ Optimizing Compiler Version 19.12 or later) is installed on your system. " + + "You can download it at https://visualstudio.microsoft.com/downloads/. " + + "If this error persists, please try and run GraalVM Native Image in an x64 Native Tools Command Prompt or file a ticket.", + e); + } +} From 48cd620853c771327ee474bfe9a7507e625801a2 Mon Sep 17 00:00:00 2001 From: Fabio Niephaus Date: Wed, 1 Feb 2023 12:57:28 +0100 Subject: [PATCH 2/3] Update docs on Windows build environments for NI. --- docs/getting-started/graalvm-community/windows.md | 3 --- .../getting-started/graalvm-enterprise/installation-windows.md | 3 --- docs/reference-manual/native-image/FAQ.md | 3 +-- docs/reference-manual/native-image/README.md | 3 +-- docs/tools/vscode/graalvm/README.md | 2 +- docs/tools/vscode/micronaut/README.md | 2 -- 6 files changed, 3 insertions(+), 13 deletions(-) diff --git a/docs/getting-started/graalvm-community/windows.md b/docs/getting-started/graalvm-community/windows.md index 2e1c534d9445..bde754381ba3 100644 --- a/docs/getting-started/graalvm-community/windows.md +++ b/docs/getting-started/graalvm-community/windows.md @@ -60,7 +60,4 @@ There are two installation options: - Install the Visual Studio Build Tools with the Windows 10 SDK - Install Visual Studio with the Windows 10 SDK -The last prerequisite is the proper [Developer Command Prompt](https://docs.microsoft.com/en-us/cpp/build/building-on-the-command-line?view=vs-2019#developer_command_prompt_shortcuts) for your version of [Visual Studio](https://visualstudio.microsoft.com/vs/). -On Windows, the `native-image` tool only works when it is executed from the **x64 Native Tools Command Prompt**. - Step-by-step instructions on installing Visual Studio Build Tools and Windows 10 SDK, and starting using Native Image can be found [here](https://medium.com/graalvm/using-graalvm-and-native-image-on-windows-10-9954dc071311). diff --git a/docs/getting-started/graalvm-enterprise/installation-windows.md b/docs/getting-started/graalvm-enterprise/installation-windows.md index 9e86d18dc93c..6bc3cb24fa9e 100644 --- a/docs/getting-started/graalvm-enterprise/installation-windows.md +++ b/docs/getting-started/graalvm-enterprise/installation-windows.md @@ -55,7 +55,4 @@ There are two installation options: - Install the Visual Studio Build Tools with the Windows 10 SDK - Install Visual Studio with the Windows 10 SDK -The last prerequisite is the proper [Developer Command Prompt](https://docs.microsoft.com/en-us/cpp/build/building-on-the-command-line?view=vs-2019#developer_command_prompt_shortcuts) for your version of [Visual Studio](https://visualstudio.microsoft.com/vs/). -On Windows the `native-image` tool only works when it is executed from the **x64 Native Tools Command Prompt**. - Step by step instructions on installing Visual Studio Build Tools and Windows 10 SDK, and starting using Native Image can be found [here](https://medium.com/graalvm/using-graalvm-and-native-image-on-windows-10-9954dc071311). diff --git a/docs/reference-manual/native-image/FAQ.md b/docs/reference-manual/native-image/FAQ.md index 3ee78d68ad73..51f6850bae6a 100644 --- a/docs/reference-manual/native-image/FAQ.md +++ b/docs/reference-manual/native-image/FAQ.md @@ -20,8 +20,7 @@ Everything that is included in a product (GraalVM Native Image) or produced by a ### Native Image doesn't work on my Windows 10? -Make sure you execute the `native-image` command from the **x64 Native Tools Command Prompt**. -This is the prerequisite for Windows: installing [Visual Studio](https://visualstudio.microsoft.com/vs/) and Microsoft Visual C++ (MSVC) with the Windows 10 SDK. +Please make sure [Visual Studio](https://visualstudio.microsoft.com/vs/) and Microsoft Visual C++ (MSVC) with the Windows 10 SDK are installed on your system. You can use Visual Studio 2017 version 15.9 or later. Check [this link](https://medium.com/graalvm/using-graalvm-and-native-image-on-windows-10-9954dc071311) for more information and step-by-step instructions. diff --git a/docs/reference-manual/native-image/README.md b/docs/reference-manual/native-image/README.md index 75833e01c570..059b1e5546ce 100644 --- a/docs/reference-manual/native-image/README.md +++ b/docs/reference-manual/native-image/README.md @@ -93,8 +93,7 @@ There are two installation options: You can use Visual Studio 2017 version 15.9 or later. -The `native-image` builder will only work when it is run from the **x64 Native Tools Command Prompt**. -The command for initiating an x64 Native Tools command prompt varies according to whether you only have the Visual Studio Build Tools installed or if you have the full Visual Studio 2019 installed. For more information, see [Using GraalVM and Native Image on Windows 10](https://medium.com/graalvm/using-graalvm-and-native-image-on-windows-10-9954dc071311). --> +For more information, see [Using GraalVM and Native Image on Windows 10](https://medium.com/graalvm/using-graalvm-and-native-image-on-windows-10-9954dc071311). --> ## Build a Native Executable diff --git a/docs/tools/vscode/graalvm/README.md b/docs/tools/vscode/graalvm/README.md index a22120f31682..5cc84192e95a 100644 --- a/docs/tools/vscode/graalvm/README.md +++ b/docs/tools/vscode/graalvm/README.md @@ -233,7 +233,7 @@ If you use the Micronaut framework to create your Java project, you can build a ### Building Native Images on Windows To use Native Image on Windows, you need Visual Studio Build Tools with Windows SDK installed. -The `native-image` builder only works when it is executed from the **x64 Native Tools Command Prompt**. Check [this link](https://medium.com/graalvm/using-graalvm-and-native-image-on-windows-10-9954dc071311) for more details. +Check [this link](https://medium.com/graalvm/using-graalvm-and-native-image-on-windows-10-9954dc071311) for more details. For Windows users, the extension provides a pre-configured x64 command prompt using Microsoft Developer Tools. Assuming you already have Visual Studio Build Tools with Windows SDK, GraalVM with Native Image installed, and opened your Java project in VS Code. diff --git a/docs/tools/vscode/micronaut/README.md b/docs/tools/vscode/micronaut/README.md index 5142ba9d3ce6..4ffe892309a8 100644 --- a/docs/tools/vscode/micronaut/README.md +++ b/docs/tools/vscode/micronaut/README.md @@ -171,8 +171,6 @@ For more information, visit the [Micronaut documentation](https://guides.microna ### Windows Users -On Windows the `native-image` builder will only work when it is executed from the **x64 Native Tools Command Prompt**. - For Micronaut users targeting GraalVM Native Image for their Micronaut applications, the extension provides a dedicated **x64 command prompt using Microsoft Developer Tools**. You can check it by invoking the **Micronaut: Build Native Image** task from Command Palette. Notice which shell is active: From 602bba6dcce323bb7243e96a946e1553808ca35d Mon Sep 17 00:00:00 2001 From: Fabio Niephaus Date: Wed, 1 Feb 2023 14:31:54 +0100 Subject: [PATCH 3/3] Add `CHANGELOG.md` entry. --- substratevm/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/substratevm/CHANGELOG.md b/substratevm/CHANGELOG.md index ac9484015d95..8825b66b05fb 100644 --- a/substratevm/CHANGELOG.md +++ b/substratevm/CHANGELOG.md @@ -15,6 +15,7 @@ This changelog summarizes major changes to GraalVM Native Image. * (GR-41912) The builder now generated reports for internal errors, which users can share when creating issues. By default, error reports follow the `svm_err_b__pid.md` pattern and are created in the working directory. Use `-H:ErrorFile` to adjust the path or filename. * (GR-36951) Add [RISC-V support](https://medium.com/p/899be38eddd9) for Native Image through the LLVM backend. * (GR-42964) Deprecate `--enable-monitoring` without an argument. The option will no longer default to `all` in a future release. Instead, please always explicitly specify the list of monitoring features to be enabled (for example, `--enable-monitoring=heapdump,jfr,jvmstat`"). +* (GR-19890) Native Image now sets up build environments for Windows users automatically. Running in an x64 Native Tools Command Prompt is no longer a requirement. ## Version 22.3.0 * (GR-35721) Remove old build output style and the `-H:±BuildOutputUseNewStyle` option.