[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/openjdk/detroit-python/master/GetPythonEnvironment.java [Back]  [Original]

/*
 * Copyright (c) 2025, 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.
 *
 * 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.
 */

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.ArrayList;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.Properties;

// Tool to get directory where Python's shared object/dll and where
// Python executable lives and write those to a Properties file specified.
public class GetPythonEnvironment {

    private GetPythonEnvironment() {
    }

    public enum OS {
        WINDOWS,
        LINUX,
        MAC,
        UNKNOWN
    }

    // detect the current OS
    private static OS getCurrentOS() {
        String osName = System.getProperty("os.name").toLowerCase();
        if (osName.contains("linux")) {
            return OS.LINUX;
        } else if (osName.contains("mac")) {
            return OS.MAC;
        } else if (osName.contains("win")) {
            return OS.WINDOWS;
        } else {
            return OS.UNKNOWN;
        }
    }

    private static final OS THE_OS = getCurrentOS();

    // result from a child Process
    record ExecResult(String output, int exitCode) {
    }

    private static ExecResult runCommand(String... commands) throws IOException, InterruptedException {
        return runCommand(false, commands);
    }

    // run a command in a child process and get its output
    private static ExecResult runCommand(boolean firstLine, String... commands) throws IOException, InterruptedException {
        ProcessBuilder builder = new ProcessBuilder(commands);
        builder.redirectErrorStream(true);
        Process process = builder.start();

        // collect all output lines
        var lines = new ArrayList();
        try (var reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
            String line;
            while ((line = reader.readLine()) != null) {
                lines.add(line);
            }
        }

        return new ExecResult(firstLine
                ? (lines.isEmpty() ? null : lines.get(0))
                : lines.stream().collect(Collectors.joining("\n")),
                process.waitFor());
    }

    // find a OS command using OS specific command search command
    private static String findCommand(String command) throws IOException, InterruptedException {
        ExecResult execResult;
        if (THE_OS == OS.WINDOWS) {
            execResult = runCommand(true, "where.exe", command);
        } else {
            execResult = runCommand(true, "which", command);
        }
        return execResult.exitCode() == 0 ? execResult.output() : null;
    }

    // check python version is expected minor version or not
    private static boolean checkPythonVersion(String pythonBin, String pythonMinor) throws IOException, InterruptedException {
        // Run 'python --version' and capture output (both stdout and stderr)
        ExecResult result = runCommand(pythonBin, "--version");
        String versionStr = result.output();
        if (versionStr == null || versionStr.trim().isEmpty()) {
            IO.println("Could not parse Python version.");
            return false;
        }

        // Regex to match 'Python 3.14.2' (or similar)
        var pattern = Pattern.compile("Python\\s+([0-9]+)\\.([0-9]+)\\.[0-9]+");
        var matcher = pattern.matcher(versionStr);
        if (matcher.matches()) {
            String major = matcher.group(1);
            String minor = matcher.group(2);

            if ("3".equals(major) && pythonMinor.equals(minor)) {
                IO.println("Python 3." + pythonMinor + ".x detected: " + versionStr);
                return true;
            } else {
                IO.println("Python is not 3." + pythonMinor + ".x: " + versionStr);
                return false;
            }
        } else {
            IO.println("Could not parse Python version.");
            return false;
        }
    }

    private static String getPythonBasePrefix(String pythonBin) throws IOException, InterruptedException {
        // get Python's sys.base_prefix value
        ExecResult execResult = runCommand(pythonBin, "-c", "import sys; print(sys.base_prefix)");
        if (execResult.exitCode != 0) {
            throw new IOException("getting python sys.base_prefix failed");
        }
        return execResult.output();
    }

    public static void main(String[] args) {
        try {
            // should pass version line 3.14 or 3.13 etc. Only major
            // and minor versions. Do not pass 3.13.2 for example!
            if (args.length != 2) {
                System.err.println("java JextractHelper  ");
                System.exit(1);
            }

            String[] parts = args[0].split("-")[0].split("\\.");
            String pythonVersion = parts[0] + "." + parts[1];
            Path targetFile = Paths.get(args[1]);
            if (THE_OS == OS.UNKNOWN) {
                System.err.println("Unsupported OS: " + System.getProperty("os.name"));
                System.exit(2);
            }

            // Try python version specified, else 3, else python
            String pythonBin = findCommand("python");
            if (pythonBin == null) {
                pythonBin = findCommand("python3");
            }
            if (pythonBin == null) {
                pythonBin = findCommand("python" + pythonVersion);
            }

            if (pythonBin != null) {
                IO.println("Using Python @ " + pythonBin);
            } else {
                IO.println("Python not found!");
                System.exit(3);
            }

            // make sure that we are on expected python version
            if (!checkPythonVersion(pythonBin, pythonVersion.substring("3.".length()))) {
                System.exit(4);
            }

            String pythonLibDir;
            if (THE_OS == OS.WINDOWS) {
                pythonLibDir = getPythonBasePrefix(pythonBin);
            } else {
                ExecResult execResult = runCommand(pythonBin, "-c",
                        "import sysconfig; print(sysconfig.get_config_var('LIBDIR'))");
                if (execResult.exitCode() != 0) {
                    System.err.println("getting python sysconfig.get_config_var('LIBDIR') failed");
                    System.exit(5);
                }
                pythonLibDir = execResult.output();
            }

            ExecResult execResult = runCommand(pythonBin, "-c",
                        "import sys; print(sys.executable)");
            if (execResult.exitCode() != 0) {
                System.err.println("getting python sys.executable failed");
                System.exit(6);
            }
            String pythonExecutable = execResult.output();

            Properties props = new Properties();
            props.setProperty("python.libdir", pythonLibDir);
            props.setProperty("python.program.name", pythonExecutable);
            targetFile.getParent().toFile().mkdirs();
            try (var writer = Files.newBufferedWriter(targetFile)) {
                props.store(writer, "Python shared object and program locations");
            }
        } catch (IOException | InterruptedException ex) {
            ex.printStackTrace(System.err);
            System.exit(5);
        }
    }
}

Web Proxy Viewer  |  New URL  |  Original Page