2010-08-20 14 views
10

Estoy usando SCons para construir un proyecto y necesito agregar un enlace simbólico a un archivo que está instalando a través de env.Install. ¿Qué comando (s) hará un enlace que es equivalente a ejecutar ln -s en la línea de comando?¿Cómo crear un enlace simbólico con SCons?

Respuesta

8

SCons no tiene un comando de enlace simbólico dedicado, pero se puede utilizar os.symlink(src, dst) desde el módulo de Python os:

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) 

Esto puede no funcionar correctamente en Windows, sólo he probado en Linux.

+0

Por alguna razón no funciona cuando se trata de crear un enlace simbólico dentro de un subdirectorio, como "env.Command (flavor + '/ Resources', 'src/Resources', SymLink)" donde el sabor es 'depurar' o 'liberar'. – Septagram

+1

@Septagram por favor vea mi edición –

+0

No funcionará para los archivos instalados – RzR

1

Esto crea un constructor para realizar el trabajo:

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) 

Una vez más, esta solución es para Linux.

+1

Desafortunadamente, no funciona bien (en absoluto) en los directorios: "TypeError: Directory/home/septi/Dropbox/Code/StreetCleaner/src/Resources found where file esperado .: " – Septagram

+0

@Septagram: ¿Cómo se soluciona el problema del directorio? –

+0

@ Nordlöw, lo siento, pero ha pasado un tiempo y no sé :(Por favor, intente con otras respuestas y comente si encuentra algo. – Septagram

7

Parece haber pocos avances en el código básico de SCons para la compatibilidad con enlaces simbólicos y no estaba satisfecho con ninguna solución que encontré en la web. Aquí hay un generador potencial que incorpora aspectos de las respuestas de Nick y Richq. Además, detectará los cambios de nombre (debido al método del emisor) y es tan independiente de la plataforma como podría obtenerlo.

Prefiero este constructor porque creará enlaces relativos al directorio en el que están instalados. Se podría agregar una opción para forzar que el enlace sea absoluto, supongo, pero aún no lo he necesitado ni lo deseo.

Actualmente, si el sistema operativo no admite enlaces simbólicos, simplemente paso y no hago nada, pero se podría usar os.copytree(), por ejemplo, pero la dependencia se vuelve desordenada si el origen es un directorio por lo que el emisor necesitaría hacer algo elegante Estoy a la altura de cualquier sugerencia aquí.

Uno puede poner el código siguiente en el archivo site_scons/site_tools/symlink.py (con blanco _ init _.py archivos en los lugares apropiados). Luego de hacer esto en el archivo SConstruct:

SConstruct:

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

symlink.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 
+0

¿usted ha escrito una variante para enlaces duros, ¿así? Quiero la misma interfaz que builtin 'Install' donde el primer argumento es un directorio. –

Cuestiones relacionadas