* Updated the build system for Mac (#645)

* Catalina env. uses Python 3.8 in MacPorts, Homebrew, and Anaconda3.
This commit is contained in:
Kazunari Sekigawa 2020-10-10 07:09:34 +09:00 committed by Matthias Koefferlein
parent 151fd81bd3
commit 2f0fa28e58
9 changed files with 521 additions and 153 deletions

View File

@ -1,4 +1,4 @@
Relevant KLayout version: 0.26.5 Relevant KLayout version: 0.26.7
# 1. Introduction # 1. Introduction
This directory **`macbuild`** contains different files required for building KLayout (http://www.klayout.de/) version 0.26.1 or later for different 64-bit Mac OSXs including: This directory **`macbuild`** contains different files required for building KLayout (http://www.klayout.de/) version 0.26.1 or later for different 64-bit Mac OSXs including:
@ -72,16 +72,16 @@ $ [python] ./build4mac.py
: MP26: use Ruby 2.6 from MacPorts | : MP26: use Ruby 2.6 from MacPorts |
: HB27: use Ruby 2.7 from Homebrew | : HB27: use Ruby 2.7 from Homebrew |
: Ana3: use Ruby 2.5 from Anaconda3 | : Ana3: use Ruby 2.5 from Anaconda3 |
[-p|--python <type>] : case-insensitive type=['nil', 'Sys', 'MP37', 'HB37', 'Ana3'] | sys [-p|--python <type>] : case-insensitive type=['nil', 'Sys', 'MP38', 'HB38', 'Ana3'] | sys
: nil: don't bind Python | : nil: don't bind Python |
: Sys: use OS-bundled Python 2.7 [ElCapitan -- Catalina] | : Sys: use OS-bundled Python 2.7 [ElCapitan -- Catalina] |
: MP37: use Python 3.7 from MacPorts | : MP38: use Python 3.8 from MacPorts |
: HB37: use Python 3.7 from Homebrew | : HB38: use Python 3.8 from Homebrew |
: Ana3: use Python 3.7 from Anaconda3 | : Ana3: use Python 3.8 from Anaconda3 |
[-n|--noqtbinding] : don't create Qt bindings for ruby scripts | disabled [-n|--noqtbinding] : don't create Qt bindings for ruby scripts | disabled
[-m|--make <option>] : option passed to 'make' | '-j4' [-m|--make <option>] : option passed to 'make' | '-j4'
[-d|--debug] : enable debug mode build | disabled [-d|--debug] : enable debug mode build | disabled
[-c|--checkcom] : check command-line and exit without building | disabled [-c|--checkcom] : check command line and exit without building | disabled
[-y|--deploy] : deploy executables and dylibs including Qt's Frameworks | disabled [-y|--deploy] : deploy executables and dylibs including Qt's Frameworks | disabled
[-Y|--DEPLOY] : deploy executables and dylibs for those who built KLayout | disabled [-Y|--DEPLOY] : deploy executables and dylibs for those who built KLayout | disabled
: from the source code and use the tools in the same machine | : from the source code and use the tools in the same machine |
@ -126,64 +126,64 @@ Then the directory name will be **`LW-qt5MP.pkg.macos-Catalina-release-RsysPsys`
#### If you build KLayout from the source code AND use it on the same machine, "-Y" option is highly recommended. #### #### If you build KLayout from the source code AND use it on the same machine, "-Y" option is highly recommended. ####
### 6B. Fully MacPorts-flavored build with MacPorts Ruby 2.6 and MacPorts Python 3.7 ### 6B. Fully MacPorts-flavored build with MacPorts Ruby 2.6 and MacPorts Python 3.8
``` ```
$ cd /where/'build.sh'/exists $ cd /where/'build.sh'/exists
$ ./build4mac.py -q qt5macports -r mp26 -p mp37 $ ./build4mac.py -q qt5macports -r mp26 -p mp38
``` ```
2. Confirm successful build (it will take about one hour depending on your machine spec). 2. Confirm successful build (it will take about one hour depending on your machine spec).
3. Run **`build4mac.py`** again with the same options used in 1. PLUS "-Y" to deploy executables and libraries under **`klayout.app`** bundle.<br> 3. Run **`build4mac.py`** again with the same options used in 1. PLUS "-Y" to deploy executables and libraries under **`klayout.app`** bundle.<br>
The buddy command-line tools (strm*) will also be deployed in this step. The buddy command-line tools (strm*) will also be deployed in this step.
``` ```
$ ./build4mac.py -q qt5macports -r mp26 -p mp37 -Y $ ./build4mac.py -q qt5macports -r mp26 -p mp38 -Y
``` ```
The application bundle **`klayout.app`** is located under:<br> The application bundle **`klayout.app`** is located under:<br>
**`LW-qt5MP.pkg.macos-Catalina-release-Rmp26Pmp37`** directory, where **`LW-qt5MP.pkg.macos-Catalina-release-Rmp26Pmp38`** directory, where
* "LW-" means that this is a lightweight package. * "LW-" means that this is a lightweight package.
* "qt5MP" means that Qt5 from MacPorts is used. * "qt5MP" means that Qt5 from MacPorts is used.
* "Rmp26Pmp37" means that Ruby is 2.6 from MacPorts; Python is 3.7 from MacPorts. * "Rmp26Pmp38" means that Ruby is 2.6 from MacPorts; Python is 3.8 from MacPorts.
4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation. 4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation.
### 6C. Fully Homebrew-flavored build with Homebrew Ruby 2.7 and Homebrew Python 3.7 ### 6C. Fully Homebrew-flavored build with Homebrew Ruby 2.7 and Homebrew Python 3.8
``` ```
$ cd /where/'build.sh'/exists $ cd /where/'build.sh'/exists
$ ./build4mac.py -q qt5brew -r hb27 -p hb37 $ ./build4mac.py -q qt5brew -r hb27 -p hb38
``` ```
2. Confirm successful build (it will take about one hour depending on your machine spec). 2. Confirm successful build (it will take about one hour depending on your machine spec).
3. Run **`build4mac.py`** again with the same options used in 1. PLUS "-Y" to deploy executables and libraries under **`klayout.app`** bundle.<br> 3. Run **`build4mac.py`** again with the same options used in 1. PLUS "-Y" to deploy executables and libraries under **`klayout.app`** bundle.<br>
The buddy command-line tools (strm*) will also be deployed in this step. The buddy command-line tools (strm*) will also be deployed in this step.
``` ```
$ ./build4mac.py -q qt5brew -r hb27 -p hb37 -Y $ ./build4mac.py -q qt5brew -r hb27 -p hb38 -Y
``` ```
The application bundle **`klayout.app`** is located under:<br> The application bundle **`klayout.app`** is located under:<br>
**`LW-qt5Brew.pkg.macos-Catalina-release-Rhb27Phb37`** directory, where **`LW-qt5Brew.pkg.macos-Catalina-release-Rhb27Phb38`** directory, where
* "LW-" means that this is a lightweight package. * "LW-" means that this is a lightweight package.
* "qt5Brew" means that Qt5 from Homebrew is used. * "qt5Brew" means that Qt5 from Homebrew is used.
* "Rhb27Phb37" means that Ruby is 2.7 from Homebrew; Python is 3.7 from Homebrew. * "Rhb27Phb38" means that Ruby is 2.7 from Homebrew; Python is 3.8 from Homebrew.
4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation. 4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation.
### 6D. Partially Homebrew-flavored build with System Ruby and Homebrew Python 3.7 ### 6D. Partially Homebrew-flavored build with System Ruby and Homebrew Python 3.8
``` ```
$ cd /where/'build.sh'/exists $ cd /where/'build.sh'/exists
$ ./build4mac.py -q qt5brew -r sys -p hb37 $ ./build4mac.py -q qt5brew -r sys -p hb38
``` ```
2. Confirm successful build (it will take about one hour depending on your machine spec). 2. Confirm successful build (it will take about one hour depending on your machine spec).
3. Run **`build4mac.py`** again with the same options used in 1. PLUS "-y" to deploy executables and libraries (including Qt's frameworks and Python frameworks) under **`klayout.app`** bundle.<br> 3. Run **`build4mac.py`** again with the same options used in 1. PLUS "-y" to deploy executables and libraries (including Qt's frameworks and Python frameworks) under **`klayout.app`** bundle.<br>
The buddy command-line tools (strm*) will also be deployed in this step. The buddy command-line tools (strm*) will also be deployed in this step.
``` ```
$ ./build4mac.py -q qt5brew -r sys -p hb37 -y $ ./build4mac.py -q qt5brew -r sys -p hb38 -y
``` ```
The application bundle **`klayout.app`** is located under:<br> The application bundle **`klayout.app`** is located under:<br>
**`HW-qt5Brew.pkg.macos-Catalina-release-RsysPhb37`** directory, where **`HW-qt5Brew.pkg.macos-Catalina-release-RsysPhb38`** directory, where
* "HW-" means that this is a heavyweight package because both Qt5 and Python are deployed. * "HW-" means that this is a heavyweight package because both Qt5 and Python are deployed.
* "qt5Brew" means that Qt5 from Homebrew is used. * "qt5Brew" means that Qt5 from Homebrew is used.
* "RsysPhb37" means that Ruby is OS-bundled; Python is 3.7 from Homebrew. * "RsysPhb38" means that Ruby is OS-bundled; Python is 3.8 from Homebrew.
4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation. 4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation.
### Important ### ### Important ###
So far, deployment of Homebrew Ruby is not supported. <br> So far, deployment of Homebrew Ruby is not supported. <br>
Therefore, if you intend to use "-y" option, you need to use "-r sys" for building. Therefore, if you intend to use "-y" option, you need to use "-r sys" for building.
### 6E. Fully Anaconda3-flavored build with Anaconda3 Ruby 2.5 and Anaconda3 Python 3.7 ### 6E. Fully Anaconda3-flavored build with Anaconda3 Ruby 2.5 and Anaconda3 Python 3.8
``` ```
$ cd /where/'build.sh'/exists $ cd /where/'build.sh'/exists
$ ./build4mac.py -q qt5ana3 -r ana3 -p ana3 $ ./build4mac.py -q qt5ana3 -r ana3 -p ana3
@ -198,7 +198,7 @@ $ ./build4mac.py -q qt5ana3 -r ana3 -p ana3 -Y
**`LW-qt5Ana3.pkg.macos-Catalina-release-Rana3Pana3`** directory, where **`LW-qt5Ana3.pkg.macos-Catalina-release-Rana3Pana3`** directory, where
* "LW-" means that this is a lightweight package. * "LW-" means that this is a lightweight package.
* "qt5Ana3" means that Qt5 from Anaconda3 is used. * "qt5Ana3" means that Qt5 from Anaconda3 is used.
* "Rana3Pana3" means that Ruby (2.5) is from Anaconda3; Python (3.7) is from Anaconda3. * "Rana3Pana3" means that Ruby (2.5) is from Anaconda3; Python (3.8) is from Anaconda3.
4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation. 4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation.
5. You may have to set `PYTHONHOME` environment variable like: 5. You may have to set `PYTHONHOME` environment variable like:
``` ```

View File

@ -73,11 +73,11 @@ def SetGlobals():
Usage += " : MP26: use Ruby 2.6 from MacPorts | \n" Usage += " : MP26: use Ruby 2.6 from MacPorts | \n"
Usage += " : HB27: use Ruby 2.7 from Homebrew | \n" Usage += " : HB27: use Ruby 2.7 from Homebrew | \n"
Usage += " : Ana3: use Ruby 2.5 from Anaconda3 | \n" Usage += " : Ana3: use Ruby 2.5 from Anaconda3 | \n"
Usage += " [-p|--python <type>] : case-insensitive type=['nil', 'Sys', 'MP37', 'HB37', 'Ana3'] | sys \n" Usage += " [-p|--python <type>] : case-insensitive type=['nil', 'Sys', 'MP38', 'HB38', 'Ana3'] | sys \n"
Usage += " : nil: don't bind Python | \n" Usage += " : nil: don't bind Python | \n"
Usage += " : Sys: use OS-bundled Python 2.7 [ElCapitan -- Catalina] | \n" Usage += " : Sys: use OS-bundled Python 2.7 [ElCapitan -- Catalina] | \n"
Usage += " : MP37: use Python 3.7 from MacPorts | \n" Usage += " : MP38: use Python 3.8 from MacPorts | \n"
Usage += " : HB37: use Python 3.7 from Homebrew | \n" Usage += " : HB38: use Python 3.8 from Homebrew | \n"
Usage += " : Ana3: use Python 3.7 from Anaconda3 | \n" Usage += " : Ana3: use Python 3.7 from Anaconda3 | \n"
Usage += " [-n|--noqtbinding] : don't create Qt bindings for ruby scripts | disabled \n" Usage += " [-n|--noqtbinding] : don't create Qt bindings for ruby scripts | disabled \n"
Usage += " [-m|--make <option>] : option passed to 'make' | '-j4' \n" Usage += " [-m|--make <option>] : option passed to 'make' | '-j4' \n"
@ -195,7 +195,7 @@ def ParseCommandLineArguments():
p.add_option( '-p', '--python', p.add_option( '-p', '--python',
dest='type_python', dest='type_python',
help="Python type=['nil', 'Sys', 'MP37', 'HB37', 'Ana3']" ) help="Python type=['nil', 'Sys', 'MP38', 'HB38', 'Ana3']" )
p.add_option( '-n', '--noqtbinding', p.add_option( '-n', '--noqtbinding',
action='store_true', action='store_true',
@ -332,8 +332,8 @@ def ParseCommandLineArguments():
candidates = dict() candidates = dict()
candidates['NIL'] = 'nil' candidates['NIL'] = 'nil'
candidates['SYS'] = 'Sys' candidates['SYS'] = 'Sys'
candidates['MP37'] = 'MP37' candidates['MP38'] = 'MP38'
candidates['HB37'] = 'HB37' candidates['HB38'] = 'HB38'
candidates['ANA3'] = 'Ana3' candidates['ANA3'] = 'Ana3'
try: try:
choicePython = candidates[ opt.type_python.upper() ] choicePython = candidates[ opt.type_python.upper() ]
@ -355,11 +355,11 @@ def ParseCommandLineArguments():
ModulePython = 'PythonSierra' ModulePython = 'PythonSierra'
elif Platform == "ElCapitan": elif Platform == "ElCapitan":
ModulePython = 'PythonElCapitan' ModulePython = 'PythonElCapitan'
elif choicePython == "MP37": elif choicePython == "MP38":
ModulePython = 'Python37MacPorts' ModulePython = 'Python38MacPorts'
NonOSStdLang = True NonOSStdLang = True
elif choicePython == "HB37": elif choicePython == "HB38":
ModulePython = 'Python37Brew' ModulePython = 'Python38Brew'
NonOSStdLang = True NonOSStdLang = True
elif choicePython == "Ana3": elif choicePython == "Ana3":
ModulePython = 'PythonAnaconda3' ModulePython = 'PythonAnaconda3'
@ -374,12 +374,12 @@ def ParseCommandLineArguments():
# Set of modules chosen # Set of modules chosen
ModuleSet = ( choiceQt5, choiceRuby, choicePython ) ModuleSet = ( choiceQt5, choiceRuby, choicePython )
NoQtBindings = opt.no_qt_binding NoQtBindings = opt.no_qt_binding
MakeOptions = opt.make_option MakeOptions = opt.make_option
DebugMode = opt.debug_build DebugMode = opt.debug_build
CheckComOnly = opt.check_command CheckComOnly = opt.check_command
DeploymentF = opt.deploy_full DeploymentF = opt.deploy_full
DeploymentP = opt.deploy_partial DeploymentP = opt.deploy_partial
if DeploymentF and DeploymentP: if DeploymentF and DeploymentP:
print("") print("")
@ -409,9 +409,9 @@ def ParseCommandLineArguments():
if (ModuleRuby in RubySys) and (ModulePython in PythonSys): if (ModuleRuby in RubySys) and (ModulePython in PythonSys):
PackagePrefix = "ST-" PackagePrefix = "ST-"
message += "a standard (ST-) package including Qt5 and using OS-bundled Ruby and Python..." message += "a standard (ST-) package including Qt5 and using OS-bundled Ruby and Python..."
elif ModulePython == 'Python37Brew': elif ModulePython == 'Python38Brew':
PackagePrefix = "HW-" PackagePrefix = "HW-"
message += "a heavyweight (HW-) package including Qt5 and Python3.7 from Homebrew..." message += "a heavyweight (HW-) package including Qt5 and Python3.8 from Homebrew..."
else: else:
PackagePrefix = "EX-" PackagePrefix = "EX-"
message += "a package with exceptional (EX-) combinations of different modules..." message += "a package with exceptional (EX-) combinations of different modules..."
@ -593,6 +593,7 @@ def RunMainBuildBash():
os.remove( tarFile ) os.remove( tarFile )
os.chdir( "../" ) os.chdir( "../" )
shutil.copy2( "macbuild/macQAT.sh", MacBuildDirQAT ) shutil.copy2( "macbuild/macQAT.sh", MacBuildDirQAT )
shutil.copy2( "macbuild/macQAT.py", MacBuildDirQAT )
print( "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++", file=sys.stderr ) print( "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++", file=sys.stderr )
print( "### <%s>: prepared the initial *.macQAT/" % myscript, file=sys.stderr ) print( "### <%s>: prepared the initial *.macQAT/" % myscript, file=sys.stderr )
print( "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++", file=sys.stderr ) print( "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++", file=sys.stderr )
@ -905,10 +906,6 @@ def DeployBinariesForBundle():
deploytool = Qt5MacPorts['deploy'] deploytool = Qt5MacPorts['deploy']
app_bundle = "klayout.app" app_bundle = "klayout.app"
options = macdepQtOpt + verbose options = macdepQtOpt + verbose
# To use Qt5 from Homebrew on Catalina...
# in "/usr/local/opt/python/lib/"
# Python.framework -> ../Frameworks/Python.framework/ <=== this symbolic was needed
# pkgconfig/
elif ModuleQt == 'Qt5Brew': elif ModuleQt == 'Qt5Brew':
deploytool = Qt5Brew['deploy'] deploytool = Qt5Brew['deploy']
app_bundle = "klayout.app" app_bundle = "klayout.app"
@ -932,39 +929,52 @@ def DeployBinariesForBundle():
os.chdir(ProjectDir) os.chdir(ProjectDir)
return 1 return 1
#------------------------------------------------------------- #-----------------------------------------------------------------------------------------------
# [9] Special deployment of Python3.7 from Homebrew # [9] Special deployment of Python3.8 from Homebrew
#------------------------------------------------------------- # To use Python3.8 from Homebrew on Catalina...
deploymentPython37HB = (ModulePython == 'Python37Brew') # in "/usr/local/opt/python/lib/"
if deploymentPython37HB and NonOSStdLang: # Python.framework -> ../Frameworks/Python.framework/ <=== this symbolic was needed
# pkgconfig/
#-----------------------------------------------------------------------------------------------
deploymentPython38HB = (ModulePython == 'Python38Brew')
if deploymentPython38HB and NonOSStdLang:
from build4mac_util import WalkFrameworkPaths, PerformChanges from build4mac_util import WalkFrameworkPaths, PerformChanges
bundlePath = AbsMacPkgDir + '/klayout.app' pythonHBVer = "3.8" # 'pinned' to this version as of KLayout version 0.26.7 (2020-09-13)
bundleExecPathAbs = '%s/Contents/MacOS/' % bundlePath bundlePath = AbsMacPkgDir + '/klayout.app'
bundleExecPathAbs = '%s/Contents/MacOS/' % bundlePath
pythonFrameworkPath = '%s/Contents/Frameworks/Python.framework' % bundlePath pythonFrameworkPath = '%s/Contents/Frameworks/Python.framework' % bundlePath
testTarget = '%s/Versions/%s/lib/python%s/test' % (pythonFrameworkPath, pythonHBVer, pythonHBVer)
resourceTarget1 = '%s/Versions/%s/Resources' % (pythonFrameworkPath, pythonHBVer)
resourceTarget2 = '%s/Resources' % pythonFrameworkPath
binTarget = '%s/Versions/%s/bin' % (pythonFrameworkPath, pythonHBVer)
sitepackagesTarget = '%s/Versions/%s/lib/python%s/site-packages' % (pythonFrameworkPath, pythonHBVer, pythonHBVer)
sitepackagesSource = '%s/Versions/%s/lib/python%s/site-packages' % (HBPython38FrameworkPath, pythonHBVer, pythonHBVer)
print( "" ) print( "" )
print( " [9] Optional deployment of Python from %s ..." % HBPython37FrameworkPath ) print( " [9] Optional deployment of Python from %s ..." % HBPython38FrameworkPath )
print( " [9.1] Copying Python Framework" ) print( " [9.1] Copying Python Framework" )
cmd1 = "rm -rf %s" % pythonFrameworkPath cmd01 = "rm -rf %s" % pythonFrameworkPath
cmd2 = "rsync -a --safe-links %s/ %s" % (HBPython37FrameworkPath, pythonFrameworkPath) cmd02 = "rsync -a --safe-links %s/ %s" % (HBPython38FrameworkPath, pythonFrameworkPath)
cmd3 = "mkdir %s/Versions/3.7/lib/python3.7/site-packages/" % pythonFrameworkPath
cmd4 = "cp -RL %s/Versions/3.7/lib/python3.7/site-packages/{pip*,pkg_resources,setuptools*,wheel*} " % \ cmd03 = "rm -rf %s" % testTarget
HBPython37FrameworkPath cmd04 = "rm -rf %s" % resourceTarget1
cmd4 += "%s/Versions/3.7/lib/python3.7/site-packages/" % pythonFrameworkPath cmd05 = "unlink %s" % resourceTarget2
cmd5 = "rm -rf %s/Versions/3.7/lib/python3.7/test" % pythonFrameworkPath cmd06 = "rm -rf %s" % binTarget
cmd6 = "rm -rf %s/Versions/3.7/Resources" % pythonFrameworkPath
cmd7 = "rm -rf %s/Versions/3.7/bin" % pythonFrameworkPath cmd07 = "mkdir %s" % sitepackagesTarget
cmd08 = "cp -RL %s/{pip*,pkg_resources,setuptools*,wheel*} %s" % (sitepackagesSource, sitepackagesTarget)
shell_commands = list() shell_commands = list()
shell_commands.append(cmd1) shell_commands.append(cmd01)
shell_commands.append(cmd2) shell_commands.append(cmd02)
shell_commands.append(cmd3) shell_commands.append(cmd03)
shell_commands.append(cmd4) shell_commands.append(cmd04)
shell_commands.append(cmd5) shell_commands.append(cmd05)
shell_commands.append(cmd6) shell_commands.append(cmd06)
shell_commands.append(cmd7) shell_commands.append(cmd07)
shell_commands.append(cmd08)
for command in shell_commands: for command in shell_commands:
if subprocess.call( command, shell=True ) != 0: if subprocess.call( command, shell=True ) != 0:
@ -973,14 +983,15 @@ def DeployBinariesForBundle():
sys.exit(1) sys.exit(1)
shutil.copy2( sourceDir2 + "/start-console.py", targetDirM ) shutil.copy2( sourceDir2 + "/start-console.py", targetDirM )
shutil.copy2( sourceDir2 + "/klayout_console", targetDirM ) shutil.copy2( sourceDir2 + "/klayout_console", targetDirM )
os.chmod( targetDirM + "/klayout_console", 0o0755 ) os.chmod( targetDirM + "/start-console.py", 0o0755 )
os.chmod( targetDirM + "/klayout_console", 0o0755 )
print(" [9.2] Relinking dylib dependencies inside Python.framework" ) print(" [9.2] Relinking dylib dependencies inside Python.framework" )
print(" [9.2.1] Patching Python Framework" ) print(" [9.2.1] Patching Python Framework" )
depdict = WalkFrameworkPaths( pythonFrameworkPath ) depdict = WalkFrameworkPaths( pythonFrameworkPath )
appPythonFrameworkPath = '@executable_path/../Frameworks/Python.framework/' appPythonFrameworkPath = '@executable_path/../Frameworks/Python.framework/'
PerformChanges(depdict, [(HBPython37FrameworkPath, appPythonFrameworkPath, False)], bundleExecPathAbs) PerformChanges(depdict, [(HBPython38FrameworkPath, appPythonFrameworkPath, False)], bundleExecPathAbs)
print(" [9.2.2] Patching /usr/local/opt/ libs") print(" [9.2.2] Patching /usr/local/opt/ libs")
usrLocalPath = '/usr/local/opt/' usrLocalPath = '/usr/local/opt/'
@ -989,17 +1000,16 @@ def DeployBinariesForBundle():
depdict = WalkFrameworkPaths(pythonFrameworkPath, search_path_filter=r'\t+/usr/local/(opt|Cellar)') depdict = WalkFrameworkPaths(pythonFrameworkPath, search_path_filter=r'\t+/usr/local/(opt|Cellar)')
PerformChanges(depdict, replacePairs, bundleExecPathAbs) PerformChanges(depdict, replacePairs, bundleExecPathAbs)
print(" [9.2.3] Patching openssl, gdbm, readline, sqlite, tcl-tk, xz") print(" [9.2.3] Patching openssl@1.1, gdbm, readline, sqlite, xz")
usrLocalPath = '/usr/local/opt' usrLocalPath = '/usr/local/opt'
appUsrLocalPath = '@executable_path/../Frameworks/' appUsrLocalPath = '@executable_path/../Frameworks/'
replacePairs = [(usrLocalPath, appUsrLocalPath, True)] replacePairs = [(usrLocalPath, appUsrLocalPath, True)]
replacePairs.extend([(openssl_version, '@executable_path/../Frameworks/openssl', True) replacePairs.extend([(openssl_version, '@executable_path/../Frameworks/openssl@1.1', True)
for openssl_version in glob.glob('/usr/local/Cellar/openssl/*')]) for openssl_version in glob.glob('/usr/local/Cellar/openssl@1.1/*')])
depdict = WalkFrameworkPaths([pythonFrameworkPath + '/../openssl', depdict = WalkFrameworkPaths([pythonFrameworkPath + '/../openssl@1.1',
pythonFrameworkPath + '/../gdbm', pythonFrameworkPath + '/../gdbm',
pythonFrameworkPath + '/../readline', pythonFrameworkPath + '/../readline',
pythonFrameworkPath + '/../sqlite', pythonFrameworkPath + '/../sqlite',
pythonFrameworkPath + '/../tcl-tk',
pythonFrameworkPath + '/../xz'], search_path_filter=r'\t+/usr/local/(opt|Cellar)') pythonFrameworkPath + '/../xz'], search_path_filter=r'\t+/usr/local/(opt|Cellar)')
PerformChanges(depdict, replacePairs, bundleExecPathAbs) PerformChanges(depdict, replacePairs, bundleExecPathAbs)
@ -1007,21 +1017,21 @@ def DeployBinariesForBundle():
print(" [9.3] Relinking dylib dependencies for klayout") print(" [9.3] Relinking dylib dependencies for klayout")
klayoutPath = bundleExecPathAbs klayoutPath = bundleExecPathAbs
depdict = WalkFrameworkPaths(klayoutPath, filter_regex=r'klayout$') depdict = WalkFrameworkPaths(klayoutPath, filter_regex=r'klayout$')
PerformChanges(depdict, [(HBPython37FrameworkPath, appPythonFrameworkPath, False)], bundleExecPathAbs) PerformChanges(depdict, [(HBPython38FrameworkPath, appPythonFrameworkPath, False)], bundleExecPathAbs)
libKlayoutPath = bundleExecPathAbs + '../Frameworks' libKlayoutPath = bundleExecPathAbs + '../Frameworks'
depdict = WalkFrameworkPaths(libKlayoutPath, filter_regex=r'libklayout') depdict = WalkFrameworkPaths(libKlayoutPath, filter_regex=r'libklayout')
PerformChanges(depdict, [(HBPython37FrameworkPath, appPythonFrameworkPath, False)], bundleExecPathAbs) PerformChanges(depdict, [(HBPython38FrameworkPath, appPythonFrameworkPath, False)], bundleExecPathAbs)
print(" [9.4] Patching site.py, pip/, and distutils/") print(" [9.4] Patching site.py, pip/, and distutils/")
site_module = "%s/Versions/3.7/lib/python3.7/site.py" % pythonFrameworkPath site_module = "%s/Versions/%s/lib/python%s/site.py" % (pythonFrameworkPath, pythonHBVer, pythonHBVer)
with open(site_module, 'r') as site: with open(site_module, 'r') as site:
buf = site.readlines() buf = site.readlines()
with open(site_module, 'w') as site: with open(site_module, 'w') as site:
import re import re
for line in buf: for line in buf:
# This will fool pip into thinking it's inside a virtual environment # This will fool pip into thinking it's inside a virtual environment
# and install new packates to the correct site-packages # and install new packages to the correct site-packages
if re.match("^PREFIXES", line) is not None: if re.match("^PREFIXES", line) is not None:
line = line + "sys.real_prefix = sys.prefix\n" line = line + "sys.real_prefix = sys.prefix\n"
# do not allow installation in the user folder. # do not allow installation in the user folder.
@ -1029,7 +1039,24 @@ def DeployBinariesForBundle():
line = "ENABLE_USER_SITE = False\n" line = "ENABLE_USER_SITE = False\n"
site.write(line) site.write(line)
pip_module = "%s/Versions/3.7/lib/python3.7/site-packages/pip/__init__.py" % pythonFrameworkPath #----------------------------------------------------------------------------------
# Typical usage of 'pip' after installation of the DMG package
#
# $ /Applications/klayout.app/Contents/MacOS/start-console.py
# Warning: Populating font family aliases took 195 ms. Replace uses of missing font\
# family "Monospace" with one that exists to avoid this cost.
# Python 3.7.8 (default, Jul 4 2020, 10:17:17)
# [Clang 11.0.3 (clang-1103.0.32.62)] on darwin
# Type "help", "copyright", "credits" or "license" for more information.
# (KLayout Python Console)
# >>> import pip
# >>> pip.main( ['install', 'numpy'] )
# >>> pip.main( ['install', 'scipy'] )
# >>> pip.main( ['install', 'pandas'] )
# >>> pip.main( ['install', 'matplotlib'] )
#----------------------------------------------------------------------------------
pip_module = "%s/Versions/%s/lib/python%s/site-packages/pip/__init__.py" % \
(pythonFrameworkPath, pythonHBVer, pythonHBVer)
with open(pip_module, 'r') as pip: with open(pip_module, 'r') as pip:
buf = pip.readlines() buf = pip.readlines()
with open(pip_module, 'w') as pip: with open(pip_module, 'w') as pip:
@ -1039,7 +1066,8 @@ def DeployBinariesForBundle():
line = re.sub("return isolated$", "return isolated or True", line) line = re.sub("return isolated$", "return isolated or True", line)
pip.write(line) pip.write(line)
distutilsconfig = "%s/Versions/3.7/lib/python3.7/distutils/distutils.cfg" % pythonFrameworkPath distutilsconfig = "%s/Versions/%s/lib/python%s/distutils/distutils.cfg" % \
(pythonFrameworkPath, pythonHBVer, pythonHBVer)
with open(distutilsconfig, 'r') as file: with open(distutilsconfig, 'r') as file:
buf = file.readlines() buf = file.readlines()
with open(distutilsconfig, 'w') as file: with open(distutilsconfig, 'w') as file:
@ -1051,7 +1079,7 @@ def DeployBinariesForBundle():
file.write(line) file.write(line)
#------------------------------------------------------------- #-------------------------------------------------------------
# [10] Special deployment of Ruby2.6 from Homebrew? # [10] Special deployment of Ruby2.7 from Homebrew?
#------------------------------------------------------------- #-------------------------------------------------------------
deploymentRuby26HB = (ModuleRuby == 'Ruby27Brew') deploymentRuby26HB = (ModuleRuby == 'Ruby27Brew')
if deploymentRuby26HB and NonOSStdLang: if deploymentRuby26HB and NonOSStdLang:
@ -1059,7 +1087,7 @@ def DeployBinariesForBundle():
print( "" ) print( "" )
print( " [10] You have reached optional deployment of Ruby from %s ..." % HBRuby27Path ) print( " [10] You have reached optional deployment of Ruby from %s ..." % HBRuby27Path )
print( " [!!!] Sorry, the deployed package will not work properly since deployment of" ) print( " [!!!] Sorry, the deployed package will not work properly since deployment of" )
print( " Ruby2.6 from Homebrew is not yet supported." ) print( " Ruby2.7 from Homebrew is not yet supported." )
print( " Since you have Homebrew development environment, there two options:" ) print( " Since you have Homebrew development environment, there two options:" )
print( " (1) Retry to make a package with '-Y|--DEPLOY' option." ) print( " (1) Retry to make a package with '-Y|--DEPLOY' option." )
print( " This will not deploy any of Qt5, Python, and Ruby from Homebrew." ) print( " This will not deploy any of Qt5, Python, and Ruby from Homebrew." )

View File

@ -154,7 +154,7 @@ RubyDictionary = { 'nil' : None,
#----------------------------------------------------- #-----------------------------------------------------
PythonNil = [ 'nil' ] PythonNil = [ 'nil' ]
PythonSys = [ 'PythonElCapitan', 'PythonSierra', 'PythonHighSierra', 'PythonMojave', 'PythonCatalina' ] PythonSys = [ 'PythonElCapitan', 'PythonSierra', 'PythonHighSierra', 'PythonMojave', 'PythonCatalina' ]
PythonExt = [ 'Python37MacPorts', 'Python37Brew', 'PythonAnaconda3' ] PythonExt = [ 'Python38MacPorts', 'Python38Brew', 'PythonAnaconda3' ]
Pythons = PythonNil + PythonSys + PythonExt Pythons = PythonNil + PythonSys + PythonExt
#----------------------------------------------------- #-----------------------------------------------------
@ -204,30 +204,30 @@ PythonCatalina = { 'exe': '/System/Library/Frameworks/Python.framework/Versions
'lib': '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/libpython2.7.dylib' 'lib': '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/libpython2.7.dylib'
} }
# Python 3.7 from MacPorts (https://www.macports.org/) *+*+*+ EXPERIMENTAL *+*+*+ # Python 3.8 from MacPorts (https://www.macports.org/) *+*+*+ EXPERIMENTAL *+*+*+
# install with 'sudo port install python37' # install with 'sudo port install python38'
# [Key Type Name] = 'MP37' # [Key Type Name] = 'MP38'
Python37MacPorts= { 'exe': '/opt/local/Library/Frameworks/Python.framework/Versions/3.7/bin/python3.7m', Python38MacPorts= { 'exe': '/opt/local/Library/Frameworks/Python.framework/Versions/3.8/bin/python3.8',
'inc': '/opt/local/Library/Frameworks/Python.framework/Versions/3.7/include/python3.7m', 'inc': '/opt/local/Library/Frameworks/Python.framework/Versions/3.8/include/python3.8',
'lib': '/opt/local/Library/Frameworks/Python.framework/Versions/3.7/lib/libpython3.7m.dylib' 'lib': '/opt/local/Library/Frameworks/Python.framework/Versions/3.8/lib/libpython3.8.dylib'
} }
# Python 3.7 from Homebrew *+*+*+ EXPERIMENTAL *+*+*+ # Python 3.8 from Homebrew *+*+*+ EXPERIMENTAL *+*+*+
# install with 'brew install python' # install with 'brew install python'
# [Key Type Name] = 'HB37' # [Key Type Name] = 'HB38'
HBPython37FrameworkPath = '/usr/local/opt/python3/Frameworks/Python.framework' HBPython38FrameworkPath = '/usr/local/opt/python3/Frameworks/Python.framework'
Python37Brew = { 'exe': '%s/Versions/3.7/bin/python3.7m' % HBPython37FrameworkPath, Python38Brew = { 'exe': '%s/Versions/3.8/bin/python3.8' % HBPython38FrameworkPath,
'inc': '%s/Versions/3.7/include/python3.7m' % HBPython37FrameworkPath, 'inc': '%s/Versions/3.8/include/python3.8' % HBPython38FrameworkPath,
'lib': '%s/Versions/3.7/lib/libpython3.7m.dylib' % HBPython37FrameworkPath 'lib': '%s/Versions/3.8/lib/libpython3.8.dylib' % HBPython38FrameworkPath
} }
# Python 3.7 bundled with anaconda3 installed under /Applications/anaconda3/ *+*+*+ EXPERIMENTAL *+*+*+ # Python 3.8 bundled with anaconda3 installed under /Applications/anaconda3/ *+*+*+ EXPERIMENTAL *+*+*+
# The standard installation deploys the tool under $HOME/opt/anaconda3/. # The standard installation deploys the tool under $HOME/opt/anaconda3/.
# If so, you need to make a symbolic link: /Applications/anaconda3 ---> $HOME/opt/anaconda3/ # If so, you need to make a symbolic link: /Applications/anaconda3 ---> $HOME/opt/anaconda3/
# [Key Type Name] = 'Ana3' # [Key Type Name] = 'Ana3'
PythonAnaconda3 = { 'exe': '/Applications/anaconda3/bin/python3.7m', PythonAnaconda3 = { 'exe': '/Applications/anaconda3/bin/python3.8',
'inc': '/Applications/anaconda3/include/python3.7m', 'inc': '/Applications/anaconda3/include/python3.8',
'lib': '/Applications/anaconda3/lib/libpython3.7m.dylib' 'lib': '/Applications/anaconda3/lib/libpython3.8.dylib'
} }
# Consolidated dictionary kit for Python # Consolidated dictionary kit for Python
@ -237,8 +237,8 @@ PythonDictionary= { 'nil' : None,
'PythonHighSierra': PythonHighSierra, 'PythonHighSierra': PythonHighSierra,
'PythonMojave' : PythonMojave, 'PythonMojave' : PythonMojave,
'PythonCatalina' : PythonCatalina, 'PythonCatalina' : PythonCatalina,
'Python37MacPorts': Python37MacPorts, 'Python38MacPorts': Python38MacPorts,
'Python37Brew' : Python37Brew, 'Python38Brew' : Python38Brew,
'PythonAnaconda3' : PythonAnaconda3 'PythonAnaconda3' : PythonAnaconda3
} }

View File

@ -1,7 +1,7 @@
#! /usr/bin/env python #! /usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
#=============================================================================== #========================================================================================
# File: "macbuild/build4mac_util.py" # File: "macbuild/build4mac_util.py"
# #
# Here are utility functions and classes ... # Here are utility functions and classes ...
@ -9,7 +9,7 @@
# version 0.26.1 or later on different Apple Mac OSX platforms. # version 0.26.1 or later on different Apple Mac OSX platforms.
# #
# This file is imported by 'build4mac.py' script. # This file is imported by 'build4mac.py' script.
#=============================================================================== #========================================================================================
from __future__ import print_function # to use print() of Python 3 in Python >= 2.7 from __future__ import print_function # to use print() of Python 3 in Python >= 2.7
import sys import sys
import os import os
@ -18,14 +18,14 @@ import string
import subprocess import subprocess
import shutil import shutil
#------------------------------------------------------------------------------- #----------------------------------------------------------------------------------------
## To import global dictionaries of different modules ## To import global dictionaries of different modules
#------------------------------------------------------------------------------- #----------------------------------------------------------------------------------------
mydir = os.path.dirname(os.path.abspath(__file__)) mydir = os.path.dirname(os.path.abspath(__file__))
sys.path.append( mydir ) sys.path.append( mydir )
from build4mac_env import * from build4mac_env import *
#------------------------------------------------------------------------------ #----------------------------------------------------------------------------------------
## To decompose strings obtained by 'otool -L <*.dylib>' command and to ## To decompose strings obtained by 'otool -L <*.dylib>' command and to
# generate a dictionary of KLayout's inter-library dependency. # generate a dictionary of KLayout's inter-library dependency.
# #
@ -41,7 +41,7 @@ from build4mac_env import *
# : # :
# #
# @return a dictionary # @return a dictionary
#------------------------------------------------------------------------------ #----------------------------------------------------------------------------------------
def DecomposeLibraryDependency( depstr ): def DecomposeLibraryDependency( depstr ):
alllines = depstr.split('\n') alllines = depstr.split('\n')
numlines = len(alllines) numlines = len(alllines)
@ -53,13 +53,13 @@ def DecomposeLibraryDependency( depstr ):
supporters.append(supporter) supporters.append(supporter)
return { dependent: supporters } return { dependent: supporters }
#------------------------------------------------------------------------------ #----------------------------------------------------------------------------------------
## To print the contents of a library dependency dictionary ## To print the contents of a library dependency dictionary
# #
# @param[in] depdic dictionary # @param[in] depdic dictionary
# @param[in] pathdic path dictionary # @param[in] pathdic path dictionary
# @param[in] namedic dictionary name # @param[in] namedic dictionary name
#------------------------------------------------------------------------------ #----------------------------------------------------------------------------------------
def PrintLibraryDependencyDictionary( depdic, pathdic, namedic ): def PrintLibraryDependencyDictionary( depdic, pathdic, namedic ):
keys = depdic.keys() keys = depdic.keys()
print("") print("")
@ -73,13 +73,13 @@ def PrintLibraryDependencyDictionary( depdic, pathdic, namedic ):
if itemName != keyName and (itemName in pathdic): if itemName != keyName and (itemName in pathdic):
print( " %s (%s)" % (item, pathdic[itemName]) ) print( " %s (%s)" % (item, pathdic[itemName]) )
#------------------------------------------------------------------------------ #----------------------------------------------------------------------------------------
## To set and change identification name of KLayout's dylib ## To set and change identification name of KLayout's dylib
# #
# @param[in] libdic inter-library dependency dictionary # @param[in] libdic inter-library dependency dictionary
# #
# @return 0 on success; non-zero on failure # @return 0 on success; non-zero on failure
#------------------------------------------------------------------------------ #----------------------------------------------------------------------------------------
def SetChangeIdentificationNameOfDyLib( libdic, pathDic ): def SetChangeIdentificationNameOfDyLib( libdic, pathDic ):
cmdNameId = XcodeToolChain['nameID'] cmdNameId = XcodeToolChain['nameID']
cmdNameChg = XcodeToolChain['nameCH'] cmdNameChg = XcodeToolChain['nameCH']
@ -115,7 +115,7 @@ def SetChangeIdentificationNameOfDyLib( libdic, pathDic ):
# for-lib # for-lib
return 0 return 0
#------------------------------------------------------------------------------ #----------------------------------------------------------------------------------------
## To set the identification names of KLayout's libraries to an executable ## To set the identification names of KLayout's libraries to an executable
# and make the application aware of the library locations # and make the application aware of the library locations
# #
@ -146,7 +146,7 @@ def SetChangeIdentificationNameOfDyLib( libdic, pathDic ):
# +-- 'strmxor' # +-- 'strmxor'
# #
# @return 0 on success; non-zero on failure # @return 0 on success; non-zero on failure
#------------------------------------------------------------------------------ #----------------------------------------------------------------------------------------
def SetChangeLibIdentificationName( executable, relativedir ): def SetChangeLibIdentificationName( executable, relativedir ):
cmdNameId = XcodeToolChain['nameID'] cmdNameId = XcodeToolChain['nameID']
cmdNameChg = XcodeToolChain['nameCH'] cmdNameChg = XcodeToolChain['nameCH']
@ -181,15 +181,21 @@ def SetChangeLibIdentificationName( executable, relativedir ):
# for-lib # for-lib
return 0 return 0
def WalkLibDependencyTree( dylibPath, depth=0, filter_regex=r'\t+/usr/local/opt'): #----------------------------------------------------------------------------------------
NOTHINGTODO = [] # return empty list if nothing to do. ## To make a library dependency dictionary by recursively walk down the lib hierarchy
cmdNameId = XcodeToolChain['nameID'] #
cmdNameChg = XcodeToolChain['nameCH'] # @param[in] dylibPath: dylib path
otoolCm = 'otool -L %s | grep -E "%s"' % (dylibPath, filter_regex) # @param[in] depth: hierarchy depth (< 5)
otoolOut = os.popen( otoolCm ).read() # @param[in] filter_regex: filter regular expression
exedepdic = DecomposeLibraryDependency( dylibPath + ":\n" + otoolOut ) #
keys = exedepdic.keys() # @return a dictionary
deplibs = exedepdic[ list(keys)[0] ] #----------------------------------------------------------------------------------------
def WalkLibDependencyTree( dylibPath, depth=0, filter_regex=r'\t+/usr/local/opt' ):
otoolCm = 'otool -L %s | grep -E "%s"' % (dylibPath, filter_regex)
otoolOut = os.popen( otoolCm ).read()
exedepdic = DecomposeLibraryDependency( dylibPath + ":\n" + otoolOut )
keys = exedepdic.keys()
deplibs = exedepdic[ list(keys)[0] ]
if depth < 5: if depth < 5:
if len(deplibs) > 0: if len(deplibs) > 0:
@ -201,9 +207,19 @@ def WalkLibDependencyTree( dylibPath, depth=0, filter_regex=r'\t+/usr/local/opt'
return deplibs return deplibs
return exedepdic return exedepdic
else: else:
raise RuntimeError("Exceeded maximum recursion depth.") raise RuntimeError( "Exceeded maximum recursion depth." )
def WalkFrameworkPaths(frameworkPaths, filter_regex=r'\.(so|dylib)$', search_path_filter=r'\t+/usr/local/opt'): #----------------------------------------------------------------------------------------
## To make a library dependency dictionary by recursively walk down the Framework
#
# @param[in] frameworkPaths: Framework path
# @param[in] filter_regex: filter regular expression
# @param[in] search_path_filter: search path filter regular expression
#
# @return a dictionary
#----------------------------------------------------------------------------------------
def WalkFrameworkPaths( frameworkPaths, filter_regex=r'\.(so|dylib)$',
search_path_filter=r'\t+/usr/local/opt' ):
if isinstance(frameworkPaths, str): if isinstance(frameworkPaths, str):
frameworkPathsIter = [frameworkPaths] frameworkPathsIter = [frameworkPaths]
@ -225,7 +241,15 @@ def WalkFrameworkPaths(frameworkPaths, filter_regex=r'\.(so|dylib)$', search_pat
dependency_dict[frameworkPath].append(dict_file) dependency_dict[frameworkPath].append(dict_file)
return dependency_dict return dependency_dict
def WalkDictTree(dependencyDict, visited_files): #----------------------------------------------------------------------------------------
## To make a list of changed libraries
#
# @param[in] dependencyDict: library dependency dictionary
# @param[in] visited_files: list of visited files
#
# @return a list
#----------------------------------------------------------------------------------------
def WalkDictTree( dependencyDict, visited_files ):
libNameChanges = list() libNameChanges = list()
for lib, dependencies in dependencyDict.items(): for lib, dependencies in dependencyDict.items():
if lib in visited_files: if lib in visited_files:
@ -253,31 +277,69 @@ def WalkDictTree(dependencyDict, visited_files):
visited_files.append(lib) visited_files.append(lib)
return libNameChanges return libNameChanges
#----------------------------------------------------------------------------------------
def FindFramework(path, root_path): ## To find the Framework name from a library name
#
# @param[in] path: path to a library
# @param[in] root_path: root path
#
# @return the path to a Framework
#----------------------------------------------------------------------------------------
def FindFramework( path, root_path ):
relPath = os.path.relpath(path, root_path) relPath = os.path.relpath(path, root_path)
return os.path.join(root_path, relPath.split(os.sep)[0]) frmPath = os.path.join(root_path, relPath.split(os.sep)[0])
def ResolveExecutablePath(path, executable_path): #print( "###", frmPath, path, root_path )
return frmPath
#----------------------------------------------------------------------------------------
## To resolve an executable path
#
# @param[in] path: a path to resolve
# @param[in] executable_path: an executable path
#
# @return the resolved path
#----------------------------------------------------------------------------------------
def ResolveExecutablePath( path, executable_path ):
""" Transforms @executable_path into executable_path""" """ Transforms @executable_path into executable_path"""
p = path.replace("@executable_path", "/%s/" % executable_path) p = path.replace("@executable_path", "/%s/" % executable_path)
return p return p
#----------------------------------------------------------------------------------------
## To detect the changed library names
#
# @param[in] frameworkDependencyDict: framework dependency dictionary
#
# @return a list of changes, each of which is stored in the form of
# * ('lib.dylib', ['dep1.dylib', ...])
# OR
# * ('lib.dylib',)
#----------------------------------------------------------------------------------------
def DetectChanges(frameworkDependencyDict): def DetectChanges(frameworkDependencyDict):
visited_files = list() visited_files = list()
libNameChanges = list() libNameChanges = list()
for framework, libraries in frameworkDependencyDict.items(): for framework, libraries in frameworkDependencyDict.items():
for libraryDict in libraries: for libraryDict in libraries:
libNameChanges.extend(WalkDictTree(libraryDict, visited_files)) libNameChanges.extend(WalkDictTree(libraryDict, visited_files))
# Changes are stored in libNameChanges in the form of ('lib.dylib', ['dep1.dylib', ...])
return libNameChanges return libNameChanges
def PerformChanges(frameworkDependencyDict, replaceFromToPairs=None, executable_path="/tmp/klayout"): #----------------------------------------------------------------------------------------
## To perform the required changes
#
# @param[in] frameworkDependencyDict: framework dependency dictionary
# @param[in] replaceFromToPairs: (from, to)-pair for replacement
# @param[in] executable_path: executable path
#
# @return 0 on success; > 0 on failure
#----------------------------------------------------------------------------------------
def PerformChanges( frameworkDependencyDict, replaceFromToPairs=None, executable_path="/tmp/klayout" ):
libNameChanges = DetectChanges(frameworkDependencyDict) libNameChanges = DetectChanges(frameworkDependencyDict)
cmdNameId = XcodeToolChain['nameID'] #print(libNameChanges)
cmdNameId = XcodeToolChain['nameID']
cmdNameChg = XcodeToolChain['nameCH'] cmdNameChg = XcodeToolChain['nameCH']
if replaceFromToPairs is not None: if replaceFromToPairs is None:
return 0
else:
for libNameChange in libNameChanges: for libNameChange in libNameChanges:
libNameChangeIterator = iter(libNameChange) libNameChangeIterator = iter(libNameChange)
lib = next(libNameChangeIterator) lib = next(libNameChangeIterator)
@ -300,8 +362,8 @@ def PerformChanges(frameworkDependencyDict, replaceFromToPairs=None, executable_
destFrameworkPath = ResolveExecutablePath(destFrameworkPath, executable_path) destFrameworkPath = ResolveExecutablePath(destFrameworkPath, executable_path)
if not os.path.exists(fileName): if not os.path.exists(fileName):
print (lib.replace(replaceFrom, replaceTo), "DOES NOT EXIST") print( " NOT FOUND:", lib.replace(replaceFrom, replaceTo) )
print ("COPY", frameworkPath, " -> ", destFrameworkPath) print( " COPYING:", frameworkPath, " -> ", destFrameworkPath )
shutil.copytree(frameworkPath, destFrameworkPath) shutil.copytree(frameworkPath, destFrameworkPath)
nameId = lib.replace(replaceFrom, replaceTo) nameId = lib.replace(replaceFrom, replaceTo)
@ -316,8 +378,8 @@ def PerformChanges(frameworkDependencyDict, replaceFromToPairs=None, executable_
for dependency in dependencies: for dependency in dependencies:
if dependency.find(replaceFrom) >= 0: if dependency.find(replaceFrom) >= 0:
print("\tIn:", fileName) print( " IN:", fileName )
print("\tRENAME", dependency, " -> ", dependency.replace(replaceFrom, replaceTo)) print( " RENAMING:", dependency, " -> ", dependency.replace(replaceFrom, replaceTo) )
# Try changing id first # Try changing id first
nameId = dependency.replace(replaceFrom, replaceTo) nameId = dependency.replace(replaceFrom, replaceTo)
@ -342,15 +404,15 @@ def PerformChanges(frameworkDependencyDict, replaceFromToPairs=None, executable_
msg = "!!! Failed to set the new identification name to <%s> !!!" msg = "!!! Failed to set the new identification name to <%s> !!!"
print( msg % fileName, file=sys.stderr ) print( msg % fileName, file=sys.stderr )
return 1 return 1
return 0
#----------------------------------------------------------------------------------------
#------------------------------------------------------------------------------
## To get KLayout's version from a file; most likely from 'version.sh' ## To get KLayout's version from a file; most likely from 'version.sh'
# #
# @param[in] verfile version file from which version is retrieved # @param[in] verfile version file from which version is retrieved
# #
# @return version string # @return version string
#------------------------------------------------------------------------------ #----------------------------------------------------------------------------------------
def GetKLayoutVersionFrom( verfile='version.h' ): def GetKLayoutVersionFrom( verfile='version.h' ):
version = "?.?.?" version = "?.?.?"
try: try:
@ -372,14 +434,14 @@ def GetKLayoutVersionFrom( verfile='version.h' ):
return version return version
return version return version
#------------------------------------------------------------------------------ #----------------------------------------------------------------------------------------
## To generate the contents of "Info.plist" file from a template ## To generate the contents of "Info.plist" file from a template
# #
# @param[in] keydic dictionary of four key words ['exe', 'icon', 'bname', 'ver'] # @param[in] keydic dictionary of four key words ['exe', 'icon', 'bname', 'ver']
# @param[in] templfile template file ("macbuild/Resources/Info.plist.template") # @param[in] templfile template file ("macbuild/Resources/Info.plist.template")
# #
# @return generated strings # @return generated strings
#------------------------------------------------------------------------------ #----------------------------------------------------------------------------------------
def GenerateInfoPlist( keydic, templfile ): def GenerateInfoPlist( keydic, templfile ):
val_exe = keydic['exe'] val_exe = keydic['exe']
val_icon = keydic['icon'] val_icon = keydic['icon']

278
macbuild/macQAT.py Executable file
View File

@ -0,0 +1,278 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#===============================================================================
# File: "macbuild/macQAT.py"
#
# The top Python script to run "ut_runner" after building KLayout
# (http://www.klayout.de/index.php) version 0.26.1 or later on different Apple
# ßMac OSX platforms.
#
# This script must be copied to a "*.macQAT/" directory to run.
#===============================================================================
from __future__ import print_function # to use print() of Python 3 in Python >= 2.7
import sys
import os
import datetime
from time import sleep
import six
import shutil
import glob
import platform
import optparse
import subprocess
#-------------------------------------------------------------------------------
## To set global variables including present directory and platform info.
#
#-------------------------------------------------------------------------------
def SetGlobals():
global ProjectDir # project directory where "ut_runner" exists
global RunnerUsage # True to print the usage of 'ut_runner'
global Run # True to run this script
global ContinueOnError # True to continue after an error
global TestsExcluded # list of tests to exclude
global Arguments # other arguments
global GitSHA1 # Git's short SHA1 value of the HEAD
global TimeStamp # time stamp
global WorkDir # work directory name
global LogFile # log file name
global Usage # string on usage
# auxiliary variables on platform
global System # 6-tuple from platform.uname()
global Node # - do -
global Release # - do -
global Version # - do -
global Machine # - do -
global Processor # - do -
global Bit # machine bit-size
Usage = "\n"
Usage += "----------------------------------------------------------------------------------------\n"
Usage += "<< Usage of 'macQAT.py' >>\n"
Usage += " for running 'ut_runner' after building KLayout.\n"
Usage += "\n"
Usage += "$ [python] ./macQAT.py \n"
Usage += " option & argument : descriptions | default value\n"
Usage += " -----------------------------------------------------------------+---------------\n"
Usage += " [-u|--usage] : print usage of 'ut_runner'and exit | disabled \n"
Usage += " | \n"
Usage += " <-r|--run> : run this script | disabled \n"
Usage += " [-s|--stop] : stop on error | disabled \n"
Usage += " [-x|--exclude <tests>] : exclude test(s) such as 'pymod,pya' | '' \n"
Usage += " [-a|--args <string>] : arguments other than '-x' and '-c' | '' \n"
Usage += " [-?|--?] : print this usage and exit | disabled \n"
Usage += "--------------------------------------------------------------------+-------------------\n"
ProjectDir = os.getcwd()
RunnerUsage = False
Run = False
ContinueOnError = True
TestsExcluded = list()
Arguments = ""
GitSHA1 = GetGitShortSHA1()
TimeStamp = GetTimeStamp()
WorkDir = "QATest_%s_%s__%s" % (GitSHA1, TimeStamp, os.path.basename(ProjectDir) )
LogFile = WorkDir + ".log"
(System, Node, Release, Version, Machine, Processor) = platform.uname()
#-------------------------------------------------------------------------------
## Get git's short SHA1 value of the HEAD
#
# @return SHA1 value string
#-------------------------------------------------------------------------------
def GetGitShortSHA1():
command = "git rev-parse --short HEAD 2>/dev/null"
sha1val = os.popen( command ).read().strip()
return sha1val
#-------------------------------------------------------------------------------
## Get the time stamp
#
# @return time stamp string
#-------------------------------------------------------------------------------
def GetTimeStamp():
ts = datetime.datetime.today()
return "%04d_%02d%02d_%02d%02d" % (ts.year, ts.month, ts.day, ts.hour, ts.minute)
#-------------------------------------------------------------------------------
## To parse the command line arguments
#
#-------------------------------------------------------------------------------
def ParseCommandLineArguments():
global Usage
global RunnerUsage
global Run
global ContinueOnError
global TestsExcluded
global Arguments
p = optparse.OptionParser( usage=Usage )
p.add_option( '-u', '--usage',
action='store_true',
dest='runner_usage',
default=False,
help="print usage of 'ut_runner' and exit (false)" )
p.add_option( '-r', '--run',
action='store_true',
dest='runme',
default=False,
help='run this script (false)' )
p.add_option( '-s', '--stop',
action='store_true',
dest='stop_on_error',
default=False,
help='stop on error (false)' )
p.add_option( '-x', '--exclude',
dest='exclude_tests',
help="exclude test(s) such as 'pymod,pya' ('')" )
p.add_option( '-a', '--args',
dest='arguments',
help="arguments other than '-x' and '-c' ('')" )
p.add_option( '-?', '--??',
action='store_true',
dest='checkusage',
default=False,
help='check usage (false)' )
p.set_defaults( runner_usage = False,
runme = False,
stop_on_error = False,
exclude_tests = "",
arguments = "",
checkusage = False )
opt, args = p.parse_args()
if opt.checkusage:
print(Usage)
quit()
RunnerUsage = opt.runner_usage
Run = opt.runme
ContinueOnError = not opt.stop_on_error
if not opt.exclude_tests == "":
TestsExcluded = [ item.strip() for item in opt.exclude_tests.split(',') ]
else:
TestsExcluded = []
Arguments = opt.arguments
#-------------------------------------------------------------------------------
## Hide/Show the private directory
#
#-------------------------------------------------------------------------------
def HidePrivateDir():
if os.path.isdir( "../private" ):
os.rename( "../private", "../private.stash" )
def ShowPrivateDir():
if os.path.isdir( "../private.stash" ):
os.rename( "../private.stash", "../private" )
#-------------------------------------------------------------------------------
## Export environment variables for "ut_runner"
#
#-------------------------------------------------------------------------------
def ExportEnvVariables():
global ProjectDir
global WorkDir
global MyEnviron # my environment variables; can be independently used later
# In older versions of subprocess module, 'env=None' argument is not provided
MyEnviron = os.environ.copy()
MyEnviron[ 'TESTSRC' ] = ".."
MyEnviron[ 'TESTTMP' ] = WorkDir
if System == "Darwin":
MyEnviron[ 'DYLD_LIBRARY_PATH' ] = "%s:%s/db_plugins" % (ProjectDir, ProjectDir)
for env in [ 'TESTSRC', 'TESTTMP', 'DYLD_LIBRARY_PATH' ]:
os.environ[env] = MyEnviron[env]
else:
MyEnviron[ 'LD_LIBRARY_PATH' ] = "%s:%s/db_plugins" % (ProjectDir, ProjectDir)
for env in [ 'TESTSRC', 'TESTTMP', 'LD_LIBRARY_PATH' ]:
os.environ[env] = MyEnviron[env]
#-------------------------------------------------------------------------------
## Run the tester
#
# @param[in] command command string to run
# @param[in] logfile log file name
#-------------------------------------------------------------------------------
def RunTester( command, logfile="" ):
if six.PY3:
proc = subprocess.Popen( command.split(), stdout=subprocess.PIPE, \
stderr=subprocess.STDOUT, \
universal_newlines=True )
else:
proc = subprocess.Popen( command.split(), stdout=subprocess.PIPE, \
stderr=subprocess.STDOUT )
if not logfile == "":
with proc.stdout, open( logfile, 'w' ) as file:
for line in proc.stdout:
sys.stdout.write(line)
file.write(line)
proc.wait()
else:
with proc.stdout:
for line in proc.stdout:
sys.stdout.write(line)
proc.wait()
#-------------------------------------------------------------------------------
# Main function
#-------------------------------------------------------------------------------
def Main():
#-------------------------------------------------------
# [1] Initialize
#-------------------------------------------------------
SetGlobals()
ParseCommandLineArguments()
ExportEnvVariables()
#-------------------------------------------------------
# [2] Print the runner's usage
#-------------------------------------------------------
if RunnerUsage:
command = './ut_runner --help-all'
RunTester( command )
quit()
#-------------------------------------------------------
# [3] Run the unit tester
#-------------------------------------------------------
if not Run:
print( "! pass <-r|--run> option to run" )
print(Usage)
quit()
command = './ut_runner'
if ContinueOnError:
command += " -c"
for item in TestsExcluded:
command += ' -x %s' % item
if not Arguments == "":
command += " %s" % Arguments
print( "" )
print( "### Dumping the log to <%s>" % LogFile )
print( "------------------------------------------------------------" )
print( " Git SHA1 = %s" % GitSHA1 )
print( " Time stamp = %s" % TimeStamp )
print( "------------------------------------------------------------" )
sleep( 1.0 )
HidePrivateDir()
RunTester( command, logfile=LogFile )
ShowPrivateDir()
#===================================================================================
if __name__ == "__main__":
Main()
#---------------
# End of file
#---------------

View File

@ -179,8 +179,8 @@ def SetGlobals():
# The package directory name should look like: # The package directory name should look like:
# * ST-qt5MP.pkg.macos-Catalina-release-RsysPsys --- (1) # * ST-qt5MP.pkg.macos-Catalina-release-RsysPsys --- (1)
# * LW-qt5Ana3.pkg.macos-Catalina-release-Rana3Pana3 # * LW-qt5Ana3.pkg.macos-Catalina-release-Rana3Pana3
# * LW-qt5Brew.pkg.macos-Catalina-release-Rhb26Phb37 # * LW-qt5Brew.pkg.macos-Catalina-release-Rhb26Phb38
# * LW-qt5MP.pkg.macos-Catalina-release-Rmp26Pmp37 # * LW-qt5MP.pkg.macos-Catalina-release-Rmp26Pmp38
# #
# Generated DMG will be, for example, # Generated DMG will be, for example,
# (1) ---> ST-klayout-0.26.1-macOS-Catalina-1-qt5MP-RsysPsys.dmg # (1) ---> ST-klayout-0.26.1-macOS-Catalina-1-qt5MP-RsysPsys.dmg
@ -221,9 +221,9 @@ def CheckPkgDirectory():
# #
# * ST-qt5MP.pkg.macos-Catalina-release-RsysPsys # * ST-qt5MP.pkg.macos-Catalina-release-RsysPsys
# * LW-qt5Ana3.pkg.macos-Catalina-release-Rana3Pana3 # * LW-qt5Ana3.pkg.macos-Catalina-release-Rana3Pana3
# * LW-qt5Brew.pkg.macos-Catalina-release-Rhb27Phb37 # * LW-qt5Brew.pkg.macos-Catalina-release-Rhb27Phb38
# * HW-qt5Brew.pkg.macos-Catalina-release-RsysPhb37 # * HW-qt5Brew.pkg.macos-Catalina-release-RsysPhb38
# * EX-qt5MP.pkg.macos-Catalina-release-Rmp26Pmp37 # * EX-qt5MP.pkg.macos-Catalina-release-Rmp26Pmp38
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
patQRP = u'(ST|LW|HW|EX)([-])(qt5[0-9A-Za-z]+)([.]pkg[.])([A-Za-z]+[-][A-Za-z]+[-]release[-])([0-9A-Za-z]+)' patQRP = u'(ST|LW|HW|EX)([-])(qt5[0-9A-Za-z]+)([.]pkg[.])([A-Za-z]+[-][A-Za-z]+[-]release[-])([0-9A-Za-z]+)'
regQRP = re.compile(patQRP) regQRP = re.compile(patQRP)
@ -243,12 +243,12 @@ def CheckPkgDirectory():
LatestOSMacPorts = Platform == LatestOS LatestOSMacPorts = Platform == LatestOS
LatestOSMacPorts &= PackagePrefix == "LW" LatestOSMacPorts &= PackagePrefix == "LW"
LatestOSMacPorts &= QtIdentification == "qt5MP" LatestOSMacPorts &= QtIdentification == "qt5MP"
LatestOSMacPorts &= RubyPythonID == "Rmp26Pmp37" LatestOSMacPorts &= RubyPythonID == "Rmp26Pmp38"
LatestOSHomebrew = Platform == LatestOS LatestOSHomebrew = Platform == LatestOS
LatestOSHomebrew &= PackagePrefix == "LW" LatestOSHomebrew &= PackagePrefix == "LW"
LatestOSHomebrew &= QtIdentification == "qt5Brew" LatestOSHomebrew &= QtIdentification == "qt5Brew"
LatestOSHomebrew &= RubyPythonID == "Rhb27Phb37" LatestOSHomebrew &= RubyPythonID == "Rhb27Phb38"
LatestOSAnaconda3 = Platform == LatestOS LatestOSAnaconda3 = Platform == LatestOS
LatestOSAnaconda3 &= PackagePrefix == "LW" LatestOSAnaconda3 &= PackagePrefix == "LW"