/* * Copyright 2017 Google LLC. * * 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 com.google.cloud.tools.managedcloudsdk.components; import com.google.cloud.tools.managedcloudsdk.command.CommandCaller; import com.google.cloud.tools.managedcloudsdk.command.CommandExecutionException; import com.google.cloud.tools.managedcloudsdk.command.CommandExitException; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableMap; import com.google.common.io.MoreFiles; import com.google.common.io.RecursiveDeleteOption; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; import java.util.List; import java.util.Map; public class WindowsBundledPythonCopier implements BundledPythonCopier { private final Path gcloud; private final CommandCaller commandCaller; WindowsBundledPythonCopier(Path gcloud, CommandCaller commandCaller) { this.gcloud = gcloud; this.commandCaller = commandCaller; } @Override public Map<String, String> copyPython() throws InterruptedException, CommandExitException, CommandExecutionException { List<String> copyPythonCommand = Arrays.asList(gcloud.toString(), "components", "copy-bundled-python"); // The path returned from gcloud points to the "python.exe" binary, e.g., // c:/users/ieuser/appdata/local/temp/tmpjmkt_z/python/python.exe // However, it may not copy but return an existing Python executable. // // A trim() required to remove newlines from call result. Using new lines in windows // environment passed through via ProcessBuilder will result in cryptic : "The syntax of // the command is incorrect." String pythonExePath = commandCaller.call(copyPythonCommand, null, null).trim(); if (isUnderTempDirectory(pythonExePath, System.getenv())) { Runtime.getRuntime().addShutdownHook(new Thread(() -> deleteCopiedPython(pythonExePath))); } return ImmutableMap.of("CLOUDSDK_PYTHON", pythonExePath); } @VisibleForTesting static boolean isUnderTempDirectory(String pythonExePath, Map<String, String> environment) { Path pythonExe = Paths.get(pythonExePath); String temp = environment.get("TEMP"); String tmp = environment.get("TMP"); return (temp != null && pythonExe.startsWith(temp)) || (tmp != null && pythonExe.startsWith(tmp)); } @VisibleForTesting static void deleteCopiedPython(String pythonExePath) { // The path returned from gcloud points to the "python.exe" binary. Delete it from the path. String pythonHome = pythonExePath.replaceAll("[pP][yY][tT][hH][oO][nN]\\.[eE][xX][eE]$", ""); boolean endsWithPythonExe = !pythonHome.equals(pythonExePath); if (endsWithPythonExe) { // just to be safe try { MoreFiles.deleteRecursively(Paths.get(pythonHome), RecursiveDeleteOption.ALLOW_INSECURE); } catch (IOException e) { // not critical to remove a temp directory } } } }