Skip to content

Commit

Permalink
[lldb] fix -print-script-interpreter-info on windows
Browse files Browse the repository at this point in the history
Apparently "{sys.prefix}/bin/python3" isn't where you find the
python interpreter on windows, so the test I wrote for
-print-script-interpreter-info is failing.

We can't rely on sys.executable at runtime, because that will point
to lldb.exe not python.exe.

We can't just record sys.executable from build time, because python
could have been moved to a different location.

But it should be OK to apply relative path from sys.prefix to sys.executable
from build-time to the sys.prefix at runtime.

Reviewed By: JDevlieghere

Differential Revision: https://reviews.llvm.org/D113650
  • Loading branch information
smoofra authored and lawrence-danna-apple committed Nov 16, 2021
1 parent 913d78c commit 4c2cf3a
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 39 deletions.
40 changes: 22 additions & 18 deletions lldb/CMakeLists.txt
Expand Up @@ -31,24 +31,28 @@ if (WIN32)
endif()

if (LLDB_ENABLE_PYTHON)
if (NOT CMAKE_CROSSCOMPILING)
execute_process(
COMMAND ${Python3_EXECUTABLE}
-c "import distutils.sysconfig; print(distutils.sysconfig.get_python_lib(True, False, ''))"
OUTPUT_VARIABLE LLDB_PYTHON_DEFAULT_RELATIVE_PATH
OUTPUT_STRIP_TRAILING_WHITESPACE)

file(TO_CMAKE_PATH ${LLDB_PYTHON_DEFAULT_RELATIVE_PATH} LLDB_PYTHON_DEFAULT_RELATIVE_PATH)
else ()
if ("${LLDB_PYTHON_RELATIVE_PATH}" STREQUAL "")
message(FATAL_ERROR
"Crosscompiling LLDB with Python requires manually setting
LLDB_PYTHON_RELATIVE_PATH.")
endif ()
endif ()

set(LLDB_PYTHON_RELATIVE_PATH ${LLDB_PYTHON_DEFAULT_RELATIVE_PATH}
CACHE STRING "Path where Python modules are installed, relative to install prefix")
set(cachestring_LLDB_PYTHON_RELATIVE_PATH
"Path where Python modules are installed, relative to install prefix")
set(cachestring_LLDB_PYTHON_EXE_RELATIVE_PATH
"Path to python interpreter exectuable, relative to install prefix")

foreach(var LLDB_PYTHON_RELATIVE_PATH LLDB_PYTHON_EXE_RELATIVE_PATH)
if(NOT DEFINED ${var} AND NOT CMAKE_CROSSCOMPILING)
execute_process(
COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/bindings/python/get-python-config.py
${var}
OUTPUT_VARIABLE value
OUTPUT_STRIP_TRAILING_WHITESPACE)
file(TO_CMAKE_PATH "${value}" value)
set(${var} ${value} CACHE STRING ${cachestring_${var}})
else()
if ("${${var}}" STREQUAL "")
message(FATAL_ERROR
"Crosscompiling LLDB with Python requires manually setting ${var}.")
endif()
endif()
endforeach()
endif ()

if (LLDB_ENABLE_LUA)
Expand Down
47 changes: 47 additions & 0 deletions lldb/bindings/python/get-python-config.py
@@ -0,0 +1,47 @@
#!/usr/bin/env python3

import os
import sys
import argparse
import sysconfig
import distutils.sysconfig


def relpath_nodots(path, base):
rel = os.path.normpath(os.path.relpath(path, base))
assert not os.path.isabs(rel)
parts = rel.split(os.path.sep)
if parts and parts[0] == '..':
raise ValueError(f"{path} is not under {base}")
return rel

def main():
parser = argparse.ArgumentParser(description="extract cmake variables from python")
parser.add_argument("variable_name")
args = parser.parse_args()
if args.variable_name == "LLDB_PYTHON_RELATIVE_PATH":
print(distutils.sysconfig.get_python_lib(True, False, ''))
elif args.variable_name == "LLDB_PYTHON_EXE_RELATIVE_PATH":
tried = list()
exe = sys.executable
while True:
try:
print(relpath_nodots(exe, sys.prefix))
break
except ValueError:
tried.append(exe)
if os.path.islink(exe):
exe = os.path.join(os.path.dirname(exe), os.readlink(exe))
continue
else:
print("Could not find a relative path to sys.executable under sys.prefix", file=sys.stderr)
for e in tried:
print("tried:", e, file=sys.stderr)
print("sys.prefix:", sys.prefix, file=sys.stderr)
sys.exit(1)

else:
parser.error(f"unknown variable {args.variable_name}")

if __name__ == '__main__':
main()
6 changes: 6 additions & 0 deletions lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt
Expand Up @@ -3,6 +3,12 @@ if(NOT LLDB_PYTHON_RELATIVE_PATH)
endif()
add_definitions(-DLLDB_PYTHON_RELATIVE_LIBDIR="${LLDB_PYTHON_RELATIVE_PATH}")

if(NOT LLDB_PYTHON_EXE_RELATIVE_PATH)
message(FATAL_ERROR "LLDB_PYTHON_EXE_RELATIVE_PATH is not set.")
endif()
add_definitions(-DLLDB_PYTHON_EXE_RELATIVE_PATH="${LLDB_PYTHON_EXE_RELATIVE_PATH}")


if (LLDB_ENABLE_LIBEDIT)
list(APPEND LLDB_LIBEDIT_LIBS ${LibEdit_LIBRARIES})
endif()
Expand Down
Expand Up @@ -410,30 +410,31 @@ FileSpec ScriptInterpreterPython::GetPythonDir() {
return g_spec;
}

static const char GetInterpreterInfoScript[] = R"(
import os
import sys
def main(lldb_python_dir, python_exe_relative_path):
info = {
"lldb-pythonpath": lldb_python_dir,
"language": "python",
"prefix": sys.prefix,
"executable": os.path.join(sys.prefix, python_exe_relative_path)
}
return info
)";

static const char python_exe_relative_path[] = LLDB_PYTHON_EXE_RELATIVE_PATH;

StructuredData::DictionarySP ScriptInterpreterPython::GetInterpreterInfo() {
GIL gil;
FileSpec python_dir_spec = GetPythonDir();
if (!python_dir_spec)
return nullptr;
PythonString python_dir(python_dir_spec.GetPath());
PythonDictionary info(PyInitialValue::Empty);
llvm::Error error = info.SetItem("lldb-pythonpath", python_dir);
if (error)
return nullptr;
static const char script[] = R"(
def main(info):
import sys
import os
name = 'python' + str(sys.version_info.major)
info.update({
"language": "python",
"prefix": sys.prefix,
"executable": os.path.join(sys.prefix, "bin", name),
})
return info
)";
PythonScript get_info(script);
auto info_json = unwrapIgnoringErrors(As<PythonDictionary>(get_info(info)));
PythonScript get_info(GetInterpreterInfoScript);
auto info_json = unwrapIgnoringErrors(
As<PythonDictionary>(get_info(PythonString(python_dir_spec.GetPath()),
PythonString(python_exe_relative_path))));
if (!info_json)
return nullptr;
return info_json.CreateStructuredDictionary();
Expand Down
2 changes: 0 additions & 2 deletions lldb/test/API/functionalities/paths/TestPaths.py
Expand Up @@ -51,8 +51,6 @@ def test_interpreter_info(self):
stream = lldb.SBStream()
self.assertTrue(info_sd.GetAsJSON(stream).Success())
info = json.loads(stream.GetData())
if os.name == 'nt': #FIXME
return
prefix = info['prefix']
self.assertEqual(os.path.realpath(sys.prefix), os.path.realpath(prefix))
self.assertEqual(
Expand Down

0 comments on commit 4c2cf3a

Please sign in to comment.