You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
194 lines
6.9 KiB
194 lines
6.9 KiB
9 years ago
|
#!/usr/bin/env python
|
||
|
from contextlib import contextmanager
|
||
|
import os
|
||
|
import biplist
|
||
|
import re
|
||
|
import shutil
|
||
|
import subprocess
|
||
|
import sys
|
||
|
|
||
|
HELP_MESSAGE = """
|
||
|
./build_distribution.py
|
||
|
"""
|
||
|
|
||
|
class Usage(Exception):
|
||
|
def __init__(self, msg):
|
||
|
super(Usage, self).__init__(msg)
|
||
|
self.msg = msg
|
||
|
|
||
|
def log(msg):
|
||
|
print >> sys.stderr, msg
|
||
|
|
||
|
@contextmanager
|
||
|
def chdir(path):
|
||
|
curdir = os.getcwd()
|
||
|
os.chdir(path)
|
||
|
try:
|
||
|
yield
|
||
|
finally:
|
||
|
os.chdir(curdir)
|
||
|
|
||
|
def escape_arg(argument):
|
||
|
"""Escapes an argument to a command line utility."""
|
||
|
argument = argument.replace('\\', "\\\\").replace("'", "\'").replace('"', '\\"').replace("!", "\\!").replace("`", "\\`")
|
||
|
return "\"%s\"" % argument
|
||
|
|
||
|
def run_command(command, verbose=False):
|
||
|
if verbose:
|
||
|
sys.stderr.write("Running: %s\n" % command)
|
||
|
p = subprocess.Popen(command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||
|
stdin, stdout = (p.stdin, p.stdout)
|
||
|
output = stdout.read()
|
||
|
output = output.strip("\n")
|
||
|
status = stdin.close()
|
||
|
stdout.close()
|
||
|
p.wait()
|
||
|
return (p.returncode, output)
|
||
|
|
||
|
class Builder(object):
|
||
|
COCOAPODS_DIST = "COCOAPODS_DIST"
|
||
|
BINARY_DIST = "BINARY_DIST"
|
||
|
build_root = "/tmp/apptentive_connect_build"
|
||
|
dist_type = None
|
||
|
def __init__(self, verbose=False, dist_type=None):
|
||
|
if not dist_type:
|
||
|
dist_type = self.BINARY_DIST
|
||
|
self.verbose = verbose
|
||
|
self.dist_type = dist_type
|
||
|
if dist_type not in [self.COCOAPODS_DIST, self.BINARY_DIST]:
|
||
|
log("Unknown dist_type: %s" % dist_type)
|
||
|
sys.exit(1)
|
||
|
|
||
|
def build(self):
|
||
|
# First, build the simulator target.
|
||
|
with chdir(self._project_dir()):
|
||
|
sim_build_command = self._build_command(is_simulator=True)
|
||
|
(status, output) = run_command(sim_build_command, verbose=self.verbose)
|
||
|
if status != 0:
|
||
|
log("Building for simulator failed with code: %d" % status)
|
||
|
log(output)
|
||
|
return False
|
||
|
dev_build_command = self._build_command()
|
||
|
(status, output) = run_command(dev_build_command, verbose=self.verbose)
|
||
|
if status != 0:
|
||
|
log("Building for device failed with code: %d" % status)
|
||
|
log(output)
|
||
|
return False
|
||
|
library_dir = self._output_dir()
|
||
|
try:
|
||
|
if os.path.exists(library_dir):
|
||
|
shutil.rmtree(library_dir)
|
||
|
os.makedirs(library_dir)
|
||
|
os.makedirs(os.path.join(library_dir, 'include'))
|
||
|
except Exception as e:
|
||
|
log("Exception %s" % e)
|
||
|
pass
|
||
|
if not os.path.exists(library_dir):
|
||
|
log("Unable to create output directory at: %s" % library_dir)
|
||
|
return False
|
||
|
(status, output) = run_command(self._lipo_command(), verbose=self.verbose)
|
||
|
if status != 0:
|
||
|
log("Unable to lipo static libraries")
|
||
|
log(output)
|
||
|
return False
|
||
|
paths_to_copy = [("source/ATConnect.h", "include/ATConnect.h"), ("source/Rating Flow/ATAppRatingFlow.h", "include/ATAppRatingFlow.h"), ("source/Surveys/ATSurveys.h", "include/ATSurveys.h"), ("../LICENSE.txt", "LICENSE.txt"), ("../README.md", "README.md"), ("../CHANGELOG.md", "CHANGELOG.md")]
|
||
|
for (project_path, destination_path) in paths_to_copy:
|
||
|
full_project_path = project_path
|
||
|
full_destination_path = os.path.join(self._output_dir(), destination_path)
|
||
|
(status, output) = self._ditto_file(full_project_path, full_destination_path)
|
||
|
if status != 0:
|
||
|
log("Unable to ditto project path: %s" % full_project_path)
|
||
|
log(output)
|
||
|
return False
|
||
|
# Copy the ApptentiveResources.bundle.
|
||
|
bundle_source = os.path.join(self._products_dir(), "ApptentiveResources.bundle")
|
||
|
bundle_dest = os.path.join(self._output_dir(), "ApptentiveResources.bundle")
|
||
|
(status, output) = self._ditto_file(bundle_source, bundle_dest)
|
||
|
# Update the Info.plist in the ApptentiveResources.bundle.
|
||
|
bundle_plist_path = os.path.join(bundle_dest, "Info.plist")
|
||
|
if not os.path.exists(bundle_plist_path):
|
||
|
log("Unable to find bundle Info.plist at %s" % bundle_plist_path)
|
||
|
return False
|
||
|
plist = biplist.readPlist(bundle_plist_path)
|
||
|
plist_key = "ATInfoDistributionKey"
|
||
|
if self.dist_type == self.COCOAPODS_DIST:
|
||
|
plist[plist_key] = "CocoaPods"
|
||
|
elif self.dist_type == self.BINARY_DIST:
|
||
|
plist[plist_key] = "binary"
|
||
|
else:
|
||
|
log("Unknown dist_type")
|
||
|
return False
|
||
|
biplist.writePlist(plist, bundle_plist_path)
|
||
|
|
||
|
# Try to get the version.
|
||
|
version = None
|
||
|
header_contents = open(os.path.join(self._project_dir(), "source", "ATConnect.h")).read()
|
||
|
match = re.search(r"#define kATConnectVersionString @\"(?P<version>.+)\"", header_contents, re.MULTILINE)
|
||
|
if match and match.group('version'):
|
||
|
version = match.group('version')
|
||
|
with chdir(self._output_dir()):
|
||
|
filename = 'apptentive_ios_sdk.tar.gz'
|
||
|
if version:
|
||
|
if self.dist_type == self.BINARY_DIST:
|
||
|
filename = 'apptentive_ios_sdk-%s.tar.gz' % version
|
||
|
elif self.dist_type == self.COCOAPODS_DIST:
|
||
|
filename = 'apptentive_ios_sdk-cocoapods-%s.tar.gz' % version
|
||
|
tar_command = "tar -zcvf ../%s ." % filename
|
||
|
(status, output) = run_command(tar_command, verbose=self.verbose)
|
||
|
if status != 0:
|
||
|
log("Unable to create library archive")
|
||
|
log("output")
|
||
|
return False
|
||
|
run_command("open .")
|
||
|
return True
|
||
|
|
||
|
def _project_dir(self):
|
||
|
return os.path.join("..", "..", "ApptentiveConnect")
|
||
|
|
||
|
def _output_dir(self):
|
||
|
return os.path.join(self.build_root, "library_dir")
|
||
|
|
||
|
def _products_dir(self, is_simulator=False):
|
||
|
products_dir = os.path.join(self.build_root, "device_product")
|
||
|
if is_simulator:
|
||
|
products_dir = os.path.join(self.build_root, "simulator_product")
|
||
|
return products_dir
|
||
|
|
||
|
def _xcode_options(self, is_simulator=False):
|
||
|
products_dir = self._products_dir(is_simulator=is_simulator)
|
||
|
symroot = os.path.join(self.build_root, "symroot")
|
||
|
temp_dir = os.path.join(self.build_root, "target_temp_dir")
|
||
|
return "CONFIGURATION_BUILD_DIR=%s SYMROOT=%s TARGET_TEMP_DIR=%s" % (escape_arg(products_dir), escape_arg(symroot), escape_arg(temp_dir))
|
||
|
|
||
|
def _lipo_command(self):
|
||
|
output_dir = self._output_dir()
|
||
|
lib = "libApptentiveConnect.a"
|
||
|
output_library = os.path.join(output_dir, lib)
|
||
|
input_a = os.path.join(self._products_dir(is_simulator=True), lib)
|
||
|
input_b = os.path.join(self._products_dir(is_simulator=False), lib)
|
||
|
return """xcrun lipo -create -output %s %s %s""" % (escape_arg(output_library), escape_arg(input_a), escape_arg(input_b))
|
||
|
|
||
|
def _build_command(self, is_simulator=False):
|
||
|
sdk = 'iphoneos'
|
||
|
if is_simulator:
|
||
|
sdk = 'iphonesimulator'
|
||
|
return """xcrun xcodebuild -target ApptentiveConnect -configuration Debug -sdk %s %s""" % (sdk, self._xcode_options(is_simulator=is_simulator))
|
||
|
|
||
|
def _project_path(self, filename):
|
||
|
"""Returns the file within the project directory for the given filename."""
|
||
|
return os.path.join(self._project_dir(), filename)
|
||
|
|
||
|
def _ditto_file(self, path_from, path_to):
|
||
|
command = """xcrun ditto %s %s""" % (escape_arg(path_from), escape_arg(path_to))
|
||
|
return run_command(command, verbose=self.verbose)
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
for dist_type in [Builder.BINARY_DIST, Builder.COCOAPODS_DIST]:
|
||
|
builder = Builder(dist_type=dist_type)
|
||
|
result = builder.build()
|
||
|
if result == True:
|
||
|
log("Build suceeded")
|
||
|
else:
|
||
|
log("Build failed!")
|
||
|
break
|