import ast import atexit import os.path import platform import shutil import subprocess import sys import tempfile import distutils.cmd import distutils.log import distutils.sysconfig from setuptools import Extension from setuptools import setup MACOS_FLAG = ['-mmacosx-version-min=10.7'] FLAGS_POSIX = [ '-fPIC', '-std=gnu++0x', '-Wall', '-Wno-parentheses', '-Werror=switch', ] FLAGS_CLANG = ['-c', '-O3'] + FLAGS_POSIX + ['-stdlib=libc++'] LFLAGS_POSIX = ['-fPIC', '-lstdc++'] LFLAGS_CLANG = ['-fPIC', '-stdlib=libc++'] sources = ['_sass.c'] headers = [] if sys.platform == 'win32': extra_compile_args = ['/Od', '/EHsc', '/MT'] extra_link_args = [] elif platform.system() == 'Darwin': extra_compile_args = FLAGS_CLANG + MACOS_FLAG extra_link_args = LFLAGS_CLANG + MACOS_FLAG elif platform.system() in {'FreeBSD', 'OpenBSD'}: extra_compile_args = FLAGS_CLANG extra_link_args = LFLAGS_CLANG else: extra_compile_args = FLAGS_POSIX extra_link_args = LFLAGS_POSIX if platform.system() in {'Darwin', 'FreeBSD', 'OpenBSD'}: os.environ.setdefault('CC', 'clang') os.environ.setdefault('CXX', 'clang++') orig_customize_compiler = distutils.sysconfig.customize_compiler def customize_compiler(compiler): orig_customize_compiler(compiler) compiler.compiler[0] = os.environ['CC'] compiler.compiler_so[0] = os.environ['CXX'] compiler.compiler_cxx[0] = os.environ['CXX'] compiler.linker_so[0] = os.environ['CXX'] return compiler distutils.sysconfig.customize_compiler = customize_compiler if os.environ.get('SYSTEM_SASS', False): libraries = ['sass'] include_dirs = [] else: LIBSASS_SOURCE_DIR = os.path.join('libsass', 'src') if ( not os.path.isfile(os.path.join('libsass', 'Makefile')) and os.path.isdir('.git') ): print(file=sys.stderr) print('Missing the libsass sumbodule. Try:', file=sys.stderr) print(' git submodule update --init', file=sys.stderr) print(file=sys.stderr) exit(1) # Determine the libsass version from the git checkout if os.path.exists(os.path.join('libsass', '.git')): out = subprocess.check_output(( 'git', '-C', 'libsass', 'describe', '--abbrev=4', '--dirty', '--always', '--tags', )) with open('.libsass-upstream-version', 'wb') as libsass_version_file: libsass_version_file.write(out) # The version file should always exist at this point with open('.libsass-upstream-version', 'rb') as libsass_version_file: libsass_version = libsass_version_file.read().decode('UTF-8').strip() if sys.platform == 'win32': # This looks wrong, but is required for some reason :( define = fr'/DLIBSASS_VERSION="\"{libsass_version}\""' else: define = f'-DLIBSASS_VERSION="{libsass_version}"' for directory in ( os.path.join('libsass', 'src'), os.path.join('libsass', 'include'), ): for pth, _, filenames in os.walk(directory): for filename in filenames: filename = os.path.join(pth, filename) if filename.endswith(('.c', '.cpp')): sources.append(filename) elif filename.endswith('.h'): headers.append(filename) if sys.platform == 'win32': from distutils.msvc9compiler import get_build_version vscomntools_env = 'VS{}{}COMNTOOLS'.format( int(get_build_version()), int(get_build_version() * 10) % 10, ) try: os.environ[vscomntools_env] = os.environ['VS140COMNTOOLS'] except KeyError: distutils.log.warn( 'You probably need Visual Studio 2015 (14.0) ' 'or higher', ) from distutils import msvccompiler, msvc9compiler if msvccompiler.get_build_version() < 14.0: msvccompiler.get_build_version = lambda: 14.0 if get_build_version() < 14.0: msvc9compiler.get_build_version = lambda: 14.0 msvc9compiler.VERSION = 14.0 elif platform.system() in {'Darwin', 'FreeBSD', 'OpenBSD'}: # Dirty workaround to avoid link error... # Python distutils doesn't provide any way # to configure different flags for each cc and c++. cencode_path = os.path.join(LIBSASS_SOURCE_DIR, 'cencode.c') cencode_body = '' with open(cencode_path) as f: cencode_body = f.read() with open(cencode_path, 'w') as f: f.write( '#ifdef __cplusplus\n' 'extern "C" {\n' '#endif\n', ) f.write(cencode_body) f.write( '#ifdef __cplusplus\n' '}\n' '#endif\n', ) @atexit.register def restore_cencode(): if os.path.isfile(cencode_path): with open(cencode_path, 'w') as f: f.write(cencode_body) libraries = [] include_dirs = [os.path.join('.', 'libsass', 'include')] extra_compile_args.append(define) # Py_LIMITED_API does not work for pypy # https://foss.heptapod.net/pypy/pypy/issues/3173 if not hasattr(sys, 'pypy_version_info'): py_limited_api = True define_macros = [('Py_LIMITED_API', None)] else: py_limited_api = False define_macros = [] sass_extension = Extension( '_sass', sorted(sources), include_dirs=include_dirs, depends=headers, extra_compile_args=extra_compile_args, extra_link_args=extra_link_args, libraries=libraries, py_limited_api=py_limited_api, define_macros=define_macros, ) def version(sass_filename='sass.py'): with open(sass_filename) as f: tree = ast.parse(f.read(), sass_filename) for node in tree.body: if isinstance(node, ast.Assign) and len(node.targets) == 1: target, = node.targets if isinstance(target, ast.Name) and target.id == '__version__': return node.value.s def readme(): try: with open(os.path.join(os.path.dirname(__file__), 'README.rst')) as f: return f.read() except OSError: pass class upload_doc(distutils.cmd.Command): """Uploads the documentation to GitHub pages.""" description = __doc__ user_options = [] def initialize_options(self): if sys.version_info < (3,): raise SystemExit('upload_doc must be run with python 3') def finalize_options(self): pass def run(self): path = tempfile.mkdtemp() build = os.path.join( os.path.dirname(os.path.abspath(__file__)), 'build', 'sphinx', 'html', ) os.chdir(path) os.system( 'git clone -b gh-pages --depth 5 ' 'git@github.com:sass/libsass-python.git .', ) os.system('git rm -r .') os.system('touch .nojekyll') os.system('cp -r ' + build + '/* .') os.system('git stage .') os.system('git commit -a -m "Documentation updated."') os.system('git push origin gh-pages') shutil.rmtree(path) cmdclass = {'upload_doc': upload_doc} if sys.version_info >= (3,) and platform.python_implementation() == 'CPython': try: import wheel.bdist_wheel except ImportError: pass else: class bdist_wheel(wheel.bdist_wheel.bdist_wheel): def finalize_options(self): self.py_limited_api = f'cp3{sys.version_info[1]}' super().finalize_options() cmdclass['bdist_wheel'] = bdist_wheel setup( name='libsass', description='Sass for Python: ' 'A straightforward binding of libsass for Python.', long_description=readme(), version=version(), ext_modules=[sass_extension], packages=['sassutils'], py_modules=['pysassc', 'sass', 'sasstests'], package_data={ '': [ 'README.rst', 'test/*.sass', ], }, license='MIT License', author='Hong Minhee', author_email='minhee' '@' 'dahlia.kr', url='https://sass.github.io/libsass-python/', download_url='https://github.com/sass/libsass-python/releases', entry_points={ 'distutils.commands': [ 'build_sass = sassutils.distutils:build_sass', ], 'distutils.setup_keywords': [ 'sass_manifests = sassutils.distutils:validate_manifests', ], 'console_scripts': [ ['pysassc = pysassc:main'], ], }, classifiers=[ 'Development Status :: 5 - Production/Stable', 'Environment :: Web Environment', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: C', 'Programming Language :: C++', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', 'Programming Language :: Python :: Implementation :: Stackless', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 'Topic :: Software Development :: Code Generators', 'Topic :: Software Development :: Compilers', ], python_requires='>=3.6', cmdclass=cmdclass, )