URL: https://github.com/freeipa/freeipa/pull/379
Author: tiran
 Title: #379: Packaging: Add IPA commands package
Action: synchronized

To pull the PR as Git branch:
git remote add ghfreeipa https://github.com/freeipa/freeipa
git fetch ghfreeipa pull/379/head:pr379
git checkout pr379
From 0544a3834cb23d0664300d1f577a1d30ccc59610 Mon Sep 17 00:00:00 2001
From: Christian Heimes <chei...@redhat.com>
Date: Thu, 16 Feb 2017 15:27:49 +0100
Subject: [PATCH] Packaging: Add IPA commands package

The ipacommands package contains ipa-getkeytab and ipa-rmkeytab for
installation in a virtual env. The programs are compiled with distutils
/ setuptools.

https://fedorahosted.org/freeipa/ticket/6484

Signed-off-by: Christian Heimes <chei...@redhat.com>
---
 .gitignore                   |   7 ++
 Makefile.am                  |   2 +
 configure.ac                 |   1 +
 pypi/Makefile.am             |   1 +
 pypi/ipacommands/MANIFEST.in |  25 ++++++
 pypi/ipacommands/Makefile.am |  79 ++++++++++++++++++
 pypi/ipacommands/setup.cfg   |   5 ++
 pypi/ipacommands/setup.py    | 194 +++++++++++++++++++++++++++++++++++++++++++
 8 files changed, 314 insertions(+)
 create mode 100644 pypi/ipacommands/MANIFEST.in
 create mode 100644 pypi/ipacommands/Makefile.am
 create mode 100644 pypi/ipacommands/setup.cfg
 create mode 100644 pypi/ipacommands/setup.py

diff --git a/.gitignore b/.gitignore
index 8b57dbc..e18bcf0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -117,3 +117,10 @@ freeipa2-dev-doc
 /ipaplatform/paths.py
 /ipaplatform/services.py
 /ipaplatform/tasks.py
+
+/pypi/ipacommands/COPYING
+/pypi/ipacommands/Contributors.txt
+/pypi/ipacommands/asn1
+/pypi/ipacommands/client
+/pypi/ipacommands/ipasetup.py
+/pypi/ipacommands/util
diff --git a/Makefile.am b/Makefile.am
index cbe4f2d..b395033 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -285,6 +285,8 @@ bdist_wheel: $(WHEELDISTDIR)
 	for dir in $(IPA_WHEEL_PACKAGES) ipatests; do \
 	    $(MAKE) $(AM_MAKEFLAGS) -C $${dir} $@ || exit 1; \
 	done
+	rm -f $(WHEELDISTDIR)/ipacommands-*.tar.gz
+	$(MAKE) $(AM_MAKEFLAGS) -C pypi/ipacommands sdist || exit 1;
 
 wheel_bundle: $(WHEELBUNDLEDIR) bdist_wheel .wheelconstraints
 	rm -f $(foreach item,$(IPA_WHEEL_PACKAGES) ipatests,$(WHEELBUNDLEDIR)/$(item)-*.whl)
diff --git a/configure.ac b/configure.ac
index c43759c..24e9408 100644
--- a/configure.ac
+++ b/configure.ac
@@ -593,6 +593,7 @@ AC_CONFIG_FILES([
     pypi/Makefile
     pypi/freeipa/Makefile
     pypi/ipa/Makefile
+    pypi/ipacommands/Makefile
     pypi/ipaplatform/Makefile
     pypi/ipaserver/Makefile
     pypi/ipatests/Makefile
diff --git a/pypi/Makefile.am b/pypi/Makefile.am
index 5d8be9c..be572c6 100644
--- a/pypi/Makefile.am
+++ b/pypi/Makefile.am
@@ -7,6 +7,7 @@ NULL =
 SUBDIRS =			\
 	freeipa			\
 	ipa				\
+	ipacommands		\
 	ipaplatform		\
 	ipaserver		\
 	ipatests		\
diff --git a/pypi/ipacommands/MANIFEST.in b/pypi/ipacommands/MANIFEST.in
new file mode 100644
index 0000000..659a1f5
--- /dev/null
+++ b/pypi/ipacommands/MANIFEST.in
@@ -0,0 +1,25 @@
+include asn1/*.c
+include asn1/*.h
+include asn1/asn1c/*.c
+include asn1/asn1c/*.h
+include asn1/asn1c/ipa.asn1
+
+include client/config.c
+include client/config.h
+include client/ipa-client-common.c
+include client/ipa-client-common.h
+include client/ipa-getkeytab.c
+include client/ipa-join.c
+include client/ipa-rmkeytab.c
+
+include util/ipa_krb5.c
+include util/ipa_krb5.h
+
+prune client/asn1
+prune client/client
+prune client/util
+
+include Contributors.txt COPYING
+include config.h
+include ipasetup.py
+include setup.cfg
diff --git a/pypi/ipacommands/Makefile.am b/pypi/ipacommands/Makefile.am
new file mode 100644
index 0000000..645ce7a
--- /dev/null
+++ b/pypi/ipacommands/Makefile.am
@@ -0,0 +1,79 @@
+# This file will be processed with automake-1.7 to create Makefile.in
+#
+AUTOMAKE_OPTIONS = 1.7
+
+NULL =
+
+pkgname = $(shell basename "$(abs_srcdir)")
+
+# hack to handle back-in-the-hierarchy depedency on ipasetup.py
+.PHONY: $(top_builddir)/ipasetup.py
+$(top_builddir)/ipasetup.py:
+	(cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) ipasetup.py)
+
+DEPENDENCIES = \
+	asn1				\
+	client				\
+	util				\
+	COPYING				\
+	Contributors.txt		\
+	config.h			\
+	ipasetup.py			\
+	$(NULL)
+
+# Python setup.py can handle symlinks to directories fine
+asn1: $(top_srcdir)/asn1
+	if [ ! -e "$@" ]; then ln -rs "$<"; fi
+
+client: $(top_srcdir)/client
+	if [ ! -e "$@" ]; then ln -rs "$<"; fi
+
+util: $(top_srcdir)/util
+	if [ ! -e "$@" ]; then ln -rs "$<"; fi
+
+# On the other hand files must be copied to create proper sdist
+COPYING: $(top_srcdir)/COPYING
+	cp -p "$<" "$@"
+
+Contributors.txt: $(top_srcdir)/Contributors.txt
+	cp -p "$<" "$@"
+
+ipasetup.py: $(top_builddir)/ipasetup.py
+	cp -p "$<" "$@"
+
+config.h: $(top_builddir)/config.h
+	cp -p "$<" "$@"
+
+
+all-local: $(DEPENDENCIES)
+
+
+check-local: $(DEPENDENCIES)
+	cd $(srcdir); $(PYTHON) setup.py \
+		$(VERBOSITY) \
+		build \
+		--build-base "$(abs_builddir)/build"
+
+clean-local: $(DEPENDENCIES)
+	$(PYTHON) "$(srcdir)/setup.py" clean --all
+	rm -rf "$(srcdir)/build" "$(srcdir)/dist" "$(srcdir)/MANIFEST"
+	find "$(srcdir)" \
+		-name "*.py[co]" -delete -o	\
+		-name "__pycache__" -delete -o	\
+		-name "*.egg-info" -exec rm -rf {} +
+	rm -f $(DEPENDENCIES)
+
+dist-hook: $(DEPENDENCIES)
+	$(PYTHON) "$(srcdir)/setup.py" egg_info
+	PYTHON_SOURCES=$$(cat "$(srcdir)/$(pkgname).egg-info/SOURCES.txt") || exit $$?;	\
+	for FILEN in $${PYTHON_SOURCES}; 						\
+	do										\
+		if test -x "$(srcdir)/$${FILEN}"; then MODE=755; else MODE=644; fi;	\
+		$(INSTALL) -D -m $${MODE} "$(srcdir)/$${FILEN}" "$(distdir)/$${FILEN}" || exit $$?;	\
+	done
+
+WHEELDISTDIR = $(top_builddir)/dist/wheels
+.PHONY: sdist
+sdist: $(DEPENDENCIES)
+	rm -rf $(WHEELDISTDIR)/$(pkgname)*.tar.gz
+	$(PYTHON) "$(srcdir)/setup.py" sdist --format=gztar --dist-dir=$(WHEELDISTDIR)
diff --git a/pypi/ipacommands/setup.cfg b/pypi/ipacommands/setup.cfg
new file mode 100644
index 0000000..ccffb47
--- /dev/null
+++ b/pypi/ipacommands/setup.cfg
@@ -0,0 +1,5 @@
+[bdist_wheel]
+universal = 0
+
+[metadata]
+license_file = COPYING
diff --git a/pypi/ipacommands/setup.py b/pypi/ipacommands/setup.py
new file mode 100644
index 0000000..e50ee50
--- /dev/null
+++ b/pypi/ipacommands/setup.py
@@ -0,0 +1,194 @@
+"""IPA commands
+"""
+import glob
+import os
+import shlex
+import subprocess
+import sys
+
+from distutils import unixccompiler
+from distutils.command.build_scripts import build_scripts \
+    as distutils_build_scripts
+from setuptools import Extension
+from setuptools.command.build_ext import build_ext as setuptools_build_ext
+from setuptools.command.install_lib import install_lib \
+    as setuptools_install_lib
+
+
+class CustomUnixCCompiler(unixccompiler.UnixCCompiler):
+    """Custom unix C compiler
+
+    Redirect link_shared_object to link_executable
+    """
+
+    def link_shared_object(self, 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):
+        # unused: export_symbols, build_temp
+        # remove pythonX.Y lib
+        libraries = list(
+            lib for lib in libraries
+            if not lib.startswith('python')
+        )
+        return self.link_executable(
+            objects,
+            output_progname=output_filename,
+            output_dir=output_dir,
+            libraries=libraries, library_dirs=library_dirs,
+            runtime_library_dirs=runtime_library_dirs,
+            debug=debug, extra_preargs=extra_preargs,
+            extra_postargs=extra_postargs,
+            target_lang=target_lang)
+
+    def _fix_lib_args(self, libraries, library_dirs, runtime_library_dirs):
+        return libraries, library_dirs, runtime_library_dirs
+
+
+# distutils does not have an API to override compiler class.
+# Let's monkey patch!
+unixccompiler.UnixCCompiler = CustomUnixCCompiler
+
+
+class build_ext(setuptools_build_ext):
+    """Custom build_ext
+
+    Don't add '.so' extension
+    """
+    def get_ext_filename(self, ext_name):
+        return ext_name
+
+
+class build_scripts(distutils_build_scripts):
+    """Custom build_scripts
+
+    Copy executables form build_lib to script dir.
+    """
+    def run(self):
+        # run and get build_ext
+        self.run_command('build_ext')
+        build_ext = self.get_finalized_command('build_ext')
+        for i, name in enumerate(self.scripts):
+            libfile = os.path.join(build_ext.build_lib, name)
+            # add binary as script
+            self.scripts[i] = libfile
+        distutils_build_scripts.run(self)
+
+    def copy_scripts(self):
+        # simple version of copy_script that does not try to read the
+        # executable as Python scripts.
+        self.mkpath(self.build_dir)
+        outfiles = []
+        for script in self.scripts:
+            outfile = os.path.join(self.build_dir, os.path.basename(script))
+            self.copy_file(script, outfile)
+            os.chmod(outfile, 0o755)
+            outfiles.append(outfile)
+        return outfiles, outfiles
+
+
+class install_lib(setuptools_install_lib):
+    """Custom install_lib
+
+    Don't install the executables as libraries.
+    """
+    def install(self):
+        return []
+
+
+def pkgconfig(flags, *pkgs):
+    cmd = ['pkg-config', flags]
+    cmd.extend(pkgs)
+    out = subprocess.check_output(cmd)
+    if isinstance(out, bytes):
+        out = out.decode(sys.getfilesystemencoding())
+    return shlex.split(out)
+
+
+def get_extensions(ipajoin=False):
+    cfiles = [
+        'asn1/ipa_asn1.c',
+        'client/config.c',
+        'client/ipa-client-common.c',
+        'util/ipa_krb5.c'
+    ] + glob.glob('asn1/asn1c/*.c')
+
+    headers = [
+        'config.h',
+        'asn1/ipa_asn1.h',
+        'client/config.h',
+        'client/ipa-client-common.h',
+        'util/ipa_krb5.h'
+    ] + glob.glob('asn1/asn1c/*.h')
+
+    include_dirs = ['.', 'asn1', 'asn1/asn1c', 'client', 'util']
+
+    pkgs = ('nss', 'krb5', 'libcrypto', 'popt', 'libsasl2', 'ini_config')
+
+    extra_compile_args = []
+    extra_compile_args.extend(pkgconfig('--cflags', *pkgs))
+
+    extra_link_args = ['-lldap_r', '-llber']  # OpenLDAP has no .pc
+    extra_link_args.extend(pkgconfig('--libs', *pkgs))
+
+    macros = [
+        ('IPACONFFILE', '"/etc/ipa/default.conf"'),
+        ('LOCALEDIR', '"/usr/share/locale"'),
+    ]
+
+    extensions = [
+        Extension(
+            'ipa-getkeytab',
+            sources=['client/ipa-getkeytab.c'] + cfiles,
+            depends=headers,
+            extra_compile_args=extra_compile_args,
+            extra_link_args=extra_link_args,
+            include_dirs=include_dirs,
+            define_macros=macros,
+        ),
+        Extension(
+            'ipa-rmkeytab',
+            sources=['client/ipa-rmkeytab.c'] + cfiles,
+            depends=headers,
+            extra_compile_args=extra_compile_args,
+            extra_link_args=extra_link_args,
+            include_dirs=include_dirs,
+            define_macros=macros,
+        ),
+    ]
+
+    if ipajoin:
+        xmlrpc_compile_args = pkgconfig('--cflags', 'xmlrpc_client')
+        xmlrpc_link_args = pkgconfig('--libs', 'xmlrpc_client')
+        extensions.append(
+            Extension(
+                'ipa-join',
+                sources=['client/ipa-join.c'] + cfiles,
+                depends=headers,
+                extra_compile_args=extra_compile_args + xmlrpc_compile_args,
+                extra_link_args=extra_link_args + xmlrpc_link_args,
+                include_dirs=include_dirs,
+                define_macros=macros,
+            )
+        )
+
+    return extensions
+
+
+if __name__ == '__main__':
+    from ipasetup import ipasetup  # noqa: E402
+
+    exts = get_extensions(ipajoin=False)
+
+    ipasetup(
+        name='ipacommands',
+        doc=__doc__,
+        ext_modules=exts,
+        cmdclass={
+            'build_ext': build_ext,
+            'build_scripts': build_scripts,
+            'install_lib': install_lib,
+        },
+        scripts=[ext.name for ext in exts],
+    )
-- 
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code

Reply via email to