~ubuntu-branches/ubuntu/lucid/python2.6/lucid

« back to all changes in this revision

Viewing changes to Mac/BuildScript/build-installer.py

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2009-04-08 02:29:05 UTC
  • mto: (10.1.3 experimental)
  • mto: This revision was merged to the branch mainline in revision 23.
  • Revision ID: james.westby@ubuntu.com-20090408022905-xa5zbe8821m2o77o
Tags: upstream-2.6.2~rc1
ImportĀ upstreamĀ versionĀ 2.6.2~rc1

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/python2.3
 
1
#!/usr/bin/python
2
2
"""
3
3
This script is used to build the "official unofficial" universal build on
4
4
Mac OS X. It requires Mac OS X 10.4, Xcode 2.2 and the 10.4u SDK to do its
5
 
work.
 
5
work.  64-bit or four-way universal builds require at least OS X 10.5 and
 
6
the 10.5 SDK.
6
7
 
7
8
Please ensure that this script keeps working with Python 2.3, to avoid
8
9
bootstrap issues (/usr/bin/python is Python 2.3 on OSX 10.4)
18
19
from plistlib import Plist
19
20
 
20
21
import MacOS
21
 
import Carbon.File
22
 
import Carbon.Icn
23
 
import Carbon.Res
24
 
from Carbon.Files import kCustomIconResource, fsRdWrPerm, kHasCustomIcon
25
 
from Carbon.Files import kFSCatInfoFinderInfo
26
22
 
27
23
try:
28
24
    from plistlib import writePlist
68
64
SDKPATH = "/Developer/SDKs/MacOSX10.4u.sdk"
69
65
#SDKPATH = "/"
70
66
 
71
 
ARCHLIST = ('i386', 'ppc',)
 
67
universal_opts_map = { '32-bit': ('i386', 'ppc',),
 
68
                       '64-bit': ('x86_64', 'ppc64',),
 
69
                       'all': ('i386', 'ppc', 'x86_64', 'ppc64',) }
 
70
 
 
71
UNIVERSALOPTS = tuple(universal_opts_map.keys())
 
72
 
 
73
UNIVERSALARCHS = '32-bit'
 
74
 
 
75
ARCHLIST = universal_opts_map[UNIVERSALARCHS]
72
76
 
73
77
# Source directory (asume we're in Mac/BuildScript)
74
78
SRCDIR = os.path.dirname(
77
81
                os.path.abspath(__file__
78
82
        ))))
79
83
 
 
84
# $MACOSX_DEPLOYMENT_TARGET -> minimum OS X level
 
85
DEPTARGET = '10.3'
 
86
 
80
87
USAGE = textwrap.dedent("""\
81
88
    Usage: build_python [options]
82
89
 
87
94
    --third-party=DIR:   Store third-party sources here (default: %(DEPSRC)r)
88
95
    --sdk-path=DIR:      Location of the SDK (default: %(SDKPATH)r)
89
96
    --src-dir=DIR:       Location of the Python sources (default: %(SRCDIR)r)
 
97
    --dep-target=10.n    OS X deployment target (default: %(DEPTARGET)r)
 
98
    --universal-archs=x  universal architectures (options: %(UNIVERSALOPTS)r, default: %(UNIVERSALARCHS)r)
90
99
""")% globals()
91
100
 
92
101
 
93
102
# Instructions for building libraries that are necessary for building a
94
103
# batteries included python.
95
 
LIBRARY_RECIPES = [
96
 
    dict(
97
 
        name="Bzip2 1.0.3",
98
 
        url="http://www.bzip.org/1.0.3/bzip2-1.0.3.tar.gz",
99
 
        configure=None,
100
 
        install='make install PREFIX=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
101
 
            shellQuote(os.path.join(WORKDIR, 'libraries')),
102
 
            ' -arch '.join(ARCHLIST),
103
 
            SDKPATH,
104
 
        ),
105
 
    ),
106
 
    dict(
107
 
        name="ZLib 1.2.3",
108
 
        url="http://www.gzip.org/zlib/zlib-1.2.3.tar.gz",
109
 
        configure=None,
110
 
        install='make install prefix=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
111
 
            shellQuote(os.path.join(WORKDIR, 'libraries')),
112
 
            ' -arch '.join(ARCHLIST),
113
 
            SDKPATH,
114
 
        ),
115
 
    ),
116
 
    dict(
117
 
        # Note that GNU readline is GPL'd software
118
 
        name="GNU Readline 5.1.4",
119
 
        url="http://ftp.gnu.org/pub/gnu/readline/readline-5.1.tar.gz" ,
120
 
        patchlevel='0',
121
 
        patches=[
122
 
            # The readline maintainers don't do actual micro releases, but
123
 
            # just ship a set of patches.
124
 
            'http://ftp.gnu.org/pub/gnu/readline/readline-5.1-patches/readline51-001',
125
 
            'http://ftp.gnu.org/pub/gnu/readline/readline-5.1-patches/readline51-002',
126
 
            'http://ftp.gnu.org/pub/gnu/readline/readline-5.1-patches/readline51-003',
127
 
            'http://ftp.gnu.org/pub/gnu/readline/readline-5.1-patches/readline51-004',
128
 
        ]
129
 
    ),
130
 
 
131
 
    dict(
132
 
        name="SQLite 3.6.3",
133
 
        url="http://www.sqlite.org/sqlite-3.6.3.tar.gz",
134
 
        checksum='93f742986e8bc2dfa34792e16df017a6feccf3a2',
135
 
        configure_pre=[
136
 
            '--enable-threadsafe',
137
 
            '--enable-tempstore',
138
 
            '--enable-shared=no',
139
 
            '--enable-static=yes',
140
 
            '--disable-tcl',
141
 
        ]
142
 
    ),
143
 
 
144
 
    dict(
145
 
        name="NCurses 5.5",
146
 
        url="http://ftp.gnu.org/pub/gnu/ncurses/ncurses-5.5.tar.gz",
147
 
        configure_pre=[
148
 
            "--without-cxx",
149
 
            "--without-ada",
150
 
            "--without-progs",
151
 
            "--without-curses-h",
152
 
            "--enable-shared",
153
 
            "--with-shared",
154
 
            "--datadir=/usr/share",
155
 
            "--sysconfdir=/etc",
156
 
            "--sharedstatedir=/usr/com",
157
 
            "--with-terminfo-dirs=/usr/share/terminfo",
158
 
            "--with-default-terminfo-dir=/usr/share/terminfo",
159
 
            "--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib"%(getVersion(),),
160
 
            "--enable-termcap",
161
 
        ],
162
 
        patches=[
163
 
            "ncurses-5.5.patch",
164
 
        ],
165
 
        useLDFlags=False,
166
 
        install='make && make install DESTDIR=%s && cd %s/usr/local/lib && ln -fs ../../../Library/Frameworks/Python.framework/Versions/%s/lib/lib* .'%(
167
 
            shellQuote(os.path.join(WORKDIR, 'libraries')),
168
 
            shellQuote(os.path.join(WORKDIR, 'libraries')),
169
 
            getVersion(),
170
 
            ),
171
 
    ),
172
 
    dict(
173
 
        name="Sleepycat DB 4.7.25",
174
 
        url="http://download.oracle.com/berkeley-db/db-4.7.25.tar.gz",
175
 
        #name="Sleepycat DB 4.3.29",
176
 
        #url="http://downloads.sleepycat.com/db-4.3.29.tar.gz",
177
 
        buildDir="build_unix",
178
 
        configure="../dist/configure",
179
 
        configure_pre=[
180
 
            '--includedir=/usr/local/include/db4',
181
 
        ]
182
 
    ),
183
 
]
 
104
#   [The recipes are defined here for convenience but instantiated later after
 
105
#    command line options have been processed.]
 
106
def library_recipes():
 
107
    return [
 
108
      dict(
 
109
          name="Bzip2 1.0.4",
 
110
          url="http://www.bzip.org/1.0.4/bzip2-1.0.4.tar.gz",
 
111
          checksum='fc310b254f6ba5fbb5da018f04533688',
 
112
          configure=None,
 
113
          install='make install PREFIX=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
 
114
              shellQuote(os.path.join(WORKDIR, 'libraries')),
 
115
              ' -arch '.join(ARCHLIST),
 
116
              SDKPATH,
 
117
          ),
 
118
      ),
 
119
      dict(
 
120
          name="ZLib 1.2.3",
 
121
          url="http://www.gzip.org/zlib/zlib-1.2.3.tar.gz",
 
122
          checksum='debc62758716a169df9f62e6ab2bc634',
 
123
          configure=None,
 
124
          install='make install prefix=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
 
125
              shellQuote(os.path.join(WORKDIR, 'libraries')),
 
126
              ' -arch '.join(ARCHLIST),
 
127
              SDKPATH,
 
128
          ),
 
129
      ),
 
130
      dict(
 
131
          # Note that GNU readline is GPL'd software
 
132
          name="GNU Readline 5.1.4",
 
133
          url="http://ftp.gnu.org/pub/gnu/readline/readline-5.1.tar.gz" ,
 
134
          checksum='7ee5a692db88b30ca48927a13fd60e46',
 
135
          patchlevel='0',
 
136
          patches=[
 
137
              # The readline maintainers don't do actual micro releases, but
 
138
              # just ship a set of patches.
 
139
              'http://ftp.gnu.org/pub/gnu/readline/readline-5.1-patches/readline51-001',
 
140
              'http://ftp.gnu.org/pub/gnu/readline/readline-5.1-patches/readline51-002',
 
141
              'http://ftp.gnu.org/pub/gnu/readline/readline-5.1-patches/readline51-003',
 
142
              'http://ftp.gnu.org/pub/gnu/readline/readline-5.1-patches/readline51-004',
 
143
          ]
 
144
      ),
 
145
 
 
146
      dict(
 
147
          name="SQLite 3.6.11",
 
148
          url="http://www.sqlite.org/sqlite-3.6.11.tar.gz",
 
149
          checksum='7ebb099696ab76cc6ff65dd496d17858',
 
150
          configure_pre=[
 
151
              '--enable-threadsafe',
 
152
              '--enable-tempstore',
 
153
              '--enable-shared=no',
 
154
              '--enable-static=yes',
 
155
              '--disable-tcl',
 
156
          ]
 
157
      ),
 
158
 
 
159
      dict(
 
160
          name="NCurses 5.5",
 
161
          url="http://ftp.gnu.org/pub/gnu/ncurses/ncurses-5.5.tar.gz",
 
162
          checksum='e73c1ac10b4bfc46db43b2ddfd6244ef',
 
163
          configure_pre=[
 
164
              "--without-cxx",
 
165
              "--without-ada",
 
166
              "--without-progs",
 
167
              "--without-curses-h",
 
168
              "--enable-shared",
 
169
              "--with-shared",
 
170
              "--datadir=/usr/share",
 
171
              "--sysconfdir=/etc",
 
172
              "--sharedstatedir=/usr/com",
 
173
              "--with-terminfo-dirs=/usr/share/terminfo",
 
174
              "--with-default-terminfo-dir=/usr/share/terminfo",
 
175
              "--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib"%(getVersion(),),
 
176
              "--enable-termcap",
 
177
          ],
 
178
          patches=[
 
179
              "ncurses-5.5.patch",
 
180
          ],
 
181
          useLDFlags=False,
 
182
          install='make && make install DESTDIR=%s && cd %s/usr/local/lib && ln -fs ../../../Library/Frameworks/Python.framework/Versions/%s/lib/lib* .'%(
 
183
              shellQuote(os.path.join(WORKDIR, 'libraries')),
 
184
              shellQuote(os.path.join(WORKDIR, 'libraries')),
 
185
              getVersion(),
 
186
              ),
 
187
      ),
 
188
      dict(
 
189
          name="Sleepycat DB 4.7.25",
 
190
          url="http://download.oracle.com/berkeley-db/db-4.7.25.tar.gz",
 
191
          checksum='ec2b87e833779681a0c3a814aa71359e',
 
192
          buildDir="build_unix",
 
193
          configure="../dist/configure",
 
194
          configure_pre=[
 
195
              '--includedir=/usr/local/include/db4',
 
196
          ]
 
197
      ),
 
198
    ]
184
199
 
185
200
 
186
201
# Instructions for building packages inside the .mpkg.
215
230
        source="/usr/local/bin",
216
231
        readme="""\
217
232
            This package installs the unix tools in /usr/local/bin for
218
 
            compatibility with older releases of MacPython. This package
219
 
            is not necessary to use MacPython.
 
233
            compatibility with older releases of Python. This package
 
234
            is not necessary to use Python.
220
235
            """,
221
236
        required=False,
222
237
    ),
239
254
        long_name="Shell profile updater",
240
255
        readme="""\
241
256
            This packages updates your shell profile to make sure that
242
 
            the MacPython tools are found by your shell in preference of
 
257
            the Python tools are found by your shell in preference of
243
258
            the system provided Python tools.
244
259
 
245
260
            If you don't install this package you'll have to add
327
342
    """
328
343
    Parse arguments and update global settings.
329
344
    """
330
 
    global WORKDIR, DEPSRC, SDKPATH, SRCDIR
 
345
    global WORKDIR, DEPSRC, SDKPATH, SRCDIR, DEPTARGET
 
346
    global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST
331
347
 
332
348
    if args is None:
333
349
        args = sys.argv[1:]
334
350
 
335
351
    try:
336
352
        options, args = getopt.getopt(args, '?hb',
337
 
                [ 'build-dir=', 'third-party=', 'sdk-path=' , 'src-dir='])
 
353
                [ 'build-dir=', 'third-party=', 'sdk-path=' , 'src-dir=',
 
354
                  'dep-target=', 'universal-archs=', 'help' ])
338
355
    except getopt.error, msg:
339
356
        print msg
340
357
        sys.exit(1)
344
361
        sys.exit(1)
345
362
 
346
363
    for k, v in options:
347
 
        if k in ('-h', '-?'):
 
364
        if k in ('-h', '-?', '--help'):
348
365
            print USAGE
349
366
            sys.exit(0)
350
367
 
360
377
        elif k in ('--src-dir',):
361
378
            SRCDIR=v
362
379
 
 
380
        elif k in ('--dep-target', ):
 
381
            DEPTARGET=v
 
382
 
 
383
        elif k in ('--universal-archs', ):
 
384
            if v in UNIVERSALOPTS:
 
385
                UNIVERSALARCHS = v
 
386
                ARCHLIST = universal_opts_map[UNIVERSALARCHS]
 
387
            else:
 
388
                raise NotImplementedError, v
 
389
 
363
390
        else:
364
391
            raise NotImplementedError, k
365
392
 
372
399
    print " * Source directory:", SRCDIR
373
400
    print " * Build directory: ", WORKDIR
374
401
    print " * SDK location:    ", SDKPATH
375
 
    print " * third-party source:", DEPSRC
 
402
    print " * Third-party source:", DEPSRC
 
403
    print " * Deployment target:", DEPTARGET
 
404
    print " * Universal architectures:", ARCHLIST
376
405
    print ""
377
406
 
378
407
 
482
511
        print "Using local copy of %s"%(name,)
483
512
 
484
513
    else:
 
514
        print "Did not find local copy of %s"%(name,)
485
515
        print "Downloading %s"%(name,)
486
516
        downloadURL(url, sourceArchive)
487
517
        print "Archive for %s stored as %s"%(name, sourceArchive)
572
602
    os.makedirs(os.path.join(universal, 'usr', 'local', 'lib'))
573
603
    os.makedirs(os.path.join(universal, 'usr', 'local', 'include'))
574
604
 
575
 
    for recipe in LIBRARY_RECIPES:
 
605
    for recipe in library_recipes():
576
606
        buildRecipe(recipe, universal, ARCHLIST)
577
607
 
578
608
 
579
609
 
580
610
def buildPythonDocs():
581
 
    # This stores the documentation as Resources/English.lproj/Docuentation
 
611
    # This stores the documentation as Resources/English.lproj/Documentation
582
612
    # inside the framwork. pydoc and IDLE will pick it up there.
583
613
    print "Install python documentation"
584
614
    rootDir = os.path.join(WORKDIR, '_root')
585
 
    version = getVersion()
 
615
    buildDir = os.path.join('../../Doc')
586
616
    docdir = os.path.join(rootDir, 'pydocs')
587
 
 
588
 
    novername = 'python-docs-html.tar.bz2'
589
 
    name = 'html-%s.tar.bz2'%(getFullVersion(),)
590
 
    sourceArchive = os.path.join(DEPSRC, name)
591
 
    if os.path.exists(sourceArchive):
592
 
        print "Using local copy of %s"%(name,)
593
 
 
594
 
    else:
595
 
        print "Downloading %s"%(novername,)
596
 
        downloadURL('http://www.python.org/ftp/python/doc/%s/%s'%(
597
 
            getFullVersion(), novername), sourceArchive)
598
 
        print "Archive for %s stored as %s"%(name, sourceArchive)
599
 
 
600
 
    extractArchive(os.path.dirname(docdir), sourceArchive)
601
 
 
602
 
    os.rename(
603
 
            os.path.join(
604
 
                os.path.dirname(docdir), 'python-docs-html'),
605
 
            docdir)
 
617
    curDir = os.getcwd()
 
618
    os.chdir(buildDir)
 
619
    runCommand('make update')
 
620
    runCommand('make html')
 
621
    os.chdir(curDir)
 
622
    if not os.path.exists(docdir):
 
623
        os.mkdir(docdir)
 
624
    os.rename(os.path.join(buildDir, 'build', 'html'),
 
625
            os.path.join(docdir, 'python-docs-html'))
606
626
 
607
627
 
608
628
def buildPython():
609
 
    print "Building a universal python"
 
629
    print "Building a universal python for %s architectures" % UNIVERSALARCHS
610
630
 
611
631
    buildDir = os.path.join(WORKDIR, '_bld', 'python')
612
632
    rootDir = os.path.join(WORKDIR, '_root')
629
649
    # several paths.
630
650
    version = getVersion()
631
651
 
 
652
    # Since the extra libs are not in their installed framework location
 
653
    # during the build, augment the library path so that the interpreter
 
654
    # will find them during its extension import sanity checks.
 
655
    os.environ['DYLD_LIBRARY_PATH'] = os.path.join(WORKDIR,
 
656
                                        'libraries', 'usr', 'local', 'lib')
632
657
    print "Running configure..."
633
 
    runCommand("%s -C --enable-framework --enable-universalsdk=%s LDFLAGS='-g -L%s/libraries/usr/local/lib' OPT='-g -O3 -I%s/libraries/usr/local/include' 2>&1"%(
634
 
        shellQuote(os.path.join(SRCDIR, 'configure')),
635
 
        shellQuote(SDKPATH), shellQuote(WORKDIR)[1:-1],
 
658
    runCommand("%s -C --enable-framework --enable-universalsdk=%s "
 
659
               "--with-universal-archs=%s "
 
660
               "LDFLAGS='-g -L%s/libraries/usr/local/lib' "
 
661
               "OPT='-g -O3 -I%s/libraries/usr/local/include' 2>&1"%(
 
662
        shellQuote(os.path.join(SRCDIR, 'configure')), shellQuote(SDKPATH),
 
663
        UNIVERSALARCHS,
 
664
        shellQuote(WORKDIR)[1:-1],
636
665
        shellQuote(WORKDIR)[1:-1]))
637
666
 
638
667
    print "Running make"
646
675
    runCommand("make frameworkinstallextras DESTDIR=%s"%(
647
676
        shellQuote(rootDir)))
648
677
 
 
678
    del os.environ['DYLD_LIBRARY_PATH']
649
679
    print "Copying required shared libraries"
650
680
    if os.path.exists(os.path.join(WORKDIR, 'libraries', 'Library')):
651
681
        runCommand("mv %s/* %s"%(
713
743
    data = fileContents(inPath)
714
744
    data = data.replace('$FULL_VERSION', getFullVersion())
715
745
    data = data.replace('$VERSION', getVersion())
716
 
    data = data.replace('$MACOSX_DEPLOYMENT_TARGET', '10.3 or later')
 
746
    data = data.replace('$MACOSX_DEPLOYMENT_TARGET', ''.join((DEPTARGET, ' or later')))
717
747
    data = data.replace('$ARCHITECTURES', "i386, ppc")
718
748
    data = data.replace('$INSTALL_SIZE', installSize())
719
749
 
793
823
        vers = getFullVersion()
794
824
        major, minor = map(int, getVersion().split('.', 2))
795
825
        pl = Plist(
796
 
                CFBundleGetInfoString="MacPython.%s %s"%(pkgname, vers,),
797
 
                CFBundleIdentifier='org.python.MacPython.%s'%(pkgname,),
798
 
                CFBundleName='MacPython.%s'%(pkgname,),
 
826
                CFBundleGetInfoString="Python.%s %s"%(pkgname, vers,),
 
827
                CFBundleIdentifier='org.python.Python.%s'%(pkgname,),
 
828
                CFBundleName='Python.%s'%(pkgname,),
799
829
                CFBundleShortVersionString=vers,
800
830
                IFMajorVersion=major,
801
831
                IFMinorVersion=minor,
816
846
 
817
847
        pl = Plist(
818
848
                    IFPkgDescriptionDescription=readme,
819
 
                    IFPkgDescriptionTitle=recipe.get('long_name', "MacPython.%s"%(pkgname,)),
 
849
                    IFPkgDescriptionTitle=recipe.get('long_name', "Python.%s"%(pkgname,)),
820
850
                    IFPkgDescriptionVersion=vers,
821
851
                )
822
852
        writePlist(pl, os.path.join(packageContents, 'Resources', 'Description.plist'))
831
861
    major, minor = map(int, getVersion().split('.', 2))
832
862
 
833
863
    pl = Plist(
834
 
            CFBundleGetInfoString="MacPython %s"%(vers,),
835
 
            CFBundleIdentifier='org.python.MacPython',
836
 
            CFBundleName='MacPython',
 
864
            CFBundleGetInfoString="Python %s"%(vers,),
 
865
            CFBundleIdentifier='org.python.Python',
 
866
            CFBundleName='Python',
837
867
            CFBundleShortVersionString=vers,
838
868
            IFMajorVersion=major,
839
869
            IFMinorVersion=minor,
867
897
        shutil.rmtree(outdir)
868
898
    os.mkdir(outdir)
869
899
 
870
 
    pkgroot = os.path.join(outdir, 'MacPython.mpkg', 'Contents')
 
900
    pkgroot = os.path.join(outdir, 'Python.mpkg', 'Contents')
871
901
    pkgcontents = os.path.join(pkgroot, 'Packages')
872
902
    os.makedirs(pkgcontents)
873
903
    for recipe in PKG_RECIPES:
884
914
 
885
915
    makeMpkgPlist(os.path.join(pkgroot, 'Info.plist'))
886
916
    pl = Plist(
887
 
                IFPkgDescriptionTitle="Universal MacPython",
 
917
                IFPkgDescriptionTitle="Python",
888
918
                IFPkgDescriptionVersion=getVersion(),
889
919
            )
890
920
 
924
954
    imagepath = imagepath + '.dmg'
925
955
 
926
956
    os.mkdir(outdir)
927
 
    runCommand("hdiutil create -volname 'Universal MacPython %s' -srcfolder %s %s"%(
928
 
            getFullVersion(),
 
957
    volname='Python %s'%(getFullVersion())
 
958
    runCommand("hdiutil create -format UDRW -volname %s -srcfolder %s %s"%(
 
959
            shellQuote(volname),
929
960
            shellQuote(os.path.join(WORKDIR, 'installer')),
930
 
            shellQuote(imagepath)))
 
961
            shellQuote(imagepath + ".tmp.dmg" )))
 
962
 
 
963
 
 
964
    if not os.path.exists(os.path.join(WORKDIR, "mnt")):
 
965
        os.mkdir(os.path.join(WORKDIR, "mnt"))
 
966
    runCommand("hdiutil attach %s -mountroot %s"%(
 
967
        shellQuote(imagepath + ".tmp.dmg"), shellQuote(os.path.join(WORKDIR, "mnt"))))
 
968
 
 
969
    # Custom icon for the DMG, shown when the DMG is mounted.
 
970
    shutil.copy("../Icons/Disk Image.icns",
 
971
            os.path.join(WORKDIR, "mnt", volname, ".VolumeIcon.icns"))
 
972
    runCommand("/Developer/Tools/SetFile -a C %s/"%(
 
973
            shellQuote(os.path.join(WORKDIR, "mnt", volname)),))
 
974
 
 
975
    runCommand("hdiutil detach %s"%(shellQuote(os.path.join(WORKDIR, "mnt", volname))))
 
976
 
 
977
    setIcon(imagepath + ".tmp.dmg", "../Icons/Disk Image.icns")
 
978
    runCommand("hdiutil convert %s -format UDZO -o %s"%(
 
979
            shellQuote(imagepath + ".tmp.dmg"), shellQuote(imagepath)))
 
980
    setIcon(imagepath, "../Icons/Disk Image.icns")
 
981
 
 
982
    os.unlink(imagepath + ".tmp.dmg")
931
983
 
932
984
    return imagepath
933
985
 
935
987
def setIcon(filePath, icnsPath):
936
988
    """
937
989
    Set the custom icon for the specified file or directory.
938
 
 
939
 
    For a directory the icon data is written in a file named 'Icon\r' inside
940
 
    the directory. For both files and directories write the icon as an 'icns'
941
 
    resource. Furthermore set kHasCustomIcon in the finder flags for filePath.
942
990
    """
943
 
    ref, isDirectory = Carbon.File.FSPathMakeRef(icnsPath)
944
 
    icon = Carbon.Icn.ReadIconFile(ref)
945
 
    del ref
946
 
 
947
 
    #
948
 
    # Open the resource fork of the target, to add the icon later on.
949
 
    # For directories we use the file 'Icon\r' inside the directory.
950
 
    #
951
 
 
952
 
    ref, isDirectory = Carbon.File.FSPathMakeRef(filePath)
953
 
 
954
 
    if isDirectory:
955
 
        # There is a problem with getting this into the pax(1) archive,
956
 
        # just ignore directory icons for now.
957
 
        return
958
 
 
959
 
        tmpPath = os.path.join(filePath, "Icon\r")
960
 
        if not os.path.exists(tmpPath):
961
 
            fp = open(tmpPath, 'w')
962
 
            fp.close()
963
 
 
964
 
        tmpRef, _ = Carbon.File.FSPathMakeRef(tmpPath)
965
 
        spec = Carbon.File.FSSpec(tmpRef)
966
 
 
967
 
    else:
968
 
        spec = Carbon.File.FSSpec(ref)
969
 
 
970
 
    try:
971
 
        Carbon.Res.HCreateResFile(*spec.as_tuple())
972
 
    except MacOS.Error:
973
 
        pass
974
 
 
975
 
    # Try to create the resource fork again, this will avoid problems
976
 
    # when adding an icon to a directory. I have no idea why this helps,
977
 
    # but without this adding the icon to a directory will fail sometimes.
978
 
    try:
979
 
        Carbon.Res.HCreateResFile(*spec.as_tuple())
980
 
    except MacOS.Error:
981
 
        pass
982
 
 
983
 
    refNum = Carbon.Res.FSpOpenResFile(spec, fsRdWrPerm)
984
 
 
985
 
    Carbon.Res.UseResFile(refNum)
986
 
 
987
 
    # Check if there already is an icon, remove it if there is.
988
 
    try:
989
 
        h = Carbon.Res.Get1Resource('icns', kCustomIconResource)
990
 
    except MacOS.Error:
991
 
        pass
992
 
 
993
 
    else:
994
 
        h.RemoveResource()
995
 
        del h
996
 
 
997
 
    # Add the icon to the resource for of the target
998
 
    res = Carbon.Res.Resource(icon)
999
 
    res.AddResource('icns', kCustomIconResource, '')
1000
 
    res.WriteResource()
1001
 
    res.DetachResource()
1002
 
    Carbon.Res.CloseResFile(refNum)
1003
 
 
1004
 
    # And now set the kHasCustomIcon property for the target. Annoyingly,
1005
 
    # python doesn't seem to have bindings for the API that is needed for
1006
 
    # this. Cop out and call SetFile
1007
 
    os.system("/Developer/Tools/SetFile -a C %s"%(
1008
 
            shellQuote(filePath),))
1009
 
 
1010
 
    if isDirectory:
1011
 
        os.system('/Developer/Tools/SetFile -a V %s'%(
1012
 
            shellQuote(tmpPath),
1013
 
        ))
 
991
 
 
992
    toolPath = os.path.join(os.path.dirname(__file__), "seticon.app/Contents/MacOS/seticon")
 
993
    dirPath = os.path.dirname(__file__)
 
994
    if not os.path.exists(toolPath) or os.stat(toolPath).st_mtime < os.stat(dirPath + '/seticon.m').st_mtime:
 
995
        # NOTE: The tool is created inside an .app bundle, otherwise it won't work due
 
996
        # to connections to the window server.
 
997
        if not os.path.exists('seticon.app/Contents/MacOS'):
 
998
            os.makedirs('seticon.app/Contents/MacOS')
 
999
        runCommand("cc -o %s %s/seticon.m -framework Cocoa"%(
 
1000
            shellQuote(toolPath), shellQuote(dirPath)))
 
1001
 
 
1002
    runCommand("%s %s %s"%(shellQuote(os.path.abspath(toolPath)), shellQuote(icnsPath),
 
1003
        shellQuote(filePath)))
1014
1004
 
1015
1005
def main():
1016
1006
    # First parse options and check if we can perform our work
1017
1007
    parseOptions()
1018
1008
    checkEnvironment()
1019
1009
 
1020
 
    os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.3'
 
1010
    os.environ['MACOSX_DEPLOYMENT_TARGET'] = DEPTARGET
1021
1011
 
1022
1012
    if os.path.exists(WORKDIR):
1023
1013
        shutil.rmtree(WORKDIR)
1028
1018
 
1029
1019
    # Now build python itself
1030
1020
    buildPython()
 
1021
 
 
1022
    # And then build the documentation
 
1023
    # Remove the Deployment Target from the shell
 
1024
    # environment, it's no longer needed and
 
1025
    # an unexpected build target can cause problems
 
1026
    # when Sphinx and its dependencies need to
 
1027
    # be (re-)installed.
 
1028
    del os.environ['MACOSX_DEPLOYMENT_TARGET']
1031
1029
    buildPythonDocs()
 
1030
 
 
1031
 
 
1032
    # Prepare the applications folder
1032
1033
    fn = os.path.join(WORKDIR, "_root", "Applications",
1033
1034
                "Python %s"%(getVersion(),), "Update Shell Profile.command")
1034
 
    patchFile("scripts/postflight.patch-profile",  fn)
1035
 
    os.chmod(fn, 0755)
 
1035
    patchScript("scripts/postflight.patch-profile",  fn)
1036
1036
 
1037
1037
    folder = os.path.join(WORKDIR, "_root", "Applications", "Python %s"%(
1038
1038
        getVersion(),))
1054
1054
    print >> fp, "# By:", pwd.getpwuid(os.getuid()).pw_gecos
1055
1055
    fp.close()
1056
1056
 
1057
 
    # Custom icon for the DMG, shown when the DMG is mounted.
1058
 
    shutil.copy("../Icons/Disk Image.icns",
1059
 
            os.path.join(WORKDIR, "installer", ".VolumeIcon.icns"))
1060
 
    os.system("/Developer/Tools/SetFile -a C %s"%(
1061
 
            os.path.join(WORKDIR, "installer", ".VolumeIcon.icns")))
1062
1057
 
1063
1058
 
1064
1059
    # And copy it to a DMG