【问题标题】:How to create a symbolic link with SCons?如何使用 SCons 创建符号链接?
【发布时间】:2010-08-20 15:20:50
【问题描述】:

我正在使用 SCons 来构建项目,并且需要将符号链接添加到它通过 env.Install 安装的文件。什么命令会创建一个相当于在命令行上运行 ln -s 的链接?

【问题讨论】:

    标签: scons symlink


    【解决方案1】:

    SCons 没有专用的符号链接命令,但您可以使用 Python 的 os 模块中的 os.symlink(src, dst)

    import os
    env = Environment()
    def SymLink(target, source, env):
        os.symlink(os.path.abspath(str(source[0])), os.path.abspath(str(target[0])))
    env.Command("file.out", "file.in", SymLink)
    

    这在 Windows 上可能无法正常工作,我只在 Linux 上尝试过。

    【讨论】:

    • 由于某种原因,它在尝试在子目录中创建符号链接时不起作用,例如 "env.Command (flavour + '/Resources', 'src/Resources', SymLink)" where flavor是“调试”或“发布”。
    • 它不应该在 Windows 上工作,因为这个操作系统不支持链接
    【解决方案2】:

    SCons 核心代码在符号链接支持方面似乎没有什么进步,我对我在网上找到的任何一种解决方案都不满意。这是一个潜在的构建者,它结合了尼克和richq 答案的各个方面。此外,它会捕获名称更改(由于发射器方法),并且与平台无关。

    我更喜欢这个构建器,因为它会创建相对于安装目录的链接。我想可以添加一个选项来强制链接是绝对的,但我还不需要或想要那个。

    目前,如果操作系统不支持符号链接,我只是通过并且什么都不做,但是例如可以使用 os.copytree() 但是如果源是目录,则依赖关系会变得混乱,因此发射器需要做一些花哨的东西。我愿意在这里提出任何建议。

    可以将以下代码放入 site_scons/site_tools/symlink.py 文件中(其中包含空白的 _init_.py 文件适当的地方)。然后在 SConstruct 文件中执行此操作:

    S构造:

    env = Environment()
    env.Tool('symlink')
    env.SymLink('link_name.txt', 'real_file.txt')
    

    符号链接.py:

    import os
    from os import path
    
    from SCons.Node import FS
    from SCons.Script import Action, Builder
    
    def generate(env):
        '''
        SymLink(link_name,source)
        env.SymLink(link_name,source)
    
        Makes a symbolic link named "link_name" that points to the
        real file or directory "source". The link produced is always
        relative.
        '''
        bldr = Builder(action = Action(symlink_builder,symlink_print),
            target_factory = FS.File,
            source_factory = FS.Entry,
            single_target = True,
            single_source = True,
            emitter = symlink_emitter)
        env.Append(BUILDERS = {'SymLink' : bldr})
    
    def exists(env):
        '''
        we could test if the OS supports symlinks here, or we could
        use copytree as an alternative in the builder.
        '''
        return True
    
    def symlink_print(target, source, env):
        lnk = path.basename(target[0].abspath)
        src = path.basename(source[0].abspath)
        return 'Link: '+lnk+' points to '+src
    
    def symlink_emitter(target, source, env):
        '''
        This emitter removes the link if the source file name has changed
        since scons does not seem to catch this case.
        '''
        lnk = target[0].abspath
        src = source[0].abspath
        lnkdir,lnkname = path.split(lnk)
        srcrel = path.relpath(src,lnkdir)
    
        if int(env.get('verbose',0)) > 3:
            ldir = path.relpath(lnkdir,env.Dir('#').abspath)
            if rellnkdir[:2] == '..':
                ldir = path.abspath(ldir)
            print '  symbolic link in directory: %s' % ldir
            print '      %s -> %s' % (lnkname,srcrel)
    
        try:
            if path.exists(lnk):
                if os.readlink(lnk) != srcrel:
                    os.remove(lnk)
        except AttributeError:
            # no symlink available, so we remove the whole tree? (or pass)
            #os.rmtree(lnk)
            print 'no os.symlink capability on this system?'
    
        return (target, source)
    
    def symlink_builder(target, source, env):
        lnk = target[0].abspath
        src = source[0].abspath
        lnkdir,lnkname = path.split(lnk)
        srcrel = path.relpath(src,lnkdir)
    
        if int(env.get('verbose',0)) > 4:
            print 'target:', target
            print 'source:', source
            print 'lnk:', lnk
            print 'src:', src
            print 'lnkdir,lnkname:', lnkdir, lnkname
            print 'srcrel:', srcrel
    
        if int(env.get('verbose',0)) > 4:
            print 'in directory: %s' % path.relpath(lnkdir,env.Dir('#').abspath)
            print '    symlink: %s -> %s' % (lnkname,srcrel)
    
        try:
            os.symlink(srcrel,lnk)
        except AttributeError:
            # no symlink available, so we make a (deep) copy? (or pass)
            #os.copytree(srcrel,lnk)
            print 'no os.symlink capability on this system?'
    
        return None
    

    【讨论】:

    • 您是否也为硬链接编写过变体?我想要与内置Install 相同的接口,其中第一个参数是目录。
    【解决方案3】:

    这会创建一个构建器来执行这项工作:

    mylib = env.SharedLibrary("foobar", SRCS)
    
    builder = Builder(action = "ln -s ${SOURCE.file} ${TARGET.file}", chdir = True)
    
    env.Append(BUILDERS = {"Symlink" : builder})
    
    mylib_link = env.Symlink("_foobar.so", mylib)
    
    env.Default(mylib)
    env.Default(mylib_link)
    

    同样,此解决方案适用于 Linux。

    【讨论】:

    • 不幸的是,它在目录上(根本)不能很好地工作:“TypeError: Directory /home/septi/Dropbox/Code/StreetCleaner/src/Resources found where file expected.:”跨度>
    • @Septagram:如何解决目录问题?
    • @Nordlöw,对不起,已经有一段时间了,我不知道:(如果你发现了什么,请尝试其他答案并发表评论。
    • 看来 SCons 不支持 target 作为目录。但是,包装函数确实解决了这个问题。
    • 可能使用 target_factory=Dir 作为您的 Builder 的 arg 将起作用:builder = Builder(action=..., target_factory=Dir) 请参阅 target_factory 下的手册页了解更多信息。
    【解决方案4】:

    如果您想直接向 shell 发出命令并了解操作系统,也可以使用subprocess

    例如:subprocess.call(['ln', '-s', '</src/path>', '</dest/path>'])

    【讨论】:

      【解决方案5】:

      除了 Nicks 解决方案之外,您还可以通过使用文件作为目录名称载体来添加目录符号链接。这不是最干净的解决方案,调试路径名很痛苦,但这很好用:

      def symlink_last(target_source_env):
          src = os.path.basename(os.path.dirname(str(source[0])))
          link = "deliverables/last"
          print "Symlinking "+ src + "as" + link
          os.symlink(src, link)
      
      BUILD_TARGETS.append('link')
      install_dir = "deliverables/subdir"
      carrier_file = "filename"
      builder = Builder(action = symlink_last, chdir=False)
      env.Append(BUILDERS={ "Symlink" : builder }) 
      env.Alias(target="link", source=env.Symlink(dir="deliverables", source = install_dir + carrier_file)
      

      这将创建一个指向名为 deliverables/lastdeliverables/subdir 的链接,前提是存在文件 deliverables/subdir/filename

      【讨论】:

        猜你喜欢
        • 2011-12-13
        • 1970-01-01
        • 2014-06-06
        • 2019-04-18
        • 2013-07-29
        • 1970-01-01
        • 1970-01-01
        • 2021-12-19
        相关资源
        最近更新 更多