install.py 20.8 KB
Newer Older
1 2 3 4 5 6
"""distutils.command.install

Implements the Distutils 'install' command."""

# created 1999/03/13, Greg Ward

7
__revision__ = "$Id$"
8 9

import sys, os, string
10
from types import *
11
from distutils.core import Command, DEBUG
12
from distutils.sysconfig import get_config_vars
13 14
from distutils.file_util import write_file
from distutils.util import convert_path, subst_vars, change_root
15
from distutils.errors import DistutilsOptionError
16
from glob import glob
17

18 19 20 21
INSTALL_SCHEMES = {
    'unix_prefix': {
        'purelib': '$base/lib/python$py_version_short/site-packages',
        'platlib': '$platbase/lib/python$py_version_short/site-packages',
22
        'headers': '$base/include/python$py_version_short/$dist_name',
23
        'scripts': '$base/bin',
24
        'data'   : '$base',
25 26 27 28
        },
    'unix_home': {
        'purelib': '$base/lib/python',
        'platlib': '$base/lib/python',
29
        'headers': '$base/include/python/$dist_name',
30
        'scripts': '$base/bin',
31
        'data'   : '$base',
32 33 34 35
        },
    'nt': {
        'purelib': '$base',
        'platlib': '$base',
36
        'headers': '$base\\Include\\$dist_name',
37
        'scripts': '$base\\Scripts',
38
        'data'   : '$base',
39 40
        },
    'mac': {
41 42
        'purelib': '$base:Lib:site-packages',
        'platlib': '$base:Lib:site-packages',
43
        'headers': '$base:Include:$dist_name',
44
        'scripts': '$base:Scripts',
45
        'data'   : '$base',
46 47 48 49
        }
    }


50
class install (Command):
51

52 53
    description = "install everything from build directory"

54
    user_options = [
55 56 57
        # Select installation scheme and set base director(y|ies)
        ('prefix=', None,
         "installation prefix"),
58
        ('exec-prefix=', None,
59 60 61 62 63 64 65 66 67 68
         "(Unix only) prefix for platform-specific files"),
        ('home=', None,
         "(Unix only) home directory to install under"),

        # Or, just set the base director(y|ies)
        ('install-base=', None,
         "base installation directory (instead of --prefix or --home)"),
        ('install-platbase=', None,
         "base installation directory for platform-specific files " +
         "(instead of --exec-prefix or --home)"),
69 70
        ('root=', None,
         "install everything relative to this alternate root directory"),
71 72 73 74

        # Or, explicitly set the installation scheme
        ('install-purelib=', None,
         "installation directory for pure Python module distributions"),
75
        ('install-platlib=', None,
76 77 78 79 80
         "installation directory for non-pure module distributions"),
        ('install-lib=', None,
         "installation directory for all module distributions " +
         "(overrides --install-purelib and --install-platlib)"),

81 82
        ('install-headers=', None,
         "installation directory for C/C++ headers"),
83 84 85 86
        ('install-scripts=', None,
         "installation directory for Python scripts"),
        ('install-data=', None,
         "installation directory for data files"),
87

88 89 90
        # Miscellaneous control options
        ('force', 'f',
         "force installation (overwrite any existing files)"),
91 92 93
        ('skip-build', None,
         "skip rebuilding everything (for testing/debugging)"),

94
        # Where to install documentation (eventually!)
95 96 97 98
        #('doc-format=', None, "format of documentation to generate"),
        #('install-man=', None, "directory for Unix man pages"),
        #('install-html=', None, "directory for HTML documentation"),
        #('install-info=', None, "directory for GNU info files"),
99

100 101
        ('record=', None,
         "filename in which to record list of installed files"),
102
        ]
103

104 105 106 107 108 109
    # 'sub_commands': a list of commands this command might have to run to
    # get its work done.  Each command is represented as a tuple (method,
    # command) where 'method' is the name of a method to call that returns
    # true if 'command' (the sub-command name, a string) needs to be run.
    # If 'method' is None, assume that 'command' must always be run.
    sub_commands = [('has_lib', 'install_lib'),
110
                    ('has_headers', 'install_headers'),
111 112
                    ('has_scripts', 'install_scripts'),
                    ('has_data', 'install_data'),
113
                   ]
114 115


116
    def initialize_options (self):
117

118 119
        # High-level options: these select both an installation base
        # and scheme.
120 121
        self.prefix = None
        self.exec_prefix = None
122 123
        self.home = None

124 125 126
        # These select only the installation base; it's up to the user to
        # specify the installation scheme (currently, that means supplying
        # the --install-{platlib,purelib,scripts,data} options).
127 128
        self.install_base = None
        self.install_platbase = None
129
        self.root = None
130

131 132 133 134 135 136
        # These options are the actual installation directories; if not
        # supplied by the user, they are filled in using the installation
        # scheme implied by prefix/exec-prefix/home and the contents of
        # that installation scheme.
        self.install_purelib = None     # for pure module distributions
        self.install_platlib = None     # non-pure (dists w/ extensions)
137
        self.install_headers = None     # for C/C++ headers
138
        self.install_lib = None         # set to either purelib or platlib
139 140
        self.install_scripts = None
        self.install_data = None
141

142 143 144 145 146
        # These two are for putting non-packagized distributions into their
        # own directory and creating a .pth file if it makes sense.
        # 'extra_path' comes from the setup file; 'install_path_file' is
        # set only if we determine that it makes sense to install a path
        # file.
147 148 149
        self.extra_path = None
        self.install_path_file = 0

150
        self.force = 0
151 152
        self.skip_build = 0

153 154 155 156 157 158 159 160 161
        # These are only here as a conduit from the 'build' command to the
        # 'install_*' commands that do the real work.  ('build_base' isn't
        # actually used anywhere, but it might be useful in future.)  They
        # are not user options, because if the user told the install
        # command where the build directory is, that wouldn't affect the
        # build command.
        self.build_base = None
        self.build_lib = None

162 163
        # Not defined yet because we don't know anything about
        # documentation yet.
164 165 166
        #self.install_man = None
        #self.install_html = None
        #self.install_info = None
167

168
        self.record = None
169

170
    def finalize_options (self):
171

172 173 174 175 176 177 178 179 180 181
        # This method (and its pliant slaves, like 'finalize_unix()',
        # 'finalize_other()', and 'select_scheme()') is where the default
        # installation directories for modules, extension modules, and
        # anything else we care to install from a Python module
        # distribution.  Thus, this code makes a pretty important policy
        # statement about how third-party stuff is added to a Python
        # installation!  Note that the actual work of installation is done
        # by the relatively simple 'install_*' commands; they just take
        # their orders from the installation directory options determined
        # here.
182

183 184
        # Check for errors/inconsistencies in the options; first, stuff
        # that's wrong on any platform.
185 186 187 188 189 190 191

        if ((self.prefix or self.exec_prefix or self.home) and
            (self.install_base or self.install_platbase)):
            raise DistutilsOptionError, \
                  ("must supply either prefix/exec-prefix/home or " +
                   "install-base/install-platbase -- not both")

192
        # Next, stuff that's wrong (or dubious) only on certain platforms.
193 194
        if os.name == 'posix':
            if self.home and (self.prefix or self.exec_prefix):
195
                raise DistutilsOptionError, \
196 197
                      ("must supply either home or prefix/exec-prefix -- " +
                       "not both")
198
        else:
199 200 201 202 203 204 205 206 207 208 209 210 211 212
            if self.exec_prefix:
                self.warn ("exec-prefix option ignored on this platform")
                self.exec_prefix = None
            if self.home:
                self.warn ("home option ignored on this platform")
                self.home = None

        # Now the interesting logic -- so interesting that we farm it out
        # to other methods.  The goal of these methods is to set the final
        # values for the install_{lib,scripts,data,...}  options, using as
        # input a heady brew of prefix, exec_prefix, home, install_base,
        # install_platbase, user-supplied versions of
        # install_{purelib,platlib,lib,scripts,data,...}, and the
        # INSTALL_SCHEME dictionary above.  Phew!
213

214
        self.dump_dirs ("pre-finalize_{unix,other}")
215

216 217
        if os.name == 'posix':
            self.finalize_unix ()
218
        else:
219 220
            self.finalize_other ()

221
        self.dump_dirs ("post-finalize_{unix,other}()")
222 223 224 225 226 227

        # Expand configuration variables, tilde, etc. in self.install_base
        # and self.install_platbase -- that way, we can use $base or
        # $platbase in the other installation directories and not worry
        # about needing recursive variable expansion (shudder).

228
        py_version = (string.split(sys.version))[0]
Greg Ward's avatar
Greg Ward committed
229
        (prefix, exec_prefix) = get_config_vars('prefix', 'exec_prefix')
230 231 232 233 234
        self.config_vars = {'dist_name': self.distribution.get_name(),
                            'dist_version': self.distribution.get_version(),
                            'dist_fullname': self.distribution.get_fullname(),
                            'py_version': py_version,
                            'py_version_short': py_version[0:3],
235 236 237 238
                            'sys_prefix': prefix,
                            'prefix': prefix,
                            'sys_exec_prefix': exec_prefix,
                            'exec_prefix': exec_prefix,
239 240 241
                           }
        self.expand_basedirs ()

242
        self.dump_dirs ("post-expand_basedirs()")
243 244 245 246 247 248

        # Now define config vars for the base directories so we can expand
        # everything else.
        self.config_vars['base'] = self.install_base
        self.config_vars['platbase'] = self.install_platbase

249 250 251 252
        if DEBUG:
            from pprint import pprint
            print "config vars:"
            pprint (self.config_vars)
253

254 255 256 257
        # Expand "~" and configuration variables in the installation
        # directories.
        self.expand_dirs ()

258
        self.dump_dirs ("post-expand_dirs()")
259

260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
        # Pick the actual directory to install all modules to: either
        # install_purelib or install_platlib, depending on whether this
        # module distribution is pure or not.  Of course, if the user
        # already specified install_lib, use their selection.
        if self.install_lib is None:
            if self.distribution.ext_modules: # has extensions: non-pure
                self.install_lib = self.install_platlib
            else:
                self.install_lib = self.install_purelib
                    
        # Well, we're not actually fully completely finalized yet: we still
        # have to deal with 'extra_path', which is the hack for allowing
        # non-packagized module distributions (hello, Numerical Python!) to
        # get their own directories.
        self.handle_extra_path ()
        self.install_libbase = self.install_lib # needed for .pth file
        self.install_lib = os.path.join (self.install_lib, self.extra_dirs)
277

278 279 280
        # If a new root directory was supplied, make all the installation
        # dirs relative to it.
        if self.root is not None:
281
            for name in ('libbase', 'lib', 'purelib', 'platlib',
282
                         'scripts', 'data', 'headers'):
283 284 285 286 287 288
                attr = "install_" + name
                new_val = change_root (self.root, getattr (self, attr))
                setattr (self, attr, new_val)

        self.dump_dirs ("after prepending root")

289
        # Find out the build directories, ie. where to install from.
290 291
        self.set_undefined_options ('build',
                                    ('build_base', 'build_base'),
292
                                    ('build_lib', 'build_lib'))
293 294 295

        # Punt on doc directories for now -- after all, we're punting on
        # documentation completely!
296

297 298 299
    # finalize_options ()


300
    def dump_dirs (self, msg):
301 302 303 304 305 306 307 308 309 310
        if DEBUG:
            from distutils.fancy_getopt import longopt_xlate
            print msg + ":"
            for opt in self.user_options:
                opt_name = opt[0]
                if opt_name[-1] == "=":
                    opt_name = opt_name[0:-1]
                opt_name = string.translate (opt_name, longopt_xlate)
                val = getattr (self, opt_name)
                print "  %s: %s" % (opt_name, val)
311 312


313 314 315 316 317 318
    def finalize_unix (self):
        
        if self.install_base is not None or self.install_platbase is not None:
            if ((self.install_lib is None and
                 self.install_purelib is None and
                 self.install_platlib is None) or
319
                self.install_headers is None or
320 321 322 323 324 325 326 327 328 329
                self.install_scripts is None or
                self.install_data is None):
                raise DistutilsOptionError, \
                      "install-base or install-platbase supplied, but " + \
                      "installation scheme is incomplete"
            return

        if self.home is not None:
            self.install_base = self.install_platbase = self.home
            self.select_scheme ("unix_home")
330
        else:
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
            if self.prefix is None:
                if self.exec_prefix is not None:
                    raise DistutilsOptionError, \
                          "must not supply exec-prefix without prefix"

                self.prefix = os.path.normpath (sys.prefix)
                self.exec_prefix = os.path.normpath (sys.exec_prefix)
                self.install_path_file = 1

            else:
                if self.exec_prefix is None:
                    self.exec_prefix = self.prefix


            # XXX since we don't *know* that a user-supplied prefix really
            # points to another Python installation, we can't be sure that
            # writing a .pth file there will actually work -- so we don't
            # try.  That is, we only set 'install_path_file' if the user
            # didn't supply prefix.  There are certainly circumstances
            # under which we *should* install a .pth file when the user
            # supplies a prefix, namely when that prefix actually points to
            # another Python installation.  Hmmm.

            self.install_base = self.prefix
            self.install_platbase = self.exec_prefix
            self.select_scheme ("unix_prefix")

    # finalize_unix ()


    def finalize_other (self):          # Windows and Mac OS for now

        if self.prefix is None:
            self.prefix = os.path.normpath (sys.prefix)
            self.install_path_file = 1

        # XXX same caveat regarding 'install_path_file' as in
        # 'finalize_unix()'.

        self.install_base = self.install_platbase = self.prefix
        try:
            self.select_scheme (os.name)
        except KeyError:
374
            raise DistutilsPlatformError, \
375
                  "I don't know how to install stuff on '%s'" % os.name
376

377 378 379 380 381 382
    # finalize_other ()


    def select_scheme (self, name):
        # it's the caller's problem if they supply a bad name!
        scheme = INSTALL_SCHEMES[name]
383
        for key in ('purelib', 'platlib', 'headers', 'scripts', 'data'):
384 385 386
            attrname = 'install_' + key
            if getattr(self, attrname) is None:
                setattr(self, attrname, scheme[key])
387 388


389 390 391 392 393 394 395 396
    def _expand_attrs (self, attrs):
        for attr in attrs:
            val = getattr (self, attr)
            if val is not None:
                if os.name == 'posix':
                    val = os.path.expanduser (val)
                val = subst_vars (val, self.config_vars)
                setattr (self, attr, val)
397 398


399 400
    def expand_basedirs (self):
        self._expand_attrs (['install_base',
401 402
                             'install_platbase',
                             'root'])        
403 404 405 406 407

    def expand_dirs (self):
        self._expand_attrs (['install_purelib',
                             'install_platlib',
                             'install_lib',
408
                             'install_headers',
409 410
                             'install_scripts',
                             'install_data',])
411 412 413


    def handle_extra_path (self):
414

415 416 417 418 419 420 421 422 423 424 425
        if self.extra_path is None:
            self.extra_path = self.distribution.extra_path

        if self.extra_path is not None:
            if type (self.extra_path) is StringType:
                self.extra_path = string.split (self.extra_path, ',')

            if len (self.extra_path) == 1:
                path_file = extra_dirs = self.extra_path[0]
            elif len (self.extra_path) == 2:
                (path_file, extra_dirs) = self.extra_path
426
            else:
427
                raise DistutilsOptionError, \
428
                      "'extra_path' option must be a list, tuple, or " + \
429 430
                      "comma-separated string with 1 or 2 elements"

431 432
            # convert to local form in case Unix notation used (as it
            # should be in setup scripts)
433
            extra_dirs = convert_path (extra_dirs)
434

435 436 437 438
        else:
            path_file = None
            extra_dirs = ''

439 440
        # XXX should we warn if path_file and not extra_dirs? (in which
        # case the path file would be harmless but pointless)
441 442 443
        self.path_file = path_file
        self.extra_dirs = extra_dirs

444
    # handle_extra_path ()
445 446


447 448 449 450 451 452 453 454 455 456 457 458 459 460
    def get_sub_commands (self):
        """Return the list of subcommands that we need to run.  This is
        based on the 'subcommands' class attribute: each tuple in that list
        can name a method that we call to determine if the subcommand needs
        to be run for the current distribution."""
        commands = []
        for (method, cmd_name) in self.sub_commands:
            if method is not None:
                method = getattr(self, method)
            if method is None or method():
                commands.append(cmd_name)
        return commands


461 462
    def run (self):

463
        # Obviously have to build before we can install
464
        if not self.skip_build:
Greg Ward's avatar
Greg Ward committed
465
            self.run_command ('build')
466

467 468
        # Run all sub-commands (at least those that need to be run)
        for cmd_name in self.get_sub_commands():
Greg Ward's avatar
Greg Ward committed
469
            self.run_command (cmd_name)
470 471 472

        if self.path_file:
            self.create_path_file ()
473

474 475 476
        # write list of installed files, if requested.
        if self.record:
            outputs = self.get_outputs()
477
            if self.root:               # strip any package prefix
478 479 480 481
                root_len = len(self.root)
                for counter in xrange (len (outputs)):
                    outputs[counter] = outputs[counter][root_len:]
            self.execute(write_file,
482 483 484
                         (self.record, outputs),
                         "writing list of installed files to '%s'" %
                         self.record)
485 486 487 488 489 490 491 492

        normalized_path = map (os.path.normpath, sys.path)
        if (not (self.path_file and self.install_path_file) and
            os.path.normpath (self.install_lib) not in normalized_path):
            self.warn (("modules installed to '%s', which is not in " +
                        "Python's module search path (sys.path) -- " +
                        "you'll have to change the search path yourself") %
                       self.install_lib)
493

494 495
    # run ()

496

497 498 499 500 501 502
    def has_lib (self):
        """Return true if the current distribution has any Python
        modules to install."""
        return (self.distribution.has_pure_modules() or
                self.distribution.has_ext_modules())

503 504 505
    def has_headers (self):
        return self.distribution.has_headers()

506 507 508 509 510 511 512
    def has_scripts (self):
        return self.distribution.has_scripts()

    def has_data (self):
        return self.distribution.has_data_files()


513 514 515 516
    def get_outputs (self):
        # This command doesn't have any outputs of its own, so just
        # get the outputs of all its sub-commands.
        outputs = []
517
        for cmd_name in self.get_sub_commands():
Greg Ward's avatar
Greg Ward committed
518
            cmd = self.get_finalized_command (cmd_name)
519
            outputs.extend (cmd.get_outputs())
520 521 522 523

        return outputs


524 525 526
    def get_inputs (self):
        # XXX gee, this looks familiar ;-(
        inputs = []
527
        for cmd_name in self.get_sub_commands():
Greg Ward's avatar
Greg Ward committed
528
            cmd = self.get_finalized_command (cmd_name)
529
            inputs.extend (cmd.get_inputs())
530 531 532 533

        return inputs


534
    def create_path_file (self):
535 536 537 538 539 540
        filename = os.path.join (self.install_libbase,
                                 self.path_file + ".pth")
        if self.install_path_file:
            self.execute (write_file,
                          (filename, [self.extra_dirs]),
                          "creating %s" % filename)
541
        else:
542 543 544 545
            self.warn (("path file '%s' not created for alternate or custom " +
                        "installation (path files only work with standard " +
                        "installations)") %
                       filename)
546

547
# class install