JDevlieghere created this revision.
JDevlieghere added reviewers: labath, kastiglione, teemperor.
JDevlieghere requested review of this revision.

Make it possible to use a relative path in `command script import` to the 
location of the file being sourced. This allows the user to put Python scripts 
next to LLDB command files and importing them without having to specify an 
absolute path.

rdar://68310384


https://reviews.llvm.org/D89334

Files:
  lldb/include/lldb/Interpreter/CommandInterpreter.h
  lldb/include/lldb/Interpreter/ScriptInterpreter.h
  lldb/source/Commands/CommandObjectCommands.cpp
  lldb/source/Interpreter/CommandInterpreter.cpp
  lldb/source/Interpreter/ScriptInterpreter.cpp
  lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp
  lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h
  lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
  lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
  lldb/test/Shell/ScriptInterpreter/Python/Inputs/zip.in
  lldb/test/Shell/ScriptInterpreter/Python/Inputs/zip.py
  lldb/test/Shell/ScriptInterpreter/Python/relative_import.test

Index: lldb/test/Shell/ScriptInterpreter/Python/relative_import.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/ScriptInterpreter/Python/relative_import.test
@@ -0,0 +1,7 @@
+# REQUIRES: python
+
+# RUN: rm -rf %t && mkdir -p %t/foo
+# RUN: cp %S/Inputs/zip.py %t/foo
+# RUN: cp %S/Inputs/zip.in %t/foo
+# RUN: %lldb --script-language python -o 'command source %t/foo/zip.in' -o 'zip' 2>&1 | FileCheck %s
+# CHECK: 95126
Index: lldb/test/Shell/ScriptInterpreter/Python/Inputs/zip.py
===================================================================
--- /dev/null
+++ lldb/test/Shell/ScriptInterpreter/Python/Inputs/zip.py
@@ -0,0 +1,7 @@
+import lldb
+
+def zip(debugger, command, result, internal_dict):
+    print("95126")
+
+def __lldb_init_module(debugger, internal_dict):
+    debugger.HandleCommand('command script add -f zip.zip zip')
Index: lldb/test/Shell/ScriptInterpreter/Python/Inputs/zip.in
===================================================================
--- /dev/null
+++ lldb/test/Shell/ScriptInterpreter/Python/Inputs/zip.in
@@ -0,0 +1 @@
+command script import zip
Index: lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
===================================================================
--- lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
+++ lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
@@ -231,10 +231,10 @@
   bool RunScriptFormatKeyword(const char *impl_function, ValueObject *value,
                               std::string &output, Status &error) override;
 
-  bool
-  LoadScriptingModule(const char *filename, bool init_session,
-                      lldb_private::Status &error,
-                      StructuredData::ObjectSP *module_sp = nullptr) override;
+  bool LoadScriptingModule(const char *filename, bool init_session,
+                           lldb_private::Status &error,
+                           StructuredData::ObjectSP *module_sp = nullptr,
+                           FileSpec extra_search_dir = {}) override;
 
   bool IsReservedWord(const char *word) override;
 
Index: lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
===================================================================
--- lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
+++ lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
@@ -2733,7 +2733,7 @@
 
 bool ScriptInterpreterPythonImpl::LoadScriptingModule(
     const char *pathname, bool init_session, lldb_private::Status &error,
-    StructuredData::ObjectSP *module_sp) {
+    StructuredData::ObjectSP *module_sp, FileSpec extra_search_dir) {
   if (!pathname || !pathname[0]) {
     error.SetErrorString("invalid pathname");
     return false;
@@ -2760,24 +2760,12 @@
     fs::file_status st;
     std::error_code ec = status(target_file.GetPath(), st);
 
-    if (ec || st.type() == fs::file_type::status_error ||
-        st.type() == fs::file_type::type_unknown ||
-        st.type() == fs::file_type::file_not_found) {
-      // if not a valid file of any sort, check if it might be a filename still
-      // dot can't be used but / and \ can, and if either is found, reject
-      if (strchr(pathname, '\\') || strchr(pathname, '/')) {
-        error.SetErrorString("invalid pathname");
-        return false;
-      }
-      basename = pathname; // not a filename, probably a package of some sort,
-                           // let it go through
-    } else if (is_directory(st) || is_regular_file(st)) {
-      if (target_file.GetDirectory().IsEmpty()) {
-        error.SetErrorString("invalid directory name");
-        return false;
+    auto ExtendSysPath = [this](std::string directory) -> llvm::Error {
+      if (directory.empty()) {
+        return llvm::make_error<llvm::StringError>(
+            "invalid directory name", llvm::inconvertibleErrorCode());
       }
 
-      std::string directory = target_file.GetDirectory().GetCString();
       replace_all(directory, "\\", "\\\\");
       replace_all(directory, "'", "\\'");
 
@@ -2793,7 +2781,35 @@
                                    .SetSetLLDBGlobals(false))
               .Success();
       if (!syspath_retval) {
-        error.SetErrorString("Python sys.path handling failed");
+        return llvm::make_error<llvm::StringError>(
+            "Python sys.path handling failed", llvm::inconvertibleErrorCode());
+      }
+
+      return llvm::Error::success();
+    };
+
+    if (extra_search_dir) {
+      if (llvm::Error e = ExtendSysPath(extra_search_dir.GetPath())) {
+        error = std::move(e);
+        return false;
+      }
+    }
+
+    if (ec || st.type() == fs::file_type::status_error ||
+        st.type() == fs::file_type::type_unknown ||
+        st.type() == fs::file_type::file_not_found) {
+      // if not a valid file of any sort, check if it might be a filename still
+      // dot can't be used but / and \ can, and if either is found, reject
+      if (strchr(pathname, '\\') || strchr(pathname, '/')) {
+        error.SetErrorString("invalid pathname");
+        return false;
+      }
+      basename = pathname; // not a filename, probably a package of some sort,
+                           // let it go through
+    } else if (is_directory(st) || is_regular_file(st)) {
+      if (llvm::Error e =
+              ExtendSysPath(target_file.GetDirectory().GetCString())) {
+        error = std::move(e);
         return false;
       }
 
@@ -3229,11 +3245,16 @@
 
   LLDBSwigPyInit();
 
-  // Update the path python uses to search for modules to include the current
-  // directory.
-
+  // We need to call this before AddToSysPath as it uses the sys module.
   PyRun_SimpleString("import sys");
+
+  // Update the path python uses to search for modules to include the current
+  // directory and the home directory.
   AddToSysPath(AddLocation::End, ".");
+  FileSpec home_dir_spec;
+  llvm::SmallString<128> home_dir;
+  if (FileSystem::Instance().GetHomeDirectory(home_dir))
+    AddToSysPath(AddLocation::End, std::string(home_dir));
 
   // Don't denormalize paths when calling file_spec.GetPath().  On platforms
   // that use a backslash as the path separator, this will result in executing
Index: lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h
===================================================================
--- lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h
+++ lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h
@@ -25,10 +25,10 @@
 
   void ExecuteInterpreterLoop() override;
 
-  bool
-  LoadScriptingModule(const char *filename, bool init_session,
-                      lldb_private::Status &error,
-                      StructuredData::ObjectSP *module_sp = nullptr) override;
+  bool LoadScriptingModule(const char *filename, bool init_session,
+                           lldb_private::Status &error,
+                           StructuredData::ObjectSP *module_sp = nullptr,
+                           FileSpec extra_search_dir = {}) override;
 
   // Static Functions
   static void Initialize();
Index: lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp
===================================================================
--- lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp
+++ lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp
@@ -124,7 +124,7 @@
 
 bool ScriptInterpreterLua::LoadScriptingModule(
     const char *filename, bool init_session, lldb_private::Status &error,
-    StructuredData::ObjectSP *module_sp) {
+    StructuredData::ObjectSP *module_sp, FileSpec extra_search_dir) {
 
   FileSystem::Instance().Collect(filename);
   if (llvm::Error e = m_lua->LoadModule(filename)) {
Index: lldb/source/Interpreter/ScriptInterpreter.cpp
===================================================================
--- lldb/source/Interpreter/ScriptInterpreter.cpp
+++ lldb/source/Interpreter/ScriptInterpreter.cpp
@@ -47,9 +47,11 @@
       "This script interpreter does not support watchpoint callbacks.");
 }
 
-bool ScriptInterpreter::LoadScriptingModule(
-    const char *filename, bool init_session, lldb_private::Status &error,
-    StructuredData::ObjectSP *module_sp) {
+bool ScriptInterpreter::LoadScriptingModule(const char *filename,
+                                            bool init_session,
+                                            lldb_private::Status &error,
+                                            StructuredData::ObjectSP *module_sp,
+                                            FileSpec extra_search_dir) {
   error.SetErrorString(
       "This script interpreter does not support importing modules.");
   return false;
Index: lldb/source/Interpreter/CommandInterpreter.cpp
===================================================================
--- lldb/source/Interpreter/CommandInterpreter.cpp
+++ lldb/source/Interpreter/CommandInterpreter.cpp
@@ -2554,11 +2554,15 @@
     debugger.SetAsyncExecution(false);
 
   m_command_source_depth++;
+  m_command_source_dirs.push_back(cmd_file.CopyByRemovingLastPathComponent());
 
   debugger.RunIOHandlerSync(io_handler_sp);
   if (!m_command_source_flags.empty())
     m_command_source_flags.pop_back();
+
+  m_command_source_dirs.pop_back();
   m_command_source_depth--;
+
   result.SetStatus(eReturnStatusSuccessFinishNoResult);
   debugger.SetAsyncExecution(old_async_execution);
 }
@@ -2964,6 +2968,12 @@
   return true;
 }
 
+FileSpec CommandInterpreter::GetCurrentSourceDir() {
+  if (m_command_source_dirs.empty())
+    return {};
+  return m_command_source_dirs.back();
+}
+
 void CommandInterpreter::GetLLDBCommandsFromIOHandler(
     const char *prompt, IOHandlerDelegate &delegate, void *baton) {
   Debugger &debugger = GetDebugger();
Index: lldb/source/Commands/CommandObjectCommands.cpp
===================================================================
--- lldb/source/Commands/CommandObjectCommands.cpp
+++ lldb/source/Commands/CommandObjectCommands.cpp
@@ -1294,6 +1294,9 @@
       return false;
     }
 
+    FileSpec source_dir =
+        GetDebugger().GetCommandInterpreter().GetCurrentSourceDir();
+
     for (auto &entry : command.entries()) {
       Status error;
 
@@ -1308,7 +1311,7 @@
       // more)
       m_exe_ctx.Clear();
       if (GetDebugger().GetScriptInterpreter()->LoadScriptingModule(
-              entry.c_str(), init_session, error)) {
+              entry.c_str(), init_session, error, nullptr, source_dir)) {
         result.SetStatus(eReturnStatusSuccessFinishNoResult);
       } else {
         result.AppendErrorWithFormat("module importing failed: %s",
Index: lldb/include/lldb/Interpreter/ScriptInterpreter.h
===================================================================
--- lldb/include/lldb/Interpreter/ScriptInterpreter.h
+++ lldb/include/lldb/Interpreter/ScriptInterpreter.h
@@ -507,7 +507,8 @@
   virtual bool
   LoadScriptingModule(const char *filename, bool init_session,
                       lldb_private::Status &error,
-                      StructuredData::ObjectSP *module_sp = nullptr);
+                      StructuredData::ObjectSP *module_sp = nullptr,
+                      FileSpec extra_search_dir = {});
 
   virtual bool IsReservedWord(const char *word) { return false; }
 
Index: lldb/include/lldb/Interpreter/CommandInterpreter.h
===================================================================
--- lldb/include/lldb/Interpreter/CommandInterpreter.h
+++ lldb/include/lldb/Interpreter/CommandInterpreter.h
@@ -551,6 +551,8 @@
   bool SaveTranscript(CommandReturnObject &result,
                       llvm::Optional<std::string> output_file = llvm::None);
 
+  FileSpec GetCurrentSourceDir();
+
 protected:
   friend class Debugger;
 
@@ -637,7 +639,13 @@
   ChildrenTruncatedWarningStatus m_truncation_warning; // Whether we truncated
                                                        // children and whether
                                                        // the user has been told
+
+  // FIXME: Stop using this to control adding to the history and then replace
+  // this with m_command_source_dirs.size().
   uint32_t m_command_source_depth;
+  /// A stack of directory paths. When not empty, the last one is the directory
+  /// of the file that's currently sourced.
+  std::vector<FileSpec> m_command_source_dirs;
   std::vector<uint32_t> m_command_source_flags;
   CommandInterpreterRunResult m_result;
 
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to