Хабрахабр

C/C++. Как использовать внедряемые ресурсы приложения, при работе в GCC на Linux

В общем, задача такая: Захотелось как-то мне использовать в Linux внедряемые ресурсы, причём, автоматически.

  1. Имеется Eclipse проект программы на C++.
  2. ОС: Linux Ubuntu. Компилятор: G++
  3. В проекте используются данные из внешних файлов: строки локализации, SQL-запросы, картинки, звуки и т.д.
  4. Все ресурсы необходимо внедрить в исполняемый файл, ибо программу планируется распространять, как портативную.
  5. Кроме того, хочется, что бы процесс был максимально автоматизирован, ибо лень.

Среди найденных наиболее универсальным мне показалась идея использовать параметр «--format=binary» линковщика «ld». Для начала, поиск по форумам дал несколько возможных способов решения задачи. Посты на форумах обещали, что команда вида:

g++ -Wl,--format=binary -Wl,my.res -Wl,--format=default

прилинкует к приложению файл «my.res» и создаст два символа — _binary_my_res_start и _binary_my_res_end, указывающих, соответственно, на начало и конец тех самых данных, которые были в прилинкованном файле. Следовательно, обращение к данным из C++ можно было бы осуществить как-то так:

extern const uint8_t my_res_start[] asm("_binary_my_res_start"); extern const uint8_t my_res_end[] asm("_binary_my_res_end");

Пишем всё, как надо, а компилятор недоволен. Но не тут-то было. Ну ничего, nm нам в помощь. Символа «_binary_my_res_start», видите ли, он найти не может. Пишем следующую команду:

nm MyProgramm |grep -w -o -P -e '_binary_[\w\d_]+'

И получаем:


_binary__home_unknown_workspace_MyProgramm_res_my_res_sql_end
_binary__home_unknown_workspace_MyProgramm_res_my_res_sql_start

Проблема решается, если в событие PostBuild в настройках проекта Eclipse добавить вызов следующего скрипта: Выходит, что имя символа включает в себя весь путь до него, что, в перспективе, может привести к необходимости постоянного переписывания заголовочного файла, содержащего ссылки на ресурсы.

#!/bin/bash
OUTPUT=$1/resources.h
printf '#ifndef __RESOURCES_H__\n' > "$OUTPUT"
printf '#define __RESOURCES_H__\n\n' >> "$OUTPUT"
printf '#include <inttypes.h>\n\n' >> "$OUTPUT"
SYMBOLS=$(nm NewsParser |grep -w -o -P -e '_binary_[\w\d_]+') >> "$OUTPUT"
VAR_SIZES_LIST=''
for SYMBOL in $SYMBOLS
do VAR_NAME=$(echo $SYMBOL | grep -o -P -e 'res_[\w\d_]+'|cut -c 5-) if [[ -z $(echo $SYMBOL|grep _size) ]] then printf '\textern const uint8_t '$VAR_NAME'[]\tasm("'$SYMBOL'");\n\n' >> "$OUTPUT" else START_VAR=$(echo $VAR_NAME|rev|cut -c 5-|rev)'start' END_VAR=$(echo $VAR_NAME|rev|cut -c 5-|rev)'end' VAR_SIZES_LIST=$VAR_SIZES_LIST$(printf '\\tconst uint64_t '$VAR_NAME'\\t=\\t'$END_VAR' - '$START_VAR';\\n\\n') fi
done
printf "$VAR_SIZES_LIST" >> "$OUTPUT"
printf '#endif\n' >> "$OUTPUT"
printf 'File '$OUTPUT' is generated.\n'

Как добавить скрипт «update_resource.sh», лежащий в корне проекта, в событие PostBuild в настройках проекта Eclipse.

Хорошо. Теперь заголовочный файл будет каждый раз как новенький, а обращаться к данным можно по именам переменных, которые не будут меняться, если только не переименовать сам ресурсный файл. Кроме того, данный скрипт вычисляет размер для каждого ресурса. Не то что бы отнять от указателя на конец указатель на начало было большой проблемой, но, всё же, так удобнее.

Ведь добавление каждого нового ресурса в проект будет превращаться в форменный АД. Но это, пока что, не всё. И эту проблему также можно решить при помощи скрипта, только уже на этапе линковки:


FLAGS=$1
OUTPUT_FLAG=$2
OUTPUT_PREFIX=$3
OUTPUT=$4
INPUTS=$5
RESOURCE_PATH=$6
RESOURCES=''
for res_file in $(ls $RESOURCE_PATH/*)
do RESOURCES=$RESOURCES' '-Wl,$res_file echo 'Ресурс '$res_file' добавлен в сборку' done
g++ $FLAGS $OUTPUT_FLAG $OUTPUT_PREFIX$OUTPUT $INPUTS -Wl,--format=binary $RESOURCES -Wl,--format=default

Как в настройках проекта Eclipse заменить вызов стандартного линковщика на собственный скрипт.

  • Красным на картинке выделено место, в котором вместо стандартной команды вызова линковщика прописан путь к скрипту «link.sh», лежащему в корне проекта.
  • Зелёным на картинке выделено место, в котором к обычным параметрам линковщика добавляется ещё один, который сообщает скрипту расположение каталога с ресурсами.
  • Кроме того, важно не забыть обернуть двойными кавычками остальные параметры, чтобы они случайно не побились пробелами не в том порядке, в котором их ждёт скрипт.

Отлично. Теперь все файлы, которые лежат в подкаталоге «res», будут сами попадать в ресурсы при каждой сборке.

Показать больше

Похожие публикации

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»