beng

Mac OS X pkg installer for Apache Ant

Dec 16th, 2013
175
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 7.27 KB | None | 0 0
  1. #!/usr/bin/env python
  2.  
  3. # Licensed to the Apache Software Foundation (ASF) under one or more
  4. # contributor license agreements.  See the NOTICE file distributed with
  5. # this work for additional information regarding copyright ownership.
  6. # The ASF licenses this file to You under the Apache License, Version 2.0
  7. # (the "License"); you may not use this file except in compliance with
  8. # the License.  You may obtain a copy of the License at
  9. #
  10. #     http://www.apache.org/licenses/LICENSE-2.0
  11. #
  12. # Unless required by applicable law or agreed to in writing, software
  13. # distributed under the License is distributed on an "AS IS" BASIS,
  14. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. # See the License for the specific language governing permissions and
  16. # limitations under the License.
  17.  
  18. # Builds a Mac OS X .pkg from a binary archive of Apache Ant.
  19.  
  20. import collections
  21. import contextlib
  22. import os
  23.  
  24. ApacheAntURL = collections.namedtuple(
  25.     'ApacheAntURL',
  26.     ('url', 'url_scheme', 'version', 'directory_name'))
  27.  
  28. @contextlib.contextmanager
  29. def make_temp_directory():
  30.     '''Creates a temporary directory which is recursively deleted when out of scope.'''
  31.     import shutil
  32.     import tempfile
  33.     temp_dir = tempfile.mkdtemp()
  34.     yield temp_dir
  35.     shutil.rmtree(temp_dir)
  36.  
  37. @contextlib.contextmanager
  38. def self_closing_url(url):
  39.     '''Opens a URL and returns a self-closing file-like object.'''
  40.     import urllib2
  41.     url_fp = urllib2.urlopen(url)
  42.     yield url_fp
  43.     url_fp.close()
  44.  
  45. def apache_ant_url(url_string):
  46.     '''Parses a URL string into an ApacheAntURL object.'''
  47.     import argparse, collections, os.path, urlparse
  48.     parse_result = urlparse.urlparse(url_string)
  49.     filename = os.path.split(parse_result.path)[1]
  50.     if not (filename.startswith('apache-ant-') and filename.endswith('-bin.zip')):
  51.         raise argparse.ArgumentTypeError(
  52.             'Expected [%s] to end with apache-ant-X.Y.Z-bin.zip' % (url_string))
  53.     extracted_directory = filename.replace('-bin.zip', '')
  54.     extracted_version = extracted_directory.replace('apache-ant-', '')
  55.     return ApacheAntURL(
  56.         url=url_string,
  57.         url_scheme=parse_result.scheme,
  58.         version=extracted_version,
  59.         directory_name=extracted_directory)
  60.  
  61. def fetch_url(url, local_output_file):
  62.     '''Downloads the contents of 'url' and writes them the opened file 'output_file'.'''
  63.     import shutil
  64.     import urllib2
  65.     CHUNK_SIZE = 16 * 1024
  66.     print 'Fetching {url}...'.format(url=url)
  67.     with self_closing_url(url) as url_input_file:
  68.         while True:
  69.             chunk = url_input_file.read(CHUNK_SIZE)
  70.             if not chunk:
  71.                 break
  72.             local_output_file.write(chunk)
  73.         local_output_file.seek(0)
  74.  
  75. def fetch_apache_ant_url(apache_ant_url, temp_dir):
  76.     '''If the ApacheAntURL object is remote, fetches and returns the local file object.
  77.    Otherwise, opens and returns a file object.'''
  78.     import tempfile
  79.     if apache_ant_url.url_scheme == '' or apache_ant_url.url_scheme == 'file':
  80.         return open(apache_ant_url.url, 'rb')
  81.     else:
  82.         fp = tempfile.TemporaryFile(dir=temp_dir)
  83.         fetch_url(apache_ant_url.url, fp)
  84.         return fp
  85.  
  86. def uncompress_contents(temp_dir, archive_file, directory_name, path_prefix):
  87.     '''Uncompresses the contents of 'archive_file' to 'temp_dir'.
  88.  
  89.    Strips the prefix 'directory_name' and prepends 'path_prefix' to each entry
  90.    of the zip file.
  91.    '''
  92.     import shutil, zipfile
  93.     output_path = os.path.join(temp_dir, 'pkg')
  94.     os.mkdir(output_path)
  95.     z = zipfile.ZipFile(archive_file)
  96.     print 'Extracting archive to {output_path}...'.format(
  97.         output_path=output_path)
  98.     for entry in z.infolist():
  99.         # We can't just extract directly, since we want to map:
  100.         #
  101.         # apache-ant-X.Y.Z/bin/foo
  102.         #
  103.         # to
  104.         #
  105.         # usr/local/ant/bin/foo
  106.         #
  107.         # So, we strip out the apache-ant-X.Y.Z prefix, then instead of
  108.         # using ZipFile.extract(), we use ZipFile.open() to get a read fd to
  109.         # the source file, then os.fdopen() with the appropriate permissions
  110.         # to geta write fd to the modified destination path.
  111.         expected_prefix = directory_name + '/'
  112.         if not entry.filename.startswith(expected_prefix):
  113.             raise Exeption('Unexpected entry in zip file: [{filename}]'.format(
  114.                     filename=entry.filename))
  115.         entry_path = entry.filename.replace(expected_prefix, '', 1)
  116.  
  117.         # Using os.path.join is annoying here (we'd have to explode output_path
  118.         # and entry_path).
  119.         entry_output_path = output_path + path_prefix + '/' + entry_path
  120.  
  121.         # Zip file paths are normalized with '/' at the end for directories.
  122.         if entry_output_path.endswith('/'):
  123.             print 'Creating directory {path}'.format(path=entry_output_path)
  124.             os.makedirs(entry_output_path)
  125.         else:
  126.             # Yes, this is really how you extract permissions from a ZipInfo entry.
  127.             perms = (entry.external_attr >> 16L) & 0777
  128.             print 'Extracting {entry_filename} to {path} with mode 0{mode:o}'.format(
  129.                 entry_filename=entry.filename, path=entry_output_path, mode=perms)
  130.             with z.open(entry) as source:
  131.                 with os.fdopen(
  132.                     os.open(entry_output_path, os.O_WRONLY | os.O_CREAT, perms), 'w') \
  133.                     as destination:
  134.                     shutil.copyfileobj(source, destination)
  135.     return output_path
  136.  
  137. def write_paths_d_entry(paths_d_directory, filename):
  138.     os.makedirs(paths_d_directory)
  139.     output_file = os.path.join(paths_d_directory, filename)
  140.     with open(output_file, 'w') as f:
  141.         print >>f, '/usr/local/ant/bin'
  142.  
  143. def make_pkg(pkg_dir, pkg_identifier, pkg_version, output_pkg_path):
  144.     import subprocess
  145.     print 'Building package at {output_pkg_path}...'.format(
  146.         output_pkg_path=output_pkg_path)
  147.     subprocess.call(
  148.         ['pkgbuild',
  149.          '--root', pkg_dir,
  150.          '--identifier', pkg_identifier,
  151.          '--version', pkg_version,
  152.          output_pkg_path])
  153.  
  154. def main():
  155.     import argparse
  156.     parser = argparse.ArgumentParser(description='Builds a Mac OS X .pkg of ant.')
  157.     parser.add_argument(
  158.         'apache_ant_url',
  159.         metavar='file-or-url',
  160.         help='Source file or URL from which to uncompress apache-ant-X.Y.Z-bin.zip',
  161.         type=apache_ant_url)
  162.     parser.add_argument(
  163.         '--output-dir',
  164.         default='.',
  165.         help='Directory to which .pkg will be written. Defaults to current directory.')
  166.     args = parser.parse_args()
  167.     with make_temp_directory() as temp_dir:
  168.         archive_file = fetch_apache_ant_url(args.apache_ant_url, temp_dir)
  169.         pkg_dir = uncompress_contents(
  170.             temp_dir, archive_file, args.apache_ant_url.directory_name, '/usr/local/ant')
  171.         etc_paths_d_dir = os.path.join(pkg_dir, 'etc', 'paths.d')
  172.         write_paths_d_entry(etc_paths_d_dir, 'org.apache.ant')
  173.         pkg_identifier = 'org.apache.ant'
  174.         output_pkg_path = os.path.join(
  175.             args.output_dir, args.apache_ant_url.directory_name + '.pkg')
  176.         make_pkg(pkg_dir, pkg_identifier, args.apache_ant_url.version, output_pkg_path)
  177.  
  178. if __name__ == '__main__':
  179.     main()
Advertisement
Add Comment
Please, Sign In to add comment