dm_k
dm_k

Reputation: 141

Compiling Cython with Xcode 10

I have tried to compile a simple example with Cython and got this linker error (macOS, Xcode 10):

gcc -fno-strict-aliasing -I/anaconda2/envs/python2.7-base/include -arch x86_64 -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -I/anaconda2/envs/python2.7-base/include/python2.7 -c noway.cpp -o build/temp.macosx-10.6-x86_64-2.7/noway.o -std=c++11 -Os -stdlib=libc++ -mmacosx-version-min=10.7
g++ -bundle -undefined dynamic_lookup -L/anaconda2/envs/python2.7-base/lib -arch x86_64 -arch x86_64 build/temp.macosx-10.6-x86_64-2.7/one.o build/temp.macosx-10.6-x86_64-2.7/noway.o -L/anaconda2/envs/python2.7-base/lib -o /Users/dkotsur/Projects/InCeM/IF-MedialAxis/test/noway.so
clang: warning: libstdc++ is deprecated; move to libc++ with a minimum deployment target of OS X 10.9 [-Wdeprecated]
ld: library not found for -lstdc++
clang: error: linker command failed with exit code 1 (use -v to see invocation)
error: command 'g++' failed with exit status 1

My anaconda environment is this:

Python 2.7.15 |Anaconda custom (64-bit)| (default, May  1 2018, 18:37:05) 
[GCC 4.2.1 Compatible Clang 4.0.1 (tags/RELEASE_401/final)] on darwin

It seems like anaconda uses libstdc++ instead of libc++ for Mac. But I have no clues how to fix it.

Had anyone have something similar? Any clues on how to deal with that?

Here is my code setup.py

if platform.system() == "Windows":
    extra_args = ["/std:c++latest", "/EHsc"]
elif platform.system() == "Darwin":
    extra_args = ['-std=c++11', "-Os", "-stdlib=libc++", "-mmacosx-version-min=10.7"]
else:
    extra_args = []

compile_files = [
    "one.pyx",
    "noway.cpp",
]

include_paths = [
]

ext_modules = [Extension("noway", compile_files,
                         language='c++',
                         include_dirs=include_paths,
                         extra_compile_args=extra_args)]

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

one.pyx:

import cython

cdef extern from "noway.hpp":
    cdef void noway();


def nway():
    noway()

noway.hpp

#ifndef noway_h
#define noway_h

void noway();

#endif

noway.cpp

#include "noway.hpp"
#include <iostream>

void noway() {
    std::cout << "No way!!!" << std::endl;
}

Upvotes: 6

Views: 1947

Answers (2)

nfranzmeier
nfranzmeier

Reputation: 121

I ran into a related program trying to update an old ionics-2 program which uses various node modules with alot of python scripts. I ended up writing a program which allowed me to use the 3rd party nodes without patching them to fix this issue. I ran across this post when coming up with the solution.

The program basically patches the clang++ arguments on the fly to replace the -mmacosx-version-min=xxx parameter and writes a log of the environment and arguments to /tmp/vardump.txt

to use it just build it with something like:

gcc clang_force.c -o clang_force

then copy it to /usr/local/bin cp clang_force /usr/local/bin then change the CXX environment variable to use it. export CXX=/usr/local/bin/force_clang

That's it. Then just do the 'npm install' or your python script again and everything is good. Hope someone finds it useful.

Get the code fix on this site: https://github.com/nodejs/node-gyp/issues/1827

/**************************************************************************
 Force clang to use version 10.9 or greater to allow building legacy code
 (overrides any -mmacosx-version-min params passed in)

 ***************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>


#define WAIT_FOR_COMPLETION
static int exec_prog(char **argv);

int main(int argc, char **argv, char **envp)
{

  FILE *fp;
  int rc = 0;

  fp = fopen("/tmp/vardump.txt","a+");
  if (fp!=NULL) {
        for (char **env = envp; *env != 0; env++)
        {
                char *thisEnv = *env;
                fprintf(fp,"%s\n", thisEnv);
        }
        int counter;
        fprintf(fp,"Program Name Is: %s",argv[0]);
        if(argc==1)
                fprintf(fp,"\nNo Extra Command Line Argument Passed Other Than Program Name");
        if(argc>=2)
        {
                fprintf(fp,"\nNumber Of Arguments Passed: %d",argc);
                fprintf(fp,"\n----Following Are The Command Line Arguments Passed----");
                for(counter=0;counter<argc;counter++) {
                        fprintf(fp,"\nargv[%d]: %s",counter,argv[counter]);
                        if (strncmp(argv[counter],"-mmacosx-version-min",20)==0) {
                                argv[counter] = "-mmacosx-version-min=10.9";
                                fprintf(fp,"\nForcing argument to %s",argv[counter]);
                        }
                }

        }
        fclose(fp);
        argv[0] = "/usr/bin/clang++";
        rc = exec_prog(argv);
  }
  return rc;
}
static int exec_prog(char **argv)
{
    FILE *fp;
    fp = fopen("/tmp/vardump.txt","a+");
    if (fp!=NULL) {
        fprintf(fp,"\nExecuting %s\n",argv[0]);
        fclose(fp);
    }
    pid_t   my_pid;
    int     status, timeout /* unused ifdef WAIT_FOR_COMPLETION */;

    if (0 == (my_pid = fork())) {
            if (-1 == execve(argv[0], (char **)argv , NULL)) {
                    perror("child process execve failed [%m]");
                    return -1;
            }
    }

#ifdef WAIT_FOR_COMPLETION
    timeout = 1000;
    while (0 == waitpid(my_pid , &status , WNOHANG)) {
            if ( --timeout < 0 ) {
                    perror("timeout");
                    return -1;
            }
            sleep(1);
    }
    fp = fopen("/tmp/vardump.txt","a+");
    if (fp!=NULL) {


        fprintf(fp,"\n %s WEXITSTATUS %d WIFEXITED %d [status %d]\n",
                argv[0], WEXITSTATUS(status), WIFEXITED(status), status);

        if (1 != WIFEXITED(status) || 0 != WEXITSTATUS(status)) {
                perror("failed, halt system");
                return -1;
        }
        fclose(fp);
   }

#endif
    return 0;
}





Upvotes: 0

dm_k
dm_k

Reputation: 141

Thanks, @ead for the comment. You helped a lot.

I have fixed setup.py by adding -stdlib=stdc++ to extra link args instead of compile args. I also had to add -mmacosx-version-min=10.9 to the extra link args.

So, setup.py looks now like this:

import platform
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
from Cython.Build import cythonize

compile_extra_args = []
link_extra_args = []

if platform.system() == "Windows":
    compile_extra_args = ["/std:c++latest", "/EHsc"]
elif platform.system() == "Darwin":
    compile_extra_args = ['-std=c++11', "-mmacosx-version-min=10.9"]
    link_extra_args = ["-stdlib=libc++", "-mmacosx-version-min=10.9"]


compile_files = [
    "one.pyx",
    "noway.cpp",
]

ext_modules = [Extension("noway", compile_files,
                         language='c++',
                         extra_compile_args=compile_extra_args,
                         extra_link_args=link_extra_args)]

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

Upvotes: 7

Related Questions