Skip to content

Commit

Permalink
Use pkg-config instead of mysql_config (#586)
Browse files Browse the repository at this point in the history
MySQL breaks mysql_config often. Use pkg-config instead.

Fixes #584
  • Loading branch information
methane committed May 9, 2023
1 parent d065827 commit 14538b2
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 143 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ $ pip install mysqlclient

### Customize build (POSIX)

mysqlclient uses `mysql_config` or `mariadb_config` by default for finding
mysqlclient uses `pkg-config --clfags --ldflags mysqlclient` by default for finding
compiler/linker flags.

You can use `MYSQLCLIENT_CFLAGS` and `MYSQLCLIENT_LDFLAGS` environment
Expand Down
5 changes: 3 additions & 2 deletions metadata.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[metadata]
version: 2.1.1
version_info: (2,1,1,'final',0)
name: mysqlclient
version: 2.1.2
version_info: (2,1,2,'dev',0)
description: Python interface to MySQL
author: Inada Naoki
author_email: songofacandy@gmail.com
Expand Down
4 changes: 2 additions & 2 deletions setup_common.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from configparser import ConfigParser as SafeConfigParser
from configparser import ConfigParser


def get_metadata_and_options():
config = SafeConfigParser()
config = ConfigParser()
config.read(["metadata.cfg", "site.cfg"])

metadata = dict(config.items("metadata"))
Expand Down
178 changes: 52 additions & 126 deletions setup_posix.py
Original file line number Diff line number Diff line change
@@ -1,168 +1,94 @@
import os
import sys
import subprocess

# This dequote() business is required for some older versions
# of mysql_config


def dequote(s):
if not s:
raise Exception(
"Wrong MySQL configuration: maybe https://bugs.mysql.com/bug.php?id=86971 ?"
)
if s[0] in "\"'" and s[0] == s[-1]:
s = s[1:-1]
return s


_mysql_config_path = "mysql_config"


def mysql_config(what):
cmd = "{} --{}".format(_mysql_config_path, what)
print(cmd)
f = os.popen(cmd)
data = f.read().strip().split()
ret = f.close()
if ret:
if ret / 256:
data = []
if ret / 256 > 1:
raise OSError("{} not found".format(_mysql_config_path))
print(data)
return data
def find_package_name():
"""Get available pkg-config package name"""
packages = ["mysqlclient", "mariadb"]
for pkg in packages:
try:
cmd = f"pkg-config --exists {pkg}"
print(f"Trying {cmd}")
subprocess.check_call(cmd, shell=True)
except subprocess.CalledProcessError as err:
print(err)
else:
return pkg
raise Exception("Can not find valid pkg-config")


def get_config():
from setup_common import get_metadata_and_options, enabled, create_release_file

global _mysql_config_path

metadata, options = get_metadata_and_options()

if "mysql_config" in options:
_mysql_config_path = options["mysql_config"]
else:
try:
mysql_config("version")
except OSError:
# try mariadb_config
_mysql_config_path = "mariadb_config"
try:
mysql_config("version")
except OSError:
_mysql_config_path = "mysql_config"

extra_objects = []
static = enabled(options, "static")

# allow a command-line option to override the base config file to permit
# a static build to be created via requirements.txt
#
if "--static" in sys.argv:
static = True
sys.argv.remove("--static")

libs = os.environ.get("MYSQLCLIENT_LDFLAGS")
if libs:
libs = libs.strip().split()
else:
libs = mysql_config("libs")
library_dirs = [dequote(i[2:]) for i in libs if i.startswith("-L")]
libraries = [dequote(i[2:]) for i in libs if i.startswith("-l")]
extra_link_args = [x for x in libs if not x.startswith(("-l", "-L"))]

ldflags = os.environ.get("MYSQLCLIENT_LDFLAGS")
cflags = os.environ.get("MYSQLCLIENT_CFLAGS")
if cflags:
use_mysqlconfig_cflags = False
cflags = cflags.strip().split()
else:
use_mysqlconfig_cflags = True
cflags = mysql_config("cflags")

include_dirs = []
extra_compile_args = ["-std=c99"]

for a in cflags:
if a.startswith("-I"):
include_dirs.append(dequote(a[2:]))
elif a.startswith(("-L", "-l")): # This should be LIBS.
pass
else:
extra_compile_args.append(a.replace("%", "%%"))

# Copy the arch flags for linking as well
try:
i = extra_compile_args.index("-arch")
if "-arch" not in extra_link_args:
extra_link_args += ["-arch", extra_compile_args[i + 1]]
except ValueError:
pass
pkg_name = None
static_opt = " --static" if static else ""
if not (cflags and ldflags):
pkg_name = find_package_name()
if not cflags:
cflags = subprocess.check_output(
f"pkg-config{static_opt} --cflags {pkg_name}", encoding="utf-8", shell=True
)
if not ldflags:
ldflags = subprocess.check_output(
f"pkg-config{static_opt} --libs {pkg_name}", encoding="utf-8", shell=True
)

if static:
# properly handle mysql client libraries that are not called libmysqlclient
client = None
CLIENT_LIST = [
"mysqlclient",
"mysqlclient_r",
"mysqld",
"mariadb",
"mariadbclient",
"perconaserverclient",
"perconaserverclient_r",
]
for c in CLIENT_LIST:
if c in libraries:
client = c
break

if client == "mariadb":
client = "mariadbclient"
if client is None:
raise ValueError("Couldn't identify mysql client library")

extra_objects.append(os.path.join(library_dirs[0], "lib%s.a" % client))
if client in libraries:
libraries.remove(client)
cflags = cflags.split()
for f in cflags:
if f.startswith("-std="):
break
else:
if use_mysqlconfig_cflags:
# mysql_config may have "-lmysqlclient -lz -lssl -lcrypto", but zlib and
# ssl is not used by _mysql. They are needed only for static build.
for L in ("crypto", "ssl", "z", "zstd"):
if L in libraries:
libraries.remove(L)
cflags += ["-std=c99"]

name = "mysqlclient"
metadata["name"] = name
ldflags = ldflags.split()

define_macros = [
("version_info", metadata["version_info"]),
("__version__", metadata["version"]),
]
create_release_file(metadata)
del metadata["version_info"]

# print(f"{cflags = }")
# print(f"{ldflags = }")
# print(f"{define_macros = }")

ext_options = dict(
library_dirs=library_dirs,
libraries=libraries,
extra_compile_args=extra_compile_args,
extra_link_args=extra_link_args,
include_dirs=include_dirs,
extra_objects=extra_objects,
extra_compile_args=cflags,
extra_link_args=ldflags,
define_macros=define_macros,
)

# newer versions of gcc require libstdc++ if doing a static build
if static:
ext_options["language"] = "c++"

print("ext_options:")
print("Options for building extention module:")
for k, v in ext_options.items():
print(" {}: {}".format(k, v))
print(f" {k}: {v}")

create_release_file(metadata)
del metadata["version_info"]

return metadata, ext_options


if __name__ == "__main__":
sys.stderr.write(
"""You shouldn't be running this directly; it is used by setup.py."""
)
from pprint import pprint

metadata, config = get_config()
print("# Metadata")
pprint(metadata, sort_dicts=False, compact=True)
print("\n# Extention options")
pprint(config, sort_dicts=False, compact=True)
14 changes: 7 additions & 7 deletions setup_windows.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import os
import sys


def get_config():
Expand Down Expand Up @@ -38,9 +37,6 @@ def get_config():

extra_link_args = ["/MANIFEST"]

name = "mysqlclient"
metadata["name"] = name

define_macros = [
("version_info", metadata["version_info"]),
("__version__", metadata["version"]),
Expand All @@ -59,6 +55,10 @@ def get_config():


if __name__ == "__main__":
sys.stderr.write(
"""You shouldn't be running this directly; it is used by setup.py."""
)
from pprint import pprint

metadata, config = get_config()
print("# Metadata")
pprint(metadata)
print("\n# Extention options")
pprint(config)
5 changes: 0 additions & 5 deletions site.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,6 @@
# static: link against a static library
static = False

# The path to mysql_config.
# Only use this if mysql_config is not on your PATH, or you have some weird
# setup that requires it.
#mysql_config = /usr/local/bin/mysql_config

# http://stackoverflow.com/questions/1972259/mysql-python-install-problem-using-virtualenv-windows-pip
# Windows connector libs for MySQL. You need a 32-bit connector for your 32-bit Python build.
connector =

0 comments on commit 14538b2

Please sign in to comment.