From 34fc3571305e54cbf127bd9da671cca90e5a9b09 Mon Sep 17 00:00:00 2001 From: Kazunari Sekigawa Date: Fri, 2 Dec 2022 06:38:45 +0900 Subject: [PATCH 1/2] Update the Ruby version in MacPorts --- macbuild/build4mac_env.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macbuild/build4mac_env.py b/macbuild/build4mac_env.py index 35f40a072..06cd5e733 100755 --- a/macbuild/build4mac_env.py +++ b/macbuild/build4mac_env.py @@ -178,7 +178,7 @@ RubyMonterey = { 'exe': '/System/Library/Frameworks/Ruby.framework/Versions/ # install with 'sudo port install ruby31' # [Key Type Name] = 'MP31' Ruby31MacPorts = { 'exe': '/opt/local/bin/ruby3.1', - 'inc': '/opt/local/include/ruby-3.1.2', + 'inc': '/opt/local/include/ruby-3.1.3', 'lib': '/opt/local/lib/libruby.3.1.dylib' } From 2e3bc28a696b9abc974369a7469276b310cc8e19 Mon Sep 17 00:00:00 2001 From: Kazunari Sekigawa Date: Sat, 17 Dec 2022 13:42:08 +0900 Subject: [PATCH 2/2] Made changes to build KLayout 0.28~: 1) Set the CPATH environment variable for including required to build the pymod 2) Not to use Python 2.7 on Catalina (nightlybuild) 3) Resolve the library dependency of Python in Homebrew 4) Add a handy tool to setup the standardized directory structures for Homebrew Python 3.x --- macbuild/build4mac.py | 108 +++++++++++++++++------ macbuild/build4mac_util.py | 136 ++++++++++++++++++++++++----- macbuild/nightlyBuild.py | 2 +- macbuild/python3HB.py | 174 +++++++++++++++++++++++++++++++++++++ 4 files changed, 369 insertions(+), 51 deletions(-) create mode 100755 macbuild/python3HB.py diff --git a/macbuild/build4mac.py b/macbuild/build4mac.py index 6996a7ce0..e9fd7e1bb 100755 --- a/macbuild/build4mac.py +++ b/macbuild/build4mac.py @@ -875,9 +875,29 @@ def Build_pymod(parameters): return 0 #-------------------------------------------------------------------- - # [2] Get the new directory names (dictionary) for "dist" + # [2] Get the new directory names (dictionary) for "dist" and + # set the CPATH environment variable for including + # required to build the pymod of 0.28 or later #-------------------------------------------------------------------- PymodDistDir = parameters['pymod_dist'] + # Using MacPorts + if PymodDistDir[ModulePython] == 'dist-MP3': + addIncPath = "/opt/local/include" + # Using Homebrew + elif PymodDistDir[ModulePython] == 'dist-HB3': + addIncPath = "%s/include" % DefaultHomebrewRoot # defined in "build4mac_env.py" + # Using Anaconda3 + elif PymodDistDir[ModulePython] == 'dist-ana3': + addIncPath = "/Applications/anaconda3/include" + else: + addIncPath = "" + if not addIncPath == "": + try: + cpath = os.environ['CPATH'] + except KeyError: + os.environ['CPATH'] = addIncPath + else: + os.environ['CPATH'] = "%s:%s" % (addIncPath, cpath) #-------------------------------------------------------------------- # [3] Set different command line parameters for building @@ -1491,6 +1511,36 @@ def Deploy_Binaries_For_Bundle(config, parameters): # in "/usr/local/opt/python@3.8/lib/" # Python.framework -> ../Frameworks/Python.framework/ <=== this symbolic was needed # pkgconfig/ + # + # Use the "python3HB.py" tool to make different symbolic links [*] including the above one. + # Catalina0{kazzz-s} lib (1)% pwd + # /usr/local/opt/python@3.8/lib + # Catalina0{kazzz-s} lib (2)% ll + # total 0 + # drwxr-xr-x 4 kazzz-s admin 128 12 16 21:40 . + # drwxr-xr-x 13 kazzz-s admin 416 12 12 23:08 .. + # [*] lrwxr-xr-x 1 kazzz-s admin 31 12 16 21:40 Python.framework -> ../Frameworks/Python.framework/ + # drwxr-xr-x 4 kazzz-s admin 128 12 12 23:08 pkgconfig + # + # Catalina0{kazzz-s} Python.framework (3)% pwd + # /usr/local/opt/python@3.8/Frameworks/Python.framework/Versions + # Catalina0{kazzz-s} Versions (4)% ll + # total 0 + # drwxr-xr-x 4 kazzz-s admin 128 12 16 21:40 . + # drwxr-xr-x 6 kazzz-s admin 192 12 16 21:40 .. + # drwxr-xr-x 9 kazzz-s admin 288 12 12 23:08 3.8 + # [*] lrwxr-xr-x 1 kazzz-s admin 4 12 16 21:40 Current -> 3.8/ + # + # Catalina0{kazzz-s} Python.framework (5)% pwd + # /usr/local/opt/python@3.8/Frameworks/Python.framework + # Catalina0{kazzz-s} Python.framework (6)% ll + # total 0 + # drwxr-xr-x 6 kazzz-s admin 192 12 16 21:40 . + # drwxr-xr-x 3 kazzz-s admin 96 12 12 23:07 .. + # [*] lrwxr-xr-x 1 kazzz-s admin 25 12 16 21:40 Headers -> Versions/Current/Headers/ + # [*] lrwxr-xr-x 1 kazzz-s admin 23 12 16 21:40 Python -> Versions/Current/Python + # [*] lrwxr-xr-x 1 kazzz-s admin 27 12 16 21:40 Resources -> Versions/Current/Resources/ + # drwxr-xr-x 4 kazzz-s admin 128 12 16 21:40 Versions #----------------------------------------------------------------------------------------------- deploymentPython38HB = (ModulePython == 'Python38Brew') deploymentPythonAutoHB = (ModulePython == 'PythonAutoBrew') @@ -1550,43 +1600,49 @@ def Deploy_Binaries_For_Bundle(config, parameters): 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.1] Patching Python Framework" ) + print( " [9.2] Relinking dylib dependencies inside Python.framework" ) + print( " [9.2.1] Patching Python Framework" ) depdict = WalkFrameworkPaths( pythonFrameworkPath ) appPythonFrameworkPath = '@executable_path/../Frameworks/Python.framework/' - PerformChanges(depdict, [(HBPythonFrameworkPath, appPythonFrameworkPath, False)], bundleExecPathAbs) + PerformChanges( depdict, [(HBPythonFrameworkPath, appPythonFrameworkPath, False)], bundleExecPathAbs ) - print(" [9.2.2] Patching /usr/local/opt/ libs") - usrLocalPath = '/usr/local/opt/' + print( " [9.2.2] Patching 'Python' itself in Python Framework" ) + filterreg = r'\t+%s/(opt|Cellar)' % DefaultHomebrewRoot + Patch_Python_In_PythonFramework( pythonFrameworkPath, filter_regex=filterreg ) + + print( " [9.2.3] Patching %s/opt/ libs" % DefaultHomebrewRoot ) # eg. DefaultHomebrewRoot == "/usr/local" + usrLocalPath = '%s/opt/' % DefaultHomebrewRoot appUsrLocalPath = '@executable_path/../Frameworks/' - replacePairs = [(usrLocalPath, appUsrLocalPath, True)] - depdict = WalkFrameworkPaths(pythonFrameworkPath, search_path_filter=r'\t+/usr/local/(opt|Cellar)') - PerformChanges(depdict, replacePairs, bundleExecPathAbs) + replacePairs = [ (usrLocalPath, appUsrLocalPath, True) ] + filterreg = r'\t+%s/(opt|Cellar)' % DefaultHomebrewRoot + depdict = WalkFrameworkPaths( pythonFrameworkPath, search_path_filter=filterreg ) + PerformChanges( depdict, replacePairs, bundleExecPathAbs ) - print(" [9.2.3] Patching openssl@1.1, gdbm, readline, sqlite, xz") - usrLocalPath = '/usr/local/opt' + print( " [9.2.4] Patching openssl@1.1, gdbm, readline, sqlite, xz" ) + usrLocalPath = '%s/opt/' % DefaultHomebrewRoot appUsrLocalPath = '@executable_path/../Frameworks/' - replacePairs = [(usrLocalPath, appUsrLocalPath, True)] - replacePairs.extend([(openssl_version, '@executable_path/../Frameworks/openssl@1.1', True) - for openssl_version in glob.glob('/usr/local/Cellar/openssl@1.1/*')]) - depdict = WalkFrameworkPaths([pythonFrameworkPath + '/../openssl@1.1', - pythonFrameworkPath + '/../gdbm', - pythonFrameworkPath + '/../readline', - pythonFrameworkPath + '/../sqlite', - pythonFrameworkPath + '/../xz'], search_path_filter=r'\t+/usr/local/(opt|Cellar)') + replacePairs = [ (usrLocalPath, appUsrLocalPath, True) ] + replacePairs.extend( [ (openssl_version, '@executable_path/../Frameworks/openssl@1.1', True) + for openssl_version in glob.glob( '%s/Cellar/openssl@1.1/*' % DefaultHomebrewRoot ) ] ) + filterreg = r'\t+%s/(opt|Cellar)' % DefaultHomebrewRoot + depdict = WalkFrameworkPaths( [pythonFrameworkPath + '/../openssl@1.1', + pythonFrameworkPath + '/../gdbm', + pythonFrameworkPath + '/../readline', + pythonFrameworkPath + '/../sqlite', + pythonFrameworkPath + '/../xz'], search_path_filter=filterreg ) - PerformChanges(depdict, replacePairs, bundleExecPathAbs) + PerformChanges( depdict, replacePairs, bundleExecPathAbs ) - print(" [9.3] Relinking dylib dependencies for klayout") + print( " [9.3] Relinking dylib dependencies for klayout" ) klayoutPath = bundleExecPathAbs - depdict = WalkFrameworkPaths(klayoutPath, filter_regex=r'klayout$') - PerformChanges(depdict, [(HBPythonFrameworkPath, appPythonFrameworkPath, False)], bundleExecPathAbs) + depdict = WalkFrameworkPaths( klayoutPath, filter_regex=r'klayout$' ) + PerformChanges( depdict, [(HBPythonFrameworkPath, appPythonFrameworkPath, False)], bundleExecPathAbs ) libKlayoutPath = bundleExecPathAbs + '../Frameworks' - depdict = WalkFrameworkPaths(libKlayoutPath, filter_regex=r'libklayout') - PerformChanges(depdict, [(HBPythonFrameworkPath, appPythonFrameworkPath, False)], bundleExecPathAbs) + depdict = WalkFrameworkPaths( libKlayoutPath, filter_regex=r'libklayout' ) + PerformChanges( depdict, [(HBPythonFrameworkPath, 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/%s/lib/python%s/site.py" % (pythonFrameworkPath, pythonHBVer, pythonHBVer) with open(site_module, 'r') as site: buf = site.readlines() diff --git a/macbuild/build4mac_util.py b/macbuild/build4mac_util.py index 42f016f0d..022805ffb 100755 --- a/macbuild/build4mac_util.py +++ b/macbuild/build4mac_util.py @@ -61,8 +61,8 @@ def DecomposeLibraryDependency( depstr ): #---------------------------------------------------------------------------------------- def PrintLibraryDependencyDictionary( depdic, pathdic, namedic ): keys = depdic.keys() - print("") - print("##### Contents of <%s> #####:" % namedic ) + print( "" ) + print( "##### Contents of <%s> #####:" % namedic ) for key in keys: supporters = depdic[key] keyName = os.path.basename(key) @@ -158,8 +158,9 @@ def SetChangeLibIdentificationName( executable, relativedir ): for lib in deplibs: #----------------------------------------------------------- # [1] Set the identification names for the library + # $ install_name_tool [-id name] input #----------------------------------------------------------- - nameOld = "klayout.app/Contents/Frameworks/%s" % lib + nameOld = "klayout.app/Contents/Frameworks/%s" % lib # input file nameNew = "@executable_path/%s/%s" % ( relativedir, lib ) command = "%s %s %s" % ( cmdNameId, nameNew, nameOld ) if subprocess.call( command, shell=True ) != 0: @@ -169,6 +170,7 @@ def SetChangeLibIdentificationName( executable, relativedir ): #----------------------------------------------------------- # [2] Make the application aware of the new identification + # $ install_name_tool [-change old new] input #----------------------------------------------------------- nameOld = "%s" % lib nameNew = "@executable_path/%s/%s" % ( relativedir, lib ) @@ -182,6 +184,8 @@ def SetChangeLibIdentificationName( executable, relativedir ): #---------------------------------------------------------------------------------------- ## To make a library dependency dictionary by recursively walk down the lib hierarchy +# DefaultHomebrewRoot = '/opt/homebrew' "arm64" Apple Silicon +# DefaultHomebrewRoot = '/usr/local' "x86_64" Intel Mac # # @param[in] dylibPath: dylib path # @param[in] depth: hierarchy depth (< 5) @@ -189,7 +193,7 @@ def SetChangeLibIdentificationName( executable, relativedir ): # # @return a dictionary #---------------------------------------------------------------------------------------- -def WalkLibDependencyTree( dylibPath, depth=0, filter_regex=r'\t+/usr/local/opt' ): +def WalkLibDependencyTree( dylibPath, depth=0, filter_regex=r'\t+%s/opt' % DefaultHomebrewRoot ): otoolCm = 'otool -L %s | grep -E "%s"' % (dylibPath, filter_regex) otoolOut = os.popen( otoolCm ).read() exedepdic = DecomposeLibraryDependency( dylibPath + ":\n" + otoolOut ) @@ -201,7 +205,7 @@ def WalkLibDependencyTree( dylibPath, depth=0, filter_regex=r'\t+/usr/local/opt' for idx, lib in enumerate(deplibs): lib = str(lib) if lib != list(keys)[0]: - deplibs[idx] = WalkLibDependencyTree(lib, depth+1, filter_regex) + deplibs[idx] = WalkLibDependencyTree( lib, depth+1, filter_regex ) if depth == 0: return deplibs return exedepdic @@ -210,6 +214,8 @@ def WalkLibDependencyTree( dylibPath, depth=0, filter_regex=r'\t+/usr/local/opt' #---------------------------------------------------------------------------------------- ## To make a library dependency dictionary by recursively walk down the Framework +# DefaultHomebrewRoot = '/opt/homebrew' "arm64" Apple Silicon +# DefaultHomebrewRoot = '/usr/local' "x86_64" Intel Mac # # @param[in] frameworkPaths: Framework path # @param[in] filter_regex: filter regular expression @@ -218,9 +224,8 @@ def WalkLibDependencyTree( dylibPath, depth=0, filter_regex=r'\t+/usr/local/opt' # @return a dictionary #---------------------------------------------------------------------------------------- def WalkFrameworkPaths( frameworkPaths, filter_regex=r'\.(so|dylib)$', - search_path_filter=r'\t+/usr/local/opt' ): - - if isinstance(frameworkPaths, str): + search_path_filter=r'\t+%s/opt' % DefaultHomebrewRoot ): + if isinstance( frameworkPaths, str ): frameworkPathsIter = [frameworkPaths] else: frameworkPathsIter = frameworkPaths @@ -229,14 +234,12 @@ def WalkFrameworkPaths( frameworkPaths, filter_regex=r'\.(so|dylib)$', for frameworkPath in frameworkPathsIter: # print("Calling:", 'find %s -type f | grep -E "%s"' % (frameworkPath, filter_regex)) - find_grep_results = os.popen('find %s -type f | grep -E "%s"' % (frameworkPath, filter_regex)).read().split('\n') - framework_files = filter(lambda x: x != '', - map(lambda x: x.strip(), - find_grep_results)) + find_grep_results = os.popen( 'find %s -type f | grep -E "%s"' % (frameworkPath, filter_regex) ).read().split('\n') + framework_files = filter( lambda x: x != '', map(lambda x: x.strip(), find_grep_results) ) dependency_dict[frameworkPath] = list() for idx, file in enumerate(framework_files): - dict_file = {file: WalkLibDependencyTree(file, filter_regex=search_path_filter)} + dict_file = { file: WalkLibDependencyTree( file, filter_regex=search_path_filter ) } dependency_dict[frameworkPath].append(dict_file) return dependency_dict @@ -262,17 +265,17 @@ def WalkDictTree( dependencyDict, visited_files ): if deplib not in visited_files: visited_files.append(deplib) elif isinstance(deplib, dict): - dependency_list.append(next(iter(deplib))) - libNameChanges.extend(WalkDictTree(deplib, visited_files)) + dependency_list.append( next(iter(deplib)) ) + libNameChanges.extend( WalkDictTree(deplib, visited_files) ) else: #raise RuntimeError("Unexpected value: %s" % deplib) pass else: - raise RuntimeError("Unexpected value: %s" % dependencies) + raise RuntimeError( "Unexpected value: %s" % dependencies ) if len(dependency_list) > 0: - libNameChanges.append((lib, dependency_list)) + libNameChanges.append( (lib, dependency_list) ) else: - libNameChanges.append((lib, )) + libNameChanges.append( (lib, ) ) visited_files.append(lib) return libNameChanges @@ -300,7 +303,7 @@ def FindFramework( path, root_path ): #---------------------------------------------------------------------------------------- def ResolveExecutablePath( path, 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 #---------------------------------------------------------------------------------------- @@ -314,11 +317,11 @@ def ResolveExecutablePath( path, executable_path ): # * ('lib.dylib',) #---------------------------------------------------------------------------------------- def DetectChanges(frameworkDependencyDict): - visited_files = list() + visited_files = list() libNameChanges = list() for framework, libraries in frameworkDependencyDict.items(): for libraryDict in libraries: - libNameChanges.extend(WalkDictTree(libraryDict, visited_files)) + libNameChanges.extend( WalkDictTree(libraryDict, visited_files) ) return libNameChanges #---------------------------------------------------------------------------------------- @@ -456,11 +459,96 @@ def GenerateInfoPlist( keydic, templfile ): t = string.Template(template) s = t.substitute( EXECUTABLE = val_exe, - ICONFILE = val_icon, - BUNDLENAME = val_bname, - VERSION = val_ver) + ICONFILE = val_icon, + BUNDLENAME = val_bname, + VERSION = val_ver ) return s +#---------------------------------------------------------------------------------------- +## To patch 'Python' itself in Python Framework +# +# A relatively new Python 3.x may depend on other dylib(s) under /usr/local/opt/. +# This was first found in: +# Catalina0{kazzz-s} klayout (1)% python3 +# Python 3.8.16 (default, Dec 12 2022, 14:07:09) +# [Clang 12.0.0 (clang-1200.0.32.29)] on darwin +# Type "help", "copyright", "credits" or "license" for more information. +# >>> +# +# Catalina0{kazzz-s} Current (2)% pwd +# /usr/local/opt/python@3.8/Frameworks/Python.framework/Versions/Current +# +# Catalina0{kazzz-s} Current (3)% ll +# total 5024 +# drwxr-xr-x 7 kazzz-s admin 224 Dec 16 23:02 . +# drwxr-xr-x 4 kazzz-s admin 128 Dec 16 21:40 .. +# lrwxr-xr-x 1 kazzz-s admin 17 Dec 17 08:46 Headers -> include/python3.8 +# -rwxr-xr-x 1 kazzz-s admin 2571960 Dec 12 23:08 Python +# drwxr-xr-x 3 kazzz-s admin 96 Dec 12 23:08 include +# drwxr-xr-x 5 kazzz-s admin 160 Dec 12 23:08 lib +# drwxr-xr-x 3 kazzz-s admin 96 Dec 12 23:08 share +# +# Catalina0{kazzz-s} Current (4)% otool -L Python +# Python: +# /usr/local/opt/python@3.8/Frameworks/Python.framework/Versions/3.8/Python (compatibility version 3.8.0, current version 3.8.0) +# /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1675.129.0) +# [*] /usr/local/opt/gettext/lib/libintl.8.dylib (compatibility version 12.0.0, current version 12.0.0) +# /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1281.100.1) +# +# This library dependency [*] has to be changed as shown below: +# Catalina0{kazzz-s} Current (5)% install_name_tool -change \ +# /usr/local/opt/gettext/lib/libintl.8.dylib \ +# @executable_path/../Frameworks/libintl.8.dylib \ +# Python +# +# Catalina0{kazzz-s} Current (6)% otool -L Python +# Python: +# /usr/local/opt/python@3.8/Frameworks/Python.framework/Versions/3.8/Python (compatibility version 3.8.0, current version 3.8.0) +# /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1675.129.0) +# [*] @executable_path/../Frameworks/libintl.8.dylib (compatibility version 12.0.0, current version 12.0.0) +# /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1281.100.1) +# +# +# @param[in] pythonFrameworkPath: Python Framework path +# @param[in] filter_regex: filter regular expression +# +# @return 0 on succcess; non-zero on failure +#---------------------------------------------------------------------------------------- +def Patch_Python_In_PythonFramework( pythonFrameworkPath, filter_regex=r'\t+%s/opt' % DefaultHomebrewRoot ): + #---------------------------------------------------------------------- + # [1] Get Python's dependency + #---------------------------------------------------------------------- + target = "%s/Python" % pythonFrameworkPath + otoolCm = 'otool -L %s | grep -E "%s"' % (target, filter_regex) + otoolOut = os.popen( otoolCm ).read() + exedepdic = DecomposeLibraryDependency( target + ":\n" + otoolOut ) + keys = exedepdic.keys() + deplibs = exedepdic[ list(keys)[0] ] + # print(deplibs) + # [ '/usr/local/opt/python@3.8/Frameworks/Python.framework/Versions/3.8/Python', + # '/usr/local/opt/gettext/lib/libintl.8.dylib' + # ] + + #---------------------------------------------------------------------- + # [2] Change the library name + #---------------------------------------------------------------------- + cmdNameChg = XcodeToolChain['nameCH'] + + for lib in deplibs: + basename = os.path.basename(lib) + if basename == "Python": # self + continue + else: + nameOld = "%s" % lib + nameNew = "@executable_path/../Frameworks/%s" % basename + command = "%s %s %s %s" % ( cmdNameChg, nameOld, nameNew, target ) + if subprocess.call( command, shell=True ) != 0: + msg = "!!! Failed to make 'Python' aware of the new identification name <%s> of supporter <%s> !!!" + print( msg % (nameNew, lib), file=sys.stderr ) + return 1 + # for-lib + return 0 + #---------------- # End of File #---------------- diff --git a/macbuild/nightlyBuild.py b/macbuild/nightlyBuild.py index 7fa596637..00a2b5dcb 100755 --- a/macbuild/nightlyBuild.py +++ b/macbuild/nightlyBuild.py @@ -200,7 +200,7 @@ def Parse_CommandLine_Arguments(): if platform in [ "Monterey", "BigSur" ]: targetopt = "1,2,3,4" elif platform in ["Catalina"]: - targetopt = "0,1,2,3,4" + targetopt = "1,2,3,4" else: targetopt = "" diff --git a/macbuild/python3HB.py b/macbuild/python3HB.py new file mode 100755 index 000000000..882328ec4 --- /dev/null +++ b/macbuild/python3HB.py @@ -0,0 +1,174 @@ +#! /usr/bin/env python3 +# -*- coding: utf-8 -*- + +#============================================================================== +# File: macbuild/python3HB.py +# +# Descriptions: A handy tool to setup the standardized directory structures +# for Homebrew's Python 3.x +#============================================================================== +import os +import sys +import platform +import optparse + +#------------------------------------------------------------------------------ +# Set global variables +#------------------------------------------------------------------------------ +def SetGlobals(): + global DefaultHomebrewRoot + global Usage + + (System, Node, Release, MacVersion, Machine, Processor) = platform.uname() + if Machine == "arm64": # Apple Silicon! + DefaultHomebrewRoot = '/opt/homebrew' + else: + DefaultHomebrewRoot = '/usr/local' + del System, Node, Release, MacVersion, Machine, Processor + + Usage = "\n" + Usage += "-------------------------------------------------------------------------------------\n" + Usage += "<< Usage of 'python3HB.py' >>\n" + Usage += " to setup the standardized directory structures for Homebrew's Python 3.x on Mac\n" + Usage += "\n" + Usage += " option & argument : descriptions | default value\n" + Usage += " -----------------------------------------------------------+---------------\n" + Usage += " <-v|--version >: in ['3.8', '3.9', '3.10', '3.11'] | ''\n" + Usage += " [-u|-unlink] : unlink only | disabled\n" + Usage += " [-?|--?] : print this usage and exit | disabled\n" + Usage += "--------------------------------------------------------------+----------------------\n" + +#------------------------------------------------------------------------------ +# Parse the command line arguments +#------------------------------------------------------------------------------ +def Parse_CLI_Args(): + global Version + global UnlinkOnly + + p = optparse.OptionParser( usage=Usage ) + + p.add_option( '-v', '--version', + dest='version', + help="python3 version=['3.8', '3.9', '3.10', '3.11']" ) + + p.add_option( '-u', '--unlink', + action='store_true', + dest='unlink', + default=False, + help='unlink only' ) + + p.add_option( '-?', '--??', + action='store_true', + dest='checkusage', + default=False, + help='check usage' ) + + p.set_defaults( version = "", + unlink = False, + checkusage = False ) + + opt, args = p.parse_args() + if (opt.checkusage): + print(Usage) + sys.exit(0) + + Version = opt.version + UnlinkOnly = opt.unlink + if not Version in [ '3.8', '3.9', '3.10', '3.11' ]: + print( "! Unsupported Python 3 version <%s>" % Version ) + print(Usage) + sys.exit(0) + +#------------------------------------------------------------------------------ +# Set the directory structures +#------------------------------------------------------------------------------ +def SetDirectoryStructures(): + #---------------------------------------------------------- + # [1] Check the root directory of python@${Version} + #---------------------------------------------------------- + root = "%s/opt/python@%s" % (DefaultHomebrewRoot, Version) + if not os.path.isdir(root): + print( "! Found no such a directory <%s>" % root ) + sys.exit(0) + + #---------------------------------------------------------- + # [2] Go to "lib/" and make + # Python.framework -> ../Frameworks/Python.framework/ + #---------------------------------------------------------- + os.chdir( root ) + os.chdir( "lib/" ) + try: + os.remove( "Python.framework" ) + except FileNotFoundError: + pass + if not UnlinkOnly: + os.symlink( "../Frameworks/Python.framework/", "Python.framework" ) + + #---------------------------------------------------------- + # [3] Go to "bin/" and make + # ./python${version} -> python3 + # ./pip${version} -> pip3 + #---------------------------------------------------------- + os.chdir( root ) + os.chdir( "bin/" ) + try: + os.remove( "python3" ) + os.remove( "pip3" ) + except FileNotFoundError: + pass + if not UnlinkOnly: + os.symlink( "./python%s" % Version, "python3" ) + os.symlink( "./pip%s" % Version, "pip3" ) + + #---------------------------------------------------------- + # [4] Go to "Frameworks/Python.framework/" and delete + # three symbolic links + #---------------------------------------------------------- + os.chdir( root ) + os.chdir( "Frameworks/Python.framework/" ) + try: + os.remove( "Headers" ) + os.remove( "Resources" ) + os.remove( "Python" ) + except FileNotFoundError: + pass + + #---------------------------------------------------------- + # [5] Go to "Versions/" and make + # Current -> ${Version}/ + #---------------------------------------------------------- + os.chdir( root ) + os.chdir( "Frameworks/Python.framework/Versions/" ) + try: + os.remove( "Current" ) + except FileNotFoundError: + pass + if not UnlinkOnly: + os.symlink( "%s/" % Version, "Current" ) + + #---------------------------------------------------------- + # [6] Go to "Frameworks/Python.framework/" and make + # three symbolic links + #---------------------------------------------------------- + if not UnlinkOnly: + os.chdir( root ) + os.chdir( "Frameworks/Python.framework/" ) + os.symlink( "Versions/Current/Headers/", "Headers" ) + os.symlink( "Versions/Current/Resources/", "Resources" ) + os.symlink( "Versions/Current/Python", "Python" ) + +#------------------------------------------------------------------------------ +# The main function +#------------------------------------------------------------------------------ +def Main(): + SetGlobals() + Parse_CLI_Args() + SetDirectoryStructures() + +#=================================================================================== +if __name__ == "__main__": + Main() + +#--------------- +# End of file +#---------------