def builtin_import(expr): """Returns a builtin pyccel-extension function/object from an import.""" if not isinstance(expr, Import): raise TypeError('Expecting an Import expression') if isinstance(expr.source, AsName): source = str(expr.source.name) else: source = str(expr.source) if source == 'pyccel.decorators': funcs = [ f[0] for f in inspect.getmembers(pyccel_decorators, inspect.isfunction) ] for target in expr.target: search_target = target.name if isinstance(target, AsName) else str(target) if search_target not in funcs: errors = Errors() errors.report( "{} does not exist in pyccel.decorators".format(target), symbol=expr, severity='error') elif source in builtin_import_registery: return collect_relevant_imports(builtin_import_registery[source], expr.target) return []
def extend_tree(code): comment_lines_no, comments, else_no = get_comments(code) try: tree = parse(code) except SyntaxError as e: errors = Errors() errors.report(INVALID_PYTHON_SYNTAX, symbol='\n' + str(e), severity='fatal') if len(tree.body) == 0: if len(comments) > 0: tree.body = list(comments) return tree insert_comments(tree, comment_lines_no, comments, else_no) return tree
def pyccel(files=None, openmp=None, openacc=None, output_dir=None, compiler=None): """ pyccel console command. """ parser = MyParser(description='pyccel command line') parser.add_argument('files', metavar='N', type=str, nargs='+', help='a Pyccel file') #... Version import pyccel version = pyccel.__version__ libpath = pyccel.__path__[0] python = 'python {}.{}'.format(*sys.version_info) message = "pyccel {} from {} ({})".format(version, libpath, python) parser.add_argument('-V', '--version', action='version', version=message) # ... # ... compiler syntax, semantic and codegen group = parser.add_argument_group('Pyccel compiling stages') group.add_argument('-x', '--syntax-only', action='store_true', help='Using pyccel for Syntax Checking') group.add_argument('-e', '--semantic-only', action='store_true', help='Using pyccel for Semantic Checking') group.add_argument('-t', '--convert-only', action='store_true', help='Converts pyccel files only without build') # group.add_argument('-f', '--f2py-compatible', action='store_true', # help='Converts pyccel files to be compiled by f2py') # ... # ... backend compiler options group = parser.add_argument_group('Backend compiler options') group.add_argument('--language', choices=('fortran', 'c', 'python'), help='Generated language') group.add_argument('--compiler', choices=('gfortran', 'ifort', 'pgfortran', \ 'gcc', 'icc'), help='Compiler name') group.add_argument('--mpi-compiler', help='MPI compiler wrapper') group.add_argument('--flags', type=str, \ help='Compiler flags.') group.add_argument('--debug', action='store_true', \ help='compiles the code in a debug mode.') group.add_argument('--include', type=str, nargs='*', dest='includes', default=(), help='list of include directories.') group.add_argument('--libdir', type=str, nargs='*', dest='libdirs', default=(), help='list of library directories.') group.add_argument('--libs', type=str, nargs='*', dest='libs', default=(), help='list of libraries to link with.') group.add_argument('--output', type=str, default = '',\ help='folder in which the output is stored.') # ... # ... Accelerators group = parser.add_argument_group('Accelerators options') group.add_argument('--openmp', action='store_true', \ help='uses openmp') group.add_argument('--openacc', action='store_true', \ help='uses openacc') # ... # ... Other options group = parser.add_argument_group('Other options') group.add_argument('--verbose', action='store_true', \ help='enables verbose mode.') group.add_argument('--developer-mode', action='store_true', \ help='shows internal messages') # ... # TODO move to another cmd line parser.add_argument('--analysis', action='store_true', \ help='enables code analysis mode.') # ... # ... args = parser.parse_args() # ... # Imports from pyccel.errors.errors import Errors, PyccelError from pyccel.errors.errors import ErrorsMode from pyccel.errors.messages import INVALID_FILE_DIRECTORY, INVALID_FILE_EXTENSION from pyccel.codegen.pipeline import execute_pyccel # ... if not files: files = args.files if args.compiler: compiler = args.compiler if not openmp: openmp = args.openmp if not openacc: openacc = args.openacc if args.convert_only or args.syntax_only or args.semantic_only: compiler = None # ... # ... if len(files) > 1: errors = Errors() # severity is error to avoid needing to catch exception errors.report('Pyccel can currently only handle 1 file at a time', severity='error') errors.check() sys.exit(1) # ... filename = files[0] # ... report error if os.path.isfile(filename): # we don't use is_valid_filename_py since it uses absolute path # file extension ext = filename.split('.')[-1] if not(ext in ['py', 'pyh']): errors = Errors() # severity is error to avoid needing to catch exception errors.report(INVALID_FILE_EXTENSION, symbol=ext, severity='error') errors.check() sys.exit(1) else: # we use Pyccel error manager, although we can do it in other ways errors = Errors() # severity is error to avoid needing to catch exception errors.report(INVALID_FILE_DIRECTORY, symbol=filename, severity='error') errors.check() sys.exit(1) # ... if compiler: if _which(compiler) is None: errors = Errors() # severity is error to avoid needing to catch exception errors.report('Could not find compiler', symbol=compiler, severity='error') errors.check() sys.exit(1) accelerator = None if openmp: accelerator = "openmp" if openacc: accelerator = "openacc" # ... # ... if args.developer_mode: # this will initialize the singelton ErrorsMode # making this settings available everywhere err_mode = ErrorsMode() err_mode.set_mode('developer') # ... base_dirpath = os.getcwd() try: # TODO: prune options execute_pyccel(filename, syntax_only = args.syntax_only, semantic_only = args.semantic_only, convert_only = args.convert_only, verbose = args.verbose, language = args.language, compiler = compiler, mpi_compiler = args.mpi_compiler, fflags = args.flags, includes = args.includes, libdirs = args.libdirs, modules = (), libs = args.libs, debug = args.debug, extra_args = '', accelerator = accelerator, folder = args.output) except PyccelError: sys.exit(1) finally: os.chdir(base_dirpath) return
def execute_pyccel(fname, *, syntax_only=False, semantic_only=False, convert_only=False, recursive=False, verbose=False, folder=None, language=None, compiler=None, mpi_compiler=None, fflags=None, includes=(), libdirs=(), modules=(), libs=(), debug=False, extra_args='', accelerator=None, output_name=None): # Reset Errors singleton before parsing a new file errors = Errors() errors.reset() # TODO [YG, 03.02.2020]: test validity of function arguments # Copy list arguments to local lists to avoid unexpected behavior includes = [*includes] libdirs = [*libdirs] modules = [*modules] libs = [*libs] # Store current directory and add it to sys.path # variable to imitate Python's import behavior base_dirpath = os.getcwd() sys.path.insert(0, base_dirpath) # Unified way to handle errors: print formatted error message, then move # to original working directory. Caller should then raise exception. def handle_error(stage): print('\nERROR at {} stage'.format(stage)) errors.check() os.chdir(base_dirpath) # Identify absolute path, directory, and filename pymod_filepath = os.path.abspath(fname) pymod_dirpath, pymod_filename = os.path.split(pymod_filepath) # Extract module name module_name = os.path.splitext(pymod_filename)[0] # Define working directory 'folder' if folder is None or folder == "": folder = pymod_dirpath else: folder = os.path.abspath(folder) # Define directory name and path for pyccel & cpython build pyccel_dirname = '__pyccel__' pyccel_dirpath = os.path.join(folder, pyccel_dirname) # Create new directories if not existing os.makedirs(folder, exist_ok=True) os.makedirs(pyccel_dirpath, exist_ok=True) # Change working directory to 'folder' os.chdir(folder) if language is None: language = 'fortran' # Choose Fortran compiler if compiler is None: if language == 'fortran': compiler = 'gfortran' elif language == 'c': compiler = 'gcc' f90exec = mpi_compiler if mpi_compiler else compiler if (language == "c"): libs = libs + ['m'] if accelerator == 'openmp': if compiler in ["gcc", "gfortran"]: if sys.platform == "darwin" and compiler == "gcc": libs = libs + ['omp'] else: libs = libs + ['gomp'] elif compiler == 'ifort': libs.append('iomp5') # ... # Construct flags for the Fortran compiler if fflags is None: fflags = construct_flags(f90exec, fflags=None, debug=debug, accelerator=accelerator, includes=()) # Build position-independent code, suited for use in shared library fflags = ' {} -fPIC '.format(fflags) # ... # Parse Python file try: parser = Parser(pymod_filepath, show_traceback=verbose) parser.parse(verbose=verbose) except NotImplementedError as error: msg = str(error) errors.report(msg + '\n' + PYCCEL_RESTRICTION_TODO, severity='error') except PyccelError: handle_error('parsing (syntax)') raise if errors.has_errors(): handle_error('parsing (syntax)') raise PyccelSyntaxError('Syntax step failed') if syntax_only: return # Annotate abstract syntax Tree try: settings = {'verbose': verbose} parser.annotate(**settings) except NotImplementedError as error: msg = str(error) errors.report(msg + '\n' + PYCCEL_RESTRICTION_TODO, severity='error') except PyccelError: handle_error('annotation (semantic)') # Raise a new error to avoid a large traceback raise PyccelSemanticError('Semantic step failed') from None if errors.has_errors(): handle_error('annotation (semantic)') raise PyccelSemanticError('Semantic step failed') if semantic_only: return if parser.module_parser: parsers = [parser.module_parser, parser] program_name = os.path.basename(os.path.splitext(parser.filename)[0]) module_names = [module_name, program_name] else: parsers = [parser] module_names = [module_name] for parser, module_name in zip(parsers, module_names): semantic_parser = parser.semantic_parser # Generate .f90 file try: codegen = Codegen(semantic_parser, module_name) fname = os.path.join(pyccel_dirpath, module_name) fname = codegen.export(fname, language=language) except NotImplementedError as error: msg = str(error) errors.report(msg + '\n' + PYCCEL_RESTRICTION_TODO, severity='error') except PyccelError: handle_error('code generation') # Raise a new error to avoid a large traceback raise PyccelCodegenError('Code generation failed') from None if errors.has_errors(): handle_error('code generation') raise PyccelCodegenError('Code generation failed') #------------------------------------------------------ # TODO: collect dependencies and proceed recursively # if recursive: # for dep in parser.sons: # # Call same function on 'dep' # pass #------------------------------------------------------ if convert_only: continue # ... # Determine all .o files and all folders needed by executable def get_module_dependencies(parser, mods=(), folders=()): mod_folder = os.path.join(os.path.dirname(parser.filename), "__pyccel__") mod_base = os.path.splitext(os.path.basename(parser.filename))[0] # Stop conditions if parser.metavars.get('ignore_at_import', False) or \ parser.metavars.get('module_name', None) == 'omp_lib': return mods, folders # Update lists mods = [*mods, os.path.join(mod_folder, mod_base)] folders = [*folders, mod_folder] # Proceed recursively for son in parser.sons: mods, folders = get_module_dependencies(son, mods, folders) return mods, folders dep_mods, inc_dirs = get_module_dependencies(parser) # Remove duplicates without changing order dep_mods = tuple(OrderedDict.fromkeys(dep_mods)) inc_dirs = tuple(OrderedDict.fromkeys(inc_dirs)) # ... includes += inc_dirs if codegen.is_program: modules += [os.path.join(pyccel_dirpath, m) for m in dep_mods[1:]] # Construct compiler flags flags = construct_flags(f90exec, fflags=fflags, debug=debug, accelerator=accelerator, includes=includes) # Compile Fortran code # # TODO: stop at object files, do not compile executable # This allows for properly linking program to modules # try: compile_files(fname, f90exec, flags, binary=None, verbose=verbose, modules=modules, is_module=codegen.is_module, output=pyccel_dirpath, libs=libs, libdirs=libdirs, language=language) except Exception: handle_error('Fortran compilation') raise # For a program stop here if codegen.is_program: if verbose: exec_filepath = os.path.join(folder, module_name) print( '> Executable has been created: {}'.format(exec_filepath)) os.chdir(base_dirpath) continue # Create shared library try: sharedlib_filepath = create_shared_library( codegen, language, pyccel_dirpath, compiler, mpi_compiler, accelerator, dep_mods, libs, libdirs, includes, flags, extra_args, output_name, verbose) except NotImplementedError as error: msg = str(error) errors.report(msg + '\n' + PYCCEL_RESTRICTION_TODO, severity='error') handle_error('code generation (wrapping)') raise PyccelCodegenError(msg) from None except PyccelError: handle_error('code generation (wrapping)') raise except Exception: handle_error('shared library generation') raise if errors.has_errors(): handle_error('code generation (wrapping)') raise PyccelCodegenError('Code generation failed') # Move shared library to folder directory # (First construct absolute path of target location) sharedlib_filename = os.path.basename(sharedlib_filepath) target = os.path.join(folder, sharedlib_filename) shutil.move(sharedlib_filepath, target) sharedlib_filepath = target if verbose: print('> Shared library has been created: {}'.format( sharedlib_filepath)) # Print all warnings now if errors.has_warnings(): errors.check() # Change working directory back to starting point os.chdir(base_dirpath)
def get_filename_from_import(module, input_folder=''): """Returns a valid filename with absolute path, that corresponds to the definition of module. The priority order is: - header files (extension == pyh) - python files (extension == py) """ if (isinstance(module, AsName)): module = str(module.name) # Remove first '.' as it doesn't represent a folder change if module[0] == '.': module = module[1:] filename = module.replace('.', '/') # relative imports folder_above = '../' while filename.startswith('/'): filename = folder_above + filename[1:] filename_pyh = '{}.pyh'.format(filename) filename_py = '{}.py'.format(filename) if is_valid_filename_pyh(filename_pyh): return os.path.abspath(filename_pyh) if is_valid_filename_py(filename_py): return os.path.abspath(filename_py) folders = input_folder.split(""".""") for i in range(len(folders)): poss_dirname = os.path.join(*folders[:i + 1]) poss_filename_pyh = os.path.join(poss_dirname, filename_pyh) poss_filename_py = os.path.join(poss_dirname, filename_py) if is_valid_filename_pyh(poss_filename_pyh): return os.path.abspath(poss_filename_pyh) if is_valid_filename_py(poss_filename_py): return os.path.abspath(poss_filename_py) source = module if len(module.split(""".""")) > 1: # we remove the last entry, since it can be a pyh file source = """.""".join(i for i in module.split(""".""")[:-1]) _module = module.split(""".""")[-1] filename_pyh = '{}.pyh'.format(_module) filename_py = '{}.py'.format(_module) try: package = importlib.import_module(source) package_dir = str(package.__path__[0]) except ImportError: errors = Errors() errors.report(PYCCEL_UNFOUND_IMPORTED_MODULE, symbol=source, severity='fatal') filename_pyh = os.path.join(package_dir, filename_pyh) filename_py = os.path.join(package_dir, filename_py) if os.path.isfile(filename_pyh): return filename_pyh elif os.path.isfile(filename_py): return filename_py errors = Errors() errors.report(PYCCEL_UNFOUND_IMPORTED_MODULE, symbol=module, severity='fatal')
def execute_pyccel(fname, *, syntax_only=False, semantic_only=False, convert_only=False, verbose=False, folder=None, language=None, compiler=None, mpi_compiler=None, fflags=None, includes=(), libdirs=(), modules=(), libs=(), debug=False, accelerator=None, output_name=None): """ Carries out the main steps required to execute pyccel - Parses the python file (syntactic stage) - Annotates the abstract syntax tree (semantic stage) - Generates the translated file(s) (codegen stage) - Compiles the files to generate an executable and/or a shared library Parameters ---------- fname : str Name of python file to be translated syntax_only : bool Boolean indicating whether the pipeline should stop after the syntax stage Default : False semantic_only : bool Boolean indicating whether the pipeline should stop after the semantic stage Default : False convert_only : bool Boolean indicating whether the pipeline should stop after the codegen stage Default : False verbose : bool Boolean indicating whether debugging messages should be printed Default : False folder : str Path to the working directory Default : folder containing the file to be translated language : str The language which pyccel is translating to Default : fortran compiler : str The compiler used to compile the generated files Default : GNU mpi_compiler : str The compiler used to compile the generated files when mpi is needed. This value must be provided to compile with mpi Default : None (compile with 'compiler') fflags : str The flags passed to the compiler Default : provided by codegen.utilities.construct_flags includes : list list of include directories paths libdirs : list list of paths to directories containing the required libraries modules : list list of files which must also be compiled in order to compile this module libs : list list of required libraries debug : bool Boolean indicating whether the file should be compiled in debug mode (currently this only implies that the flag -fcheck=bounds is added) Default : False accelerator : str Tool used to accelerate the code (e.g. openmp openacc) output_name : str Name of the generated module Default : Same name as the file which was translated """ # Reset Errors singleton before parsing a new file errors = Errors() errors.reset() # TODO [YG, 03.02.2020]: test validity of function arguments # Copy list arguments to local lists to avoid unexpected behavior includes = [*includes] libdirs = [*libdirs] modules = [*modules] libs = [*libs] # Store current directory and add it to sys.path # variable to imitate Python's import behavior base_dirpath = os.getcwd() sys.path.insert(0, base_dirpath) # Unified way to handle errors: print formatted error message, then move # to original working directory. Caller should then raise exception. def handle_error(stage): print('\nERROR at {} stage'.format(stage)) errors.check() os.chdir(base_dirpath) # Identify absolute path, directory, and filename pymod_filepath = os.path.abspath(fname) pymod_dirpath, pymod_filename = os.path.split(pymod_filepath) # Extract module name module_name = os.path.splitext(pymod_filename)[0] # Define working directory 'folder' if folder is None or folder == "": folder = pymod_dirpath else: folder = os.path.abspath(folder) # Define directory name and path for pyccel & cpython build pyccel_dirname = '__pyccel__' pyccel_dirpath = os.path.join(folder, pyccel_dirname) # Create new directories if not existing os.makedirs(folder, exist_ok=True) os.makedirs(pyccel_dirpath, exist_ok=True) # Change working directory to 'folder' os.chdir(folder) if language is None: language = 'fortran' # Choose Fortran compiler if compiler is None: if language == 'fortran': compiler = 'gfortran' elif language == 'c': compiler = 'gcc' f90exec = mpi_compiler if mpi_compiler else compiler if (language == "c"): libs = libs + ['m'] if accelerator == 'openmp': if compiler in ["gcc", "gfortran"]: if sys.platform == "darwin" and compiler == "gcc": libs = libs + ['omp'] else: libs = libs + ['gomp'] elif compiler == 'ifort': libs.append('iomp5') # ... # Construct flags for the Fortran compiler if fflags is None: fflags = construct_flags(f90exec, fflags=None, debug=debug, accelerator=accelerator, includes=()) # Build position-independent code, suited for use in shared library fflags = ' {} -fPIC '.format(fflags) # ... # Parse Python file try: parser = Parser(pymod_filepath, show_traceback=verbose) parser.parse(verbose=verbose) except NotImplementedError as error: msg = str(error) errors.report(msg + '\n' + PYCCEL_RESTRICTION_TODO, severity='error') except PyccelError: handle_error('parsing (syntax)') raise if errors.has_errors(): handle_error('parsing (syntax)') raise PyccelSyntaxError('Syntax step failed') if syntax_only: return # Annotate abstract syntax Tree try: settings = {'verbose': verbose} parser.annotate(**settings) except NotImplementedError as error: msg = str(error) errors.report(msg + '\n' + PYCCEL_RESTRICTION_TODO, severity='error') except PyccelError: handle_error('annotation (semantic)') # Raise a new error to avoid a large traceback raise PyccelSemanticError('Semantic step failed') from None if errors.has_errors(): handle_error('annotation (semantic)') raise PyccelSemanticError('Semantic step failed') if semantic_only: return if parser.module_parser: parsers = [parser.module_parser, parser] program_name = os.path.basename(os.path.splitext(parser.filename)[0]) module_names = [module_name, program_name] else: parsers = [parser] module_names = [module_name] # ------------------------------------------------------------------------- # get path to pyccel/stdlib/lib_name stdlib_path = os.path.dirname(stdlib_folder.__file__) internal_libs_name = set() internal_libs_path = [] internal_libs_files = [] for parser, module_name in zip(parsers, module_names): semantic_parser = parser.semantic_parser # Generate .f90 file try: codegen = Codegen(semantic_parser, module_name) fname = os.path.join(pyccel_dirpath, module_name) fname = codegen.export(fname, language=language) except NotImplementedError as error: msg = str(error) errors.report(msg + '\n' + PYCCEL_RESTRICTION_TODO, severity='error') except PyccelError: handle_error('code generation') # Raise a new error to avoid a large traceback raise PyccelCodegenError('Code generation failed') from None if errors.has_errors(): handle_error('code generation') raise PyccelCodegenError('Code generation failed') #------------------------------------------------------ # TODO: collect dependencies and proceed recursively # if recursive: # for dep in parser.sons: # # Call same function on 'dep' # pass #------------------------------------------------------ # Iterate over the internal_libs list and determine if the printer # requires an internal lib to be included. for lib in internal_libs: if lib in codegen.get_printer_imports(): # get the include folder path and library files if lib not in internal_libs_name: # get the library folder name lib_name = internal_libs[lib] # get lib path (stdlib_path/lib_name) lib_path = os.path.join(stdlib_path, lib_name) # remove library folder to avoid missing files and copy # new one from pyccel stdlib lib_dest_path = os.path.join(pyccel_dirpath, lib_name) if os.path.exists(lib_dest_path): shutil.rmtree(lib_dest_path) shutil.copytree(lib_path, lib_dest_path) # stop after copying lib to __pyccel__ directory for # convert only if convert_only: continue # get library source files source_files = [] for e in os.listdir(lib_dest_path): if e.endswith(lang_ext_dict[language]): source_files.append(os.path.join(lib_dest_path, e)) # compile library source files flags = construct_flags(f90exec, fflags=fflags, debug=debug, includes=[lib_dest_path]) try: for f in source_files: compile_files(f, f90exec, flags, binary=None, verbose=verbose, is_module=True, output=lib_dest_path, language=language) except Exception: handle_error('C {} library compilation'.format(lib)) raise # Add internal lib to internal_libs_name set internal_libs_name.add(lib) # add source file without extension to internal_libs_files internal_libs_files.extend( os.path.splitext(f)[0] for f in source_files) # add library path to internal_libs_path internal_libs_path.append(lib_dest_path) if convert_only: continue # ... # Determine all .o files and all folders needed by executable def get_module_dependencies(parser, mods=(), folders=()): mod_folder = os.path.join(os.path.dirname(parser.filename), "__pyccel__") mod_base = os.path.splitext(os.path.basename(parser.filename))[0] # Stop conditions if parser.metavars.get('ignore_at_import', False) or \ parser.metavars.get('module_name', None) == 'omp_lib': return mods, folders # Update lists mods = [*mods, os.path.join(mod_folder, mod_base)] folders = [*folders, mod_folder] # Proceed recursively for son in parser.sons: mods, folders = get_module_dependencies(son, mods, folders) return mods, folders dep_mods, inc_dirs = get_module_dependencies(parser) # Add internal dependencies dep_mods = [*dep_mods, *internal_libs_files] inc_dirs = [*inc_dirs, *internal_libs_path] # Remove duplicates without changing order dep_mods = tuple(OrderedDict.fromkeys(dep_mods)) inc_dirs = tuple(OrderedDict.fromkeys(inc_dirs)) # ... includes += inc_dirs if codegen.is_program: modules += [os.path.join(pyccel_dirpath, m) for m in dep_mods[1:]] # Construct compiler flags flags = construct_flags(f90exec, fflags=fflags, debug=debug, accelerator=accelerator, includes=includes) # Compile Fortran code # # TODO: stop at object files, do not compile executable # This allows for properly linking program to modules # try: compile_files(fname, f90exec, flags, binary=None, verbose=verbose, modules=modules, is_module=codegen.is_module, output=pyccel_dirpath, libs=libs, libdirs=libdirs, language=language) except Exception: handle_error('Fortran compilation') raise # For a program stop here if codegen.is_program: if verbose: exec_filepath = os.path.join(folder, module_name) print( '> Executable has been created: {}'.format(exec_filepath)) os.chdir(base_dirpath) continue # Create shared library try: sharedlib_filepath = create_shared_library( codegen, language, pyccel_dirpath, compiler, mpi_compiler, accelerator, dep_mods, libs, libdirs, includes, flags, output_name, verbose) except NotImplementedError as error: msg = str(error) errors.report(msg + '\n' + PYCCEL_RESTRICTION_TODO, severity='error') handle_error('code generation (wrapping)') raise PyccelCodegenError(msg) from None except PyccelError: handle_error('code generation (wrapping)') raise except Exception: handle_error('shared library generation') raise if errors.has_errors(): handle_error('code generation (wrapping)') raise PyccelCodegenError('Code generation failed') # Move shared library to folder directory # (First construct absolute path of target location) sharedlib_filename = os.path.basename(sharedlib_filepath) target = os.path.join(folder, sharedlib_filename) shutil.move(sharedlib_filepath, target) sharedlib_filepath = target if verbose: print('> Shared library has been created: {}'.format( sharedlib_filepath)) # Print all warnings now if errors.has_warnings(): errors.check() # Change working directory back to starting point os.chdir(base_dirpath)