changeset: 3122:f4b41e077564 bookmark: aix-soname-svr4 tag: tip parent: 3119:440f49fb5ec6 user: Michael Haubenwallner date: Tue May 20 22:45:57 2014 +0200 summary: Emulate SVR4/Linux like SONAME on AIX. diff -r 440f49fb5ec6 -r f4b41e077564 src/engine/SCons/Tool/__init__.py --- a/src/engine/SCons/Tool/__init__.py Sun May 18 19:54:14 2014 -0400 +++ b/src/engine/SCons/Tool/__init__.py Tue May 20 22:45:57 2014 +0200 @@ -256,7 +256,7 @@ if Verbose: print "VersionShLibLinkNames: linkname = ",linkname linknames.append(linkname) - elif platform == 'posix': + elif platform == 'posix' or platform == 'aix': if sys.platform.startswith('openbsd'): # OpenBSD uses x.y shared library versioning numbering convention # and doesn't use symlinks to backwards-compatible libraries @@ -305,7 +305,7 @@ if version: # set the shared library link flags - if platform == 'posix': + if platform == 'posix' or platform == 'aix': shlink_flags += [ '-Wl,-Bsymbolic' ] # OpenBSD doesn't usually use SONAME for libraries if not sys.platform.startswith('openbsd'): @@ -741,7 +741,7 @@ ars = ['ar'] elif str(platform) == 'aix': "prefer AIX Visual Age tools on AIX" - linkers = ['aixlink', 'gnulink'] + linkers = ['aixlink'] # build SONAME enabled shared libs with either gcc or xlc c_compilers = ['aixcc', 'gcc', 'cc'] cxx_compilers = ['aixc++', 'g++', 'c++'] assemblers = ['as', 'gas'] diff -r 440f49fb5ec6 -r f4b41e077564 src/engine/SCons/Tool/aixc++.py --- a/src/engine/SCons/Tool/aixc++.py Sun May 18 19:54:14 2014 -0400 +++ b/src/engine/SCons/Tool/aixc++.py Tue May 20 22:45:57 2014 +0200 @@ -60,6 +60,7 @@ if 'CXX' not in env: env['CXX'] = _cxx + env['CXXVENDOR'] = 'XLC' cplusplus.generate(env) diff -r 440f49fb5ec6 -r f4b41e077564 src/engine/SCons/Tool/aixcc.py --- a/src/engine/SCons/Tool/aixcc.py Sun May 18 19:54:14 2014 -0400 +++ b/src/engine/SCons/Tool/aixcc.py Tue May 20 22:45:57 2014 +0200 @@ -58,6 +58,7 @@ if version: env['CCVERSION'] = version + env['CCVENDOR'] = 'XLC' def exists(env): path, _cc, version = get_xlc(env) diff -r 440f49fb5ec6 -r f4b41e077564 src/engine/SCons/Tool/aixlink.py --- a/src/engine/SCons/Tool/aixlink.py Sun May 18 19:54:14 2014 -0400 +++ b/src/engine/SCons/Tool/aixlink.py Tue May 20 22:45:57 2014 +0200 @@ -34,6 +34,7 @@ import os import os.path +import subprocess import SCons.Util @@ -48,6 +49,133 @@ return '-qtempinc=' + os.path.join(build_dir, 'tempinc') return '' +def smart_rpath(target, source, env, for_signature): + if not env.subst('$RPATH'): + return None # let the -L arguments form the rpath + # need the quotes, probably for issue#1123 + return '"${vWl}-blibpath:${RPATH}:/usr/lib:/lib"' + +class vendor: + def __init__(self, vendordict): + self.vendordict = vendordict + pass + def __call__(self, target, source, env, for_signature = None): + vendor = env.subst('$LINKVENDOR') + result = self.vendordict.get(vendor,'') + return result + pass + +def getBits(target, source, env): + """Determine whether the first source object file this is 32 or 64 bits.""" + with open(str(source[0]), "rb") as s: + magic = s.read(2) + if magic == '\x01\xdf': return 32 + if magic == '\x01\xf7': return 64 + raise SCons.Errors.BuildError( + errstr='Cannot infer bitness, magic='+magic, + node = target, filename = source[0], + exc_info = exc_info, + ) + +def getShrBase(target, source, env, for_signature): + """Get the bits (32 or 64) from the first source object file.""" + #if for_signature: + # return 'shr' + bits = getBits(target, source, env) + if bits == 64: return 'shr_64' + return 'shr' + +def parseLinkflags(flags, env): + """Find the soname in the linker flags, and fix scons-hardcoded flags for AIX linker.""" + + result = { 'soname': '', 'flags': SCons.Util.CLVar() } + longopt = '' + for flag in SCons.Util.CLVar(flags): + if flag.startswith('"'): + flag = flag.strip('"') + if flag.startswith('-Wl,'): + Wl = "${vWl}" + ldflags = flag.split(',')[1:] # "-Wl,-flag1,-flag2" + else: + Wl = '' + ldflags = [ flag ] + for ldflag in ldflags: + ldflag = longopt + ldflag + longopt = '' + if ldflag == '-soname': + longopt = '-soname=' + continue + if ldflag.startswith('-soname='): + result['soname'] = ldflag[8:] + continue + if ldflag == '-Bsymbolic': # hardcoded in Tool/__init__.py + ldflag = '-bsymbolic' # TODO: identify whether ld is GNU + # need the quotes, probably for issue#1123 + result['flags'].append('"' + Wl + ldflag + '"') + return result + +class linkflagsFilter: + def __init__(self, linkflagsvar): + self.linkflagsvar = linkflagsvar + def __call__(self, target, source, env, for_signature): + return parseLinkflags(env.subst('$'+self.linkflagsvar, target=target, source=source), env).get('flags', '') + pass + +def createImportFile_str(importfile, linkflags, target, source, env): + return 'CreateImportFile("%s")' % importfile + +def createImportFile_func(importfile, linkflags, target, source, env): + + shrbase = env.subst('$SHR', target=SCons.Util.CLVar(target), source=SCons.Util.CLVar(source)) + + bits = 32 + if '64' in shrbase: + bits = 64 + + soname = parseLinkflags(linkflags, env).get('soname', target) + + symbols = {} + ignoreable = False + # TODO: support GNU nm; this is native AIX nm + symreader = SCons.Action._subproc(env, + SCons.Util.CLVar("nm -X%d -PCpgl %s" % (bits, source)), + error = 'raise', + stdin = 'devnull', + stderr = subprocess.PIPE, + stdout = subprocess.PIPE) + outlines, errlines = symreader.communicate() + errors = '' + for line in outlines.split('\n'): + if not line or line.startswith('.') or line.endswith(':'): + continue + tokens = line.split() # symname type addr + if len(tokens) < 3: + continue + if tokens[1] not in 'TDBWVZ': + continue + if tokens[1] in 'WVZ': + tokens[0] += ' weak' + symbols[tokens[0]] = None + for line in errlines.split('\n'): + if not line: + continue + if '0654-203' in line: # nm: lib.so[shr.imp]: 0654-203 Specify an XCOFF object module. + ignoreable = True + continue + errors += line + '\n' + if symreader.returncode and (not ignoreable or errors): + # Import Files found as archive members trigger an error + raise SCons.Errors.BuildError( + errstr='Error %d from nm reading symbols from %s:\n%s' % ( symreader.returncode, source, errors ), + node = target, filename = source[0], + ) + + with open(importfile, "wb") as imp: + imp.write('#! %s(%s.o)\n# %d\n' % (soname, shrbase, bits)) + for sym in symbols.keys(): + imp.write(sym + '\n') + pass + def generate(env): """ Add Builders and construction variables for Visual Age linker to @@ -56,13 +184,50 @@ link.generate(env) env['SMARTLINKFLAGS'] = smart_linkflags - env['LINKFLAGS'] = SCons.Util.CLVar('$SMARTLINKFLAGS') - env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -qmkshrobj -qsuppress=1501-218') - env['SHLIBSUFFIX'] = '.a' + env['_RPATH'] = smart_rpath + env['SHR'] = getShrBase + env['EXPSYMFILE'] = '${TARGET}.d/${SHR}.imp' + + env['vLINKFLAGS'] = vendor({ 'XLC': '$SMARTLINKFLAGS -qsuppress=1501-218' }) + env['vWl'] = vendor({ 'GNU': '-Wl,', '': '-Wl,' }) + env['vShared'] = vendor({ 'GNU': '-shared' }) + + # need the quotes, probably for issue#1123 + env['LINKFLAGS'] = '${vLINKFLAGS}' + env['LINKCOM'] = '$LINK "${vWl}-brtl" -o $TARGET $LINKFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' + + env['SHLINKFLAGS'] = "${LINKFLAGS}" + + CreateImportFile = SCons.Action.ActionFactory(createImportFile_func, createImportFile_str) + + env['SHLINKFLAGSfiltered'] = linkflagsFilter('SHLINKFLAGS') + env['SHLINKCOM'] = [ + SCons.Defaults.Delete('${TARGET}.d'), + SCons.Defaults.Mkdir('${TARGET}.d'), + CreateImportFile('$EXPSYMFILE', '$SHLINKFLAGS', '$TARGET', '$SOURCES', '$__env__'), + # need the quotes, probably for issue#1123 + '${SHLINK} ${vShared} "${vWl}-G" -o ${TARGET}.d/${SHR}.o "${vWl}-bexport:${EXPSYMFILE}" $SHLINKFLAGSfiltered $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS', + # SCons.Defaults.Move('${TARGET}.d/${SHR}.o', '$TARGET'), + 'strip -e ${TARGET}.d/${SHR}.o', + '$AR $ARFLAGS $TARGET ${TARGET}.d/${SHR}.o ${TARGET}.d/${SHR}.imp', + SCons.Defaults.Delete('${TARGET}.d'), + ] + + env['LDMODULEFLAGSfiltered'] = linkflagsFilter('LDMODULEFLAGS') + env['LDMODULECOM'] = [ + SCons.Defaults.Delete('${TARGET}.d'), + SCons.Defaults.Mkdir('${TARGET}.d'), + CreateImportFile('$EXPSYMFILE', '$LDMODULEFLAGS', '$TARGET', '$SOURCES', '$__env__'), + # need the quotes, probably for issue#1123 + '$LDMODULE ${vShared} "${vWl}-G" -o ${TARGET}.d/${SHR}.o "${vWl}-bexport:${EXPSYMFILE}" $LDMODULEFLAGSfiltered $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS', + 'strip -e ${TARGET}.d/${SHR}.o', + '$AR $ARFLAGS $TARGET ${TARGET}.d/${SHR}.o', + SCons.Defaults.Delete('${TARGET}.d'), + ] def exists(env): # TODO: sync with link.smart_link() to choose a linker - linkers = { 'CXX': ['aixc++'], 'CC': ['aixcc'] } + linkers = { 'CXX': ['aixc++', 'g++'], 'CC': ['aixcc', 'gcc'] } alltools = [] for langvar, linktools in linkers.items(): if langvar in env: # use CC over CXX when user specified CC but not CXX diff -r 440f49fb5ec6 -r f4b41e077564 src/engine/SCons/Tool/g++.py --- a/src/engine/SCons/Tool/g++.py Sun May 18 19:54:14 2014 -0400 +++ b/src/engine/SCons/Tool/g++.py Tue May 20 22:45:57 2014 +0200 @@ -57,9 +57,8 @@ # platform specific settings if env['PLATFORM'] == 'aix': - env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -mminimal-toc') - env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 - env['SHOBJSUFFIX'] = '$OBJSUFFIX' + env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -mminimal-toc -fPIC') + env['SHOBJSUFFIX'] = '.pic.o' elif env['PLATFORM'] == 'hpux': env['SHOBJSUFFIX'] = '.pic.o' elif env['PLATFORM'] == 'sunos': @@ -68,6 +67,7 @@ version = gcc.detect_version(env, env['CXX']) if version: env['CXXVERSION'] = version + env['CXXVENDOR'] = 'GNU' def exists(env): # is executable, and is a GNU compiler (or accepts '--version' at least) diff -r 440f49fb5ec6 -r f4b41e077564 src/engine/SCons/Tool/gcc.py --- a/src/engine/SCons/Tool/gcc.py Sun May 18 19:54:14 2014 -0400 +++ b/src/engine/SCons/Tool/gcc.py Tue May 20 22:45:57 2014 +0200 @@ -58,6 +58,7 @@ version = detect_version(env, env['CC']) if version: env['CCVERSION'] = version + env['CCVENDOR'] = 'GNU' def exists(env): # is executable, and is a GNU compiler (or accepts '--version' at least) diff -r 440f49fb5ec6 -r f4b41e077564 src/engine/SCons/Tool/install.py --- a/src/engine/SCons/Tool/install.py Sun May 18 19:54:14 2014 -0400 +++ b/src/engine/SCons/Tool/install.py Tue May 20 22:45:57 2014 +0200 @@ -149,7 +149,7 @@ """Check if dest is a version shared library name. Return version, libname, & install_dir if it is.""" Verbose = False platform = env.subst('$PLATFORM') - if not (platform == 'posix' or platform == 'darwin'): + if not (platform == 'posix' or platform == 'darwin' or platform == 'aix'): return (None, None, None) libname = os.path.basename(dest) @@ -160,7 +160,7 @@ version_re = re.compile("[0-9]+\\.[0-9]+\\.[0-9a-zA-Z]+") version_File = None - if platform == 'posix': + if platform == 'posix' or platform == 'aix': # handle unix names versioned_re = re.compile(re.escape(shlib_suffix + '.') + "[0-9]+\\.[0-9]+\\.[0-9a-zA-Z]+") result = versioned_re.findall(libname) diff -r 440f49fb5ec6 -r f4b41e077564 src/engine/SCons/Tool/link.py --- a/src/engine/SCons/Tool/link.py Sun May 18 19:54:14 2014 -0400 +++ b/src/engine/SCons/Tool/link.py Tue May 20 22:45:57 2014 +0200 @@ -61,4 +61,5 @@ SCons.Warnings.warn(SCons.Warnings.FortranCxxMixWarning, msg % env.subst('$CXX')) issued_mixed_link_warning = True + env['LINKVENDOR'] = '$CXXVENDOR' return '$CXX' #--- a/src/engine/SCons/Tool/link.py #+++ b/src/engine/SCons/Tool/link.py #@@ -61,4 +61,5 @@ elif has_d: env['LINKCOM'] = env['DLINKCOM'] env['SHLINKCOM'] = env['SHDLINKCOM'] + env['LINKVENDOR'] = '$DCVENDOR' return '$DC' --- a/src/engine/SCons/Tool/link.py +++ b/src/engine/SCons/Tool/link.py @@ -61,7 +61,10 @@ elif has_fortran: + env['LINKVENDOR'] = '$FORTRANVENDOR' return '$FORTRAN' elif has_cplusplus: + env['LINKVENDOR'] = '$CXXVENDOR' return '$CXX' + env['LINKVENDOR'] = '$CCVENDOR' return '$CC' def shlib_emitter(target, source, env): @@ -105,7 +110,7 @@ # We need a version of the form x.y.z to proceed raise ValueError if version: - if platform == 'posix': + if platform == 'posix' or platform == 'aix': versionparts = version.split('.') name = target[0].name # generate library name with the version number @@ -156,6 +161,7 @@ SCons.Tool.createProgBuilder(env) env['SHLINK'] = '$LINK' + env['SHLINKVENDOR'] = '$LINKVENDOR' env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared') env['SHLINKCOM'] = '$SHLINK -o $TARGET $SHLINKFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' # don't set up the emitter, cause AppendUnique will generate a list @@ -174,14 +180,13 @@ if env['PLATFORM'] == 'hpux': env['SHLIBSUFFIX'] = '.sl' - elif env['PLATFORM'] == 'aix': - env['SHLIBSUFFIX'] = '.a' # For most platforms, a loadable module is the same as a shared # library. Platforms which are different can override these, but # setting them the same means that LoadableModule works everywhere. SCons.Tool.createLoadableModuleBuilder(env) env['LDMODULE'] = '$SHLINK' + env['LDMODULEVENDOR'] = '$SHLINKVENDOR' # don't set up the emitter, cause AppendUnique will generate a list # starting with None :-( env.Append(LDMODULEEMITTER='$SHLIBEMITTER') diff -r 440f49fb5ec6 -r f4b41e077564 test/AS/AS.py #--- a/test/AS/AS.py Sun May 18 19:54:14 2014 -0400 #+++ b/test/AS/AS.py Tue May 20 22:45:57 2014 +0200 #@@ -92,7 +92,7 @@ test.write('mylink.py', r""" import getopt import sys -opts, args = getopt.getopt(sys.argv[1:], 'o:') +opts, args = getopt.getopt(sys.argv[1:], 'o:W:') for opt, arg in opts: if opt == '-o': out = arg infile = open(args[0], 'rb') diff -r 440f49fb5ec6 -r f4b41e077564 test/AS/ASFLAGS.py #--- a/test/AS/ASFLAGS.py Sun May 18 19:54:14 2014 -0400 #+++ b/test/AS/ASFLAGS.py Tue May 20 22:45:57 2014 +0200 #@@ -102,7 +102,7 @@ test.write('mylink.py', r""" import getopt import sys -opts, args = getopt.getopt(sys.argv[1:], 'o:') +opts, args = getopt.getopt(sys.argv[1:], 'o:W:') for opt, arg in opts: if opt == '-o': out = arg infile = open(args[0], 'rb') diff -r 440f49fb5ec6 -r f4b41e077564 test/AS/ASPP.py #--- a/test/AS/ASPP.py Sun May 18 19:54:14 2014 -0400 #+++ b/test/AS/ASPP.py Tue May 20 22:45:57 2014 +0200 #@@ -87,7 +87,7 @@ test.write('mylink.py', r""" import getopt import sys -opts, args = getopt.getopt(sys.argv[1:], 'o:') +opts, args = getopt.getopt(sys.argv[1:], 'o:W:') for opt, arg in opts: if opt == '-o': out = arg infile = open(args[0], 'rb') diff -r 440f49fb5ec6 -r f4b41e077564 test/AS/ASPPFLAGS.py #--- a/test/AS/ASPPFLAGS.py Sun May 18 19:54:14 2014 -0400 #+++ b/test/AS/ASPPFLAGS.py Tue May 20 22:45:57 2014 +0200 #@@ -103,7 +103,7 @@ test.write('mylink.py', r""" import getopt import sys -opts, args = getopt.getopt(sys.argv[1:], 'o:') +opts, args = getopt.getopt(sys.argv[1:], 'o:W:') for opt, arg in opts: if opt == '-o': out = arg infile = open(args[0], 'rb') diff -r 440f49fb5ec6 -r f4b41e077564 test/CC/CC.py #--- a/test/CC/CC.py Sun May 18 19:54:14 2014 -0400 #+++ b/test/CC/CC.py Tue May 20 22:45:57 2014 +0200 #@@ -87,7 +87,7 @@ test.write('mylink.py', r""" import getopt import sys -opts, args = getopt.getopt(sys.argv[1:], 'o:') +opts, args = getopt.getopt(sys.argv[1:], 'o:W:') for opt, arg in opts: if opt == '-o': out = arg infile = open(args[0], 'rb') diff -r 440f49fb5ec6 -r f4b41e077564 test/CXX/CXX.py #--- a/test/CXX/CXX.py Sun May 18 19:54:14 2014 -0400 #+++ b/test/CXX/CXX.py Tue May 20 22:45:57 2014 +0200 #@@ -87,7 +87,7 @@ test.write('mylink.py', r""" import getopt import sys -opts, args = getopt.getopt(sys.argv[1:], 'o:') +opts, args = getopt.getopt(sys.argv[1:], 'o:W:') for opt, arg in opts: if opt == '-o': out = arg infile = open(args[0], 'rb') diff -r 440f49fb5ec6 -r f4b41e077564 test/Fortran/common.py #--- a/test/Fortran/common.py Sun May 18 19:54:14 2014 -0400 #+++ b/test/Fortran/common.py Tue May 20 22:45:57 2014 +0200 #@@ -57,7 +57,7 @@ t.write('mylink.py', r""" import getopt import sys -opts, args = getopt.getopt(sys.argv[1:], 'o:') +opts, args = getopt.getopt(sys.argv[1:], 'o:W:') for opt, arg in opts: if opt == '-o': out = arg infile = open(args[0], 'rb') diff -r 440f49fb5ec6 -r f4b41e077564 test/Fortran/link-with-cxx.py #--- a/test/Fortran/link-with-cxx.py Sun May 18 19:54:14 2014 -0400 #+++ b/test/Fortran/link-with-cxx.py Tue May 20 22:45:57 2014 +0200 #@@ -41,12 +41,16 @@ test.write('test_linker.py', r""" import sys -if sys.argv[1] == '-o': - outfile = open(sys.argv[2], 'wb') - infiles = sys.argv[3:] -elif sys.argv[1][:5] == '/OUT:': - outfile = open(sys.argv[1][5:], 'wb') - infiles = sys.argv[2:] +while len(sys.argv) > 2: + if sys.argv[1] == '-o': + outfile = open(sys.argv[2], 'wb') + infiles = sys.argv[3:] + break + if sys.argv[1][:5] == '/OUT:': + outfile = open(sys.argv[1][5:], 'wb') + infiles = sys.argv[2:] + break + sys.argv = sys.argv[1:] for infile in infiles: outfile.write(open(infile, 'rb').read()) outfile.close()