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)
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
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)
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" %
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
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)
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
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
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
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
#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()'.
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')
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
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
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
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