1 """distutils.command.bdist_pkgtool
4 Author: Mark W. Alexander <slash@dotnet.net>
6 Implements the Distutils 'bdist_pkgtool' command (create Solaris pkgtool
11 from distutils
.util
import get_platform
12 from distutils
.file_util
import write_file
13 from distutils
.errors
import *
14 from distutils
.command
import bdist_packager
15 from distutils
import sysconfig
16 from distutils
import log
17 from commands
import getoutput
19 __revision__
= "$Id: bdist_pkgtool.py,v 0.3 mwa "
21 # default request script - Is also wrapped around user's request script
22 # unless --no-autorelocate is requested. Finds the python site-packages
23 # directory and prompts for verification
24 DEFAULT_REQUEST
="""#!/bin/sh
25 ######################################################################
26 # Distutils internal package relocation support #
27 ######################################################################
29 PRODUCT="__DISTUTILS_NAME__"
32 /usr/bin/which python 2>&1 >/dev/null
34 echo "The python interpretor needs to be on your path!"
36 echo "If you have more than one, make sure the first one is where"
37 echo "you want this module installed:"
41 PY_DIR=`python -c "import sys;print '%s/lib/python%s' % (sys.exec_prefix,sys.version[0:3])" 2>/dev/null`
42 PY_PKG_DIR=`python -c "import sys;print '%s/lib/python%s/site-packages' % (sys.exec_prefix,sys.version[0:3])" 2>/dev/null`
45 if [ -z "${PY_DIR}" ]; then
46 echo "I can't seem to find the python distribution."
47 echo "I'm assuming the default path for site-packages"
49 BASEDIR="${PY_PKG_DIR}"
51 Python found! The default path:
55 will install ${PRODUCT} such that all python users
58 If you just want individual access, you can install into
59 any directory and add that directory to your \$PYTHONPATH
64 BASEDIR=`ckpath -d ${BASEDIR} -ay \
65 -p "Where should ${PRODUCT} be installed? [${BASEDIR}]"` || exit $?
67 ######################################################################
68 # user supplied request script follows #
69 ######################################################################
73 # default postinstall compiles and changes ownership on the module directory
74 # XXX The ownership change _should_ be handled by adjusting the prototype file
75 DEFAULT_POSTINSTALL
="""#!/bin/sh
76 /usr/bin/test -d ${BASEDIR}/__DISTUTILS_NAME__
78 python -c "import compileall;compileall.compile_dir(\\"${BASEDIR}/__DISTUTILS_NAME__\\")"
79 chown -R root:other ${BASEDIR}/__DISTUTILS_NAME__
83 # default preremove deletes *.pyc and *.pyo from the module tree
84 # This should leave the tree empty, so pkgtool will remove it.
85 # If the user's scripts place anything else in there (e.g. config files),
86 # pkgtool will leave the directory intact
87 DEFAULT_PREREMOVE
="""#!/bin/sh
89 /usr/bin/which python 2>&1 >/dev/null
91 echo "The python interpretor needs to be on your path!"
93 echo "If you have more than one, make sure the first one is where"
94 echo "you want this module removed from"
98 /usr/bin/test -d ${BASEDIR}/__DISTUTILS_NAME__
100 find ${BASEDIR}/__DISTUTILS_NAME__ -name "*.pyc" -exec rm {} \;
101 find ${BASEDIR}/__DISTUTILS_NAME__ -name "*.pyo" -exec rm {} \;
105 # default postremove removes the module directory _IF_ no files are
106 # there (Turns out this isn't needed if the preremove does it's job
108 DEFAULT_POSTREMOVE
="""#!/bin/sh
110 /usr/bin/test -d ${BASEDIR}/__DISTUTILS_NAME__
111 if [ $? -eq 0 ]; then
112 if [ `find ${BASEDIR}/__DISTUTILS_NAME__ ! -type d | wc -l` -eq 0 ]; then
113 rm -rf ${BASEDIR}/__DISTUTILS_NAME__
118 class bdist_pkgtool (bdist_packager
.bdist_packager
):
120 description
= "create an pkgtool (Solaris) package"
122 user_options
= bdist_packager
.bdist_packager
.user_options
+ [
124 "package revision number (PSTAMP)"),
126 "Abbreviation (9 characters or less) of the package name"),
128 "file containing compatible versions of this package (man compver)"),
130 "file containing dependencies for this package (man depend)"),
132 #"Software category"),
134 "request script (Bourne shell code)"),
137 def initialize_options (self
):
138 # XXX Check for pkgtools on path...
139 bdist_packager
.bdist_packager
.initialize_options(self
)
145 self
.pkg_abrev
= None
147 # I'm not sure I should need to do this, but the setup.cfg
148 # settings weren't showing up....
149 options
= self
.distribution
.get_option_dict('bdist_packager')
150 for key
in options
.keys():
151 setattr(self
,key
,options
[key
][1])
153 # initialize_options()
156 def finalize_options (self
):
157 global DEFAULT_REQUEST
, DEFAULT_POSTINSTALL
158 global DEFAULT_PREREMOVE
, DEFAULT_POSTREMOVE
159 if self
.pkg_dir
is None:
160 dist_dir
= self
.get_finalized_command('bdist').dist_dir
161 self
.pkg_dir
= os
.path
.join(dist_dir
, "pkgtool")
163 self
.ensure_string('classes', None)
164 self
.ensure_string('revision', "1")
165 self
.ensure_script('request')
166 self
.ensure_script('preinstall')
167 self
.ensure_script('postinstall')
168 self
.ensure_script('preremove')
169 self
.ensure_script('postremove')
170 self
.ensure_string('vendor', None)
171 if self
.__dict
__.has_key('author'):
172 if self
.__dict
__.has_key('author_email'):
173 self
.ensure_string('vendor',
174 "%s <%s>" % (self
.author
,
177 self
.ensure_string('vendor',
178 "%s" % (self
.author
))
179 self
.ensure_string('category', "System,application")
180 self
.ensure_script('compver')
181 self
.ensure_script('depend')
182 bdist_packager
.bdist_packager
.finalize_options(self
)
183 if self
.pkg_abrev
is None:
184 self
.pkg_abrev
=self
.name
185 if len(self
.pkg_abrev
)>9:
186 raise DistutilsOptionError
, \
187 "pkg-abrev (%s) must be less than 9 characters" % self
.pkg_abrev
188 # Update default scripts with our metadata name
189 DEFAULT_REQUEST
= string
.replace(DEFAULT_REQUEST
,
190 "__DISTUTILS_NAME__", self
.root_package
)
191 DEFAULT_POSTINSTALL
= string
.replace(DEFAULT_POSTINSTALL
,
192 "__DISTUTILS_NAME__", self
.root_package
)
193 DEFAULT_PREREMOVE
= string
.replace(DEFAULT_PREREMOVE
,
194 "__DISTUTILS_NAME__", self
.root_package
)
195 DEFAULT_POSTREMOVE
= string
.replace(DEFAULT_POSTREMOVE
,
196 "__DISTUTILS_NAME__", self
.root_package
)
201 def make_package(self
,root
=None):
203 self
.mkpath(self
.pkg_dir
)
205 pkg_dir
= self
.pkg_dir
+"/"+root
208 pkg_dir
= self
.pkg_dir
210 self
.reinitialize_command('install', reinit_subcommands
=1)
212 log
.info('Building package')
213 self
.run_command('build')
214 log
.info('Creating pkginfo file')
215 path
= os
.path
.join(pkg_dir
, "pkginfo")
216 self
.execute(write_file
,
218 self
._make
_info
_file
()),
219 "writing '%s'" % path
)
220 # request script handling
221 if self
.request
==None:
222 self
.request
= self
._make
_request
_script
()
224 path
= os
.path
.join(pkg_dir
, "request")
225 self
.execute(write_file
,
228 "writing '%s'" % path
)
230 # Create installation scripts, since compver & depend are
231 # user created files, they work just fine as scripts
232 self
.write_script(os
.path
.join(pkg_dir
, "postinstall"),
233 'postinstall',DEFAULT_POSTINSTALL
)
234 self
.write_script(os
.path
.join(pkg_dir
, "preinstall"),
236 self
.write_script(os
.path
.join(pkg_dir
, "preremove"),
237 'preremove',DEFAULT_PREREMOVE
)
238 self
.write_script(os
.path
.join(pkg_dir
, "postremove"),
240 self
.write_script(os
.path
.join(pkg_dir
, "compver"),
242 self
.write_script(os
.path
.join(pkg_dir
, "depend"),
245 log
.info('Creating prototype file')
246 path
= os
.path
.join(pkg_dir
, "prototype")
247 self
.execute(write_file
,
249 self
._make
_prototype
()),
250 "writing '%s'" % path
)
253 if self
.control_only
: # stop if requested
257 log
.info('Creating package')
258 pkg_cmd
= ['pkgmk', '-o', '-f']
261 pkg_cmd
.append(os
.environ
['PWD'])
263 pkg_cmd
= ['pkgtrans', '-s', '/var/spool/pkg']
264 path
= os
.path
.join(os
.environ
['PWD'],pkg_dir
,
265 self
.get_binary_name() + ".pkg")
266 log
.info('Transferring package to ' + pkg_dir
)
268 pkg_cmd
.append(self
.pkg_abrev
)
270 os
.system("rm -rf /var/spool/pkg/%s" % self
.pkg_abrev
)
275 self
.subpackages
=string
.split(self
.subpackages
,",")
276 for pkg
in self
.subpackages
:
277 self
.make_package(pkg
)
283 def _make_prototype(self
):
285 proto_file
= ["i pkginfo"]
287 proto_file
.extend(['i request'])
289 proto_file
.extend(['i postinstall'])
291 proto_file
.extend(['i postremove'])
293 proto_file
.extend(['i preinstall'])
295 proto_file
.extend(['i preremove'])
297 proto_file
.extend(['i compver'])
299 proto_file
.extend(['i depend'])
300 proto_file
.extend(['!default 644 root bin'])
301 build
= self
.get_finalized_command('build')
304 self
.distribution
.packages
[0]
305 file_list
=string
.split(
306 getoutput("pkgproto %s/%s=%s" % \
307 (build
.build_lib
, self
.distribution
.packages
[0],
308 self
.distribution
.packages
[0])), "\012")
310 file_list
=string
.split(
311 getoutput("pkgproto %s=" % (build
.build_lib
)),"\012")
312 ownership
="%s %s" % (pwd
.getpwuid(os
.getuid())[0],
313 grp
.getgrgid(os
.getgid())[0])
314 for i
in range(len(file_list
)):
315 file_list
[i
] = string
.replace(file_list
[i
],ownership
,"root bin")
316 proto_file
.extend(file_list
)
319 def _make_request_script(self
):
320 global DEFAULT_REQUEST
321 # A little different from other scripts, if we are to automatically
322 # relocate to the target site-packages, we have to wrap any provided
323 # script with the autorelocation script. If no script is provided,
324 # The request script will simply be the autorelocate script
325 if self
.no_autorelocate
==0:
326 request
=string
.split(DEFAULT_REQUEST
,"\012")
328 log
.info('Creating relocation request script')
330 users_request
=self
.get_script('request')
331 if users_request
!=None and users_request
!=[]:
332 if self
.no_autorelocate
==0 and users_request
[0][0:2]=="#!":
333 users_request
.remove(users_request
[0])
334 for i
in users_request
:
337 if self
.no_autorelocate
==0:
338 request
.append("#############################################")
339 request
.append("# finalize relocation support #")
340 request
.append("#############################################")
341 request
.append('echo "BASEDIR=\\"${BASEDIR}\\"" >>$1')
344 def _make_info_file(self
):
345 """Generate the text of a pkgtool info file and return it as a
346 list of strings (one per line).
348 # definitions and headers
349 # PKG must be alphanumeric, < 9 characters
351 'PKG="%s"' % self
.pkg_abrev
,
352 'NAME="%s"' % self
.name
,
353 'VERSION="%s"' % self
.version
,
354 'PSTAMP="%s"' % self
.revision
,
356 info_file
.extend(['VENDOR="%s (%s)"' % (self
.distribution
.maintainer
, \
357 self
.distribution
.license
) ])
358 info_file
.extend(['EMAIL="%s"' % self
.distribution
.maintainer_email
])
360 p
= self
.distribution
.get_platforms()
361 if p
is None or p
==['UNKNOWN']:
362 archs
=getoutput('uname -p')
364 archs
=string
.join(self
.distribution
.get_platforms(),',')
366 #print "Assuming a sparc architecure"
368 info_file
.extend(['ARCH="%s"' % archs
])
370 if self
.distribution
.get_url():
371 info_file
.extend(['HOTLINE="%s"' % self
.distribution
.get_url() ])
373 info_file
.extend(['CLASSES="%s"' % self
.classes
])
375 info_file
.extend(['CATEGORY="%s"' % self
.category
])
378 if i
[-13:]=="site-packages":
382 info_file
.extend(['BASEDIR="%s"' % site
])
388 def _format_changelog(self
, changelog
):
389 """Format the changelog correctly and convert it to a list of strings
394 for line
in string
.split(string
.strip(changelog
), '\n'):
395 line
= string
.strip(line
)
397 new_changelog
.extend(['', line
])
399 new_changelog
.append(line
)
401 new_changelog
.append(' ' + line
)
403 # strip trailing newline inserted by first changelog entry
404 if not new_changelog
[0]:
409 # _format_changelog()