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_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' } 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 +#---------------