如何让SCons不包含zip文件中的基础目录?

Xym*_*ech 3 python zip scons

SCons提供了一个Zip构建器,用于从文件组生成zip文件.例如,假设我们有一个如下所示的文件夹foo:

foo/
foo/blah.txt
Run Code Online (Sandbox Code Playgroud)

我们foo.zip从文件夹创建zip文件foo:

env.Zip('foo.zip', 'foo/')
Run Code Online (Sandbox Code Playgroud)

这会产生一个zip文件:

$ unzip -l foo.zip
Archive:  foo.zip
  foo/
  foo/foo.txt
Run Code Online (Sandbox Code Playgroud)

但是,假设我们使用的是一个VariantDir包含foo的bar:

bar/
bar/foo/
bar/foo/foo.txt
Run Code Online (Sandbox Code Playgroud)

因为我们在a中VariantDir,我们仍然使用相同的命令来创建zip文件,即使它具有稍微不同的效果:

env.Zip('foo.zip', 'foo/')
Run Code Online (Sandbox Code Playgroud)

这会生成zip文件:

$ unzip -l bar/foo.zip
Archive:  bar/foo.zip
  bar/foo/
  bar/foo/foo.txt
Run Code Online (Sandbox Code Playgroud)

问题是bar/zip中每个文件的额外前缀.如果这不是SCons,那么简单的解决方案就是bar使用类似的东西进入并调用zip cd bar; zip -r foo.zip foo/.然而,这对SCons来说很奇怪/困难,而且无论如何看起来都非常像SCons一样.有更好的解决方案吗?

Xym*_*ech 5

您可以创建一个SCons Builder来完成此任务.我们可以使用标准的Python zipfile来制作zip文件.我们利用了zipfile.write,它允许我们指定要添加的文件,以及它应该在zip中调用的内容:

zf.write('foo/bar', 'bar') # save foo/bar as bar
Run Code Online (Sandbox Code Playgroud)

为了获得正确的路径,我们使用os.path.relpath基本文件的路径来查找整个文件的路径.

最后,我们使用os.walk遍历我们想要添加的目录的内容,并调用前两个函数将它们正确地添加到最终的zip中.

import os.path
import zipfile

def zipbetter(target, source, env):
    # Open the zip file with appending, so multiple calls will add more files
    zf = zipfile.ZipFile(str(target[0]), 'a', zipfile.ZIP_DEFLATED)
    for s in source:
        # Find the path of the base file
        basedir = os.path.dirname(str(s))
        if s.isdir():
            # If the source is a directory, walk through its files
            for dirpath, dirnames, filenames in os.walk(str(s)):
                for fname in filenames:
                    path = os.path.join(dirpath, fname)
                    if os.path.isfile(path):
                        # If this is a file, write it with its relative path
                        zf.write(path, os.path.relpath(path, basedir))
        else:
            # Otherwise, just write it to the file
            flatname = os.path.basename(str(s))
            zf.write(str(s), flatname)
    zf.close()

# Make a builder using the zipbetter function, that takes SCons files
zipbetter_bld = Builder(action = zipbetter,
                        target_factory = SCons.Node.FS.default_fs.Entry,
                        source_factory = SCons.Node.FS.default_fs.Entry)

# Add the builder to the environment
env.Append(BUILDERS = {'ZipBetter' : zipbetter_bld})
Run Code Online (Sandbox Code Playgroud)

像普通的SCons一样调用它Zip:

env.ZipBetter('foo.zip', 'foo/')
Run Code Online (Sandbox Code Playgroud)