Разбирал я значит баш скрипты на сборочных серверах и наткнулся на интересную команду source. Вызов выглядел следующим образом:
source <filename>.build
Мне сразу стало интересно, что лежит в .build файле... Там был прописан обычный bash скрипт, который определял ряд переменных и функций для компиляции и установки приложения:
#!/bin/bash
BDIR="Release"
src_config() {
cmake -DCMAKE_BUILD_TYPE=${BDIR}
}
src_compile() {
make
}
Следующий вопрос - почему именно source? Можно же выполнить скрипт внутри командной оболочки через обращение к файлу через путь:
./<filename>.build
Как оказалось, есть несколько причин, по которым данная команда существует и используется, давайте разбираться.
Принцип работы команды source:
Данная команда является полным аналогом оператора . - встроенным в оболочку инструментом, который позволяет выполнять ряд инструкций целевого файла в рамках текущего "shell environment", что, как минимум, сохраняет доступ ко всем, локально определенным, переменным оболочки:
$ MY_VAR=123
$ cat script.sh
echo $MY_VAR
$ source script.sh
123
$ . script.sh
123
Когда вы вызываете source и передаете ему файл на чтение, все команды внутри будут последовательно выполнены так, будто вы их вручную прописали в терминале. Файл тут можно передавать несколькими способами:
1) Через абсолютный/относительный путь:
source ./XTouch/xtouch.sq
2) По имени. В данном случае утилита попытается найти файл в одном из каталогов, указанных для переменной $PATH. Если попытка окончилась провалом, программа попытается взять его из текущей активной директории:
source xtouch.sq
Отличия в запуске скрипта по пути "./" и через команду source:
Как говорилось ранее, команда source выполняет инструкции в рамках текущей оболочки, не отпочковываясь и не создавая новый процесс, что позволяет использовать локальные переменные.
Запуск скрипта по пути, в свою очередь, создает дочерний процесс и запускает дополнительную оболочку со своим пуллом переменных:
$ ps -auxf
\_ /bin/bash
\_ /bin/bash ./test.sh
Если переменная оболочки не была переведена в переменную окружения через ключевое слово "export", дочерний процесс не сможет к ней обратиться. В таком случае, необходимо явно передать и определить ее при запуске:
$ MY_VAR=123
$ source script.sh
123
$ MY_VAR="$MY_VAR" script.sh
123
Также, нужно помнить, что, когда вы вызываете скрипт по имени, для корректной работы следует прописывать shebang "#!<path to app>" в начале файла. Таким образом, системе будет понятно то, каким приложением необходимо обработать указанные инструкции:
#!/usr/bin/make -f
install:
dh_installdirs
dh_install
В случае с source это не имеет смысла - тут мы работаем на уровне текущей оболочки. Операционной системой не будет запущен очередной интерпретатор, как следствие, данная строка будет проигнорирована.
Пример использования команды source:
Как говорилось в начале поста, я наткнулся на source, пока шарился на сборочном сервере. Стояла задача дебианизировать пакет, который должен был при инсталляции корректно встроить в систему ряд медиа библиотек и конфигов для декодирования видео на GPU.
Как правило, проекты такой сложности требуют выполнения немалого количества действий на разных этапах: при конфигурации нужно передать "миллион" флагов в cmake, при установке построить глубокую иерархию директорий, перенести файлы по путям и т.д.
Все эти процессы могли бы быть отображены и проработаны в файле rules, но тогда в нем было бы безумно тяжело ориентироваться. Было принято решение разделить логику на группу функций: src_install(), src_compile(), src_config(). После чего, локально, не в рамках новой оболочки, их определять и вызывать.
Определения были скомпонованы в отдельном файле, что позволило красиво и локанично оформить rules:
install:
source debian/pipeline.build && src_config && src_install
build:
source debian/pipeline.build && src_config && src_compile