From a30529bf7d9361a44490126666d0cc71d98410aa Mon Sep 17 00:00:00 2001 From: Steven Knight Date: Fri, 7 Jan 2005 14:24:10 +0000 Subject: Add LoadableModule support. (Michael McCracken) --- doc/man/scons.1 | 45 ++++++++++++++ src/CHANGES.txt | 13 +++++ src/engine/MANIFEST.in | 1 + src/engine/SCons/Defaults.py | 1 + src/engine/SCons/Tool/__init__.py | 31 ++++++++++ src/engine/SCons/Tool/applelink.py | 60 +++++++++++++++++++ src/engine/SCons/Tool/link.py | 11 ++++ test/LoadableModule.py | 117 +++++++++++++++++++++++++++++++++++++ test/SWIG/SWIG.py | 53 +++++++++++++---- test/import.py | 1 + 10 files changed, 321 insertions(+), 12 deletions(-) create mode 100644 src/engine/SCons/Tool/applelink.py create mode 100644 test/LoadableModule.py diff --git a/doc/man/scons.1 b/doc/man/scons.1 index 449eace..7b34e8a 100644 --- a/doc/man/scons.1 +++ b/doc/man/scons.1 @@ -1582,6 +1582,16 @@ A synonym for the builder method. '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP LoadableModule() +.IP env.LoadableModule() +On most systems, +this is the same as +.BR SharedLibrary (). +On Mac OS X (Darwin) platforms, +this creates a loadable module bundle. + + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .IP M4() .IP env.M4() Builds an output file from an M4 input file. @@ -5472,6 +5482,12 @@ The default list is: A function that converts a file name into a File instance relative to the target being built. +.IP FRAMEWORKSFLAGS +On Mac OS X, +frameworks options to be addad at +the end of a command +line building a loadable module. + .IP GS The Ghostscript program used to convert PostScript to PDF files. @@ -5654,6 +5670,35 @@ env = Environment(LATEXCOMSTR = "Building $TARGET from LaTeX input $SOURCES") .IP LATEXFLAGS General options passed to the LaTeX structured formatter and typesetter. +.IP LDMODULE +The linker for building loadable modules. +By default, this is the same as $SHLINK. + +.IP LDMODULECOM +The command line for building loadable modules. +On Mac OS X, this uses the $LDMODULE, +$LDMODULEFLAGS and $FRAMEWORKSFLAGS variables. +On other systems, this is the same as $SHLINK. + +.IP LDMODULECOMSTR +The string displayed when building loadable modules. +If this is not set, then $LDMODULECOM (the command line) is displayed. + +.IP LDMODULEFLAGS +General user options passed to the linker for building loadable modules. + +.IP LDMODULEPREFIX +The prefix used for loadable module file names. +On Mac OS X, this is null; +on other systems, this is +the same $SHLIBPREFIX. + +.IP LDMODULESUFFIX +The suffix used for loadable module file names. +On Mac OS X, this is null; +on other systems, this is +the same $SHLIBSUFFIX. + .IP LEX The lexical analyzer generator. diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 11c8e62..e13fa39 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -180,6 +180,19 @@ RELEASE 0.97 - XXX - Make ParseConfig() recognize and add -mno-cygwin to $LINKFLAGS and $CCFLAGS, and -mwindows to $LINKFLAGS. + From Michael McCracken: + + - Add a new "applelink" tool to handle the things like Frameworks and + bundles that Apple has added to gcc for linking. + + - Use more appropriate default search lists of linkers, compilers and + and other tools for the 'darwin' platform. + + - Add a LoadableModule Builder that builds a bundle on Mac OS X (Darwin) + and a shared library on other systems. + + - Improve SWIG tests for use on Mac OS X (Darwin). + From Elliot Murphy: - Enhance the tests to guarantee persistence of ListOption diff --git a/src/engine/MANIFEST.in b/src/engine/MANIFEST.in index 5c329bd..133ccad 100644 --- a/src/engine/MANIFEST.in +++ b/src/engine/MANIFEST.in @@ -55,6 +55,7 @@ SCons/Tool/aixc++.py SCons/Tool/aixcc.py SCons/Tool/aixf77.py SCons/Tool/aixlink.py +SCons/Tool/applelink.py SCons/Tool/ar.py SCons/Tool/as.py SCons/Tool/bcc32.py diff --git a/src/engine/SCons/Defaults.py b/src/engine/SCons/Defaults.py index b3312f7..644dadf 100644 --- a/src/engine/SCons/Defaults.py +++ b/src/engine/SCons/Defaults.py @@ -131,6 +131,7 @@ ASPPAction = SCons.Action.Action("$ASPPCOM", "$ASPPCOMSTR") LinkAction = SCons.Action.Action("$LINKCOM", "$LINKCOMSTR") ShLinkAction = SCons.Action.Action("$SHLINKCOM", "$SHLINKCOMSTR") +LdModuleLinkAction = SCons.Action.Action("$LDMODULECOM", "$LDMODULECOMSTR") ArAction = SCons.Action.Action("$ARCOM", "$ARCOMSTR") LexAction = SCons.Action.Action("$LEXCOM", "$LEXCOMSTR") diff --git a/src/engine/SCons/Tool/__init__.py b/src/engine/SCons/Tool/__init__.py index 2c793d9..5513f5e 100644 --- a/src/engine/SCons/Tool/__init__.py +++ b/src/engine/SCons/Tool/__init__.py @@ -171,6 +171,29 @@ def createSharedLibBuilder(env): return shared_lib +def createLoadableModuleBuilder(env): + """This is a utility function that creates the LoadableModule + Builder in an Environment if it is not there already. + + If it is already there, we return the existing one. + """ + + try: + loadable_module = env['BUILDERS']['LoadableModule'] + except KeyError: + action_list = [ SCons.Defaults.SharedCheck, + SCons.Defaults.LdModuleLinkAction ] + ld_module = SCons.Builder.Builder(action = action_list, + emitter = "$SHLIBEMITTER", + prefix = '$LDMODULEPREFIX', + suffix = '$LDMODULESUFFIX', + target_scanner = SCons.Defaults.ProgScan, + src_suffix = '$SHOBJSUFFIX', + src_builder = 'SharedObject') + env['BUILDERS']['LoadableModule'] = ld_module + + return ld_module + def createObjBuilders(env): """This is a utility function that creates the StaticObject and SharedObject Builders in an Environment if they @@ -309,6 +332,14 @@ def tool_list(platform, env): assemblers = ['as', 'gas'] fortran_compilers = ['aixf77', 'g77', 'fortran'] ars = ['ar'] + elif str(platform) == 'darwin': + "prefer GNU tools on Mac OS X, except for some linkers and IBM tools" + linkers = ['applelink', 'gnulink'] + c_compilers = ['gcc', 'cc'] + cxx_compilers = ['g++', 'c++'] + assemblers = ['as'] + fortran_compilers = ['g77'] + ars = ['ar'] else: "prefer GNU tools on all other platforms" linkers = ['gnulink', 'mslink', 'ilink'] diff --git a/src/engine/SCons/Tool/applelink.py b/src/engine/SCons/Tool/applelink.py new file mode 100644 index 0000000..87c92d4 --- /dev/null +++ b/src/engine/SCons/Tool/applelink.py @@ -0,0 +1,60 @@ +"""SCons.Tool.applelink + +Tool-specific initialization for the Apple gnu-like linker. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import SCons.Util + +import gnulink + +def generate(env): + """Add Builders and construction variables for applelink to an + Environment.""" + gnulink.generate(env) + + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -dynamiclib') + + # override the default for loadable modules, which are different + # on OS X than dynamic shared libs. echoing what XCode does for + # pre/suffixes: + env['LDMODULEPREFIX'] = '' + env['LDMODULESUFFIX'] = '' + env['LDMODULE'] = '$SHLINK' + env['LDMODULEFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -bundle') + env['LDMODULECOM'] = '$LDMODULE $LDMODULEFLAGS -o ${TARGET} $SOURCES $_LIBDIRFLAGS $_LIBFLAGS $FRAMEWORKSFLAGS' + + + +def exists(env): + import sys + return sys.platform == 'darwin' diff --git a/src/engine/SCons/Tool/link.py b/src/engine/SCons/Tool/link.py index 8248be9..5188634 100644 --- a/src/engine/SCons/Tool/link.py +++ b/src/engine/SCons/Tool/link.py @@ -70,6 +70,17 @@ def generate(env): 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['LDMODULEPREFIX'] = '$SHLIBPREFIX' + env['LDMODULESUFFIX'] = '$SHLIBSUFFIX' + env['LDMODULEFLAGS'] = '$SHLINKFLAGS' + env['LDMODULECOM'] = '$SHLINKCOM' + + def exists(env): # This module isn't really a Tool on its own, it's common logic for diff --git a/test/LoadableModule.py b/test/LoadableModule.py new file mode 100644 index 0000000..f3b1959 --- /dev/null +++ b/test/LoadableModule.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import os +import string +import sys + +import TestCmd +import TestSCons + +dll_ = TestSCons.dll_ +_dll = TestSCons._dll + +test = TestSCons.TestSCons() + +# Some systems apparently need -ldl on the link line, others don't. +no_dl_lib = "env.Program(target = 'dlopenprog', source = 'dlopenprog.c')" +use_dl_lib = "env.Program(target = 'dlopenprog', source = 'dlopenprog.c', LIBS=['dl'])" + +dlopen_line = { + 'darwin' : no_dl_lib, + 'freebsd4' : no_dl_lib, + 'linux2' : use_dl_lib, +} +platforms_with_dlopen = dlopen_line.keys() + +test.write('SConstruct', """ +env = Environment() +# dlopenprog tries to dynamically load foo1 at runtime using dlopen(). +env.LoadableModule(target = 'foo1', source = 'f1.c') +""" + dlopen_line.get(sys.platform, '')) + + +test.write('f1.c', r""" +#include + +void +f1(void) +{ + printf("f1.c\n"); + fflush(stdout); +} +""") + +dlopenprog = r""" +#include +#include +#include + +extern int errno; + +int +main(int argc, char *argv[]) +{ + argv[argc++] = "--"; + void *foo1_shobj = dlopen("__foo1_name__", RTLD_NOW); + if(!foo1_shobj){ + printf("Error loading foo1 '__foo1_name__' library at runtime, exiting.\n"); + printf("%d\n", errno); + perror(""); + return -1; + } + void (*f1)() = dlsym(foo1_shobj, "f1\0"); + (*f1)(); + printf("dlopenprog.c\n"); + dlclose(foo1_shobj); + return 0; +} +""" + +# Darwin dlopen()s a bundle name "foo1", +# other systems dlopen() a traditional libfoo1.so file. +foo1_name = {'darwin' : 'foo1'}.get(sys.platform, dll_+'foo1'+_dll) + +test.write('dlopenprog.c', + string.replace(dlopenprog, '__foo1_name__', foo1_name)) + +test.run(arguments = '.', + stderr=TestSCons.noisy_ar, + match=TestSCons.match_re_dotall) + +if string.find(sys.platform, 'darwin') != -1: + test.run(program='/usr/bin/file', + arguments = "foo1", + stdout="foo1: Mach-O bundle ppc\n") + +if sys.platform in platforms_with_dlopen: + os.environ['LD_LIBRARY_PATH'] = test.workpath() + test.run(program = test.workpath('dlopenprog'), + stdout = "f1.c\ndlopenprog.c\n") + + + +test.pass_test() diff --git a/test/SWIG/SWIG.py b/test/SWIG/SWIG.py index 2a0ee32..af76d6c 100644 --- a/test/SWIG/SWIG.py +++ b/test/SWIG/SWIG.py @@ -29,10 +29,24 @@ import string import sys import TestSCons -python = TestSCons.python +if sys.platform =='darwin': + # change to make it work with stock OS X python framework + # we can't link to static libpython because there isn't one on OS X + # so we link to a framework version. However, testing must also + # use the same version, or else you get interpreter errors. + python = "/System/Library/Frameworks/Python.framework/Versions/Current/bin/python" +else: + python = TestSCons.python + _exe = TestSCons._exe _obj = TestSCons._obj -_dll = TestSCons._dll + +# swig-python expects specific filenames. +# the platform specific suffix won't necessarily work. +if sys.platform == 'win32': + _dll = '.dll' +else: + _dll = '.so' test = TestSCons.TestSCons() @@ -106,6 +120,18 @@ if swig: version = sys.version[:3] # see also sys.prefix documentation + # handle testing on other platforms: + frameworks = '' + ldmodule_prefix = '' + platform_sys_prefix = sys.prefix + if sys.platform == 'darwin': + # OS X has a built-in Python but no static libpython + # so you should link to it using apple's 'framework' scheme. + # (see top of file for further explanation) + frameworks = '-framework Python' + ldmodule_prefix = '_' + platform_sys_prefix = '/System/Library/Frameworks/Python.framework/Versions/%s/' % version + test.write("wrapper.py", """import os import string @@ -116,15 +142,18 @@ os.system(string.join(sys.argv[1:], " ")) test.write('SConstruct', """ foo = Environment(SWIGFLAGS='-python', - CPPPATH='%s/include/python%s/', + CPPPATH='%(platform_sys_prefix)s/include/python%(version)s/', SHCCFLAGS='', - SHOBJSUFFIX='.o', - SHLIBPREFIX='') + LDMODULEPREFIX='%(ldmodule_prefix)s', + LDMODULESUFFIX='%(_dll)s', + FRAMEWORKSFLAGS='%(frameworks)s', + ) + swig = foo.Dictionary('SWIG') -bar = foo.Copy(SWIG = r'%s wrapper.py ' + swig) -foo.SharedLibrary(target = 'foo', source = ['foo.c', 'foo.i']) -bar.SharedLibrary(target = 'bar', source = ['bar.c', 'bar.i']) -""" % (sys.prefix, version, python)) +bar = foo.Copy(SWIG = r'%(python)s wrapper.py ' + swig) +foo.LoadableModule(target = 'foo', source = ['foo.c', 'foo.i']) +bar.LoadableModule(target = 'bar', source = ['bar.c', 'bar.i']) +""" % locals()) test.write("foo.c", """\ char * @@ -160,7 +189,7 @@ bar_string() extern char *bar_string(); """) - test.run(arguments = 'foo' + _dll) + test.run(arguments = ldmodule_prefix+'foo' + _dll) test.fail_test(os.path.exists(test.workpath('wrapper.out'))) @@ -171,9 +200,9 @@ print foo.foo_string() This is foo.c! """) - test.up_to_date(arguments = 'foo' + _dll) + test.up_to_date(arguments = ldmodule_prefix+'foo' + _dll) - test.run(arguments = 'bar' + _dll) + test.run(arguments = ldmodule_prefix+'bar' + _dll) test.fail_test(test.read('wrapper.out') != "wrapper.py\n") diff --git a/test/import.py b/test/import.py index a8a86bf..1aae7ce 100644 --- a/test/import.py +++ b/test/import.py @@ -59,6 +59,7 @@ tools = [ 'aixcc', 'aixf77', 'aixlink', + 'applelink', 'ar', 'as', 'bcc32', -- cgit v0.12