From 282b88d9268a5e8d859abd098fec52002b6d503d Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Fri, 11 Aug 2017 12:49:54 -0700 Subject: [PATCH 01/26] Python scripts for building extensions for PHP 7+ in Windows --- buildscripts/builddrivers.py | 268 +++++++++++++++++++++ buildscripts/buildtools.py | 437 +++++++++++++++++++++++++++++++++++ 2 files changed, 705 insertions(+) create mode 100644 buildscripts/builddrivers.py create mode 100644 buildscripts/buildtools.py 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') + From bdad16e904dc7c0481f29c7e9b49b4d83cefdd6c Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Mon, 14 Aug 2017 08:54:21 -0700 Subject: [PATCH 02/26] modified comments and corrected typo --- buildscripts/builddrivers.py | 20 ++++++++++++-------- buildscripts/buildtools.py | 27 +++++++++++++++++++-------- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/buildscripts/builddrivers.py b/buildscripts/builddrivers.py index 03b0c9f6..0bb771b0 100644 --- a/buildscripts/builddrivers.py +++ b/buildscripts/builddrivers.py @@ -31,11 +31,7 @@ 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 + util # BuildUtil object whose constructor takes phpver, driver, arch, thread, debug repo # GitHub repository branch # GitHub repository branch download_source # download source from GitHub or not @@ -65,10 +61,14 @@ class BuildDriver(object): 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.""" + """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.") + print(phpsrc + " exists.") + print("Choose rebuild(r) if using the same configuration. Choose clean(c) otherwise. If unsure, choose superclean(s).") choice = validate_input("Want to rebuild (r), clean (c) or superclean (s)? ", "r/c/s") self.make_clean = False if choice == 'r': @@ -86,7 +86,11 @@ class BuildDriver(object): 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.""" + """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: diff --git a/buildscripts/buildtools.py b/buildscripts/buildtools.py index 17f2d4a9..6633371a 100644 --- a/buildscripts/buildtools.py +++ b/buildscripts/buildtools.py @@ -189,7 +189,9 @@ class BuildUtil(object): @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*.""" + """Download to *dest_folder* the msphpsql archive of the specified + GitHub *repo* and *branch*. The downloaded files will be removed by default. + """ try: work_dir = os.path.dirname(os.path.realpath(__file__)) @@ -207,8 +209,8 @@ class BuildUtil(object): 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 + print ("Resort to skip ssl verification...") + # need to skip ssl verification 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) @@ -234,7 +236,9 @@ class BuildUtil(object): raise def update_driver_source(self, source_dir, driver): - """Update the *driver* source in *source_path* with the latest version, file descriptions, etc.""" + """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 @@ -327,7 +331,10 @@ class BuildUtil(object): 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.""" + """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 @@ -376,7 +383,7 @@ class BuildUtil(object): 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 + # already been modified prior to building the extensions shutil.rmtree(os.path.join(phpSDK, 'Source'), ignore_errors=True) # Next, rename the newly compiled PHP extensions @@ -393,7 +400,9 @@ class BuildUtil(object): 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.""" + """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) @@ -414,7 +423,9 @@ class BuildUtil(object): 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.""" + """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: From 2b2de0c2720839ae50ff45404297ec0db18fe27a Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Mon, 14 Aug 2017 09:21:36 -0700 Subject: [PATCH 03/26] added more checks for path existence --- buildscripts/builddrivers.py | 13 ++++++++----- buildscripts/buildtools.py | 8 +++++++- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/buildscripts/builddrivers.py b/buildscripts/builddrivers.py index 0bb771b0..b429e14a 100644 --- a/buildscripts/builddrivers.py +++ b/buildscripts/builddrivers.py @@ -105,13 +105,16 @@ class BuildDriver(object): 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 ): + + valid = True + if os.path.exists(source) and os.path.exists(os.path.join(source, 'shared')): + # Checking the existence of 'shared' folder only, assuming + # sqlsrv and/or pdo_sqlsrv are also present if it exists self.source_path = source break - else: - print('The path provided does not exist. Please re-enter.') - + + print("The path provided is invalid. Please re-enter.") + print('Copying source files from', source) os.system('ROBOCOPY ' + source + '\shared ' + work_dir + '\Source\shared /xx /xo ') diff --git a/buildscripts/buildtools.py b/buildscripts/buildtools.py index 6633371a..1436f981 100644 --- a/buildscripts/buildtools.py +++ b/buildscripts/buildtools.py @@ -111,9 +111,15 @@ class BuildUtil(object): 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""" + """Remove all binaries and source code in the + Release* or Debug* folders according to the current + configuration + """ print('Removing previous build...') build_dir = self.build_abs_path(sdk_dir) + if not os.path.exists(build_dir): + return + os.chdir(build_dir) os.system('DEL *sqlsrv*') From 99ccc281462af7c9886714b9f9dcd5a519edd515 Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Mon, 14 Aug 2017 14:58:37 -0700 Subject: [PATCH 04/26] Added code to remove optimization flags for debug build --- buildscripts/builddrivers.py | 38 +++++++++++++++++++++++++----------- buildscripts/buildtools.py | 29 ++++++++++++++++++--------- 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/buildscripts/builddrivers.py b/buildscripts/builddrivers.py index b429e14a..a27cf6dc 100644 --- a/buildscripts/builddrivers.py +++ b/buildscripts/builddrivers.py @@ -35,7 +35,7 @@ class BuildDriver(object): repo # GitHub repository branch # GitHub repository branch download_source # download source from GitHub or not - package # package name for the binaries + package # package name for the binaries (will be ignored for local builds) 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 @@ -54,42 +54,55 @@ class BuildDriver(object): self.source_path = None # None initially but will be set later if not downloading from GitHub def show_config(self): + print() 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) + print() 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. + """Only check this when building locally and not rebuilding. If the php source directory + already exists, this will prompt user whether to rebuild, clean, or superclean, the last option + will remove the entire php source directory. + + :param root_dir: the C:\ drive + :param work_dir: the directory of this script + :outcome: the old binaries, if exist, will be removed """ phpsrc = self.util.phpsrc_root(root_dir) if os.path.exists( phpsrc ): print(phpsrc + " exists.") print("Choose rebuild(r) if using the same configuration. Choose clean(c) otherwise. If unsure, choose superclean(s).") - choice = validate_input("Want to rebuild (r), clean (c) or superclean (s)? ", "r/c/s") + build_choice = validate_input("Want to rebuild (r), clean (c) or superclean (s)? ", "r/c/s") self.make_clean = False - if choice == 'r': + if build_choice == 'r': print('Will rebuild the binaries') + # only the old binaries based on the current configuration will be removed self.util.remove_prev_build(root_dir) - elif choice == 'c': + elif build_choice == 'c': print('Will make clean') self.make_clean = True - # this step is necessary in case the user has changed the configuration + # all old builds are removed, and this step is necessary because + # the user might have 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) + os.chdir(work_dir) # change back to the working directory 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. + + :param dest: either None (for remote builds) or the C:\ drive (for local builds) + :param logfile: the name of the logfile + :outcome: the drivers and symbols will renamed and placed in the appropriate location(s) + """ work_dir = os.path.dirname(os.path.realpath(__file__)) @@ -98,6 +111,7 @@ class BuildDriver(object): self.util.download_msphpsql_source(repo, branch) else: # This case only happens when building locally (interactive mode) + # because download_source must be True for remote builds while True: if self.source_path is None: source = input('Enter the full path to the Source folder: ') @@ -153,7 +167,9 @@ class BuildDriver(object): def build(self): - """This is the main entry point of building drivers for PHP.""" + """This is the main entry point of building drivers for PHP. + For local builds, this will loop till the user decides to quit. + """ self.show_config() work_dir = os.path.dirname(os.path.realpath(__file__)) @@ -221,7 +237,7 @@ if __name__ == '__main__': 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") + parser.add_argument('-p', '--PATH', default=None, help="the remote destination for the drivers (do not use this when building locally)") args = parser.parse_args() diff --git a/buildscripts/buildtools.py b/buildscripts/buildtools.py index 1436f981..9b749ac0 100644 --- a/buildscripts/buildtools.py +++ b/buildscripts/buildtools.py @@ -111,9 +111,8 @@ class BuildUtil(object): 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 according to the current - configuration + """Remove all binaries and source code in the Release* or Debug* + folders according to the current configuration """ print('Removing previous build...') build_dir = self.build_abs_path(sdk_dir) @@ -244,9 +243,20 @@ class BuildUtil(object): def update_driver_source(self, source_dir, driver): """Update the *driver* source in *source_path* with the latest version, file descriptions, etc. + If debug is enabled, will remove the optimization flag """ driver_dir = os.path.join(source_dir, driver) + if self.debug_enabled: + # Remove the optimization flag in the config file for this driver + # because '/O2' option is incompatible with Debug mode + print('Removing optimization flag for', driver) + config_file = os.path.join(driver_dir, 'config.w32') + if driver == 'sqlsrv': + self.update_file_content(config_file, 'ADD_FLAG( "CFLAGS_SQLSRV", "/O2" );', '') + elif driver == 'pdo_sqlsrv': + self.update_file_content(config_file, 'ADD_FLAG( "CFLAGS_PDO_SQLSRV", "/O2" );', '') + # Update Template.rc template_file = os.path.join(driver_dir, 'template.rc') if driver == 'sqlsrv': @@ -399,15 +409,15 @@ class BuildUtil(object): self.copy_binaries(sdk_dir, copy_to_ext) def rename_binary(self, path, driver, suffix): - """Rename sqlsrv or pdo_sqlsrv binary.""" + """Rename the *driver* binary (sqlsrv or pdo_sqlsrv) based on the *suffix*.""" 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. + """Rename the sqlsrv and/or pdo_sqlsrv binaries according to the + PHP version and thread, including pdb files (the symbols). """ # Derive the path to where the extensions are located @@ -424,13 +434,14 @@ class BuildUtil(object): 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*.""" + """Copy sqlsrv or pdo_sqlsrv binary (based on *suffix*) 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. + """Copy the sqlsrv and/or pdo_sqlsrv binaries, including pdb files, + to the right place, depending on *copy_to_ext*. The default is to + copy them to the 'ext' folder. """ build_dir = self.build_abs_path(sdk_dir) print('Copying the binaries from', build_dir) From 1b3e0279683596d123243d419ee0e537055939d4 Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Tue, 15 Aug 2017 09:50:34 -0700 Subject: [PATCH 05/26] Modified scripts based on review comments --- buildscripts/builddrivers.py | 28 ++++++++++++---------------- buildscripts/buildtools.py | 6 +++--- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/buildscripts/builddrivers.py b/buildscripts/builddrivers.py index a27cf6dc..fbd663a3 100644 --- a/buildscripts/builddrivers.py +++ b/buildscripts/builddrivers.py @@ -1,10 +1,10 @@ -#!/usr/bin/python +#!/usr/bin/python3 ######################################################################################### # # Description: This script helps to build drivers in a Windows environment for PHP 7+ (32-bit/64-bit) # # Requirement: -# python 3.4 +# python 3.x # 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*) @@ -35,18 +35,18 @@ class BuildDriver(object): repo # GitHub repository branch # GitHub repository branch download_source # download source from GitHub or not - package # package name for the binaries (will be ignored for local builds) remote_path # remote destination to where the drivers will be placed (None for local builds) - local # whether the build is local + local # a boolean flag - whether the build is local + rebuild # a boolean flag - whether the user is rebuilding + make_clean # a boolean flag - whether make clean is necessary source_path # path to a local source folder """ - def __init__(self, phpver, driver, arch, thread, debug, repo, branch, download, package, path): + def __init__(self, phpver, driver, arch, thread, debug, repo, branch, download, 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 @@ -147,8 +147,7 @@ class BuildDriver(object): 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_drivers = os.path.join(self.remote_path, 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 @@ -236,8 +235,7 @@ if __name__ == '__main__': 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 (do not use this when building locally)") + parser.add_argument('-p', '--DESTPATH', default=None, help="the remote destination for the drivers (do not use this for local builds)") args = parser.parse_args() @@ -249,19 +247,18 @@ if __name__ == '__main__': repo = args.REPO branch = args.BRANCH download = args.GITHUB.lower() == 'yes' - path = args.PATH - package = args.PACKAGE + path = args.DESTPATH 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]: ") + arch_version = input("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]? ") + debug_mode = input("Debug enabled? [y/n]: ") - answer = input("Download source from a GitHub repo [y/n]? ") + answer = input("Download source from a GitHub repo? [y/n]: ") download = False if answer == 'yes' or answer == 'y' or answer == '': download = True @@ -286,6 +283,5 @@ if __name__ == '__main__': repo, branch, download, - package, path) builder.build() diff --git a/buildscripts/buildtools.py b/buildscripts/buildtools.py index 9b749ac0..61c0496a 100644 --- a/buildscripts/buildtools.py +++ b/buildscripts/buildtools.py @@ -1,11 +1,11 @@ -#!/usr/bin/python +#!/usr/bin/python3 ######################################################################################### # # Description: The class BuildUtil will build Microsoft SQL Server PHP 7+ Drivers # for 32 bit and 64 bit. # # Requirement: -# python 3.4 +# python 3.x # PHP SDK and PHP Source # Driver source code folder # Git for Windows @@ -48,7 +48,7 @@ class BuildUtil(object): """Return the version label based on the PHP version.""" major_ver = self.major_version() - if major_ver == '7.0': + if major_ver[2] == '0': version = major_ver[0] else: version = major_ver[0] + major_ver[2] From 73480a7cb3203b6e8b2aa40758dfa0a145e2109c Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Tue, 15 Aug 2017 11:54:51 -0700 Subject: [PATCH 06/26] added a new README plus modifying the project README --- README.md | 26 ++++++++++++++++---------- buildscripts/builddrivers.py | 2 +- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 0756cdc7..dff3fda4 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Thank you for taking time to take our February survey. Let us know how we are do ## Announcements - Please visit the [blog][blog] for more announcements. +Please visit the [blog][blog] for more announcements. ## Build (Windows) @@ -46,23 +46,29 @@ Note: if you prefer, you can use the pre-compiled binary found [HERE](https://gi #### Prerequisites -You must first be able to build PHP 7 without including these extensions. For help with doing this, see the [official PHP website][phpbuild] for building your own PHP on Windows. +You must first be able to build PHP 7 without including these extensions. For help with doing this, see the [official PHP website][phpbuild] for building your own PHP on Windows. #### Compile the drivers -1. Copy the sqlsrv and/or pdo_sqlsrv source code directory from this repository into the ext subdirectory. +1. Download the source code directory from this repository -2. Run `buildconf.bat` to rebuild the configure.js script to include the driver. +2. Make a copy of *shared* folder as a subfolder in *sqlsrv* and/or *pdo_sqlsrv* folder -3. Run `configure.bat --with-odbcver=0x0380 and the desired driver options (as below) [plus other options such as --disable-zts for the Non Thread Safe build]` to generate the makefile. You can run `configure.bat --help` to see what other options are available. - * For SQLSRV use: `--enable-sqlsrv=shared` - * For PDO_SQLSRV use: `--enable-pdo=shared --with-pdo-sqlsrv=shared` +3. Copy the *sqlsrv* and/or *pdo_sqlsrv* folder into the PHP source ext subdirectory. -4. Run `nmake`. It is suggested that you run the entire build. If you wish to do so, run `nmake clean` first. +4. Run `buildconf --force` to rebuild the configure.js script to include the driver. -5. To install the resulting build, run `nmake install` or just copy php_sqlsrv.dll and/or php_pdo_sqlsrv.dll to your PHP extension directory. +5. Run `configure.bat --with-odbcver=0x0380 and the desired driver options (as below) [plus other options such as --disable-zts for the Non Thread Safe build]` to generate the makefile. You can run `configure.bat --help` to see what other options are available. + * For SQLSRV use: `--enable-sqlsrv=shared` + * For PDO_SQLSRV use: `--enable-pdo --with-pdo-sqlsrv=shared` -This software has been compiled and tested under PHP 7.0.20 and 7.1.6 using the Visual C++ 2015 compiler. +6. Run `nmake`. Optionally, you can run `nmake clean` first. + +7. To install the resulting build, run `nmake install` or just copy php_sqlsrv.dll and/or php_pdo_sqlsrv.dll to your PHP extension directory. + +The extensions have been compiled and tested with the PHP 7.0.* and 7.1.* using the Visual C++ 2015 compiler as well as PHP 7.2.0 beta using the Visual C++ 2017 V15.0. + +For an example of how to automate building the Microsoft Drivers for PHP for SQL Server, please browse the *buildscripts* directory. ## Install (Windows) diff --git a/buildscripts/builddrivers.py b/buildscripts/builddrivers.py index fbd663a3..a67af601 100644 --- a/buildscripts/builddrivers.py +++ b/buildscripts/builddrivers.py @@ -145,7 +145,7 @@ class BuildDriver(object): # Make sure drivers path is defined if self.remote_path is None: - print('Errors: Drivers destination should be defined! Do nothing.') + print('Errors: Drivers destination should be defined! Doing nothing.') else: dest_drivers = os.path.join(self.remote_path, self.util.major_version(), self.util.arch) dest_symbols = os.path.join(dest_drivers, 'Symbols') From d07c454dc9ba418731abd7ea115da1b976b1b7d9 Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Tue, 15 Aug 2017 13:20:20 -0700 Subject: [PATCH 07/26] README for build scripts --- buildscripts/README.md | 56 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 buildscripts/README.md diff --git a/buildscripts/README.md b/buildscripts/README.md new file mode 100644 index 00000000..ffe79f26 --- /dev/null +++ b/buildscripts/README.md @@ -0,0 +1,56 @@ +## Windows + +### Prerequisites +To use the Python build scripts `builddrivers.py` and `buildtools.py`, install Python 3.x. + +To build extensions for PHP 7.0* or 7.1*, install Visual Studio 2015 and make sure C++ tools are enabled. + +For PHP 7.2*, install Visual Studio 2017, and make sure Visual C++ toolset, the Windows SDK components, and Git for Windows are installed. + +See [PHP SDK page](https://github.com/OSTC/php-sdk-binary-tools) for more details. + +### Build PHP extensions + +Launch a regular `cmd` prompt and change to the directory where these Python scripts are. + +You'll be asked to input the PHP version. Simply type the version number like `7.1.7`. + +If it's alpha, beta, or RC, make sure the name you provide matches the PHP tag name without the prefix `php-`. For example, for PHP 7.2 beta 2, the tag name is `php-7.2.0beta2`, so you should enter `7.2.0beta2`. Visit https://github.com/php/php-src to find the appropriate tag names. + +**Use Interactive mode** + +Run `py builddrivers.py` + +Most questions are self-explanatory. Use lower cases for your inputs, and you can simply hit `ENTER` key for `yes/no` questions. + +When given a choice whether to download from a GitHub repository, you can choose to provide the full path to your local Source folder instead. + +**Use Command-line arguments** + +Run `py builddrivers.py` with command line arguments. For example: + +`py builddrivers.py -v=7.1.7 -a=x86 -t=ts -d=all -g=no` + +or + +`py builddrivers.py --PHPVER=7.0.22 --ARCH=x64 --THREAD=nts --DRIVER=sqlsrv --GITHUB=yes` + +**Notes** + +It's recommended that the PHP SDK is unzipped into the shortest possible path, preferrably somewhere near the root drive. Therefore, this script will create a `php-sdk` folder in C:\ drive. This `php-sdk` folder will remain unless you remove it yourself. For ongoing development, it's suggested you keep it around. The build scripts will handle updating the PHP SDK if new version is available. + +If this is not the first time you build the drivers for a PHP version with certain configuration options (such as arch, thread safe, etc.), you will be prompted whether to rebuild, clean or superclean. Choose `rebuild` if you have always used the same configuration. Otherwise, choose `clean` to remove previous builds (binaries). The last option will remove the entire `php--src` directory, which is often unnecessary. + +When something goes wrong during the build, the log file will be launched (you can find the log files in `C:\php-sdk`). Otherwise, the log file will not be shown, and they remain in `C:\php-sdk` until you remove them manually. + +After the compilation is complete, you will be given the option to rebuild or quit. If you choose rebuild, the extensions will be re-compiled (say after you have changed any source or header file). This script will keep running until you choose to quit. + +In addition to the log files in `C:\php-sdk`, you can examine the contents of `C:\php-sdk\phpsdk-build-task.bat`, which is overwritten every time you run the build scripts. + +When running build unattended, you should specify the destination path `--DESTPATH=`. In such case, the `php-sdk` folder will be created in the same working directory of these build scripts, and the drivers will be copied to your designated path. The script `builddrivers.py` provides an example for this case. Again, it's your choice whether to remove the `php-sdk` folder afterwards. + + + + + + From ad970b4b3a59bf1aee1b3173f23c998a50cab221 Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Tue, 15 Aug 2017 16:15:59 -0700 Subject: [PATCH 08/26] updated update_file_content as per review comment --- buildscripts/buildtools.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/buildscripts/buildtools.py b/buildscripts/buildtools.py index 61c0496a..32423d37 100644 --- a/buildscripts/buildtools.py +++ b/buildscripts/buildtools.py @@ -21,6 +21,7 @@ import stat import datetime import urllib.request import zipfile +import fileinput class BuildUtil(object): """Build sqlsrv and/or pdo_sqlsrv drivers with PHP source with the following properties: @@ -141,12 +142,9 @@ class BuildUtil(object): 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() + with fileinput.FileInput(file, inplace=True) as f: + for line in f: + print(line.replace(search_str, new_str), end='') @staticmethod def generateMMDD(): @@ -390,6 +388,12 @@ class BuildUtil(object): # Copy the generated batch file to phpSDK for the php starter script shutil.copy(os.path.join(work_dir, batch_file), phpSDK) + sdk_source = os.path.join(phpSDK, 'Source') + # Sometimes, for various reasons, the Source folder from previous build + # might exist in phpSDK. If so, remove it first + if os.path.exists(sdk_source): + os.chmod(sdk_source, stat.S_IWRITE) + shutil.rmtree(sdk_source, ignore_errors=True) shutil.move(source_dir, phpSDK) # Invoke phpsdk--.bat From 6f14e0f6d9c1300a685cfbf8c225bfdd85befc13 Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Wed, 16 Aug 2017 09:19:27 -0700 Subject: [PATCH 09/26] No longer renamed the pdb files --- buildscripts/builddrivers.py | 2 +- buildscripts/buildtools.py | 36 ++++++++++++++++++++---------------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/buildscripts/builddrivers.py b/buildscripts/builddrivers.py index a67af601..4005cbbf 100644 --- a/buildscripts/builddrivers.py +++ b/buildscripts/builddrivers.py @@ -148,7 +148,7 @@ class BuildDriver(object): print('Errors: Drivers destination should be defined! Doing nothing.') else: dest_drivers = os.path.join(self.remote_path, self.util.major_version(), self.util.arch) - dest_symbols = os.path.join(dest_drivers, 'Symbols') + dest_symbols = os.path.join(dest_drivers, 'Symbols', self.util.thread) # All intermediate directories will be created in order to create the leaf directory if os.path.exists(dest_symbols) == False: diff --git a/buildscripts/buildtools.py b/buildscripts/buildtools.py index 32423d37..0875398f 100644 --- a/buildscripts/buildtools.py +++ b/buildscripts/buildtools.py @@ -386,8 +386,12 @@ class BuildUtil(object): 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) + # Move the generated batch file to phpSDK for the php starter script + sdk_batch_file = os.path.join(phpSDK, batch_file) + if os.path.exists(sdk_batch_file): + os.remove(sdk_batch_file) + shutil.move(os.path.join(work_dir, batch_file), phpSDK) + sdk_source = os.path.join(phpSDK, 'Source') # Sometimes, for various reasons, the Source folder from previous build # might exist in phpSDK. If so, remove it first @@ -412,16 +416,16 @@ class BuildUtil(object): # 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 the *driver* binary (sqlsrv or pdo_sqlsrv) based on the *suffix*.""" - driver_old_name = self.driver_name(driver, suffix) - driver_new_name = self.driver_new_name(driver, suffix) + def rename_binary(self, path, driver): + """Rename the *driver* binary (sqlsrv or pdo_sqlsrv) (only the dlls).""" + driver_old_name = self.driver_name(driver, '.dll') + driver_new_name = self.driver_new_name(driver, '.dll') 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 according to the - PHP version and thread, including pdb files (the symbols). + """Rename the sqlsrv and/or pdo_sqlsrv dlls according to the PHP + version and thread. """ # Derive the path to where the extensions are located @@ -429,21 +433,21 @@ class BuildUtil(object): 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') + self.rename_binary(ext_dir, 'sqlsrv') + self.rename_binary(ext_dir, 'pdo_sqlsrv') else: - self.rename_binary(ext_dir, self.driver, '.dll') - self.rename_binary(ext_dir, self.driver, '.pdb') + self.rename_binary(ext_dir, self.driver) def copy_binary(self, from_dir, dest_dir, driver, suffix): """Copy sqlsrv or pdo_sqlsrv binary (based on *suffix*) to *dest_dir*.""" - binary = self.driver_new_name(driver, suffix) + if suffix == '.dll': + binary = self.driver_new_name(driver, suffix) + else: + binary = self.driver_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, + """Copy the sqlsrv and/or pdo_sqlsrv binaries, including the pdb files, to the right place, depending on *copy_to_ext*. The default is to copy them to the 'ext' folder. """ From eda3b7f35883eb747208a39ff45dc77019b2a8e1 Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Wed, 16 Aug 2017 13:04:26 -0700 Subject: [PATCH 10/26] Modified README files and removed odbc requirement to build --- README.md | 42 ++++------------- buildscripts/README.md | 93 ++++++++++++++++++++++++++++---------- buildscripts/buildtools.py | 2 +- 3 files changed, 78 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index dff3fda4..f77984ec 100644 --- a/README.md +++ b/README.md @@ -42,33 +42,9 @@ Please visit the [blog][blog] for more announcements. ## Build (Windows) -Note: if you prefer, you can use the pre-compiled binary found [HERE](https://github.com/Microsoft/msphpsql/releases) +If you prefer, you can use the pre-compiled binaries found [HERE](https://github.com/Microsoft/msphpsql/releases) -#### Prerequisites - -You must first be able to build PHP 7 without including these extensions. For help with doing this, see the [official PHP website][phpbuild] for building your own PHP on Windows. - -#### Compile the drivers - -1. Download the source code directory from this repository - -2. Make a copy of *shared* folder as a subfolder in *sqlsrv* and/or *pdo_sqlsrv* folder - -3. Copy the *sqlsrv* and/or *pdo_sqlsrv* folder into the PHP source ext subdirectory. - -4. Run `buildconf --force` to rebuild the configure.js script to include the driver. - -5. Run `configure.bat --with-odbcver=0x0380 and the desired driver options (as below) [plus other options such as --disable-zts for the Non Thread Safe build]` to generate the makefile. You can run `configure.bat --help` to see what other options are available. - * For SQLSRV use: `--enable-sqlsrv=shared` - * For PDO_SQLSRV use: `--enable-pdo --with-pdo-sqlsrv=shared` - -6. Run `nmake`. Optionally, you can run `nmake clean` first. - -7. To install the resulting build, run `nmake install` or just copy php_sqlsrv.dll and/or php_pdo_sqlsrv.dll to your PHP extension directory. - -The extensions have been compiled and tested with the PHP 7.0.* and 7.1.* using the Visual C++ 2015 compiler as well as PHP 7.2.0 beta using the Visual C++ 2017 V15.0. - -For an example of how to automate building the Microsoft Drivers for PHP for SQL Server, please browse the *buildscripts* directory. +The *buildscripts* directory contains step by step instructions on how to build the Microsoft Drivers for PHP for SQL Server. You can either build manually or use the sample scripts provided, which help automate the process. ## Install (Windows) @@ -86,7 +62,7 @@ For an example of how to automate building the Microsoft Drivers for PHP for SQL 3. Restart the Web server. ## Install (UNIX) -The following instructions assume a clean environment and show how to install PHP 7.x, Microsoft ODBC driver, apache, and Microsoft PHP drivers on Ubuntu 15, 16, RedHat 7, Debian 8, and Mac OS X. +The following instructions assume a clean environment and show how to install PHP 7.x, Microsoft ODBC driver, apache, and Microsoft PHP drivers on Ubuntu 15, 16, RedHat 7, Debian 8, and Mac OS. ### Step 1: Install PHP7+ @@ -126,7 +102,7 @@ The following instructions assume a clean environment and show how to install PH apt-get update apt-get install -y php7.0 php-pear php7.0-dev php7.0-xml -**Mac OS X** +**Mac OS** /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" brew tap @@ -169,7 +145,7 @@ Note that there are no PHP 7.1 packages available for Ubuntu 15.10. apt-get update apt-get install -y php7.1 php-pear php7.1-dev php7.1-xml -**Mac OS X** +**Mac OS** /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" brew tap @@ -236,7 +212,7 @@ Note that there are no PHP 7.1 packages available for Ubuntu 15.10. sudo ACCEPT_EULA=Y apt-get install msodbcsql sudo apt-get install unixodbc-dev -**Mac OS X** +**Mac OS** brew tap microsoft/msodbcsql https://github.com/Microsoft/homebrew-mssql-release brew update @@ -280,7 +256,7 @@ On all systems, run: echo "extension=sqlsrv.so" > /etc/php.d/sqlsrv.ini echo "extension=pdo_sqlsrv.so" > /etc/php.d/pdo_sqlsrv.ini -**Mac OS X** +**Mac OS** (echo ""; echo "SetHandler application/x-httpd-php"; echo "";) >> /usr/local/etc/apache2/2.4/httpd.conf @@ -303,7 +279,7 @@ On all systems, run: echo "extension=sqlsrv.so" > /etc/php.d/sqlsrv.ini echo "extension=pdo_sqlsrv.so" > /etc/php.d/pdo_sqlsrv.ini -**Mac OS X** +**Mac OS** (echo ""; echo "SetHandler application/x-httpd-php"; echo "";) >> /usr/local/etc/apache2/2.4/httpd.conf @@ -322,7 +298,7 @@ Note: On RedHat, SELinux is installed by default and runs in Enforcing mode. To sudo setsebool -P httpd_can_network_connect_db 1 -**Mac OS X** +**Mac OS** sudo apachectl restart diff --git a/buildscripts/README.md b/buildscripts/README.md index ffe79f26..da71851d 100644 --- a/buildscripts/README.md +++ b/buildscripts/README.md @@ -1,53 +1,96 @@ -## Windows +# Windows -### Prerequisites -To use the Python build scripts `builddrivers.py` and `buildtools.py`, install Python 3.x. +## Prerequisites -To build extensions for PHP 7.0* or 7.1*, install Visual Studio 2015 and make sure C++ tools are enabled. +To build extensions for + * PHP 7.0* or PHP 7.1*, install Visual Studio 2015 and make sure C++ tools are enabled. + * PHP 7.2*, install Visual Studio 2017, including Visual C++ toolset, the Windows SDK components, and Git for Windows. -For PHP 7.2*, install Visual Studio 2017, and make sure Visual C++ toolset, the Windows SDK components, and Git for Windows are installed. +To use the sample build scripts `builddrivers.py` and `buildtools.py`, install Python 3.x in Windows. -See [PHP SDK page](https://github.com/OSTC/php-sdk-binary-tools) for more details. +## Compile the drivers -### Build PHP extensions +You must first be able to build PHP 7.* without including these extensions. For help with doing this, see the [official PHP website](https://wiki.php.net/internals/windows/stepbystepbuild) for building PHP 7.0* or PHP 7.1* on Windows or [PHP SDK page](https://github.com/OSTC/php-sdk-binary-tools) for the new instructions for building PHP 7.2 and/or above. -Launch a regular `cmd` prompt and change to the directory where these Python scripts are. +The Microsoft Drivers for PHP for SQL Server have been compiled and tested with PHP 7.0.* and 7.1.* using the Visual C++ 2015 as well as PHP 7.2.0 beta using the Visual C++ 2017 v15.0. -You'll be asked to input the PHP version. Simply type the version number like `7.1.7`. +### Manually building from source -If it's alpha, beta, or RC, make sure the name you provide matches the PHP tag name without the prefix `php-`. For example, for PHP 7.2 beta 2, the tag name is `php-7.2.0beta2`, so you should enter `7.2.0beta2`. Visit https://github.com/php/php-src to find the appropriate tag names. +1. Download the *source* directory from this repository -**Use Interactive mode** +2. Make a copy of the *shared* folder as a subfolder in *sqlsrv* and/or *pdo_sqlsrv* folder -Run `py builddrivers.py` +3. Copy the *sqlsrv* and/or *pdo_sqlsrv* folder(s) into the PHP source ext subdirectory -Most questions are self-explanatory. Use lower cases for your inputs, and you can simply hit `ENTER` key for `yes/no` questions. +4. Run `buildconf --force` to rebuild the configure.js script to include the *sqlsrv* and/or *pdo_sqlsrv* driver(s). -When given a choice whether to download from a GitHub repository, you can choose to provide the full path to your local Source folder instead. +5. Run `configure.bat` with the desired driver options (as shown below) to generate the makefile. You can run `configure.bat --help` to see what other options are available. For example, for non-thread safe build, add this option `--disable-zts`. + * For SQLSRV add: `--enable-sqlsrv=shared` + * For PDO_SQLSRV add: `--enable-pdo --with-pdo-sqlsrv=shared` -**Use Command-line arguments** +6. Run `nmake`. Optionally, you can run `nmake clean` first. -Run `py builddrivers.py` with command line arguments. For example: +7. To install the drivers, there are two ways: + * Run `nmake install`, or + * Copy the drivers: + * Find the directory where the newly compiled `php.exe` is + * Locate the compiled php_sqlsrv.dll and/or php_pdo_sqlsrv.dll + * Copy the dll(s) to the `ext` subfolder -`py builddrivers.py -v=7.1.7 -a=x86 -t=ts -d=all -g=no` +### Using the sample build scripts -or +The sample build scripts, `builddrivers.py` and `buildtools.py`, are expected to build our extensions for PHP in Windows. -`py builddrivers.py --PHPVER=7.0.22 --ARCH=x64 --THREAD=nts --DRIVER=sqlsrv --GITHUB=yes` +#### Overview -**Notes** +When asked to provide the PHP version, you should enter values like `7.1.7`. If it's alpha, beta, or RC, make sure the name you provide matches the PHP tag name without the prefix `php-`. For example, for PHP 7.2 beta 2, the tag name is `php-7.2.0beta2`, so you should enter `7.2.0beta2`. Visit [PHP SRC]( https://github.com/php/php-src) to find the appropriate tag names. -It's recommended that the PHP SDK is unzipped into the shortest possible path, preferrably somewhere near the root drive. Therefore, this script will create a `php-sdk` folder in C:\ drive. This `php-sdk` folder will remain unless you remove it yourself. For ongoing development, it's suggested you keep it around. The build scripts will handle updating the PHP SDK if new version is available. +It's recommended that the PHP SDK is unzipped into the shortest possible path, preferrably somewhere near the root drive. Therefore, this script will create a `php-sdk` folder in the C:\ drive. This `php-sdk` folder will remain unless you remove it yourself. For ongoing development, it's suggested you keep it around. The build scripts will handle updating the PHP SDK if new version is available. -If this is not the first time you build the drivers for a PHP version with certain configuration options (such as arch, thread safe, etc.), you will be prompted whether to rebuild, clean or superclean. Choose `rebuild` if you have always used the same configuration. Otherwise, choose `clean` to remove previous builds (binaries). The last option will remove the entire `php--src` directory, which is often unnecessary. +#### Steps + +1. Launch a regular `cmd` prompt + +2. Change to the directory where the Python scripts `builddrivers.py` and `buildtools.py` are + +3. Interactive mode: + * Run `py builddrivers.py` to use the interactive mode. Use lower cases to answer the following questions: + * PHP Version (i.e. the version number like `7.1.7` or `7.2.0beta2`) + * 64-bit? + * Thread safe? + * Driver? + * Debug enabled? + * Download source from GitHub? + * For `yes/no` questions, you can simply hit `ENTER` key for `yes`. Other questions are self-explanatory. + +4. Use Command-line arguments + * Run `py builddrivers.py -h` to get a list of options and their descriptions + * For example, + * `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=sqlsrv --GITHUB=yes` + +5. If the script detects the presence of a PHP source directory, you will be prompted whether to rebuild, clean or superclean. Choose + * `rebuild` if you have always used the same configuration (32 bit, thread safe, etc.) + * `clean` to remove previous builds (binaries) + * `superclean` to remove the entire `php--src` directory, which is often unnecessary + +6. If you choose not to download from a GitHub repository, you will be asked to provide the full path to your local Source folder. + +7. If the compilation is successful, you will be given the option to rebuild or quit. + +#### Troubleshooting When something goes wrong during the build, the log file will be launched (you can find the log files in `C:\php-sdk`). Otherwise, the log file will not be shown, and they remain in `C:\php-sdk` until you remove them manually. -After the compilation is complete, you will be given the option to rebuild or quit. If you choose rebuild, the extensions will be re-compiled (say after you have changed any source or header file). This script will keep running until you choose to quit. - In addition to the log files in `C:\php-sdk`, you can examine the contents of `C:\php-sdk\phpsdk-build-task.bat`, which is overwritten every time you run the build scripts. -When running build unattended, you should specify the destination path `--DESTPATH=`. In such case, the `php-sdk` folder will be created in the same working directory of these build scripts, and the drivers will be copied to your designated path. The script `builddrivers.py` provides an example for this case. Again, it's your choice whether to remove the `php-sdk` folder afterwards. +#### Building the extensions unattended + +You can invoke `py builddrivers.py` with the desired options plus the destination path `--DESTPATH=`. + +In such case, the `php-sdk` folder will be created in the same directory of these build scripts. Note that the PHP drivers will also be copied to the designated path. + +The script `builddrivers.py` provides an example for this case. Again, it's your choice whether to remove the `php-sdk` folder afterwards. diff --git a/buildscripts/buildtools.py b/buildscripts/buildtools.py index 0875398f..d527febb 100644 --- a/buildscripts/buildtools.py +++ b/buildscripts/buildtools.py @@ -296,7 +296,7 @@ class BuildUtil(object): 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' + cmd_line = 'cscript configure.js --disable-all --enable-cli --enable-cgi --enable-embed' + cmd_line if self.thread == 'nts': cmd_line = cmd_line + ' --disable-zts' return cmd_line From da43f6b58c3bef8e81f3c56c77268bbc430d2d8b Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Fri, 11 Aug 2017 12:49:54 -0700 Subject: [PATCH 11/26] Python scripts for building extensions for PHP 7+ in Windows --- buildscripts/builddrivers.py | 268 +++++++++++++++++++++ buildscripts/buildtools.py | 437 +++++++++++++++++++++++++++++++++++ 2 files changed, 705 insertions(+) create mode 100644 buildscripts/builddrivers.py create mode 100644 buildscripts/buildtools.py 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') + From 82f4d99a84cd43b8b252390e340eee243a33580e Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Mon, 14 Aug 2017 08:54:21 -0700 Subject: [PATCH 12/26] modified comments and corrected typo --- buildscripts/builddrivers.py | 20 ++++++++++++-------- buildscripts/buildtools.py | 27 +++++++++++++++++++-------- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/buildscripts/builddrivers.py b/buildscripts/builddrivers.py index 03b0c9f6..0bb771b0 100644 --- a/buildscripts/builddrivers.py +++ b/buildscripts/builddrivers.py @@ -31,11 +31,7 @@ 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 + util # BuildUtil object whose constructor takes phpver, driver, arch, thread, debug repo # GitHub repository branch # GitHub repository branch download_source # download source from GitHub or not @@ -65,10 +61,14 @@ class BuildDriver(object): 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.""" + """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.") + print(phpsrc + " exists.") + print("Choose rebuild(r) if using the same configuration. Choose clean(c) otherwise. If unsure, choose superclean(s).") choice = validate_input("Want to rebuild (r), clean (c) or superclean (s)? ", "r/c/s") self.make_clean = False if choice == 'r': @@ -86,7 +86,11 @@ class BuildDriver(object): 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.""" + """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: diff --git a/buildscripts/buildtools.py b/buildscripts/buildtools.py index 17f2d4a9..6633371a 100644 --- a/buildscripts/buildtools.py +++ b/buildscripts/buildtools.py @@ -189,7 +189,9 @@ class BuildUtil(object): @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*.""" + """Download to *dest_folder* the msphpsql archive of the specified + GitHub *repo* and *branch*. The downloaded files will be removed by default. + """ try: work_dir = os.path.dirname(os.path.realpath(__file__)) @@ -207,8 +209,8 @@ class BuildUtil(object): 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 + print ("Resort to skip ssl verification...") + # need to skip ssl verification 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) @@ -234,7 +236,9 @@ class BuildUtil(object): raise def update_driver_source(self, source_dir, driver): - """Update the *driver* source in *source_path* with the latest version, file descriptions, etc.""" + """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 @@ -327,7 +331,10 @@ class BuildUtil(object): 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.""" + """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 @@ -376,7 +383,7 @@ class BuildUtil(object): 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 + # already been modified prior to building the extensions shutil.rmtree(os.path.join(phpSDK, 'Source'), ignore_errors=True) # Next, rename the newly compiled PHP extensions @@ -393,7 +400,9 @@ class BuildUtil(object): 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.""" + """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) @@ -414,7 +423,9 @@ class BuildUtil(object): 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.""" + """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: From 72c9d3a111e0548a35e45c428666fe856d4c9112 Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Mon, 14 Aug 2017 09:21:36 -0700 Subject: [PATCH 13/26] added more checks for path existence --- buildscripts/builddrivers.py | 13 ++++++++----- buildscripts/buildtools.py | 8 +++++++- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/buildscripts/builddrivers.py b/buildscripts/builddrivers.py index 0bb771b0..b429e14a 100644 --- a/buildscripts/builddrivers.py +++ b/buildscripts/builddrivers.py @@ -105,13 +105,16 @@ class BuildDriver(object): 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 ): + + valid = True + if os.path.exists(source) and os.path.exists(os.path.join(source, 'shared')): + # Checking the existence of 'shared' folder only, assuming + # sqlsrv and/or pdo_sqlsrv are also present if it exists self.source_path = source break - else: - print('The path provided does not exist. Please re-enter.') - + + print("The path provided is invalid. Please re-enter.") + print('Copying source files from', source) os.system('ROBOCOPY ' + source + '\shared ' + work_dir + '\Source\shared /xx /xo ') diff --git a/buildscripts/buildtools.py b/buildscripts/buildtools.py index 6633371a..1436f981 100644 --- a/buildscripts/buildtools.py +++ b/buildscripts/buildtools.py @@ -111,9 +111,15 @@ class BuildUtil(object): 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""" + """Remove all binaries and source code in the + Release* or Debug* folders according to the current + configuration + """ print('Removing previous build...') build_dir = self.build_abs_path(sdk_dir) + if not os.path.exists(build_dir): + return + os.chdir(build_dir) os.system('DEL *sqlsrv*') From 9142d5a213af06ccef809b9565a031a007cc747d Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Mon, 14 Aug 2017 14:58:37 -0700 Subject: [PATCH 14/26] Added code to remove optimization flags for debug build --- buildscripts/builddrivers.py | 38 +++++++++++++++++++++++++----------- buildscripts/buildtools.py | 29 ++++++++++++++++++--------- 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/buildscripts/builddrivers.py b/buildscripts/builddrivers.py index b429e14a..a27cf6dc 100644 --- a/buildscripts/builddrivers.py +++ b/buildscripts/builddrivers.py @@ -35,7 +35,7 @@ class BuildDriver(object): repo # GitHub repository branch # GitHub repository branch download_source # download source from GitHub or not - package # package name for the binaries + package # package name for the binaries (will be ignored for local builds) 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 @@ -54,42 +54,55 @@ class BuildDriver(object): self.source_path = None # None initially but will be set later if not downloading from GitHub def show_config(self): + print() 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) + print() 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. + """Only check this when building locally and not rebuilding. If the php source directory + already exists, this will prompt user whether to rebuild, clean, or superclean, the last option + will remove the entire php source directory. + + :param root_dir: the C:\ drive + :param work_dir: the directory of this script + :outcome: the old binaries, if exist, will be removed """ phpsrc = self.util.phpsrc_root(root_dir) if os.path.exists( phpsrc ): print(phpsrc + " exists.") print("Choose rebuild(r) if using the same configuration. Choose clean(c) otherwise. If unsure, choose superclean(s).") - choice = validate_input("Want to rebuild (r), clean (c) or superclean (s)? ", "r/c/s") + build_choice = validate_input("Want to rebuild (r), clean (c) or superclean (s)? ", "r/c/s") self.make_clean = False - if choice == 'r': + if build_choice == 'r': print('Will rebuild the binaries') + # only the old binaries based on the current configuration will be removed self.util.remove_prev_build(root_dir) - elif choice == 'c': + elif build_choice == 'c': print('Will make clean') self.make_clean = True - # this step is necessary in case the user has changed the configuration + # all old builds are removed, and this step is necessary because + # the user might have 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) + os.chdir(work_dir) # change back to the working directory 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. + + :param dest: either None (for remote builds) or the C:\ drive (for local builds) + :param logfile: the name of the logfile + :outcome: the drivers and symbols will renamed and placed in the appropriate location(s) + """ work_dir = os.path.dirname(os.path.realpath(__file__)) @@ -98,6 +111,7 @@ class BuildDriver(object): self.util.download_msphpsql_source(repo, branch) else: # This case only happens when building locally (interactive mode) + # because download_source must be True for remote builds while True: if self.source_path is None: source = input('Enter the full path to the Source folder: ') @@ -153,7 +167,9 @@ class BuildDriver(object): def build(self): - """This is the main entry point of building drivers for PHP.""" + """This is the main entry point of building drivers for PHP. + For local builds, this will loop till the user decides to quit. + """ self.show_config() work_dir = os.path.dirname(os.path.realpath(__file__)) @@ -221,7 +237,7 @@ if __name__ == '__main__': 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") + parser.add_argument('-p', '--PATH', default=None, help="the remote destination for the drivers (do not use this when building locally)") args = parser.parse_args() diff --git a/buildscripts/buildtools.py b/buildscripts/buildtools.py index 1436f981..9b749ac0 100644 --- a/buildscripts/buildtools.py +++ b/buildscripts/buildtools.py @@ -111,9 +111,8 @@ class BuildUtil(object): 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 according to the current - configuration + """Remove all binaries and source code in the Release* or Debug* + folders according to the current configuration """ print('Removing previous build...') build_dir = self.build_abs_path(sdk_dir) @@ -244,9 +243,20 @@ class BuildUtil(object): def update_driver_source(self, source_dir, driver): """Update the *driver* source in *source_path* with the latest version, file descriptions, etc. + If debug is enabled, will remove the optimization flag """ driver_dir = os.path.join(source_dir, driver) + if self.debug_enabled: + # Remove the optimization flag in the config file for this driver + # because '/O2' option is incompatible with Debug mode + print('Removing optimization flag for', driver) + config_file = os.path.join(driver_dir, 'config.w32') + if driver == 'sqlsrv': + self.update_file_content(config_file, 'ADD_FLAG( "CFLAGS_SQLSRV", "/O2" );', '') + elif driver == 'pdo_sqlsrv': + self.update_file_content(config_file, 'ADD_FLAG( "CFLAGS_PDO_SQLSRV", "/O2" );', '') + # Update Template.rc template_file = os.path.join(driver_dir, 'template.rc') if driver == 'sqlsrv': @@ -399,15 +409,15 @@ class BuildUtil(object): self.copy_binaries(sdk_dir, copy_to_ext) def rename_binary(self, path, driver, suffix): - """Rename sqlsrv or pdo_sqlsrv binary.""" + """Rename the *driver* binary (sqlsrv or pdo_sqlsrv) based on the *suffix*.""" 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. + """Rename the sqlsrv and/or pdo_sqlsrv binaries according to the + PHP version and thread, including pdb files (the symbols). """ # Derive the path to where the extensions are located @@ -424,13 +434,14 @@ class BuildUtil(object): 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*.""" + """Copy sqlsrv or pdo_sqlsrv binary (based on *suffix*) 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. + """Copy the sqlsrv and/or pdo_sqlsrv binaries, including pdb files, + to the right place, depending on *copy_to_ext*. The default is to + copy them to the 'ext' folder. """ build_dir = self.build_abs_path(sdk_dir) print('Copying the binaries from', build_dir) From 5b4d1271a727be4e55e21b801da6f2b645e823de Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Tue, 15 Aug 2017 09:50:34 -0700 Subject: [PATCH 15/26] Modified scripts based on review comments --- buildscripts/builddrivers.py | 28 ++++++++++++---------------- buildscripts/buildtools.py | 6 +++--- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/buildscripts/builddrivers.py b/buildscripts/builddrivers.py index a27cf6dc..fbd663a3 100644 --- a/buildscripts/builddrivers.py +++ b/buildscripts/builddrivers.py @@ -1,10 +1,10 @@ -#!/usr/bin/python +#!/usr/bin/python3 ######################################################################################### # # Description: This script helps to build drivers in a Windows environment for PHP 7+ (32-bit/64-bit) # # Requirement: -# python 3.4 +# python 3.x # 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*) @@ -35,18 +35,18 @@ class BuildDriver(object): repo # GitHub repository branch # GitHub repository branch download_source # download source from GitHub or not - package # package name for the binaries (will be ignored for local builds) remote_path # remote destination to where the drivers will be placed (None for local builds) - local # whether the build is local + local # a boolean flag - whether the build is local + rebuild # a boolean flag - whether the user is rebuilding + make_clean # a boolean flag - whether make clean is necessary source_path # path to a local source folder """ - def __init__(self, phpver, driver, arch, thread, debug, repo, branch, download, package, path): + def __init__(self, phpver, driver, arch, thread, debug, repo, branch, download, 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 @@ -147,8 +147,7 @@ class BuildDriver(object): 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_drivers = os.path.join(self.remote_path, 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 @@ -236,8 +235,7 @@ if __name__ == '__main__': 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 (do not use this when building locally)") + parser.add_argument('-p', '--DESTPATH', default=None, help="the remote destination for the drivers (do not use this for local builds)") args = parser.parse_args() @@ -249,19 +247,18 @@ if __name__ == '__main__': repo = args.REPO branch = args.BRANCH download = args.GITHUB.lower() == 'yes' - path = args.PATH - package = args.PACKAGE + path = args.DESTPATH 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]: ") + arch_version = input("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]? ") + debug_mode = input("Debug enabled? [y/n]: ") - answer = input("Download source from a GitHub repo [y/n]? ") + answer = input("Download source from a GitHub repo? [y/n]: ") download = False if answer == 'yes' or answer == 'y' or answer == '': download = True @@ -286,6 +283,5 @@ if __name__ == '__main__': repo, branch, download, - package, path) builder.build() diff --git a/buildscripts/buildtools.py b/buildscripts/buildtools.py index 9b749ac0..61c0496a 100644 --- a/buildscripts/buildtools.py +++ b/buildscripts/buildtools.py @@ -1,11 +1,11 @@ -#!/usr/bin/python +#!/usr/bin/python3 ######################################################################################### # # Description: The class BuildUtil will build Microsoft SQL Server PHP 7+ Drivers # for 32 bit and 64 bit. # # Requirement: -# python 3.4 +# python 3.x # PHP SDK and PHP Source # Driver source code folder # Git for Windows @@ -48,7 +48,7 @@ class BuildUtil(object): """Return the version label based on the PHP version.""" major_ver = self.major_version() - if major_ver == '7.0': + if major_ver[2] == '0': version = major_ver[0] else: version = major_ver[0] + major_ver[2] From f6b848ac356375d9b4a94e723a424034ed861b03 Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Tue, 15 Aug 2017 11:54:51 -0700 Subject: [PATCH 16/26] added a new README plus modifying the project README --- README.md | 26 ++++++++++++++++---------- buildscripts/builddrivers.py | 2 +- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 6c9c2aeb..b9d9a869 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Thank you for taking time to take our February survey. Let us know how we are do ## Announcements - Please visit the [blog][blog] for more announcements. +Please visit the [blog][blog] for more announcements. ## Build (Windows) @@ -46,23 +46,29 @@ Note: if you prefer, you can use the pre-compiled binary found [HERE](https://gi #### Prerequisites -You must first be able to build PHP 7 without including these extensions. For help with doing this, see the [official PHP website][phpbuild] for building your own PHP on Windows. +You must first be able to build PHP 7 without including these extensions. For help with doing this, see the [official PHP website][phpbuild] for building your own PHP on Windows. #### Compile the drivers -1. Copy the sqlsrv and/or pdo_sqlsrv source code directory from this repository into the ext subdirectory. +1. Download the source code directory from this repository -2. Run `buildconf.bat` to rebuild the configure.js script to include the driver. +2. Make a copy of *shared* folder as a subfolder in *sqlsrv* and/or *pdo_sqlsrv* folder -3. Run `configure.bat --with-odbcver=0x0380 and the desired driver options (as below) [plus other options such as --disable-zts for the Non Thread Safe build]` to generate the makefile. You can run `configure.bat --help` to see what other options are available. - * For SQLSRV use: `--enable-sqlsrv=shared` - * For PDO_SQLSRV use: `--enable-pdo=shared --with-pdo-sqlsrv=shared` +3. Copy the *sqlsrv* and/or *pdo_sqlsrv* folder into the PHP source ext subdirectory. -4. Run `nmake`. It is suggested that you run the entire build. If you wish to do so, run `nmake clean` first. +4. Run `buildconf --force` to rebuild the configure.js script to include the driver. -5. To install the resulting build, run `nmake install` or just copy php_sqlsrv.dll and/or php_pdo_sqlsrv.dll to your PHP extension directory. +5. Run `configure.bat --with-odbcver=0x0380 and the desired driver options (as below) [plus other options such as --disable-zts for the Non Thread Safe build]` to generate the makefile. You can run `configure.bat --help` to see what other options are available. + * For SQLSRV use: `--enable-sqlsrv=shared` + * For PDO_SQLSRV use: `--enable-pdo --with-pdo-sqlsrv=shared` -This software has been compiled and tested under PHP 7.0.20 and 7.1.6 using the Visual C++ 2015 compiler. +6. Run `nmake`. Optionally, you can run `nmake clean` first. + +7. To install the resulting build, run `nmake install` or just copy php_sqlsrv.dll and/or php_pdo_sqlsrv.dll to your PHP extension directory. + +The extensions have been compiled and tested with the PHP 7.0.* and 7.1.* using the Visual C++ 2015 compiler as well as PHP 7.2.0 beta using the Visual C++ 2017 V15.0. + +For an example of how to automate building the Microsoft Drivers for PHP for SQL Server, please browse the *buildscripts* directory. ## Install (Windows) diff --git a/buildscripts/builddrivers.py b/buildscripts/builddrivers.py index fbd663a3..a67af601 100644 --- a/buildscripts/builddrivers.py +++ b/buildscripts/builddrivers.py @@ -145,7 +145,7 @@ class BuildDriver(object): # Make sure drivers path is defined if self.remote_path is None: - print('Errors: Drivers destination should be defined! Do nothing.') + print('Errors: Drivers destination should be defined! Doing nothing.') else: dest_drivers = os.path.join(self.remote_path, self.util.major_version(), self.util.arch) dest_symbols = os.path.join(dest_drivers, 'Symbols') From aeaa61cc934523916abf7e3fde7d95e13a48ded1 Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Tue, 15 Aug 2017 13:20:20 -0700 Subject: [PATCH 17/26] README for build scripts --- buildscripts/README.md | 56 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 buildscripts/README.md diff --git a/buildscripts/README.md b/buildscripts/README.md new file mode 100644 index 00000000..ffe79f26 --- /dev/null +++ b/buildscripts/README.md @@ -0,0 +1,56 @@ +## Windows + +### Prerequisites +To use the Python build scripts `builddrivers.py` and `buildtools.py`, install Python 3.x. + +To build extensions for PHP 7.0* or 7.1*, install Visual Studio 2015 and make sure C++ tools are enabled. + +For PHP 7.2*, install Visual Studio 2017, and make sure Visual C++ toolset, the Windows SDK components, and Git for Windows are installed. + +See [PHP SDK page](https://github.com/OSTC/php-sdk-binary-tools) for more details. + +### Build PHP extensions + +Launch a regular `cmd` prompt and change to the directory where these Python scripts are. + +You'll be asked to input the PHP version. Simply type the version number like `7.1.7`. + +If it's alpha, beta, or RC, make sure the name you provide matches the PHP tag name without the prefix `php-`. For example, for PHP 7.2 beta 2, the tag name is `php-7.2.0beta2`, so you should enter `7.2.0beta2`. Visit https://github.com/php/php-src to find the appropriate tag names. + +**Use Interactive mode** + +Run `py builddrivers.py` + +Most questions are self-explanatory. Use lower cases for your inputs, and you can simply hit `ENTER` key for `yes/no` questions. + +When given a choice whether to download from a GitHub repository, you can choose to provide the full path to your local Source folder instead. + +**Use Command-line arguments** + +Run `py builddrivers.py` with command line arguments. For example: + +`py builddrivers.py -v=7.1.7 -a=x86 -t=ts -d=all -g=no` + +or + +`py builddrivers.py --PHPVER=7.0.22 --ARCH=x64 --THREAD=nts --DRIVER=sqlsrv --GITHUB=yes` + +**Notes** + +It's recommended that the PHP SDK is unzipped into the shortest possible path, preferrably somewhere near the root drive. Therefore, this script will create a `php-sdk` folder in C:\ drive. This `php-sdk` folder will remain unless you remove it yourself. For ongoing development, it's suggested you keep it around. The build scripts will handle updating the PHP SDK if new version is available. + +If this is not the first time you build the drivers for a PHP version with certain configuration options (such as arch, thread safe, etc.), you will be prompted whether to rebuild, clean or superclean. Choose `rebuild` if you have always used the same configuration. Otherwise, choose `clean` to remove previous builds (binaries). The last option will remove the entire `php--src` directory, which is often unnecessary. + +When something goes wrong during the build, the log file will be launched (you can find the log files in `C:\php-sdk`). Otherwise, the log file will not be shown, and they remain in `C:\php-sdk` until you remove them manually. + +After the compilation is complete, you will be given the option to rebuild or quit. If you choose rebuild, the extensions will be re-compiled (say after you have changed any source or header file). This script will keep running until you choose to quit. + +In addition to the log files in `C:\php-sdk`, you can examine the contents of `C:\php-sdk\phpsdk-build-task.bat`, which is overwritten every time you run the build scripts. + +When running build unattended, you should specify the destination path `--DESTPATH=`. In such case, the `php-sdk` folder will be created in the same working directory of these build scripts, and the drivers will be copied to your designated path. The script `builddrivers.py` provides an example for this case. Again, it's your choice whether to remove the `php-sdk` folder afterwards. + + + + + + From 26bb1d62af31f0ac3ac114ddc8e6066e5167a839 Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Tue, 15 Aug 2017 16:15:59 -0700 Subject: [PATCH 18/26] updated update_file_content as per review comment --- buildscripts/buildtools.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/buildscripts/buildtools.py b/buildscripts/buildtools.py index 61c0496a..32423d37 100644 --- a/buildscripts/buildtools.py +++ b/buildscripts/buildtools.py @@ -21,6 +21,7 @@ import stat import datetime import urllib.request import zipfile +import fileinput class BuildUtil(object): """Build sqlsrv and/or pdo_sqlsrv drivers with PHP source with the following properties: @@ -141,12 +142,9 @@ class BuildUtil(object): 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() + with fileinput.FileInput(file, inplace=True) as f: + for line in f: + print(line.replace(search_str, new_str), end='') @staticmethod def generateMMDD(): @@ -390,6 +388,12 @@ class BuildUtil(object): # Copy the generated batch file to phpSDK for the php starter script shutil.copy(os.path.join(work_dir, batch_file), phpSDK) + sdk_source = os.path.join(phpSDK, 'Source') + # Sometimes, for various reasons, the Source folder from previous build + # might exist in phpSDK. If so, remove it first + if os.path.exists(sdk_source): + os.chmod(sdk_source, stat.S_IWRITE) + shutil.rmtree(sdk_source, ignore_errors=True) shutil.move(source_dir, phpSDK) # Invoke phpsdk--.bat From 2ae378227eb6567a00db80e88ee2f38646b1d98e Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Wed, 16 Aug 2017 09:19:27 -0700 Subject: [PATCH 19/26] No longer renamed the pdb files --- buildscripts/builddrivers.py | 2 +- buildscripts/buildtools.py | 36 ++++++++++++++++++++---------------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/buildscripts/builddrivers.py b/buildscripts/builddrivers.py index a67af601..4005cbbf 100644 --- a/buildscripts/builddrivers.py +++ b/buildscripts/builddrivers.py @@ -148,7 +148,7 @@ class BuildDriver(object): print('Errors: Drivers destination should be defined! Doing nothing.') else: dest_drivers = os.path.join(self.remote_path, self.util.major_version(), self.util.arch) - dest_symbols = os.path.join(dest_drivers, 'Symbols') + dest_symbols = os.path.join(dest_drivers, 'Symbols', self.util.thread) # All intermediate directories will be created in order to create the leaf directory if os.path.exists(dest_symbols) == False: diff --git a/buildscripts/buildtools.py b/buildscripts/buildtools.py index 32423d37..0875398f 100644 --- a/buildscripts/buildtools.py +++ b/buildscripts/buildtools.py @@ -386,8 +386,12 @@ class BuildUtil(object): 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) + # Move the generated batch file to phpSDK for the php starter script + sdk_batch_file = os.path.join(phpSDK, batch_file) + if os.path.exists(sdk_batch_file): + os.remove(sdk_batch_file) + shutil.move(os.path.join(work_dir, batch_file), phpSDK) + sdk_source = os.path.join(phpSDK, 'Source') # Sometimes, for various reasons, the Source folder from previous build # might exist in phpSDK. If so, remove it first @@ -412,16 +416,16 @@ class BuildUtil(object): # 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 the *driver* binary (sqlsrv or pdo_sqlsrv) based on the *suffix*.""" - driver_old_name = self.driver_name(driver, suffix) - driver_new_name = self.driver_new_name(driver, suffix) + def rename_binary(self, path, driver): + """Rename the *driver* binary (sqlsrv or pdo_sqlsrv) (only the dlls).""" + driver_old_name = self.driver_name(driver, '.dll') + driver_new_name = self.driver_new_name(driver, '.dll') 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 according to the - PHP version and thread, including pdb files (the symbols). + """Rename the sqlsrv and/or pdo_sqlsrv dlls according to the PHP + version and thread. """ # Derive the path to where the extensions are located @@ -429,21 +433,21 @@ class BuildUtil(object): 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') + self.rename_binary(ext_dir, 'sqlsrv') + self.rename_binary(ext_dir, 'pdo_sqlsrv') else: - self.rename_binary(ext_dir, self.driver, '.dll') - self.rename_binary(ext_dir, self.driver, '.pdb') + self.rename_binary(ext_dir, self.driver) def copy_binary(self, from_dir, dest_dir, driver, suffix): """Copy sqlsrv or pdo_sqlsrv binary (based on *suffix*) to *dest_dir*.""" - binary = self.driver_new_name(driver, suffix) + if suffix == '.dll': + binary = self.driver_new_name(driver, suffix) + else: + binary = self.driver_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, + """Copy the sqlsrv and/or pdo_sqlsrv binaries, including the pdb files, to the right place, depending on *copy_to_ext*. The default is to copy them to the 'ext' folder. """ From be74169501b722c0cc06fc48ac0df878da7b6a66 Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Wed, 16 Aug 2017 13:04:26 -0700 Subject: [PATCH 20/26] Modified README files and removed odbc requirement to build --- buildscripts/README.md | 93 ++++++++++++++++++++++++++++---------- buildscripts/buildtools.py | 2 +- 2 files changed, 69 insertions(+), 26 deletions(-) diff --git a/buildscripts/README.md b/buildscripts/README.md index ffe79f26..da71851d 100644 --- a/buildscripts/README.md +++ b/buildscripts/README.md @@ -1,53 +1,96 @@ -## Windows +# Windows -### Prerequisites -To use the Python build scripts `builddrivers.py` and `buildtools.py`, install Python 3.x. +## Prerequisites -To build extensions for PHP 7.0* or 7.1*, install Visual Studio 2015 and make sure C++ tools are enabled. +To build extensions for + * PHP 7.0* or PHP 7.1*, install Visual Studio 2015 and make sure C++ tools are enabled. + * PHP 7.2*, install Visual Studio 2017, including Visual C++ toolset, the Windows SDK components, and Git for Windows. -For PHP 7.2*, install Visual Studio 2017, and make sure Visual C++ toolset, the Windows SDK components, and Git for Windows are installed. +To use the sample build scripts `builddrivers.py` and `buildtools.py`, install Python 3.x in Windows. -See [PHP SDK page](https://github.com/OSTC/php-sdk-binary-tools) for more details. +## Compile the drivers -### Build PHP extensions +You must first be able to build PHP 7.* without including these extensions. For help with doing this, see the [official PHP website](https://wiki.php.net/internals/windows/stepbystepbuild) for building PHP 7.0* or PHP 7.1* on Windows or [PHP SDK page](https://github.com/OSTC/php-sdk-binary-tools) for the new instructions for building PHP 7.2 and/or above. -Launch a regular `cmd` prompt and change to the directory where these Python scripts are. +The Microsoft Drivers for PHP for SQL Server have been compiled and tested with PHP 7.0.* and 7.1.* using the Visual C++ 2015 as well as PHP 7.2.0 beta using the Visual C++ 2017 v15.0. -You'll be asked to input the PHP version. Simply type the version number like `7.1.7`. +### Manually building from source -If it's alpha, beta, or RC, make sure the name you provide matches the PHP tag name without the prefix `php-`. For example, for PHP 7.2 beta 2, the tag name is `php-7.2.0beta2`, so you should enter `7.2.0beta2`. Visit https://github.com/php/php-src to find the appropriate tag names. +1. Download the *source* directory from this repository -**Use Interactive mode** +2. Make a copy of the *shared* folder as a subfolder in *sqlsrv* and/or *pdo_sqlsrv* folder -Run `py builddrivers.py` +3. Copy the *sqlsrv* and/or *pdo_sqlsrv* folder(s) into the PHP source ext subdirectory -Most questions are self-explanatory. Use lower cases for your inputs, and you can simply hit `ENTER` key for `yes/no` questions. +4. Run `buildconf --force` to rebuild the configure.js script to include the *sqlsrv* and/or *pdo_sqlsrv* driver(s). -When given a choice whether to download from a GitHub repository, you can choose to provide the full path to your local Source folder instead. +5. Run `configure.bat` with the desired driver options (as shown below) to generate the makefile. You can run `configure.bat --help` to see what other options are available. For example, for non-thread safe build, add this option `--disable-zts`. + * For SQLSRV add: `--enable-sqlsrv=shared` + * For PDO_SQLSRV add: `--enable-pdo --with-pdo-sqlsrv=shared` -**Use Command-line arguments** +6. Run `nmake`. Optionally, you can run `nmake clean` first. -Run `py builddrivers.py` with command line arguments. For example: +7. To install the drivers, there are two ways: + * Run `nmake install`, or + * Copy the drivers: + * Find the directory where the newly compiled `php.exe` is + * Locate the compiled php_sqlsrv.dll and/or php_pdo_sqlsrv.dll + * Copy the dll(s) to the `ext` subfolder -`py builddrivers.py -v=7.1.7 -a=x86 -t=ts -d=all -g=no` +### Using the sample build scripts -or +The sample build scripts, `builddrivers.py` and `buildtools.py`, are expected to build our extensions for PHP in Windows. -`py builddrivers.py --PHPVER=7.0.22 --ARCH=x64 --THREAD=nts --DRIVER=sqlsrv --GITHUB=yes` +#### Overview -**Notes** +When asked to provide the PHP version, you should enter values like `7.1.7`. If it's alpha, beta, or RC, make sure the name you provide matches the PHP tag name without the prefix `php-`. For example, for PHP 7.2 beta 2, the tag name is `php-7.2.0beta2`, so you should enter `7.2.0beta2`. Visit [PHP SRC]( https://github.com/php/php-src) to find the appropriate tag names. -It's recommended that the PHP SDK is unzipped into the shortest possible path, preferrably somewhere near the root drive. Therefore, this script will create a `php-sdk` folder in C:\ drive. This `php-sdk` folder will remain unless you remove it yourself. For ongoing development, it's suggested you keep it around. The build scripts will handle updating the PHP SDK if new version is available. +It's recommended that the PHP SDK is unzipped into the shortest possible path, preferrably somewhere near the root drive. Therefore, this script will create a `php-sdk` folder in the C:\ drive. This `php-sdk` folder will remain unless you remove it yourself. For ongoing development, it's suggested you keep it around. The build scripts will handle updating the PHP SDK if new version is available. -If this is not the first time you build the drivers for a PHP version with certain configuration options (such as arch, thread safe, etc.), you will be prompted whether to rebuild, clean or superclean. Choose `rebuild` if you have always used the same configuration. Otherwise, choose `clean` to remove previous builds (binaries). The last option will remove the entire `php--src` directory, which is often unnecessary. +#### Steps + +1. Launch a regular `cmd` prompt + +2. Change to the directory where the Python scripts `builddrivers.py` and `buildtools.py` are + +3. Interactive mode: + * Run `py builddrivers.py` to use the interactive mode. Use lower cases to answer the following questions: + * PHP Version (i.e. the version number like `7.1.7` or `7.2.0beta2`) + * 64-bit? + * Thread safe? + * Driver? + * Debug enabled? + * Download source from GitHub? + * For `yes/no` questions, you can simply hit `ENTER` key for `yes`. Other questions are self-explanatory. + +4. Use Command-line arguments + * Run `py builddrivers.py -h` to get a list of options and their descriptions + * For example, + * `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=sqlsrv --GITHUB=yes` + +5. If the script detects the presence of a PHP source directory, you will be prompted whether to rebuild, clean or superclean. Choose + * `rebuild` if you have always used the same configuration (32 bit, thread safe, etc.) + * `clean` to remove previous builds (binaries) + * `superclean` to remove the entire `php--src` directory, which is often unnecessary + +6. If you choose not to download from a GitHub repository, you will be asked to provide the full path to your local Source folder. + +7. If the compilation is successful, you will be given the option to rebuild or quit. + +#### Troubleshooting When something goes wrong during the build, the log file will be launched (you can find the log files in `C:\php-sdk`). Otherwise, the log file will not be shown, and they remain in `C:\php-sdk` until you remove them manually. -After the compilation is complete, you will be given the option to rebuild or quit. If you choose rebuild, the extensions will be re-compiled (say after you have changed any source or header file). This script will keep running until you choose to quit. - In addition to the log files in `C:\php-sdk`, you can examine the contents of `C:\php-sdk\phpsdk-build-task.bat`, which is overwritten every time you run the build scripts. -When running build unattended, you should specify the destination path `--DESTPATH=`. In such case, the `php-sdk` folder will be created in the same working directory of these build scripts, and the drivers will be copied to your designated path. The script `builddrivers.py` provides an example for this case. Again, it's your choice whether to remove the `php-sdk` folder afterwards. +#### Building the extensions unattended + +You can invoke `py builddrivers.py` with the desired options plus the destination path `--DESTPATH=`. + +In such case, the `php-sdk` folder will be created in the same directory of these build scripts. Note that the PHP drivers will also be copied to the designated path. + +The script `builddrivers.py` provides an example for this case. Again, it's your choice whether to remove the `php-sdk` folder afterwards. diff --git a/buildscripts/buildtools.py b/buildscripts/buildtools.py index 0875398f..d527febb 100644 --- a/buildscripts/buildtools.py +++ b/buildscripts/buildtools.py @@ -296,7 +296,7 @@ class BuildUtil(object): 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' + cmd_line = 'cscript configure.js --disable-all --enable-cli --enable-cgi --enable-embed' + cmd_line if self.thread == 'nts': cmd_line = cmd_line + ' --disable-zts' return cmd_line From 621f13327675b469a37e39ba6798d889dc64b3d1 Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Wed, 16 Aug 2017 14:12:26 -0700 Subject: [PATCH 21/26] Reverted some changes in README --- README.md | 28 ++-------------------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index b9d9a869..de5a6566 100644 --- a/README.md +++ b/README.md @@ -42,33 +42,9 @@ Please visit the [blog][blog] for more announcements. ## Build (Windows) -Note: if you prefer, you can use the pre-compiled binary found [HERE](https://github.com/Microsoft/msphpsql/releases) +If you prefer, you can use the pre-compiled binaries found [HERE](https://github.com/Microsoft/msphpsql/releases) -#### Prerequisites - -You must first be able to build PHP 7 without including these extensions. For help with doing this, see the [official PHP website][phpbuild] for building your own PHP on Windows. - -#### Compile the drivers - -1. Download the source code directory from this repository - -2. Make a copy of *shared* folder as a subfolder in *sqlsrv* and/or *pdo_sqlsrv* folder - -3. Copy the *sqlsrv* and/or *pdo_sqlsrv* folder into the PHP source ext subdirectory. - -4. Run `buildconf --force` to rebuild the configure.js script to include the driver. - -5. Run `configure.bat --with-odbcver=0x0380 and the desired driver options (as below) [plus other options such as --disable-zts for the Non Thread Safe build]` to generate the makefile. You can run `configure.bat --help` to see what other options are available. - * For SQLSRV use: `--enable-sqlsrv=shared` - * For PDO_SQLSRV use: `--enable-pdo --with-pdo-sqlsrv=shared` - -6. Run `nmake`. Optionally, you can run `nmake clean` first. - -7. To install the resulting build, run `nmake install` or just copy php_sqlsrv.dll and/or php_pdo_sqlsrv.dll to your PHP extension directory. - -The extensions have been compiled and tested with the PHP 7.0.* and 7.1.* using the Visual C++ 2015 compiler as well as PHP 7.2.0 beta using the Visual C++ 2017 V15.0. - -For an example of how to automate building the Microsoft Drivers for PHP for SQL Server, please browse the *buildscripts* directory. +The *buildscripts* directory contains step by step instructions on how to build the Microsoft Drivers for PHP for SQL Server. You can either build the extensions manually or use the sample scripts provided, which help automate the process. ## Install (Windows) From 791f95c6c01defb2d9408f4ca174dd1b96a9df09 Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Wed, 16 Aug 2017 14:41:30 -0700 Subject: [PATCH 22/26] reverted README --- README.md | 87 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 74 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 99ecc660..f877f5b3 100644 --- a/README.md +++ b/README.md @@ -31,20 +31,39 @@ Thank you for taking time to take our February survey. Let us know how we are do * [**Ubuntu + SQL Server + PHP 7**](https://www.microsoft.com/en-us/sql-server/developer-get-started/php/ubuntu) * [**RedHat + SQL Server + PHP 7**](https://www.microsoft.com/en-us/sql-server/developer-get-started/php/rhel) +* [**SUSE + SQL Server + PHP 7**](https://www.microsoft.com/en-us/sql-server/developer-get-started/php/sles) * [**Windows + SQL Server + PHP 7**](https://www.microsoft.com/en-us/sql-server/developer-get-started/php/windows) * [**Docker**](https://hub.docker.com/r/lbosqmsft/mssql-php-msphpsql/) ## Announcements -Please visit the [blog][blog] for more announcements. + Please visit the [blog][blog] for more announcements. ## Build (Windows) -If you prefer, you can use the pre-compiled binaries found [HERE](https://github.com/Microsoft/msphpsql/releases) +Note: if you prefer, you can use the pre-compiled binary found [HERE](https://github.com/Microsoft/msphpsql/releases) -The *buildscripts* directory contains step by step instructions on how to build the Microsoft Drivers for PHP for SQL Server. You can either build the extensions manually or use the sample scripts provided, which help automate the process. +#### Prerequisites + +You must first be able to build PHP 7 without including these extensions. For help with doing this, see the [official PHP website][phpbuild] for building your own PHP on Windows. + +#### Compile the drivers + +1. Copy the sqlsrv and/or pdo_sqlsrv source code directory from this repository into the ext subdirectory. + +2. Run `buildconf.bat` to rebuild the configure.js script to include the driver. + +3. Run `configure.bat --with-odbcver=0x0380 and the desired driver options (as below) [plus other options such as --disable-zts for the Non Thread Safe build]` to generate the makefile. You can run `configure.bat --help` to see what other options are available. + * For SQLSRV use: `--enable-sqlsrv=shared` + * For PDO_SQLSRV use: `--enable-pdo=shared --with-pdo-sqlsrv=shared` + +4. Run `nmake`. It is suggested that you run the entire build. If you wish to do so, run `nmake clean` first. + +5. To install the resulting build, run `nmake install` or just copy php_sqlsrv.dll and/or php_pdo_sqlsrv.dll to your PHP extension directory. + +This software has been compiled and tested under PHP 7.0.20 and 7.1.6 using the Visual C++ 2015 compiler. ## Install (Windows) @@ -62,7 +81,7 @@ The *buildscripts* directory contains step by step instructions on how to build 3. Restart the Web server. ## Install (UNIX) -The following instructions assume a clean environment and show how to install PHP 7.x, Microsoft ODBC driver, apache, and Microsoft PHP drivers on Ubuntu 15, 16, RedHat 7, Debian 8, and Mac OS. +The following instructions assume a clean environment and show how to install PHP 7.x, Microsoft ODBC driver, Apache, and Microsoft PHP drivers on Ubuntu 15, 16, RedHat 7, Debian 8, SUSE 12, and macOS. ### Step 1: Install PHP7+ @@ -102,7 +121,13 @@ The following instructions assume a clean environment and show how to install PH apt-get update apt-get install -y php7.0 php-pear php7.0-dev php7.0-xml -**Mac OS** +**SUSE 12** + + sudo su + zypper refresh + zypper install -y php7 php7-pear php7-devel + +**macOS** /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" brew tap @@ -145,7 +170,14 @@ Note that there are no PHP 7.1 packages available for Ubuntu 15.10. apt-get update apt-get install -y php7.1 php-pear php7.1-dev php7.1-xml -**Mac OS** +**SUSE 12** + + sudo su + zypper -n ar -f http://download.opensuse.org/repositories/devel:/languages:/php/openSUSE_Leap_42.3/ devel:languages:php + zypper --gpg-auto-import-keys refresh + zypper -n install php7 php7-pear php7-devel + +**macOS** /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" brew tap @@ -212,7 +244,20 @@ Note that there are no PHP 7.1 packages available for Ubuntu 15.10. sudo ACCEPT_EULA=Y apt-get install msodbcsql sudo apt-get install unixodbc-dev -**Mac OS** +**SUSE 12** + + sudo su + zypper ar https://packages.microsoft.com/config/sles/12/prod.repo + sudo zypper --gpg-auto-import-keys refresh + exit + sudo ACCEPT_EULA=Y zypper install msodbcsql + sudo ACCEPT_EULA=Y zypper install mssql-tools + sudo zypper install unixODBC-devel + echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bash_profile + echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bashrc + source ~/.bashrc + +**macOS** brew tap microsoft/msodbcsql https://github.com/Microsoft/homebrew-mssql-release brew update @@ -226,7 +271,7 @@ Note that there are no PHP 7.1 packages available for Ubuntu 15.10. *Note: You can run `sudo pecl search sqlsrv` to search for the latest releases and `sudo pecl install sqlsrv-[version]` to install a specific version. PECL installs the stable version when version is not specified. Drivers are Mac-compatible starting from `4.1.7preview` release. -On Ubuntu and Debian systems only, run: +On Ubuntu, Debian, and SUSE systems only, run: sudo pear config-set php_ini `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"` system @@ -256,7 +301,15 @@ On all systems, run: echo "extension=sqlsrv.so" > /etc/php.d/sqlsrv.ini echo "extension=pdo_sqlsrv.so" > /etc/php.d/pdo_sqlsrv.ini -**Mac OS** +**SUSE** + + sudo su + zypper install apache2 apache2-mod_php7 + a2enmod php7 + echo "extension=sqlsrv.so" >> /etc/php7/apache2/php.ini + echo "extension=pdo_sqlsrv.so" >> /etc/php7/apache2/php.ini + +**macOS** (echo ""; echo "SetHandler application/x-httpd-php"; echo "";) >> /usr/local/etc/apache2/2.4/httpd.conf @@ -279,14 +332,22 @@ On all systems, run: echo "extension=sqlsrv.so" > /etc/php.d/sqlsrv.ini echo "extension=pdo_sqlsrv.so" > /etc/php.d/pdo_sqlsrv.ini -**Mac OS** +**SUSE** + + sudo su + zypper install apache2 apache2-mod_php7 + a2enmod php7 + echo "extension=sqlsrv.so" >> /etc/php7/apache2/php.ini + echo "extension=pdo_sqlsrv.so" >> /etc/php7/apache2/php.ini + +**macOS** (echo ""; echo "SetHandler application/x-httpd-php"; echo "";) >> /usr/local/etc/apache2/2.4/httpd.conf ### Step 5: Restart Apache to load the new php.ini file -**Ubuntu and Debian** +**Ubuntu, Debian, and SUSE** sudo systemctl restart apache2 @@ -298,13 +359,13 @@ Note: On RedHat, SELinux is installed by default and runs in Enforcing mode. To sudo setsebool -P httpd_can_network_connect_db 1 -**Mac OS** +**macOS** sudo apachectl restart ### Step 6: Create your sample app -Navigate to `/var/www/html` (`/usr/local/var/www/htdocs` on Mac) and create a new file called testsql.php. Copy and paste the following code into testsql.php and change the servername, username, password and databasename. +Navigate to your system's document root -- `/var/www/html` on Ubuntu, Debian, and Redhat, `/srv/www/htdocs` on SUSE, or `/usr/local/var/www/htdocs` on Mac. Create a new file called testsql.php. Copy and paste the following code into testsql.php and change the servername, username, password and databasename. Date: Wed, 16 Aug 2017 16:09:11 -0700 Subject: [PATCH 23/26] modified README for build scripts --- buildscripts/README.md | 10 ++++------ buildscripts/builddrivers.py | 14 +++++++------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/buildscripts/README.md b/buildscripts/README.md index da71851d..584c3c3c 100644 --- a/buildscripts/README.md +++ b/buildscripts/README.md @@ -69,7 +69,7 @@ It's recommended that the PHP SDK is unzipped into the shortest possible path, p * `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=sqlsrv --GITHUB=yes` -5. If the script detects the presence of a PHP source directory, you will be prompted whether to rebuild, clean or superclean. Choose +5. Based on the given configuration, if the script detects the presence of the PHP source directory, you can choose whether to rebuild, clean or superclean: * `rebuild` if you have always used the same configuration (32 bit, thread safe, etc.) * `clean` to remove previous builds (binaries) * `superclean` to remove the entire `php--src` directory, which is often unnecessary @@ -84,13 +84,11 @@ When something goes wrong during the build, the log file will be launched (you c In addition to the log files in `C:\php-sdk`, you can examine the contents of `C:\php-sdk\phpsdk-build-task.bat`, which is overwritten every time you run the build scripts. -#### Building the extensions unattended +#### Variation -You can invoke `py builddrivers.py` with the desired options plus the destination path `--DESTPATH=`. +If you want the PHP drivers to be copied to somewhere else, you can invoke `py builddrivers.py` by providing the option `--DESTPATH=`. -In such case, the `php-sdk` folder will be created in the same directory of these build scripts. Note that the PHP drivers will also be copied to the designated path. - -The script `builddrivers.py` provides an example for this case. Again, it's your choice whether to remove the `php-sdk` folder afterwards. +In this case, you will find a copy of the drivers (unless the build fails) and the `php-sdk` folder created in the same directory of these Python scripts. It's your choice whether to remove the `php-sdk` folder and/or the drivers' binaries afterwards. diff --git a/buildscripts/builddrivers.py b/buildscripts/builddrivers.py index 4005cbbf..d02c478b 100644 --- a/buildscripts/builddrivers.py +++ b/buildscripts/builddrivers.py @@ -197,7 +197,7 @@ class BuildDriver(object): # Only ask when building locally if self.local: - choice = input("Rebuild the same configuration(yes) or quit (no) [yes/no]: ") + choice = input("Rebuild using the same configuration(yes) or quit (no) [yes/no]: ") if choice.lower() == 'yes' or choice.lower() == 'y' or choice.lower() == '': print('Rebuilding drivers...') @@ -230,12 +230,12 @@ if __name__ == '__main__': 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('-p', '--DESTPATH', default=None, help="the remote destination for the drivers (do not use this for local builds)") + parser.add_argument('-d', '--DRIVER', default='all', choices=['all', 'sqlsrv', 'pdo_sqlsrv'], help="driver to build (default: all)") + parser.add_argument('-m', '--DEBUG', default='no', choices=['yes', 'no'], help="enable debug mode (default: no)") + parser.add_argument('-r', '--REPO', default='Microsoft', help="GitHub repository (default: Microsoft)") + parser.add_argument('-b', '--BRANCH', default='dev', help="GitHub repository branch (default: dev)") + parser.add_argument('-g', '--GITHUB', default='yes', choices=['yes', 'no'], help="get source from GitHub (default: yes)") + parser.add_argument('-p', '--DESTPATH', default=None, help="the remote destination for the drivers (default: None)") args = parser.parse_args() From 1a7c1d89ce6eaab13b49982afef656b319047480 Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Thu, 17 Aug 2017 09:16:57 -0700 Subject: [PATCH 24/26] modified some wordings in the scripts and README --- buildscripts/README.md | 17 +++++++++-------- buildscripts/builddrivers.py | 11 +++++------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/buildscripts/README.md b/buildscripts/README.md index 584c3c3c..aa407ec0 100644 --- a/buildscripts/README.md +++ b/buildscripts/README.md @@ -43,9 +43,9 @@ The sample build scripts, `builddrivers.py` and `buildtools.py`, are expected to #### Overview -When asked to provide the PHP version, you should enter values like `7.1.7`. If it's alpha, beta, or RC, make sure the name you provide matches the PHP tag name without the prefix `php-`. For example, for PHP 7.2 beta 2, the tag name is `php-7.2.0beta2`, so you should enter `7.2.0beta2`. Visit [PHP SRC]( https://github.com/php/php-src) to find the appropriate tag names. +When asked to provide the PHP version, you should enter values like `7.1.7`. If it's alpha, beta, or RC version, make sure the name you provide matches the PHP tag name without the prefix `php-`. For example, for PHP 7.2 beta 2, the tag name is `php-7.2.0beta2`, so you should enter `7.2.0beta2`. Visit [PHP SRC]( https://github.com/php/php-src) to find the appropriate tag names. -It's recommended that the PHP SDK is unzipped into the shortest possible path, preferrably somewhere near the root drive. Therefore, this script will create a `php-sdk` folder in the C:\ drive. This `php-sdk` folder will remain unless you remove it yourself. For ongoing development, it's suggested you keep it around. The build scripts will handle updating the PHP SDK if new version is available. +PHP recommendeds to unzip the PHP SDK into the shortest possible path, preferrably somewhere near the root drive. Therefore, this script will create a `php-sdk` folder in the C:\ drive. This `php-sdk` directory tree will remain unless you remove it yourself. For ongoing development, we suggest you keep it around. The build scripts will handle updating the PHP SDK if a new version is available. #### Steps @@ -55,7 +55,7 @@ It's recommended that the PHP SDK is unzipped into the shortest possible path, p 3. Interactive mode: * Run `py builddrivers.py` to use the interactive mode. Use lower cases to answer the following questions: - * PHP Version (i.e. the version number like `7.1.7` or `7.2.0beta2`) + * PHP Version (e.g. `7.1.7` or `7.2.0beta2`) * 64-bit? * Thread safe? * Driver? @@ -70,7 +70,7 @@ It's recommended that the PHP SDK is unzipped into the shortest possible path, p * `py builddrivers.py --PHPVER=7.0.22 --ARCH=x64 --THREAD=nts --DRIVER=sqlsrv --GITHUB=yes` 5. Based on the given configuration, if the script detects the presence of the PHP source directory, you can choose whether to rebuild, clean or superclean: - * `rebuild` if you have always used the same configuration (32 bit, thread safe, etc.) + * `rebuild` to build again using the same configuration (32 bit, thread safe, etc.) * `clean` to remove previous builds (binaries) * `superclean` to remove the entire `php--src` directory, which is often unnecessary @@ -80,15 +80,16 @@ It's recommended that the PHP SDK is unzipped into the shortest possible path, p #### Troubleshooting -When something goes wrong during the build, the log file will be launched (you can find the log files in `C:\php-sdk`). Otherwise, the log file will not be shown, and they remain in `C:\php-sdk` until you remove them manually. +If something went wrong or the build failed, the log file will be launched (you can find the log files in `C:\php-sdk`). Otherwise, the log file will not be shown, and they remain in `C:\php-sdk` until you remove them manually. In addition to the log files in `C:\php-sdk`, you can examine the contents of `C:\php-sdk\phpsdk-build-task.bat`, which is overwritten every time you run the build scripts. -#### Variation +#### Setting alternative destination for drivers -If you want the PHP drivers to be copied to somewhere else, you can invoke `py builddrivers.py` by providing the option `--DESTPATH=`. +If your main goal is to build the drivers, and/or there is no need to keep the `php-sdk` directory around, you can invoke `py builddrivers.py` with the necessary command-line arguments plus `--DESTPATH=`, which is **None** by default. Note that this option is not available in the interactive mode. + +By setting an alternative destination automatically turns off the looping mechanism. When the build is finished, you will find a copy of the drivers (unless the build failed) and the `php-sdk` folder in the same directory of these Python scripts. This option is particularly useful in a test environment (or a virtual machine) in which these build scripts are copied to a temporary folder. After the drivers have been successfully compiled and copied to the designated location, the temporary folder can be safely removed afterwards. -In this case, you will find a copy of the drivers (unless the build fails) and the `php-sdk` folder created in the same directory of these Python scripts. It's your choice whether to remove the `php-sdk` folder and/or the drivers' binaries afterwards. diff --git a/buildscripts/builddrivers.py b/buildscripts/builddrivers.py index d02c478b..2cecc055 100644 --- a/buildscripts/builddrivers.py +++ b/buildscripts/builddrivers.py @@ -74,8 +74,7 @@ class BuildDriver(object): phpsrc = self.util.phpsrc_root(root_dir) if os.path.exists( phpsrc ): print(phpsrc + " exists.") - print("Choose rebuild(r) if using the same configuration. Choose clean(c) otherwise. If unsure, choose superclean(s).") - build_choice = validate_input("Want to rebuild (r), clean (c) or superclean (s)? ", "r/c/s") + build_choice = validate_input("(r)ebuild for the same configuration, (c)lean otherwise, (s)uperclean if unsure ", "r/c/s") self.make_clean = False if build_choice == 'r': print('Will rebuild the binaries') @@ -189,7 +188,7 @@ class BuildDriver(object): self.build_extensions(dest, logfile) print('Build Completed') except: - print('Something went wrong. Build incomplete.') + print('Something went wrong, launching log file', logfile) if self.local: # display log file only when building locally os.startfile(os.path.join(root_dir, 'php-sdk', logfile)) os.chdir(work_dir) @@ -198,8 +197,8 @@ class BuildDriver(object): # Only ask when building locally if self.local: choice = input("Rebuild using the same configuration(yes) or quit (no) [yes/no]: ") - - if choice.lower() == 'yes' or choice.lower() == 'y' or choice.lower() == '': + choice = choice.lower() + if choice == 'yes' or choice == 'y' or choice == '': print('Rebuilding drivers...') self.make_clean = False self.rebuild = True @@ -235,7 +234,7 @@ if __name__ == '__main__': parser.add_argument('-r', '--REPO', default='Microsoft', help="GitHub repository (default: Microsoft)") parser.add_argument('-b', '--BRANCH', default='dev', help="GitHub repository branch (default: dev)") parser.add_argument('-g', '--GITHUB', default='yes', choices=['yes', 'no'], help="get source from GitHub (default: yes)") - parser.add_argument('-p', '--DESTPATH', default=None, help="the remote destination for the drivers (default: None)") + parser.add_argument('-p', '--DESTPATH', default=None, help="an alternative destination for the drivers (default: None)") args = parser.parse_args() From 0439df396ca005b114095e3529d61fdb9e380cfa Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Thu, 17 Aug 2017 13:24:12 -0700 Subject: [PATCH 25/26] corrected typo and modified the wordings --- buildscripts/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/buildscripts/README.md b/buildscripts/README.md index aa407ec0..3ade48c7 100644 --- a/buildscripts/README.md +++ b/buildscripts/README.md @@ -45,7 +45,7 @@ The sample build scripts, `builddrivers.py` and `buildtools.py`, are expected to When asked to provide the PHP version, you should enter values like `7.1.7`. If it's alpha, beta, or RC version, make sure the name you provide matches the PHP tag name without the prefix `php-`. For example, for PHP 7.2 beta 2, the tag name is `php-7.2.0beta2`, so you should enter `7.2.0beta2`. Visit [PHP SRC]( https://github.com/php/php-src) to find the appropriate tag names. -PHP recommendeds to unzip the PHP SDK into the shortest possible path, preferrably somewhere near the root drive. Therefore, this script will create a `php-sdk` folder in the C:\ drive. This `php-sdk` directory tree will remain unless you remove it yourself. For ongoing development, we suggest you keep it around. The build scripts will handle updating the PHP SDK if a new version is available. +PHP recommends to unzip the PHP SDK into the shortest possible path, preferrably somewhere near the root drive. Therefore, this script will create a `php-sdk` folder in the C:\ drive. This `php-sdk` directory tree will remain unless you remove it yourself. For ongoing development, we suggest you keep it around. The build scripts will handle updating the PHP SDK if a new version is available. #### Steps @@ -88,7 +88,7 @@ In addition to the log files in `C:\php-sdk`, you can examine the contents of `C If your main goal is to build the drivers, and/or there is no need to keep the `php-sdk` directory around, you can invoke `py builddrivers.py` with the necessary command-line arguments plus `--DESTPATH=`, which is **None** by default. Note that this option is not available in the interactive mode. -By setting an alternative destination automatically turns off the looping mechanism. When the build is finished, you will find a copy of the drivers (unless the build failed) and the `php-sdk` folder in the same directory of these Python scripts. This option is particularly useful in a test environment (or a virtual machine) in which these build scripts are copied to a temporary folder. After the drivers have been successfully compiled and copied to the designated location, the temporary folder can be safely removed afterwards. +Setting an alternative destination automatically turns off the looping mechanism. When the build is finished, you will find a copy of the drivers (unless the build failed) and the `php-sdk` folder in the same directory of these Python scripts. This option is particularly useful in a test environment (or a virtual machine) in which these build scripts are copied to a temporary folder. After the drivers have been successfully compiled and copied to the designated location, the temporary folder can be safely removed. From 9667e2a303d84625984390ff1cbff48a23e840e1 Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Fri, 18 Aug 2017 10:25:19 -0700 Subject: [PATCH 26/26] modified some input arguments to allow the user to set TESTING mode --- buildscripts/README.md | 16 ++-- buildscripts/builddrivers.py | 140 ++++++++++++++++++----------------- buildscripts/buildtools.py | 5 +- 3 files changed, 83 insertions(+), 78 deletions(-) diff --git a/buildscripts/README.md b/buildscripts/README.md index 3ade48c7..3a533c27 100644 --- a/buildscripts/README.md +++ b/buildscripts/README.md @@ -45,7 +45,7 @@ The sample build scripts, `builddrivers.py` and `buildtools.py`, are expected to When asked to provide the PHP version, you should enter values like `7.1.7`. If it's alpha, beta, or RC version, make sure the name you provide matches the PHP tag name without the prefix `php-`. For example, for PHP 7.2 beta 2, the tag name is `php-7.2.0beta2`, so you should enter `7.2.0beta2`. Visit [PHP SRC]( https://github.com/php/php-src) to find the appropriate tag names. -PHP recommends to unzip the PHP SDK into the shortest possible path, preferrably somewhere near the root drive. Therefore, this script will create a `php-sdk` folder in the C:\ drive. This `php-sdk` directory tree will remain unless you remove it yourself. For ongoing development, we suggest you keep it around. The build scripts will handle updating the PHP SDK if a new version is available. +PHP recommends to unzip the PHP SDK into the shortest possible path, preferrably somewhere near the root drive. Therefore, this script will by default create a `php-sdk` folder in the C:\ drive, and this `php-sdk` directory tree will remain unless you remove it yourself. For ongoing development, we suggest you keep it around. The build scripts will handle updating the PHP SDK if a new version is available. #### Steps @@ -66,8 +66,8 @@ PHP recommends to unzip the PHP SDK into the shortest possible path, preferrably 4. Use Command-line arguments * Run `py builddrivers.py -h` to get a list of options and their descriptions * For example, - * `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=sqlsrv --GITHUB=yes` + * `py builddrivers.py --PHPVER=7.0.22 --ARCH=x64 --THREAD=nts --DRIVER=sqlsrv --SOURCE` + * `py builddrivers.py --PHPVER=7.1.8 --ARCH=x86 --THREAD=ts --DEBUG` 5. Based on the given configuration, if the script detects the presence of the PHP source directory, you can choose whether to rebuild, clean or superclean: * `rebuild` to build again using the same configuration (32 bit, thread safe, etc.) @@ -84,13 +84,13 @@ If something went wrong or the build failed, the log file will be launched (you In addition to the log files in `C:\php-sdk`, you can examine the contents of `C:\php-sdk\phpsdk-build-task.bat`, which is overwritten every time you run the build scripts. -#### Setting alternative destination for drivers - -If your main goal is to build the drivers, and/or there is no need to keep the `php-sdk` directory around, you can invoke `py builddrivers.py` with the necessary command-line arguments plus `--DESTPATH=`, which is **None** by default. Note that this option is not available in the interactive mode. - -Setting an alternative destination automatically turns off the looping mechanism. When the build is finished, you will find a copy of the drivers (unless the build failed) and the `php-sdk` folder in the same directory of these Python scripts. This option is particularly useful in a test environment (or a virtual machine) in which these build scripts are copied to a temporary folder. After the drivers have been successfully compiled and copied to the designated location, the temporary folder can be safely removed. +#### Testing mode and/or setting alternative destination +If your main goal is to build the drivers for testing, and/or there is no need to keep the `php-sdk` directory around, you can invoke `py builddrivers.py` with the necessary command-line arguments plus `--TESTING`, which turns on the *testing* mode (it is False by default). +Setting the testing mode automatically turns off the looping mechanism. When the build is finished, you will find a copy of the drivers (unless the build failed) and the `php-sdk` folder in the same directory of these Python scripts. + +In addition, you can set an alternative destination using `--DESTPATH=`, which is **None** by default. Note that these two options are *not* available in the interactive mode. However, they are particularly useful for testing purposes (such as testing in a virtual machine) in which these build scripts are copied to a temporary folder. After the drivers have been successfully compiled and copied to the designated location, the temporary folder can be safely removed. diff --git a/buildscripts/builddrivers.py b/buildscripts/builddrivers.py index 2cecc055..f4e14e34 100644 --- a/buildscripts/builddrivers.py +++ b/buildscripts/builddrivers.py @@ -12,12 +12,11 @@ # 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 +# py builddrivers.py --PHPVER=7.0.22 --ARCH=x64 --THREAD=nts --DRIVER=all --DEBUG # -# Output: Build the drivers using PHP SDK. When running locally, if build is unsuccessful, +# Output: Build the drivers using PHP SDK. When running for local development, 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). +# and copied to the designated location (if defined). # ############################################################################################# @@ -35,20 +34,20 @@ class BuildDriver(object): repo # GitHub repository branch # GitHub repository branch download_source # download source from GitHub or not - remote_path # remote destination to where the drivers will be placed (None for local builds) - local # a boolean flag - whether the build is local + dest_path # alternative destination for the drivers (None for development builds) rebuild # a boolean flag - whether the user is rebuilding make_clean # a boolean flag - whether make clean is necessary source_path # path to a local source folder + testing # whether the user has turned on testing mode """ - def __init__(self, phpver, driver, arch, thread, debug, repo, branch, download, path): + def __init__(self, phpver, driver, arch, thread, debug, repo, branch, download, path, testing): self.util = BuildUtil(phpver, driver, arch, thread, debug) self.repo = repo self.branch = branch self.download_source = download - self.remote_path = path - self.local = path is None # the default path is None, which means running locally + self.dest_path = path + self.testing = testing self.rebuild = False self.make_clean = False self.source_path = None # None initially but will be set later if not downloading from GitHub @@ -63,7 +62,7 @@ class BuildDriver(object): print() def clean_or_remove(self, root_dir, work_dir): - """Only check this when building locally and not rebuilding. If the php source directory + """Only check this for local development and not rebuilding. If the php source directory already exists, this will prompt user whether to rebuild, clean, or superclean, the last option will remove the entire php source directory. @@ -92,25 +91,22 @@ class BuildDriver(object): os.chdir(work_dir) # change back to the working directory - def build_extensions(self, dest, logfile): + def build_extensions(self, root_dir, 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. + If dest_path is defined, the binaries will be copied to the designated destinations. - :param dest: either None (for remote builds) or the C:\ drive (for local builds) + :param root_dir: the root directory :param logfile: the name of the logfile :outcome: the drivers and symbols will renamed and placed in the appropriate location(s) """ 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 + # This will download from the specified branch on GitHub repo and copy the source self.util.download_msphpsql_source(repo, branch) else: - # This case only happens when building locally (interactive mode) - # because download_source must be True for remote builds + # This case only happens when building for development while True: if self.source_path is None: source = input('Enter the full path to the Source folder: ') @@ -136,37 +132,35 @@ class BuildDriver(object): print('Start building PHP with the extension...') - self.util.build_drivers(self.make_clean, dest, logfile) + # If not testing, dest should be the root drive. Otherwise, dest should be None. + dest = None if self.testing else root_dir - if dest is None: - # This indicates the script is NOT running locally, and that - # the drivers should be in the working directory + # ext_dir is the directory where we can find the built extension(s) + ext_dir = self.util.build_drivers(self.make_clean, dest, logfile) - # Make sure drivers path is defined - if self.remote_path is None: - print('Errors: Drivers destination should be defined! Doing nothing.') - else: - dest_drivers = os.path.join(self.remote_path, self.util.major_version(), self.util.arch) - dest_symbols = os.path.join(dest_drivers, 'Symbols', self.util.thread) + # Copy the binaries if a destination path is defined + if self.dest_path is not None: + dest_drivers = os.path.join(self.dest_path, self.util.major_version(), self.util.arch) + dest_symbols = os.path.join(dest_drivers, 'Symbols', self.util.thread) + + # All intermediate directories will be created in order to create the leaf directory + if os.path.exists(dest_symbols) == False: + os.makedirs(dest_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') - + # Now copy all the binaries + if self.util.driver == 'all': + self.util.copy_binary(ext_dir, dest_drivers, 'sqlsrv', '.dll') + self.util.copy_binary(ext_dir, dest_symbols, 'sqlsrv', '.pdb') + self.util.copy_binary(ext_dir, dest_drivers, 'pdo_sqlsrv', '.dll') + self.util.copy_binary(ext_dir, dest_symbols, 'pdo_sqlsrv', '.pdb') + else: + self.util.copy_binary(ext_dir, dest_drivers, self.util.driver, '.dll') + self.util.copy_binary(ext_dir, dest_symbols, self.util.driver, '.pdb') + def build(self): """This is the main entry point of building drivers for PHP. - For local builds, this will loop till the user decides to quit. + For development, this will loop till the user decides to quit. """ self.show_config() @@ -175,27 +169,23 @@ class BuildDriver(object): quit = False while not quit: - if not self.rebuild and self.local: + if not self.rebuild and not self.testing: 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) + self.build_extensions(root_dir, logfile) print('Build Completed') except: print('Something went wrong, launching log file', logfile) - if self.local: # display log file only when building locally + # display log file only when not testing + if not self.testing: os.startfile(os.path.join(root_dir, 'php-sdk', logfile)) os.chdir(work_dir) break - # Only ask when building locally - if self.local: + if not self.testing: choice = input("Rebuild using the same configuration(yes) or quit (no) [yes/no]: ") choice = choice.lower() if choice == 'yes' or choice == 'y' or choice == '': @@ -226,15 +216,16 @@ def validate_input(question, values): ################################### 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', default='all', choices=['all', 'sqlsrv', 'pdo_sqlsrv'], help="driver to build (default: all)") - parser.add_argument('-m', '--DEBUG', default='no', choices=['yes', 'no'], help="enable debug mode (default: no)") - parser.add_argument('-r', '--REPO', default='Microsoft', help="GitHub repository (default: Microsoft)") - parser.add_argument('-b', '--BRANCH', default='dev', help="GitHub repository branch (default: dev)") - parser.add_argument('-g', '--GITHUB', default='yes', choices=['yes', 'no'], help="get source from GitHub (default: yes)") - parser.add_argument('-p', '--DESTPATH', default=None, help="an alternative destination for the drivers (default: None)") + parser.add_argument('--PHPVER', help="PHP version, e.g. 7.1.*, 7.2.* etc.") + parser.add_argument('--ARCH', choices=['x64', 'x86']) + parser.add_argument('--THREAD', choices=['nts', 'ts']) + parser.add_argument('--DRIVER', default='all', choices=['all', 'sqlsrv', 'pdo_sqlsrv'], help="driver to build (default: all)") + parser.add_argument('--DEBUG', action='store_true', help="enable debug mode (default: False)") + parser.add_argument('--REPO', default='Microsoft', help="GitHub repository (default: Microsoft)") + parser.add_argument('--BRANCH', default='dev', help="GitHub repository branch (default: dev)") + parser.add_argument('--SOURCE', action='store_true', help="get source from a local path (default: False)") + parser.add_argument('--TESTING', action='store_true', help="turns on testing mode (default: False)") + parser.add_argument('--DESTPATH', default=None, help="an alternative destination for the drivers (default: None)") args = parser.parse_args() @@ -242,21 +233,31 @@ if __name__ == '__main__': arch = args.ARCH thread = args.THREAD driver = args.DRIVER - debug = args.DEBUG == 'yes' + debug = args.DEBUG repo = args.REPO branch = args.BRANCH - download = args.GITHUB.lower() == 'yes' + download = args.SOURCE is False path = args.DESTPATH + testing = args.TESTING 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.*): ") + # starts interactive mode, testing mode is False + # will not prompt for drivers' destination path, which is None by default + while True: + # perform some minimal checks + phpver = input("PHP Version (e.g. 7.1.* or 7.2.*): ") + if phpver == '': + print('Empty PHP version entered! Please try again.') + elif phpver[0] < '7': + print('Only PHP 7.0 or above is supported. Please try again.') + else: + break + arch_version = input("64-bit? [y/n]: ") thread = validate_input("Thread safe? ", "nts/ts") driver = validate_input("Driver to build? ", "all/sqlsrv/pdo_sqlsrv") debug_mode = input("Debug enabled? [y/n]: ") - + answer = input("Download source from a GitHub repo? [y/n]: ") download = False if answer == 'yes' or answer == 'y' or answer == '': @@ -282,5 +283,6 @@ if __name__ == '__main__': repo, branch, download, - path) + path, + testing) builder.build() diff --git a/buildscripts/buildtools.py b/buildscripts/buildtools.py index d527febb..2176b94e 100644 --- a/buildscripts/buildtools.py +++ b/buildscripts/buildtools.py @@ -414,7 +414,8 @@ class BuildUtil(object): self.rename_binaries(sdk_dir) # Final step, copy the binaries to the right place - self.copy_binaries(sdk_dir, copy_to_ext) + ext_dir = self.copy_binaries(sdk_dir, copy_to_ext) + return ext_dir def rename_binary(self, path, driver): """Rename the *driver* binary (sqlsrv or pdo_sqlsrv) (only the dlls).""" @@ -470,4 +471,6 @@ class BuildUtil(object): else: self.copy_binary(build_dir, dest_dir, self.driver, '.dll') self.copy_binary(build_dir, dest_dir, self.driver, '.pdb') + + return dest_dir