diff --git a/lldb/cmake/modules/FindGNUstepObjC.cmake b/lldb/cmake/modules/FindGNUstepObjC.cmake new file mode 100644 --- /dev/null +++ b/lldb/cmake/modules/FindGNUstepObjC.cmake @@ -0,0 +1,41 @@ +#.rst: +# FindGNUstepObjC +# --------------- +# +# Find the GNUstep libobjc2 shared library. + +set(gnustep_install_dir "") + +if (UNIX) + set(gnustep_lib lib/libobjc.so) + set(gnustep_header include/objc/runtime.h) + if (GNUstepObjC_DIR) + if (EXISTS "${GNUstepObjC_DIR}/${gnustep_lib}" AND + EXISTS "${GNUstepObjC_DIR}/${gnustep_header}") + set(gnustep_install_dir ${GNUstepObjC_DIR}) + endif() + else() + set(gnustep_install_dir) + find_path(gnustep_install_dir NAMES lib/libobjc.so include/objc/runtime.h) + endif() + if (gnustep_install_dir) + set(GNUstepObjC_FOUND TRUE) + endif() +elseif (WIN32) + set(gnustep_lib lib/objc.dll) + set(gnustep_header include/objc/runtime.h) + if (GNUstepObjC_DIR) + set(gnustep_install_dir ${GNUstepObjC_DIR}) + else() + set(gnustep_install_dir "C:/Program Files (x86)/libobjc") + endif() + if (EXISTS "${gnustep_install_dir}/${gnustep_lib}" AND + EXISTS "${gnustep_install_dir}/${gnustep_header}") + set(GNUstepObjC_FOUND TRUE) + endif() +endif() + +if (GNUstepObjC_FOUND) + set(GNUstepObjC_DIR ${gnustep_install_dir}) + message(STATUS "Found GNUstep ObjC runtime: ${GNUstepObjC_DIR}/${gnustep_lib}") +endif() diff --git a/lldb/test/CMakeLists.txt b/lldb/test/CMakeLists.txt --- a/lldb/test/CMakeLists.txt +++ b/lldb/test/CMakeLists.txt @@ -50,6 +50,30 @@ file(MAKE_DIRECTORY ${LLDB_TEST_MODULE_CACHE_LLDB}) file(MAKE_DIRECTORY ${LLDB_TEST_MODULE_CACHE_CLANG}) +# Windows and Linux have no built-in ObjC runtime. Turn this on in order to run tests with GNUstep. +option(LLDB_TEST_OBJC_GNUSTEP "Enable ObjC tests with GNUstep libobjc2 on non-Apple platforms" Off) +set(LLDB_TEST_OBJC_GNUSTEP_DIR "" CACHE PATH "Custom path to the GNUstep shared library") + +if (LLDB_TEST_OBJC_GNUSTEP) + if (LLDB_TEST_OBJC_GNUSTEP_DIR) + set(GNUstepObjC_DIR ${LLDB_TEST_OBJC_GNUSTEP_DIR}) + endif() + find_package(GNUstepObjC) + if (NOT GNUstepObjC_FOUND) + if (LLDB_TEST_OBJC_GNUSTEP_DIR) + message(FATAL_ERROR "Failed to find GNUstep libobjc2 in ${LLDB_TEST_OBJC_GNUSTEP_DIR}. " + "Please check LLDB_TEST_OBJC_GNUSTEP_DIR or turn off LLDB_TEST_OBJC_GNUSTEP.") + else() + message(FATAL_ERROR "Failed to find GNUstep libobjc2. " + "Please set LLDB_TEST_OBJC_GNUSTEP_DIR or turn off LLDB_TEST_OBJC_GNUSTEP.") + endif() + endif() + set(LLDB_TEST_OBJC_GNUSTEP_DIR ${GNUstepObjC_DIR}) +elseif (LLDB_TEST_OBJC_GNUSTEP_DIR) + message(STATUS "Reset LLDB_TEST_OBJC_GNUSTEP_DIR since LLDB_TEST_OBJC_GNUSTEP is off") + set(LLDB_TEST_OBJC_GNUSTEP_DIR "" CACHE PATH "Custom path to the GNUstep shared library" FORCE) +endif() + # LLVM_BUILD_MODE is used in lit.site.cfg if (CMAKE_CFG_INTDIR STREQUAL ".") set(LLVM_BUILD_MODE ".") diff --git a/lldb/test/Shell/Expr/objc-gnustep-print.m b/lldb/test/Shell/Expr/objc-gnustep-print.m new file mode 100644 --- /dev/null +++ b/lldb/test/Shell/Expr/objc-gnustep-print.m @@ -0,0 +1,62 @@ +// REQUIRES: objc-gnustep +// XFAIL: system-windows +// +// RUN: %build %s --compiler=clang --objc-gnustep --output=%t + +#import "objc/runtime.h" + +@protocol NSCoding +@end + +#ifdef __has_attribute +#if __has_attribute(objc_root_class) +__attribute__((objc_root_class)) +#endif +#endif +@interface NSObject { + id isa; + int refcount; +} +@end +@implementation NSObject +- (id)class { + return object_getClass(self); +} ++ (id)new { + return class_createInstance(self, 0); +} +@end + +@interface TestObj : NSObject {} +- (int)ok; +@end +@implementation TestObj +- (int)ok { + return self ? 0 : 1; +} +@end + +// RUN: %lldb -b -o "b objc-gnustep-print.m:35" -o "run" -o "p self" -o "p *self" -- %t | FileCheck %s --check-prefix=SELF +// +// SELF: (lldb) b objc-gnustep-print.m:35 +// SELF: Breakpoint {{.*}} at objc-gnustep-print.m +// +// SELF: (lldb) run +// SELF: Process {{[0-9]+}} stopped +// SELF: -[TestObj ok](self=[[SELF_PTR:0x[0-9a-f]+]]{{.*}}) at objc-gnustep-print.m:35 +// +// SELF: (lldb) p self +// SELF: (TestObj *) [[SELF_PTR]] +// +// SELF: (lldb) p *self +// SELF: (TestObj) { +// SELF: NSObject = { +// SELF: isa +// SELF: refcount +// SELF: } +// SELF: } + +int main() { + TestObj *t = [TestObj new]; + return [t ok]; +} diff --git a/lldb/test/Shell/helper/build.py b/lldb/test/Shell/helper/build.py --- a/lldb/test/Shell/helper/build.py +++ b/lldb/test/Shell/helper/build.py @@ -49,6 +49,18 @@ action='append', help='If specified, a path to search in addition to PATH when --compiler is not an exact path') +parser.add_argument('--objc-gnustep-dir', + metavar='directory', + dest='objc_gnustep_dir', + required=False, + help='If specified, a path to GNUstep libobjc2 runtime for use on Windows and Linux') + +parser.add_argument('--objc-gnustep', + dest='objc_gnustep', + action='store_true', + default=False, + help='Include and link GNUstep libobjc2 (Windows and Linux only)') + if sys.platform == 'darwin': parser.add_argument('--apple-sdk', metavar='apple_sdk', @@ -238,6 +250,10 @@ self.obj_ext = obj_ext self.lib_paths = args.libs_dir self.std = args.std + assert not args.objc_gnustep or args.objc_gnustep_dir, \ + "--objc-gnustep specified without path to libobjc2" + self.objc_gnustep_inc = os.path.join(args.objc_gnustep_dir, 'include') if args.objc_gnustep_dir else None + self.objc_gnustep_lib = os.path.join(args.objc_gnustep_dir, 'lib') if args.objc_gnustep_dir else None def _exe_file_name(self): assert self.mode != 'compile' @@ -656,15 +672,20 @@ args.append('-static') args.append('-c') - args.extend(['-o', obj]) - args.append(source) - if sys.platform == 'darwin': args.extend(['-isysroot', self.apple_sdk]) + elif self.objc_gnustep_inc: + if source.endswith('.m') or source.endswith('.mm'): + args.extend(['-fobjc-runtime=gnustep-2.0', '-I', self.objc_gnustep_inc]) + if sys.platform == "win32": + args.extend(['-Xclang', '-gcodeview', '-Xclang', '--dependent-lib=msvcrtd']) if self.std: args.append('-std={0}'.format(self.std)) + args.extend(['-o', obj]) + args.append(source) + return ('compiling', [source], obj, None, args) def _get_link_command(self): @@ -686,6 +707,12 @@ if sys.platform == 'darwin': args.extend(['-isysroot', self.apple_sdk]) + elif self.objc_gnustep_lib: + args.extend(['-L', self.objc_gnustep_lib, '-lobjc']) + if sys.platform == 'linux': + args.extend(['-Wl,-rpath,' + self.objc_gnustep_lib]) + elif sys.platform == 'win32': + args.extend(['-fuse-ld=lld-link', '-g', '-Xclang', '--dependent-lib=msvcrtd']) return ('linking', self._obj_file_names(), self._exe_file_name(), None, args) diff --git a/lldb/test/Shell/helper/toolchain.py b/lldb/test/Shell/helper/toolchain.py --- a/lldb/test/Shell/helper/toolchain.py +++ b/lldb/test/Shell/helper/toolchain.py @@ -42,6 +42,8 @@ build_script_args.append('--tools-dir={0}'.format(config.lldb_tools_dir)) if config.llvm_libs_dir: build_script_args.append('--libs-dir={0}'.format(config.llvm_libs_dir)) + if config.objc_gnustep_dir: + build_script_args.append('--objc-gnustep-dir="{0}"'.format(config.objc_gnustep_dir)) lldb_init = _get_lldb_init_path(config) diff --git a/lldb/test/Shell/lit.cfg.py b/lldb/test/Shell/lit.cfg.py --- a/lldb/test/Shell/lit.cfg.py +++ b/lldb/test/Shell/lit.cfg.py @@ -24,7 +24,7 @@ # suffixes: A list of file extensions to treat as test files. This is overriden # by individual lit.local.cfg files in the test subdirectories. -config.suffixes = ['.test', '.cpp', '.s'] +config.suffixes = ['.test', '.cpp', '.s', '.m'] # excludes: A list of directories to exclude from the testsuite. The 'Inputs' # subdirectories contain auxiliary inputs for various tests in their parent @@ -135,6 +135,14 @@ if config.have_lldb_server: config.available_features.add('lldb-server') +if config.objc_gnustep_dir: + config.available_features.add('objc-gnustep') + if platform.system() == 'Windows': + # objc.dll must be in PATH since Windows has no rpath + config.environment['PATH'] = os.path.pathsep.join(( + os.path.join(config.objc_gnustep_dir, 'lib'), + config.environment.get('PATH',''))) + # NetBSD permits setting dbregs either if one is root # or if user_set_dbregs is enabled can_set_dbregs = True diff --git a/lldb/test/Shell/lit.site.cfg.py.in b/lldb/test/Shell/lit.site.cfg.py.in --- a/lldb/test/Shell/lit.site.cfg.py.in +++ b/lldb/test/Shell/lit.site.cfg.py.in @@ -16,6 +16,7 @@ config.target_triple = "@LLVM_TARGET_TRIPLE@" config.python_executable = "@Python3_EXECUTABLE@" config.have_zlib = @LLVM_ENABLE_ZLIB@ +config.objc_gnustep_dir = "@LLDB_TEST_OBJC_GNUSTEP_DIR@" config.lldb_enable_lzma = @LLDB_ENABLE_LZMA@ config.host_triple = "@LLVM_HOST_TRIPLE@" config.lldb_bitness = 64 if @LLDB_IS_64_BITS@ else 32