summaryrefslogtreecommitdiff
path: root/release
diff options
context:
space:
mode:
authorAntoine Levy-Lambert <antoine@apache.org>2013-12-23 04:42:36 +0000
committerAntoine Levy-Lambert <antoine@apache.org>2013-12-23 04:42:36 +0000
commit99dcdaae87b5fca645cf71be632b23856da8a11c (patch)
tree4df1becba34a764ff6ed8960dbc41a6945300aca /release
parent5f266021facf97ea856bf968773538cfccf7cefd (diff)
downloadant-99dcdaae87b5fca645cf71be632b23856da8a11c.tar.gz
adding Mac OS X .pkg installer generation in build, PR 55899
git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@1553066 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'release')
-rwxr-xr-xrelease/build-osx-pkg.py179
1 files changed, 179 insertions, 0 deletions
diff --git a/release/build-osx-pkg.py b/release/build-osx-pkg.py
new file mode 100755
index 000000000..4144a03c4
--- /dev/null
+++ b/release/build-osx-pkg.py
@@ -0,0 +1,179 @@
+#!/usr/bin/env python
+
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Builds a Mac OS X .pkg from a binary ZIP archive of Apache Ant.
+
+import collections
+import contextlib
+import os
+
+ApacheAntURL = collections.namedtuple(
+ 'ApacheAntURL',
+ ('url', 'url_scheme', 'version', 'directory_name'))
+
+@contextlib.contextmanager
+def make_temp_directory():
+ '''Creates a temporary directory which is recursively deleted when out of scope.'''
+ import shutil
+ import tempfile
+ temp_dir = tempfile.mkdtemp()
+ yield temp_dir
+ shutil.rmtree(temp_dir)
+
+@contextlib.contextmanager
+def self_closing_url(url):
+ '''Opens a URL and returns a self-closing file-like object.'''
+ import urllib2
+ url_fp = urllib2.urlopen(url)
+ yield url_fp
+ url_fp.close()
+
+def apache_ant_url(url_string):
+ '''Parses a URL string into an ApacheAntURL object.'''
+ import argparse, collections, os.path, urlparse
+ parse_result = urlparse.urlparse(url_string)
+ filename = os.path.split(parse_result.path)[1]
+ if not (filename.startswith('apache-ant-') and filename.endswith('-bin.zip')):
+ raise argparse.ArgumentTypeError(
+ 'Expected [%s] to end with apache-ant-X.Y.Z-bin.zip' % (url_string))
+ extracted_directory = filename.replace('-bin.zip', '')
+ extracted_version = extracted_directory.replace('apache-ant-', '')
+ return ApacheAntURL(
+ url=url_string,
+ url_scheme=parse_result.scheme,
+ version=extracted_version,
+ directory_name=extracted_directory)
+
+def fetch_url(url, local_output_file):
+ '''Downloads the contents of 'url' and writes them the opened file 'output_file'.'''
+ import shutil
+ import urllib2
+ CHUNK_SIZE = 16 * 1024
+ print 'Fetching {url}...'.format(url=url)
+ with self_closing_url(url) as url_input_file:
+ while True:
+ chunk = url_input_file.read(CHUNK_SIZE)
+ if not chunk:
+ break
+ local_output_file.write(chunk)
+ local_output_file.seek(0)
+
+def fetch_apache_ant_url(apache_ant_url, temp_dir):
+ '''If the ApacheAntURL object is remote, fetches and returns the local file object.
+ Otherwise, opens and returns a file object.'''
+ import tempfile
+ if apache_ant_url.url_scheme == '' or apache_ant_url.url_scheme == 'file':
+ return open(apache_ant_url.url, 'rb')
+ else:
+ fp = tempfile.TemporaryFile(dir=temp_dir)
+ fetch_url(apache_ant_url.url, fp)
+ return fp
+
+def uncompress_contents(temp_dir, archive_file, directory_name, path_prefix):
+ '''Uncompresses the contents of 'archive_file' to 'temp_dir'.
+
+ Strips the prefix 'directory_name' and prepends 'path_prefix' to each entry
+ of the zip file.
+ '''
+ import shutil, zipfile
+ output_path = os.path.join(temp_dir, 'pkg')
+ os.mkdir(output_path)
+ z = zipfile.ZipFile(archive_file)
+ print 'Extracting archive to {output_path}...'.format(
+ output_path=output_path)
+ for entry in z.infolist():
+ # We can't just extract directly, since we want to map:
+ #
+ # apache-ant-X.Y.Z/bin/foo
+ #
+ # to
+ #
+ # usr/local/ant/bin/foo
+ #
+ # So, we strip out the apache-ant-X.Y.Z prefix, then instead of
+ # using ZipFile.extract(), we use ZipFile.open() to get a read fd to
+ # the source file, then os.fdopen() with the appropriate permissions
+ # to geta write fd to the modified destination path.
+ expected_prefix = directory_name + '/'
+ if not entry.filename.startswith(expected_prefix):
+ raise Exeption('Unexpected entry in zip file: [{filename}]'.format(
+ filename=entry.filename))
+ entry_path = entry.filename.replace(expected_prefix, '', 1)
+
+ # Using os.path.join is annoying here (we'd have to explode output_path
+ # and entry_path).
+ entry_output_path = output_path + path_prefix + '/' + entry_path
+
+ # Zip file paths are normalized with '/' at the end for directories.
+ if entry_output_path.endswith('/'):
+ print 'Creating directory {path}'.format(path=entry_output_path)
+ os.makedirs(entry_output_path)
+ else:
+ # Yes, this is really how you extract permissions from a ZipInfo entry.
+ perms = (entry.external_attr >> 16L) & 0777
+ print 'Extracting {entry_filename} to {path} with mode 0{mode:o}'.format(
+ entry_filename=entry.filename, path=entry_output_path, mode=perms)
+ with z.open(entry) as source:
+ with os.fdopen(
+ os.open(entry_output_path, os.O_WRONLY | os.O_CREAT, perms), 'w') \
+ as destination:
+ shutil.copyfileobj(source, destination)
+ return output_path
+
+def write_paths_d_entry(paths_d_directory, filename):
+ os.makedirs(paths_d_directory)
+ output_file = os.path.join(paths_d_directory, filename)
+ with open(output_file, 'w') as f:
+ print >>f, '/usr/local/ant/bin'
+
+def make_pkg(pkg_dir, pkg_identifier, pkg_version, output_pkg_path):
+ import subprocess
+ print 'Building package at {output_pkg_path}...'.format(
+ output_pkg_path=output_pkg_path)
+ subprocess.call(
+ ['pkgbuild',
+ '--root', pkg_dir,
+ '--identifier', pkg_identifier,
+ '--version', pkg_version,
+ output_pkg_path])
+
+def main():
+ import argparse
+ parser = argparse.ArgumentParser(description='Builds a Mac OS X .pkg of ant.')
+ parser.add_argument(
+ 'apache_ant_url',
+ metavar='file-or-url',
+ help='Source file or URL from which to uncompress apache-ant-X.Y.Z-bin.zip',
+ type=apache_ant_url)
+ parser.add_argument(
+ '--output-dir',
+ default='.',
+ help='Directory to which .pkg will be written. Defaults to current directory.')
+ args = parser.parse_args()
+ with make_temp_directory() as temp_dir:
+ archive_file = fetch_apache_ant_url(args.apache_ant_url, temp_dir)
+ pkg_dir = uncompress_contents(
+ temp_dir, archive_file, args.apache_ant_url.directory_name, '/usr/local/ant')
+ etc_paths_d_dir = os.path.join(pkg_dir, 'etc', 'paths.d')
+ write_paths_d_entry(etc_paths_d_dir, 'org.apache.ant')
+ pkg_identifier = 'org.apache.ant'
+ output_pkg_path = os.path.join(
+ args.output_dir, args.apache_ant_url.directory_name + '.pkg')
+ make_pkg(pkg_dir, pkg_identifier, args.apache_ant_url.version, output_pkg_path)
+
+if __name__ == '__main__':
+ main()