2012-10-02 13 views
9

अंतिम अद्यतनCython और fortran - कैसे f2py

यह सवाल है कि कैसे एक setup.py कि एक cython मॉड्यूल जो FORTRAN कोड सीधे पहुंचता है, तो सी चाहेंगे संकलन होगा लिखने के बारे में है के बिना एक साथ संकलित करने के लिए। यह समाधान के लिए एक लंबी और कठिन यात्रा थी, लेकिन पूर्ण गड़बड़ संदर्भ के लिए नीचे शामिल है।

मूल प्रश्न

मैं एक विस्तार है जो एक Cython फ़ाइल है, जो कुछ ढेर स्मृति सेट और fortran कोड को पास कर देता है, और एक fortran फ़ाइल है, जो एक सम्मानित वर्ष मॉड्यूल है कि मैं क्या है अगर मैं कर सकता हूं तो पुनर्मूल्यांकन से बचने के लिए।

.pyx फ़ाइल सेल्सियस के लिए ठीक संकलित, लेकिन cython संकलक निम्न त्रुटि के साथ .f90 फ़ाइल पर chokes:

$ python setup.py build_ext --inplace 
running build_ext 
cythoning delaunay/__init__.pyx to delaunay/__init__.c 
building 'delaunay' extension 
error: unknown file type '.f90' (from 'delaunay/stripack.f90') 

यहाँ (के शीर्ष आधा) मेरी सेटअप फ़ाइल:

from distutils.core import setup, Extension 
from Cython.Distutils import build_ext 

ext_modules = [ 
    Extension("delaunay", 
    sources=["delaunay/__init__.pyx", 
      "delaunay/stripack.f90"]) 
] 

setup(
    cmdclass = {'build_ext': build_ext}, 
    ext_modules = ext_modules, 
    ... 
) 

नोट: मूल रूप से फोर्ट्रान फ़ाइल का स्थान गलत तरीके से निर्दिष्ट किया गया था (निर्देशिका उपसर्ग के बिना) लेकिन यह ठीक होने के ठीक उसी तरह से टूट जाता है।

बातें मैं कोशिश की है:

मैं this पाया, और fortran संकलक के नाम पर गुजर करने की कोशिश की (यानी gfortran) इस तरह:

$ python setup.py config --fcompiler=gfortran build_ext --inplace 
usage: setup.py [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...] 
    or: setup.py --help [cmd1 cmd2 ...] 
    or: setup.py --help-commands 
    or: setup.py cmd --help 

error: option --fcompiler not recognized 

और मैं भी कोशिश की है --inplace को हटाकर, यदि समस्या थी (यह शीर्ष त्रुटि संदेश के समान नहीं था)।

तो, मैं इस किले को कैसे संकलित करूं? क्या मैं इसे .o में खुद को हैक कर सकता हूं और इसे जोड़ने से दूर हो सकता हूं? या is this a bug in Cython, जो मुझे preutocessor के साथ distutils reimplement या चारों ओर हैक करने के लिए मजबूर करेगा?

अद्यतन

तो, numpy.distutils संकुल बाहर चेक करने के बाद, मैं थोड़ा अधिक समस्या को समझते हैं। ऐसा लगता है आप के लिए

  1. उपयोग cython है कि फ़ाइलें ग CPython को .pyx फ़ाइलों को कनवर्ट करने,
  2. फिर एक Extension/setup() संयोजन, fortran का समर्थन करता है कि numpy की तरह इस्तेमाल करते हैं।

इस की कोशिश की है, मेरी setup.py अब इस तरह दिखता है:

from numpy.distutils.core import setup 
from Cython.Build import cythonize 
from numpy.distutils.extension import Extension 

cy_modules = cythonize('delaunay/sphere.pyx') 
e = cy_modules[0] 

ext_modules = [ 
    Extension("delaunay.sphere", 
     sources=e.sources + ['delaunay/stripack.f90']) 
] 

setup(
    ext_modules = ext_modules, 
    name="delaunay", 
    ... 
) 

(ध्यान दें, कि मैं भी मॉड्यूल थोड़ा की पुनर्संरचना की है के बाद से मालूम होता है एक __init__.pyx की अनुमति नहीं है ...)

अब वह जगह है जहां चीजें छोटी और मंच-निर्भर हो जाती हैं। मेरे पास दो परीक्षण सिस्टम उपलब्ध हैं - एक मैक ओएस एक्स 10।6 (हिम तेंदुए), मैकपोर्ट पायथन 2.7 का उपयोग करके, और एक मैक ओएस एक्स 10.7 (शेर) सिस्टम पायथन 2.7 का उपयोग कर।

Snow Leopard पर, निम्न लागू होगा:

इसका मतलब है कि मॉड्यूल compiles (हुर्रे!) (हालांकि वहाँ numpy के लिए कोई --inplace, ऐसा लगता है, इसलिए मैं पूरे सिस्टम पर परीक्षण मॉड्यूल स्थापित करने के लिए किया था:/

>>> import delaunay 
    Traceback (most recent call last): 
    File "<input>", line 1, in <module> 
    File "<snip>site-packages/delaunay/__init__.py", line 1, in <module> 
     from sphere import delaunay_mesh 
    ImportError: dlopen(<snip>site-packages/delaunay/sphere.so, 2): no suitable image found. Did find: 
    <snip>site-packages/delaunay/sphere.so: mach-o, but wrong architecture 

और शेर पर, मैं एक संकलन त्रुटि मिलती है, एक नहीं बल्कि भ्रामक देख संकलन लाइन निम्नलिखित:) लेकिन मैं अभी भी एक दुर्घटना import पर इस प्रकार प्राप्त

gfortran:f77: build/src.macosx-10.7-intel-2.7/delaunay/sphere-f2pywrappers.f 
/usr/local/bin/gfortran -Wall -arch i686 -arch x86_64 -Wall -undefined dynamic_lookup -bundle build/temp.macosx-10.7-intel-2.7/delaunay/sphere.o build/temp.macosx-10.7-intel-2.7/build/src.macosx-10.7-intel-2.7/delaunay/spheremodule.o build/temp.macosx-10.7-intel-2.7/build/src.macosx-10.7-intel-2.7/fortranobject.o build/temp.macosx-10.7-intel-2.7/delaunay/stripack.o build/temp.macosx-10.7-intel-2.7/build/src.macosx-10.7-intel-2.7/delaunay/sphere-f2pywrappers.o -lgfortran -o build/lib.macosx-10.7-intel-2.7/delaunay/sphere.so 
ld: duplicate symbol _initsphere in build/temp.macosx-10.7-intel-2.7/build/src.macosx-10.7-intel-2.7/delaunay/spheremodule.o ldand :build /temp.macosx-10.7-intelduplicate- 2.7symbol/ delaunay/sphere.o _initsphere in forbuild architecture /i386 
temp.macosx-10.7-intel-2.7/build/src.macosx-10.7-intel-2.7/delaunay/spheremodule.o and build/temp.macosx-10.7-intel-2.7/delaunay/sphere.o for architecture x86_64 

अब हम यहां विवरणों को पूरा करने से पहले बस एक पल वापस कदम उठाएं। सबसे पहले, मुझे पता है कि 64-बिट मैक ओएस एक्स में आर्किटेक्चर क्लैश पर सिरदर्द का एक गुच्छा है; मुझे हिम तेंदुए मशीन पर काम कर रहे मैकपोर्ट पायथन (सिस्टम पायथन 2.6 से अपग्रेड करने के लिए) बहुत मुश्किल काम करना पड़ा। मुझे यह भी पता है कि जब आप gfortran -arch i686 -arch x86_64 देखते हैं तो आप अपने कंपाइलर में मिश्रित संदेश भेज रहे हैं। वहाँ प्लेटफार्म-विशिष्ट समस्याओं को दफन कर रहे हैं, कि हमें इस प्रश्न के संदर्भ में चिंता करने की आवश्यकता नहीं है।

लेकिन सिर्फ इस लाइन को देखो: gfortran:f77: build/src.macosx-10.7-intel-2.7/delaunay/sphere-f2pywrappers.f

numpy क्या कर रहा है ?! मुझे इस बिल्ड में किसी भी f2py सुविधाओं की आवश्यकता नहीं है! मैंने वास्तव में से बचने के लिए एक साइथन मॉड्यूल लिखा है ताकि मुझे f2py की पागलपन से निपटने के लिए (मुझे 4 या 5 आउटपुट वैरिएबल, साथ ही न तो इन-न-आउट तर्कों की आवश्यकता हो - इनमें से कोई भी f2py में अच्छी तरह से समर्थित नहीं है।) I बस इसे .c ->.o, और .f90 ->.o संकलित करना और उन्हें लिंक करना चाहते हैं। अगर मैं जानता हूं कि सभी प्रासंगिक शीर्षलेखों को कैसे शामिल किया जाए तो मैं खुद को इस कंपाइलर लाइन को लिख सकता हूं।

कृपया मुझे बताएं कि मुझे इसके लिए अपना खुद का मेकफ़ाइल लिखने की आवश्यकता नहीं है ... या कि फोर्टन को (आउटपुट-संगत) सी का अनुवाद करने का एक तरीका है, इसलिए मैं कभी भी pfthon को .f90 एक्सटेंशन (जो पूरी समस्या को हल करता है।) ध्यान दें कि f2c इसके लिए उपयुक्त नहीं है क्योंकि यह केवल F77 पर काम करता है और यह एक और आधुनिक बोली है (इसलिए .f90 फ़ाइल एक्सटेंशन)।

अद्यतन 2 निम्नलिखित बैश स्क्रिप्ट खुशी से संकलन और जगह में कोड लिंक करेगा:

PYTHON_H_LOCATION="/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7/" 

cython sphere.pyx 

gcc -arch x86_64 -c sphere.c -I$PYTHON_H_LOCATION 
gfortran -arch x86_64 -c stripack.f90 
gfortran -arch x86_64 -bundle -undefined dynamic_lookup -L/opt/local/lib *.o -o sphere.so 

कैसे एक setup.py के साथ संगत हैक इस तरह का बनाने के लिए पर कोई सलाह? मैं इस मॉड्यूल स्थापित करने कोई भी ढूंढ जाना है नहीं करना Python.h मैन्युअल ...

+1

फोरट्रान समर्थन 'numpy' से आते हैं लगता है। शायद आप आयात कर सकते हैं [ 'numpy.distutils.extension.Extension'] (http://www.scipy.org/doc/numpy_api_docs/numpy.distutils.extension.html) के बजाय' distutils.core.Extension' की। //docs.scipy: – MvG

+0

भी [NumPy distutils उपयोगकर्ता गाइड] (https://github.com/numpy/numpy/blob/master/doc/DISTUTILS.rst.txt) और [पैकेजिंग संदर्भ] (http पढ़ें। org/doc/numpy-1.6.0/संदर्भ/distutils.html)। – MvG

+0

क्या आपके पास gfortran इंस्टॉल है? आपको वास्तव में कुछ फोरट्रान कंपाइलर चाहिए, यह वास्तव में आवश्यक है। –

उत्तर

3

अद्यतन: मैं जो हाथ से संकलन लाइनों के इस पैदा अप लपेटता GitHub पर एक परियोजना बना लिया है। इसे complicated_build कहा जाता है।

अद्यतन 2: वास्तव में, "हाथ से पैदा" एक बहुत बुरा विचार है, क्योंकि यह मंच विशिष्ट — परियोजना अब (distutils.sysconfig मॉड्यूल है, जो अजगर संकलित करने के लिए इस्तेमाल किया सेटिंग्स है से मूल्यों को पढ़ता है यानी वास्तव में क्या हम चाहते हैं,) अनुमानित एकमात्र सेटिंग फोर्ट्रान कंपाइलर और फ़ाइल एक्सटेंशन (जो उपयोगकर्ता-कॉन्फ़िगर करने योग्य हैं) है। मुझे संदेह है कि यह अब दूरियों के एक उचित बिट को फिर से कार्यान्वित कर रहा है!


यह करने के लिए जिस तरह से अपने खुद के संकलक लाइनों लिखते हैं, और उन्हें अपने setup.py को हैक करने के लिए है। मैं जो नीचे एक उदाहरण मेरे (बहुत सरल) मामले है, जो निम्नलिखित strucutre है के लिए काम करता है दिखाने:

  • आयात
  • cythonize() किसी भी .pyx फ़ाइलें, ताकि आप केवल fortran और सी फ़ाइलों की है।
  • एक build() समारोह जो अपने कोड संकलित निर्धारित करें:
    • शायद कुछ आसान परिवर्तन स्थिरांक, संकलक नाम और वास्तुकला
    • सूची में ऊपर की तरह fortran और सी फाइलों
    • खोल आदेशों का निर्माण करेगा उत्पन्न मॉड्यूल
    • लिंकर लाइन
    • खोल आदेश चलाएं।
  • यदि आदेश install था और लक्ष्य अभी तक मौजूद नहीं है, तो इसे बनाएं।
  • रन सेटअप (जो शुद्ध पायथन खंडों का निर्माण करेगा)
  • यदि आदेश build था, तो अब बिल्ड चलाएं।

इसका मेरा कार्यान्वयन नीचे दिखाया गया है। यह केवल एक एक्सटेंशन मॉड्यूल के लिए डिज़ाइन किया गया है, और यह हर बार सभी फ़ाइलों को पुन: संकलित करता है, इसलिए अधिक सामान्य उपयोग होने के लिए और विस्तार की आवश्यकता हो सकती है। यह भी ध्यान रखें कि मैंने विभिन्न यूनिक्स / एस को हार्ड कोड किया है, इसलिए यदि आप इसे विंडोज़ पर पोर्ट कर रहे हैं तो सुनिश्चित करें कि आप os.path.sep के साथ अनुकूलित या प्रतिस्थापित करें।

from distutils.core import setup 
from distutils.sysconfig import get_python_inc 
from Cython.Build import cythonize 
import sys, os, shutil 

cythonize('delaunay/sphere.pyx') 

target = 'build/lib/delaunay/sphere.so' 

def build(): 
    fortran_compiler = 'gfortran' 
    c_compiler = 'gcc' 
    architecture = 'x86_64' 
    python_h_location = get_python_inc() 
    build_temp = 'build/custom_temp' 
    global target 

    try: 
    shutil.rmtree(build_temp) 
    except OSError: 
    pass 

    os.makedirs(build_temp) # if you get an error here, please ensure the build/ ... 
    # folder is writable by this user. 

    c_files = ['delaunay/sphere.c'] 
    fortran_files = ['delaunay/stripack.f90'] 

    c_compile_commands = [] 

    for cf in c_files: 
    # use the path (sans /s), without the extension, as the object file name: 
    components = os.path.split(cf) 
    name = components[0].replace('/', '') + '.'.join(components[1].split('.')[:-1]) 
    c_compile_commands.append(
     c_compiler + ' -arch ' + architecture + ' -I' + python_h_location + ' -o ' + 
     build_temp + '/' + name + '.o -c ' + cf 
    ) 

    fortran_compile_commands = [] 

    for ff in fortran_files: 
    # prefix with f in case of name collisions with c files: 
    components = os.path.split(ff) 
    name = components[0].replace('/', '') + 'f' + '.'.join(components[1].split('.')[:-1]) 
    fortran_compile_commands.append(
     fortran_compiler + ' -arch ' + architecture + ' -o ' + build_temp + 
     '/' + name + '.o -c ' + ff 
    ) 

    commands = c_compile_commands + fortran_compile_commands + [ 
    fortran_compiler + ' -arch ' + architecture + 
    ' -bundle -undefined dynamic_lookup ' + build_temp + '/*.o -o ' + target 
    ] 

    for c in commands: 
    os.system(c) 


if 'install' in sys.argv and not os.path.exists(target): 
    try: 
    os.makedirs('build/lib/delaunay') 
    except OSError: 
    # we don't care if the containing folder already exists. 
    pass 
    build() 

setup(
    name="delaunay", 
    version="0.1", 
    ... 
    packages=["delaunay"] 
) 

if 'build' in sys.argv: 
    build() 

यह एक नया Extension वर्ग मुझे लगता है कि में लिपटे जा सकता है के साथ अपने आप build_ext आदेश - उन्नत छात्र के लिए एक अभ्यास;)

2

आप का निर्माण और अजगर के बाहर अपने पुराने फोरट्रान पुस्तकालय स्थापित करते हैं, फिर distutils में इसे लिंक करें। आपका प्रश्न इंगित करता है कि आप इस लाइब्रेरी से गुस्सा नहीं करना चाहते हैं, इसलिए एक बार और सभी के लिए इंस्टॉल संभवतः (लाइब्रेरी के निर्माण और स्थापना निर्देशों का उपयोग करके) करेगा। फिर स्थापित बाहरी पुस्तकालय के लिए अजगर विस्तार लिंक:

ext_modules = [ 
    Extension("delaunay", 
       sources = ["delaunay/__init__.pyx"], 
       libraries = ["delaunay"]) 
] 

यह दृष्टिकोण मामला है कि आप महसूस करते हैं कि आप इस तरह के मैटलैब, सप्टक, आईडीएल, और साथ ही अन्य भाषाओं के लिए रैपर, जरूरत के लिए भी सुरक्षित है ...

अद्यतन

कुछ बिंदु पर, अगर आप कुछ इस तरह के बाहरी पुस्तकालयों कि आप रैप करने के लिए चाहते हैं की तुलना में अधिक के साथ खत्म हो, यह एक उच्च-स्तरीय निर्माण प्रणाली है कि इन सभी पुस्तकालयों को स्थापित करता है जोड़ने के लिए फायदेमंद है, और सभी रैपरों के निर्माण को भी प्रबंधित करता है। इस उद्देश्य के लिए मेरे पास cmake है, जो सिस्टम-व्यापी बिल्ड और इंस्टॉलेशन को संभालने में बहुत अच्छा है। हालांकि, यह बॉक्स से बाहर पायथन सामग्री का निर्माण नहीं कर सकता है, लेकिन प्रत्येक उपनिर्देशिका python में "python setup.py install" को कॉल करने के लिए आसानी से पढ़ाया जा सकता है, इस प्रकार distutils का आह्वान किया जा सकता है।तो कुल मिलाकर निर्माण प्रक्रिया इस प्रकार है:

mkdir build 
cd build 
cmake .. 
make 
make install 
make python 
(make octave) 
(make matlab) 

यह विशिष्ट सामने के अंत भाषाओं के लिए रैपर (यह भी अपने स्वयं के परियोजनाओं के लिए!) से करने के लिए हमेशा अलग कोर पुस्तकालय कोड बहुत महत्वपूर्ण है क्योंकि वे नहीं बल्कि तेजी से बदल जाते हैं । क्या अन्यथा होता numpy का उदाहरण में देखा जा सकता: इसके बजाय एक सामान्य प्रयोजन सी पुस्तकालय libndarray.so लेखन और अजगर के लिए पतली रैपर बनाने के बजाय, वहाँ अजगर सी API कॉल हर जगह स्रोतों में हैं। यह वही है अब वापस Pypy CPython के लिए एक गंभीर विकल्प के रूप में रखा है numpy वे CPython एपीआई, जो वे नहीं कर सकते के हर पिछले बिट के समर्थन के लिए प्राप्त करने के लिए आदेश में के बाद से, है, क्योंकि वे एक बस-इन-टाइम संकलक है और एक अलग कचरा कलेक्टर। इसका मतलब है कि हम कई संभावित सुधारों पर छूट रहे हैं।

निष्कर्ष: किसी

  1. अलग से सामान्य प्रयोजन फोरट्रान/सी पुस्तकालयों का निर्माण और उन्हें पूरे सिस्टम पर स्थापित करें।

  2. रैपर के लिए एक अलग निर्माण चरण है, जिसे जितना संभव हो हल्के वजन के रूप में रखा जाना चाहिए, ताकि अगले बड़ी भाषा एक्स के बारे में पता चलाना आसान हो। यदि एक सुरक्षित धारणा है, तो यह है कि एक्स सी पुस्तकालयों के साथ जुड़ने का समर्थन करेगा।

+0

जो एक बहुत ही सुरुचिपूर्ण समाधान है - अच्छी तरह से किया गया! मैं देखूंगा कि क्या मैं इसे काम कर सकता हूं, और यदि मैं कर सकता हूं तो "सही" उत्तर बदल सकता हूं। – tehwalrus

0

आप distutils के बाहर वस्तु फ़ाइल का निर्माण कर सकते तो जोड़ने कदम एक्सटेंशन के निर्माता को extra_objects तर्क का उपयोग करने में यह भी शामिल है। setup.py में:

... 
e = Extension(..., extra_objects = ['holycode.o']) 
... 

कमांड प्रॉम्प्ट पर:

# gfortran -c -fPIC holycode.f 
# ./setup.py build_ext ... 

केवल एक बाहरी वस्तु के साथ, इस से कई के लिए सबसे आसान तरीका हो जाएगा।

संबंधित मुद्दे