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.
193 lines
6.9 KiB
193 lines
6.9 KiB
#!/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
|
|
|