Not sure how things worked out for you, but I was able to create a DLL using Elmer with Python 2.7.8 and Elmer 1.1.7a on Windows 7 x64 and figured I'd share my experience.
My compilation/build environment was VisualStudio2005 using NMAKE
I did run into several issues along the way, especially related to the -frozen flag and my older version of visual studio, but I am getting what appears to be a working DLL now.
My top level notes:
- I had to statically link elmer with MSVCRT80 due to a conflict during dll loading with the MSVCRT90 included by python27.dll.
- The library, elmer.lib, was needed for the linking step, but existed only in the c:\elmer\src\build\temp.win32-2.7\Release\elmer directory, it seems like it should get copied someplace without the word temp in the name, but I set my makefile to use it from the temp directory.
- When using -frozen, there can be dozens of generated .c files
- PyImport_FrozenModules and Py_FrozenFlag are already declared by other include files (pydebug).
- PyImport_FrozenModules is getting redefined (due to elPythonToC), rather than just assigned.
- DL_IMPORT (used within elPythonToC.py) is deprecated per http://bugs.python.org/issue566100. should use PyApi_DATA instead I think, but makes no real difference.EDIT Not that important as I deleted that entire block as it was redefining PyImport_FrozenModules
- In the end, I had to include python27.dll, elmer.dll. I believe MSVCRT90.dll also needs to be installed via the MS redistributable installer on the target machine as python27.dll needs that. It would be nice if using frozen would get us a single distributable, but I'll take what I can get.
- I was able to run the procedure on python 2.6.5 with slight modifications (e.g. python path is c:\python26 instead of c:\python27).
- Test your script in python first, elmer will happily package a script containing malformed python.
- To create a debugable dll, add
/Z7 /Od
to the CFLAGS and /incremental:no /debug
to the LINKFLAGS
- Would be nice if some sort of formatted comment or function in the python code itself could replace the .elm file.
Now here are the detailed steps of I did (no warranties that any of these are correct and they were certainly not optimal, but they did seem to work for me):
- Install Elmer
- Download and unzip elmer to some directory (I picked C:\elmer, so I'll use that throughout my example)
- When I started, I was using 2.7.2 and I ran into this issue: Compiling with cython and mingw produces gcc: error: unrecognized command line option '-mno-cygwin'. I patched Lib/distutils/cygwinccompiler.py to remove -mno-cygwin and got it to install, but I later installed 2.7.8, which I think fixes the underlying issue.
- I wanted the Elmer.dll to statically link to the MSVCRT junk as I have an older version of MSVCRT, so I modified setup.py as follows. (I know I shouldn't statically link and you probably don't want that, but in case you do:
- Add
extraCompileArgs = []
and extraLinkArgs = []
below the libDirs = []
declaration
- In the Windows-specific build options if block add:
extraCompileArgs = ['/MT']
and extraLinkArgs = ['/MANIFEST']
- Add
, extra_compile_args=extraCompileArgs, extra_link_args=extraLinkArgs
after libraries=libs
inside Setup(..., ext_modules=[Extension(
- from command prompt, run C:\elmer\src>python setup.py build
- from command prompt, run C:\elmer\src>python setup.py install
- Modify .py and .elm files to suit my needs - that was the easy part
- Obtain makefrozen.py makefrozen is used by elmer when using the -frozen option, but it does not appear to be included in the standard win32 MSI distribution.
- makefrozen is included in the source code for python, but is not included in the windows MSI installer, so download the source tarball from www.python.org
- extract \Tools\freeze\ to c:\python27\Tools\freeze
- Modify buildAndRun
- Change ELMERDIR to C:\
- Path should point to
SET PATH=%PATH%;%ELMERDIR%;%THISDIR%
- Set ELMERDIR to C:\elmer\
Update Make files to work for my configuration (the example files didn't work at all with nmake or frozen). Here is my MAKEFILE that can build the frozen or non-frozen version using nmake:
PYTHONDIR=C:\Python27
ELMERDIR=C:\elmer
ELMERLIBDIR=$(ELMERDIR)\src\build\temp.win32-2.7\Release\elmer
ELMER_OUTPUT_DIR=Generated
CC=cl
RM=del /F /Q
ELMER=$(ELMERDIR)\elmer
shell=call
CFLAGS=/TC /LD
INCLUDEFLAGS=/I$(ELMER_OUTPUT_DIR) /I$(PYTHONDIR)\include /I$(PYTHONDIR)\Lib\site-packages\elmer
LINKFLAGS=/LIBPATH:$(PYTHONDIR)\libs python27.lib /LIBPATH:$(ELMERLIBDIR) elmer.lib
.PHONY: generatedFiles generatedFrozen all allFrozen
SRCS = Generated\*.c
OBS = $(SRCS:.c=.obj)
{Generated}.c{Generated}.obj::
@echo Compiling...
@$(CC) /nologo $(CFLAGS) $(INCLUDEFLAGS) /c /FoGenerated\ $<
generatedFrozen:
echo Elmering
@$(shell) $(ELMER) -c -frozen -o $(ELMER_OUTPUT_DIR) -int MyDll.elm MyDll.py
generatedFiles:
echo Elmering
@$(shell) $(ELMER) -c -o $(ELMER_OUTPUT_DIR) -int MyDll.elm MyDll.py
showIncludeAndLinkFlags:
@echo includeDirs:
@$(shell) $(ELMER) --printIncludeFlags
@echo linkDirs:
@$(shell) $(ELMER) --printLinkFlags
MyDll.dll: $(OBS)
@echo Linking...
@Link $(LINKFLAGS) /DLL $(OBS)
all:
@echo "Making All"
@nmake generatedFiles
@nmake showIncludeAndLinkFlags
@nmake MyDll.dll
allFrozen:
@echo "Making All"
@nmake generatedFrozen
@nmake showIncludeAndLinkFlags
@nmake MyDll.dll
clean:
@echo cleaning
$(RM) .\Generated\*.* 2>nul || echo eatFailure >nul
Create a Build.bat to setup the Visual Studio environment before calling nmake. Here are the contents of my build batch file:
@ECHO OFF
SETLOCAL
SET THISDIR=%~dp0
REM Setup Visual Studio build environment
REM Microsoft Visual Studio 8 == Visual Studio 2005
CALL "C:\Program Files (x86)\Microsoft Visual Studio 8\VC\vcvarsall.bat"
nmake clean
nmake allFrozen
pause
ENDLOCAL
- Fix compilation/link errors, it got a bit ugly here as I had to modify elmer code to get it to work
- Remove duplicate declarations and redefinition for Py_FrozenFlag and PyImport_FrozenModules
- Open C:\python27\Lib\site-packages\elmer\elPythonToC.py in a text or python editor
- Delete or comment out the
if( frozenFlag ) :
block containing retCode += "extern int Py_FrozenFlag;\n"
and retCode += "DL_IMPORT(struct _frozen *) PyImport_FrozenModules;\n"
(around line 145)
- Add the line:
retCode += " PyImport_FrozenModules = _PyImport_FrozenModules;\n"
above the line containing retCode += " retRunVal = PyImport_ImportFrozenModule( (char*)\"%s\" );\n"
(around line 189)
- Copy DLLs to a distribution folder
- Copy MyDLL.dll from the working directory containing your .elm file. Copy elmer.dll from C:\elmer\src\build\lib.win32-2.6\elmer. Copy python27.dll from C:\Windows\SysWOW64.
- Install Visual C++ Redistributable Package if necessary on target machine (or maybe just find MSVCRT90.dll in your windows side-by-side configurations and stuff that next to your DLL. see http://msdn.microsoft.com/en-us/library/vstudio/ms235299.aspx.
EDIT
When I added some meat to my dll, I noticed the frozen import stuff wasn't working properly and was causing my DLL to exit. My original modifications were causing Py_FrozenFlag and PyImport_FrozenModules to be defined locally, which caused PyImport_ImportFrozenModule to fail within the generated c code. I've made modifications to the makefile to eliminate the Py_NO_ENABLE_SHARED define, removed the definitions and declarations of those vars, changed the definition of PyImport_FrozenModules to an assignment and everything seems to be working properly now. I've successfully connected to and debugged my dll from a simple c++ app.