def start(self, cmd, discard_output=False): """Starts the given command and returns a handler to it. :param list cmd: Command to be run as a list of arguments (strings). :param bool discard_output: Should the output be discarded instead of being buffered so it can be obtained later? :returns: A handler to the started command (``subprocess.Popen``). If the output is irrelevant for you, you should set `discard_output` to ``True``. """ # The implementation is platform-specific because we want to be able to # kill the children alongside with the process. kwargs = dict( args=cmd, stdin=subprocess.PIPE, stdout=subprocess.DEVNULL if discard_output else subprocess.PIPE, stderr=subprocess.DEVNULL if discard_output else subprocess.STDOUT) if on_windows(): p = _WindowsProcess(**kwargs) else: p = _LinuxProcess(**kwargs) # We have to catch SIGTERM and terminate the subprocess to ensure that # we do not leave running processes behind when forcibly killing the # runner (= main process). def handler(signum, frame): p.kill() sys.exit(1) signal.signal(signal.SIGTERM, handler) return p
def exit_if_on_windows(): """Exits the script if it is run on MS Windows.""" # This script needs support of signal.SIGCONT, socket.AF_UNIX, and # os.killpg(), which are not present on MS Windows. if on_windows(): print_error('this script cannot be run on MS Windows') sys.exit(1)
def setup_clang_bindings(clang_dir): """Sets up Clang bindings so they can be used. This function has to be called prior to using Clang bindings. """ assert os.path.exists(clang_dir), '{}: no such directory'.format(clang_dir) # Set a proper path to libclang so that the bindings can find them. if on_windows(): libclang_file_path = os.path.join(clang_dir, 'bin', 'libclang.dll') else: libclang_file_path = os.path.join(clang_dir, 'lib', 'libclang.so') assert os.path.exists(libclang_file_path), '{}: no such file'.format(libclang_file_path) clang.cindex.conf.set_library_file(libclang_file_path) # Set proper include paths. Without them, Clang cannot find some of the # standard headers. These include paths have to be specified as additional # arguments when calling cindex.Index.parse(). # # (1) CLANG_DIR/lib/clang/VERSION/include # Detect the VERSION part automatically (this should be the only # subdirectory in CLANG_DIR/lib/clang). clang_lib_dir = os.path.join(clang_dir, 'lib', 'clang') assert os.path.exists(clang_lib_dir), '{}: no such directory'.format(clang_lib_dir) clang_version = os.listdir(clang_lib_dir) assert clang_version, 'expected a subdirectory in {}'.format(clang_lib_dir) include_path = os.path.join(clang_dir, 'lib', 'clang', clang_version[0], 'include') assert os.path.exists(include_path), '{}: no such directory'.format(include_path) INCLUDE_PATHS.append(include_path) # (2) CLANG_DIR/include clang_include_dir = os.path.join(clang_dir, 'include') assert os.path.exists(clang_include_dir), '{}: no such directory'.format(clang_include_dir) INCLUDE_PATHS.append(clang_include_dir)
def root_directory(): """Returns the root directory to be used in absolute paths in tests.""" if on_windows(): # Use e.g. 'C:\', depending on the current working directory. return os.path.splitdrive(os.path.abspath(os.getcwd()))[0] + '\\' # Linux. return '/'
def test_long_double_literal_is_correctly_recognized(self): if on_windows(): # On Windows, the literal may either be 1e320L or inf, depending on # the compiler (MSVC does not support 80b long double, so it uses # double, in which the literal "overflows" to inf). assert self.out_c.contains('1.0e\+320L') or self.out_c.contains( 'inf \* ') or self.out_c.contains('INFINITY \* ') else: # Linux # Both GCC and Clang on Linux support 80b long double. assert self.out_c.contains('1.0e\+320L')
def _get_compiler_for_out_c(self): """Returns a compiler for the output C file.""" # Currently, we always use GCC. if on_windows(): # Since MSYS2 does not support multilib, we have to use our custom # wrapper around gcc that properly handles the -m32 parameter. # Moreover, since this wrapper is a shell script, we have to run it # through sh.exe (otherwise, Windows doesn't find it). return ['sh', 'windows-gcc-32.sh'] return ['gcc']
def _get_compiler_for_out_c(self): """Returns a compiler for the output C file.""" # Currently, we always use GCC. if on_windows(): # Since MSYS2 does not support multilib, we have to use our custom # wrappers around gcc that properly handle the -m32/-m64 parameters. # Moreover, since these wrappers are shell scripts, we have to run # them through sh.exe (otherwise, Windows doesn't find them). if self._use_64_bit_compiler(): return ['sh', 'windows-gcc-64.sh'] else: return ['sh', 'windows-gcc-32.sh'] return ['gcc']
def start(self, cmd, discard_output=False): """Starts the given command and returns a handler to it. :param list cmd: Command to be run as a list of arguments (strings). :param bool discard_output: Should the output be discarded instead of being buffered so it can be obtained later? :returns: A handler to the started command (``subprocess.Popen``). If the output is irrelevant for you, you should set `discard_output` to ``True``. """ # The implementation is platform-specific because we want to be able to # kill the children alongside with the process. kwargs = dict( args=cmd, stdin=subprocess.PIPE, stdout=subprocess.DEVNULL if discard_output else subprocess.PIPE, stderr=subprocess.DEVNULL if discard_output else subprocess.STDOUT) if on_windows(): return _WindowsProcess(**kwargs) else: return _LinuxProcess(**kwargs)
def is_gcc(self, args): """Checks if GCC is run in the given arguments.""" # On Windows, GCC is run via `sh.exe windows-gcc-32` instead of `gcc`. if on_windows(): return len(args[0]) > 1 and args[0][1] == 'windows-gcc-32.sh' return args[0][0] == 'gcc'
def _get_tool_executable_name(self, tool_name): return 'retdec-decompiler' + ('.exe' if on_windows() else '')
def test_get_tool_executable_name_returns_correct_name(self): self.assertEqual( self.decomp_runner._get_tool_executable_name('decompiler'), 'retdec-decompiler.exe' if on_windows() else 'retdec-decompiler')