URL: https://github.com/freeipa/freeipa/pull/379
Author: tiran
 Title: #379: Packaging: Add placeholder and IPA commands packages
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 cb9669a00eda6fa12ebd126fdcf43f49e86b18da Mon Sep 17 00:00:00 2001
From: Christian Heimes <chei...@redhat.com>
Date: Mon, 9 Jan 2017 11:02:25 +0100
Subject: [PATCH] Packaging: Add placeholder and IPA commands packages

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

The ipa and freeipa packages are placeholders to prevent PyPI squashing
attacks and reserve the names for future use. `pip install ipa` installs
ipaclient.

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

Signed-off-by: Christian Heimes <chei...@redhat.com>
---
 .gitignore                        |   7 ++
 Makefile.am                       |   5 +-
 Makefile.python.am                |  21 +++--
 configure.ac                      |   4 +
 packaging/Makefile.am             |  11 +++
 packaging/freeipa/Makefile.am     |   3 +
 packaging/freeipa/README.txt      |   2 +
 packaging/freeipa/setup.cfg       |   6 ++
 packaging/freeipa/setup.py        |  36 +++++++
 packaging/ipa/Makefile.am         |   3 +
 packaging/ipa/README.txt          |   2 +
 packaging/ipa/setup.cfg           |   6 ++
 packaging/ipa/setup.py            |  36 +++++++
 packaging/ipacommands/MANIFEST.in |  25 +++++
 packaging/ipacommands/Makefile.am |  79 ++++++++++++++++
 packaging/ipacommands/setup.cfg   |   5 +
 packaging/ipacommands/setup.py    | 194 ++++++++++++++++++++++++++++++++++++++
 17 files changed, 436 insertions(+), 9 deletions(-)
 create mode 100644 packaging/Makefile.am
 create mode 100644 packaging/freeipa/Makefile.am
 create mode 100644 packaging/freeipa/README.txt
 create mode 100644 packaging/freeipa/setup.cfg
 create mode 100755 packaging/freeipa/setup.py
 create mode 100644 packaging/ipa/Makefile.am
 create mode 100644 packaging/ipa/README.txt
 create mode 100644 packaging/ipa/setup.cfg
 create mode 100755 packaging/ipa/setup.py
 create mode 100644 packaging/ipacommands/MANIFEST.in
 create mode 100644 packaging/ipacommands/Makefile.am
 create mode 100644 packaging/ipacommands/setup.cfg
 create mode 100644 packaging/ipacommands/setup.py

diff --git a/.gitignore b/.gitignore
index 04553fd..249f158 100644
--- a/.gitignore
+++ b/.gitignore
@@ -112,3 +112,10 @@ freeipa2-dev-doc
 /ipaplatform/paths.py
 /ipaplatform/services.py
 /ipaplatform/tasks.py
+
+/packaging/ipacommands/COPYING
+/packaging/ipacommands/Contributors.txt
+/packaging/ipacommands/asn1
+/packaging/ipacommands/client
+/packaging/ipacommands/ipasetup.py
+/packaging/ipacommands/util
diff --git a/Makefile.am b/Makefile.am
index 9bfc899..e25cea3 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,7 +1,7 @@
 ACLOCAL_AMFLAGS = -I m4
 
 IPACLIENT_SUBDIRS = ipaclient ipalib ipapython
-SUBDIRS = asn1 util client contrib daemons init install $(IPACLIENT_SUBDIRS) ipaplatform ipaserver ipatests po
+SUBDIRS = asn1 util client contrib daemons init install $(IPACLIENT_SUBDIRS) ipaplatform ipaserver ipatests packaging po
 
 MOSTLYCLEANFILES = ipasetup.pyc ipasetup.pyo \
 		   ignore_import_errors.pyc ignore_import_errors.pyo \
@@ -202,6 +202,9 @@ $(WHEELBUNDLEDIR):
 	mkdir -p $(WHEELBUNDLEDIR)
 
 bdist_wheel: $(WHEELDISTDIR)
+	$(MAKE) $(AM_MAKEFLAGS) -C packaging/ipacommands sdist || exit 1;
+	$(MAKE) $(AM_MAKEFLAGS) -C packaging/ipa bdist_wheel || exit 1;
+	$(MAKE) $(AM_MAKEFLAGS) -C packaging/freeipa bdist_wheel || exit 1;
 	for dir in $(IPACLIENT_SUBDIRS); do \
 	    $(MAKE) $(AM_MAKEFLAGS) -C $${dir} $@ || exit 1; \
 	done
diff --git a/Makefile.python.am b/Makefile.python.am
index 665893f..9c34fe3 100644
--- a/Makefile.python.am
+++ b/Makefile.python.am
@@ -1,5 +1,6 @@
 pkgname = $(shell basename "$(abs_srcdir)")
 pkgpythondir = $(pythondir)/$(pkgname)
+pkginstall = true
 
 if VERBOSE_MAKE
 VERBOSITY="--verbose"
@@ -19,16 +20,20 @@ all-local: $(top_builddir)/ipasetup.py
 		--build-base "$(abs_builddir)/build"
 
 install-exec-local: $(top_builddir)/ipasetup.py
-	$(PYTHON) $(srcdir)/setup.py \
-		$(VERBOSITY) \
-		install \
-		--prefix "$(DESTDIR)$(prefix)" \
-		--single-version-externally-managed \
-		--record "$(DESTDIR)$(pkgpythondir)/install_files.txt" \
-		--optimize 1
+	if [ "x$(pkginstall)" = "xtrue" ]; then \
+	    $(PYTHON) $(srcdir)/setup.py \
+		    $(VERBOSITY) \
+		    install \
+		    --prefix "$(DESTDIR)$(prefix)" \
+		    --single-version-externally-managed \
+		    --record "$(DESTDIR)$(pkgpythondir)/install_files.txt" \
+		    --optimize 1; \
+	fi
 
 uninstall-local:
-	cat "$(DESTDIR)$(pkgpythondir)/install_files.txt" | xargs rm -rf
+	if [ -f "$(DESTDIR)$(pkgpythondir)/install_files.txt" ]; then \
+	    cat "$(DESTDIR)$(pkgpythondir)/install_files.txt" | xargs rm -rf ; \
+	fi
 	rm -rf "$(DESTDIR)$(pkgpythondir)"
 
 clean-local: $(top_builddir)/ipasetup.py
diff --git a/configure.ac b/configure.ac
index 6cd3a89..12bc880 100644
--- a/configure.ac
+++ b/configure.ac
@@ -556,6 +556,10 @@ AC_CONFIG_FILES([
     ipaserver/Makefile
     ipatests/Makefile
     ipatests/man/Makefile
+    packaging/Makefile
+    packaging/freeipa/Makefile
+    packaging/ipa/Makefile
+    packaging/ipacommands/Makefile
     po/Makefile.in
     po/Makefile.hack
     util/Makefile
diff --git a/packaging/Makefile.am b/packaging/Makefile.am
new file mode 100644
index 0000000..e033673
--- /dev/null
+++ b/packaging/Makefile.am
@@ -0,0 +1,11 @@
+# This file will be processed with automake-1.7 to create Makefile.in
+#
+AUTOMAKE_OPTIONS = 1.7 subdir-objects
+
+NULL =
+
+SUBDIRS =			\
+	freeipa			\
+	ipa			\
+	ipacommands		\
+	$(NULL)
diff --git a/packaging/freeipa/Makefile.am b/packaging/freeipa/Makefile.am
new file mode 100644
index 0000000..15d86ce
--- /dev/null
+++ b/packaging/freeipa/Makefile.am
@@ -0,0 +1,3 @@
+include $(top_srcdir)/Makefile.python.am
+
+pkginstall = false
diff --git a/packaging/freeipa/README.txt b/packaging/freeipa/README.txt
new file mode 100644
index 0000000..b58448f
--- /dev/null
+++ b/packaging/freeipa/README.txt
@@ -0,0 +1,2 @@
+This is a dummy package for FreeIPA's ipaclient.
+
diff --git a/packaging/freeipa/setup.cfg b/packaging/freeipa/setup.cfg
new file mode 100644
index 0000000..62f65c7
--- /dev/null
+++ b/packaging/freeipa/setup.cfg
@@ -0,0 +1,6 @@
+[bdist_wheel]
+universal = 1
+
+[aliases]
+packages = clean --all egg_info bdist_wheel
+release = packages register upload
diff --git a/packaging/freeipa/setup.py b/packaging/freeipa/setup.py
new file mode 100755
index 0000000..9534c09
--- /dev/null
+++ b/packaging/freeipa/setup.py
@@ -0,0 +1,36 @@
+# Copyright (C) 2016  Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+"""Dummy package for FreeIPA
+
+Please install ipaclient instead.
+"""
+
+from os.path import abspath, dirname
+import sys
+
+if __name__ == '__main__':
+    # include ../../ for ipasetup.py
+    sys.path.append(dirname(dirname(dirname(abspath(__file__)))))
+    from ipasetup import ipasetup  # noqa: E402
+
+    ipasetup(
+        name='freeipa',
+        doc = __doc__,
+        install_requires=[
+            "ipaclient",
+        ]
+    )
diff --git a/packaging/ipa/Makefile.am b/packaging/ipa/Makefile.am
new file mode 100644
index 0000000..15d86ce
--- /dev/null
+++ b/packaging/ipa/Makefile.am
@@ -0,0 +1,3 @@
+include $(top_srcdir)/Makefile.python.am
+
+pkginstall = false
diff --git a/packaging/ipa/README.txt b/packaging/ipa/README.txt
new file mode 100644
index 0000000..b58448f
--- /dev/null
+++ b/packaging/ipa/README.txt
@@ -0,0 +1,2 @@
+This is a dummy package for FreeIPA's ipaclient.
+
diff --git a/packaging/ipa/setup.cfg b/packaging/ipa/setup.cfg
new file mode 100644
index 0000000..62f65c7
--- /dev/null
+++ b/packaging/ipa/setup.cfg
@@ -0,0 +1,6 @@
+[bdist_wheel]
+universal = 1
+
+[aliases]
+packages = clean --all egg_info bdist_wheel
+release = packages register upload
diff --git a/packaging/ipa/setup.py b/packaging/ipa/setup.py
new file mode 100755
index 0000000..16b3310
--- /dev/null
+++ b/packaging/ipa/setup.py
@@ -0,0 +1,36 @@
+# Copyright (C) 2016  Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+"""Dummy package for FreeIPA
+
+Please install ipaclient instead.
+"""
+
+from os.path import abspath, dirname
+import sys
+
+if __name__ == '__main__':
+    # include ../../ for ipasetup.py
+    sys.path.append(dirname(dirname(dirname(abspath(__file__)))))
+    from ipasetup import ipasetup  # noqa: E402
+
+    ipasetup(
+        name='ipa',
+        doc = __doc__,
+        install_requires=[
+            "ipaclient",
+        ]
+    )
diff --git a/packaging/ipacommands/MANIFEST.in b/packaging/ipacommands/MANIFEST.in
new file mode 100644
index 0000000..659a1f5
--- /dev/null
+++ b/packaging/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/packaging/ipacommands/Makefile.am b/packaging/ipacommands/Makefile.am
new file mode 100644
index 0000000..ef90ef1
--- /dev/null
+++ b/packaging/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
+	ln -sr $< $@
+
+client: | $(top_srcdir)/client
+	ln -sr $<
+
+util: | $(top_srcdir)/util
+	ln -sr $<
+
+# 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/packaging/ipacommands/setup.cfg b/packaging/ipacommands/setup.cfg
new file mode 100644
index 0000000..ccffb47
--- /dev/null
+++ b/packaging/ipacommands/setup.cfg
@@ -0,0 +1,5 @@
+[bdist_wheel]
+universal = 0
+
+[metadata]
+license_file = COPYING
diff --git a/packaging/ipacommands/setup.py b/packaging/ipacommands/setup.py
new file mode 100644
index 0000000..e50ee50
--- /dev/null
+++ b/packaging/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