1- #!/usr/bin/python
1+ #!/usr/bin/env python
22"""
33This script is used to build "official" universal installers on Mac OS X.
44It requires at least Mac OS X 10.4, Xcode 2.2 and the 10.4u SDK for
1212
1313Usage: see USAGE variable in the script.
1414"""
15- import platform , os , sys , getopt , textwrap , shutil , urllib2 , stat , time , pwd
16- import grp
15+ import platform , os , sys , getopt , textwrap , shutil , stat , time , pwd , grp
16+ try :
17+ import urllib2 as urllib_request
18+ except ImportError :
19+ import urllib .request as urllib_request
20+
21+ STAT_0o755 = ( stat .S_IRUSR | stat .S_IWUSR | stat .S_IXUSR
22+ | stat .S_IRGRP | stat .S_IXGRP
23+ | stat .S_IROTH | stat .S_IXOTH )
24+
25+ STAT_0o775 = ( stat .S_IRUSR | stat .S_IWUSR | stat .S_IXUSR
26+ | stat .S_IRGRP | stat .S_IWGRP | stat .S_IXGRP
27+ | stat .S_IROTH | stat .S_IXOTH )
1728
1829INCLUDE_TIMESTAMP = 1
1930VERBOSE = 1
2031
2132from plistlib import Plist
2233
23- import MacOS
24-
2534try :
2635 from plistlib import writePlist
2736except ImportError :
@@ -42,20 +51,35 @@ def grepValue(fn, variable):
4251 if ln .startswith (variable ):
4352 value = ln [len (variable ):].strip ()
4453 return value [1 :- 1 ]
45- raise RuntimeError , "Cannot find variable %s" % variable [:- 1 ]
54+ raise RuntimeError ("Cannot find variable %s" % variable [:- 1 ])
55+
56+ _cache_getVersion = None
4657
4758def getVersion ():
48- return grepValue (os .path .join (SRCDIR , 'configure' ), 'PACKAGE_VERSION' )
59+ global _cache_getVersion
60+ if _cache_getVersion is None :
61+ _cache_getVersion = grepValue (
62+ os .path .join (SRCDIR , 'configure' ), 'PACKAGE_VERSION' )
63+ return _cache_getVersion
4964
5065def getVersionTuple ():
5166 return tuple ([int (n ) for n in getVersion ().split ('.' )])
5267
68+ def getVersionMajorMinor ():
69+ return tuple ([int (n ) for n in getVersion ().split ('.' , 2 )])
70+
71+ _cache_getFullVersion = None
72+
5373def getFullVersion ():
74+ global _cache_getFullVersion
75+ if _cache_getFullVersion is not None :
76+ return _cache_getFullVersion
5477 fn = os .path .join (SRCDIR , 'Include' , 'patchlevel.h' )
5578 for ln in open (fn ):
5679 if 'PY_VERSION' in ln :
57- return ln .split ()[- 1 ][1 :- 1 ]
58- raise RuntimeError , "Cannot find full version??"
80+ _cache_getFullVersion = ln .split ()[- 1 ][1 :- 1 ]
81+ return _cache_getFullVersion
82+ raise RuntimeError ("Cannot find full version??" )
5983
6084# The directory we'll use to create the build (will be erased and recreated)
6185WORKDIR = "/tmp/_py"
@@ -369,7 +393,7 @@ def fileContents(fn):
369393 """
370394 Return the contents of the named file
371395 """
372- return open (fn , 'rb ' ).read ()
396+ return open (fn , 'r ' ).read ()
373397
374398def runCommand (commandline ):
375399 """
@@ -381,7 +405,7 @@ def runCommand(commandline):
381405 xit = fd .close ()
382406 if xit is not None :
383407 sys .stdout .write (data )
384- raise RuntimeError , "command failed: %s" % (commandline ,)
408+ raise RuntimeError ( "command failed: %s" % (commandline ,) )
385409
386410 if VERBOSE :
387411 sys .stdout .write (data ); sys .stdout .flush ()
@@ -392,7 +416,7 @@ def captureCommand(commandline):
392416 xit = fd .close ()
393417 if xit is not None :
394418 sys .stdout .write (data )
395- raise RuntimeError , "command failed: %s" % (commandline ,)
419+ raise RuntimeError ( "command failed: %s" % (commandline ,) )
396420
397421 return data
398422
@@ -461,12 +485,12 @@ def checkEnvironment():
461485 for ev in list (os .environ ):
462486 for prefix in environ_var_prefixes :
463487 if ev .startswith (prefix ) :
464- print "INFO: deleting environment variable %s=%s" % (
465- ev , os .environ [ev ])
488+ print ( "INFO: deleting environment variable %s=%s" % (
489+ ev , os .environ [ev ]))
466490 del os .environ [ev ]
467491
468492 os .environ ['PATH' ] = '/bin:/sbin:/usr/bin:/usr/sbin'
469- print "Setting default PATH: %s" % (os .environ ['PATH' ])
493+ print ( "Setting default PATH: %s" % (os .environ ['PATH' ]) )
470494
471495
472496def parseOptions (args = None ):
@@ -483,18 +507,18 @@ def parseOptions(args=None):
483507 options , args = getopt .getopt (args , '?hb' ,
484508 [ 'build-dir=' , 'third-party=' , 'sdk-path=' , 'src-dir=' ,
485509 'dep-target=' , 'universal-archs=' , 'help' ])
486- except getopt .error , msg :
487- print msg
510+ except getopt .GetoptError :
511+ print ( sys . exc_info ()[ 1 ])
488512 sys .exit (1 )
489513
490514 if args :
491- print "Additional arguments"
515+ print ( "Additional arguments" )
492516 sys .exit (1 )
493517
494518 deptarget = None
495519 for k , v in options :
496520 if k in ('-h' , '-?' , '--help' ):
497- print USAGE
521+ print ( USAGE )
498522 sys .exit (0 )
499523
500524 elif k in ('-d' , '--build-dir' ):
@@ -522,10 +546,10 @@ def parseOptions(args=None):
522546 # target
523547 DEPTARGET = default_target_map .get (v , '10.3' )
524548 else :
525- raise NotImplementedError , v
549+ raise NotImplementedError ( v )
526550
527551 else :
528- raise NotImplementedError , k
552+ raise NotImplementedError ( k )
529553
530554 SRCDIR = os .path .abspath (SRCDIR )
531555 WORKDIR = os .path .abspath (WORKDIR )
@@ -534,15 +558,15 @@ def parseOptions(args=None):
534558
535559 CC = target_cc_map [DEPTARGET ]
536560
537- print "Settings:"
538- print " * Source directory:" , SRCDIR
539- print " * Build directory: " , WORKDIR
540- print " * SDK location: " , SDKPATH
541- print " * Third-party source:" , DEPSRC
542- print " * Deployment target:" , DEPTARGET
543- print " * Universal architectures:" , ARCHLIST
544- print " * C compiler:" , CC
545- print ""
561+ print ( "Settings:" )
562+ print ( " * Source directory:" , SRCDIR )
563+ print ( " * Build directory: " , WORKDIR )
564+ print ( " * SDK location: " , SDKPATH )
565+ print ( " * Third-party source:" , DEPSRC )
566+ print ( " * Deployment target:" , DEPTARGET )
567+ print ( " * Universal architectures:" , ARCHLIST )
568+ print ( " * C compiler:" , CC )
569+ print ( "" )
546570
547571
548572
@@ -587,7 +611,7 @@ def extractArchive(builddir, archiveName):
587611 xit = fp .close ()
588612 if xit is not None :
589613 sys .stdout .write (data )
590- raise RuntimeError , "Cannot extract %s" % (archiveName ,)
614+ raise RuntimeError ( "Cannot extract %s" % (archiveName ,) )
591615
592616 return os .path .join (builddir , retval )
593617
@@ -609,9 +633,9 @@ def downloadURL(url, fname):
609633 pass
610634 else :
611635 if KNOWNSIZES .get (url ) == size :
612- print "Using existing file for" , url
636+ print ( "Using existing file for" , url )
613637 return
614- fpIn = urllib2 .urlopen (url )
638+ fpIn = urllib_request .urlopen (url )
615639 fpOut = open (fname , 'wb' )
616640 block = fpIn .read (10240 )
617641 try :
@@ -648,15 +672,15 @@ def buildRecipe(recipe, basedir, archList):
648672
649673
650674 if os .path .exists (sourceArchive ):
651- print "Using local copy of %s" % (name ,)
675+ print ( "Using local copy of %s" % (name ,) )
652676
653677 else :
654- print "Did not find local copy of %s" % (name ,)
655- print "Downloading %s" % (name ,)
678+ print ( "Did not find local copy of %s" % (name ,) )
679+ print ( "Downloading %s" % (name ,) )
656680 downloadURL (url , sourceArchive )
657- print "Archive for %s stored as %s" % (name , sourceArchive )
681+ print ( "Archive for %s stored as %s" % (name , sourceArchive ) )
658682
659- print "Extracting archive for %s" % (name ,)
683+ print ( "Extracting archive for %s" % (name ,) )
660684 buildDir = os .path .join (WORKDIR , '_bld' )
661685 if not os .path .exists (buildDir ):
662686 os .mkdir (buildDir )
@@ -722,24 +746,24 @@ def buildRecipe(recipe, basedir, archList):
722746 if 'configure_env' in recipe :
723747 configure_args .insert (0 , recipe ['configure_env' ])
724748
725- print "Running configure for %s" % (name ,)
749+ print ( "Running configure for %s" % (name ,) )
726750 runCommand (' ' .join (configure_args ) + ' 2>&1' )
727751
728- print "Running install for %s" % (name ,)
752+ print ( "Running install for %s" % (name ,) )
729753 runCommand ('{ ' + install + ' ;} 2>&1' )
730754
731- print "Done %s" % (name ,)
732- print ""
755+ print ( "Done %s" % (name ,) )
756+ print ( "" )
733757
734758 os .chdir (curdir )
735759
736760def buildLibraries ():
737761 """
738762 Build our dependencies into $WORKDIR/libraries/usr/local
739763 """
740- print ""
741- print "Building required libraries"
742- print ""
764+ print ( "" )
765+ print ( "Building required libraries" )
766+ print ( "" )
743767 universal = os .path .join (WORKDIR , 'libraries' )
744768 os .mkdir (universal )
745769 os .makedirs (os .path .join (universal , 'usr' , 'local' , 'lib' ))
@@ -753,7 +777,7 @@ def buildLibraries():
753777def buildPythonDocs ():
754778 # This stores the documentation as Resources/English.lproj/Documentation
755779 # inside the framwork. pydoc and IDLE will pick it up there.
756- print "Install python documentation"
780+ print ( "Install python documentation" )
757781 rootDir = os .path .join (WORKDIR , '_root' )
758782 buildDir = os .path .join ('../../Doc' )
759783 docdir = os .path .join (rootDir , 'pydocs' )
@@ -768,7 +792,7 @@ def buildPythonDocs():
768792
769793
770794def buildPython ():
771- print "Building a universal python for %s architectures" % UNIVERSALARCHS
795+ print ( "Building a universal python for %s architectures" % UNIVERSALARCHS )
772796
773797 buildDir = os .path .join (WORKDIR , '_bld' , 'python' )
774798 rootDir = os .path .join (WORKDIR , '_root' )
@@ -796,7 +820,7 @@ def buildPython():
796820 # will find them during its extension import sanity checks.
797821 os .environ ['DYLD_LIBRARY_PATH' ] = os .path .join (WORKDIR ,
798822 'libraries' , 'usr' , 'local' , 'lib' )
799- print "Running configure..."
823+ print ( "Running configure..." )
800824 runCommand ("%s -C --enable-framework --enable-universalsdk=%s "
801825 "--with-universal-archs=%s "
802826 "%s "
@@ -808,19 +832,19 @@ def buildPython():
808832 shellQuote (WORKDIR )[1 :- 1 ],
809833 shellQuote (WORKDIR )[1 :- 1 ]))
810834
811- print "Running make"
835+ print ( "Running make" )
812836 runCommand ("make" )
813837
814- print "Running make install"
838+ print ( "Running make install" )
815839 runCommand ("make install DESTDIR=%s" % (
816840 shellQuote (rootDir )))
817841
818- print "Running make frameworkinstallextras"
842+ print ( "Running make frameworkinstallextras" )
819843 runCommand ("make frameworkinstallextras DESTDIR=%s" % (
820844 shellQuote (rootDir )))
821845
822846 del os .environ ['DYLD_LIBRARY_PATH' ]
823- print "Copying required shared libraries"
847+ print ( "Copying required shared libraries" )
824848 if os .path .exists (os .path .join (WORKDIR , 'libraries' , 'Library' )):
825849 runCommand ("mv %s/* %s" % (
826850 shellQuote (os .path .join (
@@ -831,13 +855,13 @@ def buildPython():
831855 'Python.framework' , 'Versions' , getVersion (),
832856 'lib' ))))
833857
834- print "Fix file modes"
858+ print ( "Fix file modes" )
835859 frmDir = os .path .join (rootDir , 'Library' , 'Frameworks' , 'Python.framework' )
836860 gid = grp .getgrnam ('admin' ).gr_gid
837861
838862 for dirpath , dirnames , filenames in os .walk (frmDir ):
839863 for dn in dirnames :
840- os .chmod (os .path .join (dirpath , dn ), 0775 )
864+ os .chmod (os .path .join (dirpath , dn ), STAT_0o775 )
841865 os .chown (os .path .join (dirpath , dn ), - 1 , gid )
842866
843867
@@ -918,17 +942,17 @@ def patchFile(inPath, outPath):
918942
919943 # This one is not handy as a template variable
920944 data = data .replace ('$PYTHONFRAMEWORKINSTALLDIR' , '/Library/Frameworks/Python.framework' )
921- fp = open (outPath , 'wb ' )
945+ fp = open (outPath , 'w ' )
922946 fp .write (data )
923947 fp .close ()
924948
925949def patchScript (inPath , outPath ):
926950 data = fileContents (inPath )
927951 data = data .replace ('@PYVER@' , getVersion ())
928- fp = open (outPath , 'wb ' )
952+ fp = open (outPath , 'w ' )
929953 fp .write (data )
930954 fp .close ()
931- os .chmod (outPath , 0755 )
955+ os .chmod (outPath , STAT_0o755 )
932956
933957
934958
@@ -945,7 +969,7 @@ def packageFromRecipe(targetDir, recipe):
945969 readme = textwrap .dedent (recipe ['readme' ])
946970 isRequired = recipe .get ('required' , True )
947971
948- print "- building package %s" % (pkgname ,)
972+ print ( "- building package %s" % (pkgname ,) )
949973
950974 # Substitute some variables
951975 textvars = dict (
@@ -990,7 +1014,7 @@ def packageFromRecipe(targetDir, recipe):
9901014 patchScript (postflight , os .path .join (rsrcDir , 'postflight' ))
9911015
9921016 vers = getFullVersion ()
993- major , minor = map ( int , getVersion (). split ( '.' , 2 ) )
1017+ major , minor = getVersionMajorMinor ( )
9941018 pl = Plist (
9951019 CFBundleGetInfoString = "Python.%s %s" % (pkgname , vers ,),
9961020 CFBundleIdentifier = 'org.python.Python.%s' % (pkgname ,),
@@ -1027,7 +1051,7 @@ def packageFromRecipe(targetDir, recipe):
10271051def makeMpkgPlist (path ):
10281052
10291053 vers = getFullVersion ()
1030- major , minor = map ( int , getVersion (). split ( '.' , 2 ) )
1054+ major , minor = getVersionMajorMinor ( )
10311055
10321056 pl = Plist (
10331057 CFBundleGetInfoString = "Python %s" % (vers ,),
@@ -1209,7 +1233,7 @@ def main():
12091233
12101234 folder = os .path .join (WORKDIR , "_root" , "Applications" , "Python %s" % (
12111235 getVersion (),))
1212- os .chmod (folder , 0755 )
1236+ os .chmod (folder , STAT_0o755 )
12131237 setIcon (folder , "../Icons/Python Folder.icns" )
12141238
12151239 # Create the installer
@@ -1222,9 +1246,9 @@ def main():
12221246 shutil .copy ('../../LICENSE' , os .path .join (WORKDIR , 'installer' , 'License.txt' ))
12231247
12241248 fp = open (os .path .join (WORKDIR , 'installer' , 'Build.txt' ), 'w' )
1225- print >> fp , "# BUILD INFO"
1226- print >> fp , "# Date:" , time .ctime ()
1227- print >> fp , "# By:" , pwd .getpwuid (os .getuid ()).pw_gecos
1249+ fp . write ( "# BUILD INFO\n " )
1250+ fp . write ( "# Date: %s \n " % time .ctime () )
1251+ fp . write ( "# By: %s \n " % pwd .getpwuid (os .getuid ()).pw_gecos )
12281252 fp .close ()
12291253
12301254 # And copy it to a DMG
0 commit comments