diff --git a/buildscripts/builddrivers.py b/buildscripts/builddrivers.py new file mode 100644 index 00000000..03b0c9f6 --- /dev/null +++ b/buildscripts/builddrivers.py @@ -0,0 +1,268 @@ +#!/usr/bin/python +######################################################################################### +# +# Description: This script helps to build drivers in a Windows environment for PHP 7+ (32-bit/64-bit) +# +# Requirement: +# python 3.4 +# PHP SDK and PHP Source +# Driver source code folder / GitHub repository +# Visual Studio 2015 (PHP 7.0* and 7.1*) and Visual Studio 2017 (PHP 7.2*) +# +# Execution: Run with command line with required options. +# Examples: +# py builddrivers.py (for interactive mode) +# py builddrivers.py -v=7.1.7 -a=x86 -t=ts -d=all -g=no +# py builddrivers.py --PHPVER=7.0.22 --ARCH=x64 --THREAD=nts --DRIVER=all --GITHUB=yes +# +# Output: Build the drivers using PHP SDK. When running locally, if build is unsuccessful, +# the log file will be launched for examination. Otherwise, the drivers will be renamed +# and copied to the designated location(s). +# +############################################################################################# + +import sys +import shutil +import os.path +import argparse +from buildtools import BuildUtil + +class BuildDriver(object): + """Build sqlsrv and/or pdo_sqlsrv drivers with PHP source with the following properties: + + Attributes: + phpver # PHP version, e.g. 7.1.*, 7.2.* etc. + driver # all, sqlsrv, or pdo_sqlsrv + arch # x64 or x86 + thread # nts or ts + debug # whether debug is enabled + repo # GitHub repository + branch # GitHub repository branch + download_source # download source from GitHub or not + package # package name for the binaries + remote_path # remote destination to where the drivers will be placed (None for local builds) + local # whether the build is local + source_path # path to a local source folder + """ + + def __init__(self, phpver, driver, arch, thread, debug, repo, branch, download, package, path): + self.util = BuildUtil(phpver, driver, arch, thread, debug) + self.repo = repo + self.branch = branch + self.download_source = download + self.package = package + self.remote_path = path + self.local = path is None # the default path is None, which means running locally + self.rebuild = False + self.make_clean = False + self.source_path = None # None initially but will be set later if not downloading from GitHub + + def show_config(self): + print('PHP Version: ', self.util.phpver) + print('Arch: ', self.util.arch) + print('Thread: ', self.util.thread) + print('Driver: ', self.util.driver) + print('Debug enabled: ', self.util.debug_enabled) + + def clean_or_remove(self, root_dir, work_dir): + """Check if php source directory already exists. If so, prompt user whether to rebuild, clean, or superclean, meaning to remove the entire php source directory.""" + phpsrc = self.util.phpsrc_root(root_dir) + if os.path.exists( phpsrc ): + print(phpsrc + " exists.") + choice = validate_input("Want to rebuild (r), clean (c) or superclean (s)? ", "r/c/s") + self.make_clean = False + if choice == 'r': + print('Will rebuild the binaries') + self.util.remove_prev_build(root_dir) + elif choice == 'c': + print('Will make clean') + self.make_clean = True + # this step is necessary in case the user has changed the configuration + self.util.remove_old_builds(root_dir) + else: + print('Will remove ' + phpsrc) + os.system('RMDIR /s /q ' + phpsrc) + + os.chdir(work_dir) + + def build_extensions(self, dest, logfile): + """This takes care of getting the drivers' source files, building the drivers. If running locally, *dest* should be the root drive. Otherwise, *dest* should be None. In this case, remote_path must be defined such that the binaries will be copied to the designated destinations.""" + work_dir = os.path.dirname(os.path.realpath(__file__)) + + if self.download_source: + # This will download from the specified branch on GitHub repo and copy the source to the working directory + self.util.download_msphpsql_source(repo, branch) + else: + # This case only happens when building locally (interactive mode) + while True: + if self.source_path is None: + source = input('Enter the full path to the Source folder: ') + else: + source = input("Hit ENTER to reuse '" + self.source_path + "' or provide another path to the Source folder: ") + if len(source) == 0: + source = self.source_path + + if os.path.exists( source ): + self.source_path = source + break + else: + print('The path provided does not exist. Please re-enter.') + + print('Copying source files from', source) + + os.system('ROBOCOPY ' + source + '\shared ' + work_dir + '\Source\shared /xx /xo ') + os.system('ROBOCOPY ' + source + '\sqlsrv ' + work_dir + '\Source\sqlsrv /xx /xo ') + os.system('ROBOCOPY ' + source + '\pdo_sqlsrv ' + work_dir + '\Source\pdo_sqlsrv /xx /xo ') + + print('Start building PHP with the extension...') + + self.util.build_drivers(self.make_clean, dest, logfile) + + if dest is None: + # This indicates the script is NOT running locally, and that + # the drivers should be in the working directory + + # Make sure drivers path is defined + if self.remote_path is None: + print('Errors: Drivers destination should be defined! Do nothing.') + else: + OS_folder = "Windows" # hardcode this since this script is only run in Windows + dest_drivers = os.path.join(self.remote_path, 'PHP', 'Drivers', self.package, OS_folder, self.util.major_version(), self.util.arch) + dest_symbols = os.path.join(dest_drivers, 'Symbols') + + # All intermediate directories will be created in order to create the leaf directory + if os.path.exists(dest_symbols) == False: + os.makedirs(dest_symbols) + + # Now copy all the binaries + if self.util.driver == 'all': + self.util.copy_binary(work_dir, dest_drivers, 'sqlsrv', '.dll') + self.util.copy_binary(work_dir, dest_symbols, 'sqlsrv', '.pdb') + self.util.copy_binary(work_dir, dest_drivers, 'pdo_sqlsrv', '.dll') + self.util.copy_binary(work_dir, dest_symbols, 'pdo_sqlsrv', '.pdb') + else: + self.util.copy_binary(work_dir, dest_drivers, self.util.driver, '.dll') + self.util.copy_binary(work_dir, dest_symbols, self.util.driver, '.pdb') + + + def build(self): + """This is the main entry point of building drivers for PHP.""" + self.show_config() + + work_dir = os.path.dirname(os.path.realpath(__file__)) + root_dir = 'C:' + os.sep + + quit = False + while not quit: + if not self.rebuild and self.local: + self.clean_or_remove(root_dir, work_dir) + + logfile = self.util.get_logfile_name() + + try: + dest = None + if self.local: + dest = root_dir + + self.build_extensions(dest, logfile) + print('Build Completed') + except: + print('Something went wrong. Build incomplete.') + if self.local: # display log file only when building locally + os.startfile(os.path.join(root_dir, 'php-sdk', logfile)) + os.chdir(work_dir) + break + + # Only ask when building locally + if self.local: + choice = input("Rebuild the same configuration(yes) or quit (no) [yes/no]: ") + + if choice.lower() == 'yes' or choice.lower() == 'y' or choice.lower() == '': + print('Rebuilding drivers...') + self.make_clean = False + self.rebuild = True + self.util.remove_prev_build(root_dir) + else: + quit = True + else: + quit = True + + os.chdir(work_dir) + +def validate_input(question, values): + """Return the user selected value, and it must be valid based on *values*.""" + while True: + options = values.split('/') + prompt = '[' + values + ']' + value = input(question + prompt + ': ') + value = value.lower() + if not value in options: + print("An invalid choice is entered. Choose from", prompt) + else: + break + return value + +################################### Main Function ################################### +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('-v', '--PHPVER', help="PHP version, e.g. 7.1.*, 7.2.* etc.") + parser.add_argument('-a', '--ARCH', choices=['x64', 'x86']) + parser.add_argument('-t', '--THREAD', choices=['nts', 'ts']) + parser.add_argument('-d', '--DRIVER', choices=['all', 'sqlsrv', 'pdo_sqlsrv']) + parser.add_argument('-m', '--DEBUG', default='no', choices=['yes', 'no'], help="enable debug mode") + parser.add_argument('-r', '--REPO', default='Microsoft', help="GitHub repository") + parser.add_argument('-b', '--BRANCH', default='dev', help="GitHub repository branch") + parser.add_argument('-g', '--GITHUB', default='yes', help="get source from GitHub or not") + parser.add_argument('-k', '--PACKAGE', default='Latest', help="the package name for the drivers") + parser.add_argument('-p', '--PATH', default=None, help="the remote destination for the drivers") + + args = parser.parse_args() + + phpver = args.PHPVER + arch = args.ARCH + thread = args.THREAD + driver = args.DRIVER + debug = args.DEBUG == 'yes' + repo = args.REPO + branch = args.BRANCH + download = args.GITHUB.lower() == 'yes' + path = args.PATH + package = args.PACKAGE + + if phpver is None: + # assuming it is building drivers locally when required to prompt + # thus will not prompt for drivers' destination path, which is None by default + phpver = input("PHP Version (e.g. 7.1.* or 7.2.*): ") + arch_version = input("Want to build 64-bit [y/n]: ") + thread = validate_input("Thread safe? ", "nts/ts") + driver = validate_input("Driver to build? ", "all/sqlsrv/pdo_sqlsrv") + debug_mode = input("Want to build debug [y/n]? ") + + answer = input("Download source from a GitHub repo [y/n]? ") + download = False + if answer == 'yes' or answer == 'y' or answer == '': + download = True + repo = input("Name of the repo (hit enter for 'Microsoft'): ") + branch = input("Name of the branch (hit enter for 'dev'): ") + if repo == '': + repo = 'Microsoft' + if branch == '': + branch = 'dev' + + arch_version = arch_version.lower() + arch = 'x64' if arch_version == 'yes' or arch_version == 'y' or arch_version == '' else 'x86' + + debug_mode = debug_mode.lower() + debug = debug_mode == 'yes' or debug_mode == 'y' or debug_mode == '' + + builder = BuildDriver(phpver, + driver, + arch, + thread, + debug, + repo, + branch, + download, + package, + path) + builder.build() diff --git a/buildscripts/buildtools.py b/buildscripts/buildtools.py new file mode 100644 index 00000000..17f2d4a9 --- /dev/null +++ b/buildscripts/buildtools.py @@ -0,0 +1,437 @@ +#!/usr/bin/python +######################################################################################### +# +# Description: The class BuildUtil will build Microsoft SQL Server PHP 7+ Drivers +# for 32 bit and 64 bit. +# +# Requirement: +# python 3.4 +# PHP SDK and PHP Source +# Driver source code folder +# Git for Windows +# Visual Studio 2015 (PHP 7.0* and 7.1*) and Visual Studio 2017 (PHP 7.2*) +# +# Output: The drivers will be renamed and copied to the specified location. +# +############################################################################################# + +import shutil +import os.path +import stat +import datetime +import urllib.request +import zipfile + +class BuildUtil(object): + """Build sqlsrv and/or pdo_sqlsrv drivers with PHP source with the following properties: + + Attributes: + phpver # PHP version, e.g. 7.1.*, 7.2.* etc. + driver # all, sqlsrv, or pdo_sqlsrv + arch # x64 or x86 + thread # nts or ts + debug_enabled # whether debug is enabled + """ + + def __init__(self, phpver, driver, arch, thread, debug_enabled = False): + self.phpver = phpver + self.driver = driver.lower() + self.arch = arch.lower() + self.thread = thread.lower() + self.debug_enabled = debug_enabled + + def major_version(self): + """Return the major version number based on the PHP version.""" + return self.phpver[0:3] + + def version_label(self): + """Return the version label based on the PHP version.""" + major_ver = self.major_version() + + if major_ver == '7.0': + version = major_ver[0] + else: + version = major_ver[0] + major_ver[2] + return version + + def driver_name(self, driver, suffix): + """Return the *driver* name with *suffix* after PHP is successfully compiled.""" + return 'php_' + driver + suffix + + def driver_new_name(self, driver, suffix): + """Return the *driver* name with *suffix* based on PHP version and thread.""" + version = self.version_label() + return 'php_' + driver + '_' + version + '_' + self.thread + suffix + + def compiler_version(self): + """Return the appropriate compiler version based on PHP version.""" + VC = 'vc14' + version = self.version_label() + if version >= '72': # Compiler version for PHP 7.2 or above + VC = 'vc15' + return VC + + def phpsrc_root(self, sdk_dir): + """Return the path to the PHP source folder based on *sdk_dir*.""" + vc = self.compiler_version() + return os.path.join(sdk_dir, 'php-sdk', 'phpdev', vc, self.arch, 'php-'+self.phpver+'-src') + + def build_abs_path(self, sdk_dir): + """Return the absolute path to the PHP build folder based on *sdk_dir*.""" + phpsrc = self.phpsrc_root(sdk_dir) + + build_dir = 'Release' + if self.debug_enabled: + build_dir = 'Debug' + + if self.thread == 'ts': + build_dir = build_dir + '_TS' + + if self.arch == 'x64': + build_dir = self.arch + os.sep + build_dir + + return os.path.join(phpsrc, build_dir) + + def remove_old_builds(self, sdk_dir): + """Remove the extensions, e.g. the driver subfolders in php-7.*-src\ext.""" + print('Removing old builds...') + + phpsrc = self.phpsrc_root(sdk_dir) + ext_path = os.path.join(phpsrc, 'ext') + if os.path.exists( ext_path ): + shutil.rmtree(os.path.join(ext_path, 'sqlsrv'), ignore_errors=True) + shutil.rmtree(os.path.join(ext_path, 'pdo_sqlsrv'), ignore_errors=True) + + if self.arch == 'x64': + shutil.rmtree(os.path.join(phpsrc, self.arch), ignore_errors=True) + else: + shutil.rmtree(os.path.join(phpsrc, 'Debug'), ignore_errors=True) + shutil.rmtree(os.path.join(phpsrc, 'Debug_TS'), ignore_errors=True) + shutil.rmtree(os.path.join(phpsrc, 'Release'), ignore_errors=True) + shutil.rmtree(os.path.join(phpsrc, 'Release_TS'), ignore_errors=True) + + def remove_prev_build(self, sdk_dir): + """Remove all binaries and source code in the Release* or Debug* folders""" + print('Removing previous build...') + build_dir = self.build_abs_path(sdk_dir) + os.chdir(build_dir) + os.system('DEL *sqlsrv*') + + # remove the extensions in the phpsrc's release* or debug* folder's ext subfolder + release_ext_path = os.path.join(build_dir, 'ext') + if os.path.exists( release_ext_path ): + shutil.rmtree(os.path.join(release_ext_path, 'sqlsrv'), ignore_errors=True) + shutil.rmtree(os.path.join(release_ext_path, 'pdo_sqlsrv'), ignore_errors=True) + + # next remove the binaries too + os.chdir(release_ext_path) + os.system('DEL *sqlsrv*') + + @staticmethod + def get_logfile_name(): + """Return the filename for the log file based on timestamp.""" + return 'Build_' + datetime.datetime.now().strftime("%Y%m%d_%H%M") + '.log' + + @staticmethod + def update_file_content(file, search_str, new_str): + """Find *search_str* and replace it by *new_str* in a *file*""" + os.chmod(file, stat.S_IWRITE) + f = open(file, 'r') + filedata = f.read() + updatedata = filedata.replace(search_str, new_str) + f = open(file, 'w') + f.write(updatedata) + f.close() + + @staticmethod + def generateMMDD(): + """Return the generated Microsoft PHP Build Version Number""" + d = datetime.date.today() + + startYear = 2009 + startMonth = 4 + passYear = int( '%02d' % d.year ) - startYear + passMonth = int( '%02d' % d.month ) - startMonth + MM = passYear * 12 + passMonth + dd = d.day + + MMDD = "" + str( MM ) + if( dd < 10 ): + return MMDD + "0" + str( dd ) + else: + return MMDD + str( dd ) + + @staticmethod + def get_driver_version(version_file): + """Read the *version_file* and return the driver version.""" + with open(version_file) as f: + for line in f: + if 'SQLVERSION_MAJOR' in line: # major version + major = line.split()[2] + elif 'SQLVERSION_MINOR' in line: # minor version + minor = line.split()[2] + elif 'SQLVERSION_PATCH' in line: # patch + patch = line.split()[2] + break + + return major + '.' + minor + '.' + patch + + @staticmethod + def write_lines_to_copy_source(driver, file): + """Write to file the commands to copy *driver* source.""" + source = '%currDir%' + os.sep + 'Source' + os.sep + driver + dest = '%phpSrc%' + os.sep + 'ext' + os.sep + driver + file.write('@CALL ROBOCOPY ' + source + ' ' + dest + ' /s /xx /xo' + os.linesep) + + source = '%currDir%' + os.sep + 'Source' + os.sep + 'shared' + dest = '%phpSrc%' + os.sep + 'ext' + os.sep + driver + os.sep + 'shared' + file.write('@CALL ROBOCOPY ' + source + ' ' + dest + ' /s /xx /xo' + os.linesep) + + @staticmethod + def download_msphpsql_source(repo, branch, dest_folder = 'Source', clean_up = True): + """Download to *dest_folder* the msphpsql archive of the specified GitHub *repo* and *branch*.""" + try: + work_dir = os.path.dirname(os.path.realpath(__file__)) + + temppath = os.path.join(work_dir, 'temp') + if os.path.exists(temppath): + shutil.rmtree(temppath) + os.makedirs(temppath) + os.chdir(temppath) + + file = branch + '.zip' + url = 'https://github.com/' + repo + '/msphpsql/archive/' + branch + '.zip' + + print('Downloading ' + url + ' ...') + try: + with urllib.request.urlopen(url) as response, open(file, 'wb') as out_file: + shutil.copyfileobj(response, out_file) + except: + print ("Resort to skip ssl verifcation...") + # need to skip ssl verifcation on some agents + # see https://www.python.org/dev/peps/pep-0476/ + with urllib.request.urlopen(url, context=ssl._create_unverified_context()) as response, open(file, 'wb') as out_file: + shutil.copyfileobj(response, out_file) + + print('Extracting ' + file + ' ...') + zip = zipfile.ZipFile(file) + zip.extractall() + zip.close() + + msphpsqlFolder = os.path.join(temppath, 'msphpsql-' + branch) + source = os.path.join(msphpsqlFolder, 'source') + os.chdir(work_dir) + + os.system('ROBOCOPY ' + source + '\shared ' + dest_folder + '\shared /xx /xo') + os.system('ROBOCOPY ' + source + '\pdo_sqlsrv ' + dest_folder + '\pdo_sqlsrv /xx /xo') + os.system('ROBOCOPY ' + source + '\sqlsrv ' + dest_folder + '\sqlsrv /xx /xo') + + if clean_up: + shutil.rmtree(temppath) + + except: + print('Error occurred when downloading source') + raise + + def update_driver_source(self, source_dir, driver): + """Update the *driver* source in *source_path* with the latest version, file descriptions, etc.""" + driver_dir = os.path.join(source_dir, driver) + + # Update Template.rc + template_file = os.path.join(driver_dir, 'template.rc') + if driver == 'sqlsrv': + drivername = self.driver_new_name(driver, '.dll') + self.update_file_content(template_file, 'FILE_NAME \"\\0\"', '"' + drivername + '\\0"') + self.update_file_content(template_file, '\"Microsoft Drivers for PHP for SQL Server\\0\"', '"Microsoft Drivers for PHP for SQL Server (SQLSRV Driver)\\0"') + elif driver == 'pdo_sqlsrv': + drivername = self.driver_new_name(driver, '.dll') + self.update_file_content(template_file, 'FILE_NAME \"\\0\"', '"' + drivername + '\\0"') + self.update_file_content(template_file, '\"Microsoft Drivers for PHP for SQL Server\\0\"', '"Microsoft Drivers for PHP for SQL Server (PDO Driver)\\0"') + + # Update Version.h + version_file = os.path.join(source_dir, 'shared', 'version.h') + build_number = self.generateMMDD() + self.update_file_content(version_file, 'SQLVERSION_BUILD 0', 'SQLVERSION_BUILD ' + build_number) + + # get the latest version + version = self.get_driver_version(version_file) + '.' + build_number + print('Driver version is: ', version) + + # Update CREDIT file + credits_file = os.path.join(driver_dir, 'CREDITS') + if driver == 'sqlsrv': + self.update_file_content(credits_file, 'Microsoft Drivers for PHP for SQL Server', 'Microsoft Drivers ' + version + ' for PHP for SQL Server (' + self.driver.upper() + ' driver)') + elif driver == 'pdo_sqlsrv': + self.update_file_content(credits_file, 'Microsoft Drivers for PHP for SQL Server (PDO driver)', 'Microsoft Drivers ' + version + ' for PHP for SQL Server (' + self.driver.upper() + ' driver)') + + def generate_build_options(self): + """Return the generated build configuration and arguments""" + cmd_line = '' + if self.debug_enabled: + cmd_line = ' --enable-debug ' + + if self.driver == 'all': + cmd_line = ' --enable-sqlsrv=shared --enable-pdo --with-pdo-sqlsrv=shared ' + cmd_line + else: + if self.driver == 'sqlsrv': + cmd_line = ' --enable-sqlsrv=shared ' + cmd_line + else: # pdo_sqlsrv + cmd_line = ' --enable-pdo --with-pdo-sqlsrv=shared ' + cmd_line + + cmd_line = 'cscript configure.js --disable-all --enable-cli --enable-cgi' + cmd_line + ' --disable-ipv6 --with-odbcver=0x0380 --enable-embed' + if self.thread == 'nts': + cmd_line = cmd_line + ' --disable-zts' + return cmd_line + + def create_local_batch_file(self, make_clean, cmd_line, log_file): + """Generate the batch file to be picked up by the PHP starter script.""" + filename = 'phpsdk-build-task.bat' + print('Generating ', filename) + try: + file = open(filename, 'w') + file.write('@ECHO OFF' + os.linesep) + file.write('SET currDir=%CD%' + os.linesep) + file.write('SET LOG_NAME=%currDir%\\' + log_file + os.linesep) + file.write('@CALL phpsdk_buildtree phpdev > %LOG_NAME% 2>&1' + os.linesep) + + # for PHP version with release tags, such as 'RC', 'beta', etc. + # we need to remove the hyphen '-' between the version number and tag + # because in https://github.com/php/php-src the released tags have no hyphens + + php_tag = 'php-' + self.phpver.replace('-', '') + php_src = 'php-' + self.phpver +'-src' + + # if not exists, check out the specified tag + file.write('IF NOT EXIST ' + php_src + ' @CALL git clone -b ' + php_tag + ' --depth 1 --single-branch https://github.com/php/php-src.git ' + php_src + os.linesep) + file.write('CD ' + php_src + os.linesep) + file.write('SET phpSrc=%CD%' + os.linesep) + file.write('@CALL phpsdk_deps -u >> %LOG_NAME% 2>&1' + os.linesep) + + # copy source files to extension + if self.driver == 'all': + self.write_lines_to_copy_source('sqlsrv', file) + self.write_lines_to_copy_source('pdo_sqlsrv', file) + else: + self.write_lines_to_copy_source(self.driver, file) + + # configure and build + file.write('@CALL buildconf --force >> %LOG_NAME% 2>&1' + os.linesep) + file.write('@CALL ' + cmd_line + ' >> %LOG_NAME% 2>&1' + os.linesep) + if make_clean: + file.write('nmake clean >> %LOG_NAME% 2>&1' + os.linesep) + file.write('nmake >> %LOG_NAME% 2>&1' + os.linesep) + file.write('exit' + os.linesep) + file.close() + return filename + except: + print('Cannot create ', filename) + + def build_drivers(self, make_clean = False, dest = None, log_file = None): + """Build sqlsrv/pdo_sqlsrv extensions for PHP, assuming the Source folder exists in the working directory, and this folder will be removed when the build is complete.""" + work_dir = os.path.dirname(os.path.realpath(__file__)) + + # First, update the driver source file contents + source_dir = os.path.join(work_dir, 'Source') + if self.driver == 'all': + self.update_driver_source(source_dir, 'sqlsrv') + self.update_driver_source(source_dir, 'pdo_sqlsrv') + else: + self.update_driver_source(source_dir, self.driver) + + # Next, generate the build configuration and arguments + cmd_line = self.generate_build_options() + print('cmd_line: ' + cmd_line) + + # Generate a batch file based on the inputs + if log_file is None: + log_file = self.get_logfile_name() + + batch_file = self.create_local_batch_file(make_clean, cmd_line, log_file) + + # Reference: https://github.com/OSTC/php-sdk-binary-tools + # Clone the master branch of PHP sdk if the directory does not exist + print('Downloading the latest php SDK...') + + # if *dest* is None, simply use the current working directory + sdk_dir = dest + copy_to_ext = True # this determines where to copy the binaries to + if dest is None: + sdk_dir = work_dir + copy_to_ext = False + + phpSDK = os.path.join(sdk_dir, 'php-sdk') + if not os.path.exists( phpSDK ): + os.system('git clone https://github.com/OSTC/php-sdk-binary-tools.git --branch master --single-branch --depth 1 ' + phpSDK) + os.chdir(phpSDK) + os.system('git pull ') + + # Copy the generated batch file to phpSDK for the php starter script + shutil.copy(os.path.join(work_dir, batch_file), phpSDK) + shutil.move(source_dir, phpSDK) + + # Invoke phpsdk--.bat + vc = self.compiler_version() + starter_script = 'phpsdk-' + vc + '-' + self.arch + '.bat' + print('Running starter script: ', starter_script) + os.system(starter_script + ' -t ' + batch_file) + + # Now we can safely remove the Source folder, because its contents have + # already been modified when building the extensions + shutil.rmtree(os.path.join(phpSDK, 'Source'), ignore_errors=True) + + # Next, rename the newly compiled PHP extensions + self.rename_binaries(sdk_dir) + + # Final step, copy the binaries to the right place + self.copy_binaries(sdk_dir, copy_to_ext) + + def rename_binary(self, path, driver, suffix): + """Rename sqlsrv or pdo_sqlsrv binary.""" + driver_old_name = self.driver_name(driver, suffix) + driver_new_name = self.driver_new_name(driver, suffix) + + os.rename(os.path.join(path, driver_old_name), os.path.join(path, driver_new_name)) + + def rename_binaries(self, sdk_dir): + """Rename the sqlsrv and/or pdo_sqlsrv binaries based on PHP version and thread, including pdb files.""" + + # Derive the path to where the extensions are located + ext_dir = self.build_abs_path(sdk_dir) + print("Renaming binaries in ", ext_dir) + + if self.driver == 'all': + self.rename_binary(ext_dir, 'sqlsrv', '.dll') + self.rename_binary(ext_dir, 'sqlsrv', '.pdb') + self.rename_binary(ext_dir, 'pdo_sqlsrv', '.dll') + self.rename_binary(ext_dir, 'pdo_sqlsrv', '.pdb') + else: + self.rename_binary(ext_dir, self.driver, '.dll') + self.rename_binary(ext_dir, self.driver, '.pdb') + + def copy_binary(self, from_dir, dest_dir, driver, suffix): + """Copy sqlsrv or pdo_sqlsrv binary to *dest_dir*.""" + binary = self.driver_new_name(driver, suffix) + shutil.copy2(os.path.join(from_dir, binary), dest_dir) + + def copy_binaries(self, sdk_dir, copy_to_ext): + """Copy the sqlsrv and/or pdo_sqlsrv binaries, including pdb files, to the right place.""" + build_dir = self.build_abs_path(sdk_dir) + print('Copying the binaries from', build_dir) + if copy_to_ext: + dest_dir = os.path.join(build_dir, 'ext') + else: + # Simply make a copy of the binaries in sdk_dir + dest_dir = sdk_dir + + print('Destination:', dest_dir) + + # Now copy the binaries + if self.driver == 'all': + self.copy_binary(build_dir, dest_dir, 'sqlsrv', '.dll') + self.copy_binary(build_dir, dest_dir, 'sqlsrv', '.pdb') + self.copy_binary(build_dir, dest_dir, 'pdo_sqlsrv', '.dll') + self.copy_binary(build_dir, dest_dir, 'pdo_sqlsrv', '.pdb') + else: + self.copy_binary(build_dir, dest_dir, self.driver, '.dll') + self.copy_binary(build_dir, dest_dir, self.driver, '.pdb') +