Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for z/OS compilers; Fixes #215 #216

Merged
merged 2 commits into from
Feb 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions distutils/ccompiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -1060,6 +1060,7 @@ def mkpath(self, name, mode=0o777):
# on a cygwin built python we can use gcc like an ordinary UNIXish
# compiler
('cygwin.*', 'unix'),
('zos', 'zos'),
# OS name mappings
('posix', 'unix'),
('nt', 'msvc'),
Expand Down Expand Up @@ -1107,6 +1108,7 @@ def get_default_compiler(osname=None, platform=None):
"Mingw32 port of GNU C Compiler for Win32",
),
'bcpp': ('bcppcompiler', 'BCPPCompiler', "Borland C++ Compiler"),
'zos': ('zosccompiler', 'zOSCCompiler', 'IBM XL C/C++ Compilers'),
}


Expand Down
36 changes: 26 additions & 10 deletions distutils/command/build_ext.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,31 @@ def initialize_options(self):
self.user = None
self.parallel = None

@staticmethod
def _python_lib_dir(sysconfig):
"""
Resolve Python's library directory for building extensions
that rely on a shared Python library.

See python/cpython#44264 and python/cpython#48686
"""
if not sysconfig.get_config_var('Py_ENABLE_SHARED'):
return

if sysconfig.python_build:
yield '.'
return

if sys.platform == 'zos':
# On z/OS, a user is not required to install Python to
# a predetermined path, but can use Python portably
installed_dir = sysconfig.get_config_var('base')
lib_dir = sysconfig.get_config_var('platlibdir')
yield os.path.join(installed_dir, lib_dir)
else:
# building third party extensions
yield sysconfig.get_config_var('LIBDIR')

def finalize_options(self): # noqa: C901
from distutils import sysconfig

Expand Down Expand Up @@ -231,16 +256,7 @@ def finalize_options(self): # noqa: C901
# building python standard extensions
self.library_dirs.append('.')

# For building extensions with a shared Python library,
# Python's library directory must be appended to library_dirs
# See Issues: #1600860, #4366
if sysconfig.get_config_var('Py_ENABLE_SHARED'):
if not sysconfig.python_build:
# building third party extensions
self.library_dirs.append(sysconfig.get_config_var('LIBDIR'))
else:
# building python standard extensions
self.library_dirs.append('.')
self.library_dirs.extend(self._python_lib_dir(sysconfig))

# The argument parsing will result in self.define being a string, but
# it has to be a list of 2-tuples. All the preprocessor symbols
Expand Down
228 changes: 228 additions & 0 deletions distutils/zosccompiler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
"""distutils.zosccompiler

Contains the selection of the c & c++ compilers on z/OS. There are several
different c compilers on z/OS, all of them are optional, so the correct
one needs to be chosen based on the users input. This is compatible with
the following compilers:

IBM C/C++ For Open Enterprise Languages on z/OS 2.0
IBM Open XL C/C++ 1.1 for z/OS
IBM XL C/C++ V2.4.1 for z/OS 2.4 and 2.5
IBM z/OS XL C/C++
"""

import os
from .unixccompiler import UnixCCompiler
from . import sysconfig
from .errors import DistutilsExecError, CompileError

_cc_args = {
'ibm-openxl': [
'-m64',
'-fvisibility=default',
'-fzos-le-char-mode=ascii',
'-fno-short-enums',
],
'ibm-xlclang': [
'-q64',
'-qexportall',
'-qascii',
'-qstrict',
'-qnocsect',
'-Wa,asa,goff',
'-Wa,xplink',
'-qgonumber',
'-qenum=int',
'-Wc,DLL',
],
'ibm-xlc': [
'-q64',
'-qexportall',
'-qascii',
'-qstrict',
'-qnocsect',
'-Wa,asa,goff',
'-Wa,xplink',
'-qgonumber',
'-qenum=int',
'-Wc,DLL',
'-qlanglvl=extc99',
],
}

_cxx_args = {
'ibm-openxl': [
'-m64',
'-fvisibility=default',
'-fzos-le-char-mode=ascii',
'-fno-short-enums',
],
'ibm-xlclang': [
'-q64',
'-qexportall',
'-qascii',
'-qstrict',
'-qnocsect',
'-Wa,asa,goff',
'-Wa,xplink',
'-qgonumber',
'-qenum=int',
'-Wc,DLL',
],
'ibm-xlc': [
'-q64',
'-qexportall',
'-qascii',
'-qstrict',
'-qnocsect',
'-Wa,asa,goff',
'-Wa,xplink',
'-qgonumber',
'-qenum=int',
'-Wc,DLL',
'-qlanglvl=extended0x',
],
}

_asm_args = {
'ibm-openxl': ['-fasm', '-fno-integrated-as', '-Wa,--ASA', '-Wa,--GOFF'],
'ibm-xlclang': [],
'ibm-xlc': [],
}

_ld_args = {
'ibm-openxl': [],
'ibm-xlclang': ['-Wl,dll', '-q64'],
'ibm-xlc': ['-Wl,dll', '-q64'],
}


# Python on z/OS is built with no compiler specific options in it's CFLAGS.
# But each compiler requires it's own specific options to build successfully,
# though some of the options are common between them
class zOSCCompiler(UnixCCompiler):
src_extensions = ['.c', '.C', '.cc', '.cxx', '.cpp', '.m', '.s']
_cpp_extensions = ['.cc', '.cpp', '.cxx', '.C']
_asm_extensions = ['.s']

def _get_zos_compiler_name(self):
zos_compiler_names = [
os.path.basename(binary)
for envvar in ('CC', 'CXX', 'LDSHARED')
if (binary := os.environ.get(envvar, None))
]
if len(zos_compiler_names) == 0:
return 'ibm-openxl'

zos_compilers = {}
for compiler in (
'ibm-clang',
'ibm-clang64',
'ibm-clang++',
'ibm-clang++64',
'clang',
'clang++',
'clang-14',
):
zos_compilers[compiler] = 'ibm-openxl'

for compiler in ('xlclang', 'xlclang++', 'njsc', 'njsc++'):
zos_compilers[compiler] = 'ibm-xlclang'

for compiler in ('xlc', 'xlC', 'xlc++'):
zos_compilers[compiler] = 'ibm-xlc'

return zos_compilers.get(zos_compiler_names[0], 'ibm-openxl')

def __init__(self, verbose=0, dry_run=0, force=0):
super().__init__(verbose, dry_run, force)
self.zos_compiler = self._get_zos_compiler_name()
sysconfig.customize_compiler(self)

def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
local_args = []
if ext in self._cpp_extensions:
compiler = self.compiler_cxx
local_args.extend(_cxx_args[self.zos_compiler])
elif ext in self._asm_extensions:
compiler = self.compiler_so
local_args.extend(_cc_args[self.zos_compiler])
local_args.extend(_asm_args[self.zos_compiler])
else:
compiler = self.compiler_so
local_args.extend(_cc_args[self.zos_compiler])
local_args.extend(cc_args)

try:
self.spawn(compiler + local_args + [src, '-o', obj] + extra_postargs)
except DistutilsExecError as msg:
raise CompileError(msg)

def runtime_library_dir_option(self, dir):
return '-L' + dir

def link(
self,
target_desc,
objects,
output_filename,
output_dir=None,
libraries=None,
library_dirs=None,
runtime_library_dirs=None,
export_symbols=None,
debug=0,
extra_preargs=None,
extra_postargs=None,
build_temp=None,
target_lang=None,
):
# For a built module to use functions from cpython, it needs to use Pythons
# side deck file. The side deck is located beside the libpython3.xx.so
ldversion = sysconfig.get_config_var('LDVERSION')
if sysconfig.python_build:
side_deck_path = os.path.join(
sysconfig.get_config_var('abs_builddir'),
f'libpython{ldversion}.x',
)
else:
side_deck_path = os.path.join(
sysconfig.get_config_var('installed_base'),
sysconfig.get_config_var('platlibdir'),
f'libpython{ldversion}.x',
)

if os.path.exists(side_deck_path):
if extra_postargs:
extra_postargs.append(side_deck_path)
else:
extra_postargs = [side_deck_path]

# Check and replace libraries included side deck files
if runtime_library_dirs:
for dir in runtime_library_dirs:
for library in libraries[:]:
library_side_deck = os.path.join(dir, f'{library}.x')
if os.path.exists(library_side_deck):
libraries.remove(library)
extra_postargs.append(library_side_deck)
break

# Any required ld args for the given compiler
extra_postargs.extend(_ld_args[self.zos_compiler])

super().link(
target_desc,
objects,
output_filename,
output_dir,
libraries,
library_dirs,
runtime_library_dirs,
export_symbols,
debug,
extra_preargs,
extra_postargs,
build_temp,
target_lang,
)
Loading