def install_tinycbor() -> Optional[str]: """ download, unpack, build, and install tinycbor. """ cc_cmd_db = os.path.join(c.CBOR_SRC, "compile_commands.json") def path_to_cc_db(): if not os.path.isfile(cc_cmd_db) and not on_mac(): die("not found: " + cc_cmd_db) return cc_cmd_db # skip recompilation iff: # 1. cbor appears to have been installed, # 2. we have the right archive downloaded (catches version changes), and # 3. we have a compile commands database for sanity testing or # we're on mac where we can't use bear to generate the database. if os.path.isdir(c.CBOR_PREFIX) and \ os.path.isfile(c.CBOR_ARCHIVE) and \ (os.path.isfile(cc_cmd_db) or on_mac()): logging.debug("skipping tinycbor installation") return path_to_cc_db() # download if not os.path.isfile(c.CBOR_ARCHIVE): curl = get_cmd_or_die("curl") curl['-s', c.CBOR_URL, '-o', c.CBOR_ARCHIVE] & pb.TEE # remove any existing build dir since we don't know if # tinycbor was built for the current host environment. if os.path.isdir(c.CBOR_SRC): shutil.rmtree(c.CBOR_SRC, ignore_errors=True) # unpack tar = get_cmd_or_die("tar") with pb.local.cwd(c.DEPS_DIR): tar['xf', c.CBOR_ARCHIVE] & pb.TEE # update install prefix update_cbor_prefix(os.path.join(c.CBOR_SRC, "Makefile")) # make && install # NOTE: we use bear to wrap make invocations such that # we get a .json database of compiler commands that we # can use to test ast-exporter. On macOS, bear requires # system integrity protection to be turned off, so we # only use bear on Ubuntu Linux hosts. with pb.local.cwd(c.CBOR_SRC): make = get_cmd_or_die("make") if not on_mac(): bear = get_cmd_or_die(c.BEAR_BIN) make = bear[make] make & pb.TEE # nopep8 make('install') # & pb.TEE return path_to_cc_db()
def _parse_args(): """ define and parse command line arguments here. """ desc = 'download dependencies for the AST exporter and built it.' parser = argparse.ArgumentParser(description=desc) parser.add_argument('-c', '--clean-all', default=False, action='store_true', dest='clean_all', help='clean everything before building') parser.add_argument('--with-clang', default=False, action='store_true', dest='with_clang', help='build clang with this tool') llvm_ver_help = 'fetch and build specified version of clang/LLVM (default: {})'.format(c.LLVM_VER) # FIXME: build this list by globbing for scripts/llvm-*.0.*-key.asc llvm_ver_choices = ["6.0.0", "6.0.1", "7.0.0", "7.0.1", "8.0.0"] parser.add_argument('--with-llvm-version', default=None, action='store', dest='llvm_ver', help=llvm_ver_help, choices=llvm_ver_choices) parser.add_argument('--without-assertions', default=True, action='store_false', dest='assertions', help='build the tool and clang without assertions') parser.add_argument('-x', '--xcode', default=False, action='store_true', dest='xcode', help='generate Xcode project files (macOS only)') parser.add_argument('-v', '--verbose', default=False, action='store_true', dest='verbose', help='emit verbose information during build') c.add_args(parser) args = parser.parse_args() if not on_mac() and args.xcode: die("-x/--xcode option requires macOS host.") c.update_args(args) return args
def build_transpiler(args): cargo = get_cmd_or_die("cargo") build_flags = ["build", "--features", "llvm-static"] if not args.debug: build_flags.append("--release") llvm_config = os.path.join(c.LLVM_BLD, "bin/llvm-config") assert os.path.isfile(llvm_config), "missing binary: " + llvm_config if on_mac(): llvm_system_libs = "-lz -lcurses -lm -lxml2" else: # linux llvm_system_libs = "-lz -lrt -ltinfo -ldl -lpthread -lm" llvm_libdir = os.path.join(c.LLVM_BLD, "lib") # log how we run `cargo build` to aid troubleshooting, IDE setup, etc. msg = "invoking cargo build as\ncd {} && \\\n".format(c.C2RUST_DIR) msg += "LLVM_CONFIG_PATH={} \\\n".format(llvm_config) msg += "LLVM_SYSTEM_LIBS='{}' \\\n".format(llvm_system_libs) msg += "C2RUST_AST_EXPORTER_LIB_DIR={} \\\n".format(llvm_libdir) msg += " cargo +{} ".format(c.CUSTOM_RUST_NAME) msg += " ".join(build_flags) logging.debug(msg) with pb.local.cwd(c.C2RUST_DIR): with pb.local.env(LLVM_CONFIG_PATH=llvm_config, LLVM_SYSTEM_LIBS=llvm_system_libs, C2RUST_AST_EXPORTER_LIB_DIR=llvm_libdir): # build with custom rust toolchain invoke(cargo, "+" + c.CUSTOM_RUST_NAME, *build_flags)
def _main(): if on_mac(): die("Cross-checking is only supported on Linux hosts.") setup_logging() logging.debug("args: %s", " ".join(sys.argv)) # earlier plumbum versions are missing features such as TEE if pb.__version__ < c.MIN_PLUMBUM_VERSION: err = "locally installed version {} of plumbum is too old.\n" \ .format(pb.__version__) err += "please upgrade plumbum to version {} or later." \ .format(c.MIN_PLUMBUM_VERSION) die(err) args = _parse_args() if args.clean_all: logging.info("cleaning all dependencies and previous built files") shutil.rmtree(c.CLANG_XCHECK_PLUGIN_BLD, ignore_errors=True) make = get_cmd_or_die('make') with pb.local.cwd(c.LIBFAKECHECKS_DIR): make('clean') # clang 3.6.0 is known to work; 3.4.0 known to not work. ensure_clang_version([3, 6, 0]) # NOTE: it seems safe to disable this check since we now # that we use a rust-toolchain file for rustc versioning. # ensure_rustc_version(c.CUSTOM_RUST_RUSTC_VERSION) ensure_dir(c.CLANG_XCHECK_PLUGIN_BLD) ensure_dir(c.BUILD_DIR) git_ignore_dir(c.BUILD_DIR) build_clang_plugin(args)
def translate(self, cc_db, extra_args: List[str] = []) -> RustFile: extensionless_file, _ = os.path.splitext(self.path) # help plumbum find rust ld_lib_path = get_rust_toolchain_libpath() if 'LD_LIBRARY_PATH' in pb.local.env: ld_lib_path += ':' + pb.local.env['LD_LIBRARY_PATH'] # run the transpiler transpiler = get_cmd_or_die(c.TRANSPILER) args = [ cc_db, "--prefix-function-names", "rust_", "--overwrite-existing", ] if self.disable_incremental_relooper: args.append("--no-incremental-relooper") if self.disallow_current_block: args.append("--fail-on-multiple") if self.translate_const_macros: args.append("--translate-const-macros") if self.reorganize_definitions: args.append("--reorganize-definitions") if self.emit_build_files: args.append("--emit-build-files") if self.logLevel == 'DEBUG': args.append("--log-level=debug") args.append("--") args.extend(extra_args) # Add -isysroot on MacOS to get SDK directory if on_mac(): try: xcrun = pb.local["xcrun"] args.append("-isysroot" + xcrun("--show-sdk-path").strip()) except pb.CommandNotFound: pass with pb.local.env(RUST_BACKTRACE='1', LD_LIBRARY_PATH=ld_lib_path): # log the command in a format that's easy to re-run translation_cmd = "LD_LIBRARY_PATH=" + ld_lib_path + " \\\n" translation_cmd += str(transpiler[args]) logging.debug("translation command:\n %s", translation_cmd) retcode, stdout, stderr = (transpiler[args]).run( retcode=None) logging.debug("stdout:\n%s", stdout) logging.debug("stderr:\n%s", stderr) if retcode != 0: raise NonZeroReturn(stderr) return RustFile(extensionless_file + ".rs")
def _main(): setup_logging() logging.debug("args: %s", " ".join(sys.argv)) # FIXME: allow env/cli override of LLVM_SRC, LLVM_VER, and LLVM_BLD # FIXME: check that cmake and ninja are installed # FIXME: option to build LLVM/Clang from master? # earlier plumbum versions are missing features such as TEE if pb.__version__ < c.MIN_PLUMBUM_VERSION: err = "locally installed version {} of plumbum is too old.\n" \ .format(pb.__version__) err += "please upgrade plumbum to version {} or later." \ .format(c.MIN_PLUMBUM_VERSION) die(err) args = _parse_args() if args.clean_all: logging.info("cleaning all dependencies and previous built files") shutil.rmtree(c.LLVM_SRC, ignore_errors=True) shutil.rmtree(c.LLVM_BLD, ignore_errors=True) shutil.rmtree(c.DEPS_DIR, ignore_errors=True) # prerequisites if not have_rust_toolchain(c.CUSTOM_RUST_NAME): die("missing rust toolchain: " + c.CUSTOM_RUST_NAME, errno.ENOENT) # clang 3.6.0 is known to work; 3.4.0 known to not work. ensure_clang_version([3, 6, 0]) ensure_rustc_version(c.CUSTOM_RUST_RUSTC_VERSION) ensure_dir(c.LLVM_BLD) ensure_dir(c.DEPS_DIR) git_ignore_dir(c.DEPS_DIR) if on_linux(): build_a_bear() if not os.path.isfile(c.BEAR_BIN): die("bear not found", errno.ENOENT) download_llvm_sources() integrate_ast_exporter() cc_db = install_tinycbor() configure_and_build_llvm(args) # NOTE: we're not doing this anymore since it is # faster and takes less space to simply pull the # prebuilt nightly binaries with rustup # download_and_build_custom_rustc(args) build_ast_importer(args.debug) if not on_mac() and args.sanity_test: test_ast_exporter(cc_db)
def build_transpiler(args): nice = get_cmd_or_die("nice") cargo = get_cmd_or_die("cargo") if need_cargo_clean(args): invoke(cargo, "clean") build_flags = [ "-n", "19", str(cargo), "build", "--features", "llvm-static" ] if not args.debug: build_flags.append("--release") if args.verbose: build_flags.append("-vv") llvm_config = os.path.join(c.LLVM_BLD, "bin/llvm-config") assert os.path.isfile(llvm_config), "missing binary: " + llvm_config if on_mac(): llvm_system_libs = "-lz -lcurses -lm -lxml2" else: # linux llvm_system_libs = "-lz -lrt -ltinfo -ldl -lpthread -lm" llvm_libdir = os.path.join(c.LLVM_BLD, "lib") # log how we run `cargo build` to aid troubleshooting, IDE setup, etc. msg = "invoking cargo build as\ncd {} && \\\n".format(c.C2RUST_DIR) msg += "LIBCURL_NO_PKG_CONFIG=1\\\n" msg += "ZLIB_NO_PKG_CONFIG=1\\\n" msg += "LLVM_CONFIG_PATH={} \\\n".format(llvm_config) msg += "LLVM_SYSTEM_LIBS='{}' \\\n".format(llvm_system_libs) msg += "C2RUST_AST_EXPORTER_LIB_DIR={} \\\n".format(llvm_libdir) msg += " cargo" msg += " ".join(build_flags) logging.debug(msg) # NOTE: the `curl-rust` and `libz-sys` crates use the `pkg_config` # crate to locate the system libraries they wrap. This causes # `pkg_config` to add `/usr/lib` to `rustc`s library search path # which means that our `cargo` invocation picks up the system # libraries even when we're trying to link against libs we built. # https://docs.rs/pkg-config/0.3.14/pkg_config/ with pb.local.cwd(c.C2RUST_DIR): with pb.local.env(LIBCURL_NO_PKG_CONFIG=1, ZLIB_NO_PKG_CONFIG=1, LLVM_CONFIG_PATH=llvm_config, LLVM_SYSTEM_LIBS=llvm_system_libs, C2RUST_AST_EXPORTER_LIB_DIR=llvm_libdir): invoke(nice, *build_flags)
def _is_excluded(name): """ The examples that use x11 and the `f128` crate need to be excluded on macOS. This function can be extended to exclude examples on the linux platform as well. """ mac_exclusion_set = { "grabc", "libxml2", "tinycc", "xzoom", } return name in mac_exclusion_set and on_mac()
def build_transpiler(args): nice = get_cmd_or_die("nice") cargo = get_cmd_or_die("cargo") if need_cargo_clean(args): invoke(cargo, "clean") build_flags = [ "-n", "19", str(cargo), "build", "--features", "llvm-static" ] if not args.debug: build_flags.append("--release") if args.verbose: build_flags.append("-vv") llvm_config = os.path.join(c.LLVM_BLD, "bin/llvm-config") assert os.path.isfile(llvm_config), \ "expected llvm_config at " + llvm_config if on_mac(): llvm_system_libs = "-lz -lcurses -lm -lxml2" else: # linux llvm_system_libs = "-lz -lrt -ltinfo -ldl -lpthread -lm" llvm_libdir = os.path.join(c.LLVM_BLD, "lib") # NOTE: the `curl-rust` and `libz-sys` crates use the `pkg_config` # crate to locate the system libraries they wrap. This causes # `pkg_config` to add `/usr/lib` to `rustc`s library search path # which means that our `cargo` invocation picks up the system # libraries even when we're trying to link against libs we built. # https://docs.rs/pkg-config/0.3.14/pkg_config/ with pb.local.cwd(c.C2RUST_DIR): with pb.local.env(LIBCURL_NO_PKG_CONFIG=1, ZLIB_NO_PKG_CONFIG=1, LLVM_CONFIG_PATH=llvm_config, LLVM_LIB_DIR=llvm_libdir, LLVM_SYSTEM_LIBS=llvm_system_libs): invoke(nice, *build_flags)
def test_ast_exporter(cc_db_path: str): """ run ast-exporter on tinycbor if on linux. testing is not supported on macOS since bear requires system integrity protection to be disabled. """ assert not on_mac(), "sanity testing requires linux host" ast_extr = os.path.join(c.LLVM_BIN, "ast-exporter") if not os.path.isfile(ast_extr): die("ast-exporter not found in " + c.LLVM_BIN) ast_extr = get_cmd_or_die(ast_extr) include_dirs = get_system_include_dirs() with open(cc_db_path, "r") as handle: cc_db = json.load(handle) for cmd in cc_db: exporter_ast_from(ast_extr, cc_db_path, include_dirs, **cmd) logging.info("PASS sanity testing")
def test_ruby(args: argparse.Namespace) -> bool: if on_mac(): die("transpiling ruby on mac is not supported.") if not os.path.isfile(os.path.join(c.DEPS_DIR, RUBY_ARCHIVE)): with pb.local.cwd(c.DEPS_DIR): download_archive(RUBY_URL, RUBY_ARCHIVE) invoke_quietly(TAR, "xf", RUBY_ARCHIVE) cc_db_file = os.path.join(RUBY_SRC, c.CC_DB_JSON) # unconditionally compile ruby since we don't know if # cc_db was generated from the environment we're in. with pb.local.cwd(RUBY_SRC), pb.local.env(CC="clang", cflags="-w"): configure = pb.local.get("./configure") invoke(configure) invoke(BEAR[MAKE[JOBS]]) if not os.path.isfile(cc_db_file): die("missing " + cc_db_file, errno.ENOENT) with open(cc_db_file) as cc_db: return transpile_files(cc_db, args.jobs, None, False, args.verbose)
def _parse_args(): """ define and parse command line arguments here. """ desc = 'download dependencies for the AST exporter and built it.' parser = argparse.ArgumentParser(description=desc) parser.add_argument('-c', '--clean-all', default=False, action='store_true', dest='clean_all', help='clean everything before building') parser.add_argument('--with-clang', default=False, action='store_true', dest='with_clang', help='build clang with this tool') parser.add_argument('--without-assertions', default=True, action='store_false', dest='assertions', help='build the tool and clang without assertions') parser.add_argument('-x', '--xcode', default=False, action='store_true', dest='xcode', help='generate Xcode project files (macOS only)') c.add_args(parser) args = parser.parse_args() if not on_mac() and args.xcode: die("-x/--xcode option requires macOS host.") c.update_args(args) return args
def path_to_cc_db(): if not os.path.isfile(cc_cmd_db) and not on_mac(): die("not found: " + cc_cmd_db) return cc_cmd_db
def transpile_files(cc_db: TextIO, filter: str = None, extra_impo_args: List[str] = [], import_only: bool = False, verbose: bool = False, emit_build_files: bool = True, main_module_for_build_files: str = None, cross_checks: bool = False, use_fakechecks: bool = False, cross_check_config: List[str] = [], reloop_cfgs: bool = True) -> bool: """ run the ast-exporter and ast-importer on all C files in a compile commands database. """ rustfmt = os.path.join(get_rust_toolchain_binpath(), "rustfmt") rustfmt = get_cmd_or_die(rustfmt) ast_expo = get_cmd_or_die(c.AST_EXPO) ast_impo = get_cmd_or_die(c.AST_IMPO) cc_db_name = cc_db.name cc_db = json.load(cc_db) check_main_module(main_module_for_build_files, cc_db) if filter: # skip commands not matching file filter cc_db = [cmd for cmd in cc_db if filter in c['file']] if not on_mac(): ensure_code_compiled_with_clang(cc_db) include_dirs = get_system_include_dirs() impo_args = ['--translate-entry'] if emit_build_files: impo_args.append('--emit-module') if cross_checks: impo_args.append('--cross-checks') for ccc in cross_check_config: impo_args.append('--cross-check-config') impo_args.append(ccc) if reloop_cfgs: impo_args.append('--reloop-cfgs') def transpile_single(cmd) -> Tuple[str, int, str, str, str]: if import_only: cbor_file = os.path.join(cmd['directory'], cmd['file'] + ".cbor") else: cbor_file = export_ast_from(ast_expo, cc_db_name, include_dirs, **cmd) assert os.path.isfile(cbor_file), "missing: " + cbor_file ld_lib_path = get_rust_toolchain_libpath() # don't overwrite existing ld lib path if any... if 'LD_LIBRARY_PATH' in pb.local.env: ld_lib_path += ':' + pb.local.env['LD_LIBRARY_PATH'] # import ast with pb.local.env(RUST_BACKTRACE='1', LD_LIBRARY_PATH=ld_lib_path): file_basename = os.path.basename(cmd['file']) cbor_basename = os.path.basename(cbor_file) logging.info(" importing ast from %s", cbor_basename) translation_cmd = "RUST_BACKTRACE=1 \\\n" translation_cmd += "LD_LIBRARY_PATH=" + ld_lib_path + " \\\n" translation_cmd += str(ast_impo[cbor_file, impo_args, extra_impo_args]) logging.debug("translation command:\n %s", translation_cmd) try: retcode, _stdout, _stderr = ast_impo[cbor_file, impo_args, extra_impo_args].run() e = "Expected file suffix `.c.cbor`; actual: " + cbor_basename assert cbor_file.endswith(".c.cbor"), e rust_file = cbor_file[:-7] + ".rs" with open(rust_file, "w") as rust_fh: rust_fh.writelines(_stdout) logging.debug("wrote output rust to %s", rust_file) rustfmt(rust_file) return (file_basename, retcode, _stdout, _stderr, os.path.abspath(rust_file)) except pb.ProcessExecutionError as pee: return (file_basename, pee.retcode, pee.stdout, pee.stderr, None) commands = sorted(cc_db, key=lambda cmd: os.path.basename(cmd['file'])) results = (transpile_single(cmd) for cmd in commands) if emit_build_files: modules = [(rust_src, retcode == 0) for (_, retcode, _, _, rust_src) in results if rust_src is not None] cc_db_dir = os.path.dirname(cc_db_name) write_build_files(cc_db_dir, modules, main_module_for_build_files, cross_checks, use_fakechecks, cross_check_config) successes, failures = 0, 0 for (fname, retcode, stdout, stderr, _) in results: if not retcode: successes += 1 print(Colors.OKGREEN + " import successful" + Colors.NO_COLOR) logging.debug(" import successful") else: # non-zero retcode failures += 1 if verbose: print(Colors.FAIL + " import failed" + Colors.NO_COLOR) logging.debug(" import failed") logging.warning(stderr) else: print(Colors.FAIL + " import failed (error in log)" + Colors.NO_COLOR) logging.debug(" import failed") logging.debug(stderr) print("translations: " + str(successes + failures)) print("successes...: " + str(successes)) print("failures....: " + str(failures)) return failures == 0
def transpile_files(cc_db: TextIO, filter: Optional[Callable[[str], bool]] = None, extra_impo_args: List[str] = [], import_only: bool = False, verbose: bool = False, emit_build_files: bool = True, emit_modules: bool = False, main_module_for_build_files: str = None, cross_checks: bool = False, use_fakechecks: bool = False, cross_check_config: List[str] = [], reloop_cfgs: bool = True, reorganize_definitions: bool = False) -> bool: """ run the ast-exporter and ast-importer on all C files in a compile commands database. """ rustfmt = os.path.join(get_rust_toolchain_binpath(), "rustfmt") rustfmt = get_cmd_or_die(rustfmt) ast_expo = get_cmd_or_die(c.AST_EXPO) ast_impo = get_cmd_or_die(c.AST_IMPO) cc_db_name = cc_db.name cc_db = json.load(cc_db) check_main_module(main_module_for_build_files, cc_db) if filter: # skip commands not matching file filter cc_db = [cmd for cmd in cc_db if filter(cmd['file'])] if not on_mac(): ensure_code_compiled_with_clang(cc_db) # MacOS Mojave does not have `/usr/include` even if the command line # tools are installed. The fix is to run the developer package: # `macOS_SDK_headers_for_macOS_10.14.pkg` in # `/Library/Developer/CommandLineTools/Packages`. # Source https://forums.developer.apple.com/thread/104296 if on_mac() and not os.path.isdir('/usr/include'): emsg = ("directory /usr/include not found. " "Please install the following package: " "/Library/Developer/CommandLineTools/Packages/" "macOS_SDK_headers_for_macOS_10.14.pkg " "or the equivalent version on your host.") die(emsg, errno.ENOENT) impo_args = ['--translate-entry'] if emit_build_files or emit_modules: impo_args.append('--emit-module') if cross_checks: impo_args.append('--cross-checks') for ccc in cross_check_config: impo_args.append('--cross-check-config') impo_args.append(ccc) if reloop_cfgs: impo_args.append('--reloop-cfgs') if reorganize_definitions: impo_args.append('--reorganize-definitions') def transpile_single(cmd) -> Tuple[str, int, str, str, str]: if import_only: cbor_file = os.path.join(cmd['directory'], cmd['file'] + ".cbor") else: cbor_file = export_ast_from(ast_expo, cc_db_name, **cmd) assert os.path.isfile(cbor_file), "missing: " + cbor_file ld_lib_path = get_rust_toolchain_libpath() # don't overwrite existing ld lib path if any... if 'LD_LIBRARY_PATH' in pb.local.env: ld_lib_path += ':' + pb.local.env['LD_LIBRARY_PATH'] # import ast with pb.local.env(RUST_BACKTRACE='1', LD_LIBRARY_PATH=ld_lib_path): file_basename = os.path.basename(cmd['file']) cbor_basename = os.path.basename(cbor_file) logging.info(" importing ast from %s", cbor_basename) translation_cmd = "RUST_BACKTRACE=1 \\\n" translation_cmd += "LD_LIBRARY_PATH=" + ld_lib_path + " \\\n" translation_cmd += str(ast_impo[cbor_file, impo_args, extra_impo_args]) logging.debug("translation command:\n %s", translation_cmd) try: ast_impo_cmd = ast_impo[cbor_file, impo_args, extra_impo_args] # NOTE: this will log ast-importer output but not in color retcode, stdout, importer_warnings = ast_impo_cmd.run() if importer_warnings: if verbose: logging.warning(importer_warnings) else: logging.debug(importer_warnings) e = "Expected file suffix `.c.cbor`; actual: " + cbor_basename assert cbor_file.endswith(".c.cbor"), e rust_file = cbor_file[:-7] + ".rs" path, file_name = os.path.split(rust_file) file_name = file_name.replace('-', '_') rust_file = os.path.join(path, file_name) rustfmt(rust_file) return (file_basename, retcode, stdout, importer_warnings, os.path.abspath(rust_file)) except pb.ProcessExecutionError as pee: return (file_basename, pee.retcode, pee.stdout, pee.stderr, None) commands = sorted(cc_db, key=lambda cmd: os.path.basename(cmd['file'])) results = [transpile_single(cmd) for cmd in commands] if emit_build_files: modules = [(rust_src, retcode == 0) for (_, retcode, _, _, rust_src) in results if rust_src is not None] cc_db_dir = os.path.dirname(cc_db_name) write_build_files(cc_db_dir, modules, main_module_for_build_files, cross_checks, reorganize_definitions, use_fakechecks, cross_check_config) successes, failures = 0, 0 for (fname, retcode, stdout, stderr, _) in results: if not retcode: successes += 1 print(Colors.OKGREEN + " import successful" + Colors.NO_COLOR) logging.debug(" import successful") else: # non-zero retcode failures += 1 if verbose: print(Colors.FAIL + " import failed" + Colors.NO_COLOR) logging.debug(" import failed") logging.warning(stderr) else: print(Colors.FAIL + " import failed (error in log)" + Colors.NO_COLOR) logging.debug(" import failed") logging.debug(stderr) print("translations: " + str(successes + failures)) print("successes...: " + str(successes)) print("failures....: " + str(failures)) return failures == 0