Я использую SCons для создания проекта и вам нужно добавить символическую ссылку на файл, который он устанавливает через env.Install. Какая команда сделает ссылку, эквивалентную запуску ln -s
в командной строке?
Как создать символическую ссылку со SCons?
Ответ 1
SCons не имеет выделенной команды символьной ссылки, но вы можете использовать os.symlink(src, dst)
из модуля 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)
Это может работать некорректно в Windows, я пробовал только в Linux.
Ответ 2
Похоже, что в коде ядра SCons нет ничего лучше для поддержки символических ссылок, и я не был удовлетворен каким-либо одним решением, которое нашел в сети. Вот потенциальный строитель, который включает в себя аспекты ответов Ника и Ричка. Кроме того, он будет перехватывать изменения имени (из-за метода emitter) и настолько независим от платформы, насколько я мог себе представить.
Я предпочитаю этот конструктор, потому что он будет создавать ссылки относительно каталога, в котором они установлены. Можно предположить, что можно добавить опцию, чтобы ссылка была абсолютной, но я пока не нуждался или не хотел этого.
В настоящее время, если ОС не поддерживает символические ссылки, я просто пропускаю и ничего не делаю, но можно, например, использовать os.copytree(), однако зависимость становится беспорядочной, если источником является каталог, поэтому эмитент должен будет сделать что-то необычное. Я здесь для любых предложений.
Следующий код можно поместить в файл site_scons/site_tools/symlink.py (с пустыми _init_.py файлами в соответствующих местах). Затем сделайте это в файле 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
Ответ 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.
Ответ 4
Если вы хотите ввести команду непосредственно в оболочку и знать ОС, можно также использовать subprocess
.
Например: subprocess.call(['ln', '-s', '</src/path>', '</dest/path>'])