在使用cx_freeze将python程序转换成exe的时候碰到如下错误:
setup.py内容如下:
import sys from cx_Freeze import setup, Executable # Dependencies are automatically detected, but it might need fine tuning. build_exe_options = { # "packages": ["os"], "include-files": ["security/cert.pem", "security/privatekey.pem"], # "create_shared_zip": True, # "excludes": ["tkinter"], } # GUI applications require a different base on Windows (the default is for a # console application). base = None # if sys.platform == "win32": # base = "Win32GUI" setup( name = "test", version = "0.1", description = "My GUI application!", options = {"build_exe": build_exe_options}, executables = [Executable("test.py", base=base)])
对于error: error in setup script: command 'build_exe' has no such option 'include-files'的错误觉得很是奇怪,因为在使用help查看的时候发现include-files是有效的:
仔细看了看源码才发现,原来是cx_freeze同distutils配合的问题。
cx_freeze最终还是使用distutils来实现打包分发,
cx_freeze将python转换为exe时主要是调用了distutils.dist.py中def run_command(self, command)和def _set_command_options(self, command_obj, option_dict=None):方法
def _set_command_options(self, command_obj, option_dict=None): """Set the options for 'command_obj' from 'option_dict'. Basically this means copying elements of a dictionary ('option_dict') to attributes of an instance ('command'). 'command_obj' must be a Command instance. If 'option_dict' is not supplied, uses the standard option dictionary for this command (from 'self.command_options'). """ command_name = command_obj.get_command_name() if option_dict is None: option_dict = self.get_option_dict(command_name) if DEBUG: self.announce(" setting options for '%s' command:" % command_name) for (option, (source, value)) in option_dict.items(): # print 'option= ', option # print '(source, value) = ', (source, value) if DEBUG: self.announce(" %s = %s (from %s)" % (option, value, source)) try: bool_opts = map(translate_longopt, command_obj.boolean_options) except AttributeError: bool_opts = [] try: neg_opt = command_obj.negative_opt except AttributeError: neg_opt = {} try: is_string = isinstance(value, str) if option in neg_opt and is_string: setattr(command_obj, neg_opt[option], not strtobool(value)) elif option in bool_opts and is_string: setattr(command_obj, option, strtobool(value)) elif hasattr(command_obj, option): setattr(command_obj, option, value) else: raise DistutilsOptionError, \ ("error in %s: command '%s' has no such option '%s'" % (source, command_name, option)) except ValueError, msg: raise DistutilsOptionError, msg def run_command(self, command): """Do whatever it takes to run a command (including nothing at all, if the command has already been run). Specifically: if we have already created and run the command named by 'command', return silently without doing anything. If the command named by 'command' doesn't even have a command object yet, create one. Then invoke 'run()' on that command object (or an existing one). """ # Already been here, done that? then return silently. if self.have_run.get(command): return log.info("running %s", command) cmd_obj = self.get_command_obj(command) cmd_obj.ensure_finalized() # print "begin to run" cmd_obj.run() self.have_run[command] = 1
通过_set_command_options源码可以看出,error: error in setup script: command 'build_exe' has no such option 'include-files'的错误是出在hasattr(command_obj, option)部分,也就是说distutils.dist在执行命令时先去检测Command是否有相应的属性,我们来看一下cx_freeze的class build_exe(distutils.core.Command)定义:
def initialize_options(self): self.optimize = 0 self.build_exe = None self.excludes = [] self.includes = [] self.packages = [] self.namespace_packages = [] self.replace_paths = [] self.compressed = None self.copy_dependent_files = None self.init_script = None self.base = None self.path = None self.create_shared_zip = None self.append_script_to_exe = None self.include_in_shared_zip = None self.include_msvcr = None self.icon = None self.constants = [] self.include_files = [] self.zip_includes = [] self.bin_excludes = [] self.bin_includes = [] self.bin_path_includes = [] self.bin_path_excludes = [] self.silent = None
通过代码可以看出,build_exe并没有include-files,通过追溯可以发现build_exe有的是include_files
对于其他的参数,也会存在这样的问题,比如在--help中看到的create-shared-zip,实际上在setup.py中应该是create_shared_zip,也就是说需要将-转换成_,这点需要特别注意
就其原因是因为python的命名规范,连字符(-)不允许出现在python的变量定义中,但下划线(_)可以