Commits

ariovistus committed 4039b0f

pydexe works on linux now

for ldc and dmd.
ran into some annoying inconsistencies with dmd linking.
can build pyd as rpm now.
added some unittests to hopefully rule out bad linking before
I spend too much time puzzling over the error message.
made runtests.py a bit more featureful.
still needs to be implemented for python 3, though

Comments (0)

Files changed (13)

         return False
     if len(pathsubs) == 1 and ext.lower() == '.py':
         return True
+    if len(pathsubs) == 1 and pathsubs[0] == "version.txt":
+        return True
     return False
 
 
         # _outputOpts
         self._outputOpts = ['-of%s']
         # _linkOpts
-        self._exeLinkOpts = self._linkOpts = []
+        self._exeLinkOpts = []
+        self._linkOpts = []
         # _includeOpts
         self._includeOpts = ['-I%s']
         # _versionOpt
         'compiler'     : ['ldc2'],
         'compiler_so'  : ['ldc2'],
         'linker_so'    : ['gcc'],
-        'linker_exe'   : ['gcc'],
+        'linker_exe'   : ['ldc2'],
     }
 
     # this is not a env! (but it isn't GDC)
         self._linkOutputOpts = ['-o', '%s']
         self._exeLinkOpts = []
         # _linkOpts
-        self._linkOpts = ['-nostartfiles', '-shared','-Wl,--no-as-needed','-lphobos-ldc','-ldruntime-ldc', '-lrt','-lpthread','-ldl','-lm']
+        self._SharedLinkOpts = ['-nostartfiles', '-shared','-Wl,--no-as-needed','-lphobos-ldc','-ldruntime-ldc', '-lrt','-lpthread','-ldl','-lm']
+        self._ExeLinkOpts = []
         # _includeOpts
         self._includeOpts = ['-I', '%s']
         # _versionOpt
         return ['-Wl,-soname,' + os.path.basename(output_filename)]
 
     def library_dir_option(self, dir):
-        return '-L' + dir
+        if self.build_exe:
+            return "-L-L" + dir
+        else:
+            return '-L' + dir
 
     def runtime_library_dir_option(self, dir):
         return '-Wl,-R' + dir
 
     def library_option(self, lib):
-        return '-l' + lib
+        if self.build_exe:
+            return "-L-l" + lib
+        else:
+            return '-l' + lib
     def link (self, *args, **kwargs):
         target_desc = args[0]
-        if target_desc != cc.CCompiler.SHARED_OBJECT:
+        if target_desc == cc.CCompiler.SHARED_OBJECT:
+            self._binpath = self.executables['linker_so'][0]
+            self._linkOpts = self._SharedLinkOpts
+        elif target_desc == cc.CCompiler.EXECUTABLE:
+            self._binpath = self.executables['linker_exe'][0]
+            self._linkOpts = self._ExeLinkOpts
+            self._linkOutputOpts = self._outputOpts
+        else:
             raise LinkError('This CCompiler implementation does not know'
                 ' how to link anything except an extension module (that is, a'
                 ' shared object file).'
             )
-        self._binpath = self.executables['linker_so'][0]
         return DCompiler.link(self, *args, **kwargs)
         
 

examples/deimos_unittests/link.d

+import std.conv;
+import deimos.python.Python;
+
+// Linker seems to be a primary cause of failure.
+// So test that the dratted thing is working!
+
+unittest {
+    PyObject* p = cast(PyObject*) &PyString_Type;
+    assert(p !is null);
+    assert(p.ob_type !is null);
+    assert(p.ob_type.tp_name !is null);
+    assert(to!string(p.ob_type.tp_name) == "type");
+}
+
+void main() {}

examples/deimos_unittests/makefile

 	rm -f *.o
 
 %.x: %.d
-	#$(DC) $(PYTHON_2_7) $< -of$@  -I$(PYD_DIR)
-	$(LDC) -of $@ $(PYTHON_2_7_LDC) $< -I$(PYD_DIR) $(PYD_FILES)
+	$(DC) $(PYTHON_2_7) $< -of$@  -I$(PYD_DIR) -v
+	#$(LDC) -of $@ $(PYTHON_2_7_LDC) $< -I$(PYD_DIR) $(PYD_FILES)
 

examples/deimos_unittests/object_.d

     Py_XDECREF(cast(PyObject*) Py_None());
     // breaks linking?
     PyObject_TypeCheck(cast(PyObject*) Py_None(), &PyType_Type);
-    
 }
 
 

examples/deimos_unittests/setup.py

+# usage: python setup.py pydexe
+import sys
+if sys.argv[1] != 'pydexe':
+    print( "use pydexe, not %s" % sys.argv[1] )
+    sys.exit(1)
+from celerid.support import setup, Extension
+import platform
+projName = "deimos_unittests"
+
+setup(
+    name=projName,
+    version='1.0',
+    ext_modules=[
+    Extension("link", ['link.d'],
+    build_deimos=True,
+    d_unittest=True
+        ),
+    Extension("object_", ['object_.d'],
+    build_deimos=True,
+    d_unittest=True
+        )
+    ],
+)

examples/hello/makefile

 all: 
 	echo 'import sys; sys.path.append("../../build/lib/");' > tsetup.py
 	cat setup.py >> tsetup.py 
-	python3 tsetup.py build --compiler=ldc
+	python tsetup.py build --compiler=ldc
 	#echo 'import sys; sys.path.append("../../build/lib/");' > tsetup.py
 	#cat setup2.py >> tsetup.py
 	#python tsetup.py build

examples/pyind/makefile

 	     -version=Python_2_5_Or_Later \
 	     -version=Python_2_4_Or_Later \
 	     -L-lpython3.2mu
-DC = dmd -m64 -unittest -property -debug  
+DC = dmd -unittest -property -debug -g 
 LDC = ldc2 -unittest -property 
 #DC = ldmd2 -unittest -property -debug -gc
 all: pyind pyind3

examples/pyind/setup.py

     version='1.0',
     ext_modules=[
     Extension(projName, srcs,
+    build_deimos=True
         )
     ],
 )

infrastructure/deimos/python/pyport.d

 
         // todo: why does ldc/linux not work this way? 
         //  --export-dynamic seems not to change anything
+        // export causes dmd to prepend symbols with _imp__, so no use.
+        // extern is not necessary for single-command builds
+        //               necessary for traditional per-file builds.
         enum PyAPI_DATA = (q{
             extern(C)
+            extern 
             __gshared
         } ~ decl ~ ";");
     }else{
 import sys
 import os, os.path
+import shutil
 import subprocess
+import platform
 from distutils.sysconfig import get_config_var
 here = os.getcwd()
+parts = [
+"hello",
+"arraytest",
+"inherit",
+"rawexample",
+"testdll",
+"deimos_unittests",
+"pyind",
+"pyd_unittests",
+]
+use_parts = set()
 exe_ext = get_config_var("EXE")
+verz_maj = platform.python_version_tuple()[0]
+print "%r" % (verz_maj,), verz_maj == 2
+if verz_maj == "3":
+    pass
+elif verz_maj == "2":
+    import optparse
+    oparser = optparse.OptionParser()
+    oparser.add_option("-b", action="store_true", dest="use_build")
+    oparser.add_option('-C',"--compiler", dest="compiler")
+    oparser.add_option('-c',"--clean", action="store_true",dest="clean")
+    (opts, args) = oparser.parse_args()
+else:
+    assert 0
+if args:
+    for arg in args:
+        if arg in parts:
+            use_parts.add(arg)
+else:
+    for arg in parts:
+        use_parts.add(arg)
+if opts.use_build:
+    build = os.path.abspath(os.path.join("build","lib"));
+    old_path = os.getenv("PYTHONPATH")
+    if not os.path.exists(build):
+        subprocess.check_call([sys.executable, "setup.py", "build"]);
+    print "using build: %r" % build
+    os.putenv("PYTHONPATH", build)
 
 def check_exe(cmd):
-    subprocess.check_call([cmd + exe_ext])
-os.chdir("examples")
-os.chdir("hello")
-subprocess.check_call([sys.executable, "setup.py", "build"])
-subprocess.check_call([sys.executable, "test.py"])
-os.chdir("..")
-os.chdir("arraytest")
-subprocess.check_call([sys.executable, "setup.py", "build"])
-subprocess.check_call([sys.executable, "test.py"])
-os.chdir("..")
-os.chdir("inherit")
-subprocess.check_call([sys.executable, "setup.py", "build"])
-subprocess.check_call([sys.executable, "test.py"])
-os.chdir("..")
-os.chdir("rawexample")
-subprocess.check_call([sys.executable, "setup.py", "build"])
-subprocess.check_call([sys.executable, "test.py"])
-os.chdir("..")
-os.chdir("testdll")
-subprocess.check_call([sys.executable, "setup.py", "build"])
-subprocess.check_call([sys.executable, "test.py"])
-os.chdir("..")
-os.chdir("pyind")
-subprocess.check_call([sys.executable, "setup.py", "pydexe"])
-subprocess.check_call(["pyind" + exe_ext])
-os.chdir("..")
-os.chdir("pyd_unittests")
-subprocess.check_call([sys.executable, "setup.py", "pydexe"])
-check_exe("class_wrap")
-check_exe("def")
-check_exe("embedded")
-check_exe("func_wrap")
-check_exe("make_object")
-check_exe("pydobject")
-check_exe("struct_wrap")
+    subprocess.check_call([os.path.join(".",cmd + exe_ext)])
+def remove_exe(cmd):
+    if os.path.exists(cmd + exe_ext):
+        os.remove(cmd+exe_ext)
+def pydexe():
+    cmds = [sys.executable, "setup.py", "pydexe"]
+    if opts.compiler:
+        cmds.append("--compiler="+opts.compiler)
+    subprocess.check_call(cmds)
+def check_py(scrpt):
+    subprocess.check_call([sys.executable, scrpt])
+def pybuild():
+    cmds = [sys.executable, "setup.py", "build"]
+    if opts.compiler:
+        cmds.append("--compiler="+opts.compiler)
+    subprocess.check_call(cmds)
+try:
+    os.chdir("examples")
+    if "deimos_unittests" in use_parts:
+        os.chdir("deimos_unittests")
+        exes = ["link", "object_"]
+        if opts.clean:
+            if os.path.exists("build"): shutil.rmtree("build")
+            for exe in exes: remove_exe(exe)
+        else:
+            pydexe()
+            for exe in exes:
+                check_exe(exe)
+        os.chdir("..")
+    if "pyind" in use_parts:
+        os.chdir("pyind")
+        if opts.clean:
+            if os.path.exists("build"): shutil.rmtree("build")
+            remove_exe("pyind")
+        else:
+            pydexe()
+            check_exe("pyind")
+        os.chdir("..")
+    if "pyd_unittests" in use_parts:
+        os.chdir("pyd_unittests")
+        exes = ["class_wrap", "def", "embedded", "make_object", 
+                "pydobject", "struct_wrap"]
+        if opts.clean:
+            if os.path.exists("build"): shutil.rmtree("build")
+            for exe in exes:
+                remove_exe(exe)
+        else:
+            pydexe()
+            for exe in exes:
+                check_exe(exe)
+        os.chdir("..")
+    if "hello" in use_parts:
+        os.chdir("hello")
+        if opts.clean:
+            if os.path.exists("build"): shutil.rmtree("build")
+        else:
+            pybuild()
+            check_py("test.py")
+        os.chdir("..")
+    if "arraytest" in use_parts:
+        os.chdir("arraytest")
+        if opts.clean:
+            if os.path.exists("build"): shutil.rmtree("build")
+        else:
+            pybuild()
+            check_py("test.py")
+        os.chdir("..")
+    if "inherit" in use_parts:
+        os.chdir("inherit")
+        if opts.clean:
+            if os.path.exists("build"): shutil.rmtree("build")
+        else:
+            pybuild()
+            check_py("test.py")
+        os.chdir("..")
+    if "rawexample" in use_parts:
+        os.chdir("rawexample")
+        if opts.clean:
+            if os.path.exists("build"): shutil.rmtree("build")
+        else:
+            pybuild()
+            check_py("test.py")
+        os.chdir("..")
+    if "testdll" in use_parts:
+        os.chdir("testdll")
+        if opts.clean:
+            if os.path.exists("build"): shutil.rmtree("build")
+        else:
+            pybuild()
+            check_py("test.py")
+        os.chdir("..")
+finally:
+    if opts.use_build and old_path is not None:
+        os.putenv("PYTHONPATH", old_path)
 
+    
 
 f = open('MANIFEST', 'w')
 try:
-    build_manifest.buildManifest(f, isSourceDist)
+    build_manifest.buildManifest(f, True)
 finally:
     f.close()
 
     description = "Build a D application that embeds python with Pyd"
 
     user_options = [
-            ("optimize", "O", "Ask the D compiler to optimize the generated code, at the expense of"
+        ('compiler=', 'c',
+         "specify the compiler type"),
+        ("optimize", "O", "Ask the D compiler to optimize the generated code, at the expense of"
                 " safety features such as array bounds checks."),
-            ("print-flags", None, "Don't build, just print out version flags for pyd") ]
+        ('debug', 'g',
+         "compile extensions and libraries with debugging information"),
+        ('force', 'f',
+         "forcibly build everything (ignore file timestamps)"),
+        ("print-flags", None, "Don't build, just print out version flags for pyd") ]
 
-    boolean_options = ['print-flags']
+    boolean_options = ['print-flags', 'debug', 'force']
 
     def initialize_options(self):
         self.print_flags = False
         self.libraries = None
         self.library_dirs = None
         self.link_objects = None
-        self.debug = 0
+        self.debug = None
         self.optimize = 0
         self.dry_run = 0
         self.verbose = 0
         return os.path.join(*ext_path) + exe_ext
 
     def get_libraries(self, ext):
-        return ext.libraries
-
+        # mostly copied from build_ext.get_libraries
+        if sys.platform == "win32":
+            return ext.libraries
+        elif sys.platform[:6] == "cygwin":
+            return ext.libraries
+        else:
+            from distutils import sysconfig
+            if sysconfig.get_config_var('Py_ENABLE_SHARED'):
+                template = "python%d.%d"
+                pythonlib = (template %
+                             (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff))
+                if sys.pydebug:
+                    pythonlib += '_d'
+                return ext.libraries + [pythonlib]
+            else:
+                return ext.libraries
 
 def setup(*args, **kwargs):
     if 'cmdclass' not in kwargs: