Skip to content

Commit a4fa13e

Browse files
author
Geert Vanderkelen
committed
BUG20114310: Add support static linking C Extension
We add support for statically linking C libraries into the C extension. A new command 'build_ext_static' was added and works similar to 'build_ext' (which will build dynamically). The 'install' command accepts a new option --static. This is most useful for packaging and/or when you do not want to have dependencies on, for example, the MySQL C libraries.
1 parent d11b703 commit a4fa13e

File tree

2 files changed

+80
-10
lines changed

2 files changed

+80
-10
lines changed

lib/cpy_distutils.py

Lines changed: 74 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
from distutils.command.install_lib import install_lib
3030
from distutils.errors import DistutilsExecError
3131
from distutils.util import get_platform
32+
from distutils.dir_util import copy_tree
3233
from distutils import log
3334
from glob import glob
3435
import os
@@ -41,12 +42,17 @@
4142

4243
CEXT_OPTIONS = [
4344
('with-mysql-capi=', None,
44-
"Location of MySQL C API installation or path to mysql_config")
45+
"Location of MySQL C API installation or path to mysql_config"),
46+
]
47+
48+
CEXT_STATIC_OPTIONS = [
49+
('static', None,
50+
"Link C libraries statically with the C Extension"),
4551
]
4652

4753
INSTALL_OPTIONS = [
4854
('byte-code-only=', None,
49-
"Remove Python .py files; leave byte code .pyc only")
55+
"Remove Python .py files; leave byte code .pyc only"),
5056
]
5157

5258

@@ -199,7 +205,7 @@ def remove_cext(distribution):
199205
distribution.ext_modules.remove(ext_mod)
200206

201207

202-
class BuildMySQLExt(build_ext):
208+
class BuildExtDynamic(build_ext):
203209

204210
"""Build Connector/Python C Extension"""
205211

@@ -220,13 +226,13 @@ def _finalize_connector_c(self, connc_loc):
220226
"""
221227
platform = get_platform()
222228
self._mysql_config_info = None
223-
min_version = BuildMySQLExt.min_connector_c_version
229+
min_version = BuildExtDynamic.min_connector_c_version
224230

225231
err_invalid_loc = "MySQL C API location is invalid; was %s"
226232

227233
mysql_config = None
228234
err_version = "MySQL C API {0}.{1}.{2} or later required".format(
229-
*BuildMySQLExt.min_connector_c_version)
235+
*BuildExtDynamic.min_connector_c_version)
230236

231237
if not os.path.exists(connc_loc):
232238
log.error(err_invalid_loc, connc_loc)
@@ -397,6 +403,62 @@ def run(self):
397403
self.real_build_extensions()
398404

399405

406+
class BuildExtStatic(BuildExtDynamic):
407+
408+
"""Build and Link libraries statically with the C Extensions"""
409+
410+
user_options = build_ext.user_options + CEXT_OPTIONS
411+
412+
def finalize_options(self):
413+
self.set_undefined_options('install',
414+
('with_mysql_capi', 'with_mysql_capi'))
415+
416+
build_ext.finalize_options(self)
417+
self.connc_lib = os.path.join(self.build_temp, 'connc', 'lib')
418+
self.connc_include = os.path.join(self.build_temp, 'connc', 'include')
419+
420+
if self.with_mysql_capi:
421+
self._finalize_connector_c(self.with_mysql_capi)
422+
423+
def _finalize_connector_c(self, connc_loc):
424+
if not os.path.isdir(connc_loc):
425+
log.error("MySQL C API should be a directory")
426+
sys.exit(1)
427+
428+
copy_tree(os.path.join(connc_loc, 'lib'), self.connc_lib)
429+
copy_tree(os.path.join(connc_loc, 'include'), self.connc_include)
430+
431+
for lib_file in os.listdir(self.connc_lib):
432+
if os.name == 'posix' and not lib_file.endswith('.a'):
433+
os.unlink(os.path.join(self.connc_lib, lib_file))
434+
435+
def fix_compiler(self):
436+
BuildExtDynamic.fix_compiler(self)
437+
438+
extra_compile_args = []
439+
extra_link_args = []
440+
441+
if os.name == 'posix':
442+
extra_compile_args = [
443+
'-I%s' % self.connc_include
444+
]
445+
extra_link_args = [
446+
#'-lstdc++',
447+
'-L%s' % self.connc_lib,
448+
'-lmysqlclient',
449+
]
450+
451+
if not extra_compile_args and not extra_link_args:
452+
return
453+
454+
for ext in self.extensions:
455+
if extra_compile_args:
456+
ext.extra_compile_args.extend(extra_compile_args)
457+
if extra_link_args:
458+
ext.extra_link_args.extend(extra_link_args)
459+
460+
461+
400462
class InstallLib(install_lib):
401463

402464
user_options = install_lib.user_options + CEXT_OPTIONS + INSTALL_OPTIONS
@@ -435,19 +497,24 @@ class Install(install):
435497

436498
description = "install MySQL Connector/Python"
437499

438-
user_options = install.user_options + CEXT_OPTIONS + INSTALL_OPTIONS
500+
user_options = install.user_options + CEXT_OPTIONS + INSTALL_OPTIONS + \
501+
CEXT_STATIC_OPTIONS
439502

440-
boolean_options = ['byte-code-only']
503+
boolean_options = ['byte-code-only', 'static']
441504
need_ext = False
442505

443506
def initialize_options(self):
444507
install.initialize_options(self)
445508
self.with_mysql_capi = None
446509
self.byte_code_only = None
510+
self.static = None
447511

448512
def finalize_options(self):
449513
install.finalize_options(self)
450514

515+
if self.static:
516+
self.distribution.cmdclass['build_ext'] = BuildExtStatic
517+
451518
if self.byte_code_only is None:
452519
self.byte_code_only = False
453520

setupinfo.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@
2525
import os
2626
import sys
2727

28-
from lib.cpy_distutils import Install, InstallLib, BuildMySQLExt
28+
from lib.cpy_distutils import (
29+
Install, InstallLib, BuildExtDynamic, BuildExtStatic
30+
)
2931

3032
# Development Status Trove Classifiers significant for Connector/Python
3133
DEVELOPMENT_STATUSES = {
@@ -46,9 +48,10 @@
4648
with open(version_py, 'rb') as fp:
4749
exec(compile(fp.read(), version_py, 'exec'))
4850

49-
BuildMySQLExt.min_connector_c_version = (5, 5, 8)
51+
BuildExtDynamic.min_connector_c_version = (5, 5, 8)
5052
command_classes = {
51-
'build_ext': BuildMySQLExt,
53+
'build_ext': BuildExtDynamic,
54+
'build_ext_static': BuildExtStatic,
5255
'install_lib': InstallLib,
5356
'install': Install,
5457
}

0 commit comments

Comments
 (0)