def test_user_similar(self): # Issue 8759 : make sure the posix scheme for the users # is similar to the global posix_prefix one base = get_config_var('base') user = get_config_var('userbase') for name in ('stdlib', 'platstdlib', 'purelib', 'platlib'): global_path = get_path(name, 'posix_prefix') user_path = get_path(name, 'posix_user') self.assertEqual(user_path, global_path.replace(base, user))
def test_user_similar(self): # Issue #8759: make sure the posix scheme for the users # is similar to the global posix_prefix one base = get_config_var('base') user = get_config_var('userbase') # the global scheme mirrors the distinction between prefix and # exec-prefix but not the user scheme, so we have to adapt the paths # before comparing (issue #9100) adapt = sys.prefix != sys.exec_prefix for name in ('stdlib', 'platstdlib', 'purelib', 'platlib'): global_path = get_path(name, 'posix_prefix') if adapt: global_path = global_path.replace(sys.exec_prefix, sys.prefix) base = base.replace(sys.exec_prefix, sys.prefix) user_path = get_path(name, 'posix_user') self.assertEqual(user_path, global_path.replace(base, user, 1))
def test_user_site(self): # site.USER_SITE was introduced in 2.6 if sys.version < '2.6': return # preparing the environment for the test self.old_user_base = get_config_var('userbase') self.old_user_site = get_path('purelib', '%s_user' % os.name) self.tmpdir = self.mkdtemp() self.user_base = os.path.join(self.tmpdir, 'B') self.user_site = os.path.join(self.tmpdir, 'S') _CONFIG_VARS['userbase'] = self.user_base scheme = '%s_user' % os.name _SCHEMES.set(scheme, 'purelib', self.user_site) def _expanduser(path): if path[0] == '~': path = os.path.normpath(self.tmpdir) + path[1:] return path self.old_expand = os.path.expanduser os.path.expanduser = _expanduser try: # this is the actual test self._test_user_site() finally: _CONFIG_VARS['userbase'] = self.old_user_base _SCHEMES.set(scheme, 'purelib', self.old_user_site) os.path.expanduser = self.old_expand
def runtime_library_dir_option(self, dir): # XXX Hackish, at the very least. See Python bug #445902: # http://sourceforge.net/tracker/index.php # ?func=detail&aid=445902&group_id=5470&atid=105470 # Linkers on different platforms need different options to # specify that directories need to be added to the list of # directories searched for dependencies when a dynamic library # is sought. GCC on GNU systems (Linux, FreeBSD, ...) has to # be told to pass the -R option through to the linker, whereas # other compilers and gcc on other systems just know this. # Other compilers may need something slightly different. At # this time, there's no way to determine this information from # the configuration data stored in the Python installation, so # we use this hack. compiler = os.path.basename(sysconfig.get_config_var("CC")) if sys.platform[:6] == "darwin": # MacOSX's linker doesn't understand the -R flag at all return "-L" + dir elif sys.platform[:5] == "hp-ux": if self._is_gcc(compiler): return ["-Wl,+s", "-L" + dir] return ["+s", "-L" + dir] elif sys.platform[:7] == "irix646" or sys.platform[:6] == "osf1V5": return ["-rpath", dir] elif self._is_gcc(compiler): # gcc on non-GNU systems does not need -Wl, but can # use it anyway. Since distutils has always passed in # -Wl whenever gcc was used in the past it is probably # safest to keep doing so. if sysconfig.get_config_var("GNULD") == "yes": # GNU ld needs an extra option to get a RUNPATH # instead of just an RPATH. return "-Wl,--enable-new-dtags,-R" + dir else: return "-Wl,-R" + dir elif sys.platform[:3] == "aix": return "-blibpath:" + dir else: # No idea how --enable-new-dtags would be passed on to # ld if this system was using GNU ld. Don't know if a # system like this even exists. return "-R" + dir
def test_user_site(self): # test install with --user # preparing the environment for the test self.old_user_base = get_config_var('userbase') self.old_user_site = get_path('purelib', '%s_user' % os.name) self.tmpdir = self.mkdtemp() self.user_base = os.path.join(self.tmpdir, 'B') self.user_site = os.path.join(self.tmpdir, 'S') _CONFIG_VARS['userbase'] = self.user_base scheme = '%s_user' % os.name _SCHEMES.set(scheme, 'purelib', self.user_site) def _expanduser(path): if path[0] == '~': path = os.path.normpath(self.tmpdir) + path[1:] return path self.old_expand = os.path.expanduser os.path.expanduser = _expanduser def cleanup(): _CONFIG_VARS['userbase'] = self.old_user_base _SCHEMES.set(scheme, 'purelib', self.old_user_site) os.path.expanduser = self.old_expand self.addCleanup(cleanup) schemes = get_scheme_names() for key in ('nt_user', 'posix_user', 'os2_home'): self.assertIn(key, schemes) dist = Distribution({'name': 'xx'}) cmd = install_dist(dist) # making sure the user option is there options = [name for name, short, lable in cmd.user_options] self.assertIn('user', options) # setting a value cmd.user = True # user base and site shouldn't be created yet self.assertFalse(os.path.exists(self.user_base)) self.assertFalse(os.path.exists(self.user_site)) # let's run finalize cmd.ensure_finalized() # now they should self.assertTrue(os.path.exists(self.user_base)) self.assertTrue(os.path.exists(self.user_site)) self.assertIn('userbase', cmd.config_vars) self.assertIn('usersite', cmd.config_vars)
def fixup_build_ext(cmd): """Function needed to make build_ext tests pass. When Python was built with --enable-shared on Unix, -L. is not enough to find libpython<blah>.so, because regrtest runs in a tempdir, not in the source directory where the .so lives. (Mac OS X embeds absolute paths to shared libraries into executables, so the fixup is a no-op on that platform.) When Python was built with in debug mode on Windows, build_ext commands need their debug attribute set, and it is not done automatically for some reason. This function handles both of these things, and also fixes cmd.distribution.include_dirs if the running Python is an uninstalled build. Example use: cmd = build_ext(dist) support.fixup_build_ext(cmd) cmd.ensure_finalized() """ if os.name == 'nt': cmd.debug = sys.executable.endswith('_d.exe') elif sysconfig.get_config_var('Py_ENABLE_SHARED'): # To further add to the shared builds fun on Unix, we can't just add # library_dirs to the Extension() instance because that doesn't get # plumbed through to the final compiler command. runshared = sysconfig.get_config_var('RUNSHARED') if runshared is None: cmd.library_dirs = ['.'] else: if sys.platform == 'darwin': cmd.library_dirs = [] else: # FIXME no partition in 2.4 name, equals, value = runshared.partition('=') cmd.library_dirs = value.split(os.pathsep) # Allow tests to run with an uninstalled Python if sysconfig.is_python_build(): pysrcdir = sysconfig.get_config_var('projectbase') cmd.distribution.include_dirs.append(os.path.join(pysrcdir, 'Include'))
def test_deployment_target_higher_ok(self): # Issue 9516: Test that an extension module can be compiled with a # deployment target higher than that of the interpreter: the ext # module may depend on some newer OS feature. deptarget = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') if deptarget: # increment the minor version number (i.e. 10.6 -> 10.7) deptarget = [int(x) for x in deptarget.split('.')] deptarget[-1] += 1 deptarget = '.'.join(str(i) for i in deptarget) self._try_compile_deployment_target('<', deptarget)
def _try_compile_deployment_target(self, operator, target): orig_environ = os.environ os.environ = orig_environ.copy() self.addCleanup(setattr, os, 'environ', orig_environ) if target is None: if os.environ.get('MACOSX_DEPLOYMENT_TARGET'): del os.environ['MACOSX_DEPLOYMENT_TARGET'] else: os.environ['MACOSX_DEPLOYMENT_TARGET'] = target deptarget_c = os.path.join(self.tmp_dir, 'deptargetmodule.c') fp = open(deptarget_c, 'w') try: fp.write(textwrap.dedent('''\ #include <AvailabilityMacros.h> int dummy; #if TARGET %s MAC_OS_X_VERSION_MIN_REQUIRED #else #error "Unexpected target" #endif ''' % operator)) finally: fp.close() # get the deployment target that the interpreter was built with target = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') target = tuple(map(int, target.split('.'))) target = '%02d%01d0' % target deptarget_ext = Extension( 'deptarget', [deptarget_c], extra_compile_args=['-DTARGET=%s' % (target,)], ) dist = Distribution({ 'name': 'deptarget', 'ext_modules': [deptarget_ext], }) dist.package_dir = self.tmp_dir cmd = build_ext(dist) cmd.build_lib = self.tmp_dir cmd.build_temp = self.tmp_dir try: cmd.ensure_finalized() cmd.run() except CompileError: self.fail("Wrong deployment target during compilation")
def install(project): """Installs a project. Returns True on success, False on failure """ if is_python_build(): # Python would try to install into the site-packages directory under # $PREFIX, but when running from an uninstalled code checkout we don't # want to create directories under the installation root message = ('installing third-party projects from an uninstalled ' 'Python is not supported') logger.error(message) return False logger.info('Checking the installation location...') purelib_path = get_path('purelib') # trying to write a file there try: testfile = tempfile.NamedTemporaryFile(suffix=project, dir=purelib_path) try: testfile.write('test') finally: testfile.close() except OSError: # FIXME this should check the errno, or be removed altogether (race # condition: the directory permissions could be changed between here # and the actual install) logger.info('Unable to write in "%s". Do you have the permissions ?' % purelib_path) return False logger.info('Getting information about %r...', project) try: info = get_infos(project) except InstallationException: logger.info('Cound not find %r', project) return False if info['install'] == []: logger.info('Nothing to install') return False install_path = get_config_var('base') try: install_from_infos(install_path, info['install'], info['remove'], info['conflict']) except InstallationConflict, e: if logger.isEnabledFor(logging.INFO): projects = ('%r %s' % (p.name, p.version) for p in e.args[0]) logger.info('%r conflicts with %s', project, ','.join(projects))
def install(project): """Installs a project. Returns True on success, False on failure """ if is_python_build(): # Python would try to install into the site-packages directory under # $PREFIX, but when running from an uninstalled code checkout we don't # want to create directories under the installation root message = ('installing third-party projects from an uninstalled ' 'Python is not supported') logger.error(message) return False logger.info('Checking the installation location...') purelib_path = get_path('purelib') # trying to write a file there try: with tempfile.NamedTemporaryFile(suffix=project, dir=purelib_path) as testfile: testfile.write(b'test') except OSError: # FIXME this should check the errno, or be removed altogether (race # condition: the directory permissions could be changed between here # and the actual install) logger.info('Unable to write in "%s". Do you have the permissions ?' % purelib_path) return False logger.info('Getting information about %r...', project) try: info = get_infos(project) except InstallationException: logger.info('Cound not find %r', project) return False if info['install'] == []: logger.info('Nothing to install') return False install_path = get_config_var('base') try: install_from_infos(install_path, info['install'], info['remove'], info['conflict']) except InstallationConflict as e: if logger.isEnabledFor(logging.INFO): projects = ('%r %s' % (p.name, p.version) for p in e.args[0]) logger.info('%r conflicts with %s', project, ','.join(projects)) return True
def get_ext_filename(self, ext_name): r"""Convert the name of an extension (eg. "foo.bar") into the name of the file from which it will be loaded (eg. "foo/bar.so", or "foo\bar.pyd"). """ ext_path = ext_name.split('.') # OS/2 has an 8 character module (extension) limit :-( if os.name == "os2": ext_path[len(ext_path) - 1] = ext_path[len(ext_path) - 1][:8] # extensions in debug_mode are named 'module_d.pyd' under windows so_ext = sysconfig.get_config_var('SO') if os.name == 'nt' and self.debug: return os.path.join(*ext_path) + '_d' + so_ext return os.path.join(*ext_path) + so_ext
def _try_compile_deployment_target(self, operator, target): orig_environ = os.environ os.environ = orig_environ.copy() self.addCleanup(setattr, os, 'environ', orig_environ) if target is None: if os.environ.get('MACOSX_DEPLOYMENT_TARGET'): del os.environ['MACOSX_DEPLOYMENT_TARGET'] else: os.environ['MACOSX_DEPLOYMENT_TARGET'] = target deptarget_c = os.path.join(self.tmp_dir, 'deptargetmodule.c') with open(deptarget_c, 'w') as fp: fp.write(textwrap.dedent('''\ #include <AvailabilityMacros.h> int dummy; #if TARGET %s MAC_OS_X_VERSION_MIN_REQUIRED #else #error "Unexpected target" #endif ''' % operator)) # get the deployment target that the interpreter was built with target = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') target = tuple(map(int, target.split('.'))) target = '%02d%01d0' % target deptarget_ext = Extension( 'deptarget', [deptarget_c], extra_compile_args=['-DTARGET=%s' % (target,)], ) dist = Distribution({ 'name': 'deptarget', 'ext_modules': [deptarget_ext], }) dist.package_dir = self.tmp_dir cmd = build_ext(dist) cmd.build_lib = self.tmp_dir cmd.build_temp = self.tmp_dir try: cmd.ensure_finalized() cmd.run() except CompileError: self.fail("Wrong deployment target during compilation")
def test_solaris_enable_shared(self): dist = Distribution({'name': 'xx'}) cmd = build_ext(dist) old = sys.platform sys.platform = 'sunos' # fooling finalize_options old_var = sysconfig.get_config_var('Py_ENABLE_SHARED') sysconfig._CONFIG_VARS['Py_ENABLE_SHARED'] = 1 try: cmd.ensure_finalized() finally: sys.platform = old if old_var is None: del sysconfig._CONFIG_VARS['Py_ENABLE_SHARED'] else: sysconfig._CONFIG_VARS['Py_ENABLE_SHARED'] = old_var # make sure we get some library dirs under solaris self.assertGreater(len(cmd.library_dirs), 0)
def finalize_options(self): self.set_undefined_options('build', 'build_lib', 'build_temp', 'compiler', 'debug', 'force', 'plat_name') if self.package is None: self.package = self.distribution.ext_package # Ensure that the list of extensions is valid, i.e. it is a list of # Extension objects. self.extensions = self.distribution.ext_modules if self.extensions: if not isinstance(self.extensions, (list, tuple)): type_name = (self.extensions is None and 'None' or type(self.extensions).__name__) raise PackagingSetupError( "'ext_modules' must be a sequence of Extension instances," " not %s" % (type_name,)) for i, ext in enumerate(self.extensions): if isinstance(ext, Extension): continue # OK! (assume type-checking done # by Extension constructor) type_name = (ext is None and 'None' or type(ext).__name__) raise PackagingSetupError( "'ext_modules' item %d must be an Extension instance," " not %s" % (i, type_name)) # Make sure Python's include directories (for Python.h, pyconfig.h, # etc.) are in the include search path. py_include = sysconfig.get_path('include') plat_py_include = sysconfig.get_path('platinclude') if self.include_dirs is None: self.include_dirs = self.distribution.include_dirs or [] if isinstance(self.include_dirs, basestring): self.include_dirs = self.include_dirs.split(os.pathsep) # Put the Python "system" include dir at the end, so that # any local include dirs take precedence. self.include_dirs.append(py_include) if plat_py_include != py_include: self.include_dirs.append(plat_py_include) self.ensure_string_list('libraries') # Life is easier if we're not forever checking for None, so # simplify these options to empty lists if unset if self.libraries is None: self.libraries = [] if self.library_dirs is None: self.library_dirs = [] elif isinstance(self.library_dirs, basestring): self.library_dirs = self.library_dirs.split(os.pathsep) if self.rpath is None: self.rpath = [] elif isinstance(self.rpath, basestring): self.rpath = self.rpath.split(os.pathsep) # for extensions under windows use different directories # for Release and Debug builds. # also Python's library directory must be appended to library_dirs if os.name == 'nt': # the 'libs' directory is for binary installs - we assume that # must be the *native* platform. But we don't really support # cross-compiling via a binary install anyway, so we let it go. self.library_dirs.append(os.path.join(sys.exec_prefix, 'libs')) if self.debug: self.build_temp = os.path.join(self.build_temp, "Debug") else: self.build_temp = os.path.join(self.build_temp, "Release") # Append the source distribution include and library directories, # this allows distutils on windows to work in the source tree self.include_dirs.append(os.path.join(sys.exec_prefix, 'PC')) if MSVC_VERSION == 9: # Use the .lib files for the correct architecture if self.plat_name == 'win32': suffix = '' else: # win-amd64 or win-ia64 suffix = self.plat_name[4:] new_lib = os.path.join(sys.exec_prefix, 'PCbuild') if suffix: new_lib = os.path.join(new_lib, suffix) self.library_dirs.append(new_lib) elif MSVC_VERSION == 8: self.library_dirs.append(os.path.join(sys.exec_prefix, 'PC', 'VS8.0')) elif MSVC_VERSION == 7: self.library_dirs.append(os.path.join(sys.exec_prefix, 'PC', 'VS7.1')) else: self.library_dirs.append(os.path.join(sys.exec_prefix, 'PC', 'VC6')) # OS/2 (EMX) doesn't support Debug vs Release builds, but has the # import libraries in its "Config" subdirectory if os.name == 'os2': self.library_dirs.append(os.path.join(sys.exec_prefix, 'Config')) # for extensions under Cygwin and AtheOS Python's library directory must be # appended to library_dirs if sys.platform[:6] == 'cygwin' or sys.platform[:6] == 'atheos': if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): # building third party extensions self.library_dirs.append(os.path.join(sys.prefix, "lib", "python" + sysconfig.get_python_version(), "config")) else: # building python standard extensions self.library_dirs.append(os.curdir) # for extensions under Linux or Solaris with a shared Python library, # Python's library directory must be appended to library_dirs sysconfig.get_config_var('Py_ENABLE_SHARED') if ((sys.platform.startswith('linux') or sys.platform.startswith('gnu') or sys.platform.startswith('sunos')) and sysconfig.get_config_var('Py_ENABLE_SHARED')): if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): # building third party extensions self.library_dirs.append(sysconfig.get_config_var('LIBDIR')) else: # building python standard extensions self.library_dirs.append(os.curdir) # The argument parsing will result in self.define being a string, but # it has to be a list of 2-tuples. All the preprocessor symbols # specified by the 'define' option will be set to '1'. Multiple # symbols can be separated with commas. if self.define: defines = self.define.split(',') self.define = [(symbol, '1') for symbol in defines] # The option for macros to undefine is also a string from the # option parsing, but has to be a list. Multiple symbols can also # be separated with commas here. if self.undef: self.undef = self.undef.split(',') if self.swig_opts is None: self.swig_opts = [] else: self.swig_opts = self.swig_opts.split(' ') # Finally add the user include and library directories if requested if HAS_USER_SITE and self.user: user_include = os.path.join(site.USER_BASE, "include") user_lib = os.path.join(site.USER_BASE, "lib") if os.path.isdir(user_include): self.include_dirs.append(user_include) if os.path.isdir(user_lib): self.library_dirs.append(user_lib) self.rpath.append(user_lib)
def get_libraries(self, ext): """Return the list of libraries to link against when building a shared extension. On most platforms, this is just 'ext.libraries'; on Windows and OS/2, we add the Python library (eg. python20.dll). """ # The python library is always needed on Windows. For MSVC, this # is redundant, since the library is mentioned in a pragma in # pyconfig.h that MSVC groks. The other Windows compilers all seem # to need it mentioned explicitly, though, so that's what we do. # Append '_d' to the python import library on debug builds. if sys.platform == "win32": from distutils2.compiler.msvccompiler import MSVCCompiler if not isinstance(self.compiler_obj, MSVCCompiler): template = "python%d%d" if self.debug: template = template + '_d' pythonlib = template % sys.version_info[:2] # don't extend ext.libraries, it may be shared with other # extensions, it is a reference to the original list return ext.libraries + [pythonlib] else: return ext.libraries elif sys.platform == "os2emx": # EMX/GCC requires the python library explicitly, and I # believe VACPP does as well (though not confirmed) - AIM Apr01 template = "python%d%d" # debug versions of the main DLL aren't supported, at least # not at this time - AIM Apr01 #if self.debug: # template = template + '_d' pythonlib = template % sys.version_info[:2] # don't extend ext.libraries, it may be shared with other # extensions, it is a reference to the original list return ext.libraries + [pythonlib] elif sys.platform[:6] == "cygwin": template = "python%d.%d" pythonlib = template % sys.version_info[:2] # don't extend ext.libraries, it may be shared with other # extensions, it is a reference to the original list return ext.libraries + [pythonlib] elif sys.platform[:6] == "atheos": template = "python%d.%d" pythonlib = template % sys.version_info[:2] # Get SHLIBS from Makefile extra = [] for lib in sysconfig.get_config_var('SHLIBS').split(): if lib.startswith('-l'): extra.append(lib[2:]) else: extra.append(lib) # don't extend ext.libraries, it may be shared with other # extensions, it is a reference to the original list return ext.libraries + [pythonlib, "m"] + extra elif sys.platform == 'darwin': # Don't use the default code below return ext.libraries else: if sysconfig.get_config_var('Py_ENABLE_SHARED'): template = 'python%d.%d' + getattr(sys, 'abiflags', '') pythonlib = template % sys.version_info[:2] return ext.libraries + [pythonlib] else: return ext.libraries
def finalize_options(self): self.set_undefined_options('build', 'build_lib', 'build_temp', 'compiler', 'debug', 'force', 'plat_name') if self.package is None: self.package = self.distribution.ext_package # Ensure that the list of extensions is valid, i.e. it is a list of # Extension objects. self.extensions = self.distribution.ext_modules if self.extensions: if not isinstance(self.extensions, (list, tuple)): type_name = (self.extensions is None and 'None' or type(self.extensions).__name__) raise PackagingSetupError( "'ext_modules' must be a sequence of Extension instances," " not %s" % (type_name, )) for i, ext in enumerate(self.extensions): if isinstance(ext, Extension): continue # OK! (assume type-checking done # by Extension constructor) type_name = (ext is None and 'None' or type(ext).__name__) raise PackagingSetupError( "'ext_modules' item %d must be an Extension instance," " not %s" % (i, type_name)) # Make sure Python's include directories (for Python.h, pyconfig.h, # etc.) are in the include search path. py_include = sysconfig.get_path('include') plat_py_include = sysconfig.get_path('platinclude') if self.include_dirs is None: self.include_dirs = self.distribution.include_dirs or [] if isinstance(self.include_dirs, str): self.include_dirs = self.include_dirs.split(os.pathsep) # Put the Python "system" include dir at the end, so that # any local include dirs take precedence. self.include_dirs.append(py_include) if plat_py_include != py_include: self.include_dirs.append(plat_py_include) self.ensure_string_list('libraries') # Life is easier if we're not forever checking for None, so # simplify these options to empty lists if unset if self.libraries is None: self.libraries = [] if self.library_dirs is None: self.library_dirs = [] elif isinstance(self.library_dirs, str): self.library_dirs = self.library_dirs.split(os.pathsep) if self.rpath is None: self.rpath = [] elif isinstance(self.rpath, str): self.rpath = self.rpath.split(os.pathsep) # for extensions under windows use different directories # for Release and Debug builds. # also Python's library directory must be appended to library_dirs if os.name == 'nt': # the 'libs' directory is for binary installs - we assume that # must be the *native* platform. But we don't really support # cross-compiling via a binary install anyway, so we let it go. self.library_dirs.append(os.path.join(sys.exec_prefix, 'libs')) if self.debug: self.build_temp = os.path.join(self.build_temp, "Debug") else: self.build_temp = os.path.join(self.build_temp, "Release") # Append the source distribution include and library directories, # this allows distutils on windows to work in the source tree self.include_dirs.append(os.path.join(sys.exec_prefix, 'PC')) if MSVC_VERSION == 9: # Use the .lib files for the correct architecture if self.plat_name == 'win32': suffix = '' else: # win-amd64 or win-ia64 suffix = self.plat_name[4:] new_lib = os.path.join(sys.exec_prefix, 'PCbuild') if suffix: new_lib = os.path.join(new_lib, suffix) self.library_dirs.append(new_lib) elif MSVC_VERSION == 8: self.library_dirs.append( os.path.join(sys.exec_prefix, 'PC', 'VS8.0')) elif MSVC_VERSION == 7: self.library_dirs.append( os.path.join(sys.exec_prefix, 'PC', 'VS7.1')) else: self.library_dirs.append( os.path.join(sys.exec_prefix, 'PC', 'VC6')) # OS/2 (EMX) doesn't support Debug vs Release builds, but has the # import libraries in its "Config" subdirectory if os.name == 'os2': self.library_dirs.append(os.path.join(sys.exec_prefix, 'Config')) # for extensions under Cygwin and AtheOS Python's library directory must be # appended to library_dirs if sys.platform[:6] == 'cygwin' or sys.platform[:6] == 'atheos': if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): # building third party extensions self.library_dirs.append( os.path.join(sys.prefix, "lib", "python" + sysconfig.get_python_version(), "config")) else: # building python standard extensions self.library_dirs.append(os.curdir) # for extensions under Linux or Solaris with a shared Python library, # Python's library directory must be appended to library_dirs sysconfig.get_config_var('Py_ENABLE_SHARED') if (sys.platform.startswith(('linux', 'gnu', 'sunos')) and sysconfig.get_config_var('Py_ENABLE_SHARED')): if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): # building third party extensions self.library_dirs.append(sysconfig.get_config_var('LIBDIR')) else: # building python standard extensions self.library_dirs.append(os.curdir) # The argument parsing will result in self.define being a string, but # it has to be a list of 2-tuples. All the preprocessor symbols # specified by the 'define' option will be set to '1'. Multiple # symbols can be separated with commas. if self.define: defines = self.define.split(',') self.define = [(symbol, '1') for symbol in defines] # The option for macros to undefine is also a string from the # option parsing, but has to be a list. Multiple symbols can also # be separated with commas here. if self.undef: self.undef = self.undef.split(',') if self.swig_opts is None: self.swig_opts = [] else: self.swig_opts = self.swig_opts.split(' ') # Finally add the user include and library directories if requested if self.user: user_include = os.path.join(site.USER_BASE, "include") user_lib = os.path.join(site.USER_BASE, "lib") if os.path.isdir(user_include): self.include_dirs.append(user_include) if os.path.isdir(user_lib): self.library_dirs.append(user_lib) self.rpath.append(user_lib)
def initialize_options(self): # High-level options: these select both an installation base # and scheme. self.prefix = None self.exec_prefix = None self.home = None self.user = False # These select only the installation base; it's up to the user to # specify the installation scheme (currently, that means supplying # the --install-{platlib,purelib,scripts,data} options). self.install_base = None self.install_platbase = None self.root = None # These options are the actual installation directories; if not # supplied by the user, they are filled in using the installation # scheme implied by prefix/exec-prefix/home and the contents of # that installation scheme. self.install_purelib = None # for pure module distributions self.install_platlib = None # non-pure (dists w/ extensions) self.install_headers = None # for C/C++ headers self.install_lib = None # set to either purelib or platlib self.install_scripts = None self.install_data = None self.install_userbase = get_config_var('userbase') self.install_usersite = get_path('purelib', '%s_user' % os.name) self.compile = None self.optimize = None # These two are for putting non-packagized distributions into their # own directory and creating a .pth file if it makes sense. # 'extra_path' comes from the setup file; 'install_path_file' can # be turned off if it makes no sense to install a .pth file. (But # better to install it uselessly than to guess wrong and not # install it when it's necessary and would be used!) Currently, # 'install_path_file' is always true unless some outsider meddles # with it. self.extra_path = None self.install_path_file = True # 'force' forces installation, even if target files are not # out-of-date. 'skip_build' skips running the "build" command, # handy if you know it's not necessary. 'warn_dir' (which is *not* # a user option, it's just there so the bdist_* commands can turn # it off) determines whether we warn about installing to a # directory not in sys.path. self.force = False self.skip_build = False self.warn_dir = True # These are only here as a conduit from the 'build' command to the # 'install_*' commands that do the real work. ('build_base' isn't # actually used anywhere, but it might be useful in future.) They # are not user options, because if the user told the install # command where the build directory is, that wouldn't affect the # build command. self.build_base = None self.build_lib = None # Not defined yet because we don't know anything about # documentation yet. #self.install_man = None #self.install_html = None #self.install_info = None self.record = None self.resources = None # .dist-info related options self.no_distinfo = None self.installer = None self.requested = None self.no_record = None
def _make_ext_name(modname): if os.name == 'nt' and sys.executable.endswith('_d.exe'): modname += '_d' return modname + get_config_var('SO')
def test_get_outputs(self): tmp_dir = self.mkdtemp() c_file = os.path.join(tmp_dir, 'foo.c') self.write_file(c_file, 'void PyInit_foo(void) {}\n') ext = Extension('foo', [c_file], optional=False) dist = Distribution({'name': 'xx', 'ext_modules': [ext]}) cmd = build_ext(dist) support.fixup_build_ext(cmd) cmd.ensure_finalized() self.assertEqual(len(cmd.get_outputs()), 1) cmd.build_lib = os.path.join(self.tmp_dir, 'build') cmd.build_temp = os.path.join(self.tmp_dir, 'tempt') # issue #5977 : distutils build_ext.get_outputs # returns wrong result with --inplace other_tmp_dir = os.path.realpath(self.mkdtemp()) old_wd = os.getcwd() os.chdir(other_tmp_dir) try: cmd.inplace = True cmd.run() so_file = cmd.get_outputs()[0] finally: os.chdir(old_wd) self.assertTrue(os.path.exists(so_file)) so_ext = sysconfig.get_config_var('SO') self.assertTrue(so_file.endswith(so_ext)) so_dir = os.path.dirname(so_file) self.assertEqual(so_dir, other_tmp_dir) cmd.inplace = False cmd.run() so_file = cmd.get_outputs()[0] self.assertTrue(os.path.exists(so_file)) self.assertTrue(so_file.endswith(so_ext)) so_dir = os.path.dirname(so_file) self.assertEqual(so_dir, cmd.build_lib) # inplace = False, cmd.package = 'bar' build_py = cmd.get_finalized_command('build_py') build_py.package_dir = 'bar' path = cmd.get_ext_fullpath('foo') # checking that the last directory is the build_dir path = os.path.split(path)[0] self.assertEqual(path, cmd.build_lib) # inplace = True, cmd.package = 'bar' cmd.inplace = True other_tmp_dir = os.path.realpath(self.mkdtemp()) old_wd = os.getcwd() os.chdir(other_tmp_dir) try: path = cmd.get_ext_fullpath('foo') finally: os.chdir(old_wd) # checking that the last directory is bar path = os.path.split(path)[0] lastdir = os.path.split(path)[-1] self.assertEqual(lastdir, 'bar')
def copy_scripts(self): """Copy each script listed in 'self.scripts'; if it's marked as a Python script in the Unix way (first line matches 'first_line_re', ie. starts with "\#!" and contains "python"), then adjust the first line to refer to the current Python interpreter as we copy. """ # XXX use self.execute(shutil.rmtree, ...) self.rmpath(self.build_dir) self.mkpath(self.build_dir) outfiles = [] for script in self.scripts: adjust = False script = convert_path(script) outfile = os.path.join(self.build_dir, os.path.basename(script)) outfiles.append(outfile) # Always open the file, but ignore failures in dry-run mode -- # that way, we'll get accurate feedback if we can read the # script. try: f = open(script, "rb") except IOError: if not self.dry_run: raise f = None else: encoding, lines = detect_encoding(f.readline) f.seek(0) first_line = f.readline() if not first_line: logger.warning('%s: %s is an empty file (skipping)', self.get_command_name(), script) continue match = first_line_re.match(first_line) if match: adjust = True post_interp = match.group(1) or '' if adjust: logger.info("copying and adjusting %s -> %s", script, self.build_dir) if not self.dry_run: if not sysconfig.is_python_build(): executable = self.executable else: executable = os.path.join( sysconfig.get_config_var("BINDIR"), "python%s%s" % (sysconfig.get_config_var("VERSION"), sysconfig.get_config_var("EXE"))) executable = fsencode(executable) shebang = "#!" + executable + post_interp + "\n" # Python parser starts to read a script using UTF-8 until # it gets a #coding:xxx cookie. The shebang has to be the # first line of a file, the #coding:xxx cookie cannot be # written before. So the shebang has to be decodable from # UTF-8. try: shebang.decode('utf-8') except UnicodeDecodeError: raise ValueError( "The shebang (%r) is not decodable " "from utf-8" % shebang) # If the script is encoded to a custom encoding (use a # #coding:xxx cookie), the shebang has to be decodable from # the script encoding too. try: shebang.decode(encoding) except UnicodeDecodeError: raise ValueError( "The shebang (%r) is not decodable " "from the script encoding (%s)" % ( shebang, encoding)) outf = open(outfile, "wb") try: outf.write(shebang) outf.writelines(f.readlines()) finally: outf.close() if f: f.close() else: if f: f.close() self.copy_file(script, outfile) if os.name == 'posix': for file in outfiles: if self.dry_run: logger.info("changing mode of %s", file) else: oldmode = os.stat(file).st_mode & 07777 newmode = (oldmode | 0555) & 07777 if newmode != oldmode: logger.info("changing mode of %s from %o to %o", file, oldmode, newmode) os.chmod(file, newmode) return outfiles
def copy_scripts(self): """Copy each script listed in 'self.scripts'; if it's marked as a Python script in the Unix way (first line matches 'first_line_re', ie. starts with "\#!" and contains "python"), then adjust the first line to refer to the current Python interpreter as we copy. """ # XXX use self.execute(shutil.rmtree, ...) self.rmpath(self.build_dir) self.mkpath(self.build_dir) outfiles = [] for script in self.scripts: adjust = False script = convert_path(script) outfile = os.path.join(self.build_dir, os.path.basename(script)) outfiles.append(outfile) # Always open the file, but ignore failures in dry-run mode -- # that way, we'll get accurate feedback if we can read the # script. try: f = open(script, "rb") except IOError: if not self.dry_run: raise f = None else: encoding, lines = detect_encoding(f.readline) f.seek(0) first_line = f.readline() if not first_line: logger.warning('%s: %s is an empty file (skipping)', self.get_command_name(), script) continue match = first_line_re.match(first_line) if match: adjust = True post_interp = match.group(1) or b'' if adjust: logger.info("copying and adjusting %s -> %s", script, self.build_dir) if not self.dry_run: if not sysconfig.is_python_build(): executable = self.executable else: executable = os.path.join( sysconfig.get_config_var("BINDIR"), "python%s%s" % (sysconfig.get_config_var("VERSION"), sysconfig.get_config_var("EXE"))) executable = fsencode(executable) shebang = b"#!" + executable + post_interp + b"\n" # Python parser starts to read a script using UTF-8 until # it gets a #coding:xxx cookie. The shebang has to be the # first line of a file, the #coding:xxx cookie cannot be # written before. So the shebang has to be decodable from # UTF-8. try: shebang.decode('utf-8') except UnicodeDecodeError: raise ValueError("The shebang (%r) is not decodable " "from utf-8" % shebang) # If the script is encoded to a custom encoding (use a # #coding:xxx cookie), the shebang has to be decodable from # the script encoding too. try: shebang.decode(encoding) except UnicodeDecodeError: raise ValueError("The shebang (%r) is not decodable " "from the script encoding (%s)" % (shebang, encoding)) with open(outfile, "wb") as outf: outf.write(shebang) outf.writelines(f.readlines()) if f: f.close() else: if f: f.close() self.copy_file(script, outfile) if os.name == 'posix': for file in outfiles: if self.dry_run: logger.info("changing mode of %s", file) else: oldmode = os.stat(file).st_mode & 0o7777 newmode = (oldmode | 0o555) & 0o7777 if newmode != oldmode: logger.info("changing mode of %s from %o to %o", file, oldmode, newmode) os.chmod(file, newmode) return outfiles
def get_libraries(self, ext): """Return the list of libraries to link against when building a shared extension. On most platforms, this is just 'ext.libraries'; on Windows and OS/2, we add the Python library (eg. python20.dll). """ # The python library is always needed on Windows. For MSVC, this # is redundant, since the library is mentioned in a pragma in # pyconfig.h that MSVC groks. The other Windows compilers all seem # to need it mentioned explicitly, though, so that's what we do. # Append '_d' to the python import library on debug builds. if sys.platform == "win32": from distutils2.compiler.msvccompiler import MSVCCompiler if not isinstance(self.compiler_obj, MSVCCompiler): template = "python%d%d" if self.debug: template = template + '_d' pythonlib = template % sys.version_info[:2] # don't extend ext.libraries, it may be shared with other # extensions, it is a reference to the original list return ext.libraries + [pythonlib] else: return ext.libraries elif sys.platform == "os2emx": # EMX/GCC requires the python library explicitly, and I # believe VACPP does as well (though not confirmed) - AIM Apr01 template = "python%d%d" # debug versions of the main DLL aren't supported, at least # not at this time - AIM Apr01 #if self.debug: # template = template + '_d' pythonlib = template % sys.version_info[:2] # don't extend ext.libraries, it may be shared with other # extensions, it is a reference to the original list return ext.libraries + [pythonlib] elif sys.platform[:6] == "cygwin": template = "python%d.%d" pythonlib = template % sys.version_info[:2] # don't extend ext.libraries, it may be shared with other # extensions, it is a reference to the original list return ext.libraries + [pythonlib] elif sys.platform[:6] == "atheos": template = "python%d.%d" pythonlib = template % sys.version_info[:2] # Get SHLIBS from Makefile extra = [] for lib in sysconfig.get_config_var('SHLIBS').split(): if lib.startswith('-l'): extra.append(lib[2:]) else: extra.append(lib) # don't extend ext.libraries, it may be shared with other # extensions, it is a reference to the original list return ext.libraries + [pythonlib, "m"] + extra elif sys.platform == 'darwin': # Don't use the default code below return ext.libraries else: if sysconfig.get_config_var('Py_ENABLE_SHARED'): template = 'python%d.%d' pythonlib = template % sys.version_info[:2] return ext.libraries + [pythonlib] else: return ext.libraries
def test_ldshared_value(self): ldflags = sysconfig.get_config_var('LDFLAGS') ldshared = sysconfig.get_config_var('LDSHARED') self.assertIn(ldflags, ldshared)
def initialize_options(self): # High-level options: these select both an installation base # and scheme. self.prefix = None self.exec_prefix = None self.home = None if HAS_USER_SITE: self.user = False # These select only the installation base; it's up to the user to # specify the installation scheme (currently, that means supplying # the --install-{platlib,purelib,scripts,data} options). self.install_base = None self.install_platbase = None self.root = None # These options are the actual installation directories; if not # supplied by the user, they are filled in using the installation # scheme implied by prefix/exec-prefix/home and the contents of # that installation scheme. self.install_purelib = None # for pure module distributions self.install_platlib = None # non-pure (dists w/ extensions) self.install_headers = None # for C/C++ headers self.install_lib = None # set to either purelib or platlib self.install_scripts = None self.install_data = None if HAS_USER_SITE: self.install_userbase = get_config_var('userbase') self.install_usersite = get_path('purelib', '%s_user' % os.name) self.compile = None self.optimize = None # These two are for putting non-packagized distributions into their # own directory and creating a .pth file if it makes sense. # 'extra_path' comes from the setup file; 'install_path_file' can # be turned off if it makes no sense to install a .pth file. (But # better to install it uselessly than to guess wrong and not # install it when it's necessary and would be used!) Currently, # 'install_path_file' is always true unless some outsider meddles # with it. self.extra_path = None self.install_path_file = True # 'force' forces installation, even if target files are not # out-of-date. 'skip_build' skips running the "build" command, # handy if you know it's not necessary. 'warn_dir' (which is *not* # a user option, it's just there so the bdist_* commands can turn # it off) determines whether we warn about installing to a # directory not in sys.path. self.force = False self.skip_build = False self.warn_dir = True # These are only here as a conduit from the 'build' command to the # 'install_*' commands that do the real work. ('build_base' isn't # actually used anywhere, but it might be useful in future.) They # are not user options, because if the user told the install # command where the build directory is, that wouldn't affect the # build command. self.build_base = None self.build_lib = None # Not defined yet because we don't know anything about # documentation yet. #self.install_man = None #self.install_html = None #self.install_info = None self.record = None self.resources = None # .dist-info related options self.no_distinfo = None self.installer = None self.requested = None self.no_record = None
def test_record_basic(self): install_dir = self.mkdtemp() modules_dest = os.path.join(install_dir, 'lib') scripts_dest = os.path.join(install_dir, 'bin') project_dir, dist = self.create_dist( name='Spamlib', version='0.1', py_modules=['spam'], scripts=['spamd'], ext_modules=[Extension('_speedspam', ['_speedspam.c'])]) # using a real install_dist command is too painful, so we use a mock # class that's only a holder for options to be used by install_distinfo # and we create placeholder files manually instead of using build_*. # the install_* commands will still be consulted by install_distinfo. os.chdir(project_dir) self.write_file('spam', '# Python module') self.write_file('spamd', '# Python script') extmod = '_speedspam' + sysconfig.get_config_var('SO') self.write_file(extmod, '') install = DummyInstallCmd(dist) install.outputs = ['spam', 'spamd', extmod] install.install_lib = modules_dest install.install_scripts = scripts_dest dist.command_obj['install_dist'] = install cmd = install_distinfo(dist) cmd.ensure_finalized() dist.command_obj['install_distinfo'] = cmd cmd.run() # checksum and size are not hard-coded for METADATA as it is # platform-dependent (line endings) metadata = os.path.join(modules_dest, 'Spamlib-0.1.dist-info', 'METADATA') with open(metadata, 'rb') as fp: content = fp.read() metadata_size = str(len(content)) metadata_md5 = hashlib.md5(content).hexdigest() record = os.path.join(modules_dest, 'Spamlib-0.1.dist-info', 'RECORD') with open(record, encoding='utf-8') as fp: content = fp.read() found = [] for line in content.splitlines(): filename, checksum, size = line.split(',') filename = os.path.basename(filename) found.append((filename, checksum, size)) expected = [ ('spam', '6ab2f288ef2545868effe68757448b45', '15'), ('spamd', 'd13e6156ce78919a981e424b2fdcd974', '15'), (extmod, 'd41d8cd98f00b204e9800998ecf8427e', '0'), ('METADATA', metadata_md5, metadata_size), ('INSTALLER', '44e3fde05f3f537ed85831969acf396d', '9'), ('REQUESTED', 'd41d8cd98f00b204e9800998ecf8427e', '0'), ('RECORD', '', ''), ] self.assertEqual(found, expected)
def test_record_basic(self): install_dir = self.mkdtemp() modules_dest = os.path.join(install_dir, 'lib') scripts_dest = os.path.join(install_dir, 'bin') project_dir, dist = self.create_dist( name='Spamlib', version='0.1', py_modules=['spam'], scripts=['spamd'], ext_modules=[Extension('_speedspam', ['_speedspam.c'])]) # using a real install_dist command is too painful, so we use a mock # class that's only a holder for options to be used by install_distinfo # and we create placeholder files manually instead of using build_*. # the install_* commands will still be consulted by install_distinfo. os.chdir(project_dir) self.write_file('spam', '# Python module') self.write_file('spamd', '# Python script') extmod = '_speedspam' + sysconfig.get_config_var('SO') self.write_file(extmod, '') install = DummyInstallCmd(dist) install.outputs = ['spam', 'spamd', extmod] install.install_lib = modules_dest install.install_scripts = scripts_dest dist.command_obj['install_dist'] = install cmd = install_distinfo(dist) cmd.ensure_finalized() dist.command_obj['install_distinfo'] = cmd cmd.run() # checksum and size are not hard-coded for METADATA as it is # platform-dependent (line endings) metadata = os.path.join(modules_dest, 'Spamlib-0.1.dist-info', 'METADATA') fp = open(metadata, 'rb') try: content = fp.read() finally: fp.close() metadata_size = str(len(content)) metadata_md5 = hashlib.md5(content).hexdigest() record = os.path.join(modules_dest, 'Spamlib-0.1.dist-info', 'RECORD') fp = codecs.open(record, encoding='utf-8') try: content = fp.read() finally: fp.close() found = [] for line in content.splitlines(): filename, checksum, size = line.split(',') filename = os.path.basename(filename) found.append((filename, checksum, size)) expected = [ ('spam', '6ab2f288ef2545868effe68757448b45', '15'), ('spamd', 'd13e6156ce78919a981e424b2fdcd974', '15'), (extmod, 'd41d8cd98f00b204e9800998ecf8427e', '0'), ('METADATA', metadata_md5, metadata_size), ('INSTALLER', '44e3fde05f3f537ed85831969acf396d', '9'), ('REQUESTED', 'd41d8cd98f00b204e9800998ecf8427e', '0'), ('RECORD', '', ''), ] self.assertEqual(found, expected)