Description: Export PyPy symbols, that are used by cpyext, when build with --no-shared
Author: Armin Rigo <arigo@tunes.org>
Origin: upstream, https://bitbucket.org/pypy/pypy/commits/70d63dd6d506
                  https://bitbucket.org/pypy/pypy/commits/b4bf2c4a705f
                  https://bitbucket.org/pypy/pypy/commits/af205bb7dad4
Bug-upstream: https://bitbucket.org/pypy/pypy/issue/1979
Last-Updated: 2015-02-11

--- a/rpython/translator/platform/__init__.py
+++ b/rpython/translator/platform/__init__.py
@@ -201,10 +201,14 @@
         library_dirs = self._libdirs(library_dirs)
         libraries = self._libs(eci.libraries)
         link_files = self._linkfiles(eci.link_files)
-        return (library_dirs + list(self.link_flags) +
+        export_flags = self._exportsymbols_link_flags()
+        return (library_dirs + list(self.link_flags) + export_flags +
                 link_files + list(eci.link_extra) + libraries +
                 list(self.extra_libs))
 
+    def _exportsymbols_link_flags(self):
+        return []
+
     def _finish_linking(self, ofiles, eci, outputfilename, standalone):
         if outputfilename is None:
             outputfilename = ofiles[0].purebasename
--- a/rpython/translator/platform/darwin.py
+++ b/rpython/translator/platform/darwin.py
@@ -36,6 +36,13 @@
         include_dirs = self._includedirs(eci.include_dirs)
         return (args + frameworks + include_dirs)
 
+    def _exportsymbols_link_flags(self):
+        # XXX unsure if OS/X requires an option to the linker to tell
+        # "please export all RPY_EXPORTED symbols even in the case of
+        # making a binary and not a dynamically-linked library".
+        # It's not "-exported_symbols_list" but something close.
+        return []
+
     def gen_makefile(self, cfiles, eci, exe_name=None, path=None,
                      shared=False, headers_to_precompile=[],
                      no_precompile_cfiles = []):
--- a/rpython/translator/platform/posix.py
+++ b/rpython/translator/platform/posix.py
@@ -43,6 +43,12 @@
     def _link_args_from_eci(self, eci, standalone):
         return Platform._link_args_from_eci(self, eci, standalone)
 
+    def _exportsymbols_link_flags(self):
+        if (self.cc == 'mingw32' or (self.cc== 'gcc' and os.name=='nt')
+                or sys.platform == 'cygwin'):
+            return ["-Wl,--export-all-symbols"]
+        return ["-Wl,--export-dynamic"]
+
     def _link(self, cc, ofiles, link_args, standalone, exe_name):
         args = [str(ofile) for ofile in ofiles] + link_args
         args += ['-o', str(exe_name)]
@@ -105,6 +111,8 @@
         if shared:
             linkflags = self._args_for_shared(linkflags)
 
+        linkflags += self._exportsymbols_link_flags()
+
         if shared:
             libname = exe_name.new(ext='').basename
             target_name = 'lib' + exe_name.new(ext=self.so_ext).basename
--- a/rpython/translator/platform/windows.py
+++ b/rpython/translator/platform/windows.py
@@ -271,6 +271,7 @@
         linkflags = list(self.link_flags)
         if shared:
             linkflags = self._args_for_shared(linkflags)
+        linkflags += self._exportsymbols_link_flags()
         # Make sure different functions end up at different addresses!
         # This is required for the JIT.
         linkflags.append('/opt:noicf')
--- a/rpython/translator/c/test/test_standalone.py
+++ b/rpython/translator/c/test/test_standalone.py
@@ -71,6 +71,36 @@
 
 class TestStandalone(StandaloneTests):
 
+    def compile(self, *args, **kwds):
+        t, builder = StandaloneTests.compile(self, *args, **kwds)
+        #
+        # verify that the executable re-export symbols, but not too many
+        if sys.platform.startswith('linux') and not kwds.get('shared', False):
+            seen_main = False
+            g = os.popen("objdump -T '%s'" % builder.executable_name, 'r')
+            for line in g:
+                if not line.strip():
+                    continue
+                if '*UND*' in line:
+                    continue
+                name = line.split()[-1]
+                if name.startswith('__'):
+                    continue
+                if name == 'main':
+                    seen_main = True
+                    continue
+                if name == 'pypy_debug_file':     # ok to export this one
+                    continue
+                if 'pypy' in name.lower() or 'rpy' in name.lower():
+                    raise Exception("Unexpected exported name %r.  "
+                        "What is likely missing is RPY_EXTERN before the "
+                        "declaration of this C function or global variable"
+                        % (name,))
+            g.close()
+            assert seen_main, "did not see 'main' exported"
+        #
+        return t, builder
+
     def test_hello_world(self):
         def entry_point(argv):
             os.write(1, "hello world\n")
--- a/rpython/rtyper/module/ll_os.py
+++ b/rpython/rtyper/module/ll_os.py
@@ -164,7 +164,7 @@
         # we need an indirection via c functions to get macro calls working on llvm XXX still?
         if hasattr(os, 'WCOREDUMP'):
             decl_snippet = """
-            %(ret_type)s pypy_macro_wrapper_%(name)s (int status);
+            RPY_EXTERN %(ret_type)s pypy_macro_wrapper_%(name)s (int status);
             """
             def_snippet = """
             %(ret_type)s pypy_macro_wrapper_%(name)s (int status) {
--- a/rpython/translator/c/src/debug_print.c
+++ b/rpython/translator/c/src/debug_print.c
@@ -116,7 +116,7 @@
 
 #ifndef _WIN32
 
-     long long pypy_read_timestamp(void)
+     RPY_EXTERN long long pypy_read_timestamp(void)
      {
 #  ifdef CLOCK_THREAD_CPUTIME_ID
        struct timespec tspec;
--- a/rpython/translator/c/src/entrypoint.c
+++ b/rpython/translator/c/src/entrypoint.c
@@ -18,6 +18,7 @@
 #ifdef __GNUC__
 /* Hack to prevent this function from being inlined.  Helps asmgcc
    because the main() function has often a different prologue/epilogue. */
+RPY_EXTERN
 int pypy_main_function(int argc, char *argv[]) __attribute__((__noinline__));
 #endif
 
@@ -26,6 +27,7 @@
 #  include "forwarddecl.h"
 # endif
 
+RPY_EXTERN
 int pypy_main_function(int argc, char *argv[])
 {
     char *errmsg;
--- a/rpython/translator/c/src/mem.c
+++ b/rpython/translator/c/src/mem.c
@@ -15,6 +15,7 @@
 
 static struct pypy_debug_alloc_s *pypy_debug_alloc_list = NULL;
 
+RPY_EXTERN
 void pypy_debug_alloc_start(void *addr, const char *funcname)
 {
   struct pypy_debug_alloc_s *p = malloc(sizeof(struct pypy_debug_alloc_s));
@@ -25,6 +26,7 @@
   pypy_debug_alloc_list = p;
 }
 
+RPY_EXTERN
 void pypy_debug_alloc_stop(void *addr)
 {
   struct pypy_debug_alloc_s **p;
@@ -40,6 +42,7 @@
   RPyAssert(0, "free() of a never-malloc()ed object");
 }
 
+RPY_EXTERN
 void pypy_debug_alloc_results(void)
 {
   long count = 0;
--- a/rpython/translator/c/src/rtyper.c
+++ b/rpython/translator/c/src/rtyper.c
@@ -9,7 +9,7 @@
 #include <stdlib.h>
 #include <string.h>
 
-struct _RPyString_dump_t {
+static struct _RPyString_dump_t {
 	struct _RPyString_dump_t *next;
 	char data[1];
 } *_RPyString_dump = NULL;
--- a/rpython/translator/c/src/support.c
+++ b/rpython/translator/c/src/support.c
@@ -10,6 +10,7 @@
 
 /*** misc ***/
 
+RPY_EXTERN
 void RPyAssertFailed(const char* filename, long lineno,
                      const char* function, const char *msg) {
   fprintf(stderr,
@@ -19,8 +20,8 @@
   abort();
 }
 
+RPY_EXTERN
 void RPyAbort(void) {
   fprintf(stderr, "Invalid RPython operation (NULL ptr or bad array index)\n");
   abort();
 }
-
