Example #1
0
def check_output(*popenargs, **kwargs):
    """Call a process and check result code.

    This is for Python 2.6 compatibility, which doesn't have that in its
    standard library.

    Note: We use same name as in Python stdlib, violating our rules to
    make it more recognizable what this does.
    """

    if "stdout" in kwargs:
        raise ValueError("stdout argument not allowed, it will be overridden.")

    if "stderr" not in kwargs:
        kwargs["stderr"] = subprocess.PIPE

    process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs)

    output, stderr = process.communicate()
    retcode = process.poll()

    if retcode:
        cmd = kwargs.get("args")
        if cmd is None:
            cmd = popenargs[0]

        raise NuitkaCalledProcessError(retcode, cmd, output=output, stderr=stderr)

    return output
Example #2
0
def executeToolChecked(logger, command, absence_message, stderr_filter=None):
    """Execute external tool, checking for success and no error outputs, returning result."""

    command = list(command)
    tool = command[0]

    if not isExecutableCommand(tool):
        logger.sysexit(absence_message)

    # Allow to avoid repeated scans in PATH for the tool.
    command[0] = getExecutablePath(tool)

    process = subprocess.Popen(
        command,
        stdin=getNullInput(),
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        shell=False,
    )

    stdout, stderr = process.communicate()
    result = process.poll()

    if stderr_filter is not None:
        stderr = stderr_filter(stderr)

    if result != 0:
        logger.sysexit("Error, call to %r failed: %s -> %s." % (tool, command, stderr))
    elif stderr:
        logger.sysexit(
            "Error, call to %r gave warnings: %s -> %s." % (tool, command, stderr)
        )

    return stdout
Example #3
0
def executeProcess(command, env=None, stdin=False, shell=False, external_cwd=False):
    if not env:
        env = os.environ

    # Note: Empty string should also be allowed for stdin, therefore check for
    # default "False" and "None" precisely.

    process = subprocess.Popen(
        command,
        stdin=subprocess.PIPE if stdin not in (False, None) else getNullInput(),
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        shell=shell,
        # On Windows, closing file descriptions is now working with capturing outputs.
        close_fds=not isWin32Windows(),
        env=env,
        # For tools that want short paths to work.
        cwd=getExternalUsePath(os.getcwd()) if external_cwd else None,
    )

    if stdin is True:
        process_input = None
    elif stdin is not False:
        process_input = stdin
    else:
        process_input = None

    stdout, stderr = process.communicate(input=process_input)
    exit_code = process.wait()

    return stdout, stderr, exit_code
Example #4
0
def updateWorkingFile(path, orig_object_hash, new_object_hash):
    patch = check_output(
        ["git", "diff", "--no-color", orig_object_hash, new_object_hash])

    git_path = path.replace(os.path.sep, "/").encode("utf8")

    def updateLine(line):
        if line.startswith(b"diff --git"):
            line = b"diff --git a/%s b/%s" % (git_path, git_path)
        elif line.startswith(b"--- a/"):
            line = b"--- a/" + git_path
        elif line.startswith(b"+++ b/"):
            line = b"+++ b/" + git_path

        return line

    # Substitute object hashes in patch header with path to working tree file
    patch = b"\n".join(updateLine(line) for line in patch.splitlines()) + b"\n"

    # Cannot use executeProcess, because we supply input, and we cannot
    # use context managers, because not all Python versions have it that
    # way, xpylintx: disable=consider-using-with
    apply_patch = subprocess.Popen(
        ["git", "apply", "-"],
        stdin=subprocess.PIPE,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
    )

    output, err = apply_patch.communicate(input=patch)
    success = apply_patch.returncode == 0

    if not success:
        # TODO: In case of failure, do we need to abort, or what do we do.

        if output:
            my_print(output, style="yellow")
        if err:
            my_print(err, style="yellow")

    return success
Example #5
0
def getRuntimeTraceOfLoadedFiles(logger, command, required=False):
    """Returns the files loaded when executing a binary."""

    # This will make a crazy amount of work,
    # pylint: disable=I0021,too-many-branches,too-many-locals,too-many-statements

    path = command[0]

    if not os.path.exists(path):
        logger.sysexit("Error, cannot find %r (%r)." % (path, os.path.abspath(path)))

    result = []

    if os.name == "posix":
        if getOS() in ("Darwin", "FreeBSD"):
            if not isExecutableCommand("dtruss"):
                logger.sysexit(
                    """\
Error, needs 'dtruss' on your system to scan used libraries."""
                )

            if not isExecutableCommand("sudo"):
                logger.sysexit(
                    """\
Error, needs 'sudo' on your system to scan used libraries."""
                )

            args = ("sudo", "dtruss", "-t", "open", os.path.abspath(path))
        else:
            if not isExecutableCommand("strace"):
                logger.sysexit(
                    """\
Error, needs 'strace' on your system to scan used libraries."""
                )

            args = (
                "strace",
                "-e",
                "file",
                "-s4096",  # Some paths are truncated in output otherwise
                os.path.abspath(path),
            )

        # Ensure executable is not polluted with third party stuff,
        # tests may fail otherwise due to unexpected libs being loaded
        with withEnvironmentVarOverridden("LD_PRELOAD", None):
            tracing_command = args[0] if args[0] != "sudo" else args[1]

            process = subprocess.Popen(
                args=args, stdout=subprocess.PIPE, stderr=subprocess.PIPE
            )

            _stdout_strace, stderr_strace = process.communicate()
            exit_strace = process.returncode

            if exit_strace != 0:
                if str is not bytes:
                    stderr_strace = stderr_strace.decode("utf8")

                logger.warning(stderr_strace)
                logger.sysexit("Failed to run %r." % tracing_command)

            with open(path + ".strace", "wb") as f:
                f.write(stderr_strace)

            for line in stderr_strace.split(b"\n"):
                if process.returncode != 0:
                    logger.my_print(line)

                if not line:
                    continue

                # Don't consider files not found. The "site" module checks lots
                # of things.
                if b"ENOENT" in line:
                    continue

                if line.startswith(b"stat(") and b"S_IFDIR" in line:
                    continue

                result.extend(
                    os.path.abspath(match)
                    for match in re.findall(b'"(.*?)(?:\\\\0)?"', line)
                )

            if str is not bytes:
                result = [s.decode("utf8") for s in result]
    elif os.name == "nt":
        command = (
            getDependsExePath(),
            "-c",  # Console mode
            "-ot%s" % path + ".depends",
            "-f1",
            "-pb",
            "-pa1",  # Turn on all profiling options.
            "-ps1",  # Simulate ShellExecute with app dirs in PATH.
            "-pp1",  # Do not long DllMain calls.
            "-po1",  # Log DllMain call for all other messages.
            "-ph1",  # Hook the process.
            "-pl1",  # Log LoadLibrary calls.
            "-pt1",  # Thread information.
            "-pe1",  # First chance exceptions.
            "-pg1",  # Log GetProcAddress calls.
            "-pf1",  # Use full paths.
            "-pc1",  # Profile child processes.
        ) + tuple(command)

        # TODO: Move the handling of this into nuitka.tools.Execution module methods.
        try:
            callProcess(command, timeout=5 * 60)
        except Exception as e:  # Catch all the things, pylint: disable=broad-except
            if e.__class__.__name__ == "TimeoutExpired":
                if required:
                    logger.sysexit(
                        "Timeout encountered when running dependency walker."
                    )

                logger.warning("Timeout encountered when running dependency walker.")
                return []
            else:
                raise

        result = parseDependsExeOutput(path + ".depends")

        os.unlink(path + ".depends")

    result = list(sorted(set(result)))

    return result