diff --git a/ChangeLog b/ChangeLog index fd09df0..b6e3837 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,9 +1,20 @@ +* 0.8 + - Fixed issue #21 Added Ubuntu 11.04(Natty) support + - Fixed issue #24 non-framework python27 now defines environ properly. Thanks npinto. + - Fixed issue #27 Cleanup of OS X python build flags + - Fixed issue #28 Describe the 'symlink' command better. Thanks tgs. + - Fixed issue #30 py command does not accept arguments with a space + - Fixed bug: `pythonbrew off` need not have removed the symlink in bin directory + - Added --no-test option to the install command + - Added --verbose option to the install command + - `pythonbrew clean` has been renamed. Added `pythonbrew cleanup` instead. + * 0.7.3 - - Improved symlink command. - - support python3 + - Improved symlink command + - Added python3 support * 0.7.2 - - Bug fixed. + - Bug fixed * 0.7.1 - Enable parallel make option (--jobs). diff --git a/PKG-INFO b/PKG-INFO deleted file mode 120000 index 92cacd2..0000000 --- a/PKG-INFO +++ /dev/null @@ -1 +0,0 @@ -README.rst \ No newline at end of file diff --git a/PKG-INFO b/PKG-INFO new file mode 100644 index 0000000..6ade579 --- /dev/null +++ b/PKG-INFO @@ -0,0 +1,159 @@ +Overview +======== + +pythonbrew is a program to automate the building and installation of Python in the users $HOME. + +pythonbrew is inspired by `perlbrew `_ and `rvm `_. + +Installation +============ + +The recommended way to download and install pythonbrew is to run these statements in your shell:: + + curl -kL http://xrl.us/pythonbrewinstall | bash + +After that, pythonbrew installs itself to ~/.pythonbrew, and you should follow the instruction on screen to setup your .bashrc to put it in your PATH. + +If you need to install pythonbrew into somewhere else, you can do that by setting a PYTHONBREW_ROOT environment variable:: + + export PYTHONBREW_ROOT=/path/to/pythonbrew + curl -kLO http://xrl.us/pythonbrewinstall + chmod +x pythonbrewinstall + ./pythonbrewinstall + +Usage +===== + +pythonbrew command [options] + +Install some pythons:: + + pythonbrew install 2.7.2 + pythonbrew install --verbose 2.7.2 + pythonbrew install --force 2.7.2 + pythonbrew install --no-test 2.7.2 + pythonbrew install --configure="CC=gcc_4.1" 2.7.2 + pythonbrew install --no-setuptools 2.7.2 + pythonbrew install http://www.python.org/ftp/python/2.7/Python-2.7.2.tgz + pythonbrew install /path/to/Python-2.7.2.tgz + pythonbrew install /path/to/Python-2.7.2 + pythonbrew install 2.7.2 3.2 + +Permanently use the specified python:: + + pythonbrew switch 2.7.2 + pythonbrew switch 3.2 + +Use the specified python in current shell:: + + pythonbrew use 2.7.2 + +Runs a named python file against specified and/or all pythons:: + + pythonbrew py test.py + pythonbrew py -v test.py # Show running python version + pythonbrew py -p 2.7.2 -p 3.2 test.py # Use the specified pythons + +List the installed pythons:: + + pythonbrew list + +List the available installation pythons:: + + pythonbrew list -k + +Uninstall the specified python:: + + pythonbrew uninstall 2.7.2 + pythonbrew uninstall 2.7.2 3.2 + +Remove stale source folders and archives:: + + pythonbrew cleanup + +Upgrades pythonbrew to the latest version:: + + pythonbrew update + pythonbrew update --master + pythonbrew update --develop + +Disable pythonbrew:: + + pythonbrew off + +Create/Remove a symbolic link to python (in a directory on your $PATH):: + + pythonbrew symlink # Create a symbolic link, like "py2.7.2", for each installed version + pythonbrew symlink -p 2.7.2 + pythonbrew symlink pip # Create a symbolic link to the specified script in bin directory + pythonbrew symlink -r # Remove a symbolic link + +Show version:: + + pythonbrew version + +COMMANDS +======== + +install + Build and install the given version of python. + Install setuptools and pip automatically. + +switch + Permanently use the specified python as default. + +use + Use the specified python in current shell. + +py + Runs a named python file against specified and/or all pythons. + +list + List the installed all pythons. + +list -k + List the available install pythons. + +uninstall + Uninstall the given version of python. + +cleanup + Remove stale source folders and archives. + +update + Upgrades pythonbrew to the latest version. + +off + Disable pythonbrew. + +version + Show version. + +See more details below:: + + pythonbrew help + +LICENCE +======= + +The MIT License + +Copyright (c) <2010-2011> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.rst b/README.rst index fd9c14b..6ade579 100644 --- a/README.rst +++ b/README.rst @@ -1,7 +1,7 @@ Overview ======== -pythonbrew is a program to automate the building and installation of Python in the users HOME. +pythonbrew is a program to automate the building and installation of Python in the users $HOME. pythonbrew is inspired by `perlbrew `_ and `rvm `_. @@ -10,20 +10,16 @@ Installation The recommended way to download and install pythonbrew is to run these statements in your shell:: - curl -kLO http://github.com/utahta/pythonbrew/raw/master/pythonbrew-install - chmod +x pythonbrew-install - ./pythonbrew-install - -or more simply like this:: - - curl -kL http://github.com/utahta/pythonbrew/raw/master/pythonbrew-install | bash + curl -kL http://xrl.us/pythonbrewinstall | bash After that, pythonbrew installs itself to ~/.pythonbrew, and you should follow the instruction on screen to setup your .bashrc to put it in your PATH. If you need to install pythonbrew into somewhere else, you can do that by setting a PYTHONBREW_ROOT environment variable:: export PYTHONBREW_ROOT=/path/to/pythonbrew - ./pythonbrew-install + curl -kLO http://xrl.us/pythonbrewinstall + chmod +x pythonbrewinstall + ./pythonbrewinstall Usage ===== @@ -32,50 +28,54 @@ pythonbrew command [options] Install some pythons:: - pythonbrew install 2.6.6 - pythonbrew install --force 2.6.6 - pythonbrew install --configure="CC=gcc_4.1" 2.6.6 - pythonbrew install --no-setuptools 2.6.6 - pythonbrew install http://www.python.org/ftp/python/2.7/Python-2.6.6.tgz - pythonbrew install file:///path/to/Python-2.6.6.tgz - pythonbrew install /path/to/Python-2.6.6.tgz - pythonbrew install 2.5.5 2.6.6 + pythonbrew install 2.7.2 + pythonbrew install --verbose 2.7.2 + pythonbrew install --force 2.7.2 + pythonbrew install --no-test 2.7.2 + pythonbrew install --configure="CC=gcc_4.1" 2.7.2 + pythonbrew install --no-setuptools 2.7.2 + pythonbrew install http://www.python.org/ftp/python/2.7/Python-2.7.2.tgz + pythonbrew install /path/to/Python-2.7.2.tgz + pythonbrew install /path/to/Python-2.7.2 + pythonbrew install 2.7.2 3.2 -Permanently use the specified python as default:: +Permanently use the specified python:: - pythonbrew switch 2.6.6 - pythonbrew switch 2.5.5 + pythonbrew switch 2.7.2 + pythonbrew switch 3.2 Use the specified python in current shell:: - pythonbrew use 2.6.6 + pythonbrew use 2.7.2 Runs a named python file against specified and/or all pythons:: pythonbrew py test.py pythonbrew py -v test.py # Show running python version - pythonbrew py -p 2.6.6 -p 3.1.2 test.py # Use the specified pythons + pythonbrew py -p 2.7.2 -p 3.2 test.py # Use the specified pythons List the installed pythons:: pythonbrew list -List the available install pythons:: +List the available installation pythons:: pythonbrew list -k Uninstall the specified python:: - pythonbrew uninstall 2.6.6 - pythonbrew uninstall 2.5.5 2.6.6 + pythonbrew uninstall 2.7.2 + pythonbrew uninstall 2.7.2 3.2 Remove stale source folders and archives:: - pythonbrew clean + pythonbrew cleanup Upgrades pythonbrew to the latest version:: pythonbrew update + pythonbrew update --master + pythonbrew update --develop Disable pythonbrew:: @@ -83,8 +83,8 @@ Disable pythonbrew:: Create/Remove a symbolic link to python (in a directory on your $PATH):: - pythonbrew symlink # Create a symbolic link, like "py2.5.5", for each installed version - pythonbrew symlink -p 2.5.5 + pythonbrew symlink # Create a symbolic link, like "py2.7.2", for each installed version + pythonbrew symlink -p 2.7.2 pythonbrew symlink pip # Create a symbolic link to the specified script in bin directory pythonbrew symlink -r # Remove a symbolic link @@ -97,10 +97,7 @@ COMMANDS install Build and install the given version of python. - - Setuptools and pip is automatically installed. - - options: --force, --no-setuptools, --configure and --as. + Install setuptools and pip automatically. switch Permanently use the specified python as default. @@ -120,7 +117,7 @@ list -k uninstall Uninstall the given version of python. -clean +cleanup Remove stale source folders and archives. update @@ -131,21 +128,10 @@ off version Show version. + +See more details below:: -Options -======= - -\-f | --force - Force installation of a python. (skip `make test`) - -\-C | --configure - Custom configure options. - -\-n | --no-setuptools - Skip installation of setuptools. - -\--as - Install a python under an alias. + pythonbrew help LICENCE ======= diff --git a/pythonbrew/__init__.py b/pythonbrew/__init__.py index 5e2f4d8..e09793c 100644 --- a/pythonbrew/__init__.py +++ b/pythonbrew/__init__.py @@ -1,6 +1,7 @@ import sys from pythonbrew.basecommand import command_dict, load_all_commands from pythonbrew.baseparser import parser +from pythonbrew.log import logger def main(): options, args = parser.parse_args(sys.argv[1:]) @@ -12,6 +13,10 @@ def main(): load_all_commands() command = args[0].lower() if command not in command_dict: + if command == 'clean': + # note: for some time + logger.info('\nDEPRECATION WARNING: `pythonbrew clean` has been renamed. Please run `pythonbrew cleanup` instead.\n') + return parser.error("Unknown command: `%s`" % command) return command = command_dict[command] diff --git a/pythonbrew/commands/clean.py b/pythonbrew/commands/cleanup.py similarity index 67% rename from pythonbrew/commands/clean.py rename to pythonbrew/commands/cleanup.py index 99aed07..2de8c48 100644 --- a/pythonbrew/commands/clean.py +++ b/pythonbrew/commands/cleanup.py @@ -3,17 +3,17 @@ from pythonbrew.define import PATH_BUILD, PATH_DISTS from pythonbrew.util import rm_r -class CleanCommand(Command): - name = "clean" +class CleanupCommand(Command): + name = "cleanup" usage = "%prog" summary = "Remove stale source folders and archives" def run_command(self, options, args): - self._clean(PATH_BUILD) - self._clean(PATH_DISTS) + self._cleanup(PATH_BUILD) + self._cleanup(PATH_DISTS) - def _clean(self, root): + def _cleanup(self, root): for dir in os.listdir(root): rm_r(os.path.join(root, dir)) -CleanCommand() +CleanupCommand() diff --git a/pythonbrew/commands/help.py b/pythonbrew/commands/help.py index a3dda2a..7256ae4 100644 --- a/pythonbrew/commands/help.py +++ b/pythonbrew/commands/help.py @@ -22,6 +22,6 @@ def run_command(self, options, args): for command in commands: logger.info(" %s: %s" % (command.name, command.summary)) logger.info("\nFurther Instructions:") - logger.info(" http://github.com/utahta/pythonbrew") + logger.info(" https://github.com/utahta/pythonbrew") HelpCommand() diff --git a/pythonbrew/commands/install.py b/pythonbrew/commands/install.py index edbb013..34e6877 100644 --- a/pythonbrew/commands/install.py +++ b/pythonbrew/commands/install.py @@ -2,7 +2,7 @@ from pythonbrew.log import logger from pythonbrew.installer.pythoninstaller import PythonInstaller,\ PythonInstallerMacOSX -from pythonbrew.util import is_macosx_snowleopard +from pythonbrew.util import is_macosx from pythonbrew.exceptions import UnknownVersionException,\ AlreadyInstalledException, NotSupportedVersionException @@ -18,7 +18,21 @@ def __init__(self): dest="force", action="store_true", default=False, - help="Force install of python.(skip make test)" + help="Force installation of python." + ) + self.parser.add_option( + "-n", "--no-test", + dest="no_test", + action="store_true", + default=False, + help="Skip `make test`." + ) + self.parser.add_option( + "-v", "--verbose", + dest="verbose", + action="store_true", + default=False, + help="Display log information on the console." ) self.parser.add_option( "-C", "--configure", @@ -28,11 +42,11 @@ def __init__(self): help="Options passed directly to configure." ) self.parser.add_option( - "-n", "--no-setuptools", + "--no-setuptools", dest="no_setuptools", action="store_true", default=False, - help="Skip install of setuptools." + help="Skip installation of setuptools." ) self.parser.add_option( "--as", @@ -50,10 +64,10 @@ def __init__(self): def run_command(self, options, args): if args: - # Install pythons + # installing python for arg in args: try: - if is_macosx_snowleopard(): + if is_macosx(): p = PythonInstallerMacOSX(arg, options) else: p = PythonInstaller(arg, options) diff --git a/pythonbrew/commands/py.py b/pythonbrew/commands/py.py index ca11af9..9390dca 100644 --- a/pythonbrew/commands/py.py +++ b/pythonbrew/commands/py.py @@ -1,10 +1,10 @@ import os import sys +import subprocess from pythonbrew.basecommand import Command from pythonbrew.define import PATH_PYTHONS from pythonbrew.util import Package from pythonbrew.log import logger -from subprocess import Popen class PyCommand(Command): name = "py" @@ -40,13 +40,11 @@ def run_command(self, options, args): logger.info('*** %s ***' % d) path = os.path.join(PATH_PYTHONS, d, 'bin', args[0]) if os.path.isfile(path) and os.access(path, os.X_OK): - p = Popen([path] + args[1:]) - p.wait() + subprocess.call([path] + args[1:]) else: path = os.path.join(PATH_PYTHONS, d, 'bin', 'python') if os.path.isfile(path) and os.access(path, os.X_OK): - p = Popen([path] + args) - p.wait() + subprocess.call([path] + args) else: logger.info('%s: No such file or directory.' % path) diff --git a/pythonbrew/commands/update.py b/pythonbrew/commands/update.py index d342224..f9d760a 100644 --- a/pythonbrew/commands/update.py +++ b/pythonbrew/commands/update.py @@ -6,7 +6,7 @@ from pythonbrew.log import logger from pythonbrew.downloader import Downloader, get_pythonbrew_update_url,\ get_stable_version, get_headerinfo_from_url -from pythonbrew.util import rm_r, unpack_downloadfile, Link, is_gzip, Subprocess +from pythonbrew.util import rm_r, extract_downloadfile, Link, is_gzip, Subprocess class UpdateCommand(Command): name = "update" @@ -16,11 +16,18 @@ class UpdateCommand(Command): def __init__(self): super(UpdateCommand, self).__init__() self.parser.add_option( - '--head', - dest='head', + '--master', + dest='master', action='store_true', default=False, - help='Update the pythonbrew to the github version.' + help='Update the pythonbrew to the `master` branch on github.' + ) + self.parser.add_option( + '--develop', + dest='develop', + action='store_true', + default=False, + help='Update the pythonbrew to the `develop` branch on github.' ) self.parser.add_option( '--config', @@ -61,9 +68,10 @@ def _update_config(self, options, args): logger.info("The config.cfg has been updated.") def _update_pythonbrew(self, options, args): - # pythonbrew update - if options.head: - version = 'head' + if options.master: + version = 'master' + elif options.develop: + version = 'develop' else: version = get_stable_version() # check for version @@ -77,10 +85,10 @@ def _update_pythonbrew(self, options, args): sys.exit(1) headinfo = get_headerinfo_from_url(download_url) content_type = headinfo['content-type'] - # head is only for gzip. - if not options.head and not is_gzip(content_type, Link(download_url).filename): - logger.error("Invalid content-type: `%s`" % content_type) - sys.exit(1) + if not options.master and not options.develop: + if not is_gzip(content_type, Link(download_url).filename): + logger.error("content type should be gzip. content-type:`%s`" % content_type) + sys.exit(1) filename = "pythonbrew-%s" % version distname = "%s.tgz" % filename @@ -94,13 +102,13 @@ def _update_pythonbrew(self, options, args): extract_dir = os.path.join(PATH_BUILD, filename) rm_r(extract_dir) - if not unpack_downloadfile(content_type, download_file, extract_dir): + if not extract_downloadfile(content_type, download_file, extract_dir): sys.exit(1) try: logger.info("Installing %s into %s" % (extract_dir, ROOT)) s = Subprocess() - s.check_call('%s %s/pythonbrew_install.py --upgrade' % (sys.executable, extract_dir)) + s.check_call([sys.executable, os.path.join(extract_dir,'pythonbrew_install.py'), '--upgrade']) except: logger.error("Failed to update pythonbrew.") sys.exit(1) diff --git a/pythonbrew/curl.py b/pythonbrew/curl.py index 62bccba..719639c 100644 --- a/pythonbrew/curl.py +++ b/pythonbrew/curl.py @@ -3,7 +3,7 @@ import subprocess from subprocess import Popen, PIPE from pythonbrew.log import logger -from pythonbrew.util import u +from pythonbrew.util import to_str class Curl(object): def __init__(self): @@ -26,11 +26,11 @@ def readheader(self, url): raise respinfo = {} for line in p.stdout: - line = u(line.strip()) + line = to_str(line.strip()) if re.match('^HTTP.*? 200 OK$', line): break for line in p.stdout: - line = u(line.strip()).split(":", 1) + line = to_str(line.strip()).split(":", 1) if len(line) == 2: respinfo[line[0].strip().lower()] = line[1].strip() return respinfo diff --git a/pythonbrew/define.py b/pythonbrew/define.py index 8a51b3f..593d2a7 100644 --- a/pythonbrew/define.py +++ b/pythonbrew/define.py @@ -6,7 +6,7 @@ import configparser as ConfigParser # pythonbrew version -VERSION = "0.7.3" +VERSION = "0.8" # pythonbrew root path ROOT = os.environ.get("PYTHONBREW_ROOT") @@ -28,6 +28,7 @@ PATH_SCRIPTS_PYTHONBREW_COMMANDS = os.path.join(PATH_SCRIPTS_PYTHONBREW,"commands") PATH_SCRIPTS_PYTHONBREW_INSTALLER = os.path.join(PATH_SCRIPTS_PYTHONBREW,"installer") PATH_PATCHES = os.path.join(ROOT,"patches") +PATH_PATCHES_ALL = os.path.join(PATH_PATCHES,"all") PATH_PATCHES_MACOSX = os.path.join(PATH_PATCHES,"macosx") PATH_PATCHES_MACOSX_PYTHON27 = os.path.join(PATH_PATCHES_MACOSX,"python27") PATH_PATCHES_MACOSX_PYTHON26 = os.path.join(PATH_PATCHES_MACOSX,"python26") @@ -53,7 +54,8 @@ def _get_or_default(section, option, default=''): DISTRIBUTE_SETUP_DLSITE = _get_or_default('distribute', 'url') # pythonbrew download -PYTHONBREW_UPDATE_URL_HEAD = _get_or_default('pythonbrew', 'head') +PYTHONBREW_UPDATE_URL_MASTER = _get_or_default('pythonbrew', 'master') +PYTHONBREW_UPDATE_URL_DEVELOP = _get_or_default('pythonbrew', 'develop') PYTHONBREW_UPDATE_URL_PYPI = _get_or_default('pythonbrew', 'pypi') PYTHONBREW_UPDATE_URL_CONFIG = _get_or_default('pythonbrew', 'config') diff --git a/pythonbrew/downloader.py b/pythonbrew/downloader.py index f5266a1..ee21c02 100644 --- a/pythonbrew/downloader.py +++ b/pythonbrew/downloader.py @@ -1,8 +1,9 @@ from pythonbrew.define import PYTHON_VERSION_URL, PYTHONBREW_STABLE_VERSION_URL, \ - PYTHONBREW_UPDATE_URL_PYPI, PYTHONBREW_UPDATE_URL_HEAD + PYTHONBREW_UPDATE_URL_PYPI, PYTHONBREW_UPDATE_URL_MASTER,\ + PYTHONBREW_UPDATE_URL_DEVELOP from pythonbrew.log import logger from pythonbrew.curl import Curl -from pythonbrew.util import u +from pythonbrew.util import to_str def get_headerinfo_from_url(url): c = Curl() @@ -10,7 +11,7 @@ def get_headerinfo_from_url(url): def get_stable_version(): c = Curl() - return u(c.read(PYTHONBREW_STABLE_VERSION_URL).strip()) + return to_str(c.read(PYTHONBREW_STABLE_VERSION_URL).strip()) class Downloader(object): def download(self, msg, url, path): @@ -19,8 +20,10 @@ def download(self, msg, url, path): c.fetch(url, path) def get_pythonbrew_update_url(version): - if version == "head": - return PYTHONBREW_UPDATE_URL_HEAD + if version == "master": + return PYTHONBREW_UPDATE_URL_MASTER + elif version == 'develop': + return PYTHONBREW_UPDATE_URL_DEVELOP else: return PYTHONBREW_UPDATE_URL_PYPI % (version) diff --git a/pythonbrew/etc/config.cfg b/pythonbrew/etc/config.cfg index cf6ec26..dbdc698 100644 --- a/pythonbrew/etc/config.cfg +++ b/pythonbrew/etc/config.cfg @@ -2,7 +2,8 @@ url = http://python-distribute.org/distribute_setup.py [pythonbrew] -head = https://github.com/utahta/pythonbrew/tarball/master +master = https://github.com/utahta/pythonbrew/tarball/master +develop = https://github.com/utahta/pythonbrew/tarball/develop pypi = http://pypi.python.org/packages/source/p/pythonbrew/pythonbrew-%%s.tar.gz stable-version = https://github.com/utahta/pythonbrew/raw/master/stable-version.txt config = https://github.com/utahta/pythonbrew/raw/master/pythonbrew/etc/config.cfg diff --git a/pythonbrew/installer/__init__.py b/pythonbrew/installer/__init__.py index 2cbc24e..f4362e4 100644 --- a/pythonbrew/installer/__init__.py +++ b/pythonbrew/installer/__init__.py @@ -20,8 +20,8 @@ def install_pythonbrew(): After that, exit this shell, start a new one, and install some fresh pythons: - pythonbrew install 2.6.6 - pythonbrew install 2.5.5 + pythonbrew install 2.7.2 + pythonbrew install 3.2 For further instructions, run: diff --git a/pythonbrew/installer/pythoninstaller.py b/pythonbrew/installer/pythoninstaller.py index 87f31f6..50a6a77 100644 --- a/pythonbrew/installer/pythoninstaller.py +++ b/pythonbrew/installer/pythoninstaller.py @@ -5,12 +5,13 @@ from pythonbrew.util import makedirs, symlink, Package, is_url, Link,\ unlink, is_html, Subprocess, rm_r,\ is_python25, is_python24, is_python26, is_python27,\ - unpack_downloadfile, is_archive_file, path_to_fileurl, is_file,\ - fileurl_to_path + extract_downloadfile, is_archive_file, path_to_fileurl, is_file,\ + fileurl_to_path, is_python30, is_python31, is_python32,\ + get_macosx_deployment_target from pythonbrew.define import PATH_BUILD, PATH_DISTS, PATH_PYTHONS,\ ROOT, PATH_LOG, DISTRIBUTE_SETUP_DLSITE,\ PATH_PATCHES_MACOSX_PYTHON25, PATH_PATCHES_MACOSX_PYTHON24,\ - PATH_PATCHES_MACOSX_PYTHON26, PATH_PATCHES_MACOSX_PYTHON27 + PATH_PATCHES_MACOSX_PYTHON26, PATH_PATCHES_MACOSX_PYTHON27, PATH_PATCHES_ALL from pythonbrew.downloader import get_python_version_url, Downloader,\ get_headerinfo_from_url from pythonbrew.log import logger @@ -22,15 +23,13 @@ class PythonInstaller(object): """ def __init__(self, arg, options): - if is_url(arg): - name = arg - elif is_archive_file(arg): + if is_archive_file(arg): name = path_to_fileurl(arg) elif os.path.isdir(arg): name = path_to_fileurl(arg) else: name = arg - + if is_url(name): self.download_url = name filename = Link(self.download_url).filename @@ -46,25 +45,35 @@ def __init__(self, arg, options): self.install_dir = os.path.join(PATH_PYTHONS, pkg.name) self.build_dir = os.path.join(PATH_BUILD, pkg.name) self.download_file = os.path.join(PATH_DISTS, filename) + + # cleanup + if os.path.isdir(self.build_dir): + shutil.rmtree(self.build_dir) + + # get content type. if is_file(self.download_url): path = fileurl_to_path(self.download_url) self.content_type = mimetypes.guess_type(path)[0] else: headerinfo = get_headerinfo_from_url(self.download_url) self.content_type = headerinfo['content-type'] + if is_html(self.content_type): + # note: maybe got 404 or 503 http status code. + logger.error("Invalid content-type: `%s`" % self.content_type) + return + self.options = options self.logfile = os.path.join(PATH_LOG, 'build.log') self.configure_options = '' + self.patches = [] def install(self): if os.path.isdir(self.install_dir): logger.info("You are already installed `%s`" % self.pkg.name) raise AlreadyInstalledException - self.download_unpack() - logger.info("") - logger.info("This could take a while. You can run the following command on another shell to track the status:") - logger.info(" tail -f %s" % self.logfile) - logger.info("") + self.download_and_extract() + logger.info("\nThis could take a while. You can run the following command on another shell to track the status:") + logger.info(" tail -f %s\n" % self.logfile) self.patch() logger.info("Installing %s into %s" % (self.pkg.name, self.install_dir)) try: @@ -81,57 +90,105 @@ def install(self): logger.info("Installed %(pkgname)s successfully. Run the following command to switch to %(pkgname)s." % {"pkgname":self.pkg.name}) logger.info(" pythonbrew switch %s" % self.pkg.alias) - - def download_unpack(self): - content_type = self.content_type - if is_html(content_type): - logger.error("Invalid content-type: `%s`" % content_type) - sys.exit(1) + + def download_and_extract(self): if is_file(self.download_url): path = fileurl_to_path(self.download_url) if os.path.isdir(path): logger.info('Copying %s into %s' % (path, self.build_dir)) - if os.path.isdir(self.build_dir): - shutil.rmtree(self.build_dir) shutil.copytree(path, self.build_dir) return if os.path.isfile(self.download_file): logger.info("Use the previously fetched %s" % (self.download_file)) else: - msg = Link(self.download_url).show_msg + base_url = Link(self.download_url).base_url try: dl = Downloader() - dl.download(msg, self.download_url, self.download_file) + dl.download(base_url, self.download_url, self.download_file) except: unlink(self.download_file) logger.info("\nInterrupt to abort. `%s`" % (self.download_url)) sys.exit(1) - # unpack - if not unpack_downloadfile(self.content_type, self.download_file, self.build_dir): + # extracting + if not extract_downloadfile(self.content_type, self.download_file, self.build_dir): sys.exit(1) def patch(self): - pass - + version = self.pkg.version + # for ubuntu 11.04(Natty) + if is_python24(version): + patch_dir = os.path.join(PATH_PATCHES_ALL, "python24") + self._add_patches_to_list(patch_dir, ['patch-setup.py.diff']) + elif is_python25(version): + patch_dir = os.path.join(PATH_PATCHES_ALL, "python25") + self._add_patches_to_list(patch_dir, ['patch-setup.py.diff']) + elif is_python26(version): + patch_dir = os.path.join(PATH_PATCHES_ALL, "common") + self._add_patches_to_list(patch_dir, ['patch-setup.py.diff']) + elif is_python27(version): + if version < '2.7.2': + patch_dir = os.path.join(PATH_PATCHES_ALL, "common") + self._add_patches_to_list(patch_dir, ['patch-setup.py.diff']) + elif is_python30(version): + patch_dir = os.path.join(PATH_PATCHES_ALL, "python30") + self._add_patches_to_list(patch_dir, ['patch-setup.py.diff']) + elif is_python31(version): + if version < '3.1.4': + patch_dir = os.path.join(PATH_PATCHES_ALL, "common") + self._add_patches_to_list(patch_dir, ['patch-setup.py.diff']) + elif is_python32(version): + if version == '3.2': + patch_dir = os.path.join(PATH_PATCHES_ALL, "python32") + self._add_patches_to_list(patch_dir, ['patch-setup.py.diff']) + self._do_patch() + + def _do_patch(self): + try: + s = Subprocess(log=self.logfile, cwd=self.build_dir, verbose=self.options.verbose) + if self.patches: + logger.info("Patching %s" % self.pkg.name) + for patch in self.patches: + if type(patch) is dict: + for (ed, source) in patch.items(): + s.shell('ed - %s < %s' % (source, ed)) + else: + s.shell("patch -p0 < %s" % patch) + except: + logger.error("Failed to patch `%s`" % self.build_dir) + sys.exit(1) + + def _add_patches_to_list(self, patch_dir, patch_files): + for patch in patch_files: + if type(patch) is dict: + tmp = patch + patch = {} + for key in tmp.keys(): + patch[os.path.join(patch_dir, key)] = tmp[key] + self.patches.append(patch) + else: + self.patches.append(os.path.join(patch_dir, patch)) + def configure(self): - s = Subprocess(log=self.logfile, cwd=self.build_dir) + s = Subprocess(log=self.logfile, cwd=self.build_dir, verbose=self.options.verbose) s.check_call("./configure --prefix=%s %s %s" % (self.install_dir, self.options.configure, self.configure_options)) def make(self): jobs = self.options.jobs make = ((jobs > 0 and 'make -j%s' % jobs) or 'make') - s = Subprocess(log=self.logfile, cwd=self.build_dir) - if self.options.force: - s.check_call(make) - else: - s.check_call(make) - s.check_call("make test") + s = Subprocess(log=self.logfile, cwd=self.build_dir, verbose=self.options.verbose) + s.check_call(make) + if not self.options.no_test: + if self.options.force: + # note: ignore tests failure error. + s.call("make test") + else: + s.check_call("make test") def make_install(self): version = self.pkg.version if version == "1.5.2" or version == "1.6.1": makedirs(self.install_dir) - s = Subprocess(log=self.logfile, cwd=self.build_dir) + s = Subprocess(log=self.logfile, cwd=self.build_dir, verbose=self.options.verbose) s.check_call("make install") def symlink(self): @@ -149,7 +206,7 @@ def install_setuptools(self): options = self.options pkgname = self.pkg.name if options.no_setuptools: - logger.info("Skip installation setuptools.") + logger.info("Skip installation of setuptools.") return download_url = DISTRIBUTE_SETUP_DLSITE filename = Link(download_url).filename @@ -161,94 +218,79 @@ def install_setuptools(self): install_dir = os.path.join(PATH_PYTHONS, pkgname) path_python = os.path.join(install_dir,"bin","python") try: - s = Subprocess(log=self.logfile, cwd=PATH_DISTS) + s = Subprocess(log=self.logfile, cwd=PATH_DISTS, verbose=self.options.verbose) logger.info("Installing distribute into %s" % install_dir) - s.check_call("%s %s" % (path_python, filename)) - # Using easy_install install pip + s.check_call([path_python, filename]) + # installing pip easy_install = os.path.join(install_dir, 'bin', 'easy_install') if os.path.isfile(easy_install): logger.info("Installing pip into %s" % install_dir) - s.check_call("%s pip" % (easy_install)) + s.check_call([easy_install, 'pip']) except: logger.error("Failed to install setuptools. See %s/build.log to see why." % (ROOT)) - logger.info("Skip install setuptools.") + logger.info("Skip installation of setuptools.") class PythonInstallerMacOSX(PythonInstaller): - """Python installer for MacOSX SnowLeopard + """Python installer for MacOSX """ def __init__(self, arg, options): super(PythonInstallerMacOSX, self).__init__(arg, options) - version = self.pkg.version + # check for version + version = self.pkg.version if version < '2.6' and (version != '2.4.6' and version < '2.5.5'): logger.info("`%s` is not supported on MacOSX Snow Leopard" % self.pkg.name) raise NotSupportedVersionException # set configure options + target = get_macosx_deployment_target() + if target: + self.configure_options = 'MACOSX_DEPLOYMENT_TARGET=%s' % target + + # note: skip `make test` to avoid hanging test_threading. + if is_python25(version) or is_python24(version): + self.options.no_test = True + + def patch(self): + # note: want an interface to the source patching functionality. like a patchperl. + version = self.pkg.version if is_python24(version): - self.configure_options = '--with-universal-archs="intel" MACOSX_DEPLOYMENT_TARGET=10.6 CPPFLAGS="-D__DARWIN_UNIX03"' + patch_dir = PATH_PATCHES_MACOSX_PYTHON24 + self._add_patches_to_list(patch_dir, ['patch-configure', 'patch-Makefile.pre.in', + 'patch-Lib-cgi.py.diff', 'patch-Lib-site.py.diff', + 'patch-setup.py.diff', 'patch-Include-pyport.h', + 'patch-Mac-OSX-Makefile.in', 'patch-Mac-OSX-IDLE-Makefile.in', + 'patch-Mac-OSX-PythonLauncher-Makefile.in', 'patch-configure-badcflags.diff', + 'patch-configure-arch_only.diff', 'patch-macosmodule.diff', + 'patch-mactoolboxglue.diff', 'patch-pymactoolbox.diff', + 'patch-gestaltmodule.c.diff']) elif is_python25(version): - self.configure_options = '--with-universal-archs="intel" MACOSX_DEPLOYMENT_TARGET=10.6 CPPFLAGS="-D_DARWIN_C_SOURCE"' + patch_dir = PATH_PATCHES_MACOSX_PYTHON25 + self._add_patches_to_list(patch_dir, ['patch-Makefile.pre.in.diff', + 'patch-Lib-cgi.py.diff', + 'patch-Lib-distutils-dist.py.diff', + 'patch-setup.py.diff', + 'patch-configure-badcflags.diff', + 'patch-configure-arch_only.diff', + 'patch-64bit.diff', + 'patch-pyconfig.h.in.diff', + 'patch-gestaltmodule.c.diff', + {'_localemodule.c.ed': 'Modules/_localemodule.c'}, + {'locale.py.ed': 'Lib/locale.py'}]) elif is_python26(version): - self.configure_options = '--with-universal-archs="intel" --enable-universalsdk=/ MACOSX_DEPLOYMENT_TARGET=10.6' + patch_dir = PATH_PATCHES_MACOSX_PYTHON26 + self._add_patches_to_list(patch_dir, ['patch-Lib-cgi.py.diff', + 'patch-Lib-distutils-dist.py.diff', + 'patch-Mac-IDLE-Makefile.in.diff', + 'patch-Mac-Makefile.in.diff', + 'patch-Mac-PythonLauncher-Makefile.in.diff', + 'patch-Mac-Tools-Doc-setup.py.diff', + 'patch-setup.py-db46.diff', + 'patch-Lib-ctypes-macholib-dyld.py.diff', + 'patch-setup_no_tkinter.py.diff', + {'_localemodule.c.ed': 'Modules/_localemodule.c'}, + {'locale.py.ed': 'Lib/locale.py'}]) elif is_python27(version): - self.configure_options = '--with-universal-archs="intel" --enable-universalsdk=/ MACOSX_DEPLOYMENT_TARGET=10.6' - else: - self.configure_options = '--with-universal-archs="intel" --enable-universalsdk=/ MACOSX_DEPLOYMENT_TARGET=10.6' - - def patch(self): - version = self.pkg.version - try: - s = Subprocess(log=self.logfile, cwd=self.build_dir) - patches = [] - eds = {} - if is_python24(version): - patch_dir = PATH_PATCHES_MACOSX_PYTHON24 - patches = ['patch-configure', 'patch-Makefile.pre.in', - 'patch-Lib-cgi.py.diff', 'patch-Lib-site.py.diff', - 'patch-setup.py.diff', 'patch-Include-pyport.h', - 'patch-Mac-OSX-Makefile.in', 'patch-Mac-OSX-IDLE-Makefile.in', - 'patch-Mac-OSX-PythonLauncher-Makefile.in', 'patch-configure-badcflags.diff', - 'patch-configure-arch_only.diff', 'patch-macosmodule.diff', - 'patch-mactoolboxglue.diff', 'patch-pymactoolbox.diff', - 'patch-gestaltmodule.c.diff'] - elif is_python25(version): - patch_dir = PATH_PATCHES_MACOSX_PYTHON25 - patches = ['patch-Makefile.pre.in.diff', - 'patch-Lib-cgi.py.diff', - 'patch-Lib-distutils-dist.py.diff', - 'patch-setup.py.diff', - 'patch-configure-badcflags.diff', - 'patch-configure-arch_only.diff', - 'patch-64bit.diff', - 'patch-pyconfig.h.in.diff', - 'patch-gestaltmodule.c.diff'] - eds = {'_localemodule.c.ed': 'Modules/_localemodule.c', - 'locale.py.ed': 'Lib/locale.py'} - elif is_python26(version): - patch_dir = PATH_PATCHES_MACOSX_PYTHON26 - patches = ['patch-Lib-cgi.py.diff', - 'patch-Lib-distutils-dist.py.diff', - 'patch-Mac-IDLE-Makefile.in.diff', - 'patch-Mac-Makefile.in.diff', - 'patch-Mac-PythonLauncher-Makefile.in.diff', - 'patch-Mac-Tools-Doc-setup.py.diff', - 'patch-setup.py-db46.diff', - 'patch-Lib-ctypes-macholib-dyld.py.diff', - 'patch-setup_no_tkinter.py.diff'] - eds = {'_localemodule.c.ed': 'Modules/_localemodule.c', - 'locale.py.ed': 'Lib/locale.py'} - elif is_python27(version): - patch_dir = PATH_PATCHES_MACOSX_PYTHON27 - patches = ['patch-Modules-posixmodule.diff'] - - if patches or eds: - logger.info("Patching %s" % self.pkg.name) - for patch in patches: - s.check_call("patch -p0 < %s" % os.path.join(patch_dir, patch)) - for (ed, source) in eds.items(): - ed = os.path.join(patch_dir, ed) - s.check_call('ed - %s < %s' % (source, ed)) - except: - logger.error("Failed to patch `%s`" % self.build_dir) - sys.exit(1) - + patch_dir = PATH_PATCHES_MACOSX_PYTHON27 + self._add_patches_to_list(patch_dir, ['patch-Modules-posixmodule.diff']) + + self._do_patch() diff --git a/pythonbrew/patches/all/common/patch-setup.py.diff b/pythonbrew/patches/all/common/patch-setup.py.diff new file mode 100644 index 0000000..b2998bc --- /dev/null +++ b/pythonbrew/patches/all/common/patch-setup.py.diff @@ -0,0 +1,47 @@ + +# HG changeset patch +# User Barry Warsaw +# Date 1302190091 14400 +# Node ID bd0f73a9538e05f526feaf05821e68bdcff498fa +# Parent 2e4cdaffe493e879fb5367a4aa454491de451137 +Backport for Python 2.7 of issue 11715 support for building Python on +multiarch Debian/Ubuntu. + +diff --git a/setup.py b/setup.py +--- setup.py.orig ++++ setup.py +@@ -345,10 +345,33 @@ class PyBuildExt(build_ext): + return platform + return sys.platform + ++ def add_multiarch_paths(self): ++ # Debian/Ubuntu multiarch support. ++ # https://wiki.ubuntu.com/MultiarchSpec ++ if not find_executable('dpkg-architecture'): ++ return ++ tmpfile = os.path.join(self.build_temp, 'multiarch') ++ if not os.path.exists(self.build_temp): ++ os.makedirs(self.build_temp) ++ ret = os.system( ++ 'dpkg-architecture -qDEB_HOST_MULTIARCH > %s 2> /dev/null' % ++ tmpfile) ++ try: ++ if ret >> 8 == 0: ++ with open(tmpfile) as fp: ++ multiarch_path_component = fp.readline().strip() ++ add_dir_to_list(self.compiler.library_dirs, ++ '/usr/lib/' + multiarch_path_component) ++ add_dir_to_list(self.compiler.include_dirs, ++ '/usr/include/' + multiarch_path_component) ++ finally: ++ os.unlink(tmpfile) ++ + def detect_modules(self): + # Ensure that /usr/local is always used + add_dir_to_list(self.compiler.library_dirs, '/usr/local/lib') + add_dir_to_list(self.compiler.include_dirs, '/usr/local/include') ++ self.add_multiarch_paths() + + # Add paths specified in the environment variables LDFLAGS and + # CPPFLAGS for header and library files. + diff --git a/pythonbrew/patches/all/python24/patch-setup.py.diff b/pythonbrew/patches/all/python24/patch-setup.py.diff new file mode 100644 index 0000000..ffe4b0d --- /dev/null +++ b/pythonbrew/patches/all/python24/patch-setup.py.diff @@ -0,0 +1,45 @@ +--- setup.py.orig 2011-07-07 19:19:43.800122463 +0900 ++++ setup.py 2011-07-07 19:25:04.548416377 +0900 +@@ -13,6 +13,7 @@ + from distutils.command.build_ext import build_ext + from distutils.command.install import install + from distutils.command.install_lib import install_lib ++from distutils.spawn import find_executable + + # This global variable is used to hold the list of modules to be disabled. + disabled_module_list = [] +@@ -242,10 +243,34 @@ + return platform + return sys.platform + ++ def add_multiarch_paths(self): ++ # Debian/Ubuntu multiarch support. ++ # https://wiki.ubuntu.com/MultiarchSpec ++ if not find_executable('dpkg-architecture'): ++ return ++ tmpfile = os.path.join(self.build_temp, 'multiarch') ++ if not os.path.exists(self.build_temp): ++ os.makedirs(self.build_temp) ++ ret = os.system( ++ 'dpkg-architecture -qDEB_HOST_MULTIARCH > %s 2> /dev/null' % ++ tmpfile) ++ try: ++ if ret >> 8 == 0: ++ fp = open(tmpfile) ++ multiarch_path_component = fp.readline().strip() ++ fp.close() ++ add_dir_to_list(self.compiler.library_dirs, ++ '/usr/lib/' + multiarch_path_component) ++ add_dir_to_list(self.compiler.include_dirs, ++ '/usr/include/' + multiarch_path_component) ++ finally: ++ os.unlink(tmpfile) ++ + def detect_modules(self): + # Ensure that /usr/local is always used + add_dir_to_list(self.compiler.library_dirs, '/usr/local/lib') + add_dir_to_list(self.compiler.include_dirs, '/usr/local/include') ++ self.add_multiarch_paths() + + # Add paths specified in the environment variables LDFLAGS and + # CPPFLAGS for header and library files. diff --git a/pythonbrew/patches/all/python25/patch-setup.py.diff b/pythonbrew/patches/all/python25/patch-setup.py.diff new file mode 100644 index 0000000..ffe4b0d --- /dev/null +++ b/pythonbrew/patches/all/python25/patch-setup.py.diff @@ -0,0 +1,45 @@ +--- setup.py.orig 2011-07-07 19:19:43.800122463 +0900 ++++ setup.py 2011-07-07 19:25:04.548416377 +0900 +@@ -13,6 +13,7 @@ + from distutils.command.build_ext import build_ext + from distutils.command.install import install + from distutils.command.install_lib import install_lib ++from distutils.spawn import find_executable + + # This global variable is used to hold the list of modules to be disabled. + disabled_module_list = [] +@@ -242,10 +243,34 @@ + return platform + return sys.platform + ++ def add_multiarch_paths(self): ++ # Debian/Ubuntu multiarch support. ++ # https://wiki.ubuntu.com/MultiarchSpec ++ if not find_executable('dpkg-architecture'): ++ return ++ tmpfile = os.path.join(self.build_temp, 'multiarch') ++ if not os.path.exists(self.build_temp): ++ os.makedirs(self.build_temp) ++ ret = os.system( ++ 'dpkg-architecture -qDEB_HOST_MULTIARCH > %s 2> /dev/null' % ++ tmpfile) ++ try: ++ if ret >> 8 == 0: ++ fp = open(tmpfile) ++ multiarch_path_component = fp.readline().strip() ++ fp.close() ++ add_dir_to_list(self.compiler.library_dirs, ++ '/usr/lib/' + multiarch_path_component) ++ add_dir_to_list(self.compiler.include_dirs, ++ '/usr/include/' + multiarch_path_component) ++ finally: ++ os.unlink(tmpfile) ++ + def detect_modules(self): + # Ensure that /usr/local is always used + add_dir_to_list(self.compiler.library_dirs, '/usr/local/lib') + add_dir_to_list(self.compiler.include_dirs, '/usr/local/include') ++ self.add_multiarch_paths() + + # Add paths specified in the environment variables LDFLAGS and + # CPPFLAGS for header and library files. diff --git a/pythonbrew/patches/all/python30/patch-setup.py.diff b/pythonbrew/patches/all/python30/patch-setup.py.diff new file mode 100644 index 0000000..8f1a177 --- /dev/null +++ b/pythonbrew/patches/all/python30/patch-setup.py.diff @@ -0,0 +1,44 @@ +--- setup.py.orig 2011-07-07 19:41:48.610196111 +0900 ++++ setup.py 2011-07-07 19:46:44.986310031 +0900 +@@ -14,6 +14,7 @@ + from distutils.command.build_ext import build_ext + from distutils.command.install import install + from distutils.command.install_lib import install_lib ++from distutils.spawn import find_executable + + # This global variable is used to hold the list of modules to be disabled. + disabled_module_list = [] +@@ -308,10 +309,33 @@ + return platform + return sys.platform + ++ def add_multiarch_paths(self): ++ # Debian/Ubuntu multiarch support. ++ # https://wiki.ubuntu.com/MultiarchSpec ++ if not find_executable('dpkg-architecture'): ++ return ++ tmpfile = os.path.join(self.build_temp, 'multiarch') ++ if not os.path.exists(self.build_temp): ++ os.makedirs(self.build_temp) ++ ret = os.system( ++ 'dpkg-architecture -qDEB_HOST_MULTIARCH > %s 2> /dev/null' % ++ tmpfile) ++ try: ++ if ret >> 8 == 0: ++ with open(tmpfile) as fp: ++ multiarch_path_component = fp.readline().strip() ++ add_dir_to_list(self.compiler.library_dirs, ++ '/usr/lib/' + multiarch_path_component) ++ add_dir_to_list(self.compiler.include_dirs, ++ '/usr/include/' + multiarch_path_component) ++ finally: ++ os.unlink(tmpfile) ++ + def detect_modules(self): + # Ensure that /usr/local is always used + add_dir_to_list(self.compiler.library_dirs, '/usr/local/lib') + add_dir_to_list(self.compiler.include_dirs, '/usr/local/include') ++ self.add_multiarch_paths() + + # Add paths specified in the environment variables LDFLAGS and + # CPPFLAGS for header and library files. diff --git a/pythonbrew/patches/all/python32/patch-setup.py.diff b/pythonbrew/patches/all/python32/patch-setup.py.diff new file mode 100644 index 0000000..f4c361e --- /dev/null +++ b/pythonbrew/patches/all/python32/patch-setup.py.diff @@ -0,0 +1,38 @@ +--- setup.py.orig 2011-07-07 20:26:15.000000000 +0900 ++++ setup.py 2011-07-07 20:29:28.735543350 +0900 +@@ -370,12 +370,35 @@ + return platform + return sys.platform + ++ def add_multiarch_paths(self): ++ # Debian/Ubuntu multiarch support. ++ # https://wiki.ubuntu.com/MultiarchSpec ++ if not find_executable('dpkg-architecture'): ++ return ++ tmpfile = os.path.join(self.build_temp, 'multiarch') ++ if not os.path.exists(self.build_temp): ++ os.makedirs(self.build_temp) ++ ret = os.system( ++ 'dpkg-architecture -qDEB_HOST_MULTIARCH > %s 2> /dev/null' % ++ tmpfile) ++ try: ++ if ret >> 8 == 0: ++ with open(tmpfile) as fp: ++ multiarch_path_component = fp.readline().strip() ++ add_dir_to_list(self.compiler.library_dirs, ++ '/usr/lib/' + multiarch_path_component) ++ add_dir_to_list(self.compiler.include_dirs, ++ '/usr/include/' + multiarch_path_component) ++ finally: ++ os.unlink(tmpfile) ++ + def detect_modules(self): + # Ensure that /usr/local is always used, but the local build + # directories (i.e. '.' and 'Include') must be first. See issue + # 10520. + add_dir_to_list(self.compiler.library_dirs, '/usr/local/lib') + add_dir_to_list(self.compiler.include_dirs, '/usr/local/include') ++ self.add_multiarch_paths() + + # Add paths specified in the environment variables LDFLAGS and + # CPPFLAGS for header and library files. diff --git a/pythonbrew/util.py b/pythonbrew/util.py index 50e7ed3..457e936 100644 --- a/pythonbrew/util.py +++ b/pythonbrew/util.py @@ -1,17 +1,17 @@ import os +import sys import errno import shutil -import subprocess import re import posixpath import tarfile import platform import urllib -from subprocess import PIPE, Popen -from pythonbrew.define import PATH_BIN, PATH_PYTHONS, PATH_ETC_CURRENT +import subprocess +import shlex +from pythonbrew.define import PATH_BIN, PATH_ETC_CURRENT from pythonbrew.exceptions import ShellCommandException from pythonbrew.log import logger -import sys def size_format(b): kb = 1000 @@ -61,9 +61,15 @@ def is_gzip(content_type, filename): return True return False -def is_macosx_snowleopard(): +def is_macosx(): mac_ver = platform.mac_ver()[0] - return mac_ver >= '10.6' and mac_ver < '10.7' + return mac_ver >= '10.6' + +def get_macosx_deployment_target(): + m = re.search('^([0-9]+\.[0-9]+)', platform.mac_ver()[0]) + if m: + return m.group(1) + return None def is_python24(version): return version >= '2.4' and version < '2.5' @@ -77,6 +83,15 @@ def is_python26(version): def is_python27(version): return version >= '2.7' and version < '2.8' +def is_python30(version): + return version >= '3.0' and version < '3.1' + +def is_python31(version): + return version >= '3.1' and version < '3.2' + +def is_python32(version): + return version >= '3.2' and version < '3.3' + def makedirs(path): try: os.makedirs(path) @@ -107,12 +122,6 @@ def rm_r(path): unlink(path) def off(): - for root, dirs, files in os.walk(PATH_BIN): - for f in files: - if f == "pythonbrew" or f == "pybrew": - continue - unlink("%s/%s" % (root, f)) - unlink("%s/current" % PATH_PYTHONS) set_current_path(PATH_BIN) def split_leading_dir(path): @@ -142,7 +151,7 @@ def has_leading_dir(paths): def untar_file(filename, location): if not os.path.exists(location): - makedirs(location) + os.makedirs(location) if filename.lower().endswith('.gz') or filename.lower().endswith('.tgz'): mode = 'r:gz' elif filename.lower().endswith('.bz2') or filename.lower().endswith('.tbz'): @@ -186,12 +195,15 @@ def untar_file(filename, location): shutil.copyfileobj(fp, destfp) finally: destfp.close() - os.chmod(path, member.mode) fp.close() + # note: configure ...etc + os.chmod(path, member.mode) + # note: the file timestamps should be such that asdl_c.py is not invoked. + os.utime(path, (member.mtime, member.mtime)) finally: tar.close() -def unpack_downloadfile(content_type, download_file, target_dir): +def extract_downloadfile(content_type, download_file, target_dir): logger.info("Extracting %s into %s" % (os.path.basename(download_file), target_dir)) if is_gzip(content_type, download_file): untar_file(download_file, target_dir) @@ -201,12 +213,10 @@ def unpack_downloadfile(content_type, download_file, target_dir): return True def get_current_python_path(): - p = Popen('command -v python', stdout=PIPE, shell=True) - p.wait() - if p.returncode == 0: - return p.stdout.read().strip() - else: - return None + """return: python path or '' + """ + p = subprocess.Popen('command -v python', stdout=subprocess.PIPE, shell=True) + return to_str(p.communicate()[0].strip()) def set_current_path(path): fp = open(PATH_ETC_CURRENT, 'w') @@ -225,40 +235,71 @@ def fileurl_to_path(url): url = '/' + url[len('file:'):].lstrip('/') return urllib.unquote(url) -def u(val): - """to unicode - """ +def to_str(val): try: - # for python3 - if type(val) == bytes: + # python3 + if type(val) is bytes: return val.decode() except: - if type(val) == str: - return val.decode("utf-8") - return val + if type(val) is unicode: + return val.encode("utf-8") + return val + +def is_str(val): + try: + # python2 + return isinstance(val, basestring) + except: + # python3 + return isinstance(val, str) + return False class Subprocess(object): - def __init__(self, log=None, shell=True, cwd=None, print_cmd=False): + def __init__(self, log=None, cwd=None, verbose=False, debug=False): self._log = log - self._shell = shell self._cwd = cwd - self._print_cmd = print_cmd + self._verbose = verbose + self._debug = debug def chdir(self, cwd): self._cwd = cwd - def check_call(self, cmd, shell=None, cwd=None): - if shell: - self._shell = shell - if cwd: - self._cwd = cwd - if self._print_cmd: + def shell(self, cmd): + if self._debug: logger.info(cmd) if self._log: - cmd = "(%s) >> '%s' 2>&1" % (cmd, self._log) - retcode = subprocess.call(cmd, shell=self._shell, cwd=self._cwd) - if retcode != 0: - raise ShellCommandException('Failed to `%s` command' % cmd) + if self._verbose: + cmd = "(%s) 2>&1 | tee '%s'" % (cmd, self._log) + else: + cmd = "(%s) >> '%s' 2>&1" % (cmd, self._log) + returncode = subprocess.call(cmd, shell=True, cwd=self._cwd) + if returncode: + raise ShellCommandException('%s: failed to `%s`' % (returncode, cmd)) + + def call(self, cmd): + if is_str(cmd): + cmd = shlex.split(cmd) + if self._debug: + logger.info(cmd) + + fp = ((self._log and open(self._log, 'a')) or None) + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=self._cwd) + while p.returncode is None: + p.poll() + line = to_str(p.stdout.readline()) + if self._verbose: + logger.info(line.strip()) + if fp: + fp.write(line) + fp.flush() + if fp: + fp.close() + return p.returncode + + def check_call(self, cmd): + returncode = self.call(cmd) + if returncode: + raise ShellCommandException('%s: failed to `%s`' % (returncode, cmd)) class Package(object): def __init__(self, name, alias=None): @@ -293,7 +334,7 @@ def filename(self): return name @property - def show_msg(self): + def base_url(self): return posixpath.basename(self._url.split('#', 1)[0].split('?', 1)[0]) diff --git a/stable-version.txt b/stable-version.txt index b09a54c..ce609ca 100644 --- a/stable-version.txt +++ b/stable-version.txt @@ -1 +1 @@ -0.7.3 \ No newline at end of file +0.8 \ No newline at end of file diff --git a/tests/test_01_update.py b/tests/test_01_update.py index 99e0f5f..328a86a 100644 --- a/tests/test_01_update.py +++ b/tests/test_01_update.py @@ -1,5 +1,6 @@ class UpdateOptions(object): - head = False + master = False + develop = False config = False force = False @@ -7,4 +8,4 @@ def test_update(): from pythonbrew.commands.update import UpdateCommand c = UpdateCommand() c.run_command(UpdateOptions(), None) - + \ No newline at end of file diff --git a/tests/test_04_install.py b/tests/test_04_install.py index f54e26f..6d0cd17 100644 --- a/tests/test_04_install.py +++ b/tests/test_04_install.py @@ -2,6 +2,8 @@ class InstallOptions(object): force = True + no_test = True + verbose = False configure = "" no_setuptools = False alias = None diff --git a/tests/test_11_clean.py b/tests/test_11_clean.py deleted file mode 100644 index 7823339..0000000 --- a/tests/test_11_clean.py +++ /dev/null @@ -1,4 +0,0 @@ -def test_clean(): - from pythonbrew.commands.clean import CleanCommand - c = CleanCommand() - c.run_command(None, None) diff --git a/tests/test_11_cleanup.py b/tests/test_11_cleanup.py new file mode 100644 index 0000000..3549334 --- /dev/null +++ b/tests/test_11_cleanup.py @@ -0,0 +1,4 @@ +def test_clean(): + from pythonbrew.commands.cleanup import CleanupCommand + c = CleanupCommand() + c.run_command(None, None)