Exemple #1
0
def copy_file(src,
              dst,
              preserve_mode=1,
              preserve_times=1,
              update=0,
              link=None,
              verbose=1,
              dry_run=0):
    """Copy a file 'src' to 'dst'.

    If 'dst' is a directory, then 'src' is copied there with the same name;
    otherwise, it must be a filename.  (If the file exists, it will be
    ruthlessly clobbered.)  If 'preserve_mode' is true (the default),
    the file's mode (type and permission bits, or whatever is analogous on
    the current platform) is copied.  If 'preserve_times' is true (the
    default), the last-modified and last-access times are copied as well.
    If 'update' is true, 'src' will only be copied if 'dst' does not exist,
    or if 'dst' does exist but is older than 'src'.

    'link' allows you to make hard links (os.link) or symbolic links
    (os.symlink) instead of copying: set it to "hard" or "sym"; if it is
    None (the default), files are copied.  Don't set 'link' on systems that
    don't support it: 'copy_file()' doesn't check if hard or symbolic
    linking is available. If hardlink fails, falls back to
    _copy_file_contents().

    Under Mac OS, uses the native file copy function in macostools; on
    other systems, uses '_copy_file_contents()' to copy file contents.

    Return a tuple (dest_name, copied): 'dest_name' is the actual name of
    the output file, and 'copied' is true if the file was copied (or would
    have been copied, if 'dry_run' true).
    """
    # XXX if the destination file already exists, we clobber it if
    # copying, but blow up if linking.  Hmmm.  And I don't know what
    # macostools.copyfile() does.  Should definitely be consistent, and
    # should probably blow up if destination exists and we would be
    # changing it (ie. it's not already a hard/soft link to src OR
    # (not update) and (src newer than dst).

    from distutils.dep_util import newer
    from stat import ST_ATIME, ST_MTIME, ST_MODE, S_IMODE

    if not os.path.isfile(src):
        raise DistutilsFileError(
            "can't copy '%s': doesn't exist or not a regular file" % src)

    if os.path.isdir(dst):
        dir = dst
        dst = os.path.join(dst, os.path.basename(src))
    else:
        dir = os.path.dirname(dst)

    if update and not newer(src, dst):
        if verbose >= 1:
            log.debug("not copying %s (output up-to-date)", src)
        return dst, 0

    try:
        action = _copy_action[link]
    except KeyError:
        raise ValueError("invalid value '%s' for 'link' argument" % link)

    if verbose >= 1:
        if os.path.basename(dst) == os.path.basename(src):
            log.info("%s %s -> %s", action, src, dir)
        else:
            log.info("%s %s -> %s", action, src, dst)

    if dry_run:
        return (dst, 1)

    # If linking (hard or symbolic), use the appropriate system call
    # (Unix only, of course, but that's the caller's responsibility)
    if link == 'hard':
        if not (os.path.exists(dst) and os.path.samefile(src, dst)):
            try:
                os.link(src, dst)
                return (dst, 1)
            except OSError:
                # If hard linking fails, fall back on copying file
                # (some special filesystems don't support hard linking
                #  even under Unix, see issue #8876).
                pass
    elif link == 'sym':
        if not (os.path.exists(dst) and os.path.samefile(src, dst)):
            os.symlink(src, dst)
            return (dst, 1)

    # Otherwise (non-Mac, not linking), copy the file contents and
    # (optionally) copy the times and mode.
    _copy_file_contents(src, dst)
    if preserve_mode or preserve_times:
        st = os.stat(src)

        # According to David Ascher <*****@*****.**>, utime() should be done
        # before chmod() (at least under NT).
        if preserve_times:
            os.utime(dst, (st[ST_ATIME], st[ST_MTIME]))
        if preserve_mode:
            os.chmod(dst, S_IMODE(st[ST_MODE]))

    return (dst, 1)
Exemple #2
0
def mkpath(name, mode=0o777, verbose=1, dry_run=0):
    """Create a directory and any missing ancestor directories.

    If the directory already exists (or if 'name' is the empty string, which
    means the current directory, which of course exists), then do nothing.
    Raise DistutilsFileError if unable to create some directory along the way
    (eg. some sub-path exists, but is a file rather than a directory).
    If 'verbose' is true, print a one-line summary of each mkdir to stdout.
    Return the list of directories actually created.
    """

    global _path_created

    # Detect a common bug -- name is None
    if not isinstance(name, str):
        raise DistutilsInternalError(
            "mkpath: 'name' must be a string (got %r)" % (name, ))

    # XXX what's the better way to handle verbosity? print as we create
    # each directory in the path (the current behaviour), or only announce
    # the creation of the whole path? (quite easy to do the latter since
    # we're not using a recursive algorithm)

    name = os.path.normpath(name)
    created_dirs = []
    if os.path.isdir(name) or name == '':
        return created_dirs
    if _path_created.get(os.path.abspath(name)):
        return created_dirs

    (head, tail) = os.path.split(name)
    tails = [tail]  # stack of lone dirs to create

    while head and tail and not os.path.isdir(head):
        (head, tail) = os.path.split(head)
        tails.insert(0, tail)  # push next higher dir onto stack

    # now 'head' contains the deepest directory that already exists
    # (that is, the child of 'head' in 'name' is the highest directory
    # that does *not* exist)
    for d in tails:
        #print "head = %s, d = %s: " % (head, d),
        head = os.path.join(head, d)
        abs_head = os.path.abspath(head)

        if _path_created.get(abs_head):
            continue

        if verbose >= 1:
            log.info("creating %s", head)

        if not dry_run:
            try:
                os.mkdir(head)
                created_dirs.append(head)
            except OSError as exc:
                raise DistutilsFileError("could not create '%s': %s" %
                                         (head, exc.args[-1]))

        _path_created[abs_head] = 1
    return created_dirs
Exemple #3
0
    def build_extension(self, ext):
        executable = ext.binding == Binding.Exec

        # Make sure that if pythonXX-sys is used, it builds against the current
        # executing python interpreter.
        bindir = os.path.dirname(sys.executable)

        env = os.environ.copy()
        env.update({
            # disables rust's pkg-config seeking for specified packages,
            # which causes pythonXX-sys to fall back to detecting the
            # interpreter from the path.
            "PATH": os.path.join(bindir, os.environ.get("PATH", ""))
        })

        # Find where to put the temporary build files created by `cargo`
        metadata_command = [
            "cargo",
            "metadata",
            "--manifest-path",
            ext.path,
            "--format-version",
            "1",
        ]
        # The decoding is needed for python 3.5 compatibility
        metadata = json.loads(check_output(metadata_command).decode("utf-8"))
        target_dir = metadata["target_directory"]
        if ext.platform_target is not None:
            target_dir = os.path.join(target_dir, ext.platform_target)

        if not os.path.exists(ext.path):
            raise DistutilsFileError(
                "Can not find rust extension project file: %s" % ext.path)

        features = set(ext.features)
        features.update(cpython_feature(binding=ext.binding))

        debug_build = ext.debug if ext.debug is not None else self.inplace
        debug_build = self.debug if self.debug is not None else debug_build
        if self.release:
            debug_build = False

        quiet = self.qbuild or ext.quiet

        # build cargo command
        feature_args = ["--features", " ".join(features)] if features else []

        if executable:
            args = (["cargo", "build", "--manifest-path", ext.path] +
                    feature_args + list(ext.args or []))
            if not debug_build:
                args.append("--release")
            if quiet:
                args.append("-q")
            #elif self.verbose:
            #    args.append("--verbose")

        else:
            args = (["cargo", "rustc", "--lib", "--manifest-path", ext.path] +
                    feature_args + list(ext.args or []))
            if not debug_build:
                args.append("--release")
            if quiet:
                args.append("-q")
            #elif self.verbose:
            #    args.append("--verbose")

            args.extend(["--", "--crate-type", "cdylib"])
            args.extend(ext.rustc_flags or [])

            # OSX requires special linker argument
            if sys.platform == "darwin":
                args.extend([
                    "-C", "link-arg=-undefined", "-C",
                    "link-arg=dynamic_lookup"
                ])

        if not quiet:
            print(" ".join(args), file=sys.stderr)

        if ext.native:
            env["RUSTFLAGS"] = "-C target-cpu=native"

        # Execute cargo
        try:
            output = subprocess.check_output(args, env=env)
        except subprocess.CalledProcessError as e:
            output = e.output
            if isinstance(output, bytes):
                output = e.output.decode("latin-1").strip()
            raise CompileError("cargo failed with code: %d\n%s" %
                               (e.returncode, output))

        except OSError:
            raise DistutilsExecError(
                "Unable to execute 'cargo' - this package "
                "requires rust to be installed and cargo to be on the PATH")

        if not quiet:
            if isinstance(output, bytes):
                output = output.decode("latin-1")
            if output:
                print(output, file=sys.stderr)

        # Find the shared library that cargo hopefully produced and copy
        # it into the build directory as if it were produced by build_ext.
        if debug_build:
            suffix = "debug"
        else:
            suffix = "release"

        # location of cargo compiled files
        artifactsdir = os.path.join(target_dir, suffix)
        dylib_paths = []

        if executable:
            for name, dest in ext.target.items():
                if name:
                    path = os.path.join(artifactsdir, name)
                    if os.access(path, os.X_OK):
                        dylib_paths.append((dest, path))
                        continue
                    else:
                        raise DistutilsExecError(
                            "rust build failed; "
                            'unable to find executable "%s" in %s' %
                            (name, target_dir))
                else:
                    # search executable
                    for name in os.listdir(artifactsdir):
                        path = os.path.join(artifactsdir, name)
                        if name.startswith(".") or not os.path.isfile(path):
                            continue

                        if os.access(path, os.X_OK):
                            dylib_paths.append((ext.name, path))
                            break

            if not dylib_paths:
                raise DistutilsExecError(
                    "rust build failed; unable to find executable in %s" %
                    target_dir)
        else:
            if sys.platform == "win32" or sys.platform == "cygwin":
                dylib_ext = "dll"
            elif sys.platform == "darwin":
                dylib_ext = "dylib"
            else:
                dylib_ext = "so"

            wildcard_so = "*{}.{}".format(ext.get_lib_name(), dylib_ext)

            try:
                dylib_paths.append((
                    ext.name,
                    next(glob.iglob(os.path.join(artifactsdir, wildcard_so))),
                ))
            except StopIteration:
                raise DistutilsExecError(
                    "rust build failed; unable to find any %s in %s" %
                    (wildcard_so, artifactsdir))
        print("Dylib paths:", dylib_paths)

        # Ask build_ext where the shared library would go if it had built it,
        # then copy it there.
        build_ext = self.get_finalized_command("build_ext")
        build_ext.inplace = self.inplace

        for target_fname, dylib_path in dylib_paths:
            if not target_fname:
                target_fname = os.path.basename(
                    os.path.splitext(os.path.basename(dylib_path)[3:])[0])

            if executable:
                ext_path = build_ext.get_ext_fullpath(target_fname)
                # remove .so extension
                ext_path, _ = os.path.splitext(ext_path)
                # remove python3 extension (i.e. cpython-36m)
                ext_path, _ = os.path.splitext(ext_path)

                ext.install_script(ext_path)
            else:
                ext_path = build_ext.get_ext_fullpath(target_fname)
                ext_path, ender = os.path.splitext(ext_path)
                ext_path, _ = os.path.splitext(ext_path)
                ext_path += ender

            try:
                os.makedirs(os.path.dirname(ext_path))
            except OSError:
                pass

            shutil.copyfile(dylib_path, ext_path)

            if sys.platform != "win32" and not debug_build:
                args = []
                if ext.strip == Strip.All:
                    args.append("-x")
                elif ext.strip == Strip.Debug:
                    args.append("-S")

                if args:
                    args.insert(0, "strip")
                    args.append(ext_path)
                    try:
                        output = subprocess.check_output(args, env=env)
                    except subprocess.CalledProcessError as e:
                        pass

            # executables and win32(cygwin)-dll's need X bits
            if executable or sys.platform == "win32" or sys.platform == "cygwin":
                mode = os.stat(ext_path).st_mode
                mode |= (mode & 0o444) >> 2  # copy R bits to X
                os.chmod(ext_path, mode)
Exemple #4
0
        try:
            fsrc = open(src, 'rb')
        except os.error, (errno, errstr):
            raise DistutilsFileError("could not open '%s': %s" % (src, errstr))

        if os.path.exists(dst):
            try:
                os.unlink(dst)
            except os.error, (errno, errstr):
                raise DistutilsFileError("could not delete '%s': %s" %
                                         (dst, errstr))

        try:
            fdst = open(dst, 'wb')
        except os.error, (errno, errstr):
            raise DistutilsFileError("could not create '%s': %s" %
                                     (dst, errstr))

        while 1:
            try:
                buf = fsrc.read(buffer_size)
            except os.error, (errno, errstr):
                raise DistutilsFileError("could not read from '%s': %s" %
                                         (src, errstr))

            if not buf:
                break

            try:
                fdst.write(buf)
            except os.error, (errno, errstr):
                raise DistutilsFileError("could not write to '%s': %s" %
Exemple #5
0
def move_file (src, dst,
               verbose=1,
               dry_run=0):

    """Move a file 'src' to 'dst'.  If 'dst' is a directory, the file will
    be moved into it with the same name; otherwise, 'src' is just renamed
    to 'dst'.  Return the new full name of the file.

    Handles cross-device moves on Unix using 'copy_file()'.  What about
    other systems???
    """
    from os.path import exists, isfile, isdir, basename, dirname
    import errno

    if verbose >= 1:
        log.info("moving %s -> %s", src, dst)

    if dry_run:
        return dst

    if not isfile(src):
        raise DistutilsFileError("can't move '%s': not a regular file" % src)

    if isdir(dst):
        dst = os.path.join(dst, basename(src))
    elif exists(dst):
        raise DistutilsFileError(
              "can't move '%s': destination '%s' already exists" %
              (src, dst))

    if not isdir(dirname(dst)):
        raise DistutilsFileError(
              "can't move '%s': destination '%s' not a valid path" %
              (src, dst))

    copy_it = False
    try:
        os.rename(src, dst)
    except os.error as e:
        (num, msg) = e
        if num == errno.EXDEV:
            copy_it = True
        else:
            raise DistutilsFileError(
                  "couldn't move '%s' to '%s': %s" % (src, dst, msg))

    if copy_it:
        copy_file(src, dst, verbose=verbose)
        try:
            os.unlink(src)
        except os.error as e:
            (num, msg) = e
            try:
                os.unlink(dst)
            except os.error:
                pass
            raise DistutilsFileError(
                  "couldn't move '%s' to '%s' by copy/delete: "
                  "delete '%s' failed: %s"
                  % (src, dst, src, msg))
    return dst
Exemple #6
0
    def build_extension(self, ext):
        executable = ext.binding == Binding.Exec

        rust_target_info = get_rust_target_info()

        # Make sure that if pythonXX-sys is used, it builds against the current
        # executing python interpreter.
        bindir = os.path.dirname(sys.executable)

        env = os.environ.copy()
        env.update({
            # disables rust's pkg-config seeking for specified packages,
            # which causes pythonXX-sys to fall back to detecting the
            # interpreter from the path.
            "PATH":
            os.path.join(bindir, os.environ.get("PATH", "")),
            "PYTHON_SYS_EXECUTABLE":
            os.environ.get("PYTHON_SYS_EXECUTABLE", sys.executable),
            "PYO3_PYTHON":
            os.environ.get("PYO3_PYTHON", sys.executable),
        })
        rustflags = ""

        # If we are on a 64-bit machine, but running a 32-bit Python, then
        # we'll target a 32-bit Rust build.
        # Automatic target detection can be overridden via the CARGO_BUILD_TARGET
        # environment variable.
        # TODO: include --target for all platforms so env vars can't break the build
        target_triple = None
        target_args = []
        if os.getenv("CARGO_BUILD_TARGET"):
            target_triple = os.environ["CARGO_BUILD_TARGET"]
        elif self.plat_name == "win32":
            target_triple = "i686-pc-windows-msvc"
        elif self.plat_name == "win-amd64":
            target_triple = "x86_64-pc-windows-msvc"

        if target_triple is not None:
            target_args = ["--target", target_triple]

        # Find where to put the temporary build files created by `cargo`
        metadata_command = [
            "cargo",
            "metadata",
            "--manifest-path",
            ext.path,
            "--format-version",
            "1",
        ]
        # The decoding is needed for python 3.5 compatibility
        metadata = json.loads(check_output(metadata_command).decode("utf-8"))
        target_dir = metadata["target_directory"]

        if not os.path.exists(ext.path):
            raise DistutilsFileError(
                "Can not find rust extension project file: %s" % ext.path)

        features = set(ext.features)
        features.update(cpython_feature(binding=ext.binding))

        debug_build = ext.debug if ext.debug is not None else self.inplace
        debug_build = self.debug if self.debug is not None else debug_build
        if self.release:
            debug_build = False

        quiet = self.qbuild or ext.quiet

        # build cargo command
        feature_args = ["--features", " ".join(features)] if features else []

        if executable:
            args = (["cargo", "build", "--manifest-path", ext.path] +
                    feature_args + target_args + list(ext.args or []))
            if not debug_build:
                args.append("--release")
            if quiet:
                args.append("-q")
            elif self.verbose:
                args.append("--verbose")

        else:
            args = (["cargo", "rustc", "--lib", "--manifest-path", ext.path] +
                    feature_args + target_args + list(ext.args or []))
            if not debug_build:
                args.append("--release")
            if quiet:
                args.append("-q")
            elif self.verbose:
                args.append("--verbose")

            args.extend(["--", "--crate-type", "cdylib"])
            args.extend(ext.rustc_flags or [])

            # OSX requires special linker argument
            if sys.platform == "darwin":
                args.extend([
                    "-C", "link-arg=-undefined", "-C",
                    "link-arg=dynamic_lookup"
                ])
            # Tell musl targets not to statically link libc. See
            # https://github.com/rust-lang/rust/issues/59302 for details.
            if b'target_env="musl"' in rust_target_info:
                rustflags += " -C target-feature=-crt-static"

        if not quiet:
            print(" ".join(args), file=sys.stderr)

        if ext.native:
            rustflags += " -C target-cpu=native"

        if rustflags:
            env["RUSTFLAGS"] = rustflags

        # Execute cargo
        try:
            output = subprocess.check_output(args, env=env)
        except subprocess.CalledProcessError as e:
            output = e.output
            if isinstance(output, bytes):
                output = e.output.decode("latin-1").strip()
            raise CompileError("cargo failed with code: %d\n%s" %
                               (e.returncode, output))

        except OSError:
            raise DistutilsExecError(
                "Unable to execute 'cargo' - this package "
                "requires rust to be installed and cargo to be on the PATH")

        if not quiet:
            if isinstance(output, bytes):
                output = output.decode("latin-1")
            if output:
                print(output, file=sys.stderr)

        # Find the shared library that cargo hopefully produced and copy
        # it into the build directory as if it were produced by build_ext.
        if debug_build:
            suffix = "debug"
        else:
            suffix = "release"

        # location of cargo compiled files
        artifactsdir = os.path.join(target_dir, target_triple or "", suffix)
        dylib_paths = []

        if executable:
            for name, dest in ext.target.items():
                if name:
                    path = os.path.join(artifactsdir, name)
                    if os.access(path, os.X_OK):
                        dylib_paths.append((dest, path))
                        continue
                    else:
                        raise DistutilsExecError(
                            "rust build failed; "
                            'unable to find executable "%s" in %s' %
                            (name, target_dir))
                else:
                    # search executable
                    for name in os.listdir(artifactsdir):
                        path = os.path.join(artifactsdir, name)
                        if name.startswith(".") or not os.path.isfile(path):
                            continue

                        if os.access(path, os.X_OK):
                            dylib_paths.append((ext.name, path))
                            break

            if not dylib_paths:
                raise DistutilsExecError(
                    "rust build failed; unable to find executable in %s" %
                    target_dir)
        else:
            if sys.platform == "win32" or sys.platform == "cygwin":
                dylib_ext = "dll"
            elif sys.platform == "darwin":
                dylib_ext = "dylib"
            else:
                dylib_ext = "so"

            wildcard_so = "*{}.{}".format(ext.get_lib_name(), dylib_ext)

            try:
                dylib_paths.append((
                    ext.name,
                    next(glob.iglob(os.path.join(artifactsdir, wildcard_so))),
                ))
            except StopIteration:
                raise DistutilsExecError(
                    "rust build failed; unable to find any %s in %s" %
                    (wildcard_so, artifactsdir))

        # Ask build_ext where the shared library would go if it had built it,
        # then copy it there.
        build_ext = self.get_finalized_command("build_ext")
        build_ext.inplace = self.inplace

        for target_fname, dylib_path in dylib_paths:
            if not target_fname:
                target_fname = os.path.basename(
                    os.path.splitext(os.path.basename(dylib_path)[3:])[0])

            if executable:
                ext_path = build_ext.get_ext_fullpath(target_fname)
                # remove .so extension
                ext_path, _ = os.path.splitext(ext_path)
                # remove python3 extension (i.e. cpython-36m)
                ext_path, _ = os.path.splitext(ext_path)

                ext.install_script(ext_path)
            else:
                # Technically it's supposed to contain a
                # `setuptools.Extension`, but in practice the only attribute it
                # checks is `ext.py_limited_api`.
                modpath = target_fname.split('.')[-1]
                assert modpath not in build_ext.ext_map
                build_ext.ext_map[modpath] = ext
                try:
                    ext_path = build_ext.get_ext_fullpath(target_fname)
                finally:
                    del build_ext.ext_map[modpath]

            try:
                os.makedirs(os.path.dirname(ext_path))
            except OSError:
                pass

            shutil.copyfile(dylib_path, ext_path)

            if sys.platform != "win32" and not debug_build:
                args = []
                if ext.strip == Strip.All:
                    args.append("-x")
                elif ext.strip == Strip.Debug:
                    args.append("-S")

                if args:
                    args.insert(0, "strip")
                    args.append(ext_path)
                    try:
                        output = subprocess.check_output(args, env=env)
                    except subprocess.CalledProcessError as e:
                        pass

            # executables and win32(cygwin)-dll's need X bits
            if executable or sys.platform == "win32" or sys.platform == "cygwin":
                mode = os.stat(ext_path).st_mode
                mode |= (mode & 0o444) >> 2  # copy R bits to X
                os.chmod(ext_path, mode)
Exemple #7
0
def copy_tree(
    src,
    dst,
    preserve_mode=1,
    preserve_times=1,
    preserve_symlinks=0,
    update=0,
    verbose=0,
    dry_run=0,
    condition=None,
):
    """
    Copy an entire directory tree 'src' to a new location 'dst'.  Both
    'src' and 'dst' must be directory names.  If 'src' is not a
    directory, raise DistutilsFileError.  If 'dst' does not exist, it is
    created with 'mkpath()'.  The end result of the copy is that every
    file in 'src' is copied to 'dst', and directories under 'src' are
    recursively copied to 'dst'.  Return the list of files that were
    copied or might have been copied, using their output name.  The
    return value is unaffected by 'update' or 'dry_run': it is simply
    the list of all files under 'src', with the names changed to be
    under 'dst'.

    'preserve_mode' and 'preserve_times' are the same as for
    'copy_file'; note that they only apply to regular files, not to
    directories.  If 'preserve_symlinks' is true, symlinks will be
    copied as symlinks (on platforms that support them!); otherwise
    (the default), the destination of the symlink will be copied.
    'update' and 'verbose' are the same as for 'copy_file'.
    """
    assert isinstance(src, (str, unicode)), repr(src)
    assert isinstance(dst, (str, unicode)), repr(dst)

    from distutils import log
    from distutils.dep_util import newer
    from distutils.dir_util import mkpath
    from distutils.errors import DistutilsFileError

    src = fsencoding(src)
    dst = fsencoding(dst)

    if condition is None:
        condition = skipscm

    if not dry_run and not zipio.isdir(src):
        raise DistutilsFileError("cannot copy tree '%s': not a directory" %
                                 src)
    try:
        names = zipio.listdir(src)
    except os.error as exc:
        (errno, errstr) = exc.args
        if dry_run:
            names = []
        else:
            raise DistutilsFileError("error listing files in '%s': %s" %
                                     (src, errstr))

    if not dry_run:
        mkpath(dst)

    outputs = []

    for n in names:
        src_name = os.path.join(src, n)
        dst_name = os.path.join(dst, n)
        if (condition is not None) and (not condition(src_name)):
            continue

        # Note: using zipio's internal _locate function throws an IOError on
        # dead symlinks, so handle it here.
        if os.path.islink(src_name) and not os.path.exists(
                os.path.join(src, os.readlink(src_name))):
            continue

        if preserve_symlinks and zipio.islink(src_name):
            link_dest = zipio.readlink(src_name)
            log.info("linking %s -> %s", dst_name, link_dest)
            if not dry_run:
                if update and not newer(src, dst_name):
                    pass
                else:
                    make_symlink(link_dest, dst_name)
            outputs.append(dst_name)

        elif zipio.isdir(src_name) and not os.path.isfile(src_name):
            # ^^^ this odd tests ensures that resource files that
            # happen to be a zipfile won't get extracted.
            outputs.extend(
                copy_tree(
                    src_name,
                    dst_name,
                    preserve_mode,
                    preserve_times,
                    preserve_symlinks,
                    update,
                    dry_run=dry_run,
                    condition=condition,
                ))
        else:
            copy_file(
                src_name,
                dst_name,
                preserve_mode,
                preserve_times,
                update,
                dry_run=dry_run,
            )
            outputs.append(dst_name)

    return outputs
Exemple #8
0
    def compile(
        self, sources,
        output_dir=None, macros=None, include_dirs=None, debug=0,
        extra_preargs=None, extra_postargs=None, depends=None
    ):
        macros = macros or []
        include_dirs = include_dirs or []
        extra_preargs = extra_preargs or []
        extra_postargs = extra_postargs or []

        pythonVersionOpts = self.versionOpts()

        if not os.path.exists(output_dir):
            os.makedirs(output_dir)

        binpath = _qp(self._binpath)
        if self.build_exe:
            compileOpts = self._exeCompileOpts
        else:
            compileOpts = self._compileOpts
        outputOpts = self._outputOpts

        includePathOpts = []

        # All object files will be placed in one of three directories:
        # infra   - All of the infrastructure's object files.
        # project - The project's own object files.
        # outside - Any source files specified by the project which are not
        #           contained in the project's own directory.
        orig_sources = sources
        sources = []
        for source in orig_sources:
            if os.path.abspath(source).startswith(os.getcwd()):
                sources.append((winpath(source,self.winonly), 'project'))
            else:
                sources.append((winpath(source, self.winonly), 'outside'))

        if self.with_pyd:
            for file in _pydFiles:
                filePath = os.path.join(_infraDir, 'pyd', file)
                if not os.path.isfile(filePath):
                    raise DistutilsPlatformError("Required Pyd source file '%s' is"
                        " missing." % filePath
                    )
                sources.append((winpath(filePath,self.winonly), 'infra'))
            for file in _utilFiles:
                filePath = os.path.join(_infraDir, 'util', file)
                if not os.path.isfile(filePath):
                    raise DistutilsPlatformError("Required util source file '%s' is"
                        " missing." % filePath
                    )
                sources.append((winpath(filePath,self.winonly), 'infra'))
        if self.build_deimos:
            for file in _deimosFiles:
                filePath = os.path.join(_infraDir, 'deimos', 'python', file)
                if not os.path.isfile(filePath):
                    raise DistutilsPlatformError("Required deimos header "
                        "file '%s' is missing." % filePath
                    )
                sources.append((winpath(filePath,self.winonly), 'infra'))
        # If using PydMain, parse the template file
        if self.build_exe:
            pass
        elif self.with_main:
            name = self.proj_name
            # Store the finished pydmain.d file alongside the object files
            infra_output_dir = winpath(os.path.join(output_dir, 'infra'), self.winonly)
            if not os.path.exists(infra_output_dir):
                os.makedirs(infra_output_dir)
            mainFilename = os.path.join(infra_output_dir, 'pydmain.d')
            make_pydmain(mainFilename, name)
            sources.append((winpath(mainFilename,self.winonly), 'infra'))
        # Add the infraDir to the include path for pyd and utils.
        includePathOpts += self._includeOpts
        includePathOpts[-1] = includePathOpts[-1] % winpath(os.path.join(_infraDir), self.winonly)

        for include_dir in include_dirs:
            includePathOpts += self._includeOpts
            includePathOpts[-1] %= winpath(include_dir, self.winonly)

        if self.build_exe:
            pass
        else:
            # Add DLL/SO boilerplate code file.
            if _isPlatWin:
                boilerplatePath = os.path.join(_infraDir, 'd',
                    'python_dll_windows_boilerplate.d'
                )
            else:
                boilerplatePath = os.path.join(_infraDir, 'd',
                    'python_so_linux_boilerplate.d'
                )
            if not os.path.isfile(boilerplatePath):
                raise DistutilsFileError('Required supporting code file "%s"'
                    ' is missing.' % boilerplatePath
                )
            sources.append((winpath(boilerplatePath,self.winonly), 'infra'))

        for imp in self.string_imports_from_ext:
            if not (os.path.isfile(imp) or os.path.isdir(imp)):
                raise DistutilsFileError('String import file "%s" does not exist' %
                        imp)

        userVersionAndDebugOpts = (
              [self._versionOpt % v for v in self.version_flags_from_ext] +
              [self._debugOpt   % v for v in self.debug_flags_from_ext]
        )

        # Optimization opts
        if debug:
            optimizationOpts = self._debugOptimizeOpts
        elif self.optimize:
            optimizationOpts = self._releaseOptimizeOpts
        else:
            optimizationOpts = self._defaultOptimizeOpts

        unittestOpt = []
        if self.unittest_from_ext:
            unittestOpt.append(self._unittestOpt)
        if self.property_from_ext:
            unittestOpt.append(self._propertyOpt)
        if self.string_imports_from_ext:
            imps = set()
            for imp in self.string_imports_from_ext:
                if os.path.isfile(imp):
                    imps.add(os.path.dirname(os.path.abspath(imp)))
                else:
                    imps.add(os.path.abspath(imp))
            unittestOpt.extend([self._stringImportOpt % (imp,)
                for imp in imps])
        objFiles = []

        if self.lump:
            objName = self._make_object_name(output_dir, 'infra', 'temp')
            outOpts = outputOpts[:]
            outOpts[-1] = outOpts[-1] % objName
            cmdElements = (
                [binpath] + extra_preargs + unittestOpt + compileOpts +
                pythonVersionOpts + optimizationOpts +
                includePathOpts + outOpts + userVersionAndDebugOpts +
                [_qp(source[0]) for source in sources] + extra_postargs
            )
            cmdElements = [el for el in cmdElements if el]
            wrapped_spawn(self,cmdElements, 'pyd_compile')
            return [objName]
        else:
          for source, source_type in sources:
            outOpts = outputOpts[:]
            objFilename = os.path.splitext(source)[0]
            if source_type == 'project':
                objName = self._make_object_name(output_dir, 'project', objFilename)
            elif source_type == 'outside':
                objName = self._make_object_name(output_dir, 'outside', os.path.basename(objFilename))
            else: # infra
                objName = self._make_object_name(output_dir, 'infra', os.path.basename(objFilename))
            objFiles.append(objName)
            outOpts[-1] = outOpts[-1] % objName

            cmdElements = (
                [binpath] + extra_preargs + unittestOpt + compileOpts +
                pythonVersionOpts + optimizationOpts +
                includePathOpts + outOpts + userVersionAndDebugOpts +
                [_qp(source)] + extra_postargs
            )
            cmdElements = [el for el in cmdElements if el]
            spawn0(self,cmdElements)
        return objFiles
Exemple #9
0
    def build_extension(self,
                        ext: RustExtension,
                        forced_target_triple: Optional[str] = None
                        ) -> List["_BuiltModule"]:

        target_info = self._detect_rust_target(forced_target_triple)
        if target_info is not None:
            target_triple = target_info.triple
            cross_lib = target_info.cross_lib
            linker = target_info.linker
            # We're ignoring target_info.linker_args for now because we're not
            # sure if they will always do the right thing. Might help with some
            # of the OS-specific logic if it does.

        else:
            target_triple = None
            cross_lib = None
            linker = None

        rustc_cfgs = get_rustc_cfgs(target_triple)

        env = _prepare_build_environment(cross_lib)

        if not os.path.exists(ext.path):
            raise DistutilsFileError(
                f"can't find Rust extension project file: {ext.path}")

        quiet = self.qbuild or ext.quiet
        debug = self._is_debug_build(ext)

        # Find where to put the temporary build files created by `cargo`
        target_dir = _base_cargo_target_dir(ext, quiet=quiet)
        if target_triple is not None:
            target_dir = os.path.join(target_dir, target_triple)

        cargo_args = self._cargo_args(ext=ext,
                                      target_triple=target_triple,
                                      release=not debug,
                                      quiet=quiet)

        rustflags = []

        if linker is not None:
            rustflags.extend(["-C", "linker=" + linker])

        if ext._uses_exec_binding():
            command = [
                self.cargo, "build", "--manifest-path", ext.path, *cargo_args
            ]

        else:
            rustc_args = [
                "--crate-type",
                "cdylib",
                *ext.rustc_flags,
            ]

            # OSX requires special linker arguments
            if sys.platform == "darwin":
                ext_basename = os.path.basename(
                    self.get_dylib_ext_path(ext, ext.name))
                rustc_args.extend([
                    "-C",
                    f"link-args=-undefined dynamic_lookup -Wl,-install_name,@rpath/{ext_basename}",
                ])

            # Tell musl targets not to statically link libc. See
            # https://github.com/rust-lang/rust/issues/59302 for details.
            if rustc_cfgs.get("target_env") == "musl":
                # This must go in the env otherwise rustc will refuse to build
                # the cdylib, see https://github.com/rust-lang/cargo/issues/10143
                rustflags.append("-Ctarget-feature=-crt-static")

            command = [
                self.cargo,
                "rustc",
                "--lib",
                "--manifest-path",
                ext.path,
                *cargo_args,
                "--",
                *rustc_args,
            ]

        if rustflags:
            existing_rustflags = env.get("RUSTFLAGS")
            if existing_rustflags is not None:
                rustflags.append(existing_rustflags)
            new_rustflags = " ".join(rustflags)
            env["RUSTFLAGS"] = new_rustflags

            # print RUSTFLAGS being added before the command
            if not quiet:
                print(f"[RUSTFLAGS={new_rustflags}]", end=" ", file=sys.stderr)

        if not quiet:
            print(" ".join(command), file=sys.stderr)

        # Execute cargo
        try:
            # If quiet, capture all output and only show it in the exception
            # If not quiet, forward all cargo output to stderr
            stdout = subprocess.PIPE if quiet else sys.stderr.fileno()
            stderr = subprocess.PIPE if quiet else None
            subprocess.run(command,
                           env=env,
                           stdout=stdout,
                           stderr=stderr,
                           text=True,
                           check=True)
        except subprocess.CalledProcessError as e:
            raise CompileError(format_called_process_error(e))

        except OSError:
            raise DistutilsExecError(
                "Unable to execute 'cargo' - this package "
                "requires Rust to be installed and cargo to be on the PATH")

        # Find the shared library that cargo hopefully produced and copy
        # it into the build directory as if it were produced by build_ext.

        profile = ext.get_cargo_profile()
        if profile:
            # https://doc.rust-lang.org/cargo/reference/profiles.html
            if profile in {"dev", "test"}:
                profile_dir = "debug"
            elif profile == "bench":
                profile_dir = "release"
            else:
                profile_dir = profile
        else:
            profile_dir = "debug" if debug else "release"
        artifacts_dir = os.path.join(target_dir, profile_dir)
        dylib_paths = []

        if ext._uses_exec_binding():
            for name, dest in ext.target.items():
                if not name:
                    name = dest.split(".")[-1]
                exe = sysconfig.get_config_var("EXE")
                if exe is not None:
                    name += exe

                path = os.path.join(artifacts_dir, name)
                if os.access(path, os.X_OK):
                    dylib_paths.append(_BuiltModule(dest, path))
                else:
                    raise DistutilsExecError(
                        "Rust build failed; "
                        f"unable to find executable '{name}' in '{artifacts_dir}'"
                    )
        else:
            platform = sysconfig.get_platform()
            if "win" in platform:
                dylib_ext = "dll"
            elif platform.startswith("macosx"):
                dylib_ext = "dylib"
            elif "wasm32" in platform:
                dylib_ext = "wasm"
            else:
                dylib_ext = "so"

            wildcard_so = "*{}.{}".format(ext.get_lib_name(quiet=quiet),
                                          dylib_ext)

            try:
                dylib_paths.append(
                    _BuiltModule(
                        ext.name,
                        next(
                            glob.iglob(os.path.join(artifacts_dir,
                                                    wildcard_so))),
                    ))
            except StopIteration:
                raise DistutilsExecError(
                    f"Rust build failed; unable to find any {wildcard_so} in {artifacts_dir}"
                )
        return dylib_paths
Exemple #10
0
    def run(self):  # noqa: C901
        if DEBUG:
            print("before _get_package_data():")
            print("vendor =", self.vendor)
            print("packager =", self.packager)
            print("doc_files =", self.doc_files)
            print("changelog =", self.changelog)

        # make directories
        if self.spec_only:
            spec_dir = self.dist_dir
            self.mkpath(spec_dir)
        else:
            rpm_dir = {}
            for d in ('SOURCES', 'SPECS', 'BUILD', 'RPMS', 'SRPMS'):
                rpm_dir[d] = os.path.join(self.rpm_base, d)
                self.mkpath(rpm_dir[d])
            spec_dir = rpm_dir['SPECS']

        # Spec file goes into 'dist_dir' if '--spec-only specified',
        # build/rpm.<plat> otherwise.
        spec_path = os.path.join(spec_dir, "%s.spec" % self.distribution.get_name())
        self.execute(
            write_file, (spec_path, self._make_spec_file()), "writing '%s'" % spec_path
        )

        if self.spec_only:  # stop if requested
            return

        # Make a source distribution and copy to SOURCES directory with
        # optional icon.
        saved_dist_files = self.distribution.dist_files[:]
        sdist = self.reinitialize_command('sdist')
        if self.use_bzip2:
            sdist.formats = ['bztar']
        else:
            sdist.formats = ['gztar']
        self.run_command('sdist')
        self.distribution.dist_files = saved_dist_files

        source = sdist.get_archive_files()[0]
        source_dir = rpm_dir['SOURCES']
        self.copy_file(source, source_dir)

        if self.icon:
            if os.path.exists(self.icon):
                self.copy_file(self.icon, source_dir)
            else:
                raise DistutilsFileError("icon file '%s' does not exist" % self.icon)

        # build package
        log.info("building RPMs")
        rpm_cmd = ['rpmbuild']

        if self.source_only:  # what kind of RPMs?
            rpm_cmd.append('-bs')
        elif self.binary_only:
            rpm_cmd.append('-bb')
        else:
            rpm_cmd.append('-ba')
        rpm_cmd.extend(['--define', '__python %s' % self.python])
        if self.rpm3_mode:
            rpm_cmd.extend(['--define', '_topdir %s' % os.path.abspath(self.rpm_base)])
        if not self.keep_temp:
            rpm_cmd.append('--clean')

        if self.quiet:
            rpm_cmd.append('--quiet')

        rpm_cmd.append(spec_path)
        # Determine the binary rpm names that should be built out of this spec
        # file
        # Note that some of these may not be really built (if the file
        # list is empty)
        nvr_string = "%{name}-%{version}-%{release}"
        src_rpm = nvr_string + ".src.rpm"
        non_src_rpm = "%{arch}/" + nvr_string + ".%{arch}.rpm"
        q_cmd = r"rpm -q --qf '{} {}\n' --specfile '{}'".format(
            src_rpm,
            non_src_rpm,
            spec_path,
        )

        out = os.popen(q_cmd)
        try:
            binary_rpms = []
            source_rpm = None
            while True:
                line = out.readline()
                if not line:
                    break
                ell = line.strip().split()
                assert len(ell) == 2
                binary_rpms.append(ell[1])
                # The source rpm is named after the first entry in the spec file
                if source_rpm is None:
                    source_rpm = ell[0]

            status = out.close()
            if status:
                raise DistutilsExecError("Failed to execute: %s" % repr(q_cmd))

        finally:
            out.close()

        self.spawn(rpm_cmd)

        if not self.dry_run:
            if self.distribution.has_ext_modules():
                pyversion = get_python_version()
            else:
                pyversion = 'any'

            if not self.binary_only:
                srpm = os.path.join(rpm_dir['SRPMS'], source_rpm)
                assert os.path.exists(srpm)
                self.move_file(srpm, self.dist_dir)
                filename = os.path.join(self.dist_dir, source_rpm)
                self.distribution.dist_files.append(('bdist_rpm', pyversion, filename))

            if not self.source_only:
                for rpm in binary_rpms:
                    rpm = os.path.join(rpm_dir['RPMS'], rpm)
                    if os.path.exists(rpm):
                        self.move_file(rpm, self.dist_dir)
                        filename = os.path.join(self.dist_dir, os.path.basename(rpm))
                        self.distribution.dist_files.append(
                            ('bdist_rpm', pyversion, filename)
                        )
def copy_tree(src, dst,
        preserve_mode=1,
        preserve_times=1,
        preserve_symlinks=0,
        update=0,
        verbose=0,
        dry_run=0,
        condition=None):

    """
    Copy an entire directory tree 'src' to a new location 'dst'.  Both
    'src' and 'dst' must be directory names.  If 'src' is not a
    directory, raise DistutilsFileError.  If 'dst' does not exist, it is
    created with 'mkpath()'.  The end result of the copy is that every
    file in 'src' is copied to 'dst', and directories under 'src' are
    recursively copied to 'dst'.  Return the list of files that were
    copied or might have been copied, using their output name.  The
    return value is unaffected by 'update' or 'dry_run': it is simply
    the list of all files under 'src', with the names changed to be
    under 'dst'.

    'preserve_mode' and 'preserve_times' are the same as for
    'copy_file'; note that they only apply to regular files, not to
    directories.  If 'preserve_symlinks' is true, symlinks will be
    copied as symlinks (on platforms that support them!); otherwise
    (the default), the destination of the symlink will be copied.
    'update' and 'verbose' are the same as for 'copy_file'.
    """


    from distutils.dir_util import mkpath
    from distutils.file_util import copy_file
    from distutils.dep_util import newer
    from distutils.errors import DistutilsFileError
    from distutils import log

    src = fsencoding(src)
    dst = fsencoding(dst)

    if condition is None:
        condition = skipjunk

    if not dry_run and not os.path.isdir(src):
        raise DistutilsFileError(
            "cannot copy tree '%s': not a directory" % src)
    try:
        names = os.listdir(src)
    except OSError:
        err = sys.exc_info()[1] # Python 2.5 / 3 compatibility
        if dry_run:
            names = []
        else:
            raise DistutilsFileError("error listing files in '%s': %s" % (
                src, err))

    if not dry_run:
        mkpath(dst)

    outputs = []

    for n in names:
        src_name = os.path.join(src, n)
        dst_name = os.path.join(dst, n)
        if (condition is not None) and (not condition(src_name)):
            continue

        if preserve_symlinks and os.path.islink(src_name):
            link_dest = os.readlink(src_name)
            log.info("linking %s -> %s", dst_name, link_dest)
            if not dry_run:
                if update and not newer(src, dst_name):
                    pass
                else:
                    if os.path.islink(dst_name):
                        os.remove(dst_name)
                    os.symlink(link_dest, dst_name)
            outputs.append(dst_name)

        elif os.path.isdir(src_name):
            outputs.extend(
                copy_tree(src_name, dst_name, preserve_mode,
                          preserve_times, preserve_symlinks, update,
                          dry_run=dry_run, condition=condition))
        else:
            copy_file(src_name, dst_name, preserve_mode,
                      preserve_times, update, dry_run=dry_run)
            outputs.append(dst_name)

    return outputs
Exemple #12
0
        #print "head = %s, d = %s: " % (head, d),
        head = os.path.join(head, d)
        abs_head = os.path.abspath(head)

        if _path_created.get(abs_head):
            continue

        if verbose >= 1:
            log.info("creating %s", head)

        if not dry_run:
            try:
                os.mkdir(head, mode)
            except OSError, exc:
                if not (exc.errno == errno.EEXIST and os.path.isdir(head)):
                    raise DistutilsFileError("could not create '%s': %s" %
                                             (head, exc.args[-1]))
            created_dirs.append(head)

        _path_created[abs_head] = 1
    return created_dirs


def create_tree(base_dir, files, mode=0777, verbose=1, dry_run=0):
    """Create all the empty directories under 'base_dir' needed to put 'files'
    there.

    'base_dir' is just the a name of a directory which doesn't necessarily
    exist yet; 'files' is a list of filenames to be interpreted relative to
    'base_dir'.  'base_dir' + the directory portion of every file in 'files'
    will be created if it doesn't already exist.  'mode', 'verbose' and
    'dry_run' flags are as for 'mkpath()'.
Exemple #13
0
    def link (self,
        target_desc, objects, output_filename,
        output_dir=None,
        libraries=None, library_dirs=None, runtime_library_dirs=None,
        export_symbols=None, debug=0,
        extra_preargs=None, extra_postargs=None,
        build_temp=None, target_lang=None
    ):
        # Distutils defaults to None for "unspecified option list"; we want
        # empty lists in that case (this substitution is done here in the body
        # rather than by changing the default parameters in case distutils
        # passes None explicitly).
        libraries = libraries or []
        library_dirs = library_dirs or []
        runtime_library_dirs = runtime_library_dirs or []
        export_symbols = export_symbols or []
        extra_preargs = extra_preargs or []
        extra_postargs = extra_postargs or []

        # On 64-bit Windows we just link to pythonXX.lib from the installation
        if _is_win64(): 
            library_dirs = list(set(_build_ext_library_dirs() + library_dirs))

        binpath = self._binpath
        if hasattr(self, '_linkOutputOpts'):
            outputOpts = self._linkOutputOpts[:]
        else:
            outputOpts = self._outputOpts[:]
        objectOpts = [_qp(fn) for fn in objects]

        (objects, output_dir) = self._fix_object_args (objects, output_dir)
        (libraries, library_dirs, runtime_library_dirs) = \
            self._fix_lib_args (libraries, library_dirs, runtime_library_dirs)
        if runtime_library_dirs:
            self.warn('This CCompiler implementation does nothing with'
                ' "runtime_library_dirs": ' + str(runtime_library_dirs)
            )

        if output_dir and os.path.basename(output_filename) == output_filename:
            output_filename = os.path.join(output_dir, output_filename)
        else:
            if not output_filename:
                raise DistutilsFileError( 'Neither output_dir nor' \
                    ' output_filename was specified.')
            output_dir = os.path.dirname(output_filename)
            if not output_dir:
                raise DistutilsFileError( 'Unable to guess output_dir on the'\
                    ' bases of output_filename "%s" alone.' % output_filename)

        # Format the output filename option
        # (-offilename in DMD, -o filename in GDC, -of=filename in LDC)
        outputOpts[-1] = outputOpts[-1] % _qp(winpath(output_filename,self.winonly))

        if not os.path.exists(output_dir):
            os.makedirs(output_dir)

        if not self._need_link(objects, output_filename):
            print ("All binary output files are up to date.")
            return

        if self.build_exe:
            sharedOpts = []
            linkOpts = self._exeLinkOpts
            pythonLibOpt = []
            if target_desc != cc.CCompiler.EXECUTABLE:
                raise LinkError('This CCompiler implementation should be building'
                    ' an executable'
                )
        else:
            # The .def file (on Windows) or -shared and -soname (on Linux)
            sharedOpts = self._def_file(build_temp, output_filename)
            if target_desc != cc.CCompiler.SHARED_OBJECT:
                raise LinkError('This CCompiler implementation should be building '
                    ' a shared object'
                )
            linkOpts = self._linkOpts
        # The python .lib file, if needed
        pythonLibOpt = self._lib_file(libraries)
        if pythonLibOpt:
            pythonLibOpt = _qp(pythonLibOpt)


        # Library linkage options
        print ("library_dirs: %s" % (library_dirs,))
        print ("runtime_library_dirs: %s" % (runtime_library_dirs,))
        print ("libraries: %s"% (libraries,))
        libOpts = gen_lib_options(self, library_dirs, runtime_library_dirs, libraries)

        # Optimization opts
        if debug:
            optimizationOpts = self._debugOptimizeOpts
        elif self.optimize:
            optimizationOpts = self._releaseOptimizeOpts
        else:
            optimizationOpts = self._defaultOptimizeOpts

        cmdElements = (
            [binpath] + extra_preargs + linkOpts + optimizationOpts +
            outputOpts + [pythonLibOpt] + objectOpts + libOpts + sharedOpts +
            extra_postargs
        )
        cmdElements = [el for el in cmdElements if el]

        wrapped_spawn(self,cmdElements, 'pyd_link')
Exemple #14
0
def cfg_to_args(path='setup.cfg', script_args=()):
    """ Distutils2 to distutils1 compatibility util.

        This method uses an existing setup.cfg to generate a dictionary of
        keywords that can be used by distutils.core.setup(kwargs**).

        :param file:
            The setup.cfg path.
        :parm script_args:
            List of commands setup.py was called with.
        :raises DistutilsFileError:
            When the setup.cfg file is not found.

    """

    # The method source code really starts here.
    if sys.version_info >= (3, 2):
        parser = configparser.ConfigParser()
    else:
        parser = configparser.SafeConfigParser()
    if not os.path.exists(path):
        raise DistutilsFileError("file '%s' does not exist" %
                                 os.path.abspath(path))
    parser.read(path)
    config = {}
    for section in parser.sections():
        config[section] = dict(parser.items(section))
        for k in config[section]:
            config[section][k.replace('-', '_')] = config[section].pop(k)

    # Run setup_hooks, if configured
    setup_hooks = has_get_option(config, 'global', 'setup_hooks')
    package_dir = has_get_option(config, 'files', 'packages_root')

    # Add the source package directory to sys.path in case it contains
    # additional hooks, and to make sure it's on the path before any existing
    # installations of the package
    if package_dir:
        package_dir = os.path.abspath(package_dir)
        sys.path.insert(0, package_dir)

    try:
        if setup_hooks:
            setup_hooks = [
                hook for hook in split_multiline(setup_hooks)
                if hook != 'pbr.hooks.setup_hook'
            ]
            for hook in setup_hooks:
                hook_fn = resolve_name(hook)
                try:
                    hook_fn(config)
                except SystemExit:
                    log.error('setup hook %s terminated the installation')
                except:
                    e = sys.exc_info()[1]
                    log.error('setup hook %s raised exception: %s\n' %
                              (hook, e))
                    log.error(traceback.format_exc())
                    sys.exit(1)

        # Run the pbr hook
        pbr.hooks.setup_hook(config)

        kwargs = setup_cfg_to_setup_kwargs(config, script_args)

        # Set default config overrides
        kwargs['include_package_data'] = True
        kwargs['zip_safe'] = False

        register_custom_compilers(config)

        ext_modules = get_extension_modules(config)
        if ext_modules:
            kwargs['ext_modules'] = ext_modules

        entry_points = get_entry_points(config)
        if entry_points:
            kwargs['entry_points'] = entry_points

        wrap_commands(kwargs)

        # Handle the [files]/extra_files option
        files_extra_files = has_get_option(config, 'files', 'extra_files')
        if files_extra_files:
            extra_files.set_extra_files(split_multiline(files_extra_files))

    finally:
        # Perform cleanup if any paths were added to sys.path
        if package_dir:
            sys.path.pop(0)

    return kwargs
Exemple #15
0
    def build_extension(self, ext: RustExtension, target_triple=None):
        executable = ext.binding == Binding.Exec

        rust_target_info = get_rust_target_info()

        # Make sure that if pythonXX-sys is used, it builds against the current
        # executing python interpreter.
        bindir = os.path.dirname(sys.executable)

        env = os.environ.copy()
        env.update({
            # disables rust's pkg-config seeking for specified packages,
            # which causes pythonXX-sys to fall back to detecting the
            # interpreter from the path.
            "PATH":
            os.path.join(bindir, os.environ.get("PATH", "")),
            "PYTHON_SYS_EXECUTABLE":
            os.environ.get("PYTHON_SYS_EXECUTABLE", sys.executable),
            "PYO3_PYTHON":
            os.environ.get("PYO3_PYTHON", sys.executable),
        })
        rustflags = ""

        target_triple = target_triple or self.get_target_triple()
        target_args = []
        if target_triple is not None:
            target_args = ["--target", target_triple]

        # Find where to put the temporary build files created by `cargo`
        metadata_command = [
            "cargo",
            "metadata",
            "--manifest-path",
            ext.path,
            "--format-version",
            "1",
        ]
        metadata = json.loads(check_output(metadata_command))
        target_dir = metadata["target_directory"]

        if not os.path.exists(ext.path):
            raise DistutilsFileError(
                f"can't find Rust extension project file: {ext.path}")

        features = set(ext.features)
        features.update(rust_features(binding=ext.binding))

        debug_build = ext.debug if ext.debug is not None else self.inplace
        debug_build = self.debug if self.debug is not None else debug_build
        if self.release:
            debug_build = False

        quiet = self.qbuild or ext.quiet

        # build cargo command
        feature_args = ["--features", " ".join(features)] if features else []

        if executable:
            args = (["cargo", "build", "--manifest-path", ext.path] +
                    feature_args + target_args + list(ext.args or []))
            if not debug_build:
                args.append("--release")
            if quiet:
                args.append("-q")
            elif self.verbose:
                args.append("--verbose")

        else:
            args = (["cargo", "rustc", "--lib", "--manifest-path", ext.path] +
                    feature_args + target_args + list(ext.args or []))
            if not debug_build:
                args.append("--release")
            if quiet:
                args.append("-q")
            elif self.verbose:
                args.append("--verbose")

            args.extend(["--", "--crate-type", "cdylib"])
            args.extend(ext.rustc_flags or [])

            # OSX requires special linker argument
            if sys.platform == "darwin":
                args.extend([
                    "-C", "link-arg=-undefined", "-C",
                    "link-arg=dynamic_lookup"
                ])
            # Tell musl targets not to statically link libc. See
            # https://github.com/rust-lang/rust/issues/59302 for details.
            if b'target_env="musl"' in rust_target_info:
                rustflags += " -C target-feature=-crt-static"

        if not quiet:
            print(" ".join(args), file=sys.stderr)

        if ext.native:
            rustflags += " -C target-cpu=native"

        if not executable and sys.platform == "darwin":
            ext_basename = os.path.basename(
                self.get_dylib_ext_path(ext, ext.name))
            rustflags += f" -C link-args=-Wl,-install_name,@rpath/{ext_basename}"

        if rustflags:
            env["RUSTFLAGS"] = (env.get("RUSTFLAGS", "") + " " +
                                rustflags).strip()

        # Execute cargo
        try:
            output = subprocess.check_output(args, env=env, encoding="latin-1")
        except subprocess.CalledProcessError as e:
            raise CompileError(
                f"cargo failed with code: {e.returncode}\n{e.output}")

        except OSError:
            raise DistutilsExecError(
                "Unable to execute 'cargo' - this package "
                "requires Rust to be installed and cargo to be on the PATH")

        if not quiet:
            if output:
                print(output, file=sys.stderr)

        # Find the shared library that cargo hopefully produced and copy
        # it into the build directory as if it were produced by build_ext.
        if debug_build:
            suffix = "debug"
        else:
            suffix = "release"

        # location of cargo compiled files
        artifactsdir = os.path.join(target_dir, target_triple or "", suffix)
        dylib_paths = []

        if executable:
            for name, dest in ext.target.items():
                if name:
                    path = os.path.join(artifactsdir, name)
                    if os.access(path, os.X_OK):
                        dylib_paths.append((dest, path))
                        continue
                    else:
                        raise DistutilsExecError(
                            "Rust build failed; "
                            f"unable to find executable '{name}' in '{target_dir}'"
                        )
                else:
                    # search executable
                    for name in os.listdir(artifactsdir):
                        path = os.path.join(artifactsdir, name)
                        if name.startswith(".") or not os.path.isfile(path):
                            continue

                        if os.access(path, os.X_OK):
                            dylib_paths.append((ext.name, path))
                            break

            if not dylib_paths:
                raise DistutilsExecError(
                    f"Rust build failed; unable to find executable in {target_dir}"
                )
        else:
            if sys.platform == "win32" or sys.platform == "cygwin":
                dylib_ext = "dll"
            elif sys.platform == "darwin":
                dylib_ext = "dylib"
            else:
                dylib_ext = "so"

            wildcard_so = "*{}.{}".format(ext.get_lib_name(), dylib_ext)

            try:
                dylib_paths.append((
                    ext.name,
                    next(glob.iglob(os.path.join(artifactsdir, wildcard_so))),
                ))
            except StopIteration:
                raise DistutilsExecError(
                    f"Rust build failed; unable to find any {wildcard_so} in {artifactsdir}"
                )
        return dylib_paths
Exemple #16
0
def copy_tree(src,
              dst,
              preserve_mode=1,
              preserve_times=1,
              preserve_symlinks=0,
              update=0,
              verbose=1,
              dry_run=0):
    """Copy an entire directory tree 'src' to a new location 'dst'.

    Both 'src' and 'dst' must be directory names.  If 'src' is not a
    directory, raise DistutilsFileError.  If 'dst' does not exist, it is
    created with 'mkpath()'.  The end result of the copy is that every
    file in 'src' is copied to 'dst', and directories under 'src' are
    recursively copied to 'dst'.  Return the list of files that were
    copied or might have been copied, using their output name.  The
    return value is unaffected by 'update' or 'dry_run': it is simply
    the list of all files under 'src', with the names changed to be
    under 'dst'.

    'preserve_mode' and 'preserve_times' are the same as for
    'copy_file'; note that they only apply to regular files, not to
    directories.  If 'preserve_symlinks' is true, symlinks will be
    copied as symlinks (on platforms that support them!); otherwise
    (the default), the destination of the symlink will be copied.
    'update' and 'verbose' are the same as for 'copy_file'.
    """
    from distutils.file_util import copy_file

    if not dry_run and not os.path.isdir(src):
        raise DistutilsFileError("cannot copy tree '%s': not a directory" %
                                 src)
    try:
        names = os.listdir(src)
    except OSError as e:
        if dry_run:
            names = []
        else:
            raise DistutilsFileError("error listing files in '%s': %s" %
                                     (src, e.strerror))

    ext_suffix = sysconfig.get_config_var('EXT_SUFFIX')
    new_suffix = "%s-%s%s" % (ext_suffix[:-3], _multiarch, ext_suffix[-3:])

    if not dry_run:
        mkpath(dst, verbose=verbose)

    outputs = []

    for n in names:
        src_name = os.path.join(src, n)
        dst_name = os.path.join(dst, n)
        if _multiarch and n.endswith(
                ext_suffix) and not n.endswith(new_suffix):
            dst_name = os.path.join(dst, n.replace(ext_suffix, new_suffix))
            log.info("renaming extension %s -> %s", n,
                     n.replace(ext_suffix, new_suffix))

        if n.startswith('.nfs'):
            # skip NFS rename files
            continue

        if preserve_symlinks and os.path.islink(src_name):
            link_dest = os.readlink(src_name)
            if verbose >= 1:
                log.info("linking %s -> %s", dst_name, link_dest)
            if not dry_run:
                os.symlink(link_dest, dst_name)
            outputs.append(dst_name)

        elif os.path.isdir(src_name):
            outputs.extend(
                copy_tree(src_name,
                          dst_name,
                          preserve_mode,
                          preserve_times,
                          preserve_symlinks,
                          update,
                          verbose=verbose,
                          dry_run=dry_run))
        else:
            copy_file(src_name,
                      dst_name,
                      preserve_mode,
                      preserve_times,
                      update,
                      verbose=verbose,
                      dry_run=dry_run)
            outputs.append(dst_name)

    return outputs
Exemple #17
0
def to_setup(path='setup.cfg'):
    """
    Reads given setup.cfg file and returns keyword arguments to setup().

    Also installs plugins and patches to distutils as needed to support some
    of the features supported by setup.cfg (pre/post-command hooks, etc.)
    """

    # The method source code really starts here.
    parser = RawConfigParser()
    if not os.path.exists(path):
        raise DistutilsFileError("file '%s' does not exist" %
                                 os.path.abspath(path))
    parser.read(path)
    config = {}
    for section in parser.sections():
        config[section] = dict(parser.items(section))

    # Run setup_hooks, if configured
    setup_hooks = has_get_option(config, 'global', 'setup_hooks')
    package_dir = has_get_option(config, 'files', 'packages_root')

    # Add the source package directory to sys.path in case it contains
    # additional hooks, and to make sure it's on the path before any existing
    # installations of the package
    if package_dir:
        package_dir = os.path.abspath(package_dir)
        sys.path.insert(0, package_dir)

    try:
        if setup_hooks:
            setup_hooks = split_multiline(setup_hooks)
            for hook in setup_hooks:
                hook_fn = resolve_name(hook)
                try:
                    hook_fn(config)
                except SystemExit:
                    log.error('setup hook %s terminated the installation')
                except:
                    e = sys.exc_info()[1]
                    log.error('setup hook %s raised exception: %s\n' %
                              (hook, e))
                    log.error(traceback.format_exc())
                    sys.exit(1)

        kwargs = process_config(config)

        register_custom_compilers(config)

        ext_modules = get_extension_modules(config)
        if ext_modules:
            kwargs['ext_modules'] = ext_modules

        entry_points = get_entry_points(config)
        if entry_points:
            kwargs['entry_points'] = entry_points

        wrap_commands(kwargs)

        # Handle the [files]/extra_files option
        extra_files = has_get_option(config, 'files', 'extra_files')
        if extra_files:
            extra_files = split_multiline(extra_files)
            # Let's do a sanity check
            for filename in extra_files:
                if not os.path.exists(filename):
                    raise DistutilsFileError(
                        '%s from the extra_files option in setup.cfg does not '
                        'exist' % filename)

            # Unfortunately the only really sensible way to do this is to
            # monkey-patch the manifest_maker class
            @monkeypatch_method(manifest_maker)
            def add_defaults(self, extra_files=extra_files, log=log):
                log.info('[setup.cfg] running patched manifest_maker command '
                         'with extra_files support')
                add_defaults._orig(self)
                self.filelist.extend(extra_files)

    finally:
        # Perform cleanup if any paths were added to sys.path
        if package_dir:
            sys.path.pop(0)

    return kwargs